fix: keep gateway model ids intact for OpenAI-compatible embeddings

LiteLLMEmbeddingWrapper dropped the provider prefix whenever the resolved provider was openai, so the raw model id reached litellm.embedding() and LiteLLM re-parsed its first path segment as a provider. Gateway model ids that contain a slash were therefore mangled or rejected before the request left the process: nvidia/llama-nemotron-embed-vl-1b-v2:free -> LLM Provider NOT provided auto/embedding -> LLM Provider NOT provided openrouter/openai/text-embedding-3-small -> routed as provider openrouter, forwarded as openai/text-embedding-3-small Always prefix instead, matching LiteLLMChatWrapper, which is why chat models already work against the same endpoints. LiteLLM strips a recognised openai/ prefix and forwards the remainder verbatim, so plain OpenAI model ids resolve exactly as before.

Md. Asiqur Rahman Khan committed Aug 21, 2026 at 00:05 UTC 655056985602e72e87f4690821c2bd000ef55a9f
2 files changed +47 -1
models.py
+1 -1
@@ -795,7 +795,7 @@ class LiteLLMEmbeddingWrapper(Embeddings):
795 model_config: Optional[ModelConfig] = None,
796 **kwargs: Any,
797 ):
798 - self.model_name = f"{provider}/{model}" if provider != "openai" else model
798 + self.model_name = f"{provider}/{model}"
799 self.kwargs = kwargs
800 self.a0_model_conf = model_config
801
tests/test_model_config_api_keys.py
+46
@@ -723,6 +723,52 @@ def test_local_provider_runtime_defaults_and_overrides(monkeypatch):
723 assert custom_vllm_chat.kwargs["api_key"] == "real-local-key"
724
725
726 +def test_openai_compatible_embedding_keeps_gateway_model_string(monkeypatch):
727 + """Gateway model ids must reach LiteLLM with an explicit `openai/` provider.
728 +
729 + An OpenAI-compatible gateway owns its own model namespace, so ids such as
730 + `nvidia/...` or `auto/embedding` are model names, not provider prefixes.
731 + Without the prefix LiteLLM re-parses the first segment as a provider and
732 + either raises "LLM Provider NOT provided" or routes to the wrong provider,
733 + mangling the model id before the gateway ever sees it.
734 + """
735 + monkeypatch.setattr(models, "get_api_key", lambda provider: "None")
736 +
737 + gateway = "https://gateway.example/v1"
738 + for model in (
739 + "nvidia/llama-nemotron-embed-vl-1b-v2:free",
740 + "openrouter/openai/text-embedding-3-small",
741 + "auto/embedding",
742 + ):
743 + embedding = models.get_embedding_model(
744 + "other", model, api_base=gateway, api_key="gateway-key"
745 + )
746 + assert embedding.model_name == f"openai/{model}"
747 + assert embedding.kwargs["api_base"] == gateway
748 +
749 + # The bundled OpenRouter embedding provider is affected the same way: it
750 + # resolves to litellm_provider `openai` against OpenRouter's api_base, so an
751 + # OpenRouter-style id has to survive intact. Previously `openai/<model>` was
752 + # handed to LiteLLM bare, which consumed the `openai/` segment and forwarded
753 + # only `<model>` to OpenRouter.
754 + for model in (
755 + "openai/text-embedding-3-small",
756 + "nvidia/llama-nemotron-embed-vl-1b-v2:free",
757 + ):
758 + openrouter_embedding = models.get_embedding_model("openrouter", model)
759 + assert openrouter_embedding.model_name == f"openai/{model}"
760 + assert openrouter_embedding.kwargs["api_base"] == "https://openrouter.ai/api/v1"
761 +
762 + # Plain OpenAI behaviour is unchanged: LiteLLM strips the `openai/` prefix
763 + # and forwards the bare model id, exactly as it did without a prefix.
764 + openai_embedding = models.get_embedding_model("openai", "text-embedding-3-small")
765 + assert openai_embedding.model_name == "openai/text-embedding-3-small"
766 +
767 + # Providers that do not resolve to `openai` keep their existing prefix.
768 + ollama_embedding = models.get_embedding_model("ollama", "nomic-embed-text")
769 + assert ollama_embedding.model_name == "ollama/nomic-embed-text"
770 +
771 +
772 def test_embedding_config_repairs_sentence_transformer_aliases(monkeypatch):
773 from plugins._model_config.helpers import model_config
774