mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-22 08:38:08 +02:00
fix(cli): align ingest step counter with SDK num_turns
The Claude Code runtime counted every SDKAssistantMessage with parent_tool_use_id === null as a step, but the SDK emits extra messages within a single num_turns round-trip — `stop_reason: 'pause_turn'` continuations and errored partials it retries internally. The local counter then outran maxTurns and the ingest HUD rendered confusing ratios like `step 69/40`. Filter both cases in collectResult so stepIndex tracks num_turns and stays bounded by the work-unit stepBudget.
This commit is contained in:
parent
a94f35800a
commit
20454bcb8f
3 changed files with 77 additions and 3 deletions
|
|
@ -415,6 +415,64 @@ describe('ClaudeCodeKtxLlmRuntime', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('counts only assistant turns the SDK counts toward num_turns', async () => {
|
||||
const assistantMessage = (
|
||||
overrides: Partial<Extract<SDKMessage, { type: 'assistant' }>> & { uuid: string },
|
||||
): SDKMessage =>
|
||||
({
|
||||
type: 'assistant',
|
||||
message: { role: 'assistant', content: [], stop_reason: 'end_turn' },
|
||||
parent_tool_use_id: null,
|
||||
session_id: 'session-id',
|
||||
...overrides,
|
||||
}) as unknown as SDKMessage;
|
||||
|
||||
const query = vi.fn((_input: any) =>
|
||||
stream([
|
||||
initMessage(),
|
||||
assistantMessage({
|
||||
uuid: '00000000-0000-4000-8000-0000000000a1',
|
||||
error: 'max_output_tokens',
|
||||
}),
|
||||
assistantMessage({
|
||||
uuid: '00000000-0000-4000-8000-0000000000a2',
|
||||
message: { role: 'assistant', content: [], stop_reason: 'pause_turn' } as never,
|
||||
}),
|
||||
assistantMessage({ uuid: '00000000-0000-4000-8000-0000000000a3' }),
|
||||
{
|
||||
type: 'assistant',
|
||||
message: { role: 'assistant', content: [], stop_reason: 'end_turn' },
|
||||
parent_tool_use_id: 'tool-use-1',
|
||||
uuid: '00000000-0000-4000-8000-0000000000a4',
|
||||
session_id: 'session-id',
|
||||
} as unknown as SDKMessage,
|
||||
resultMessage({ subtype: 'success', terminal_reason: 'completed' }),
|
||||
]),
|
||||
);
|
||||
const runtime = new ClaudeCodeKtxLlmRuntime({
|
||||
projectDir: '/tmp/project',
|
||||
modelSlots: { default: 'sonnet' },
|
||||
query,
|
||||
env: {},
|
||||
});
|
||||
const onStepFinish = vi.fn();
|
||||
|
||||
await expect(
|
||||
runtime.runAgentLoop({
|
||||
modelRole: 'default',
|
||||
systemPrompt: 'system',
|
||||
userPrompt: 'user',
|
||||
toolSet: {},
|
||||
stepBudget: 40,
|
||||
telemetryTags: { operationName: 'test' },
|
||||
onStepFinish,
|
||||
}),
|
||||
).resolves.toEqual({ stopReason: 'natural' });
|
||||
|
||||
expect(onStepFinish).toHaveBeenCalledTimes(1);
|
||||
expect(onStepFinish).toHaveBeenCalledWith({ stepIndex: 1, stepBudget: 40 });
|
||||
});
|
||||
|
||||
it('logs and ignores onStepFinish callback errors', async () => {
|
||||
const query = vi.fn((_input: any) =>
|
||||
stream([
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue