Skip to content

fix(anthropic): default tool_choice to auto when only parallel_tool_calls is set - #1292

Merged
tbille merged 1 commit into
mozilla-ai:mainfrom
JamMaster1999:fix/anthropic-tool-choice-parallel-only
Aug 18, 2026
Merged

tbille merged 1 commit into
mozilla-ai:mainfrom
JamMaster1999:fix/anthropic-tool-choice-parallel-only

Conversation

@JamMaster1999

@JamMaster1999 JamMaster1999 commented Aug 16, 2026 •

Copy link
Copy Markdown
Contributor

Description

Anthropic and OpenAI set tool calling differently:

OpenAI (chat completions) Anthropic (messages)
may/must the model call a tool? tool_choice: "auto" (default), "required", "none", or a dict naming one function tool_choice object: {"type": "auto"}, {"type": "any"} (= "required"), {"type": "tool", "name": ...}, {"type": "none"}
may it return several calls at once? parallel_tool_calls, a separate top-level boolean no separate field; disable_parallel_tool_use lives inside the tool_choice object

So _convert_tool_choice has two jobs:

  • map the values,
  • carry parallel_tool_calls into disable_parallel_tool_use inside whatever tool_choice object it sends.

The corner case is a caller who passes only parallel_tool_calls. Anthropic has no standalone field for it, so the converter must synthesize a tool_choice object just to carry the flag.

The neutral carrier is {"type": "auto"}: identical model behavior to not mentioning tool choice at all. What happens today instead:

  • the converter defaults the missing tool_choice to "any": Anthropic's "must call a tool" mode. So a caller who only asked about parallelism gets forced tool use;
  • the call site gates on truthiness (if params.tool_choice or params.parallel_tool_calls), so parallel_tool_calls=False never reaches the converter at all (the restriction is silently dropped).

The fix is two lines: default the synthesized tool_choice to "auto" and gate on is not None.
With the fix, parallel_tool_calls=False alone sends {"type": "auto", "disable_parallel_tool_use": true}. Explicit tool_choice values ("auto"/"required"/dict form) are unchanged.

Example

llm = AnyLLM.create("anthropic")
resp = await llm.acompletion(
    model="claude-haiku-4-5",
    messages=[{"role": "user", "content": "Just say the word 'hello'. Do not use any tools."}],
    tools=[get_weather_tool],
    parallel_tool_calls=True,
)

Before (live, main): the model is not allowed to reply in text, so it fabricates a tool call:

content:    ""
tool_calls: [get_weather(arguments='{"city": "hello"}')]

After (live, this branch):

content:    "Hello."
tool_calls: None

PR Type

  • 🐛 Bug Fix

Relevant issues

Related: #646 (fixed the dict-form translation; this fixes the unset-default path)

Checklist

  • I understand the code I am submitting.
  • I have added unit tests that prove my fix/feature works
  • I have run this code locally and verified it fixes the issue.
  • New and existing tests pass locally
  • Documentation was updated where necessary (not applicable)
  • I have read and followed the contribution guidelines
  • AI Usage:
    • No AI was used.
    • AI was used for drafting/refactoring.
    • This is fully AI-generated.

AI Usage Information

  • AI Model used: Claude (Fable 5)

  • AI Developer Tool used: Claude Code

  • Any other info you'd like to share:

  • I am an AI Agent filling out this form (check box if true)

Summary by CodeRabbit

  • Bug Fixes

    • Improved Anthropic tool-use configuration when parallel tool calls are enabled without an explicit tool choice.
    • Tool selection now defaults to automatic mode, allowing the provider to choose whether tools should be used.
    • Explicit tool-choice settings, including disabled or otherwise falsy values, are now handled correctly.
  • Tests

    • Added coverage for parallel tool calls with both enabled and disabled settings.

@coderabbitai

coderabbitai Bot commented Aug 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ba39304f-de04-46cf-a1a2-9711be317b7b

📥 Commits

Reviewing files that changed from the base of the PR and between c0f3ffb and 99ae569.

📒 Files selected for processing (2)
  • src/any_llm/providers/anthropic/utils.py
  • tests/unit/providers/test_anthropic_provider.py

Included review availability: Your plan includes up to 8 reviews per rolling hour; 6 remain after this review.


Walkthrough

The Anthropic provider now defaults unspecified tool choice to "auto". It also processes explicitly provided falsy settings and adds parametrised coverage for parallel_tool_calls without tool_choice.

Changes

Anthropic tool choice conversion

Layer / File(s) Summary
Tool choice conversion and validation
src/any_llm/providers/anthropic/utils.py, tests/unit/providers/test_anthropic_provider.py
The converter defaults unspecified tool choice to "auto" and processes settings when either option is not None. Parametrised tests cover parallel tool call behaviour without an explicit tool choice.

Suggested reviewers: njbrake

Merge Risk: ⚪ Minimal · up to 99ae5

This is a localized Anthropic tool-choice fix with tests added, and no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main Anthropic tool-choice fix when only parallel_tool_calls is set.
Description check ✅ Passed The description explains the bug, fix, expected behaviour, tests, issue reference, checklist, and AI usage information.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Aug 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
src/any_llm/providers/anthropic/utils.py 89.74% <100.00%> (-4.71%) ⬇️

