feat: add MixRoute provider with model-specific endpoint routing

This commit is contained in:
AsyncHexDev 2026-10-31 22:21:11 +08:00
parent a713ed6bab
commit 79904d6869
6 changed files with 117 additions and 0 deletions

View file

@ -1079,6 +1079,7 @@ openai_compatible_endpoints: Final[list[str]] = [
"https://api.meta.ai/v1",
"https://api.sailresearch.com/v1",
"https://api.cognition.ai/v1",
"https://api.mixroute.ai/v1",
"https://inference.coralbricks.ai/v1",
"https://api.cortecs.ai/v1",
"https://api.scx.ai/v1",
@ -1163,6 +1164,7 @@ openai_compatible_providers: Final[list[str]] = [
"darkbloom",
"meta", # Meta Model API (Muse Spark) - JSON-configured provider
"cognition",
"mixroute",
"coralbricks",
"cortecs",
"scx-ai",

View file

@ -302,5 +302,16 @@
"preserve_cache_control": true
},
"supported_endpoints": ["/v1/chat/completions"]
},
"mixroute": {
"base_url": "https://api.mixroute.ai/v1",
"api_key_env": "MIXROUTE_API_KEY",
"api_base_env": "MIXROUTE_API_BASE",
"supported_endpoints": ["/v1/chat/completions", "/v1/embeddings"],
"model_supported_endpoints": {
"claude-opus-5-5": ["/v1/messages"],
"claude-sonnet-5-5": ["/v1/messages"],
"gpt-6.1-sol": ["/v1/responses"]
}
}
}

View file

@ -905,6 +905,32 @@
"generateContent": true
}
},
"mixroute": {
"display_name": "MixRoute (`mixroute`)",
"url": "https://mixroute.ai/docs/api-reference/endpoint/chat-openai",
"endpoints": {
"chat_completions": true,
"responses": true,
"text_completion": false,
"embeddings": true,
"image_generations": false,
"image_edits": false,
"audio_transcriptions": false,
"audio_speech": false,
"rerank": false,
"ocr": false,
"search": false,
"moderations": false,
"realtime": false,
"video_generations": false,
"batches": false,
"messages": true,
"interactions": false,
"systemone": false,
"a2a": false,
"generateContent": false
}
},
"cognition": {
"display_name": "Cognition (`cognition`)",
"url": "https://docs.litellm.ai/docs/providers/cognition",

View file

@ -4677,6 +4677,7 @@ class LlmProviders(str, Enum):
LIBERTAI = "libertai"
PINSTRIPES = "pinstripes"
COGNITION = "cognition"
MIXROUTE = "mixroute"
CORALBRICKS = "coralbricks"
CORTECS = "cortecs"
SCX_AI = "scx-ai"

View file

@ -905,6 +905,32 @@
"generateContent": true
}
},
"mixroute": {
"display_name": "MixRoute (`mixroute`)",
"url": "https://mixroute.ai/docs/api-reference/endpoint/chat-openai",
"endpoints": {
"chat_completions": true,
"responses": true,
"text_completion": false,
"embeddings": true,
"image_generations": false,
"image_edits": false,
"audio_transcriptions": false,
"audio_speech": false,
"rerank": false,
"ocr": false,
"search": false,
"moderations": false,
"realtime": false,
"video_generations": false,
"batches": false,
"messages": true,
"interactions": false,
"systemone": false,
"a2a": false,
"generateContent": false
}
},
"cognition": {
"display_name": "Cognition (`cognition`)",
"url": "https://docs.litellm.ai/docs/providers/cognition",

View file

@ -0,0 +1,51 @@
from typing import Final
import pytest
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
from litellm.llms.openai_like.dynamic_config import create_config_class
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
def test_mixroute_prefix_preserves_model_and_resolves_credentials(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("MIXROUTE_API_KEY", "mixroute-test-key")
monkeypatch.delenv("MIXROUTE_API_BASE", raising=False)
config: Final = JSONProviderRegistry.get("mixroute")
assert config is not None
model, provider, key, base = get_llm_provider(model="mixroute/example-model")
assert (model, provider, key, base) == ("example-model", "mixroute", "mixroute-test-key", config.base_url)
def test_mixroute_endpoint_detection_preserves_explicit_key(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("MIXROUTE_API_KEY", "environment-key")
config: Final = JSONProviderRegistry.get("mixroute")
assert config is not None
model, provider, key, base = get_llm_provider(
model="example-model", api_base=config.base_url, api_key="caller-key"
)
assert (model, provider, key, base) == ("example-model", "mixroute", "caller-key", config.base_url)
def test_mixroute_base_override_and_chat_url(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("MIXROUTE_API_KEY", "environment-key")
monkeypatch.setenv("MIXROUTE_API_BASE", "https://mixroute.example.test/v1")
config: Final = JSONProviderRegistry.get("mixroute")
assert config is not None
handler: Final = create_config_class(config)()
base, key = handler.get_openai_compatible_provider_info(None, None)
assert (base, key) == ("https://mixroute.example.test/v1", "environment-key")
assert handler.get_complete_url(base, key, "example-model", {}, {}) == (
"https://mixroute.example.test/v1/chat/completions"
)
assert handler.get_openai_compatible_provider_info("https://override.example.test/v1", "explicit-key") == (
"https://override.example.test/v1", "explicit-key"
)
def test_mixroute_native_endpoints_do_not_apply_to_other_models() -> None:
assert JSONProviderRegistry.serves_endpoint("mixroute", "claude-opus-5-5", "/v1/messages")
assert JSONProviderRegistry.serves_endpoint("mixroute", "mixroute/claude-sonnet-5-5", "/v1/messages")
assert JSONProviderRegistry.serves_endpoint("mixroute", "gpt-6.1-sol", "/v1/responses")
assert not JSONProviderRegistry.serves_endpoint("mixroute", "unlisted-model", "/v1/messages")
assert not JSONProviderRegistry.serves_endpoint("mixroute", "unlisted-model", "/v1/responses")
assert JSONProviderRegistry.supports_embeddings("mixroute")