From afd160763e38d0ac078748408601c8a2d2e3a580 Mon Sep 17 00:00:00 2001 From: Unite VPN Date: Mon, 10 Aug 2026 01:58:27 +0330 Subject: [PATCH] fix: surface openrouter/fusion in the model picker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OpenRouter meta-routers (openrouter/fusion, openrouter/fusion-flash) publish supported_parameters: [] because the concrete model is resolved per-request, yet they fully support tool calling (verified against the live chat/completions endpoint: the outer model returns tool_calls). _openrouter_model_supports_tools treated the empty list as an explicit 'no tools' declaration and dropped these routers from the picker. An empty list carries no capability information, so treat it like a missing field (permissive) — only a non-empty list that omits 'tools' hides a model. Also adds openrouter/fusion and openrouter/fusion-flash to the curated OPENROUTER_MODELS fallback list so they appear in the picker. --- hermes_cli/models.py | 14 ++++++++++++-- tests/hermes_cli/test_models.py | 18 +++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/hermes_cli/models.py b/hermes_cli/models.py index 3a809c5f1a644..5cd06d65622d8 100644 --- a/hermes_cli/models.py +++ b/hermes_cli/models.py @@ -95,6 +95,8 @@ def _urlopen_model_catalog_request(req: urllib.request.Request, *, timeout: floa ("sakana/fugu-ultra", ""), # OpenRouter routers ("openrouter/pareto-code", "auto-routes to cheapest coder meeting openrouter.min_coding_score"), + ("openrouter/fusion", "multi-model deliberation panel + analyst; ~4-5x cost when invoked"), + ("openrouter/fusion-flash", "fusion with latency-tuned general-fast preset"), # Free tier ("openrouter/elephant-alpha", "free"), ("poolside/laguna-m.1:free", "free"), @@ -1490,13 +1492,21 @@ def _openrouter_model_supports_tools(item: Any) -> bool: so the picker doesn't silently empty for those users. Only hide models whose ``supported_parameters`` is an explicit list that omits ``tools``. + **Permissive when the list is empty.** OpenRouter's meta-routers (e.g. + ``openrouter/fusion``) publish ``supported_parameters: []`` because the + concrete model is resolved per-request, yet they fully support tool + calling (verified against the live endpoint). An empty list carries no + capability information, so treat it like a missing field rather than an + explicit "no tools" declaration. + Ported from Kilo-Org/kilocode#9068. """ if not isinstance(item, dict): return True params = item.get("supported_parameters") - if not isinstance(params, list): - # Field absent / malformed / None — be permissive. + if not isinstance(params, list) or not params: + # Field absent / malformed / None / empty — carries no capability + # information, so be permissive. return True return "tools" in params diff --git a/tests/hermes_cli/test_models.py b/tests/hermes_cli/test_models.py index db14b3ed3c656..ad67ec093e45f 100644 --- a/tests/hermes_cli/test_models.py +++ b/tests/hermes_cli/test_models.py @@ -113,11 +113,23 @@ def test_tools_in_supported_parameters(self): ) is True - def test_empty_supported_parameters_list_drops_model(self): - """Explicit empty list → no tools → drop.""" + def test_empty_supported_parameters_list_is_permissive(self): + """Empty list carries no capability info → allow (openrouter/fusion). + + OpenRouter meta-routers (e.g. ``openrouter/fusion``) publish + ``supported_parameters: []`` because the concrete model is resolved + per-request, yet they support tool calling. Treat empty like missing. + """ + from hermes_cli.models import _openrouter_model_supports_tools + assert _openrouter_model_supports_tools( + {"id": "openrouter/fusion", "supported_parameters": []} + ) is True + + def test_explicit_list_without_tools_drops_model(self): + """Non-empty list that omits 'tools' → drop.""" from hermes_cli.models import _openrouter_model_supports_tools assert _openrouter_model_supports_tools( - {"id": "x", "supported_parameters": []} + {"id": "x", "supported_parameters": ["temperature", "response_format"]} ) is False