refactor: identify and freeze messages at creation
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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' } },
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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])
|
||||
|
||||
Reference in New Issue
Block a user