Tech Stack
- Next.js + React for the chat UI and real-time updates
- Nest.js for the backend and orchestration
- MongoDB for conversation history and user context
- Redis for caching conversation state and recent context windows
- Supabase for real-time channels and auth where needed
- TypeScript end-to-end for type-safe message and context shapes
- LLM / NLU layer for intent and entity extraction and response generation
The Challenge
The Solution
1. Typed conversation context
Tsx
// types/conversation.ts
export interface ConversationContext {
sessionId: string;
locale: string;
intent: 'browse' | 'support' | 'recommendation' | 'unknown';
entities: {
category?: string;
priceMax?: number;
attributes?: Record<string, string>;
};
lastProductIds: string[];
turnCount: number;
updatedAt: string;
}
2. Context cache and short history
Tsx
// Simplified: load or build context for this turn
async function getContextForTurn(sessionId: string, newMessage: string): Promise<ConversationContext> {
const cached = await redis.get<ConversationContext>(`ctx:${sessionId}`);
const base = cached ?? { sessionId, locale: 'en', intent: 'unknown', entities: {}, lastProductIds: [], turnCount: 0, updatedAt: new Date().toISOString() };
const updated = await extractAndMergeEntities(base, newMessage);
await redis.setex(`ctx:${sessionId}`, 3600, JSON.stringify(updated)); // 1h TTL
return updated;
}