Major rewire, all modules connected. Intake still wonkey
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import express from "express";
|
||||
import dotenv from "dotenv";
|
||||
import cors from "cors";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
@@ -11,146 +9,99 @@ 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);
|
||||
// core endpoints
|
||||
const CORTEX_REASON = process.env.CORTEX_REASON_URL || "http://cortex:7081/reason";
|
||||
const CORTEX_INGEST = process.env.CORTEX_INGEST_URL || "http://cortex:7081/ingest";
|
||||
const INTAKE_URL = process.env.INTAKE_URL || "http://intake:7082/summary";
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Helper: fetch with timeout + error detail
|
||||
// Helper request wrapper
|
||||
// -----------------------------------------------------
|
||||
async function fetchJSON(url, method = "POST", body = null, timeoutMs = 20000) {
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
||||
async function postJSON(url, data) {
|
||||
const resp = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
|
||||
const raw = await resp.text();
|
||||
let json;
|
||||
|
||||
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);
|
||||
json = raw ? JSON.parse(raw) : null;
|
||||
} catch (e) {
|
||||
throw new Error(`Non-JSON from ${url}: ${raw}`);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
// 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";
|
||||
if (!resp.ok) {
|
||||
throw new Error(json?.detail || json?.error || raw);
|
||||
}
|
||||
|
||||
fs.appendFileSync(file, line, "utf8");
|
||||
return json;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
// HEALTHCHECK
|
||||
// -----------------------------------------------------
|
||||
app.get("/_health", (_, res) => {
|
||||
res.json({ ok: true, time: new Date().toISOString() });
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
// -----------------------------------------------------
|
||||
// MAIN ENDPOINT
|
||||
// MAIN ENDPOINT (new canonical)
|
||||
// -----------------------------------------------------
|
||||
app.post("/v1/chat/completions", async (req, res) => {
|
||||
app.post("/chat", async (req, res) => {
|
||||
try {
|
||||
const { messages, model } = req.body;
|
||||
const session_id = req.body.session_id || "default";
|
||||
const user_msg = req.body.message || "";
|
||||
|
||||
if (!messages?.length) {
|
||||
return res.status(400).json({ error: "invalid_messages" });
|
||||
}
|
||||
console.log(`Relay → received: "${user_msg}"`);
|
||||
|
||||
const userMsg = messages[messages.length - 1]?.content || "";
|
||||
console.log(`🛰️ Relay received message → "${userMsg}"`);
|
||||
|
||||
// -------------------------------------------------
|
||||
// Step 1: Ask Cortex to process the prompt
|
||||
// -------------------------------------------------
|
||||
let cortexResp;
|
||||
// 1. → Cortex.reason
|
||||
let reason;
|
||||
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,
|
||||
reason = await postJSON(CORTEX_REASON, {
|
||||
session_id,
|
||||
user_prompt: user_msg
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Relay → Cortex.reason error:", e.message);
|
||||
return res.status(500).json({ error: "cortex_reason_failed", detail: e.message });
|
||||
}
|
||||
|
||||
const personaText = cortexResp.persona || "(no persona text returned)";
|
||||
const persona = reason.final_output || reason.persona || "(no persona text)";
|
||||
|
||||
// -------------------------------------------------
|
||||
// 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);
|
||||
}
|
||||
// 2. → Cortex.ingest
|
||||
postJSON(CORTEX_INGEST, {
|
||||
session_id,
|
||||
user_msg,
|
||||
assistant_msg: persona
|
||||
}).catch(e => console.warn("Relay → Cortex.ingest failed:", e.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);
|
||||
}
|
||||
// 3. → Intake summary
|
||||
postJSON(INTAKE_URL, {
|
||||
session_id,
|
||||
user_msg,
|
||||
assistant_msg: persona
|
||||
}).catch(e => console.warn("Relay → Intake failed:", e.message));
|
||||
|
||||
// -------------------------------------------------
|
||||
// Step 4: Return OpenAI-style response to UI
|
||||
// -------------------------------------------------
|
||||
// 4. → Return 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",
|
||||
},
|
||||
],
|
||||
session_id,
|
||||
reply: persona
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
console.error("💥 relay fatal error", err);
|
||||
console.error("Relay fatal:", err);
|
||||
res.status(500).json({
|
||||
error: "relay_failed",
|
||||
detail: err?.message || String(err),
|
||||
detail: err.message || String(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// -----------------------------------------------------
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Relay is online at port ${PORT}`);
|
||||
console.log(`Relay is online on port ${PORT}`);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user