Skip to content

fix(opencode): native output_format structured output (works with thinking) - #32637

Closed
lu-zero wants to merge 1 commit into
anomalyco:devfrom
lu-zero:luca/structured-output-thinking
Closed

lu-zero wants to merge 1 commit into
anomalyco:devfrom
lu-zero:luca/structured-output-thinking

Conversation

@lu-zero

@lu-zero lu-zero commented Jun 17, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #15226

Type of change

  • Bug fix

What does this PR do?

format: json_schema forces tool_choice: "required", which Anthropic rejects with thinking on (400 "Thinking may not be enabled when tool_choice forces tool use.").

For Anthropic, this uses the provider's native output_format instead (output: Output.object({schema}) + structuredOutputMode: "outputFormat"), skipping the forced tool. The model returns the JSON as text, parsed into message.structured. Not a forced tool, so it coexists with thinking. Non-Anthropic providers keep the existing path.

See also #29565, that workarounds for non-anthropic providers.

How did you verify your code works?

  • bun run typecheck clean; bun test test/session/llm.test.ts → 27 pass (added a test asserting a thinking Anthropic model sends output_config.format and no forced tool_choice).
  • Ran opencode serve from source against the live Anthropic API with thinking enabled + format: json_schema: no 400, reasoning emitted, message.structured populated.

Screenshots / recordings

N/A — provider request-shape change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Based on my search results, I found one related PR that is mentioned in the PR description itself:

Related PR (not a duplicate):

Additionally, there's:

PR #32637 appears to be a targeted fix for the specific issue (#15226) where Anthropic's native output_format is leveraged to support thinking mode with structured output, whereas the related PRs address similar but distinct aspects of the problem.

@lu-zero
lu-zero marked this pull request as ready for review June 17, 2026 06:33
@lu-zero
lu-zero force-pushed the luca/structured-output-thinking branch from 42b8d47 to 2828ca1 Compare June 29, 2026 13:39
…nking)

opencode implements structured output (`format: { type: "json_schema" }`)
by injecting a `StructuredOutput` tool and forcing `tool_choice: "required"`.
Anthropic and several reasoning providers reject thinking + forced tool use
with `400 "Thinking may not be enabled when tool_choice forces tool use."`,
so structured output either 400s or the model can't reason.

Route `json_schema` structured output through the AI SDK's native structured
output for providers that support it (Anthropic via `@ai-sdk/anthropic`'s
`structuredOutputMode: "outputFormat"`), exactly as the Anthropic SDK does it
(`output_format`, not a forced tool). The forced-tool path is kept as a
fallback for non-native providers, so their behavior is unchanged (strict
superset).

- llm.ts: add `structuredOutput` to `StreamInput`; on native-capable providers
  pass `output: Output.object(jsonSchema(...))` and merge
  `{ anthropic: { structuredOutputMode: "outputFormat" } }` into provider
  options; defensively downgrade a forced `tool_choice` to `auto` on the native
  path so the two can never collide. Add `supportsNativeStructuredOutput`.
- prompt.ts: skip the `StructuredOutput` tool, the coercion system prompt, and
  the forced `tool_choice` on the native path; thread `structuredOutput`
  through `handle.process`; parse the assistant text into `message.structured`,
  degrading to the existing `StructuredOutputError` on parse failure.
- test: structured output on a thinking-enabled Anthropic model requests
  `output_config.format` and does NOT force `tool_choice`.

Refs anomalyco#15226. Alternative to anomalyco#29565 (which relaxes the forced
tool_choice required->auto for thinking surfaces); this is the native
output_format approach.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lu-zero
lu-zero force-pushed the luca/structured-output-thinking branch from 2828ca1 to ab5ad27 Compare June 29, 2026 13:54
@github-actions

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

@poja-soptim

Copy link
Copy Markdown

I think this PR is still relevant..

@lu-zero

lu-zero commented Jul 21, 2026

Copy link
Copy Markdown
Author

I'm happy to reroll it but it needs reviewers...

@lu-zero

lu-zero commented Aug 24, 2026

Copy link
Copy Markdown
Author

Rerolled as #44634 — rebased onto current dev, typecheck and tests green. The forced tool_choice: "required" is still present at packages/opencode/src/session/prompt.ts:1285, so this is still a live bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tool_choice: 'required' incompatible with thinking-enabled models when using structured output

2 participants