refactor: identify and freeze messages at creation

This commit is contained in:
_Kerman
2026-07-28 13:55:59 +08:00
parent c49c0ba497
commit fbf87e660c
345 changed files with 5220 additions and 2901 deletions

View File

@@ -1,6 +1,6 @@
import { Context } from 'cordis'
import { describe, expect, it } from 'vitest'
import LlmService, { LlmAdapter } from '@deepseek-ai/dsh-llm'
import LlmService, { createUserMessage, LlmAdapter } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
import SessionTitleService from '@deepseek-ai/dsh-session-title'
@@ -33,9 +33,9 @@ describe('all-messages LLM title provider', () => {
it('includes seeded history and the latest prompt while inheriting the logged request route', async () => {
const seeded = new Session(SessionId('seed-source'))
seeded.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
const inherited = seeded.append('user/message', {
const inherited = seeded.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'inherited prompt' }], source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
seeded.append('session/title', {
title: 'Inherited fallback', messageSeqs: [inherited.seq], source: { kind: 'fallback' },
})
@@ -53,9 +53,9 @@ describe('all-messages LLM title provider', () => {
meta: { parentSession: seeded.id, seedLength: seeded.seq },
})
session.append('turn/start', { turn: 2, trigger: { kind: 'message', source: { kind: 'user' } } })
const latest = session.append('user/message', {
const latest = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'latest prompt' }], source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await settle()
session.append('request/header', {
header: { config: { provider: 'current-route', model: 'current-model' } }, reason: 'resume',

View File

@@ -6,7 +6,7 @@ import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import LlmService, { LlmAdapter } from '@deepseek-ai/dsh-llm'
import LlmService, { createUserMessage, LlmAdapter } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import SessionTitleService from '@deepseek-ai/dsh-session-title'
@@ -95,10 +95,10 @@ describe('session-title Loader composition', () => {
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
const message = session.append('user/message', {
const message = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'Compose a title through Loader' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await new Promise(resolve => setTimeout(resolve, 0))
session.append('request/header', {
header: { config: { provider: 'main-route', model: 'main-model' } },

View File

@@ -1,3 +1,4 @@
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import LlmService from '@deepseek-ai/dsh-llm'
@@ -38,10 +39,10 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('first-message title provider wit
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
const message = session.append('user/message', {
const message = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'Explain why append-only logs make session titles durable.' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
const title = await ctx.sessionTitle.refresh(session)

View File

@@ -1,6 +1,6 @@
import { Context } from 'cordis'
import { describe, expect, it, vi } from 'vitest'
import LlmService, { LlmAdapter } from '@deepseek-ai/dsh-llm'
import LlmService, { createUserMessage, LlmAdapter } from '@deepseek-ai/dsh-llm'
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
import SessionTitleService, { type SessionTitleProvider } from '@deepseek-ai/dsh-session-title'
@@ -61,17 +61,17 @@ describe('first-message LLM title provider', () => {
await ctx.plugin(providerPlugin, LLM_CONFIG)
const session = ctx.sessions.create(SessionId('first-plugin'))
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
const first = session.append('user/message', {
const first = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'first input' }], source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await settle()
session.append('request/header', {
header: { config: { provider: 'main', model: 'main-model' } }, reason: 'initial',
})
await settle()
session.append('user/message', {
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'second input must be ignored' }], source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await ctx.sessionTitle.refresh(session)

View File

@@ -6,7 +6,7 @@
import type { Context } from 'cordis'
import z from 'schemastery'
import { BlockAssembler, deepFreeze } from '@deepseek-ai/dsh-llm'
import { createUserMessage, BlockAssembler, deepFreeze } from '@deepseek-ai/dsh-llm'
import type { FinishReason, GenerateOptions, Message } from '@deepseek-ai/dsh-llm'
import { deadline, MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
import {
@@ -248,10 +248,10 @@ export async function generateSessionTitleWithLlm(
throw new Error(`session-title-llm: input is ${inputBytes} bytes, exceeding maxInputBytes ${config.maxInputBytes}`)
}
const route = resolveRoute(config, request)
const messages: Message[] = [{
role: 'user',
const messages: Message[] = [createUserMessage({
content: [{ type: 'text', text: framedInput }],
}]
source: { kind: 'plugin', plugin: 'dsh-session-title-llm' },
})]
const system = systemPrompt(config)
using callDeadline = deadline(request.signal, config.timeoutMs, SESSION_TITLE_TIMEOUT_CODE)
const options: GenerateOptions = deepFreeze({
@@ -281,7 +281,7 @@ export async function generateSessionTitleWithLlm(
callDeadline.signal.throwIfAborted()
const terminalError = finishError(assembler.finish)
if (terminalError !== undefined) throw terminalError
const blocks = assembler.message().content
const blocks = assembler.blocks()
if (blocks.some(block => block.type === 'tool-call')) {
throw new Error('session-title-llm: title output must contain text only')
}

View File

@@ -1,6 +1,6 @@
import { Context } from 'cordis'
import { describe, expect, it, vi } from 'vitest'
import LlmService, { CallId, isAgentLoopRequest, LlmAdapter } from '@deepseek-ai/dsh-llm'
import LlmService, { createUserMessage, CallId, isAgentLoopRequest, LlmAdapter } from '@deepseek-ai/dsh-llm'
import type { FinishReason, GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import { SessionTitleProviderId } from '@deepseek-ai/dsh-session-title'
@@ -82,14 +82,14 @@ function request(ctx: Context, signal = new AbortController().signal): SessionTi
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
const first = session.append('user/message', {
const first = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'first prompt' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
const second = session.append('user/message', {
}), { surfaceOp: 'append' })
const second = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: '第二个问题' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
return {
session,

View File

@@ -1,3 +1,4 @@
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import { mkdtemp, rm } from 'node:fs/promises'
@@ -26,10 +27,10 @@ async function appendPersistedTitle(ctx: Context, id: ReturnType<typeof SessionI
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
session.append('user/message', {
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'Persist this session title' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await new Promise(resolve => setTimeout(resolve, 0))
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
await ctx.parallel('session/flush', session)

View File

@@ -1,6 +1,6 @@
import { Context } from 'cordis'
import { describe, expect, it, vi } from 'vitest'
import LlmService, { deepFreeze, markAgentLoopRequest } from '@deepseek-ai/dsh-llm'
import LlmService, { createUserMessage, deepFreeze, markAgentLoopRequest } from '@deepseek-ai/dsh-llm'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import SessionTitleService, {
SessionTitleProviderId,
@@ -34,10 +34,10 @@ async function settle(): Promise<void> {
}
function appendHumanPrompt(session: ReturnType<Context['sessions']['create']>, text: string) {
return session.append('user/message', {
return session.append('user/message', createUserMessage({
content: [{ type: 'text', text }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
}
function appendRoute(session: ReturnType<Context['sessions']['create']>, reason: 'initial' | 'change' = 'initial'): void {

View File

@@ -1,3 +1,4 @@
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { Context, type Fiber } from 'cordis'
import { describe, expect, it, vi } from 'vitest'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
@@ -53,10 +54,10 @@ function startSession(ctx: Context, id: string): ReturnType<Context['sessions'][
}
function appendPrompt(session: ReturnType<Context['sessions']['create']>, text: string) {
return session.append('user/message', {
return session.append('user/message', createUserMessage({
content: [{ type: 'text', text }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
}
describe('SessionTitleService configuration and refresh boundaries', () => {

View File

@@ -1,3 +1,4 @@
import { createUserMessage } from '@deepseek-ai/dsh-llm'
import { Context } from 'cordis'
import { describe, expect, it } from 'vitest'
import SessionStore, { Session, SessionId } from '@deepseek-ai/dsh-session'
@@ -44,10 +45,10 @@ describe('SessionTitleService', () => {
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
const message = session.append('user/message', {
const message = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: ' Build\nlog-backed session titles please ' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await settleTitles()
@@ -81,10 +82,10 @@ describe('SessionTitleService', () => {
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
session.append('user/message', {
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'Explain this referenced session' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await settleTitles()
@@ -100,31 +101,31 @@ describe('SessionTitleService', () => {
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
session.append('user/message', {
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'plugin text' }],
source: { kind: 'plugin', plugin: 'seed' },
}, { surfaceOp: 'append' })
session.append('user/message', {
}), { surfaceOp: 'append' })
session.append('user/message', createUserMessage({
content: [{ type: 'reasoning', text: 'not visible text' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
session.append('user/message', {
}), { surfaceOp: 'append' })
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: ' \n\t ' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await settleTitles()
expect(ctx.sessionTitle.get(session)).toBeUndefined()
const eligible = session.append('user/message', {
const eligible = session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'first real prompt' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await settleTitles()
const first = ctx.sessionTitle.get(session)
session.append('user/message', {
session.append('user/message', createUserMessage({
content: [{ type: 'text', text: 'later prompt' }],
source: { kind: 'user' },
}, { surfaceOp: 'append' })
}), { surfaceOp: 'append' })
await settleTitles()
expect(first?.messageSeqs).toEqual([eligible.seq])