trading-platform-backend/src/modules/llm/controllers/llm.controller.ts

261 lines
6.2 KiB
TypeScript

/**
* 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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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<void> {
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);
}
}