Release NU (v0.51.408): reasoning selector for nested Gemini custom-provider routes (#4165) - #4169
Conversation
#3431) Version-gated the nested-gateway allow to 2.5-series/3-era (reviewer fix: gemini-1.5/1.0 have no thinking controls -> selector would fail on send) + pre-2.5 exclusion regression tests. Stamped v0.51.408 (Release NU). Co-authored-by: b3nw <b3nw@users.noreply.github.com>
|
| Filename | Overview |
|---|---|
| api/config.py | Adds two new helper functions (_nested_route_reasoning_denied and _nested_gateway_route_reasoning) that detect nested Gemini gateway routes and apply a version-gated allow (2.5-series and 3-era only). Hard deny for embedding/image routes is injected at the top of resolve_model_reasoning_efforts before any downstream metadata lookup. Logic is correct; minor DRY issue with duplicated embed/image exclusion predicate. |
| tests/test_custom_provider_bare_model_reasoning.py | Adds three new parametrized test groups for nested Gemini routes. Pre-2.5 exclusion and embed/image exclusion are well covered, but the positive-allow group only tests 3-era ids and is missing a vertex/gemini-2.5-* case, leaving the primary motivating use case untested. |
| CHANGELOG.md | Adds a correctly dated v0.51.408 release note that accurately describes the version-gated allow, the embedding/image exclusion, and the pre-2.5 restriction. No issues. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[resolve_model_reasoning_efforts] --> B{_nested_route_reasoning_denied?}
B -->|embedding or image gateway route| C[return empty list]
B -->|not denied| D{hermes_cli / lmstudio path?}
D -->|yes| E[return filtered efforts]
D -->|no| F[_models_dev_reasoning_efforts]
F -->|metadata known| G[return metadata efforts]
F -->|None| H[_heuristic_reasoning_efforts]
H --> I{slash-prefix list match?}
I -->|yes e.g. google/gemini-2| J[return VALID_REASONING_EFFORTS]
I -->|no| K{_nested_gateway_route_reasoning?}
K -->|vertex/gemini-2.5 or 3-era or thinking tail| J
K -->|pre-2.5 or no match| L{_candidate_supports_reasoning}
L -->|match| J
L -->|no match| M[return empty list]
Reviews (1): Last reviewed commit: "fix(config): reasoning toggle for nested..." | Re-trigger Greptile
| @pytest.mark.parametrize( | ||
| "model_id", | ||
| [ | ||
| "vertex/gemini-3.1-pro-preview", | ||
| "vertex/gemini-3-pro-preview", | ||
| "gemini_cli/gemini-3-pro-preview", | ||
| ], | ||
| ) | ||
| def test_custom_nested_gemini_routes_expose_reasoning(model_id): | ||
| efforts = cfg.resolve_model_reasoning_efforts( | ||
| model_id, | ||
| provider_id="custom:newapi", | ||
| ) | ||
| assert set(efforts) >= {"low", "medium", "high"}, ( | ||
| f"{model_id} via custom:newapi should expose reasoning efforts" | ||
| ) |
There was a problem hiding this comment.
No positive test for the 2.5-series prefix
The positive-allow parametrize list only covers 3-era model ids (3-pro-preview, 3.1-pro-preview). The PR description calls out vertex/gemini-2.5-… as the primary motivating use case, and the version-gate in _nested_gateway_route_reasoning uses a separate "2.5-" / "2.5." branch for it — but no test exercises that branch. A typo in the startswith tuple (e.g. "2.5-" silently changed to "25-") would regress the most common case with no test failure.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| def _nested_route_reasoning_denied(model: str) -> bool: | ||
| """Hard deny for nested Gemini gateway routes that must never show a reasoning toggle.""" | ||
| lower = str(model or "").strip().lower() | ||
| if not lower: | ||
| return False | ||
| for prefix in ("vertex/gemini-", "gemini_cli/gemini-"): | ||
| if lower.startswith(prefix): | ||
| tail = lower[len(prefix) :] | ||
| if tail.startswith("embedding") or "image" in tail or "imagine" in tail: | ||
| return True | ||
| return False | ||
|
|
||
|
|
||
| def _nested_gateway_route_reasoning(model: str) -> bool: | ||
| """Recognize nested ``vertex/gemini-`` and ``gemini_cli/gemini-`` routes on custom providers. | ||
|
|
||
| The slash-prefix heuristic list includes ``google/gemini-2`` but not gateway-prefixed | ||
| Gemini ids, so capable models behind custom aggregators stayed hidden. | ||
| """ | ||
| lower = str(model or "").strip().lower() | ||
| if not lower: | ||
| return False | ||
| for prefix in ("vertex/gemini-", "gemini_cli/gemini-"): | ||
| if lower.startswith(prefix): | ||
| tail = lower[len(prefix) :] | ||
| if tail.startswith("embedding") or "image" in tail or "imagine" in tail: | ||
| return False | ||
| # Gemini thinking/reasoning controls are documented for the 2.5 | ||
| # series and 3-era models only — 1.5 (and earlier) have no thinking | ||
| # support, so a reasoning selector on e.g. ``vertex/gemini-1.5-pro`` | ||
| # would let a user pick an effort that the route then rejects. | ||
| # Version-gate the allow to the reasoning-capable families. | ||
| if ( | ||
| tail == "2.5" | ||
| or tail.startswith(("2.5-", "2.5.", "3-", "3.")) | ||
| or "thinking" in tail | ||
| or "reasoning" in tail | ||
| ): | ||
| return True | ||
| return False |
There was a problem hiding this comment.
Embed/image exclusion predicate is duplicated across both functions
Both _nested_route_reasoning_denied (lines 2493) and _nested_gateway_route_reasoning (lines 2510-2511) independently check tail.startswith("embedding") or "image" in tail or "imagine" in tail. If a new exclusion pattern is added later (e.g. a video or audio generation variant), a maintainer touching only one function would create a gap where the hard-deny at the top of resolve_model_reasoning_efforts fires correctly but the heuristic allow-gate is left inconsistent. Extracting the check into a small shared helper would eliminate the duplication.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Release NU (v0.51.408): reasoning selector for nested Gemini custom-provider routes (nesquena#4165)
Release NU (v0.51.408) — Reasoning selector for nested Gemini custom-provider routes
Ships #4165 (by @b3nw, follow-up to #3431) as Release NU.
What this fixes
Custom OpenAI-compatible aggregators often list Gemini as nested routes (
vertex/gemini-…,gemini_cli/gemini-…) rather than OpenRouter-stylegoogle/gemini-*. The reasoning-effort selector only shows whenresolve_model_reasoning_efforts()returns a non-empty list, and those gateway-prefixed ids weren't recognized — so capable Gemini models behind custom aggregators never showed the selector. This adds detection for those routes (matching the #3431 maintainer direction: fix capability false-negatives, not a universal always-on chip).Reviewer fix applied (was the one gate finding)
Codex caught that the contributor's original allow matched every
gemini-*tail (except embedding/image), sovertex/gemini-1.5-prowould falsely show a reasoning selector — but Gemini 1.5/1.0 have no thinking controls, so a user picking an effort there could get failed sends. I version-gated the allow to the reasoning-capable families only (2.5series and3-/3.era; plus explicitthinking/reasoningtails), and added regression tests assertingvertex/gemini-1.5-pro,gemini-1.5-flash,gemini_cli/gemini-1.5-pro,gemini-1.0-proall return[]. Verified empirically: 1.5/1.0 → no selector, 2.5/3 → selector, embedding/image → excluded.Gates
python astclean; the custom-provider reasoning test file (34 cases incl. the 4 new pre-2.5 exclusions) green.Closes #4165