refactor: remove legacy data compatibility

This commit is contained in:
2026-06-07 16:56:23 +08:00
parent 5e0c16f8b2
commit 1ed7e56f35
6 changed files with 46 additions and 412 deletions
+10 -34
View File
@@ -43,6 +43,9 @@ type SessionTranscriptContext = {
sessionId: string;
};
const DEFAULT_SEARCH_MAX_RESULTS = 8;
const DEFAULT_SEARCH_MAX_QUERY_CHARS = 240;
export class SessionTranscriptStore {
private readonly writeQueues = new Map<string, Promise<void>>();
@@ -62,6 +65,7 @@ export class SessionTranscriptStore {
) {
const key = this.filePath(context);
return this.serializeWrite(key, async () => {
// 同一会话的多次写入串行化,防止流式结束和 UI 同步同时写同一个 transcript。
const transcript = (await this.readTranscript(context)) ?? {
actorKey: context.actorKey,
clientSessionId: context.clientSessionId,
@@ -82,6 +86,7 @@ export class SessionTranscriptStore {
lastTurn?.userMessage === userMessage &&
lastTurn.assistantMessage === assistantMessage
) {
// 相同问答重复写入时只更新工具调用数量,避免刷新/重试造成 transcript 重复膨胀。
lastTurn.toolCallCount = Math.max(lastTurn.toolCallCount, turn.toolCallCount);
transcript.clientSessionId = context.clientSessionId ?? transcript.clientSessionId;
transcript.sessionId = context.sessionId;
@@ -129,6 +134,7 @@ export class SessionTranscriptStore {
) {
const sourceTranscript = await this.readTranscript(sourceContext);
const timestamp = new Date().toISOString();
// 分叉会话只复制用户选择保留的上下文,后续新分支拥有独立 transcript 文件。
const nextTranscript: SessionTranscriptRecord = {
actorKey: targetContext.actorKey,
clientSessionId: targetContext.clientSessionId,
@@ -144,9 +150,10 @@ export class SessionTranscriptStore {
async search(
context: Pick<SessionTranscriptContext, "actorKey" | "projectKey">,
query: string,
maxResults = config.SESSION_SEARCH_MAX_RESULTS,
maxResults = DEFAULT_SEARCH_MAX_RESULTS,
): Promise<SessionSearchHit[]> {
const normalizedQuery = query.trim().toLowerCase().slice(0, config.SESSION_SEARCH_MAX_QUERY_CHARS);
// 当前搜索是轻量本地文本匹配,按 actor/project 过滤后再计算简单相关性分数。
const normalizedQuery = query.trim().toLowerCase().slice(0, DEFAULT_SEARCH_MAX_QUERY_CHARS);
if (!normalizedQuery) {
return [];
}
@@ -189,38 +196,7 @@ export class SessionTranscriptStore {
}
private async readTranscript(context: SessionTranscriptContext) {
const direct = await readJsonFile<SessionTranscriptRecord>(this.filePath(context));
if (direct) {
return direct;
}
const clientSessionId = context.clientSessionId?.trim();
if (!clientSessionId) {
return null;
}
const files = await listJsonFiles(this.baseDir);
const matches: SessionTranscriptRecord[] = [];
for (const file of files) {
const transcript = await readJsonFile<SessionTranscriptRecord>(file);
if (!transcript) {
continue;
}
if (
transcript.actorKey !== context.actorKey ||
transcript.projectKey !== context.projectKey ||
transcript.clientSessionId !== clientSessionId
) {
continue;
}
matches.push(transcript);
}
if (matches.length === 0) {
return null;
}
return matches.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0] ?? null;
return await readJsonFile<SessionTranscriptRecord>(this.filePath(context));
}
private filePath(context: SessionTranscriptContext) {