"use strict"; /* * /api/chat-v2 — 第五域聊天路由 * 冰朔 TCS-0002∞ · 铸渊 ICE-GL-ZY001 * * 支持: * - 人格体选择(霜砚·语言主控层 / 铸渊·现实执行层) * - 引擎选择(DeepSeek R1 / 智谱 GLM-4-Plus / 通义 Qwen-Max / 火山 Doubao-pro) * - Anti-Hallucination 工具链强制锁 * - 30轮记忆 → 滚动重点提取 * - SSE 流式输出 * - 人格体语境从 persona-brain.db 动态加载 * * cc-002: 永不在 messages 里补 system role * cc-004: 中文回执 */ const express = require("express"); const router = express.Router(); const crypto = require("crypto"); const path = require("path"); const fs = require("fs"); const engine = require("../engine/engine-registry"); const { TOOLS } = require("../engine/tool-registry"); // ─── 人格体ID映射 ───────────────────────────────────────────── const PERSONA_IDS = { shuangyan: 'ICE-GL-SY001', zhuyuan: 'ICE-GL-ZY001' }; // ─── 临时访问码存储 ─────────────────────────────────────────── const TEMP_CODES_FILE = path.resolve(__dirname, "..", "data", "temp-codes.json"); function loadTempCodes() { try { if (fs.existsSync(TEMP_CODES_FILE)) { return JSON.parse(fs.readFileSync(TEMP_CODES_FILE, "utf-8")); } } catch (e) {} return {}; } function saveTempCodes(codes) { try { fs.mkdirSync(path.dirname(TEMP_CODES_FILE), { recursive: true }); fs.writeFileSync(TEMP_CODES_FILE, JSON.stringify(codes, null, 2), "utf-8"); } catch (e) {} } // ─── POST /api/chat-v2 流式聊天 ─────────────────────────────── router.post("/", async (req, res) => { const body = req.body || {}; const { persona, engine: engineName, message, history, user, isTemp } = body; if (!message || !message.trim()) { return res.status(400).json({ error: true, code: "empty_msg", message: "消息不能为空" }); } if (!persona || !["shuangyan", "zhuyuan"].includes(persona)) { return res.status(400).json({ error: true, code: "bad_persona", message: "人格体选择错误" }); } if (!engineName || !["deepseek", "zhipu", "tongyi", "huoshan"].includes(engineName)) { return res.status(400).json({ error: true, code: "bad_engine", message: "推理引擎选择错误" }); } if (isTemp) { const codes = loadTempCodes(); const codeData = codes[user]; if (!codeData) { return res.status(403).json({ error: true, code: "invalid_code", message: "临时访问码无效或已过期" }); } if (codeData.used >= 20) { return res.status(403).json({ error: true, code: "code_exhausted", message: "临时访问码已用完 (20/20)" }); } codes[user].used = (codes[user].used || 0) + 1; saveTempCodes(codes); } // ─── 设置 SSE 响应头 ──────────────────────────────────────── res.setHeader("Content-Type", "text/event-stream"); res.setHeader("Cache-Control", "no-cache"); res.setHeader("Connection", "keep-alive"); res.setHeader("X-Accel-Buffering", "no"); res.flushHeaders(); // ─── Phase 0: 从数据库加载人格体语境 ──────────────────────── const personaId = PERSONA_IDS[persona]; const personaCtx = engine.loadPersonaContext(personaId); if (personaCtx.loadedFromDB) { res.write("data: " + JSON.stringify({ type: "persona-loaded", persona: personaCtx.name, principles: (personaCtx.principles || []).length, fromDB: true }) + "\n\n"); } // ─── Phase 1: Anti-Hallucination 工具拦截 ──────────────────── const interception = engine.interceptToolCall(message, TOOLS); let toolResult = null; if (interception.hit) { if (interception.available) { res.write("data: " + JSON.stringify({ type: "tool-call", tool: interception.tool, name: interception.name }) + "\n\n"); try { toolResult = await interception.executor(message); res.write("data: " + JSON.stringify({ type: "tool-result", tool: interception.tool, result: toolResult }) + "\n\n"); } catch (e) { toolResult = "[系统] 工具执行错误: " + e.message; } } else { res.write("data: " + JSON.stringify({ type: "tool-unavailable", tool: interception.tool, name: interception.name }) + "\n\n"); toolResult = "[系统回执] 当前没有「" + interception.name + "」工具。"; } } // ─── Phase 2: 构建上下文 + 记忆管理 ────────────────────────── const compressed = engine.compressMemory(history || [], 30); const messages = engine.buildMessages(compressed, message, personaCtx); // ─── Phase 3: 调用外部引擎流式输出 ────────────────────────── engine.streamEngine(engineName, messages, res); }); // ─── POST /api/generate-temp-code 生成临时访问码 ───────────── router.post("/generate-temp-code", (req, res) => { const { username, isAdmin } = req.body || {}; if (username !== "bingshuo" && !isAdmin) { return res.status(403).json({ error: true, message: "仅主权者可生成临时访问码" }); } const code = "GH-" + crypto.randomBytes(4).toString("hex").toUpperCase(); const codes = loadTempCodes(); codes[code] = { created: Date.now(), used: 0, max: 20 }; saveTempCodes(codes); res.json({ ok: true, code: code, max_uses: 20 }); }); // ─── POST /api/validate-temp-code 验证临时访问码 ───────────── router.post("/validate-temp-code", (req, res) => { const { code } = req.body || {}; if (!code) return res.status(400).json({ error: true, message: "请输入访问码" }); const codes = loadTempCodes(); const data = codes[code]; if (!data) { return res.status(403).json({ error: true, message: "访问码无效" }); } if (data.used >= 20) { return res.status(403).json({ error: true, message: "访问码已用完 (20/20)" }); } res.json({ ok: true, remain: 20 - data.used }); }); module.exports = router;