import express from "express"; import dotenv from "dotenv"; import cors from "cors"; import fs from "fs"; import path from "path"; dotenv.config(); const app = express(); app.use(cors()); app.use(express.json()); const PORT = Number(process.env.PORT || 7078); const CORTEX_API = process.env.CORTEX_API || "http://cortex:7081"; const CORTEX_INGEST = process.env.CORTEX_URL_INGEST || "http://cortex:7081/ingest"; const sessionsDir = path.join(process.cwd(), "sessions"); if (!fs.existsSync(sessionsDir)) fs.mkdirSync(sessionsDir); // ----------------------------------------------------- // Helper: fetch with timeout + error detail // ----------------------------------------------------- async function fetchJSON(url, method = "POST", body = null, timeoutMs = 20000) { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const resp = await fetch(url, { method, headers: { "Content-Type": "application/json" }, body: body ? JSON.stringify(body) : null, signal: controller.signal, }); const text = await resp.text(); const parsed = text ? JSON.parse(text) : null; if (!resp.ok) { throw new Error( parsed?.detail || parsed?.error || parsed?.message || text || resp.statusText ); } return parsed; } finally { clearTimeout(timeout); } } // ----------------------------------------------------- // Helper: append session turn // ----------------------------------------------------- async function appendSessionExchange(sessionId, entry) { const file = path.join(sessionsDir, `${sessionId}.jsonl`); const line = JSON.stringify({ ts: new Date().toISOString(), user: entry.user, assistant: entry.assistant, raw: entry.raw, }) + "\n"; fs.appendFileSync(file, line, "utf8"); } // ----------------------------------------------------- // HEALTHCHECK // ----------------------------------------------------- app.get("/_health", (_, res) => { res.json({ ok: true, time: new Date().toISOString() }); }); // ----------------------------------------------------- // MAIN ENDPOINT // ----------------------------------------------------- app.post("/v1/chat/completions", async (req, res) => { try { const { messages, model } = req.body; if (!messages?.length) { return res.status(400).json({ error: "invalid_messages" }); } const userMsg = messages[messages.length - 1]?.content || ""; console.log(`🛰️ Relay received message → "${userMsg}"`); // ------------------------------------------------- // Step 1: Ask Cortex to process the prompt // ------------------------------------------------- let cortexResp; try { cortexResp = await fetchJSON(`${CORTEX_API}/reason`, "POST", { session_id: "default", user_prompt: userMsg, }); } catch (err) { console.error("💥 Relay → Cortex error:", err.message); return res.status(500).json({ error: "cortex_failed", detail: err.message, }); } const personaText = cortexResp.persona || "(no persona text returned)"; // ------------------------------------------------- // Step 2: Forward to Cortex ingest (fire-and-forget) // ------------------------------------------------- try { await fetchJSON(CORTEX_INGEST, "POST", cortexResp); } catch (err) { console.warn("⚠️ Cortex ingest failed:", err.message); } // ------------------------------------------------- // Step 3: Local session logging // ------------------------------------------------- try { await appendSessionExchange("default", { user: userMsg, assistant: personaText, raw: cortexResp, }); } catch (err) { console.warn("⚠️ Relay log write failed:", err.message); } // ------------------------------------------------- // Step 4: Return OpenAI-style response to UI // ------------------------------------------------- return res.json({ id: "relay-" + Date.now(), object: "chat.completion", model: model || "lyra", choices: [ { index: 0, message: { role: "assistant", content: personaText, }, finish_reason: "stop", }, ], }); } catch (err) { console.error("💥 relay fatal error", err); res.status(500).json({ error: "relay_failed", detail: err?.message || String(err), }); } }); // ----------------------------------------------------- app.listen(PORT, () => { console.log(`Relay is online at port ${PORT}`); });