test(agent-loop): cover step-start disposal
This commit is contained in:
@@ -13,7 +13,7 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { Context } from 'cordis'
|
import { Context } from 'cordis'
|
||||||
import LlmService from '@deepseek-ai/dsh-llm'
|
import LlmService from '@deepseek-ai/dsh-llm'
|
||||||
import SessionStore, { TurnEndReason } from '@deepseek-ai/dsh-session'
|
import SessionStore, { SessionId, TurnEndReason } from '@deepseek-ai/dsh-session'
|
||||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||||
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
import ToolRegistry from '@deepseek-ai/dsh-tools'
|
||||||
import AgentRegistry, { AgentId } from '@deepseek-ai/dsh-agent'
|
import AgentRegistry, { AgentId } from '@deepseek-ai/dsh-agent'
|
||||||
@@ -224,6 +224,43 @@ describe('Agent.cancel()', () => {
|
|||||||
expect(types.filter(t => t === 'step/start').length).toBe(types.filter(t => t === 'step/end').length)
|
expect(types.filter(t => t === 'step/start').length).toBe(types.filter(t => t === 'step/end').length)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('disposal from a synchronous agent/step-start listener closes the open step as disposed', async () => {
|
||||||
|
const adapter = new MockAdapter([textResponse('should not stream')])
|
||||||
|
const ctx = new Context()
|
||||||
|
await ctx.plugin(LlmService)
|
||||||
|
await ctx.plugin(SessionStore)
|
||||||
|
await ctx.plugin(SystemPrompt)
|
||||||
|
await ctx.plugin(ToolRegistry)
|
||||||
|
await ctx.plugin(AgentRegistry)
|
||||||
|
await ctx.plugin(AgentLoop, { agents: [] })
|
||||||
|
ctx.llm.registerAdapter(['mock'], adapter)
|
||||||
|
|
||||||
|
const handle = ctx.agents.create({
|
||||||
|
agentId: AgentId('a-dispose-step-start'),
|
||||||
|
sessionId: SessionId('dispose-step-start-session'),
|
||||||
|
agentOptions: { model: 'mock' },
|
||||||
|
})
|
||||||
|
const agent = handle.agent as ReactLoopAgent
|
||||||
|
|
||||||
|
let disposalDone: Promise<void> | undefined
|
||||||
|
let streamed = false
|
||||||
|
ctx.on('agent/stream-chunk', () => { streamed = true })
|
||||||
|
ctx.on('agent/step-start', (subject) => {
|
||||||
|
if (subject === agent) disposalDone = handle.dispose()
|
||||||
|
})
|
||||||
|
|
||||||
|
send(agent, 'go')
|
||||||
|
await disposalDone
|
||||||
|
await agent.done
|
||||||
|
|
||||||
|
expect(streamed).toBe(false)
|
||||||
|
expect(adapter.requests).toHaveLength(0)
|
||||||
|
const turnEnd = agent.session.events.findLast(e => e.type === 'turn/end')
|
||||||
|
expect(turnEnd?.type === 'turn/end' && turnEnd.data.reason).toEqual({ kind: 'disposed' })
|
||||||
|
const types = agent.session.events.map(e => e.type)
|
||||||
|
expect(types.filter(t => t === 'step/start').length).toBe(types.filter(t => t === 'step/end').length)
|
||||||
|
})
|
||||||
|
|
||||||
it('cancel during the continuation window ends the turn aborted and runs no further step', async () => {
|
it('cancel during the continuation window ends the turn aborted and runs no further step', async () => {
|
||||||
// A continuation-waterfall listener cancels DURING the continuation decision
|
// A continuation-waterfall listener cancels DURING the continuation decision
|
||||||
// (the finished step's AbortController is already cleared), and votes to
|
// (the finished step's AbortController is already cleared), and votes to
|
||||||
|
|||||||
Reference in New Issue
Block a user