feat: rolling conversation summary + session continuity

- Backend emits session id in SSE; frontend sets activeChat so follow-ups
  reuse the same session (fixes fragmented history).
- sessions.summary column; messages past RECENT_LIMIT (10) are folded into
  a persisted summary via a non-streaming model call.
- buildMessages sends system(+summary) + last 10 messages, bounding context
  while preserving long-range memory.
- Tests: aiMessages (capping + summary injection).
This commit is contained in:
2026-08-19 10:23:56 -04:00
parent 56d6f355d1
commit 534b51b41b
7 changed files with 175 additions and 26 deletions
+21 -3
View File
@@ -1,6 +1,6 @@
import express from "express";
import { streamChatResponse } from "../services/ai.js";
import { createSession, addMessage, getMessages, getSession, listSessions } from "../services/db.js";
import { RECENT_LIMIT, streamChatResponse, summarizeConversation } from "../services/ai.js";
import { createSession, addMessage, getMessages, getSession, listSessions, setSummary } from "../services/db.js";
import { isOwnedSession, validateChatInput } from "./chatValidation.js";
const router = express.Router();
@@ -33,6 +33,23 @@ router.post("/", async (req, res) => {
res.setHeader("Connection", "keep-alive");
res.setHeader("X-Accel-Buffering", "no");
// Tell the client which session this conversation belongs to, so follow-ups
// reuse the same session and history accumulates.
res.write(`data: ${JSON.stringify({ type: "session", id: currentChatId })}\n\n`);
// Bounded context: fold older messages into a persistent summary, keep the
// last RECENT_LIMIT raw.
let summary = session.summary || "";
if (chatMessages.length > RECENT_LIMIT) {
const overflow = chatMessages.slice(0, chatMessages.length - RECENT_LIMIT);
try {
summary = await summarizeConversation(session.summary, overflow);
await setSummary(currentChatId, summary);
} catch (err) {
console.error("Summarize error:", err.message);
}
}
let assistantText = "";
await streamChatResponse(
chatMessages,
@@ -50,7 +67,8 @@ router.post("/", async (req, res) => {
() => {
res.write(`data: ${JSON.stringify({ type: "error", message: "AI service error" })}\n\n`);
res.end();
}
},
summary
);
res.write(`data: ${JSON.stringify({ type: "done" })}\n\n`);