... and 31 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tbille
tbille merged commit cbe5d82 into mozilla-ai:main Aug 18, 2026
16 checks passed
tbille pushed a commit that referenced this pull request Aug 18, 2026
## Description
Bedrock and SageMaker `_convert_params` gated `temperature` and `top_p`
on truthiness, so `0.0` was dropped and the request used the model
default (e.g. Claude 1.0 on Bedrock) instead of greedy decoding. Other
providers already use `is not None`.

`max_tokens` and `stop` stay on truthiness: `0` and empty sequences are
rejected by both providers.

## PR Type
- Bug Fix

## Relevant issues
No open issue. Distinct from #1292 (Anthropic tool_choice) and our
#1296/#1297/#1298.

## Checklist
- [x] I understand the code I am submitting.
- [x] I have added unit tests that prove my fix/feature works
- [x] I have run this code locally and verified it fixes the issue.
- [x] New and existing tests pass locally
- [x] Documentation was updated where necessary
- [x] I have read and followed the contribution guidelines
- [x] **AI Usage:**
    - [x] This is fully AI-generated.

## AI Usage Information
- AI Model used: Claude Opus 5
- AI Developer Tool used: Cursor cloud agent
- [x] I am an AI Agent filling out this form (check box if true)

`uv run pytest tests/unit` → 2161 passed, 69 skipped. Target files fail
on current main (`KeyError: inferenceConfig` / missing keys) and pass
after.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* AWS Bedrock and SageMaker integrations now correctly honour explicitly
configured zero values for temperature and top-p sampling parameters.
  * Unset sampling parameters continue to be omitted from requests.

* **Tests**
* Added coverage confirming zero-valued parameters are forwarded and
unset values are excluded.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
tbille pushed a commit that referenced this pull request Aug 18, 2026
## Description
Non-streaming _convert_response mapped only text, tool_use, and
thinking, then raised ValueError on anything else. Anthropic returns
redacted_thinking when safety flags part of an extended-thinking trace,
so completion() crashed even though the rest of the answer was fine. The
streaming converter already ignores unmapped block types.

Skip redacted_thinking quietly; warn and skip other unmapped types.
Shared by anthropic, azureanthropic, vertexaianthropic, and meta. Tests:
uv run pytest tests/unit -> 2158 passed.

Distinct from #1292 and #1296-#1302.

## PR Type
- Bug Fix

## Relevant issues
No open issue.

## Checklist
- [x] I understand the code I am submitting.
- [x] I have added unit tests that prove my fix/feature works
- [x] I have run this code locally and verified it fixes the issue.
- [x] New and existing tests pass locally
- [x] Documentation was updated where necessary
- [x] I have read and followed the contribution guidelines
- [x] **AI Usage:**
    - [x] This is fully AI-generated.

## AI Usage Information
- AI Model used: Claude Opus 5
- AI Developer Tool used: Cursor cloud agent
- [x] I am an AI Agent filling out this form (check box if true)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
JamMaster1999 added a commit to JamMaster1999/any-llm that referenced this pull request Aug 18, 2026
Brings in upstream's merges of our mozilla-ai#1291/mozilla-ai#1292/mozilla-ai#1310 plus mozilla-ai#1297, mozilla-ai#1299,
mozilla-ai#1301, mozilla-ai#1302, mozilla-ai#1303, mozilla-ai#1305. Carried-until-merged fork work stays:
mozilla-ai#1294 (gemini reasoning_effort=none), mozilla-ai#1308 (aresponses timeout),
mozilla-ai#1309 (gemini native tool dicts), and the mozilla-ai#1300 carry.
One conflict in tests/unit/test_responses.py: kept our mozilla-ai#1308 timeout
test next to upstream's flatten test. Unit suite: 2234 passed.

Claude-Session: https://claude.ai/code/session_018D3FGNvb1hRZQmsXFoA44J
@JamMaster1999
JamMaster1999 deleted the fix/anthropic-tool-choice-parallel-only branch August 19, 2026 19:43
@github-actions github-actions Bot added the 1.27.0 Included in release 1.27.0 label Sep 3, 2026
JamMaster1999 added a commit to JamMaster1999/any-llm that referenced this pull request Sep 3, 2026
…mozilla-ai#1319

Upstream has merged mozilla-ai#1291, mozilla-ai#1292, mozilla-ai#1308, mozilla-ai#1309, mozilla-ai#1310, mozilla-ai#1317, mozilla-ai#1318, mozilla-ai#1320,
mozilla-ai#1325 and mozilla-ai#1352 in their final form, so the fork's own copies are dropped in
favour of upstream's. The tree is exactly upstream main plus the two fixes
still open there: gemini reasoning_effort="none" (mozilla-ai#1294) and closing the
provider stream when the wrapped stream closes (mozilla-ai#1319).

Claude-Session: https://claude.ai/code/session_01MmJSSofg7Lk7nBKZZyKV7w

This branch was previously deployed

1 inactive deployment
integration-tests — 99ae5699 Deployed Aug 18, 2026 by JamMaster1999 via run-docs-tests #2446
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.27.0 Included in release 1.27.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants