weiqingy opened a new pull request, #1128: URL: https://github.com/apache/flink-agents/pull/1128
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_schema` through 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 and `effective_model_for(model_kwargs)` in Python answer which model the capability predicate judges for a given parameter map. The base returns the plain `model` entry. 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 | Connection | Model it sends | Model the predicate judges | Override | |---|---|---|---| | Azure OpenAI | the deployment | the backing model | yes | | OpenAI, OpenAI Responses, Anthropic, Gemini, Bedrock (Java) | `model`, else its default | same | yes | | Tongyi, watsonx (Python) | `model`, else its default | same | yes | | Ollama, watsonx (Java), OpenAI and Anthropic (Python) | `model` | same | no, the base answer is already right | | vLLM | inherits the OpenAI connection | same | no | #### Behavioral contracts 1. The hook returns the model the connection's own capability predicate judges for a given parameter map. 2. It accepts an absent, null or blank model and returns null rather than raising, so a caller can ask speculatively. 3. It reads the parameter map without consuming it, so asking does not change what the request would send. 4. A predicate must accept a null model without raising. A connection that classifies by model name reports such a model not capable; one whose capability belongs to the endpoint may report a name it has never seen as capable. #### 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 | Contract | Tests | |---|---| | 1 | `effectiveModelForNamesTheModelTheBuilderJudges` on Azure, OpenAI, Anthropic, Bedrock and Gemini; the Python binding tests on Azure, Tongyi, watsonx, OpenAI, Anthropic and Ollama | | 2 | the base-default and fallback tests in `BaseChatModelTest` and `test_chat_model_base.py`, over explicit, blank and absent models | | 3 | `testEffectiveModelForDoesNotConsumeTheBackingModel` | | 4 | the existing capability predicate tests in both languages, unchanged | Coverage 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 `protected` method 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-needed` - [x] `doc-not-needed` - [ ] `doc-included` ### Was this patch authored or co-authored using generative AI tooling? - [x] Yes - [ ] No Generated-by: Claude Code 2.1.272 (Claude Opus 5) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
