import { Router } from "express"; import { getSession, addMessage } from "../services/sessionStore.js"; import { trackUsage, getUsageSummary, formatCost, getSessionUsage } from "../services/usageTracker.js"; import { getClaudeModel, buildLangChainMessages, aiMessageToClaudeResponse } from "../services/langchainModels.js"; const router = Router(); router.post("/*path", async (req, res) => { const requestStart = Date.now(); console.log(`[Claude API] ========== REQUEST START ==========`); const apiKey = process.env.ANTHROPIC_API_KEY; if (!apiKey) { console.error(`[Claude API] ERROR: API key not configured`); return res.status(500).json({ error: "API key not configured" }); } const { sessionId, model: modelId, system: systemPrompt, ...requestBody } = req.body; console.log(`[Claude API] Session ID: ${sessionId || 'none'}`); console.log(`[Claude API] Model: ${modelId}`); console.log(`[Claude API] Messages count: ${requestBody.messages?.length || 0}`); try { // Get LangChain model with tools bound const model = getClaudeModel(modelId); // Build messages with entity context and history const messages = buildLangChainMessages( sessionId, requestBody.messages, systemPrompt ); console.log(`[Claude API] Sending request via LangChain...`); const fetchStart = Date.now(); // Invoke model const response = await model.invoke(messages); const fetchDuration = Date.now() - fetchStart; console.log(`[Claude API] Response received in ${fetchDuration}ms`); // Convert to Claude API format for client compatibility const data = aiMessageToClaudeResponse(response, modelId); console.log(`[Claude API] Response converted. Stop reason: ${data.stop_reason}, content blocks: ${data.content?.length || 0}`); // Track and log token usage if (data.usage) { const userMessage = requestBody.messages?.[requestBody.messages.length - 1]; const inputText = typeof userMessage?.content === 'string' ? userMessage.content : null; const outputText = data.content ?.filter(c => c.type === 'text') .map(c => c.text) .join('\n') || null; const toolCalls = data.content ?.filter(c => c.type === 'tool_use') .map(c => ({ name: c.name, input: c.input })) || []; const usageRecord = trackUsage(sessionId, modelId, data.usage, { inputText, outputText, toolCalls }); console.log(`[Claude API] REQUEST USAGE: ${getUsageSummary(usageRecord)}`); if (sessionId) { const sessionStats = getSessionUsage(sessionId); if (sessionStats) { console.log(`[Claude API] SESSION TOTALS (${sessionStats.requestCount} requests):`); console.log(`[Claude API] Total input: ${sessionStats.totalInputTokens} tokens`); console.log(`[Claude API] Total output: ${sessionStats.totalOutputTokens} tokens`); console.log(`[Claude API] Total cost: ${formatCost(sessionStats.totalCost)}`); } } } // Store messages to session if (sessionId && data.content) { const session = getSession(sessionId); if (session) { const userMessage = requestBody.messages?.[requestBody.messages.length - 1]; if (userMessage && userMessage.role === 'user' && typeof userMessage.content === 'string') { addMessage(sessionId, { role: 'user', content: userMessage.content }); console.log(`[Claude API] Stored user message to session`); } const assistantContent = data.content .filter(c => c.type === 'text') .map(c => c.text) .join('\n'); if (assistantContent) { addMessage(sessionId, { role: 'assistant', content: assistantContent }); console.log(`[Claude API] Stored assistant response to session (${assistantContent.length} chars)`); } } } const totalDuration = Date.now() - requestStart; console.log(`[Claude API] ========== REQUEST COMPLETE (${totalDuration}ms) ==========`); res.json(data); } catch (error) { const totalDuration = Date.now() - requestStart; console.error(`[Claude API] ========== REQUEST FAILED (${totalDuration}ms) ==========`); console.error(`[Claude API] Error:`, error); console.error(`[Claude API] Error message:`, error.message); res.status(500).json({ error: "Failed to call Claude API", details: error.message }); } }); export default router;