Compare commits

...
1 Commits
Author SHA1 Message Date
c25c649048 fix: strip openrouter/ prefix before forwarding to OpenRouter API (#672)
* fix: strip openrouter/ prefix before forwarding to OpenRouter API

cloud_router.get_provider() correctly detects LiteLLM-style
"openrouter/anthropic/claude-haiku-4.5" strings as the openrouter
provider, but was forwarding them verbatim, so the redundant prefix
reached OpenRouter's API and the request failed.

* fix: preserve native OpenRouter model IDs

---------

Co-authored-by: Ari <ari.silva@paipe.co>
Co-authored-by: Elliot Slusky <elliot@slusky.com>
2026-08-14 18:01:53 -07:00
2 changed files with 61 additions and 1 deletions
+12 -1
View File
@@ -84,6 +84,17 @@ def is_cloud_model(model: str) -> bool:
return get_provider(model) is not None
def _openrouter_model_id(model: str) -> str:
"""Return the provider-facing ID for an OpenRouter model."""
prefix = "openrouter/"
candidate = model.removeprefix(prefix)
# OpenRouter owns IDs such as "openrouter/auto" itself. Only remove the
# LiteLLM routing prefix when the remainder is still a provider/model ID.
if model.startswith(prefix) and "/" in candidate:
return candidate
return model
# ---------------------------------------------------------------------------
# Message conversion
# ---------------------------------------------------------------------------
@@ -371,7 +382,7 @@ async def stream_cloud(
"OPENROUTER_API_KEY not set — add it in the Cloud Models tab"
)
async for token in _stream_openai(
model,
_openrouter_model_id(model),
messages,
temperature,
max_tokens,
+49
View File
@@ -0,0 +1,49 @@
"""Regression tests for OpenRouter model ID normalization."""
from __future__ import annotations
import pytest
from openjarvis.core.types import Message
from openjarvis.server import cloud_router
def test_get_provider_detects_bare_openrouter_id():
assert cloud_router.get_provider("anthropic/claude-haiku-4.5") == "openrouter"
def test_get_provider_detects_litellm_prefixed_openrouter_id():
model = "openrouter/anthropic/claude-haiku-4.5"
assert cloud_router.get_provider(model) == "openrouter"
@pytest.mark.parametrize(
"requested_model,expected_forwarded_model",
[
("anthropic/claude-haiku-4.5", "anthropic/claude-haiku-4.5"),
("openrouter/anthropic/claude-haiku-4.5", "anthropic/claude-haiku-4.5"),
("openrouter/auto", "openrouter/auto"),
],
)
@pytest.mark.asyncio
async def test_stream_cloud_normalizes_openrouter_model_before_forwarding(
monkeypatch, requested_model, expected_forwarded_model
):
monkeypatch.setenv("OPENROUTER_API_KEY", "test-key")
captured: dict[str, str] = {}
async def fake_stream_openai(model, messages, temperature, max_tokens, **kwargs):
captured["model"] = model
yield "ok"
monkeypatch.setattr(cloud_router, "_stream_openai", fake_stream_openai)
tokens = [
token
async for token in cloud_router.stream_cloud(
requested_model, [Message(role="user", content="hi")]
)
]
assert tokens == ["ok"]
assert captured["model"] == expected_forwarded_model