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]

Reply via email to