Fix: Drop the Gemini version floor at the CFC gate in Runner - #219
Open
AmaadMartin wants to merge 3 commits into
Open
Fix: Drop the Gemini version floor at the CFC gate in Runner#219AmaadMartin wants to merge 3 commits into
AmaadMartin wants to merge 3 commits into
Conversation
added 3 commits
August 10, 2026 22:02
The gate tested `model_name.startswith('gemini-2')`, so it raised
ValueError on ADK's own LlmAgent.DEFAULT_LIVE_MODEL
('gemini-live-2.5-flash-native-audio'), on every Gemini 3 id, and on
path- or provider-prefixed ids. Compositional Function Calling routes
through the Gemini Live API, so the gate rejected the model family the
feature exists for.
Commit 745de0a moved every production caller onto is_gemini_model and
deprecated the version helpers. This was the last version-bucketing
site. The gate now asks the same question as the BuiltInCodeExecutor it
installs two lines later: is this a Gemini model, or is
ADK_DISABLE_GEMINI_MODEL_ID_CHECK set. Non-Gemini models still fail
fast with the unchanged message.
CI compares mypy output against main and fails on new errors. Every test method now declares its return type, and the live-turn test narrows `Content.parts` before it indexes.
…test `_new_invocation_context` is annotated `-> InvocationContext`, so the isinstance check on its return value cannot fail. The code_executor assertion on the next line already proves the call did not raise, which is what the accept case pins. The sibling cases assert only that.
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.
Please ensure you have read the contribution guide before creating a pull request.
Link to Issue or Description of Change
Link to an existing issue (if applicable):
N/A
Or, if no issue exists, describe the change:
Problem: The CFC gate in
Runner._new_invocation_contexttestedmodel_name.startswith('gemini-2'). Compositional Function Calling routes through the Gemini Live API, so the gate raisedValueErroron ADK's ownLlmAgent.DEFAULT_LIVE_MODEL(gemini-live-2.5-flash-native-audio). It also rejected every Gemini 3 id and every path- or provider-prefixed id. AValueErrorhas no workaround short of renaming the model.Solution: The gate now asks "is this a Gemini model", and honors
ADK_DISABLE_GEMINI_MODEL_ID_CHECK. This is the predicate theBuiltInCodeExecutortwo lines below already uses, so one constraint is checked once. Commit745de0acmoved every production caller ontois_gemini_modeland deprecated both version helpers;runners.pywas the last version-bucketing site. Dropping the floor also admits Early Access ids such asgemini-flash-early-exp, which was the intended answer and not an accident.Non-Gemini models still fail fast, with the same
ValueErrortype and the same message text.Why not a normalising
major >= 2parse: it does not fix the reported defect.gemini-live-2.5-flash-native-audioyields the version tokenlive, which no version parser accepts, so amajor >= 2gate rejects the Live default model too.Collision check: I ran
gh pr list --repo AmaadMartin/adk-python --state open --limit 100and read the diff of every adjacent PR. Two PRs touch the same line and reach the opposite decision:fix/cfc-gate-normalise-gemini-version— addsis_gemini_2_or_aboveand keeps the floor. Its test pinsgemini-1.5-proandgemini-flash-early-expas rejected.fix/cfc-gate-early-access-decision— adds a comment and pins Early Access ids as rejected.Neither lands this change, so this is not a duplicate. I branched from
mainrather than stacking, because stacking would add a helper and then delete its only caller in the same review. The conflict with each is the single predicate line plus theTestRunnerCfcModelGateclass name. A maintainer should merge one of the three.Cross-runtime note:
adk-jscarries the same defect incore/src/runner/runner.ts. It is tracked as a separate change, because itsBuiltInCodeExecutorstill usesisGemini2OrAboveand must be migrated in the same commit. No file underadk-jsis touched here.Testing Plan
Please describe the tests that you ran to verify your changes. This is required for all PRs that are not small documentation or typo fixes.
Unit Tests:
[x] I have added or updated unit tests for my change.
[x] All unit tests pass locally.
TestRunnerCfcModelGateintests/unittests/test_runners.pyadds 16 tests. No test asserted this gate before, so this is net-new coverage.Coverage of the changed block is 100% line and 100% branch, measured with
coverage run --branch: no missing lines and no partial branches inrunners.py:2211-2219.Mutation results. I ran the new tests against seven mutations of the gate. Each test dies to at least one.
startswith('gemini-2')test_cfc_gate_accepts_any_gemini_model[gemini-live-2.5-flash-native-audio]:AssertionError: assert isinstance(None, BuiltInCodeExecutor)test_cfc_gate_rejects_a_non_gemini_model[claude-3-5-sonnet]:DID NOT RAISE <class 'ValueError'>or is_gemini_model_id_check_disabled()test_cfc_gate_accepts_any_model_when_the_id_check_is_disabledsupport_cfcconditiontest_cfc_gate_is_inert_when_support_cfc_is_falsehasattr(canonical_model)conditiontest_cfc_gate_is_inert_for_an_agent_without_a_canonical_modeltest_cfc_gate_keeps_an_already_installed_code_executorOne deviation from the plan.
models/gemini-2.5-procannot reach the gate as a plain string:LLMRegistrydoes not match that form, socanonical_modelraises first. That is anLLMRegistrylimit, not a CFC one, and is out of scope.test_cfc_gate_accepts_a_models_prefixed_gemini_idsupplies the id on a realGeminiinstance instead, which is how a user reaches the gate with that form.Manual End-to-End (E2E) Tests:
test_cfc_run_async_completes_on_the_default_live_modeldrives the whole public path:runner.run_async(run_config=RunConfig(support_cfc=True))onDEFAULT_LIVE_MODEL, through the real gate and the realrun_liveflow, and asserts the model's reply arrives. The only fake istesting_utils.MockModelat the connection boundary. The live loop does not end on its own, so the test stops at the first agent reply, which is the patterntesting_utilsalready uses.To reproduce by hand:
The first four are accepted with
code_executor=BuiltInCodeExecutor, andclaude-3-5-sonnetis rejected with the unchanged message. Before this change all four are rejected.Checklist
[x] I have read the CONTRIBUTING.md document.
[x] I have performed a self-review of my own code.
[x] I have commented my code, particularly in hard-to-understand areas.
[x] I have added tests that prove my fix is effective or that my feature works.
[x] New and existing unit tests pass locally with my changes.
CI
Unit Tests pass on Python 3.10 to 3.14. Mypy Check passes on 3.10 to 3.13. A2A v0.3 Tests pass on 3.10 to 3.14.
Pre-commit Linterfails, and the cause is not in this diff. The only failing hook isupdate-constraints, which regeneratesconstraints-3.10.txtthroughconstraints-3.14.txtbecause upstream package versions moved past the recorded snapshot date. This PR touches neither those files norpyproject.toml. The same hook fails on the sibling PRs #216 and #217, and PR #214 tracks the fix.