/** * LLM Controller * Handles AI chat assistant endpoints */ import { Request, Response, NextFunction } from 'express'; import { llmService } from '../services/llm.service'; // ============================================================================ // Types // ============================================================================ // Use Request directly - user is already declared globally in auth.middleware.ts type AuthRequest = Request; // ============================================================================ // Session Management // ============================================================================ /** * Create a new chat session */ export async function createSession(req: AuthRequest, res: Response, next: NextFunction): Promise { try { const userId = req.user?.id; if (!userId) { res.status(401).json({ success: false, error: { message: 'Unauthorized', code: 'UNAUTHORIZED' }, }); return; } const { watchlist, preferences, portfolioSummary } = req.body; const session = await llmService.createSession(userId, { watchlist, preferences, portfolioSummary, }); res.status(201).json({ success: true, data: { sessionId: session.id, createdAt: session.createdAt, }, }); } catch (error) { next(error); } } /** * Get user's chat sessions */ export async function getSessions(req: AuthRequest, res: Response, next: NextFunction): Promise { try { const userId = req.user?.id; if (!userId) { res.status(401).json({ success: false, error: { message: 'Unauthorized', code: 'UNAUTHORIZED' }, }); return; } const sessions = await llmService.getUserSessions(userId); res.json({ success: true, data: sessions.map((s) => ({ id: s.id, messagesCount: s.messages.length, lastMessage: s.messages[s.messages.length - 1]?.content.substring(0, 100), createdAt: s.createdAt, updatedAt: s.updatedAt, })), }); } catch (error) { next(error); } } /** * Get a specific session with messages */ export async function getSession(req: AuthRequest, res: Response, next: NextFunction): Promise { try { const userId = req.user?.id; if (!userId) { res.status(401).json({ success: false, error: { message: 'Unauthorized', code: 'UNAUTHORIZED' }, }); return; } const { sessionId } = req.params; const session = await llmService.getSession(sessionId); if (!session) { res.status(404).json({ success: false, error: { message: 'Session not found', code: 'NOT_FOUND' }, }); return; } if (session.userId !== userId) { res.status(403).json({ success: false, error: { message: 'Forbidden', code: 'FORBIDDEN' }, }); return; } res.json({ success: true, data: session, }); } catch (error) { next(error); } } /** * Delete a session */ export async function deleteSession(req: AuthRequest, res: Response, next: NextFunction): Promise { try { const userId = req.user?.id; if (!userId) { res.status(401).json({ success: false, error: { message: 'Unauthorized', code: 'UNAUTHORIZED' }, }); return; } const { sessionId } = req.params; const session = await llmService.getSession(sessionId); if (!session) { res.status(404).json({ success: false, error: { message: 'Session not found', code: 'NOT_FOUND' }, }); return; } if (session.userId !== userId) { res.status(403).json({ success: false, error: { message: 'Forbidden', code: 'FORBIDDEN' }, }); return; } await llmService.deleteSession(sessionId); res.json({ success: true, message: 'Session deleted', }); } catch (error) { next(error); } } // ============================================================================ // Chat // ============================================================================ /** * Send a message and get a response */ export async function chat(req: AuthRequest, res: Response, next: NextFunction): Promise { try { const userId = req.user?.id; if (!userId) { res.status(401).json({ success: false, error: { message: 'Unauthorized', code: 'UNAUTHORIZED' }, }); return; } const { sessionId } = req.params; const { message } = req.body; if (!message || typeof message !== 'string' || message.trim().length === 0) { res.status(400).json({ success: false, error: { message: 'Message is required', code: 'VALIDATION_ERROR' }, }); return; } const session = await llmService.getSession(sessionId); if (!session) { res.status(404).json({ success: false, error: { message: 'Session not found', code: 'NOT_FOUND' }, }); return; } if (session.userId !== userId) { res.status(403).json({ success: false, error: { message: 'Forbidden', code: 'FORBIDDEN' }, }); return; } const response = await llmService.chat(sessionId, message.trim()); res.json({ success: true, data: response, }); } catch (error) { next(error); } } // ============================================================================ // Quick Actions // ============================================================================ /** * Get quick analysis for a symbol (no session required) */ export async function getQuickAnalysis(req: Request, res: Response, next: NextFunction): Promise { try { const { symbol } = req.params; if (!symbol) { res.status(400).json({ success: false, error: { message: 'Symbol is required', code: 'VALIDATION_ERROR' }, }); return; } const analysis = await llmService.getQuickAnalysis(symbol.toUpperCase()); res.json({ success: true, data: { symbol: symbol.toUpperCase(), analysis, timestamp: new Date().toISOString(), }, }); } catch (error) { next(error); } }