or gpt-4o, gpt-3.5-turbo
### Part 1: Creating the OpenAI Service
Create `apps/backend/src/services/openaiService.ts`:
import OpenAI from 'openai';
import { MessageDTO } from '@ayw/shared';
export class OpenAIService {
private client: OpenAI;
private model: string;
constructor() {
this.client = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
this.model = process.env.OPENAI_MODEL || 'gpt-4o-mini';
}
async generateResponse(options: {
botId: string;
botName: string;
systemPrompt: string;
conversationHistory: MessageDTO[];
userMessage: string;
temperature?: number;
}): Promise<{
response: string;
tokensUsed: number;
confidence: number;
}> {
try {
// Build messages array from conversation history
const messages: OpenAI.Chat.ChatCompletionMessageParam[] = [
{ role: 'system', content: options.systemPrompt },
...this.convertHistoryToMessages(options.conversationHistory),
{ role: 'user', content: options.userMessage }
];
const completion = await this.client.chat.completions.create({
model: this.model,
messages,
temperature: options.temperature || 0.7,
max_tokens: 500,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0
});
const response = completion.choices[0]?.message?.content || 'I apologize, I couldn\'t generate a response.';
const tokensUsed = completion.usage?.total_tokens || 0;
// Calculate a simple confidence score
const confidence = completion.choices[0]?.finish_reason === 'stop'
? 0.9
: 0.5;
return {
response,
tokensUsed,
confidence
};
} catch (error: any) {
console.error('OpenAI API error:', error);
// Handle rate limits
if (error?.status === 429) {
throw new Error('RATE_LIMITED: OpenAI API rate limit exceeded. Please try again later.');
}
// Handle authentication errors
if (error?.status === 401) {
throw new Error('AUTH_ERROR: Invalid OpenAI API key.');
}
throw new Error(`OpenAI API error: ${error.message}`);
}
}
private convertHistoryToMessages(history: MessageDTO[]): OpenAI.Chat.ChatCompletionMessageParam[] {
return history.map(msg => ({
role: msg.sender === 'user' ? 'user' as const : 'assistant' as const,
content: msg.content
}));
}
async generateQuickReplies(options: {
botId: string;
context: string;
count?: number;
}): Promise<string[]> {
try {
const completion = await this.client.chat.completions.create({
model: this.model,
messages: [
{
role: 'system',
content: You are a helpful assistant that generates concise quick reply options for a chatbot. Generate ${options.count || 3} short, actionable quick reply labels based on the context provided.
},
{
role: 'user',
content: Context: ${options.context}\n\nGenerate quick reply options as a JSON array of strings.
}
],
temperature: 0.7,
max_tokens: 150,
response_format: { type: 'json_object' }
});
const content = completion.choices[0]?.message?.content || '[]';
const parsed = JSON.parse(content);
return Array.isArray(parsed) ? parsed : parsed.replies || [];
} catch (error) {
console.error('Error generating quick replies:', error);
return ['Tell me more', 'Get started', 'Contact us']; // Fallback
}
}
}
### Part 2: Integrating with AYW Bots
#### Update ChatbotService
Edit `apps/backend/src/services/chatbotService.ts`:
import { OpenAIService } from './openaiService';
export class ChatbotService {
private openai: OpenAIService;
constructor() {
this.openai = new OpenAIService();
}
async processMessage(
conversationId: string,
userId: string | null,
message: string,
context: any
): Promise<ChatbotProcessResponse> {
const { botId, conversationHistory, userPreferences } = context;
// Get bot config for system prompt
const botConfig = await prisma.botConfig.findUnique({
where: { botId }
});
if (!botConfig || !botConfig.isActive) {
return this.getFallbackResponse();
}
// Use OpenAI for supported bots
const aiPoweredBots = ['support', 'sales', 'knowledge'];
if (aiPoweredBots.includes(botId)) {
return this.processWithAI(botId, botConfig, conversationHistory, message);
}
// Keep Welcome Bot logic rule-based
if (botId === 'welcome') {
return this.processWelcomeBot(message);
}
// Default fallback
return this.getPlaceholderResponse(botId);
}
private async processWithAI(
botId: string,
botConfig: any,
conversationHistory: MessageDTO[],
userMessage: string
): Promise<ChatbotProcessResponse> {
try {
// Define system prompts per bot
const systemPrompts: Record<string, string> = {
support: You are a customer support bot for As You Wish (AYW). Be helpful, empathetic, and concise. If you can't solve an issue, offer to escalate to a human. Available capabilities: ${botConfig.capabilities.join(', ')},
sales: `You are a sales bot for As You Wish (AYW) chatbot platform.
Help potential customers understand our offerings.
Be persuasive but not pushy. Highlight key benefits.
Available capabilities: ${botConfig.capabilities.join(', ')}`,
knowledge: `You are a knowledge base bot for AYW.
Provide accurate information from our documentation.
If you don't know something, say so and suggest contacting support.
Available capabilities: ${botConfig.capabilities.join(', ')}`
};
const result = await this.openai.generateResponse({
botId,
botName: botConfig.name,
systemPrompt: systemPrompts[botId] || 'You are a helpful assistant.',
conversationHistory,
userMessage
});
// Generate dynamic quick replies
const quickReplies = await this.openai.generateQuickReplies({
botId,
context: userMessage,
count: 3
});
return {
botId,
response: result.response,
suggestedActions: quickReplies.map(label
## Pitfall Guide
1. **Unbounded Context Window Overflow**: Failing to truncate or summarize `conversationHistory` before sending to the API causes token limit errors and degrades response quality. Always enforce a maximum message count or implement sliding-window summarization.
2. **Naive JSON Parsing for Quick Replies**: LLMs do not guarantee strict JSON output. Direct `JSON.parse()` calls will throw on malformed responses. Always wrap parsing in try/catch blocks and provide deterministic fallback arrays.
3. **Ignoring API Rate Limits & Retry Logic**: Production traffic inevitably triggers `429` errors. Without exponential backoff, request queuing, or circuit breaker patterns, your service will cascade fail under load.
4. **Hardcoded System Prompts Without Validation**: Injecting dynamic capabilities or user data directly into prompts without sanitization risks prompt leakage, injection attacks, or inconsistent persona behavior. Use template interpolation with strict allowlists.
5. **Untracked Token Consumption**: Not logging `completion.usage.total_tokens` per request leads to unpredictable billing and budget overruns. Implement centralized token accounting and alerting thresholds.
6. **Blending Rule-Based & AI Logic Without Clear Routing**: Mixing deterministic fallbacks, welcome bots, and LLM calls in a single unguarded flow causes unpredictable state transitions. Explicitly separate routing logic and define clear handoff boundaries.
## Deliverables
- **π AYW OpenAI Integration Blueprint**: Architecture diagram detailing intent routing, context window management, token accounting, and fallback pathways. Includes sequence diagrams for `processMessage` β `generateResponse` β `generateQuickReplies`.
- **β
Pre-Deployment Validation Checklist**: Step-by-step verification for API key rotation, environment variable injection, Prisma schema alignment, rate limit simulation, and fallback behavior testing.
- **βοΈ Configuration Templates**: Production-ready `.env` examples, `botConfig` JSON schema with capability allowlists, system prompt registry structure, and token budget alerting configurations.