feat: add purchase reflections
This commit is contained in:
@@ -1,2 +1,4 @@
|
|||||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/persona?schema=public&connect_timeout=300"
|
DATABASE_URL="postgresql://postgres:postgres@localhost:5433/persona?schema=public&connect_timeout=300"
|
||||||
|
PERSONA_PROMPT=
|
||||||
|
PURCHASE_REFLECTION_THRESHOLD=50
|
||||||
ANTHROPIC_API_KEY=
|
ANTHROPIC_API_KEY=
|
||||||
|
|||||||
65
README.md
65
README.md
@@ -1 +1,64 @@
|
|||||||
# purchases-personas
|
# Purchases Personas Generator
|
||||||
|
|
||||||
|
A TypeScript application that leverages the Anthropic Claude API to generate realistic fictional personas and their weekly purchase behaviors. For creating synthetic datasets for retail/e-commerce applications with believable user behaviors and spending patterns.
|
||||||
|
|
||||||
|
## 🌟 Features
|
||||||
|
|
||||||
|
- Generate detailed fictional personas including:
|
||||||
|
- Personal demographics and household details
|
||||||
|
- Daily routines and activities
|
||||||
|
- Shopping preferences and brand loyalties
|
||||||
|
- Financial patterns and spending habits
|
||||||
|
- Contextual behaviors and upcoming events
|
||||||
|
- Create realistic weekly purchase histories that match persona profiles
|
||||||
|
- Store generated data in:
|
||||||
|
- PostgreSQL database for structured querying
|
||||||
|
- JSON files for easy data portability
|
||||||
|
|
||||||
|
## 🚀 Getting Started
|
||||||
|
|
||||||
|
1. Install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Set up your environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Initialize the database:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Build and start the application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn build
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
|
||||||
|
For development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ Available Scripts
|
||||||
|
|
||||||
|
- `yarn start` - Start the production server
|
||||||
|
- `yarn dev` - Start development server with hot reload
|
||||||
|
- `yarn build` - Build the TypeScript project
|
||||||
|
- `yarn lint` - Run ESLint with automatic fixes
|
||||||
|
- `yarn format` - Format code using Prettier
|
||||||
|
- `yarn typecheck` - Check TypeScript types
|
||||||
|
- `yarn generate` - Generate Prisma client
|
||||||
|
- `yarn migrate` - Run database migrations
|
||||||
|
|
||||||
|
## ⚠️ Disclaimer
|
||||||
|
|
||||||
|
The personas and purchase histories generated by this tool are fictional and should not be used as real user data. They are intended for testing and development purposes only.
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "purchases-personas",
|
"name": "purchases-personas",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Generate realistic fictional personas and their weekly purchase behaviors using AI",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "prisma generate && node dist/index.js",
|
"start": "prisma generate && node dist/index.js",
|
||||||
"dev": "nodemon src/index.ts",
|
"dev": "nodemon src/index.ts",
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "items" ADD COLUMN "reflections" JSONB;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "items" ALTER COLUMN "reflections" SET DEFAULT null;
|
||||||
@@ -20,16 +20,17 @@ model User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model Item {
|
model Item {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String
|
||||||
amount Float
|
amount Float
|
||||||
datetime DateTime
|
datetime DateTime
|
||||||
location String
|
location String
|
||||||
notes String?
|
notes String?
|
||||||
user User @relation(fields: [userId], references: [id])
|
reflections Json? @default(dbgenerated("null"))
|
||||||
userId Int
|
user User @relation(fields: [userId], references: [id])
|
||||||
createdAt DateTime @default(now())
|
userId Int
|
||||||
updatedAt DateTime @updatedAt
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
@@map("items")
|
@@map("items")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import { generate as generatePurchases } from './purchase/store';
|
|||||||
|
|
||||||
const personaPromise = generatePersona();
|
const personaPromise = generatePersona();
|
||||||
|
|
||||||
|
console.log(`Generating persona...`);
|
||||||
|
|
||||||
personaPromise.then(id => {
|
personaPromise.then(id => {
|
||||||
console.log(`Persona generated! Now generating purchases for id ${id}`);
|
console.log(`Generating purchases for id ${id}...`);
|
||||||
|
|
||||||
const purchasesPromise = generatePurchases(id);
|
const purchasesPromise = generatePurchases(id);
|
||||||
|
|
||||||
purchasesPromise.then(() => {
|
purchasesPromise.then(() => {
|
||||||
console.log('Purchases generated!');
|
console.log('Complete');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
import { prisma } from '../utils/prismaClient';
|
import { prisma } from '../utils/prismaClient';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { Persona } from './types';
|
import { Persona } from './types';
|
||||||
import { Tool } from './tool';
|
import { Tool } from './tool';
|
||||||
import { makeRequest } from '../utils/anthropicClient';
|
import { BaseTool, makeRequest } from '../utils/anthropicClient';
|
||||||
import { createFolderIfNotExists } from '../utils/createFolder';
|
import { createFolderIfNotExists } from '../utils/createFolder';
|
||||||
|
import { generatePrompt } from '../utils/generatePersonaSeed';
|
||||||
const prompt =
|
|
||||||
'Generate a detailed, realistic customer persona that follows the schema structure exactly. Create someone whose traits, habits, and behaviors form a coherent narrative about their purchasing decisions. Randomly select one option from each seed group: LIFE STAGE [young professional | mid-career parent | empty nester | recent graduate | career shifter | semi-retired | newly married | single parent | remote worker | retiree] FINANCIAL STYLE [debt-averse minimalist | luxury spender | budget optimizer | investment-focused | experience seeker | conscious consumer | tech enthusiast | security planner | impulse buyer | traditional saver] LOCATION [urban core | older suburb | new suburb | small town | rural area | coastal city | mountain town | college town | cultural district | tech hub] SPECIAL FACTOR [health-focused | hobby enthusiast | side hustler | community leader | creative professional | outdoor adventurer | tech worker | environmental advocate | cultural enthusiast | academic] ATTITUDE [optimist | pragmatist | skeptic] TECH COMFORT [early adopter | mainstream | traditional] SOCIAL STYLE [extrovert | ambivert | introvert] SEASON [winter | spring | summer | fall]. Ensure all numerical values and scores are justified by the persona context and lifestyle.';
|
|
||||||
|
|
||||||
export async function generate() {
|
export async function generate() {
|
||||||
const result = (await makeRequest(prompt, Tool as any)) as Persona;
|
if (!process.env.PERSONA_PROMPT) {
|
||||||
|
throw Error('Persona prompt missing.');
|
||||||
|
}
|
||||||
|
|
||||||
|
const prompt = `${generatePrompt()} ${process.env.PERSONA_PROMPT}`;
|
||||||
|
|
||||||
|
const result = (await makeRequest(prompt, Tool as BaseTool)) as Persona;
|
||||||
|
|
||||||
const id = await saveToDb(result);
|
const id = await saveToDb(result);
|
||||||
|
|
||||||
await saveToJson(result, id);
|
await saveToJson(result, id);
|
||||||
|
|
||||||
console.log('Persona:', result.core.name);
|
console.log(`Persona name: ${result.core.name}`);
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@@ -29,19 +34,17 @@ export async function saveToDb(persona: Persona) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Persona ${result.name} inserted in DB with id ${result.id}`);
|
console.log(`Persona '${result.name}' inserted in DB with id ${result.id}`);
|
||||||
|
|
||||||
return result.id;
|
return result.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveToJson(persona: Persona, id: number) {
|
export async function saveToJson(persona: Persona, id: number) {
|
||||||
await createFolderIfNotExists('personas');
|
await createFolderIfNotExists(`personas/${id}/`);
|
||||||
|
|
||||||
await fs.promises.writeFile(
|
const jsonName = `personas/${id}/${id}-persona.json`;
|
||||||
`personas/${id}.json`,
|
|
||||||
JSON.stringify(persona),
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`Persona ${persona.core.name} saved as persona/${id}.json`);
|
await fs.promises.writeFile(jsonName, JSON.stringify(persona), 'utf8');
|
||||||
|
|
||||||
|
console.log(`Persona '${persona.core.name}' saved as ${jsonName}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
interface Pet {
|
interface Pet {
|
||||||
[key: string]: any;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FrequencyObject {
|
interface FrequencyObject {
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ function formatList(items?: string[]): string {
|
|||||||
return items.join(', ');
|
return items.join(', ');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function generatePurchasePrompt(persona: Persona): Promise<string> {
|
export async function generatePurchasePrompt(
|
||||||
|
persona: Persona,
|
||||||
|
reflectionThreshold: number
|
||||||
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
const sections: string[] = [];
|
const sections: string[] = [];
|
||||||
|
|
||||||
@@ -187,13 +190,49 @@ ${persona.context?.recent_changes?.length ? `- Recent lifestyle changes: ${forma
|
|||||||
Format each purchase as:
|
Format each purchase as:
|
||||||
[DATE] [TIME] | [STORE] | [ITEMS] | $[AMOUNT] | [CATEGORY] | [PLANNED/IMPULSE]
|
[DATE] [TIME] | [STORE] | [ITEMS] | $[AMOUNT] | [CATEGORY] | [PLANNED/IMPULSE]
|
||||||
|
|
||||||
|
|
||||||
|
For purchases over €${reflectionThreshold}, randomly include 0-5 reflections following these rules:
|
||||||
|
|
||||||
|
1. Each reflection must occur AFTER the purchase date
|
||||||
|
2. Reflections must be in chronological order
|
||||||
|
3. First reflection should typically be within a week of purchase
|
||||||
|
4. Space out subsequent reflections realistically (e.g., weeks or months apart)
|
||||||
|
5. No reflection should be dated after ${new Date().toISOString()}
|
||||||
|
|
||||||
|
Each reflection must include:
|
||||||
|
1. A date when the reflection was made
|
||||||
|
2. A personal comment that makes sense for that point in time
|
||||||
|
3. A satisfaction score from 1-10 (10 being extremely satisfied, 1 being completely regretful)
|
||||||
|
4. The persona's mood or context when making the reflection
|
||||||
|
|
||||||
|
Consider how reflection timing affects content:
|
||||||
|
- Immediate reflections (1-7 days): Initial impressions, emotional responses
|
||||||
|
- Short-term reflections (1-4 weeks): Early usage experience, discovering features/issues
|
||||||
|
- Medium-term reflections (1-3 months): More balanced assessment, practical value
|
||||||
|
- Long-term reflections (3+ months): Durability, long-term value, retrospective thoughts
|
||||||
|
|
||||||
|
Factor these into reflections:
|
||||||
|
- How the persona's view typically evolves over time
|
||||||
|
- Seasonal or contextual factors (e.g., using winter clothes in summer)
|
||||||
|
- Financial impact becoming more/less significant over time
|
||||||
|
- Product durability or performance changes
|
||||||
|
- Changes in the persona's life circumstances
|
||||||
|
- Whether the novelty wears off or appreciation grows
|
||||||
|
|
||||||
|
Format each reflection as:
|
||||||
|
[REFLECTION_DATE] | Mood: [MOOD] | [COMMENT] | Satisfaction: [SCORE]/10
|
||||||
|
|
||||||
|
Example of a purchase with reflections:
|
||||||
|
2024-01-15 12:30 | Nike Store | Running Shoes XC90 | $180 | Clothing | PLANNED
|
||||||
|
Reflections:
|
||||||
|
2024-01-16 | Mood: Excited | "First run with these was amazing - the cushioning is perfect for my style" | Satisfaction: 9/10
|
||||||
|
2024-01-30 | Mood: Focused | "After two weeks of regular runs, they're holding up great and no knee pain" | Satisfaction: 8/10
|
||||||
|
2024-03-10 | Mood: Practical | "Three months in, still performing well but showing some wear on the sides" | Satisfaction: 8/10
|
||||||
|
|
||||||
Generate purchases that align with the persona's lifestyle, income level, and spending patterns.`);
|
Generate purchases that align with the persona's lifestyle, income level, and spending patterns.`);
|
||||||
|
|
||||||
return sections.filter(section => section.trim().length > 0).join('\n\n');
|
return sections.filter(section => section.trim().length > 0).join('\n\n');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error generating prompt:', error);
|
throw new Error(JSON.stringify(error));
|
||||||
throw new Error('Failed to generate purchase prompt');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default generatePurchasePrompt;
|
|
||||||
|
|||||||
@@ -1,57 +1,74 @@
|
|||||||
import { prisma } from '../utils/prismaClient';
|
import { prisma } from '../utils/prismaClient';
|
||||||
import fs, { readFileSync } from 'fs';
|
import fs, { readFileSync } from 'fs';
|
||||||
import { PurchaseList } from './types';
|
import { PurchaseList, Reflection } from './types';
|
||||||
import { Tool } from './tool';
|
import { Tool } from './tool';
|
||||||
import { makeRequest } from '../utils/anthropicClient';
|
import { BaseTool, makeRequest } from '../utils/anthropicClient';
|
||||||
import { createFolderIfNotExists } from '../utils/createFolder';
|
import { createFolderIfNotExists } from '../utils/createFolder';
|
||||||
import generatePurchasePrompt from './promptGenerator';
|
import { generatePurchasePrompt } from './promptGenerator';
|
||||||
|
import { Persona } from '../persona/types';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
export async function generate(personaId: number) {
|
export async function generate(personaId: number) {
|
||||||
const jsonFile = readFileSync(`personas/${personaId}.json`, 'utf-8');
|
const jsonFile = readFileSync(
|
||||||
|
`personas/${personaId}/${personaId}-persona.json`,
|
||||||
|
'utf-8'
|
||||||
|
);
|
||||||
|
|
||||||
const persona = JSON.parse(jsonFile);
|
const persona: Persona = JSON.parse(jsonFile);
|
||||||
|
|
||||||
const personaPrompt = await generatePurchasePrompt(persona);
|
const personaPrompt = await generatePurchasePrompt(
|
||||||
|
persona,
|
||||||
|
parseInt(process.env.PURCHASE_REFLECTION_THRESHOLD ?? '50')
|
||||||
|
);
|
||||||
|
|
||||||
const result = (await makeRequest(
|
const result = (await makeRequest(
|
||||||
personaPrompt,
|
personaPrompt,
|
||||||
Tool as any
|
Tool as BaseTool
|
||||||
)) as PurchaseList;
|
)) as PurchaseList;
|
||||||
|
|
||||||
await saveToDb(personaId, result);
|
await saveToDb(personaId, result);
|
||||||
|
|
||||||
await saveToJson(result, personaId);
|
await saveToJson(result, personaId);
|
||||||
|
|
||||||
console.log('Purchases:', result.items.length);
|
console.log(`Generated ${result.items.length} purchases`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function reflectionToJson(reflection: Reflection): Prisma.JsonObject {
|
||||||
|
return {
|
||||||
|
comment: reflection.comment,
|
||||||
|
satisfactionScore: reflection.satisfactionScore,
|
||||||
|
date: reflection.date,
|
||||||
|
mood: reflection.mood || null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveToDb(personaId: number, purchases: PurchaseList) {
|
export async function saveToDb(personaId: number, purchases: PurchaseList) {
|
||||||
const result = await prisma.item.createMany({
|
const result = await prisma.item.createMany({
|
||||||
data: purchases.items.map(purchase => ({
|
data: purchases.items.map(
|
||||||
userId: personaId,
|
purchase =>
|
||||||
name: purchase.name,
|
({
|
||||||
amount: purchase.amount,
|
userId: personaId,
|
||||||
datetime: purchase.datetime,
|
name: purchase.name,
|
||||||
location: purchase.location,
|
amount: purchase.amount,
|
||||||
notes: purchase.notes
|
datetime: purchase.datetime,
|
||||||
}))
|
location: purchase.location,
|
||||||
|
notes: purchase.notes,
|
||||||
|
reflections: purchase.reflections
|
||||||
|
? purchase.reflections.map(reflectionToJson)
|
||||||
|
: Prisma.JsonNull
|
||||||
|
}) satisfies Prisma.ItemCreateManyInput
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Inserted ${result.count} purchases with persona ${personaId}`);
|
console.log(`Inserted ${result.count} purchases for persona ${personaId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function saveToJson(purchaseList: PurchaseList, id: number) {
|
export async function saveToJson(purchaseList: PurchaseList, id: number) {
|
||||||
await createFolderIfNotExists('purchases');
|
await createFolderIfNotExists(`personas/${id}/`);
|
||||||
|
|
||||||
await createFolderIfNotExists(`purchases/${id}`);
|
const jsonName = `personas/${id}/${id}-purchases.json`;
|
||||||
|
|
||||||
await fs.promises.writeFile(
|
await fs.promises.writeFile(jsonName, JSON.stringify(purchaseList), 'utf8');
|
||||||
`purchases/${id}/${id}.json`,
|
|
||||||
JSON.stringify(purchaseList),
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(
|
console.log(`Saved ${purchaseList.items.length} purchases as ${jsonName}`);
|
||||||
`Saved ${purchaseList.items.length} purchases as purchases/${id}/${id}.json`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const Tool = {
|
|||||||
},
|
},
|
||||||
amount: {
|
amount: {
|
||||||
type: 'number' as const,
|
type: 'number' as const,
|
||||||
description: 'Purchase amount in USD'
|
description: 'Purchase amount in EUR'
|
||||||
},
|
},
|
||||||
datetime: {
|
datetime: {
|
||||||
type: 'string' as const,
|
type: 'string' as const,
|
||||||
@@ -28,6 +28,36 @@ export const Tool = {
|
|||||||
notes: {
|
notes: {
|
||||||
type: 'string' as const,
|
type: 'string' as const,
|
||||||
description: 'Additional purchase details (optional)'
|
description: 'Additional purchase details (optional)'
|
||||||
|
},
|
||||||
|
reflections: {
|
||||||
|
type: 'array' as const,
|
||||||
|
description:
|
||||||
|
'Array of reflections on purchases over threshold amount',
|
||||||
|
items: {
|
||||||
|
type: 'object' as const,
|
||||||
|
properties: {
|
||||||
|
comment: {
|
||||||
|
type: 'string' as const,
|
||||||
|
description: 'Reflective comment about the purchase'
|
||||||
|
},
|
||||||
|
satisfactionScore: {
|
||||||
|
type: 'number' as const,
|
||||||
|
description: 'Purchase satisfaction score (1-10)',
|
||||||
|
minimum: 1,
|
||||||
|
maximum: 10
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
type: 'string' as const,
|
||||||
|
description: 'Date of the reflection in ISO 8601 format',
|
||||||
|
format: 'date-time'
|
||||||
|
},
|
||||||
|
mood: {
|
||||||
|
type: 'string' as const,
|
||||||
|
description: 'Optional context about mood during reflection'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['comment', 'satisfactionScore', 'date'] as const
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
required: ['name', 'amount', 'datetime', 'location'] as const
|
required: ['name', 'amount', 'datetime', 'location'] as const
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
|
export interface Reflection {
|
||||||
|
comment: string;
|
||||||
|
satisfactionScore: number;
|
||||||
|
date: string;
|
||||||
|
mood?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Purchase {
|
export interface Purchase {
|
||||||
name: string;
|
name: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
datetime: string;
|
datetime: string;
|
||||||
location: string;
|
location: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
|
reflections?: Reflection[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PurchaseList {
|
export interface PurchaseList {
|
||||||
items: Purchase[];
|
items: Purchase[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ReflectionJson {
|
||||||
|
comment: string;
|
||||||
|
satisfactionScore: number;
|
||||||
|
date: string;
|
||||||
|
mood: string | null;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import Anthropic from '@anthropic-ai/sdk';
|
import Anthropic from '@anthropic-ai/sdk';
|
||||||
|
|
||||||
export async function makeRequest(prompt: string, tool: any) {
|
export interface BaseTool {
|
||||||
|
readonly name: string;
|
||||||
|
readonly input_schema: {
|
||||||
|
readonly type: 'object';
|
||||||
|
readonly properties: Record<string, unknown>;
|
||||||
|
readonly required?: readonly string[];
|
||||||
|
readonly description?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function makeRequest<T extends BaseTool>(prompt: string, tool: T) {
|
||||||
if (!process.env.ANTHROPIC_API_KEY) {
|
if (!process.env.ANTHROPIC_API_KEY) {
|
||||||
throw Error('Anthropic API key missing.');
|
throw Error('Anthropic API key missing.');
|
||||||
}
|
}
|
||||||
@@ -12,7 +22,7 @@ export async function makeRequest(prompt: string, tool: any) {
|
|||||||
|
|
||||||
const response = await anthropic.messages.create({
|
const response = await anthropic.messages.create({
|
||||||
model: 'claude-3-5-sonnet-20241022',
|
model: 'claude-3-5-sonnet-20241022',
|
||||||
max_tokens: 2000,
|
max_tokens: 8192,
|
||||||
temperature: 1,
|
temperature: 1,
|
||||||
tools: [tool],
|
tools: [tool],
|
||||||
messages: [{ role: 'user', content: prompt }]
|
messages: [{ role: 'user', content: prompt }]
|
||||||
|
|||||||
121
src/utils/generatePersonaSeed.ts
Normal file
121
src/utils/generatePersonaSeed.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
const PROVINCE_CODES = [
|
||||||
|
'00', // Roma
|
||||||
|
'04', // Latina
|
||||||
|
'10', // Torino
|
||||||
|
'12', // Cuneo
|
||||||
|
'16', // Genova
|
||||||
|
'20', // Milano
|
||||||
|
'24', // Bergamo
|
||||||
|
'25', // Brescia
|
||||||
|
'30', // Venezia
|
||||||
|
'31', // Treviso
|
||||||
|
'35', // Padova
|
||||||
|
'40', // Bologna
|
||||||
|
'45', // Rovigo
|
||||||
|
'47', // Forli-Cesena
|
||||||
|
'48', // Ravenna
|
||||||
|
'50', // Firenze
|
||||||
|
'51', // Pistoia
|
||||||
|
'52', // Arezzo
|
||||||
|
'53', // Siena
|
||||||
|
'54', // Massa-Carrara
|
||||||
|
'55', // Lucca
|
||||||
|
'56', // Pisa
|
||||||
|
'57', // Livorno
|
||||||
|
'58', // Grosseto
|
||||||
|
'60', // Ancona
|
||||||
|
'61', // Pesaro
|
||||||
|
'63', // Ascoli Piceno
|
||||||
|
'65', // Pescara
|
||||||
|
'66', // Chieti
|
||||||
|
'67', // L'Aquila
|
||||||
|
'70', // Bari
|
||||||
|
'71', // Foggia
|
||||||
|
'72', // Brindisi
|
||||||
|
'73', // Lecce
|
||||||
|
'74', // Taranto
|
||||||
|
'75', // Matera
|
||||||
|
'80', // Napoli
|
||||||
|
'81', // Caserta
|
||||||
|
'82', // Benevento
|
||||||
|
'83', // Avellino
|
||||||
|
'84', // Salerno
|
||||||
|
'87', // Cosenza
|
||||||
|
'88', // Catanzaro
|
||||||
|
'89', // Reggio Calabria
|
||||||
|
'90', // Palermo
|
||||||
|
'91', // Trapani
|
||||||
|
'92', // Agrigento
|
||||||
|
'93', // Caltanissetta
|
||||||
|
'94', // Enna
|
||||||
|
'95', // Catania
|
||||||
|
'96', // Siracusa
|
||||||
|
'97', // Ragusa
|
||||||
|
'98' // Messina
|
||||||
|
];
|
||||||
|
|
||||||
|
export function generateRandomCAP(): string {
|
||||||
|
const provinceCode =
|
||||||
|
PROVINCE_CODES[Math.floor(Math.random() * PROVINCE_CODES.length)];
|
||||||
|
|
||||||
|
const lastThreeDigits = Math.floor(Math.random() * 1000)
|
||||||
|
.toString()
|
||||||
|
.padStart(3, '0');
|
||||||
|
|
||||||
|
return `${provinceCode}${lastThreeDigits}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateLetters(): string {
|
||||||
|
const consonants = 'BCDFGLMNPRSTVZ';
|
||||||
|
const vowels = 'AEIOU';
|
||||||
|
|
||||||
|
let result = '';
|
||||||
|
|
||||||
|
const consonantCount = 4 + Math.floor(Math.random() * 2);
|
||||||
|
for (let i = 0; i < consonantCount; i++) {
|
||||||
|
result += consonants[Math.floor(Math.random() * consonants.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
const extraVowels = 3 + Math.floor(Math.random() * 2);
|
||||||
|
for (let i = 0; i < extraVowels; i++) {
|
||||||
|
result += vowels[Math.floor(Math.random() * vowels.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
.split('')
|
||||||
|
.sort(() => Math.random() - 0.5)
|
||||||
|
.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateBirthYear(): string {
|
||||||
|
const currentYear = new Date().getFullYear();
|
||||||
|
const minYear = currentYear - 50;
|
||||||
|
const maxYear = currentYear - 20;
|
||||||
|
return Math.floor(Math.random() * (maxYear - minYear) + minYear).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPersonaSeed(
|
||||||
|
letters: string,
|
||||||
|
year: string,
|
||||||
|
postalCode: string
|
||||||
|
): string {
|
||||||
|
return `${letters}:${year}:${postalCode}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generatePersonaSeed(): string {
|
||||||
|
const letters = generateLetters();
|
||||||
|
const birthYear = generateBirthYear();
|
||||||
|
const postalCode = generateRandomCAP();
|
||||||
|
|
||||||
|
return formatPersonaSeed(letters, birthYear, postalCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generatePrompt(): string {
|
||||||
|
const seed = generatePersonaSeed();
|
||||||
|
const [letters, year, postalCode] = seed.split(':');
|
||||||
|
|
||||||
|
return `Using the Italian persona seed ${seed}, create a detailed persona of an Italian individual where:
|
||||||
|
- The letters "${letters}" MUST ALL be included in the person's full name (first name + last name), though the name can contain additional letters
|
||||||
|
- The person was born in ${year}
|
||||||
|
- They live in an area with postal code (CAP) ${postalCode}.`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user