test: update conversation assembly coverage and docs

This commit is contained in:
imccyu
2026-08-09 15:48:56 +08:00
parent 6d09b3168d
commit 522acc7432
101 changed files with 3970 additions and 2295 deletions
+72 -1
View File
@@ -5,6 +5,7 @@ import { createUserMessage, ProviderRequestId } from '@deepseek-ai/dsh-llm'
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
import InvariantService from '@deepseek-ai/dsh-invariants'
import * as RetryInvariant from '@deepseek-ai/dsh-llm-retry/invariant'
import { RetryId } from '@deepseek-ai/dsh-llm-retry/brand'
import { providerForOpenStep } from '../src/history.ts'
async function setup(): Promise<Context> {
@@ -38,6 +39,7 @@ function appendRetryTurn(session: Session, turn: number) {
const failure = { message: 'provider busy', code: 'RATE_LIMIT', status: 429 }
const normal = {
retryId: RetryId('normal-retry-chain'),
provider: 'mock',
mode: 'normal' as const,
policyKey: 'normal-policy',
@@ -47,6 +49,7 @@ const normal = {
failure,
}
const always = {
retryId: RetryId('always-retry-chain'),
provider: 'mock',
mode: 'always' as const,
policyKey: 'always-policy',
@@ -130,6 +133,7 @@ describe('llm-retry invariants', () => {
})
it.each([
['empty-retry-id', { ...normal, retryId: RetryId('') }, /retryId must be a non-empty string/],
['retry-zero', { ...normal, retry: 0 }, /positive safe integer/],
['retry-fraction', { ...normal, retry: 1.5 }, /positive safe integer/],
['max-zero', { ...normal, maxRetries: 0 }, /positive safe maxRetries/],
@@ -212,10 +216,65 @@ describe('llm-retry invariants', () => {
reset.append('step/end', { turn: 1, step: 1 })
reset.append('step/start', { turn: 1, step: 2 })
expect(() => {
reset.append('llm/retry', { turn: 1, step: 2, ...normal })
reset.append('llm/retry', {
turn: 1,
step: 2,
...normal,
retryId: RetryId('reset-step-2-retry-chain'),
})
}).not.toThrow()
})
it('keeps one retry identity per provider-policy chain', async () => {
const ctx = await setup()
const changed = openStep(ctx, 'retry-invariant-changed-chain-id')
changed.append('llm/retry', { turn: 1, step: 1, ...normal })
expect(() => changed.append('llm/retry', {
turn: 1,
step: 1,
...normal,
retry: 2,
retryId: RetryId('changed-retry-chain'),
})).toThrow(/must preserve retryId/)
const reused = openStep(ctx, 'retry-invariant-reused-chain-id')
reused.append('llm/retry', { turn: 1, step: 1, ...normal })
expect(() => reused.append('llm/retry', {
turn: 1,
step: 1,
...always,
retryId: normal.retryId,
})).toThrow(/already owned by another chain/)
})
it('validates retry-started correlation and uniqueness', async () => {
const ctx = await setup()
const empty = openStep(ctx, 'retry-started-empty-id')
expect(() => empty.append('llm/retry-started', {
retryId: RetryId(''), turn: 1, step: 1, retry: 1,
})).toThrow(/retryId must be a non-empty string/)
const missing = openStep(ctx, 'retry-started-missing-schedule')
expect(() => missing.append('llm/retry-started', {
retryId: RetryId('missing-retry-chain'), turn: 1, step: 1, retry: 1,
})).toThrow(/pairs no prior scheduled attempt/)
const mismatch = openStep(ctx, 'retry-started-location-mismatch')
mismatch.append('llm/retry', { turn: 1, step: 1, ...normal })
expect(() => mismatch.append('llm/retry-started', {
retryId: normal.retryId, turn: 2, step: 1, retry: 1,
})).toThrow(/turn\/step must match/)
const repeated = openStep(ctx, 'retry-started-repeated')
repeated.append('llm/retry', { turn: 1, step: 1, ...normal })
repeated.append('llm/retry-started', {
retryId: normal.retryId, turn: 1, step: 1, retry: 1,
})
expect(() => repeated.append('llm/retry-started', {
retryId: normal.retryId, turn: 1, step: 1, retry: 1,
})).toThrow(/repeats one scheduled attempt/)
})
it('starts a fresh retry chain after incomplete predecessor boundaries', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
@@ -261,4 +320,16 @@ describe('llm-retry invariants', () => {
await ctx.plugin(InvariantService)
await expect(ctx.plugin(RetryInvariant)).rejects.toThrow(/inside an open turn/)
})
it('accepts a scheduled and started attempt on late registration', async () => {
const ctx = new Context()
await ctx.plugin(SessionStore)
const session = openStep(ctx, 'retry-invariant-late-started')
session.append('llm/retry', { turn: 1, step: 1, ...normal })
session.append('llm/retry-started', {
retryId: normal.retryId, turn: 1, step: 1, retry: 1,
})
await ctx.plugin(InvariantService)
await expect(ctx.plugin(RetryInvariant)).resolves.toBeDefined()
})
})
@@ -6,6 +6,7 @@ import { Context } from 'cordis'
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
import SessionPersistenceSqlite from '@deepseek-ai/dsh-session-persistence-sqlite'
import { RetryId } from '@deepseek-ai/dsh-llm-retry/brand'
import type {} from '../src/index.ts'
const dirs: string[] = []
@@ -39,6 +40,7 @@ describe.each(['jsonl', 'sqlite'] as const)('%s retry-event persistence', (kind)
reason: 'initial',
})
const event = session.append('llm/retry', {
retryId: RetryId(`retry-${kind}-chain`),
turn: 1,
step: 1,
provider: 'mock',
@@ -191,7 +191,9 @@ describe('provider-routed retry policy', () => {
agent.followup(createUserMessage({ content: [{ type: 'text', text: 'go' }], source: { kind: 'user' } }))
const event = await scheduled
expect(event.data.retryId).toEqual(expect.any(String))
expect(event.data).toEqual({
retryId: event.data.retryId,
turn: 1,
step: 1,
provider: 'mock',
@@ -10,6 +10,7 @@ import type { Session, SessionEvent } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
import type { ContextBreakdownProjection } from '@deepseek-ai/dsh-token-meter/client'
import { CompactionId } from '@deepseek-ai/dsh-compact/brand'
import { contextBreakdownProjectionDefinition } from '../src/breakdown-projection.ts'
import {
estimateContent,
@@ -59,6 +60,7 @@ function appendSummaryMeter(ctx: Context, session: Session, start: number, end:
const endIdx = nodes.findIndex(node => node.seq === end)
const shadowed = nodes.slice(startIdx, endIdx + 1)
session.append('compact/summary', {
compactionId: CompactionId('context-breakdown-summary'),
summary: [{ type: 'text', text: 'summary' }],
shadowedRange: { start, end },
shadowedSeqs: shadowed.map(node => node.seq),
@@ -7,6 +7,7 @@ import type { Session } from '@deepseek-ai/dsh-session'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import TokenMeterService from '@deepseek-ai/dsh-token-meter'
import type { ContextPressureProjection, TokenUsageProjection } from '@deepseek-ai/dsh-token-meter/client'
import { CompactionId } from '@deepseek-ai/dsh-compact/brand'
const ZERO: TokenUsageProjection = {
uncachedInputTokens: 0,
@@ -81,6 +82,7 @@ function appendSummaryMeter(ctx: Context, session: Session, start: number, end:
const endIdx = nodes.findIndex(node => node.seq === end)
const shadowed = nodes.slice(startIdx, endIdx + 1)
session.append('compact/summary', {
compactionId: CompactionId('token-usage-summary'),
summary: [{ type: 'text', text: 'summary' }],
shadowedRange: { start, end },
shadowedSeqs: shadowed.map(node => node.seq),