Conversation
A connection resolves the model it calls from the request parameters, and some connections judge a different name than the one they send: an Azure deployment is a user-chosen name, while the capability predicate has to see the model behind it. The predicate's contract said its argument was the model actually being called, which would walk any caller outside the connection into asking about the wrong name. Add effectiveModelFor, which answers which model the predicate judges for a given parameter map, and override it wherever a connection resolves that name differently from reading the model parameter: the default model fallbacks, and Azure's backing model. Reword the contract so a caller asks that hook rather than substituting its own resolution, and so it no longer claims an unrecognized model reports false, which is untrue of the connections whose capability belongs to the endpoint. Nothing calls the hook yet, so behavior is unchanged. The tests pin the hook against the model each request builder actually feeds the predicate, which a hook-side assertion cannot catch. Generated-by: Claude Code 2.1.272 (Claude Opus 5)
Mirror the Java hook on the Python connection base. A connection resolves the model it calls from the chat kwargs, and some connections judge a different name than the one they send: an Azure deployment is a user-chosen name, while the capability predicate has to see the model behind it, and Tongyi and watsonx fall back to their own default when the caller omits the key. Add effective_model_for, which answers which model the predicate judges for a given kwargs mapping, and override it on those three connections. Reword the capability docstring to the contract the Java side now states: a caller outside the connection asks that hook rather than resolving the model itself, the predicate accepts a missing model without raising, and a connection whose capability belongs to the endpoint may report support for a name it has never seen. Nothing calls the hook yet, so behavior is unchanged. The tests pin each hook against the model its own request path feeds the predicate, which a hook-side assertion cannot catch. Generated-by: Claude Code 2.1.272 (Claude Opus 5)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Linked issue: #912
Purpose of change
Nothing changes for a caller: no production code calls what this adds. It is the first step of wiring an agent
output_schemathrough to the providers that can enforce it, and it removes a trap that step would otherwise fall into.A chat model connection resolves the model it calls from the request parameters, and some connections judge a different name than the one they send. An Azure deployment is a name the user chooses, while the capability predicate has to see the model behind it. Several connections fall back to their own default when the caller omits the model. Until now the predicate's contract said its argument was "the model actually being called", which would walk any caller outside the connection into asking about the wrong name.
Runtime flow
effectiveModelFor(modelParams)in Java andeffective_model_for(model_kwargs)in Python answer which model the capability predicate judges for a given parameter map. The base returns the plainmodelentry. A connection overrides it wherever its own request builder resolves that name differently: Azure returns the backing model rather than the deployment, and the connections with a default model return that default when the key is absent or blank.Key decisions
The fallback lives in each override, not in the base. In both languages the default model is private to the subclass, so the base cannot reach it. That is also what normalizes the two languages, which previously disagreed about what an omitted model means.
The hook sits on the connection. Only the connection knows how its own request builder resolves the model, and keeping the two together is what the tests pin.
The contract no longer promises a universal answer for an unknown model. Connections whose capability belongs to the endpoint rather than the model report support for names they have never seen, so the contract now separates them from the connections that classify by name.
Behavioral Semantics
Interaction decisions
model, else its defaultmodel, else its defaultmodelBehavioral contracts
Failure behavior
No new failure path. Nothing calls the hook, a missing model is an answer rather than an error, and the connections that raise on an unresolvable model during request building continue to do so unchanged.
Tests
effectiveModelForNamesTheModelTheBuilderJudgeson Azure, OpenAI, Anthropic, Bedrock and Gemini; the Python binding tests on Azure, Tongyi, watsonx, OpenAI, Anthropic and OllamaBaseChatModelTestandtest_chat_model_base.py, over explicit, blank and absent modelstestEffectiveModelForDoesNotConsumeTheBackingModelCoverage by risk. The failure that matters is a hook that answers about a model the request will not use, which no assertion against a literal can catch. The binding tests capture the model the request builder actually feeds the predicate and compare it with the hook, so a builder that later gains or loses a fallback without a matching override fails them. That was verified by mutation: changing a builder alone leaves every other test green.
Not verified. No live provider was called. There is no end-to-end evidence that a gate reads the right model, because the gate arrives in a later step; what is proven here is agreement between each hook and its own request builder.
API
New
protectedmethod on the Java connection base and its public Python counterpart, both on a beta base class, plus overrides. No user-facing API changes and no behavior change for an existing caller: the hook has no production caller in this PR.Documentation
doc-neededdoc-not-neededdoc-includedWas this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code 2.1.272 (Claude Opus 5)