weiqingy opened a new pull request, #1129: URL: https://github.com/apache/flink-agents/pull/1129
Linked issue: #912 Stacked on #1128, which adds the effective-model hook this builds beside. Review that one first; the diff here shows its two commits until it merges. ### Purpose of change Nothing changes for a caller yet: no production code asks what this adds. It is the second step of wiring an agent `output_schema` through to the providers that can enforce it, and it removes a drift hazard the first step exposed. Whether a native schema reaches a provider depends on more than the model. The schema form decides it, and so do conditions that differ per provider: an api version floor on Azure, a caller supplied output config on Anthropic, bound tools on Gemini. Each connection already made that decision inside its own request path, where a caller cannot see it. Asking a connection to answer the same question separately would mean writing those conditions twice, and two copies drift silently, because a caller only ever sees the answer. #### Runtime flow Both connection bases gain a feasibility query: can this connection apply this schema, to a request built from these tools and parameters, with the effective model's capability set aside. The default is `false`, which suits a connection that translates no schema at all. Every connection with a native branch implements it, and each branch now calls the query instead of restating its conditions, so the branch and the answer cannot disagree. Capability stays a separate conjunct of each branch. The query is asked with the parameters the caller supplied, before a request path strips its own keys, because two connections read keys that a stripped copy no longer holds. #### Key decisions **Feasibility is asked separately from capability, and neither bounds the other.** A schema form a connection cannot translate is infeasible on a model it calls capable; a translatable form is feasible on a model it calls incapable. Merging them would have hidden one behind the other. **Each override answers from its own request path rather than from a copy of its conditions.** That is what the tests pin, and it is why the query is asked with the unstripped parameters. **Anthropic's JSON prefill keeps reading the live parameters** rather than sharing the query's snapshot, because prefill has to see the output config the branch derives, which the caller never supplied. ### Behavioral Semantics #### Interaction decisions | Connection | Conditions the query answers | Kept in the branch | |---|---|---| | OpenAI, Ollama, Bedrock, watsonx, Tongyi | schema form | capability | | Azure | schema form, api version floor | capability | | Anthropic | schema form, no caller supplied output config | capability | | Gemini | schema form, no bound tools | capability | #### Behavioral contracts 1. The query answers whether the connection could apply that schema to such a request, with the effective model's capability excluded. 2. It accepts a missing schema, missing tools and missing parameters without raising, so a caller can ask speculatively, and it reads the parameters without consuming them. 3. A `false` answer is not an error: the caller keeps the prompt fallback. 4. A `true` answer is not a promise the call succeeds. A connection may still raise once its branch has decided to apply, where the caller supplied a conflicting format, or where the schema cannot be rendered. 5. An override answers from the same logic its own request path uses. #### Failure behavior No new failure path, and one narrow change on invalid input. A branch consults the query before the capability predicate, so a request the query reports infeasible no longer evaluates that predicate. Where the model is not a string, four Python connections raised there and now send the request without a schema: OpenAI, Anthropic, Tongyi and Azure. Anthropic reaches that path with a translatable schema when the caller supplied an output config, and Azure when the api version is below its floor, since both conditions moved into the query. Ollama and watsonx are unaffected. Measured by driving every connection across 1032 request shapes on both trees: 70 cells differ, none of them carrying a valid or absent model, and all 696 that do are byte-identical. ### Tests | Contract | Tests | |---|---| | 1, 2, 3 | the base-class tests in both languages: default, missing inputs accepted, parameters not consumed | | 4 | each connection's contract states it; the conflicting-format raises are pinned by existing tests | | 5 | a binding test per connection, capturing what the branch actually does and comparing it with the answer | | the exclusion | a dedicated test per connection that capability is not folded into the answer | | the unstripped parameters | a test per stripping connection that the query sees keys the request path later removes | Coverage by risk. The failure that matters is an answer that stops matching the branch, which no assertion against a literal can catch. The binding tests capture the model or the request the branch produced and compare it with the query, so a branch that stops agreeing fails. The exclusion tests exist because a binding test is structurally blind to capability being folded into an override: it moves both sides together, which was measured on both languages. Suite results: Java api 428, chat-models common 6, openai 131, anthropic 103, bedrock 90, gemini 71, ollama 18, watsonx 51. Python 797 passed, 12 skipped. Not verified. No live provider was called. ### API New method on each chat-model connection base, `protected` in Java and public in Python, plus overrides. No user-facing API changes, and no behavior change for an existing caller beyond the invalid-model case above. ### 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]
