Repository navigation
Conversation
… (TypeScript) finalizeResponsesStream let an in-flight function_call override the maxTokens stop reason set from response.incomplete, so a truncated tool call surfaced as toolUse and was parsed/executed instead of raising MaxTokensError. Port of strands-agents#4139 to strands-ts. Fixes strands-agents#4158
| }) | ||
| const model = new OpenAIModel({ api: 'responses', client }) | ||
| const events = await collectIterator(model.stream([new Message({ role: 'user', content: [new TextBlock('x')] })])) | ||
| const stop = events.find((e: any) => e.type === 'modelMessageStopEvent') as any |
There was a problem hiding this comment.
Issue: The regression test covers the partial-arguments case, but issue #4158 describes a second distinct scenario — the function call cut off before any argument bytes stream (TRUNCATED_NO_ARGS: output_item.added then response.incomplete, with no function_call_arguments.delta). That path previously executed the tool with input: {} and ended with endTurn, which is arguably the more dangerous of the two regressions.
Suggestion: Add a second test case (or a parameterized case) for the no-argument-bytes variant asserting stopReason === 'maxTokens'. Both variants share the same finalizer code path, so this is non-blocking, but it locks in the behavior for the silent-execution case the issue specifically called out.
|
Assessment: Approve Minimal, correct, well-scoped bug fix. The one-line precedence change makes the Responses adapter agree with the chat adapter ( Review Categories
Clean, focused fix that closes a real parity gap between the two OpenAI adapters. |
|
Approval noted on a0b5774; no changes were requested, so none were made. |
|
State check, then one question.
Since |
|
Sorry, this one is on us. Your fix was approved in September and we never merged it, then shipped the same change in #5749 (released in typescript/v1.24.0) without linking back here. Closing as superseded. Thank you for the clear repro and the fix. |
Pull request was closed
Description
TypeScript port of #4139.
When the Responses API stream emits
response.incompletewithincomplete_details.reason === 'max_output_tokens'after afunction_calloutput item has started,finalizeResponsesStreamreportedstopReason: 'toolUse'instead of'maxTokens'.Model.streamAggregatedthen either threw a genericModelError('unable to parse tool input JSON')(partial arguments) or executed the tool withinput: {}(cut off before any argument bytes), instead of throwingMaxTokensError.Root cause:
state.toolCallsis populated onresponse.output_item.added, i.e. before any arguments have streamed, and the finalizer unconditionally overrode the stop reason with'toolUse'whenever that map was non-empty. So any function call in flight when the response was cut off won over the'maxTokens'set by theresponse.incompletehandler.Changes:
responses-adapter.ts: only override with'toolUse'when the stop reason is not already'maxTokens'. This matches the chat adapter, which mapsfinish_reason: 'length'to'maxTokens'regardless of accumulated tool-call deltas, and the Python Responses model after fix(openai): report max_tokens when a Responses function call is cut off #4139.responses.test.ts: add a regression test for a function call cut off bymax_output_tokens, assertingstopReason === 'maxTokens'.Related Issues
Fixes #4158
Documentation PR
No documentation change needed.
Type of Change
Bug fix
Testing
npx vitest run --project unit-node src/models/openai/__tests__/responses.test.ts: 61 passed (60 existing + 1 new). The new test fails onmainwithout the source change (expected 'toolUse' to be 'maxTokens') and passes with it.npx vitest run --project unit-node(full strands-ts unit suite): 165 files, 4560 tests passed, no type errors.eslint src/models/openai,prettier --checkon the two changed files,tsc --noEmit --project src/tsconfig.json: clean.Repro from [BUG] OpenAIModel (Responses, TypeScript) reports toolUse instead of maxTokens when a function call is cut off by max_output_tokens #4158 now prints
stopReason: 'maxTokens'at the model level, and the agent throwsMaxTokensErrorfor both the partial-arguments and no-arguments-yet cases instead of parsing or executing the truncated tool call.I ran
hatch run prepare(this is a strands-ts-only change; I ran the correspondingnpm runlint / format / type-check / test commands above)Checklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.