重构会话管理,简化上下文存储逻辑
This commit is contained in:
+61
-16
@@ -11,6 +11,7 @@ import { type ResultReferenceResolver } from "../results/resolver.js";
|
||||
import { RESULT_REFERENCE_KIND } from "../results/store.js";
|
||||
import { type OpencodeRuntimeAdapter } from "../runtime/opencode.js";
|
||||
import { type ChatSessionBridge } from "../chat/sessionBridge.js";
|
||||
import { type ConversationRecord } from "../conversations/store.js";
|
||||
import { toActorKey, toProjectKey } from "../utils/fileStore.js";
|
||||
import {
|
||||
buildPromptWithLearningContext,
|
||||
@@ -51,6 +52,12 @@ const conversationStateSchema = z.object({
|
||||
branch_groups: z.array(z.unknown()).default([]),
|
||||
});
|
||||
|
||||
const toConversationStateContext = (conversation: ConversationRecord) => ({
|
||||
actorKey: conversation.actorKey,
|
||||
projectKey: conversation.projectKey,
|
||||
sessionId: conversation.sessionId,
|
||||
});
|
||||
|
||||
export const buildChatRouter = (
|
||||
sessionBridge: ChatSessionBridge,
|
||||
runtime: OpencodeRuntimeAdapter,
|
||||
@@ -145,7 +152,9 @@ export const buildChatRouter = (
|
||||
return;
|
||||
}
|
||||
|
||||
const state = await conversationStateStore.read(conversation.sessionScopeKey);
|
||||
const state = await conversationStateStore.read(
|
||||
toConversationStateContext(conversation),
|
||||
);
|
||||
res.json({
|
||||
id: conversation.sessionId,
|
||||
title: conversation.title ?? "新对话",
|
||||
@@ -190,7 +199,7 @@ export const buildChatRouter = (
|
||||
const nextRecord = await conversationStore.touch(record, {
|
||||
...(parsed.data.title ? { title: parsed.data.title } : {}),
|
||||
});
|
||||
await conversationStateStore.write(nextRecord.sessionScopeKey, {
|
||||
await conversationStateStore.write(toConversationStateContext(nextRecord), {
|
||||
sessionId: nextRecord.sessionId,
|
||||
isTitleManuallyEdited: parsed.data.is_title_manually_edited,
|
||||
messages: parsed.data.messages,
|
||||
@@ -231,13 +240,18 @@ export const buildChatRouter = (
|
||||
return;
|
||||
}
|
||||
const nextConversation = await conversationStore.touch(conversation, { title });
|
||||
const state = await conversationStateStore.read(nextConversation.sessionScopeKey);
|
||||
const state = await conversationStateStore.read(
|
||||
toConversationStateContext(nextConversation),
|
||||
);
|
||||
if (state) {
|
||||
await conversationStateStore.write(nextConversation.sessionScopeKey, {
|
||||
await conversationStateStore.write(
|
||||
toConversationStateContext(nextConversation),
|
||||
{
|
||||
...state,
|
||||
isTitleManuallyEdited:
|
||||
isTitleManuallyEdited ?? state.isTitleManuallyEdited,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
res.json({
|
||||
id: nextConversation.sessionId,
|
||||
@@ -264,7 +278,13 @@ export const buildChatRouter = (
|
||||
res.status(204).end();
|
||||
return;
|
||||
}
|
||||
await conversationStateStore.remove(conversation.sessionScopeKey);
|
||||
await conversationStateStore.remove(toConversationStateContext(conversation));
|
||||
if (conversation.opencodeSessionId) {
|
||||
await sessionBridge.deleteConversationSession({
|
||||
clientSessionId: conversation.sessionId,
|
||||
sessionId: conversation.opencodeSessionId,
|
||||
});
|
||||
}
|
||||
await conversationStore.remove(conversation);
|
||||
res.status(204).end();
|
||||
});
|
||||
@@ -323,9 +343,20 @@ export const buildChatRouter = (
|
||||
}
|
||||
|
||||
try {
|
||||
const binding = await sessionBridge.abort({
|
||||
clientSessionId: parsed.data.session_id,
|
||||
});
|
||||
const projectId = req.header("x-project-id") ?? undefined;
|
||||
const userId = req.header("x-user-id") ?? undefined;
|
||||
const actorKey = toActorKey(userId);
|
||||
const projectKey = toProjectKey(projectId);
|
||||
const conversation = await conversationStore.get(
|
||||
{ actorKey, projectId, projectKey, userId },
|
||||
parsed.data.session_id,
|
||||
);
|
||||
const binding = conversation?.opencodeSessionId
|
||||
? await sessionBridge.abort({
|
||||
clientSessionId: conversation.sessionId,
|
||||
sessionId: conversation.opencodeSessionId,
|
||||
})
|
||||
: null;
|
||||
|
||||
if (!binding) {
|
||||
res.status(204).end();
|
||||
@@ -467,14 +498,22 @@ export const buildChatRouter = (
|
||||
userId,
|
||||
});
|
||||
const activeConversation = await conversationStore.touch(conversation);
|
||||
const hadExistingRuntimeSession = Boolean(activeConversation.opencodeSessionId);
|
||||
|
||||
const { binding, requestContext, created } = await sessionBridge.resolve({
|
||||
clientSessionId: activeConversation.sessionId,
|
||||
sessionId: activeConversation.opencodeSessionId,
|
||||
accessToken,
|
||||
projectId,
|
||||
traceId,
|
||||
userId,
|
||||
});
|
||||
const conversationWithRuntime =
|
||||
created && binding.sessionId !== activeConversation.opencodeSessionId
|
||||
? await conversationStore.touch(activeConversation, {
|
||||
opencodeSessionId: binding.sessionId,
|
||||
})
|
||||
: activeConversation;
|
||||
const historyContext = {
|
||||
actorKey: requestContext.actorKey,
|
||||
clientSessionId: requestContext.clientSessionId,
|
||||
@@ -482,6 +521,9 @@ export const buildChatRouter = (
|
||||
sessionId: requestContext.clientSessionId,
|
||||
};
|
||||
const recentTurns = await sessionHistoryStore.getRecentTurns(historyContext, 8);
|
||||
const initialConversationState = await conversationStateStore.read(
|
||||
toConversationStateContext(conversationWithRuntime),
|
||||
);
|
||||
|
||||
logger.info(
|
||||
{
|
||||
@@ -521,8 +563,12 @@ export const buildChatRouter = (
|
||||
memoryStore,
|
||||
requestContext.actorKey,
|
||||
requestContext.projectKey,
|
||||
recentTurns,
|
||||
parsed.data.message,
|
||||
{
|
||||
recentTurns,
|
||||
persistedMessages: initialConversationState?.messages,
|
||||
message: parsed.data.message,
|
||||
restoreConversation: !hadExistingRuntimeSession,
|
||||
},
|
||||
);
|
||||
const streamResult = await streamPromptResponse({
|
||||
runtime,
|
||||
@@ -550,10 +596,10 @@ export const buildChatRouter = (
|
||||
const latestConversation =
|
||||
(await conversationStore.get(
|
||||
{ actorKey, projectId, projectKey, userId },
|
||||
activeConversation.sessionId,
|
||||
)) ?? activeConversation;
|
||||
conversationWithRuntime.sessionId,
|
||||
)) ?? conversationWithRuntime;
|
||||
const latestConversationState = await conversationStateStore.read(
|
||||
latestConversation.sessionScopeKey,
|
||||
toConversationStateContext(latestConversation),
|
||||
);
|
||||
const existingSessionTitle = latestConversation.title;
|
||||
let sessionTitle = existingSessionTitle;
|
||||
@@ -606,8 +652,7 @@ export const buildChatRouter = (
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
await sessionBridge.releaseRuntimeSession(clientSessionId, binding.sessionId);
|
||||
sessionBridge.deleteAbortController(clientSessionId);
|
||||
sessionBridge.finalizeRequest(clientSessionId);
|
||||
streamClosed = true;
|
||||
req.off("close", handleClientClose);
|
||||
res.off("close", handleClientClose);
|
||||
|
||||
Reference in New Issue
Block a user