Skip to content

fix: tolerate partial Responses output items - #10108

Merged
DOsinga merged 1 commit into
aaif-goose:mainfrom
yyy33:relax-responses-output-items
Jul 1, 2026
Merged

fix: tolerate partial Responses output items#10108
DOsinga merged 1 commit into
aaif-goose:mainfrom
yyy33:relax-responses-output-items

Conversation

@yyy33

@yyy33 yyy33 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Some OpenAI-compatible Responses providers return usable response.completed events while omitting envelope fields on final output items, such as id and status.

For example, a provider may return a final output item like:

{"type":"message","role":"assistant","content":[...]}

or a function call like:

{"type":"function_call","call_id":"call_abc","name":"shell","arguments":"{...}"}

The current streaming parser rejects those final response.completed events before Goose can consume the data that is actually needed. This causes the UI to display the streamed answer/tool call and then append a final stream decode error.

This change relaxes only the final output item envelope fields that Goose does not require:

  • reasoning.id is optional
  • message.id and message.status are optional
  • function_call.id and function_call.status are optional

Function calls still require an identifier before producing a tool request. Goose now uses call_id first, then falls back to id, and returns an error if both are missing. This avoids synthesizing empty tool call identifiers.

This mirrors the practical handling used by OpenCode / AI SDK style Responses streaming, where function call handling is keyed by call_id and the final completed event is not treated as invalid solely because envelope fields are absent.

Tests

cargo test -p goose-providers

Result: 342 passed locally.

Some OpenAI-compatible Responses providers omit envelope fields such as output item id/status in the final response.completed event while still returning usable content, reasoning summaries, usage, and function call call_id values.

Relax the final output item deserialization for message, reasoning, and function_call items to match the fields Goose actually consumes. Function calls still require either call_id or id before producing a tool request, so Goose does not synthesize empty tool call identifiers.

Add regression coverage for message and function_call output items that omit id/status, plus a guard that rejects function calls missing both call_id and id.

@DOsinga DOsinga left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a clean, well-scoped fix — thanks! Making the streaming ResponseOutputItemInfo fields optional to match the non-streaming ResponseOutputItem is the right call, and the tests exercise the real parser end-to-end.

One thing worth following up on (here or in a separate PR): process_streaming_output_items and responses_api_to_message are now structurally the same 3-variant match, and this PR improved the function-call id handling on the streaming side (call_id.unwrap_or(id) + proper error) but left the non-streaming path at responses_api_to_message still using call_id.clone().or_else(|| id.clone()).unwrap_or_default() — i.e. the empty-string-id footgun you're fixing here still exists on that twin. It'd be nice to either apply the same fix there or unify the two into a shared helper so they can't drift. Not a blocker for this PR.

Approving.

@DOsinga
DOsinga added this pull request to the merge queue Jul 1, 2026
Merged via the queue into aaif-goose:main with commit 9165e27 Jul 1, 2026
23 checks passed
lifeizhou-ap added a commit that referenced this pull request Jul 1, 2026
* main:
  fix: tolerate partial Responses output items (#10108)
  fix(databricks): always use OpenAI-compatible "parameters" for tools (#10101)
  fix(providers): unescape shell-escaped image paths (#10098)
  chore(deps-dev): bump js-yaml from 4.1.1 to 4.2.0 in /documentation (#10107)
  regenerate schema (#10166)
  chore(deps): bump actions/stale from 10.1.1 to 10.3.0 (#10050)
  chore(deps): bump actions/attest-build-provenance from 4.1.0 to 4.1.1 (#10049)
  chore(deps): bump pnpm/action-setup from 6.0.8 to 6.0.9 (#9892)
  Update EmpirioLabs provider display name to EmpirioLabs AI (#10126)
  add declarative provider support to goose-providers crate (#9992)
  fix(desktop): seed mac auto-update config (#10132)
  Update release testing instructions (#10122)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants