From a3200e1aab6e67c793d6f32bc5d91d2fcad796dc Mon Sep 17 00:00:00 2001 From: Enrico Ros Date: Fri, 3 Apr 2026 16:21:12 -0700 Subject: [PATCH] AIX: remove the pause_continue token stop reason, as we handle it in the server-side loop --- src/modules/aix/client/ContentReassembler.ts | 1 - src/modules/aix/server/api/aix.wiretypes.ts | 1 - .../chatGenerate/parsers/anthropic.parser.ts | 32 ++++++++++--------- tools/develop/llm-parameter-sweep/sweep.ts | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/modules/aix/client/ContentReassembler.ts b/src/modules/aix/client/ContentReassembler.ts index 2929ee93e..821195bdb 100644 --- a/src/modules/aix/client/ContentReassembler.ts +++ b/src/modules/aix/client/ContentReassembler.ts @@ -819,7 +819,6 @@ export class ContentReassembler { // normal completions 'ok': undefined, 'ok-tool_invocations': undefined, - 'ok-pause_continue': undefined, // issues: dialect, dispatch, or client 'cg-issue': 'issue', // interruptions diff --git a/src/modules/aix/server/api/aix.wiretypes.ts b/src/modules/aix/server/api/aix.wiretypes.ts index 923c74d8d..b98833957 100644 --- a/src/modules/aix/server/api/aix.wiretypes.ts +++ b/src/modules/aix/server/api/aix.wiretypes.ts @@ -686,7 +686,6 @@ export namespace AixWire_Particles { export type GCTokenStopReason = | 'ok' // clean, including reaching 'stop sequences' | 'ok-tool_invocations' // clean & tool invocations - | 'ok-pause_continue' // clean, but paused (e.g. Anthropic server tools like web search) - requires continuation // premature: | 'cg-issue' // [1][2] chat-generation issue (see CGIssueId, mostly a dispatch or dialect issue) | 'filter-content' // content filter (e.g. profanity) diff --git a/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts b/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts index 31b5a56c4..fe72c1215 100644 --- a/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts +++ b/src/modules/aix/server/dispatch/chatGenerate/parsers/anthropic.parser.ts @@ -422,7 +422,7 @@ export function createAnthropicMessageParser(): ChatGenerateParseFunction { Object.assign(responseMessage, delta); // -> Token Stop Reason - const tokenStopReason = _fromAnthropicStopReason(delta.stop_reason); + const tokenStopReason = _fromAnthropicStopReason(delta.stop_reason, 'message_delta'); if (tokenStopReason !== null) pt.setTokenStopReason(tokenStopReason); @@ -638,11 +638,6 @@ export function createAnthropicMessageParserNS(): ChatGenerateParseFunction { needsTextSeparator = hotFixAntInjectToolsTextSpacer; } - // -> Token Stop Reason - const tokenStopReason = _fromAnthropicStopReason(stop_reason); - if (tokenStopReason !== null) - pt.setTokenStopReason(tokenStopReason); - // -> Stats if (usage) { const elapsedTimeMilliseconds = Date.now() - parserCreationTimestamp; @@ -670,6 +665,11 @@ export function createAnthropicMessageParserNS(): ChatGenerateParseFunction { throw new DispatchContinuationSignal( _createAnthropicPauseTurnContinuation(content, container?.id), ); + + // -> Token Stop Reason (pause_turn already thrown above) + const tokenStopReason = _fromAnthropicStopReason(stop_reason, 'parser_NS'); + if (tokenStopReason !== null) + pt.setTokenStopReason(tokenStopReason); }; } @@ -938,7 +938,7 @@ function _createAnthropicPauseTurnContinuation( } -function _fromAnthropicStopReason(stopReason: AnthropicWire_API_Message_Create.Response['stop_reason']) { +function _fromAnthropicStopReason(stopReason: AnthropicWire_API_Message_Create.Response['stop_reason'], debugCaller: string) { switch (stopReason) { case 'end_turn': @@ -948,13 +948,6 @@ function _fromAnthropicStopReason(stopReason: AnthropicWire_API_Message_Create.R case 'tool_use': return 'ok-tool_invocations'; - /** - * https://docs.claude.com/en/api/handling-stop-reasons#pause-turn - * Used with server tools like web search when Claude needs to pause a long-running operation. - */ - case 'pause_turn': - return 'ok-pause_continue'; - case 'max_tokens': return 'out-of-tokens'; @@ -964,8 +957,17 @@ function _fromAnthropicStopReason(stopReason: AnthropicWire_API_Message_Create.R case 'refusal': return 'filter-refusal'; // Safety concerns - refusal to answer + case 'pause_turn': + // pause_turn hits this function from message_delta, but the return is irrelevant - + // message_stop will throw DispatchContinuationSignal before the tokenStopReason matters + // https://docs.claude.com/en/api/handling-stop-reasons#pause-turn + return null; + default: - console.warn(`_fromAnthropicStopReason: unknown stop reason: ${stopReason}`); + const _exhaustiveCheck: never = stopReason; + // fallthrough + case null: + console.warn(`_fromAnthropicStopReason(${debugCaller}): unexpected stop_reason: ${stopReason}`); return null; } } diff --git a/tools/develop/llm-parameter-sweep/sweep.ts b/tools/develop/llm-parameter-sweep/sweep.ts index a80f178ab..9c3520155 100644 --- a/tools/develop/llm-parameter-sweep/sweep.ts +++ b/tools/develop/llm-parameter-sweep/sweep.ts @@ -503,7 +503,7 @@ async function testParameterValue( // Check tokenStopReason for non-ok outcomes const stopReason = collector.tokenStopReason; - const isValidStop = !stopReason || stopReason === 'ok' || stopReason === 'ok-tool_invocations' || stopReason === 'ok-pause_continue'; + const isValidStop = !stopReason || stopReason === 'ok' || stopReason === 'ok-tool_invocations'; const isTruncated = stopReason === 'out-of-tokens'; const preview = collector.hasText