Skip to content

feat(gateway): add per-request model routing to API server - #5862

Closed
jbarket wants to merge 1 commit into
NousResearch:mainfrom
jbarket:feat/api-server-model-routing
Closed

feat(gateway): add per-request model routing to API server#5862
jbarket wants to merge 1 commit into
NousResearch:mainfrom
jbarket:feat/api-server-model-routing

Conversation

@jbarket

@jbarket jbarket commented Apr 7, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

The API server /v1/chat/completions endpoint now respects the model field from the request body. When a model other than "hermes-agent" is specified, it overrides the config.yaml default for that request.

Problem: The API server always uses the model configured in config.yaml. There is no way to route individual requests to different models or providers. When using the API server as a backend for automation pipelines (e.g., n8n workflows), every step uses the same model — even when cheaper models would suffice for implementation tasks and local models could handle research/writing.

Solution: Pass the request body's model field through to _create_agent as a model_override. Two formats are supported:

// Simple model name — uses default provider with different model
{"model": "claude-sonnet-4-20250514", "messages": [...]}

// Provider-prefixed — resolves provider config from config.yaml
{"model": "ollama/gemma4:26b", "messages": [...]}

The provider-prefixed format looks up the provider name in config.yaml's providers section and resolves base_url, api_key, and transport automatically.

Related Issue

N/A — discovered while building multi-model n8n automation pipelines (architecture → implement → review) where different steps benefit from different models.

Type of Change

  • ✨ New feature (non-breaking change that adds functionality)

Changes Made

  • gateway/platforms/api_server.py: Added model_override parameter to _create_agent() and _run_agent(). When provided, overrides the config default. Provider-prefixed models (e.g. "ollama/gemma4:26b") resolve credentials from the providers config section. Wired into both streaming and non-streaming /v1/chat/completions paths.
  • tests/gateway/test_api_server_model_routing.py: 7 new tests covering default behavior, simple override, provider-prefixed override, and unknown provider handling.

How to Test

  1. Configure a provider in config.yaml:
    providers:
      ollama:
        api: http://localhost:11434/v1
        transport: chat_completions
  2. Send a request with a model override:
    curl -X POST http://localhost:8642/v1/chat/completions \
      -H "Content-Type: application/json" \
      -d '{"model": "ollama/gemma4:26b", "messages": [{"role": "user", "content": "Hello"}]}'
  3. Verify it routes to Ollama instead of the default provider.
  4. Send "model": "hermes-agent" — should use the config default as before.
  5. Run tests: pytest tests/gateway/test_api_server_model_routing.py -v (7 passed)

Checklist

  • Contributing guide read
  • Conventional commits
  • No duplicate PRs
  • Focused single change
  • Tests pass (7 new tests)
  • Tested on Ubuntu 24.04 (verified with Ollama/Gemma, Anthropic/Sonnet, and default Opus)
  • Cross-platform safe (pure Python, no OS dependency)
  • Backward compatible — "model": "hermes-agent" (or omitted) behaves identically to before

The API server /v1/chat/completions endpoint now respects the "model"
field from the request body. When a model other than "hermes-agent" is
specified, it overrides the config.yaml default for that request.

Supports two formats:
  - Simple model name: "claude-sonnet-4-20250514"
    Uses the default provider with the specified model.
  - Provider-prefixed: "ollama/gemma4:26b"
    Resolves the provider's base_url, api_key, and transport from the
    "providers" section in config.yaml, then uses the model name after
    the slash.

This enables multi-model pipelines through a single Hermes API server
instance — e.g. n8n workflows that route implementation tasks to Sonnet,
research to a local Gemma model via Ollama, and review steps to Opus,
all through the same endpoint.

The model_override flows through _create_agent -> _run_agent for both
streaming and non-streaming paths.
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/gateway Gateway runner, session dispatch, delivery labels Apr 30, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Related to #16216, #10773, #16403 — same feature (per-request model routing in API server). Consider consolidating.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the focused API-server routing work. This is now implemented on current main through the consolidated model_routes design.

  • Automated hermes-sweeper review verified commit 4a09b692ecc385ad48f00694a1e315b8eed120cd (feat(api-server): per-client model routing via model_routes (#3176 salvage)) is an ancestor of current main.
  • gateway/platforms/api_server.py:2194 resolves the incoming /v1/chat/completions model field to a configured route; gateway/platforms/api_server.py:1291 applies its model/provider credentials to that request's agent.
  • The implementation also covers /v1/responses and /v1/runs (gateway/platforms/api_server.py:3312, gateway/platforms/api_server.py:4285) and exposes configured aliases through /v1/models (gateway/platforms/api_server.py:1421).
  • Current coverage in tests/gateway/test_api_server.py:3907 verifies request propagation, provider credential resolution, and fallback behavior.

The merged implementation uses configured model_routes aliases rather than unrestricted direct provider-prefixed request values, consolidating the same per-request routing capability noted in the prior discussion.

@teknium1 teknium1 closed this Jul 12, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jul 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have sweeper:implemented-on-main Sweeper: behavior already present on current main type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants