diff --git a/litellm/constants.py b/litellm/constants.py index 6b984c2673c..1db63b2d4ab 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -893,6 +893,7 @@ openai_compatible_endpoints: Final[list] = [ "https://pinstripes.io/v1", "https://api.meta.ai/v1", "https://api.cognition.ai/v1", + "https://api.mizumi.co/v1", "https://api.scx.ai/v1", "https://gigachat.devices.sberbank.ru/api/v1", ] @@ -965,6 +966,7 @@ openai_compatible_providers: Final[list] = [ "darkbloom", "meta", # Meta Model API (Muse Spark) - JSON-configured provider "cognition", + "mizumi", "scx-ai", ] openai_text_completion_compatible_providers: Final[list] = [ # providers that support `/v1/completions` diff --git a/litellm/llms/openai_like/providers.json b/litellm/llms/openai_like/providers.json index a458a209ea9..ded845e0f13 100644 --- a/litellm/llms/openai_like/providers.json +++ b/litellm/llms/openai_like/providers.json @@ -180,6 +180,11 @@ "api_key_env": "COGNITION_API_KEY", "api_base_env": "COGNITION_API_BASE" }, + "mizumi": { + "base_url": "https://api.mizumi.co/v1", + "api_key_env": "MIZUMI_API_KEY", + "api_base_env": "MIZUMI_API_BASE" + }, "pinstripes": { "base_url": "https://pinstripes.io/v1", "api_key_env": "PINSTRIPES_API_KEY", diff --git a/litellm/provider_endpoints_support_backup.json b/litellm/provider_endpoints_support_backup.json index dbeaccdda2d..b7547354719 100644 --- a/litellm/provider_endpoints_support_backup.json +++ b/litellm/provider_endpoints_support_backup.json @@ -545,6 +545,23 @@ "a2a": false } }, + "mizumi": { + "display_name": "Mizumi (`mizumi`)", + "url": "https://docs.litellm.ai/docs/providers/mizumi", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, "cohere": { "display_name": "Cohere (`cohere`)", "url": "https://docs.litellm.ai/docs/providers/cohere", diff --git a/litellm/types/utils.py b/litellm/types/utils.py index e3ea37dc0c8..6d4c0db6883 100644 --- a/litellm/types/utils.py +++ b/litellm/types/utils.py @@ -4011,6 +4011,7 @@ class LlmProviders(str, Enum): LIBERTAI = "libertai" PINSTRIPES = "pinstripes" COGNITION = "cognition" + MIZUMI = "mizumi" SCX_AI = "scx-ai" DARKBLOOM = "darkbloom" META = "meta" diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index c71f4a82a4a..aacf881c076 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -580,6 +580,23 @@ "a2a": false } }, + "mizumi": { + "display_name": "Mizumi (`mizumi`)", + "url": "https://docs.litellm.ai/docs/providers/mizumi", + "endpoints": { + "chat_completions": true, + "messages": false, + "responses": false, + "embeddings": false, + "image_generations": false, + "audio_transcriptions": false, + "audio_speech": false, + "moderations": false, + "batches": false, + "rerank": false, + "a2a": false + } + }, "cohere": { "display_name": "Cohere (`cohere`)", "url": "https://docs.litellm.ai/docs/providers/cohere", diff --git a/tests/test_litellm/llms/openai_like/test_mizumi_provider.py b/tests/test_litellm/llms/openai_like/test_mizumi_provider.py new file mode 100644 index 00000000000..d28e6ddeff1 --- /dev/null +++ b/tests/test_litellm/llms/openai_like/test_mizumi_provider.py @@ -0,0 +1,143 @@ +""" +Tests for the Mizumi provider identity. + +Mizumi (https://mizumi.co) serves an OpenAI-compatible /v1/chat/completions surface, but it +must resolve to its own `mizumi` provider so OpenAI-specific pricing and provider-level +reporting never apply to its traffic. + +All tests are offline: no network access is required. +""" + +import json +from pathlib import Path + +import pytest + +import litellm + + +class TestMizumiProviderIdentity: + def test_mizumi_is_a_registered_provider(self): + from litellm import LlmProviders + + assert LlmProviders.MIZUMI.value == "mizumi" + assert "mizumi" in litellm.provider_list + + def test_mizumi_json_config(self): + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + mizumi = JSONProviderRegistry.get("mizumi") + assert mizumi is not None + assert mizumi.base_url == "https://api.mizumi.co/v1" + assert mizumi.api_key_env == "MIZUMI_API_KEY" + assert mizumi.api_base_env == "MIZUMI_API_BASE" + + def test_mizumi_in_openai_compatible_providers(self): + from litellm.constants import openai_compatible_providers + + assert "mizumi" in openai_compatible_providers + + def test_prefixed_model_resolves_to_mizumi_not_openai(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + model, provider, _, api_base = get_llm_provider( + model="mizumi/gpt-5.5", + custom_llm_provider=None, + api_base=None, + api_key=None, + ) + + assert model == "gpt-5.5" + assert provider == "mizumi" + assert api_base == "https://api.mizumi.co/v1" + + def test_explicit_api_base_and_key_win(self): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + _, provider, api_key, api_base = get_llm_provider( + model="mizumi/gpt-5.5", + custom_llm_provider=None, + api_base="https://mizumi.internal.example/v1", + api_key="sk-test", + ) + + assert provider == "mizumi" + assert api_base == "https://mizumi.internal.example/v1" + assert api_key == "sk-test" + + def test_api_base_autodetects_mizumi(self, monkeypatch: pytest.MonkeyPatch): + """Endpoint autodetection works off the JSON config alone. + + Regression guard: no provider-specific branch exists in + get_llm_provider_logic.py — the generic JSONProviderRegistry fallback + must pick Mizumi up from its registered base URL. + """ + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + monkeypatch.setenv("MIZUMI_API_KEY", "sk-mizumi-env") + + _, provider, api_key, api_base = get_llm_provider( + model="gpt-5.5", + custom_llm_provider=None, + api_base="https://api.mizumi.co/v1", + api_key=None, + ) + + assert provider == "mizumi" + assert api_base == "https://api.mizumi.co/v1" + assert api_key == "sk-mizumi-env" + + def test_autodetected_api_base_keeps_the_caller_api_key(self, monkeypatch: pytest.MonkeyPatch): + from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider + + monkeypatch.setenv("MIZUMI_API_KEY", "sk-mizumi-env") + + _, provider, api_key, _ = get_llm_provider( + model="gpt-5.5", + custom_llm_provider=None, + api_base="https://api.mizumi.co/v1", + api_key="sk-mizumi-caller", + ) + + assert provider == "mizumi" + assert api_key == "sk-mizumi-caller" + + def test_env_api_key_is_read_from_mizumi_variable(self, monkeypatch: pytest.MonkeyPatch): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + monkeypatch.delenv("OPENAI_API_KEY", raising=False) + monkeypatch.setenv("MIZUMI_API_KEY", "sk-mizumi-env") + + provider = JSONProviderRegistry.get("mizumi") + assert provider is not None + + api_base, api_key = create_config_class(provider)()._get_openai_compatible_provider_info(None, None) + assert api_base == "https://api.mizumi.co/v1" + assert api_key == "sk-mizumi-env" + + def test_complete_url_appends_chat_completions(self): + from litellm.llms.openai_like.dynamic_config import create_config_class + from litellm.llms.openai_like.json_loader import JSONProviderRegistry + + provider = JSONProviderRegistry.get("mizumi") + assert provider is not None + config = create_config_class(provider)() + + url = config.get_complete_url( + api_base="https://api.mizumi.co/v1", + api_key="sk-test", + model="mizumi/gpt-5.5", + optional_params={}, + litellm_params={}, + stream=False, + ) + + assert url == "https://api.mizumi.co/v1/chat/completions" + + def test_supported_endpoints_matrix(self): + matrix = json.loads((Path(litellm.__file__).parent / "provider_endpoints_support_backup.json").read_text()) + + endpoints = matrix["providers"]["mizumi"]["endpoints"] + assert endpoints["chat_completions"] is True + assert endpoints["embeddings"] is False