feat: add purchases generation
This commit is contained in:
@@ -4,7 +4,6 @@
|
|||||||
"es2021": true
|
"es2021": true
|
||||||
},
|
},
|
||||||
"extends": [
|
"extends": [
|
||||||
"next/core-web-vitals",
|
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:@typescript-eslint/recommended",
|
"plugin:@typescript-eslint/recommended",
|
||||||
"prettier"
|
"prettier"
|
||||||
@@ -17,8 +16,7 @@
|
|||||||
"plugins": ["@typescript-eslint"],
|
"plugins": ["@typescript-eslint"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-unused-vars": "error",
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
"@typescript-eslint/consistent-type-definitions": "error",
|
"@typescript-eslint/consistent-type-definitions": "error"
|
||||||
"react-hooks/rules-of-hooks": "error",
|
},
|
||||||
"react-hooks/exhaustive-deps": "error"
|
"ignorePatterns": ["node_modules/", "dist/"]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -132,5 +132,6 @@ dist
|
|||||||
.editorconfig
|
.editorconfig
|
||||||
|
|
||||||
personas/
|
personas/
|
||||||
|
purchases/
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"start": "prisma generate && node dist/index.js",
|
"start": "prisma generate && node dist/index.js",
|
||||||
"dev": "nodemon src/index.ts",
|
"dev": "nodemon src/index.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"lint": "next lint",
|
"lint": "eslint . --fix",
|
||||||
"format": "prettier --config .prettierrc '**/*.{ts,json,md}' --write",
|
"format": "prettier --config .prettierrc '**/*.{ts,json,md}' --write",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
|
|||||||
20
src/index.ts
20
src/index.ts
@@ -1,17 +1,15 @@
|
|||||||
import { makeRequest } from './utils/anthropicClient';
|
import { generate as generatePersona } from './persona/store';
|
||||||
import { savePersonaJson } from './utils/savePersonaJson';
|
|
||||||
import { savePersonaDb } from './utils/savePersonaDb';
|
|
||||||
import { generatePromptWithMBTI } from './utils/personalityTrait';
|
|
||||||
|
|
||||||
const prompt =
|
import { generate as generatePurchases } from './purchase/store';
|
||||||
'Generate a detailed, realistic persona with specific real-world values including store names, brands and locations. Use frequencies per week, ISO timestamps relative to the current week. Personality trait: <MBTI_AND_TRAITS_HERE>';
|
|
||||||
|
|
||||||
const fullPrompt = generatePromptWithMBTI(prompt);
|
const personaPromise = generatePersona();
|
||||||
|
|
||||||
const personaPromise = makeRequest(fullPrompt);
|
personaPromise.then(id => {
|
||||||
|
console.log(`Persona generated! Now generating purchases for id ${id}`);
|
||||||
|
|
||||||
personaPromise.then(persona => {
|
const purchasesPromise = generatePurchases(id);
|
||||||
savePersonaDb(persona).then(id => savePersonaJson(persona, id));
|
|
||||||
|
|
||||||
console.log('New persona:', persona);
|
purchasesPromise.then(() => {
|
||||||
|
console.log('Purchases generated!');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
47
src/persona/store.ts
Normal file
47
src/persona/store.ts
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import { prisma } from '../utils/prismaClient';
|
||||||
|
import fs from 'fs';
|
||||||
|
import { Persona } from './types';
|
||||||
|
import { Tool } from './tool';
|
||||||
|
import { makeRequest } from '../utils/anthropicClient';
|
||||||
|
import { createFolderIfNotExists } from '../utils/createFolder';
|
||||||
|
|
||||||
|
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() {
|
||||||
|
const result = (await makeRequest(prompt, Tool as any)) as Persona;
|
||||||
|
|
||||||
|
const id = await saveToDb(result);
|
||||||
|
|
||||||
|
await saveToJson(result, id);
|
||||||
|
|
||||||
|
console.log('Persona:', result.core.name);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveToDb(persona: Persona) {
|
||||||
|
const result = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
name: persona.core.name,
|
||||||
|
age: persona.core.age,
|
||||||
|
persona: JSON.stringify(persona)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Persona ${result.name} inserted in DB with id ${result.id}`);
|
||||||
|
|
||||||
|
return result.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveToJson(persona: Persona, id: number) {
|
||||||
|
await createFolderIfNotExists('personas');
|
||||||
|
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
`personas/${id}.json`,
|
||||||
|
JSON.stringify(persona),
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Persona ${persona.core.name} saved as persona/${id}.json`);
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export const PersonaTool = {
|
export const Tool = {
|
||||||
name: 'PersonaSchema' as const,
|
name: 'PersonaSchema' as const,
|
||||||
input_schema: {
|
input_schema: {
|
||||||
type: 'object' as const,
|
type: 'object' as const,
|
||||||
@@ -1,55 +1,55 @@
|
|||||||
type Pet = {
|
interface Pet {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
}
|
||||||
|
|
||||||
type FrequencyObject = {
|
interface FrequencyObject {
|
||||||
name: string;
|
name: string;
|
||||||
frequency: number;
|
frequency: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
type SubscriptionBill = {
|
interface SubscriptionBill {
|
||||||
name: string;
|
name: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
date: string | Date;
|
date: string | Date;
|
||||||
};
|
}
|
||||||
|
|
||||||
type ActivityObject = {
|
interface ActivityObject {
|
||||||
name: string;
|
name: string;
|
||||||
frequency: number;
|
frequency: number;
|
||||||
schedule?: string[];
|
schedule?: string[];
|
||||||
};
|
}
|
||||||
|
|
||||||
type BrandLoyalty = {
|
interface BrandLoyalty {
|
||||||
name: string;
|
name: string;
|
||||||
loyaltyScore: number;
|
loyaltyScore: number;
|
||||||
};
|
}
|
||||||
|
|
||||||
type Event = {
|
interface Event {
|
||||||
name: string;
|
name: string;
|
||||||
date: string | Date;
|
date: string | Date;
|
||||||
details?: string;
|
details?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type TimelineActivity = {
|
interface TimelineActivity {
|
||||||
activity: string;
|
activity: string;
|
||||||
duration: string;
|
duration: string;
|
||||||
location?: string;
|
location?: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type RegularStop = {
|
interface RegularStop {
|
||||||
location: string;
|
location: string;
|
||||||
purpose: string;
|
purpose: string;
|
||||||
frequency: string;
|
frequency: string;
|
||||||
};
|
}
|
||||||
|
|
||||||
type SpendingCategories = {
|
interface SpendingCategories {
|
||||||
[category: string]: {
|
[category: string]: {
|
||||||
preference: number;
|
preference: number;
|
||||||
frequency: number;
|
frequency: number;
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
export type Persona = {
|
export interface Persona {
|
||||||
core: {
|
core: {
|
||||||
age: number;
|
age: number;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -115,9 +115,4 @@ export type Persona = {
|
|||||||
upcoming_events: Event[];
|
upcoming_events: Event[];
|
||||||
recent_changes: string[];
|
recent_changes: string[];
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
export interface MBTIType {
|
|
||||||
type: string;
|
|
||||||
traits: string[];
|
|
||||||
}
|
}
|
||||||
199
src/purchase/promptGenerator.ts
Normal file
199
src/purchase/promptGenerator.ts
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
import { Persona } from '../persona/types';
|
||||||
|
|
||||||
|
function formatFrequencyList(
|
||||||
|
items?: Array<{ name: string; frequency: number }>
|
||||||
|
): string {
|
||||||
|
if (!items?.length) return '(No data available)';
|
||||||
|
return items
|
||||||
|
.sort((a, b) => b.frequency - a.frequency)
|
||||||
|
.map(item => `- ${item.name} (${item.frequency}x per month)`)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatCategories(categories?: {
|
||||||
|
[key: string]: { preference: number; frequency: number };
|
||||||
|
}): string {
|
||||||
|
if (!categories || Object.keys(categories).length === 0)
|
||||||
|
return '(No categories defined)';
|
||||||
|
return Object.entries(categories)
|
||||||
|
.map(([category, data]) => {
|
||||||
|
const weeklyFrequency = Math.round(data.frequency * 4.33); // Monthly to weekly
|
||||||
|
return `- ${category}: ${weeklyFrequency}x per week (preference: ${data.preference}/10)`;
|
||||||
|
})
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatActivities(
|
||||||
|
activities?: Array<{ name: string; frequency: number; schedule?: string[] }>
|
||||||
|
): string {
|
||||||
|
if (!activities?.length) return '(No activities listed)';
|
||||||
|
return activities
|
||||||
|
.map(
|
||||||
|
act =>
|
||||||
|
`- ${act.name}: ${act.frequency}x per ${act.schedule ? act.schedule.join(', ') : 'week'}`
|
||||||
|
)
|
||||||
|
.join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatList(items?: string[]): string {
|
||||||
|
if (!items?.length) return '(None listed)';
|
||||||
|
return items.join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generatePurchasePrompt(persona: Persona): Promise<string> {
|
||||||
|
try {
|
||||||
|
const sections: string[] = [];
|
||||||
|
|
||||||
|
sections.push(`PERSONAL PROFILE:
|
||||||
|
Name: ${persona.core.name || 'Unknown'}
|
||||||
|
Age: ${persona.core.age || 'Unknown'}
|
||||||
|
Occupation: ${persona.core.occupation?.title || 'Unknown'}${
|
||||||
|
persona.core.occupation?.level
|
||||||
|
? ` (${persona.core.occupation.level})`
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
Income: ${persona.core.occupation?.income ? `$${persona.core.occupation.income.toLocaleString()}/year` : 'Unknown'}
|
||||||
|
Location: ${persona.core.home?.location || 'Unknown'}
|
||||||
|
Household: ${persona.core.household?.status || 'Unknown'}${
|
||||||
|
persona.core.household?.pets?.length
|
||||||
|
? `\nPets: ${persona.core.household.pets
|
||||||
|
.map(pet => `${pet.type || 'pet'} named ${pet.name}`)
|
||||||
|
.join(', ')}`
|
||||||
|
: ''
|
||||||
|
}`);
|
||||||
|
|
||||||
|
if (persona.core.occupation?.schedule?.length) {
|
||||||
|
sections.push(
|
||||||
|
`WORK SCHEDULE:\n${persona.core.occupation.schedule.join('\n')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.preferences?.shopping) {
|
||||||
|
sections.push(`REGULAR SHOPPING PATTERNS:
|
||||||
|
${persona.preferences.shopping.grocery_stores?.length ? `Grocery Stores:\n${formatFrequencyList(persona.preferences.shopping.grocery_stores)}` : ''}
|
||||||
|
${persona.preferences.shopping.coffee_shops?.length ? `\nCoffee Shops:\n${formatFrequencyList(persona.preferences.shopping.coffee_shops)}` : ''}
|
||||||
|
${persona.preferences.shopping.restaurants?.length ? `\nRestaurants:\n${formatFrequencyList(persona.preferences.shopping.restaurants)}` : ''}
|
||||||
|
${persona.preferences.shopping.retail?.length ? `\nRetail:\n${formatFrequencyList(persona.preferences.shopping.retail)}` : ''}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.finances?.spending_patterns?.categories) {
|
||||||
|
sections.push(
|
||||||
|
`SPENDING CATEGORIES & FREQUENCY:\n${formatCategories(persona.finances.spending_patterns.categories)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.preferences || persona.finances?.spending_patterns) {
|
||||||
|
sections.push(`PAYMENT PREFERENCES:
|
||||||
|
${persona.preferences?.payment_methods ? `- Methods: ${formatList(persona.preferences.payment_methods)}` : ''}
|
||||||
|
${persona.preferences?.price_sensitivity ? `- Price Sensitivity: ${persona.preferences.price_sensitivity}/10` : ''}
|
||||||
|
${persona.finances?.spending_patterns?.impulsive_score ? `- Impulsiveness Score: ${persona.finances.spending_patterns.impulsive_score}/10` : ''}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.routines?.commute?.regular_stops?.length) {
|
||||||
|
sections.push(`REGULAR ROUTINES:
|
||||||
|
Commute Stops:
|
||||||
|
${persona.routines.commute.regular_stops
|
||||||
|
.map(
|
||||||
|
stop => `- ${stop.frequency} visits to ${stop.location} for ${stop.purpose}`
|
||||||
|
)
|
||||||
|
.join('\n')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.preferences) {
|
||||||
|
const preferencesSection = [`PREFERENCES:`];
|
||||||
|
if (persona.preferences.diet?.length) {
|
||||||
|
preferencesSection.push(
|
||||||
|
`- Diet: ${formatList(persona.preferences.diet)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (persona.preferences.brands?.length) {
|
||||||
|
preferencesSection.push(
|
||||||
|
`- Favorite Brands: ${persona.preferences.brands
|
||||||
|
.map(b => `${b.name} (loyalty: ${b.loyaltyScore}/10)`)
|
||||||
|
.join(', ')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sections.push(preferencesSection.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.habits) {
|
||||||
|
const activitiesSection = [`REGULAR ACTIVITIES:`];
|
||||||
|
if (persona.habits.exercise?.length) {
|
||||||
|
activitiesSection.push(
|
||||||
|
`Exercise:\n${formatActivities(persona.habits.exercise)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (persona.habits.social?.length) {
|
||||||
|
activitiesSection.push(
|
||||||
|
`\nSocial:\n${formatActivities(persona.habits.social)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (persona.habits.entertainment?.length) {
|
||||||
|
activitiesSection.push(
|
||||||
|
`\nEntertainment:\n${formatActivities(persona.habits.entertainment)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sections.push(activitiesSection.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.context) {
|
||||||
|
const contextSection = [`CONTEXT:`];
|
||||||
|
if (persona.context.upcoming_events?.length) {
|
||||||
|
contextSection.push(
|
||||||
|
`Upcoming Events:\n${persona.context.upcoming_events
|
||||||
|
.map(
|
||||||
|
event =>
|
||||||
|
`- ${event.name} on ${event.date}${event.details ? `: ${event.details}` : ''}`
|
||||||
|
)
|
||||||
|
.join('\n')}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (persona.context.stress_triggers?.length) {
|
||||||
|
contextSection.push(
|
||||||
|
`\nStress Triggers: ${formatList(persona.context.stress_triggers)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (persona.context.reward_behaviors?.length) {
|
||||||
|
contextSection.push(
|
||||||
|
`\nReward Behaviors: ${formatList(persona.context.reward_behaviors)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
sections.push(contextSection.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (persona.finances?.subscriptions?.length) {
|
||||||
|
sections.push(`EXISTING SUBSCRIPTIONS (exclude from weekly purchases):
|
||||||
|
${persona.finances.subscriptions.map(sub => `- ${sub.name}: $${sub.amount} (due: ${sub.date})`).join('\n')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
sections.push(`Please generate a detailed list of purchases for one week, including:
|
||||||
|
1. Date and time of purchase
|
||||||
|
2. Store/vendor name
|
||||||
|
3. Items purchased
|
||||||
|
4. Amount spent
|
||||||
|
5. Category of spending
|
||||||
|
6. Whether it was planned or impulse purchase
|
||||||
|
|
||||||
|
Consider:
|
||||||
|
- Regular commute stops and routines
|
||||||
|
- Exercise and social activities
|
||||||
|
- Dietary preferences and restrictions
|
||||||
|
- Brand loyalties and preferred stores
|
||||||
|
- Work schedule and regular activities
|
||||||
|
- Price sensitivity and impulsiveness score
|
||||||
|
- Upcoming events and potential related purchases
|
||||||
|
${persona.context?.recent_changes?.length ? `- Recent lifestyle changes: ${formatList(persona.context.recent_changes)}` : ''}
|
||||||
|
|
||||||
|
Format each purchase as:
|
||||||
|
[DATE] [TIME] | [STORE] | [ITEMS] | $[AMOUNT] | [CATEGORY] | [PLANNED/IMPULSE]
|
||||||
|
|
||||||
|
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');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error generating prompt:', error);
|
||||||
|
throw new Error('Failed to generate purchase prompt');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default generatePurchasePrompt;
|
||||||
57
src/purchase/store.ts
Normal file
57
src/purchase/store.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { prisma } from '../utils/prismaClient';
|
||||||
|
import fs, { readFileSync } from 'fs';
|
||||||
|
import { PurchaseList } from './types';
|
||||||
|
import { Tool } from './tool';
|
||||||
|
import { makeRequest } from '../utils/anthropicClient';
|
||||||
|
import { createFolderIfNotExists } from '../utils/createFolder';
|
||||||
|
import generatePurchasePrompt from './promptGenerator';
|
||||||
|
|
||||||
|
export async function generate(personaId: number) {
|
||||||
|
const jsonFile = readFileSync(`personas/${personaId}.json`, 'utf-8');
|
||||||
|
|
||||||
|
const persona = JSON.parse(jsonFile);
|
||||||
|
|
||||||
|
const personaPrompt = await generatePurchasePrompt(persona);
|
||||||
|
|
||||||
|
const result = (await makeRequest(
|
||||||
|
personaPrompt,
|
||||||
|
Tool as any
|
||||||
|
)) as PurchaseList;
|
||||||
|
|
||||||
|
await saveToDb(personaId, result);
|
||||||
|
|
||||||
|
await saveToJson(result, personaId);
|
||||||
|
|
||||||
|
console.log('Purchases:', result.items.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveToDb(personaId: number, purchases: PurchaseList) {
|
||||||
|
const result = await prisma.item.createMany({
|
||||||
|
data: purchases.items.map(purchase => ({
|
||||||
|
userId: personaId,
|
||||||
|
name: purchase.name,
|
||||||
|
amount: purchase.amount,
|
||||||
|
datetime: purchase.datetime,
|
||||||
|
location: purchase.location,
|
||||||
|
notes: purchase.notes
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`Inserted ${result.count} purchases with persona ${personaId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function saveToJson(purchaseList: PurchaseList, id: number) {
|
||||||
|
await createFolderIfNotExists('purchases');
|
||||||
|
|
||||||
|
await createFolderIfNotExists(`purchases/${id}`);
|
||||||
|
|
||||||
|
await fs.promises.writeFile(
|
||||||
|
`purchases/${id}/${id}.json`,
|
||||||
|
JSON.stringify(purchaseList),
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Saved ${purchaseList.items.length} purchases as purchases/${id}/${id}.json`
|
||||||
|
);
|
||||||
|
}
|
||||||
39
src/purchase/tool.ts
Normal file
39
src/purchase/tool.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
export const Tool = {
|
||||||
|
name: 'PurchaseSchema' as const,
|
||||||
|
input_schema: {
|
||||||
|
type: 'object' as const,
|
||||||
|
properties: {
|
||||||
|
items: {
|
||||||
|
type: 'array' as const,
|
||||||
|
items: {
|
||||||
|
type: 'object' as const,
|
||||||
|
properties: {
|
||||||
|
name: {
|
||||||
|
type: 'string' as const,
|
||||||
|
description: 'Name of the purchased item'
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
type: 'number' as const,
|
||||||
|
description: 'Purchase amount in USD'
|
||||||
|
},
|
||||||
|
datetime: {
|
||||||
|
type: 'string' as const,
|
||||||
|
description: 'Purchase date and time in ISO 8601 format',
|
||||||
|
format: 'date-time'
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
type: 'string' as const,
|
||||||
|
description: 'Purchase location'
|
||||||
|
},
|
||||||
|
notes: {
|
||||||
|
type: 'string' as const,
|
||||||
|
description: 'Additional purchase details (optional)'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['name', 'amount', 'datetime', 'location'] as const
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: ['items'] as const
|
||||||
|
}
|
||||||
|
} as const;
|
||||||
11
src/purchase/types.ts
Normal file
11
src/purchase/types.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
export interface Purchase {
|
||||||
|
name: string;
|
||||||
|
amount: number;
|
||||||
|
datetime: string;
|
||||||
|
location: string;
|
||||||
|
notes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PurchaseList {
|
||||||
|
items: Purchase[];
|
||||||
|
}
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
import 'dotenv/config';
|
import 'dotenv/config';
|
||||||
import Anthropic from '@anthropic-ai/sdk';
|
import Anthropic from '@anthropic-ai/sdk';
|
||||||
import { Persona } from './types';
|
|
||||||
import { PersonaTool } from './personaSchema';
|
|
||||||
|
|
||||||
export async function makeRequest(prompt: string) {
|
export async function makeRequest(prompt: string, tool: any) {
|
||||||
if (!process.env.ANTHROPIC_API_KEY) {
|
if (!process.env.ANTHROPIC_API_KEY) {
|
||||||
throw Error('Anthropic API key missing.');
|
throw Error('Anthropic API key missing.');
|
||||||
}
|
}
|
||||||
@@ -16,7 +14,7 @@ export async function makeRequest(prompt: string) {
|
|||||||
model: 'claude-3-5-sonnet-20241022',
|
model: 'claude-3-5-sonnet-20241022',
|
||||||
max_tokens: 2000,
|
max_tokens: 2000,
|
||||||
temperature: 1,
|
temperature: 1,
|
||||||
tools: [PersonaTool],
|
tools: [tool],
|
||||||
messages: [{ role: 'user', content: prompt }]
|
messages: [{ role: 'user', content: prompt }]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -33,5 +31,5 @@ export async function makeRequest(prompt: string) {
|
|||||||
{ type: string; input: object }
|
{ type: string; input: object }
|
||||||
];
|
];
|
||||||
|
|
||||||
return content[1].input as Persona;
|
return content[1].input;
|
||||||
}
|
}
|
||||||
|
|||||||
18
src/utils/createFolder.ts
Normal file
18
src/utils/createFolder.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
|
export async function createFolderIfNotExists(
|
||||||
|
folderPath: string
|
||||||
|
): Promise<void> {
|
||||||
|
try {
|
||||||
|
await fs.access(folderPath);
|
||||||
|
console.log('Folder already exists');
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
await fs.mkdir(folderPath, { recursive: true });
|
||||||
|
console.log('Folder created successfully');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error creating folder:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
import { MBTIType } from './types';
|
|
||||||
|
|
||||||
const mbtiTypes: MBTIType[] = [
|
|
||||||
{
|
|
||||||
type: 'INTJ',
|
|
||||||
traits: ['analytical', 'planning-focused', 'independent', 'private']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'ENTJ',
|
|
||||||
traits: ['strategic', 'leadership-oriented', 'decisive', 'organized']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'ISFP',
|
|
||||||
traits: ['artistic', 'spontaneous', 'nature-loving', 'adaptable']
|
|
||||||
},
|
|
||||||
{ type: 'ESFJ', traits: ['caring', 'social', 'traditional', 'organized'] },
|
|
||||||
{ type: 'INTP', traits: ['logical', 'abstract', 'adaptable', 'private'] },
|
|
||||||
{
|
|
||||||
type: 'ENFP',
|
|
||||||
traits: ['enthusiastic', 'creative', 'spontaneous', 'people-oriented']
|
|
||||||
},
|
|
||||||
{ type: 'ISTJ', traits: ['practical', 'factual', 'organized', 'reliable'] },
|
|
||||||
{
|
|
||||||
type: 'ENFJ',
|
|
||||||
traits: ['charismatic', 'inspiring', 'idealistic', 'people-focused']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'ISTP',
|
|
||||||
traits: ['practical', 'adaptable', 'experiential', 'logical']
|
|
||||||
},
|
|
||||||
{ type: 'ESFP', traits: ['spontaneous', 'energetic', 'social', 'practical'] },
|
|
||||||
{
|
|
||||||
type: 'INFJ',
|
|
||||||
traits: ['idealistic', 'organized', 'insightful', 'private']
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'ESTP',
|
|
||||||
traits: ['energetic', 'practical', 'spontaneous', 'experiential']
|
|
||||||
},
|
|
||||||
{ type: 'INFP', traits: ['idealistic', 'creative', 'authentic', 'adaptive'] },
|
|
||||||
{
|
|
||||||
type: 'ENTP',
|
|
||||||
traits: ['innovative', 'adaptable', 'analytical', 'outgoing']
|
|
||||||
},
|
|
||||||
{ type: 'ISFJ', traits: ['practical', 'caring', 'organized', 'traditional'] },
|
|
||||||
{
|
|
||||||
type: 'ESTJ',
|
|
||||||
traits: ['practical', 'organized', 'leadership-oriented', 'traditional']
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export function generatePromptWithMBTI(prompt: string): string {
|
|
||||||
const selectedType = mbtiTypes[Math.floor(Math.random() * mbtiTypes.length)];
|
|
||||||
|
|
||||||
const mbtiJson = JSON.stringify({
|
|
||||||
type: selectedType.type,
|
|
||||||
traits: selectedType.traits
|
|
||||||
});
|
|
||||||
|
|
||||||
return prompt.replace('<MBTI_AND_TRAITS_HERE>', mbtiJson);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { prisma } from './prismaClient';
|
|
||||||
import { Persona } from './types';
|
|
||||||
|
|
||||||
export async function savePersonaDb(persona: Persona) {
|
|
||||||
const result = await prisma.user.create({
|
|
||||||
data: {
|
|
||||||
name: persona.core.name,
|
|
||||||
age: persona.core.age,
|
|
||||||
persona: JSON.stringify(persona)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Persona ${result.name} inserted with ID ${result.id}`);
|
|
||||||
|
|
||||||
return result.id;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import { Persona } from './types';
|
|
||||||
|
|
||||||
export function savePersonaJson(persona: Persona, id: number) {
|
|
||||||
fs.promises.writeFile(`personas/${id}.json`, JSON.stringify(persona), 'utf8');
|
|
||||||
|
|
||||||
console.log(`Persona ${persona.core.name} saved as persona/${id}.json`);
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user