261 lines
6.2 KiB
TypeScript
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);
|
|
}
|
|
}
|