Initial API scaffold: Elysia + Bun + Drizzle + BetterAuth + LangChain
This commit is contained in:
109
src/services/ai.ts
Normal file
109
src/services/ai.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { ChatAnthropic } from '@langchain/anthropic';
|
||||
import { ChatPromptTemplate } from '@langchain/core/prompts';
|
||||
import { StringOutputParser } from '@langchain/core/output_parsers';
|
||||
|
||||
export type AIProvider = 'anthropic' | 'openai';
|
||||
|
||||
// Get model based on provider
|
||||
function getModel(provider: AIProvider = 'anthropic') {
|
||||
if (provider === 'anthropic') {
|
||||
return new ChatAnthropic({
|
||||
modelName: 'claude-sonnet-4-20250514',
|
||||
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
||||
});
|
||||
}
|
||||
|
||||
// Add OpenAI support later
|
||||
throw new Error(`Provider ${provider} not yet supported`);
|
||||
}
|
||||
|
||||
// Email generation prompt
|
||||
const emailPrompt = ChatPromptTemplate.fromMessages([
|
||||
['system', `You are a professional wealth advisor writing to a valued client.
|
||||
Maintain a warm but professional tone. Incorporate personal details naturally.
|
||||
Keep emails concise (3-4 paragraphs max).
|
||||
Do not include subject line - just the body.`],
|
||||
['human', `Advisor: {advisorName}
|
||||
Client: {clientName}
|
||||
Their interests: {interests}
|
||||
Recent notes: {notes}
|
||||
Purpose: {purpose}
|
||||
|
||||
Generate a personalized email that feels genuine, not templated.`],
|
||||
]);
|
||||
|
||||
export interface GenerateEmailParams {
|
||||
advisorName: string;
|
||||
clientName: string;
|
||||
interests: string[];
|
||||
notes: string;
|
||||
purpose: string;
|
||||
provider?: AIProvider;
|
||||
}
|
||||
|
||||
export async function generateEmail(params: GenerateEmailParams): Promise<string> {
|
||||
const model = getModel(params.provider);
|
||||
const parser = new StringOutputParser();
|
||||
const chain = emailPrompt.pipe(model).pipe(parser);
|
||||
|
||||
const response = await chain.invoke({
|
||||
advisorName: params.advisorName,
|
||||
clientName: params.clientName,
|
||||
interests: params.interests.join(', ') || 'not specified',
|
||||
notes: params.notes || 'No recent notes',
|
||||
purpose: params.purpose,
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Birthday message generation
|
||||
const birthdayPrompt = ChatPromptTemplate.fromMessages([
|
||||
['system', `Generate a thoughtful birthday message from a wealth advisor to their client.
|
||||
Should feel personal, not generic. Keep it brief (2-3 sentences) and sincere.`],
|
||||
['human', `Client: {clientName}
|
||||
Years as client: {yearsAsClient}
|
||||
Interests: {interests}
|
||||
|
||||
Generate a warm birthday message.`],
|
||||
]);
|
||||
|
||||
export interface GenerateBirthdayMessageParams {
|
||||
clientName: string;
|
||||
yearsAsClient: number;
|
||||
interests: string[];
|
||||
provider?: AIProvider;
|
||||
}
|
||||
|
||||
export async function generateBirthdayMessage(params: GenerateBirthdayMessageParams): Promise<string> {
|
||||
const model = getModel(params.provider);
|
||||
const parser = new StringOutputParser();
|
||||
const chain = birthdayPrompt.pipe(model).pipe(parser);
|
||||
|
||||
const response = await chain.invoke({
|
||||
clientName: params.clientName,
|
||||
yearsAsClient: params.yearsAsClient.toString(),
|
||||
interests: params.interests.join(', ') || 'not specified',
|
||||
});
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
// Email subject generation
|
||||
const subjectPrompt = ChatPromptTemplate.fromMessages([
|
||||
['system', `Generate a professional but warm email subject line for a wealth advisor's email.
|
||||
Keep it short (under 50 characters). Do not use quotes.`],
|
||||
['human', `Purpose: {purpose}
|
||||
Client name: {clientName}
|
||||
|
||||
Generate just the subject line, nothing else.`],
|
||||
]);
|
||||
|
||||
export async function generateSubject(purpose: string, clientName: string, provider?: AIProvider): Promise<string> {
|
||||
const model = getModel(provider);
|
||||
const parser = new StringOutputParser();
|
||||
const chain = subjectPrompt.pipe(model).pipe(parser);
|
||||
|
||||
const response = await chain.invoke({ purpose, clientName });
|
||||
return response.trim();
|
||||
}
|
||||
Reference in New Issue
Block a user