Files
strix/strix/config/models.py
T
0xallamandAhmed Allam deb4e1ccbd Cover bare claude-/gemini- shorthands in env mirror
normalize_model_name expands `claude-*` and `gemini-*` shorthands into
`litellm/anthropic/...` and `litellm/gemini/...` at routing time, but
the mirror helper was looking at the raw pre-normalization name — bare
shorthands had no `/` and hit the early return, so ANTHROPIC_API_KEY /
GEMINI_API_KEY were never populated for those users.

Run the same normalization inside the mirror helper so the provider
prefix is consistent with what LiteLLM actually sees downstream.
2026-06-07 17:36:19 -07:00

133 lines
3.8 KiB
Python

"""SDK model configuration helpers."""
from __future__ import annotations
import os
from typing import TYPE_CHECKING
from agents import set_default_openai_api, set_default_openai_key, set_tracing_disabled
from agents.retry import (
ModelRetryBackoffSettings,
ModelRetrySettings,
retry_policies,
)
if TYPE_CHECKING:
from strix.config.settings import Settings
_SDK_PREFIXES = {"any-llm", "litellm", "openai"}
DEFAULT_MODEL_RETRY = ModelRetrySettings(
max_retries=5,
backoff=ModelRetryBackoffSettings(
initial_delay=2.0,
max_delay=90.0,
multiplier=2.0,
jitter=False,
),
policy=retry_policies.any(
retry_policies.provider_suggested(),
retry_policies.network_error(),
retry_policies.http_status((429, 500, 502, 503, 504)),
),
)
def configure_sdk_model_defaults(settings: Settings) -> None:
"""Apply Strix config to SDK-native defaults.
OpenAI-compatible base URLs are handled by the SDK OpenAI provider.
Non-OpenAI providers should use the SDK's native ``litellm/`` or
``any-llm/`` routing, produced by :func:`normalize_model_name`.
"""
llm = settings.llm
set_tracing_disabled(True)
_configure_litellm_compatibility()
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)
set_default_openai_api("chat_completions")
else:
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 = normalize_model_name(model_name)
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
litellm.drop_params = True
litellm.modify_params = True
def _configure_litellm_default(name: str, value: str) -> None:
"""Set LiteLLM's module-level defaults without adding a provider wrapper."""
import litellm
setattr(litellm, name, value)
def normalize_model_name(model_name: str) -> str:
"""Normalize friendly Strix model names to SDK-native model ids."""
model = model_name.strip()
if not model:
return model
if "/" in model:
prefix = model.split("/", 1)[0].lower()
if prefix in _SDK_PREFIXES:
return model
return f"litellm/{model}"
lower = model.lower()
if lower.startswith("claude"):
return f"litellm/anthropic/{model}"
if lower.startswith("gemini"):
return f"litellm/gemini/{model}"
return model
def uses_chat_completions_tool_schema(model_name: str, settings: Settings) -> bool:
"""Return whether the resolved SDK route can only receive JSON function tools."""
model = model_name.strip().lower()
if model.startswith(("litellm/", "any-llm/")):
return True
return bool(settings.llm.api_base)
def model_supports_reasoning(model_name: str) -> bool:
import litellm
name = model_name.strip().lower()
for prefix in ("litellm/", "any-llm/"):
if name.startswith(prefix):
name = name[len(prefix) :]
break
entry = litellm.model_cost.get(name)
if entry is None and "/" in name:
entry = litellm.model_cost.get(name.rsplit("/", 1)[1])
return bool(entry and entry.get("supports_reasoning"))