Skip to content

Release NU (v0.51.408): reasoning selector for nested Gemini custom-provider routes (#4165) - #4169

Merged
nesquena-hermes merged 1 commit into
masterfrom
stage-4165
Jun 14, 2026
Merged

nesquena-hermes merged 1 commit into
masterfrom
stage-4165

Conversation

@nesquena-hermes

Copy link
Copy Markdown
Collaborator

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-style google/gemini-*. The reasoning-effort selector only shows when resolve_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), so vertex/gemini-1.5-pro would 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.5 series and 3-/3. era; plus explicit thinking/reasoning tails), and added regression tests asserting vertex/gemini-1.5-pro, gemini-1.5-flash, gemini_cli/gemini-1.5-pro, gemini-1.0-pro all return []. Verified empirically: 1.5/1.0 → no selector, 2.5/3 → selector, embedding/image → excluded.

Gates

  • Codex (post-fix diff): SAFE TO SHIP — version-gate correctly excludes pre-2.5 while keeping 2.5+/3, no new false-negative, deny-before-allow ordering correct.
  • Full suite: 8900 passed, 9 skipped, 3 xpassed, 0 failed.
  • python ast clean; the custom-provider reasoning test file (34 cases incl. the 4 new pre-2.5 exclusions) green.

Closes #4165

#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>
@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends the reasoning-effort selector heuristic to recognize nested Gemini gateway route ids (vertex/gemini-…, gemini_cli/gemini-…) that custom OpenAI-compatible aggregators use, matching the direction set in #3431. A version gate restricts the allow to the 2.5-series and 3-era models (plus explicit thinking/reasoning tails), while pre-2.5 routes and embedding/image variants are correctly excluded.

  • api/config.py: Adds _nested_gateway_route_reasoning (version-gated allow) and _nested_route_reasoning_denied (hard deny for embed/image routes injected before any metadata lookup in resolve_model_reasoning_efforts).
  • tests/…: Three new parametrized groups cover the 3-era positive case, embedding/image exclusion, and four pre-2.5 regression cases; the 2.5-series positive branch is untested.
  • CHANGELOG.md: Accurate release note added for v0.51.408.

Confidence Score: 4/5

Safe to merge; the core version-gate and hard-deny logic are correct and regression tests confirm pre-2.5 routes stay excluded.

The positive test suite covers only 3-era ids — the vertex/gemini-2.5-* branch in the startswith tuple is exercised by no test, so a typo there would regress the primary use case silently. The embed/image exclusion predicate is also duplicated across two functions, creating a future maintenance hazard.

tests/test_custom_provider_bare_model_reasoning.py — the positive-allow parametrize list should include at least one vertex/gemini-2.5-* id to cover the primary motivating use case.

Important Files Changed

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]
Loading

Reviews (1): Last reviewed commit: "fix(config): reasoning toggle for nested..." | Re-trigger Greptile

Comment on lines +254 to +269
@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"
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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!

Comment thread api/config.py
Comment on lines +2485 to +2524
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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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!

@nesquena-hermes
nesquena-hermes merged commit dfb4007 into master Jun 14, 2026
11 checks passed
@nesquena-hermes
nesquena-hermes deleted the stage-4165 branch June 14, 2026 04:53
SysAdminDoc pushed a commit to SysAdminDoc/hermes-webui that referenced this pull request Jun 26, 2026
Release NU (v0.51.408): reasoning selector for nested Gemini custom-provider routes (nesquena#4165)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants