Skip to content

feat(cli): add /free-models command to list free OpenRouter models - #17994

Closed
Seenfinity wants to merge 1 commit into
NousResearch:mainfrom
Seenfinity:feat/free-models-filter
Closed

Seenfinity wants to merge 1 commit into
NousResearch:mainfrom
Seenfinity:feat/free-models-filter

Conversation

@Seenfinity

Copy link
Copy Markdown

What

Adds /free-models slash command that lists every OpenRouter model with free pricing and tool-calling support, fetched live from openrouter.ai/api/v1/models.

Resolves #17923.

Why

Free models are a key entry point for users evaluating Hermes Agent, but the existing /model picker only surfaces models that are explicitly in the curated OPENROUTER_MODELS list. Free :free variants like nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free exist on OpenRouter but aren't discoverable through the CLI today.

How

  1. New helper fetch_openrouter_free_models() in hermes_cli/models.py that hits the live OpenRouter /v1/models endpoint, filters to models where pricing.prompt and pricing.completion are both zero AND supported_parameters advertises tools, then returns [(id, "free"), ...]. Reuses the existing _openrouter_model_is_free and _openrouter_model_supports_tools helpers so policy stays consistent with the curated picker.

  2. Network/parse failures fall back to the :free entries already in OPENROUTER_MODELS so the command still works offline.

  3. New /free-models CommandDef in hermes_cli/commands.py and handler _handle_free_models_command in cli.py that prints the list with a hint on how to switch.

  4. Six tests covering: free+tool filtering, tool-support filtering, permissive handling of missing supported_parameters, network-failure fallback, malformed-payload fallback, and de-duplication.

Why a separate command and not /model --free

The issue suggested either a --free flag on /model or a separate /free-models command. I went with the separate command because the /model picker pulls from a hardcoded curated list across multiple providers (OPENROUTER_MODELS, _PROVIDER_MODELS), and threading a free-only mode through parse_model_flags, switch_model, and list_authenticated_providers would be a much larger change that touches the CLI, gateway, and TUI gateway. A standalone command keeps the diff small and surface area predictable. Happy to reshape if you'd prefer the flag approach — just let me know.

Test plan

  • python3 -m py_compile on all modified files
  • Manual runtime test of fetch_openrouter_free_models with mocked OpenRouter responses (filter, fallback, dedup paths)
  • resolve_command("free-models") returns the registered CommandDef
  • CI runs the new test class on push

Followup ideas

  • Adding context-length info next to each model id (the live payload exposes context_length)
  • Sorting by context length / popularity for nicer output
  • Wiring the same source into the /model picker behind a --free flag (separate PR if the maintainer wants this)

Adds a new /free-models slash command and a fetch_openrouter_free_models()
helper that surfaces every OpenRouter model with zero pricing and tool
support, bypassing the curated picker list. This addresses the gap where
free :free variants (e.g. nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free)
exist on OpenRouter but never make it into the curated catalog.

The helper falls back to the :free entries already in OPENROUTER_MODELS
when the live API is unreachable, so the command stays useful offline.

Resolves NousResearch#17923
@Seenfinity
Seenfinity force-pushed the feat/free-models-filter branch from da690d3 to ebc17b6 Compare May 25, 2026 19:54
@Seenfinity

Copy link
Copy Markdown
Author

Rebased onto current main (post-#25450). Conflict in hermes_cli/commands.py was just registry ordering between codex-runtime and free-models — resolved.

Re: #23359 — this is a slash command, not a hermes models list surface, and it doesn't walk the inventory. fetch_openrouter_free_models() hits OpenRouter's /api/v1/models directly to surface :free variants the curated picker deliberately omits, with the curated OPENROUTER_MODELS :free rows as offline fallback — consuming hermes_cli/inventory.py here would surface the wrong set. Happy to fold this into a broader models list --filter=free once that surface lands.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks for isolating live free-model discovery from the curated picker. The underlying request remains unmet on current main: hermes_cli/models.py:1395-1432 only returns preferred curated IDs, while website/docs/user-guide/configuring-models.md:44 documents that intentional picker behavior.

Problems

  • hermes_cli/models.py:1223, :1227, and :1247 use mid.endswith(":free") for fallback. But the PR-head catalog marks openrouter/elephant-alpha and openrouter/owl-alpha as "free" at hermes_cli/models.py:62-63; neither has that suffix. A network, parse, or empty-result fallback therefore omits known free models.

Suggested changes

  • Centralize fallback selection around the catalog's "free" description and use it at all three fallback sites.
  • Add a regression test for a free static entry without a :free suffix, including the fetch-failure path.

Automated hermes-sweeper review.

Comment thread hermes_cli/models.py
with urllib.request.urlopen(req, timeout=timeout) as resp:
payload = json.loads(resp.read().decode())
except Exception:
return [(mid, "free") for mid, _ in OPENROUTER_MODELS if mid.endswith(":free")]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This fallback loses static entries whose catalog description is "free" but whose ID has no :free suffix: this PR's OPENROUTER_MODELS includes openrouter/elephant-alpha and openrouter/owl-alpha. Filter by the catalog's free description through a shared fallback helper, and use it at the other two fallback sites as well.

@teknium1 teknium1 added the sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users label Jul 12, 2026
@teknium1

Copy link
Copy Markdown
Collaborator

Closing per maintainer decision on the free-model picker sweep (Aug 27 2026): the OpenRouter picker stays a curated allowlist, and a dedicated /free-models listing command isn't being taken — same ruling that closed #89323/#80036/#90346. Thanks for the contribution.

@teknium1 teknium1 closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have provider/openrouter OpenRouter aggregator sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Add free-tier filter to /models and include all free OpenRouter models in curated list

3 participants