Skip to content

Route on tool-control requirements instead of dropping them in translation #1727

Open

Description

@BILLKISHORE

Problem

TranslationRequirements (internal/router/router.go:37) lets routing skip targets that cannot serve a request's audio, files, structured output and so on, with the off, shadow and enforce rollout. Tool controls are not in it. When a request carries one the target cannot express, the emitter drops it and the request goes out without the constraint.

Most open rows of #1385 are this one gap:

Control Source field Targets that cannot express it
Parallel limit parallel_tool_calls: false, disable_parallel_tool_use: true Gemini (case 11)
Tool subset tool_choice.allowed_tools with more than one tool Anthropic (case 4)
Strict tools function.strict, tool strict Gemini (case 9), some OpenAI compatible gateways
Caller restriction tool allowed_callers Gemini (case 13)

Each one can be patched in its emitter by returning an incompatibility error, which the proxy rescues to the Anthropic baseline. That works, but it decides after a model was already picked, and it only ever falls back to one model.

Proposal

  1. Add ParallelToolLimit, ToolSubset, StrictTools and RestrictedToolCallers to TranslationRequirements, set in RequestEnvelope.TranslationRequirements from the fields above.
  2. Teach the existing compatibility check which wire families honour each one.
  3. Ship behind the existing rollout: shadow first, so you can see how much traffic each requirement would move before enforcing.

Shadow mode also answers the open question on #1385 case 11: whether common clients send parallel_tool_calls: false by default and would lose Gemini under a strict reject.

Scope

One requirement per PR, starting with ParallelToolLimit. Each PR adds the field, the extraction, the family rule and table tests, with no change in off mode and no filtering in shadow mode.

I'll open the ParallelToolLimit PR next so there is something concrete to look at. If you'd rather go another way, I'll close it and do case 11 as a plain emitter reject instead.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions