Skip to content

bug(models): Nous Portal picker shows only 4 hardcoded models — should live-fetch the full catalog (~30 models) #1538

Description

@nesquena-hermes

Summary

A user reported in Discord (Deor, May 03 2026):

Somethings happened with providers. I'm only seeing 4 models via Nous portal, none of the stuff they expose from OpenRouter.

The Settings → Default Model picker shows only 4 models under the "Nous Portal" group — Claude Opus 4.6, Claude Sonnet 4.6, GPT-5.4 Mini, Gemini 3.1 Pro Preview. Nous Portal's actual catalog (via inference-api.nousresearch.com/v1/models) is much larger — currently 30+ models including the latest Anthropic 4.7 family, GPT-5.5, Gemini 3.1 Pro/Flash, Kimi-K2.6, MiniMax M2.7, Qwen3.5, and several Xiaomi/Tencent/StepFun entries.

Root cause

In api/config.py, _PROVIDER_MODELS["nous"] (line 687–691) has just four hardcoded entries:

"nous": [
    {"id": "@nous:anthropic/claude-opus-4.6",     "label": "Claude Opus 4.6 (via Nous)"},
    {"id": "@nous:anthropic/claude-sonnet-4.6",   "label": "Claude Sonnet 4.6 (via Nous)"},
    {"id": "@nous:openai/gpt-5.4-mini",           "label": "GPT-5.4 Mini (via Nous)"},
    {"id": "@nous:google/gemini-3.1-pro-preview", "label": "Gemini 3.1 Pro Preview (via Nous)"},
],

In _build_available_models_uncached() (line ~1502), the dispatch for pid == "nous" falls through to the generic pid in _PROVIDER_MODELS branch at line 2054, which uses copy.deepcopy(_PROVIDER_MODELS.get("nous", [])) — i.e. it always returns the static four-entry list, never a live fetch.

Compare with pid == "ollama-cloud" (line 2033), which has a dedicated branch:

elif pid == "ollama-cloud":
    raw_models = []
    try:
        from hermes_cli.models import provider_model_ids as _provider_model_ids
        raw_models = [
            {"id": mid, "label": _format_ollama_label(mid)}
            for mid in (_provider_model_ids("ollama-cloud") or [])
        ]
    except Exception:
        logger.warning("Failed to load Ollama Cloud models from hermes_cli")

That live-fetch path is what makes the "Ollama Cloud" group in Deor's screenshot show 8 models (Cogito 2.1, GLM 4.6, Deepseek V4 Flash, MiniMax M2.7, Gemma3 27B, GLM 4.7, Kimi K2 Thinking, Qwen3 Coder Next) — none of which are hardcoded.

Nous Portal needs the same treatment. Verified live:

>>> from hermes_cli.models import provider_model_ids
>>> provider_model_ids("nous")
['moonshotai/kimi-k2.6', 'xiaomi/mimo-v2.5-pro', 'xiaomi/mimo-v2.5',
 'tencent/hy3-preview', 'anthropic/claude-opus-4.7', 'anthropic/claude-opus-4.6',
 'anthropic/claude-sonnet-4.6', 'anthropic/claude-sonnet-4.5',
 'anthropic/claude-haiku-4.5', 'openai/gpt-5.5', 'openai/gpt-5.4-mini',
 'openai/gpt-5.3-codex', 'google/gemini-3-pro-preview',
 'google/gemini-3-flash-preview', 'google/gemini-3.1-pro-preview',
 'google/gemini-3.1-flash-lite-preview', 'qwen/qwen3.5-plus-02-15',
 'qwen/qwen3.5-35b-a3b', 'stepfun/step-3.5-flash', 'minimax/minimax-m2.7',
 ... 10 more]

That's the data the picker should be using.

Why the report mentions "OpenRouter"

Deor's wording — "none of the stuff they expose from OpenRouter" — is a small misread of where the upstream models come from. Nous Portal is its own gateway and proxies its own catalog. There's no OpenRouter passthrough involved. The actual issue is exactly what he observed: most of the Nous catalog is missing from our picker. Once the picker shows the full Nous list, his confusion dissolves.

Fix

Add a dedicated nous branch in _build_available_models_uncached() that mirrors the ollama-cloud pattern:

elif pid == "nous":
    raw_models = []
    try:
        from hermes_cli.models import provider_model_ids as _provider_model_ids
        raw_models = [
            {"id": mid, "label": _format_nous_label(mid)}
            for mid in (_provider_model_ids("nous") or [])
        ]
    except Exception:
        logger.warning("Failed to load Nous Portal models from hermes_cli")

    if raw_models:
        models = _apply_provider_prefix(raw_models, pid, active_provider)
        groups.append({
            "provider": provider_name,
            "provider_id": pid,
            "models": models,
        })
    else:
        # Fall back to the curated four-entry list when hermes_cli is unavailable
        # (e.g. during isolated WebUI tests). Same pattern as the existing branch.
        raw_models = copy.deepcopy(_PROVIDER_MODELS.get("nous", []))
        models = _apply_provider_prefix(raw_models, pid, active_provider)
        groups.append({"provider": provider_name, "provider_id": pid, "models": models})

_format_nous_label() should produce display strings like "Claude Opus 4.7 (via Nous)" — same convention as the static list.

Tests

  • test_nous_models_live_fetch_when_hermes_cli_available — monkeypatch provider_model_ids("nous") to return a list, assert the resulting /api/models group has more than 4 entries.
  • test_nous_models_static_fallback_when_hermes_cli_missing — monkeypatch provider_model_ids to raise ImportError, assert we still get the 4-entry static fallback.
  • test_nous_model_labels_carry_via_nous_suffix — assert all live-fetched labels end with " (via Nous)" so the user can distinguish them from same-named entries in other groups (e.g. "Claude Opus 4.7" via direct Anthropic).

Acceptance criteria

  • Settings → Default Model picker shows the full Nous catalog (≥20 entries) under the "Nous Portal" group, with " (via Nous)" suffix on each label.
  • Picker still works when the agent's hermes_cli.models.provider_model_ids is unavailable (test environments, package mismatches) — falls back to the curated 4-entry list with a warning log.
  • No change to the existing @nous: ID prefix convention (already correct after fix: restore inflight session on bfcache pageshow #1480-era change).

Reporter

  • Deor (Discord, May 03 2026, 03:09 AM)
  • Relayed by AvidFuturist

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions