Merge pull request #103 from JackChen-me/fix/issue-98-run-error-propagation

fix: propagate error events in AgentRunner.run()
This commit is contained in:
JackChen 2026-04-12 22:22:22 +08:00 committed by GitHub
commit 97c5e457dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 87 additions and 0 deletions

View File

@ -490,6 +490,8 @@ export class AgentRunner {
for await (const event of this.stream(messages, options)) {
if (event.type === 'done') {
Object.assign(accumulated, event.data)
} else if (event.type === 'error') {
throw event.data
}
}

View File

@ -0,0 +1,85 @@
import { describe, it, expect } from 'vitest'
import { Agent } from '../src/agent/agent.js'
import { AgentRunner } from '../src/agent/runner.js'
import { ToolRegistry } from '../src/tool/framework.js'
import { ToolExecutor } from '../src/tool/executor.js'
import type { AgentConfig, LLMAdapter, LLMMessage } from '../src/types.js'
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
/** Adapter whose chat() always throws. */
function errorAdapter(error: Error): LLMAdapter {
return {
name: 'error-mock',
async chat(_messages: LLMMessage[]) {
throw error
},
async *stream() {
/* unused */
},
}
}
function buildAgentWithAdapter(config: AgentConfig, adapter: LLMAdapter) {
const registry = new ToolRegistry()
const executor = new ToolExecutor(registry)
const agent = new Agent(config, registry, executor)
const runner = new AgentRunner(adapter, registry, executor, {
model: config.model,
systemPrompt: config.systemPrompt,
maxTurns: config.maxTurns,
agentName: config.name,
})
;(agent as any).runner = runner
return agent
}
const baseConfig: AgentConfig = {
name: 'test-agent',
model: 'mock-model',
systemPrompt: 'You are a test agent.',
}
// ---------------------------------------------------------------------------
// Tests — #98: AgentRunner.run() must propagate errors from stream()
// ---------------------------------------------------------------------------
describe('AgentRunner.run() error propagation (#98)', () => {
it('LLM adapter error surfaces as success:false in AgentRunResult', async () => {
const apiError = new Error('API 500: internal server error')
const agent = buildAgentWithAdapter(baseConfig, errorAdapter(apiError))
const result = await agent.run('hello')
expect(result.success).toBe(false)
expect(result.output).toContain('API 500')
})
it('AgentRunner.run() throws when adapter errors', async () => {
const apiError = new Error('network timeout')
const adapter = errorAdapter(apiError)
const registry = new ToolRegistry()
const executor = new ToolExecutor(registry)
const runner = new AgentRunner(adapter, registry, executor, {
model: 'mock-model',
systemPrompt: 'test',
agentName: 'test',
})
await expect(
runner.run([{ role: 'user', content: [{ type: 'text', text: 'hi' }] }]),
).rejects.toThrow('network timeout')
})
it('agent transitions to error state on LLM failure', async () => {
const agent = buildAgentWithAdapter(baseConfig, errorAdapter(new Error('boom')))
await agent.run('hello')
expect(agent.getState().status).toBe('error')
})
})