import express, { Request, Response } from "express"; import { storage } from "../storage"; import { classifyInternalChat } from "../ai/internal-chat-graph"; import { runInternalChatWorkflow } from "../ai/internal-chat-workflow"; const router = express.Router(); // GET /api/ai/settings router.get("/settings", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const settings = await storage.getAiSettings(userId); if (!settings) return res.status(200).json(null); return res.status(200).json({ id: settings.id, apiKey: settings.apiKey, openPhoneReply: settings.openPhoneReply ?? false, }); } catch (err) { return res.status(500).json({ error: "Failed to fetch AI settings", details: String(err) }); } }); // PUT /api/ai/settings router.put("/settings", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const { apiKey } = req.body; if (!apiKey?.trim()) { return res.status(400).json({ message: "apiKey is required" }); } const settings = await storage.upsertAiSettings(userId, apiKey.trim()); return res.status(200).json({ id: settings.id, apiKey: settings.apiKey }); } catch (err) { return res.status(500).json({ error: "Failed to save AI settings", details: String(err) }); } }); // GET /api/ai/advanced-settings router.get("/advanced-settings", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const openPhoneReply = await storage.getOpenPhoneReply(userId); return res.status(200).json({ openPhoneReply }); } catch (err) { return res.status(500).json({ error: "Failed to fetch advanced settings", details: String(err) }); } }); // PUT /api/ai/advanced-settings router.put("/advanced-settings", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const { openPhoneReply } = req.body; if (typeof openPhoneReply !== "boolean") { return res.status(400).json({ message: "openPhoneReply must be a boolean" }); } await storage.setOpenPhoneReply(userId, openPhoneReply); return res.status(200).json({ openPhoneReply }); } catch (err) { return res.status(500).json({ error: "Failed to save advanced settings", details: String(err) }); } }); // GET /api/ai/chat-templates router.get("/chat-templates", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const templates = await storage.getAiChatTemplates(userId); return res.status(200).json(templates); } catch (err) { return res.status(500).json({ error: "Failed to fetch AI chat templates", details: String(err) }); } }); // PUT /api/ai/chat-templates router.put("/chat-templates", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const { reminderGreeting, newPatientGreeting, generalFallback, rescheduleGreeting, reminderSms } = req.body; await storage.saveAiChatTemplates(userId, { reminderGreeting, newPatientGreeting, generalFallback, rescheduleGreeting, reminderSms }); const updated = await storage.getAiChatTemplates(userId); return res.status(200).json(updated); } catch (err) { return res.status(500).json({ error: "Failed to save AI chat templates", details: String(err) }); } }); // GET /api/ai/internal-chat-settings router.get("/internal-chat-settings", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const systemPrompt = await storage.getInternalChatSystemPrompt(userId); return res.status(200).json({ systemPrompt }); } catch (err) { return res.status(500).json({ error: "Failed to fetch internal chat settings", details: String(err) }); } }); // PUT /api/ai/internal-chat-settings router.put("/internal-chat-settings", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const { systemPrompt } = req.body; if (typeof systemPrompt !== "string") return res.status(400).json({ message: "systemPrompt must be a string" }); await storage.saveInternalChatSystemPrompt(userId, systemPrompt.trim()); return res.status(200).json({ systemPrompt: systemPrompt.trim() }); } catch (err) { return res.status(500).json({ error: "Failed to save internal chat settings", details: String(err) }); } }); // GET /api/ai/cdt-aliases router.get("/cdt-aliases", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const aliases = await storage.getCdtAliases(userId); return res.status(200).json(aliases); } catch (err) { return res.status(500).json({ error: "Failed to fetch CDT aliases", details: String(err) }); } }); // PUT /api/ai/cdt-aliases router.put("/cdt-aliases", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const aliases = req.body; if (!Array.isArray(aliases)) { return res.status(400).json({ message: "Body must be an array of { phrase, cdtCode }" }); } const cleaned = aliases .filter((a: any) => typeof a?.phrase === "string" && typeof a?.cdtCode === "string") .map((a: any) => ({ phrase: a.phrase.trim().toLowerCase(), cdtCode: a.cdtCode.trim().toUpperCase(), })); await storage.saveCdtAliases(userId, cleaned); return res.status(200).json(cleaned); } catch (err) { return res.status(500).json({ error: "Failed to save CDT aliases", details: String(err) }); } }); // POST /api/ai/internal-chat router.post("/internal-chat", async (req: Request, res: Response): Promise => { try { const userId = req.user?.id; if (!userId) return res.status(401).json({ message: "Unauthorized" }); const { message } = req.body; if (!message?.trim()) return res.status(400).json({ message: "message is required" }); const aiSettings = await storage.getAiSettings(userId); if (!aiSettings?.apiKey) { return res.status(200).json({ reply: "AI is not configured. Please add a Google AI API key in Settings.", }); } const [extraSystemPrompt, customAliases] = await Promise.all([ storage.getInternalChatSystemPrompt(userId), storage.getCdtAliases(userId), ]); const classification = await classifyInternalChat( message.trim(), aiSettings.apiKey, extraSystemPrompt || undefined ); const response = await runInternalChatWorkflow(classification, userId, storage, customAliases); return res.status(200).json(response); } catch (err) { return res.status(500).json({ error: "Internal chat error", details: String(err) }); } }); export default router;