test(llm-retry): cover incomplete retry predecessors
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { Context } from 'cordis'
|
import { Context } from 'cordis'
|
||||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
import SessionStore, { SessionId, type Session } from '@deepseek-ai/dsh-session'
|
||||||
import { ProviderRequestId } from '@deepseek-ai/dsh-llm'
|
import { ProviderRequestId } from '@deepseek-ai/dsh-llm'
|
||||||
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
|
import { MAX_TIMER_DELAY_MS } from '@deepseek-ai/dsh-timeout'
|
||||||
import InvariantService from '@deepseek-ai/dsh-invariants'
|
import InvariantService from '@deepseek-ai/dsh-invariants'
|
||||||
@@ -27,6 +27,17 @@ function closeStep(ctx: Context, id: string, turn = 1, step = 1) {
|
|||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function appendRetryTurn(session: Session, turn: number) {
|
||||||
|
session.append('turn/start', { turn, trigger: { kind: 'retry' } })
|
||||||
|
session.append('step/start', { turn, step: 1 })
|
||||||
|
session.append('request/header', {
|
||||||
|
header: { config: { provider: 'mock', model: 'mock' } },
|
||||||
|
reason: 'initial',
|
||||||
|
})
|
||||||
|
session.append('step/end', { turn, step: 1 })
|
||||||
|
session.append('llm/retry', { turn, step: 1, ...normal })
|
||||||
|
}
|
||||||
|
|
||||||
const failure = { message: 'provider busy', code: 'RATE_LIMIT', status: 429 }
|
const failure = { message: 'provider busy', code: 'RATE_LIMIT', status: 429 }
|
||||||
const normal = {
|
const normal = {
|
||||||
provider: 'mock',
|
provider: 'mock',
|
||||||
@@ -225,6 +236,36 @@ describe('llm-retry invariants', () => {
|
|||||||
}).not.toThrow()
|
}).not.toThrow()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('starts a fresh retry chain after incomplete predecessor boundaries', async () => {
|
||||||
|
const ctx = new Context()
|
||||||
|
await ctx.plugin(SessionStore)
|
||||||
|
|
||||||
|
const missingEnd = ctx.sessions.create(SessionId('retry-invariant-missing-end'))
|
||||||
|
missingEnd.append('user/message', {
|
||||||
|
content: [{ type: 'text', text: 'idle context' }],
|
||||||
|
source: { kind: 'user' },
|
||||||
|
}, { surfaceOp: 'append' })
|
||||||
|
appendRetryTurn(missingEnd, 2)
|
||||||
|
|
||||||
|
const nonFailureEnd = ctx.sessions.create(SessionId('retry-invariant-non-failure-end'))
|
||||||
|
nonFailureEnd.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
|
||||||
|
nonFailureEnd.append('user/message', {
|
||||||
|
content: [{ type: 'text', text: 'idle context' }],
|
||||||
|
source: { kind: 'user' },
|
||||||
|
}, { surfaceOp: 'append' })
|
||||||
|
appendRetryTurn(nonFailureEnd, 2)
|
||||||
|
|
||||||
|
const missingStart = ctx.sessions.create(SessionId('retry-invariant-missing-start'))
|
||||||
|
missingStart.append('turn/end', {
|
||||||
|
turn: 1,
|
||||||
|
reason: { kind: 'error', step: 1, failure },
|
||||||
|
})
|
||||||
|
appendRetryTurn(missingStart, 2)
|
||||||
|
|
||||||
|
await ctx.plugin(InvariantService)
|
||||||
|
await expect(ctx.plugin(RetryInvariant)).resolves.toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
it('rejects a provider that does not match the failed request route', async () => {
|
it('rejects a provider that does not match the failed request route', async () => {
|
||||||
const ctx = await setup()
|
const ctx = await setup()
|
||||||
const session = closeStep(ctx, 'retry-invariant-provider')
|
const session = closeStep(ctx, 'retry-invariant-provider')
|
||||||
|
|||||||
Reference in New Issue
Block a user