Skip to content

fix(provider): never force a tool choice on models that refuse one - #377

Merged
filipeforattini merged 1 commit into
mainfrom
forced-tool-audit
Sep 23, 2026
Merged

filipeforattini merged 1 commit into
mainfrom
forced-tool-audit

Conversation

@filipeforattini

@filipeforattini filipeforattini commented Sep 23, 2026 •

Copy link
Copy Markdown

Follow-up to #369 (ProviderTransform.supportsForcedToolChoice): audit of every place that forces a tool on a provider.

Changed

  • packages/redcode/src/session/llm.ts: the one boundary every legacy session request crosses (AI SDK and native runtimes) now downgrades toolChoice: "required" to "auto" for models that refuse a forced tool choice, using the model's declared router parameters. prompt.ts already did this for structured output; this covers any other caller.
  • packages/redcode/src/agent/agent.ts: Agent.generate used generateObject, which forces a JSON tool wherever the provider lacks native structured output (Vertex Anthropic sets supportsNativeStructuredOutput: false; gateway/combo ids without claude- fall back to the JSON tool). For models that refuse a forced tool choice it now uses prompted JSON (generateText) validated against the schema, with one repair retry.

Audited, already safe

  • Semantic.generate/transform (core): tools: [], no tool choice.
  • Title, summary, goal judge (legacy + core): plain text, no tool choice.
  • Legacy compaction: toolChoice: "none" or undefined. Core runner: "none" on the last step only.
  • Structured output in prompt.ts: already guarded by fix(provider): complete support for Claude Opus 5.5 and GPT-6 #369.
  • packages/llm protocols (anthropic-messages, openai, bedrock, gemini): faithfully lower whatever tool choice the request carries; the guard lives at the call sites.
  • LLM.generateObject in packages/llm forces a named tool but has no production callers (examples/tests only).

Tests

  • test/session/llm.test.ts: Opus 5.5 with toolChoice: "required" sends tool_choice: {type: "auto"}; Opus 4.6 still sends {type: "any"}.
  • test/agent/generate.test.ts: Opus 5.5 agent generation sends no tools/tool_choice, repairs a non-JSON reply once and decodes a fenced JSON reply.

Validated by CI only.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@filipeforattini
filipeforattini enabled auto-merge (squash) September 23, 2026 05:59
@filipeforattini
filipeforattini merged commit 8110a0c into main Sep 23, 2026
22 checks passed
@filipeforattini
filipeforattini deleted the forced-tool-audit branch September 23, 2026 06:05
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.

1 participant