Files
project-lyra/core/relay/server.js
2025-11-28 18:05:59 -05:00

169 lines
4.4 KiB
JavaScript

import express from "express";
import dotenv from "dotenv";
import cors from "cors";
dotenv.config();
const app = express();
app.use(cors());
app.use(express.json());
const PORT = Number(process.env.PORT || 7078);
// 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:7080/add_exchange";
// -----------------------------------------------------
// Helper request wrapper
// -----------------------------------------------------
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 {
json = raw ? JSON.parse(raw) : null;
} catch (e) {
throw new Error(`Non-JSON from ${url}: ${raw}`);
}
if (!resp.ok) {
throw new Error(json?.detail || json?.error || raw);
}
return json;
}
// -----------------------------------------------------
// Shared chat handler logic
// -----------------------------------------------------
async function handleChatRequest(session_id, user_msg) {
// 1. → Cortex.reason
let reason;
try {
reason = await postJSON(CORTEX_REASON, {
session_id,
user_prompt: user_msg
});
} catch (e) {
console.error("Relay → Cortex.reason error:", e.message);
throw new Error(`cortex_reason_failed: ${e.message}`);
}
const persona = reason.final_output || reason.persona || "(no persona text)";
// 2. → Cortex.ingest (async, non-blocking)
postJSON(CORTEX_INGEST, {
session_id,
user_msg,
assistant_msg: persona
}).catch(e => console.warn("Relay → Cortex.ingest failed:", e.message));
// 3. → Intake summary (async, non-blocking)
postJSON(INTAKE_URL, {
session_id,
user_msg,
assistant_msg: persona
}).catch(e => console.warn("Relay → Intake failed:", e.message));
// 4. Return result
return {
session_id,
reply: persona
};
}
// -----------------------------------------------------
// HEALTHCHECK
// -----------------------------------------------------
app.get("/_health", (_, res) => {
res.json({ ok: true });
});
// -----------------------------------------------------
// OPENAI-COMPATIBLE ENDPOINT (for UI)
// -----------------------------------------------------
app.post("/v1/chat/completions", async (req, res) => {
try {
// Extract from OpenAI format
const session_id = req.body.session_id || req.body.user || "default";
const messages = req.body.messages || [];
const lastMessage = messages[messages.length - 1];
const user_msg = lastMessage?.content || "";
if (!user_msg) {
return res.status(400).json({ error: "No message content provided" });
}
console.log(`Relay (v1) → received: "${user_msg}"`);
// Call the same logic as /chat
const result = await handleChatRequest(session_id, user_msg);
// Return in OpenAI format
return res.json({
id: `chatcmpl-${Date.now()}`,
object: "chat.completion",
created: Math.floor(Date.now() / 1000),
model: "lyra",
choices: [{
index: 0,
message: {
role: "assistant",
content: result.reply
},
finish_reason: "stop"
}],
usage: {
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0
}
});
} catch (err) {
console.error("Relay v1 endpoint fatal:", err);
res.status(500).json({
error: {
message: err.message || String(err),
type: "server_error",
code: "relay_failed"
}
});
}
});
// -----------------------------------------------------
// MAIN ENDPOINT (new canonical)
// -----------------------------------------------------
app.post("/chat", async (req, res) => {
try {
const session_id = req.body.session_id || "default";
const user_msg = req.body.message || "";
console.log(`Relay → received: "${user_msg}"`);
const result = await handleChatRequest(session_id, user_msg);
return res.json(result);
} catch (err) {
console.error("Relay fatal:", err);
res.status(500).json({
error: "relay_failed",
detail: err.message || String(err)
});
}
});
// -----------------------------------------------------
app.listen(PORT, () => {
console.log(`Relay is online on port ${PORT}`);
});