Mirror LLM_API_KEY to provider env var (closes #504)

LiteLLM's per-provider branches (deepseek, anthropic, groq, etc.)
don't consult ``litellm.api_key`` (the module global Strix sets).
They only check the per-call ``api_key`` kwarg and the
``<PROVIDER>_API_KEY`` env var. The SDK's LitellmModel passes
``api_key=None`` by default, so requests went out with an empty
bearer and DeepSeek (and friends) returned 401.

Mirror the user's LLM_API_KEY into the provider-specific env var
(``DEEPSEEK_API_KEY`` for ``deepseek/...``, ``ANTHROPIC_API_KEY``
for ``anthropic/...``, etc.) using LiteLLM's documented convention.
``os.environ.setdefault`` is used so an explicit user env is never
clobbered. The OpenAI branch was already working via
``set_default_openai_key`` + the existing ``litellm.api_key`` global
fallback.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
0xallam
2026-06-07 22:42:03 +03:00
committed by Ahmed Allam
parent 13046cc74a
commit 9ab70c6d61
+17
View File
@@ -49,6 +49,7 @@ def configure_sdk_model_defaults(settings: Settings) -> None:
if llm.api_key:
set_default_openai_key(llm.api_key, use_for_tracing=False)
_configure_litellm_default("api_key", llm.api_key)
_mirror_api_key_to_provider_env(llm.model, llm.api_key)
if llm.api_base:
os.environ["OPENAI_BASE_URL"] = llm.api_base
_configure_litellm_default("api_base", llm.api_base)
@@ -57,6 +58,22 @@ def configure_sdk_model_defaults(settings: Settings) -> None:
set_default_openai_api("responses")
def _mirror_api_key_to_provider_env(model_name: str | None, api_key: str) -> None:
if not model_name:
return
name = model_name.strip()
for prefix in ("litellm/", "any-llm/"):
if name.lower().startswith(prefix):
name = name[len(prefix) :]
break
if "/" not in name:
return
provider = name.split("/", 1)[0].strip().upper()
if not provider:
return
os.environ.setdefault(f"{provider}_API_KEY", api_key)
def _configure_litellm_compatibility() -> None:
"""Enable LiteLLM's permissive param-handling mode."""
import litellm