mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-11 22:51:28 +00:00
* test: use monkeypatch.setenv for env writes in tests/test_litellm `os.environ["X"] = v` inside a test leaks the value into every test that runs after it in the same worker, so ordering decides the result. 262 of those writes across 40 files now go through pytest's `monkeypatch` fixture, which restores the previous value at teardown. The rewrite skips any test that a mock.patch-family decorator wraps, any test with defaulted positional parameters, any test whose own name is called directly elsewhere, and rebinds nothing inside nested defs, because in each of those cases appending a fixture parameter changes what pytest or mock binds. Ratchets the TQ004 ceiling from 768 to 506. * fix(test): delete the key through monkeypatch instead of popping it first Five tests popped a key straight out of `os.environ`, ran, then restored it with `monkeypatch.setenv`. By the time monkeypatch saw the name it was already gone, so it recorded "absent" as the value to go back to and deleted the key at teardown. On a worker that inherited a real `RESEND_API_KEY`, `SENDGRID_API_KEY`, `UI_PASSWORD`, `LITELLM_SALT_KEY` or `OPENAI_API_KEY`, every test after the first one ran without it. `monkeypatch.delenv(..., raising=False)` removes the key and restores whatever was there, so the try/finally the manual restore needed goes with it. * chore(test): leave the two cost-calc files to the PR that rewrites them fully Both files are also in #37815, which converts the module-global writes as well as the env writes and folds them into one fixture. Two PRs rewriting the same lines differently is a conflict nobody benefits from resolving, so this one drops back to staging on those two and keeps the other 39. TQ004 clears 200 here instead of 275; the rest moves with #37815.
135 lines
5.2 KiB
Python
135 lines
5.2 KiB
Python
import os
|
|
from unittest.mock import patch
|
|
|
|
CRUSOE_API_BASE = "https://managed-inference-api-proxy.crusoecloud.com/v1"
|
|
|
|
|
|
def test_crusoe_json_registry():
|
|
"""Test Crusoe is registered in the JSON provider registry"""
|
|
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
|
|
|
assert JSONProviderRegistry.exists("crusoe")
|
|
config = JSONProviderRegistry.get("crusoe")
|
|
assert config is not None
|
|
assert config.base_url == CRUSOE_API_BASE
|
|
assert config.api_key_env == "CRUSOE_API_KEY"
|
|
assert config.api_base_env == "CRUSOE_API_BASE"
|
|
|
|
|
|
def test_crusoe_dynamic_config_defaults():
|
|
"""Test dynamic config returns correct default API base"""
|
|
from litellm.llms.openai_like.dynamic_config import create_config_class
|
|
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
|
|
|
config = create_config_class(JSONProviderRegistry.get("crusoe"))()
|
|
|
|
with patch.dict(os.environ, {}, clear=True):
|
|
api_base, api_key = config._get_openai_compatible_provider_info(None, None)
|
|
|
|
assert api_base == CRUSOE_API_BASE
|
|
assert api_key is None
|
|
|
|
|
|
def test_crusoe_dynamic_config_env_vars():
|
|
"""Test dynamic config reads CRUSOE_API_KEY and CRUSOE_API_BASE from env"""
|
|
from litellm.llms.openai_like.dynamic_config import create_config_class
|
|
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
|
|
|
config = create_config_class(JSONProviderRegistry.get("crusoe"))()
|
|
|
|
with patch.dict(
|
|
os.environ,
|
|
{"CRUSOE_API_KEY": "test-key", "CRUSOE_API_BASE": "https://custom.crusoe.com/v1"},
|
|
):
|
|
api_base, api_key = config._get_openai_compatible_provider_info(None, None)
|
|
|
|
assert api_base == "https://custom.crusoe.com/v1"
|
|
assert api_key == "test-key"
|
|
|
|
|
|
def test_crusoe_dynamic_config_explicit_params():
|
|
"""Test explicit params override env vars"""
|
|
from litellm.llms.openai_like.dynamic_config import create_config_class
|
|
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
|
|
|
config = create_config_class(JSONProviderRegistry.get("crusoe"))()
|
|
|
|
with patch.dict(os.environ, {"CRUSOE_API_KEY": "env-key"}):
|
|
api_base, api_key = config._get_openai_compatible_provider_info(
|
|
"https://override.crusoe.com/v1", "override-key"
|
|
)
|
|
|
|
assert api_base == "https://override.crusoe.com/v1"
|
|
assert api_key == "override-key"
|
|
|
|
|
|
def test_crusoe_supported_params():
|
|
"""Test dynamic config returns standard OpenAI params"""
|
|
from litellm.llms.openai_like.dynamic_config import create_config_class
|
|
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
|
|
|
config = create_config_class(JSONProviderRegistry.get("crusoe"))()
|
|
params = config.get_supported_openai_params(model="meta-llama/Llama-3.3-70B-Instruct")
|
|
|
|
assert isinstance(params, list)
|
|
assert len(params) > 0
|
|
assert "temperature" in params
|
|
assert "max_tokens" in params
|
|
assert "stream" in params
|
|
|
|
|
|
def test_crusoe_param_mapping_max_completion_tokens():
|
|
"""Test max_completion_tokens is mapped to max_tokens for Crusoe"""
|
|
from litellm.llms.openai_like.dynamic_config import create_config_class
|
|
from litellm.llms.openai_like.json_loader import JSONProviderRegistry
|
|
|
|
config = create_config_class(JSONProviderRegistry.get("crusoe"))()
|
|
optional_params = config.map_openai_params(
|
|
non_default_params={"max_completion_tokens": 1024},
|
|
optional_params={},
|
|
model="meta-llama/Llama-3.3-70B-Instruct",
|
|
drop_params=False,
|
|
)
|
|
|
|
assert "max_tokens" in optional_params, "max_completion_tokens should be mapped to max_tokens"
|
|
assert optional_params["max_tokens"] == 1024
|
|
assert "max_completion_tokens" not in optional_params
|
|
|
|
|
|
def test_crusoe_provider_detection_by_prefix():
|
|
"""Test crusoe/model prefix is correctly routed"""
|
|
from litellm.litellm_core_utils.get_llm_provider_logic import get_llm_provider
|
|
|
|
model, provider, _, _ = get_llm_provider("crusoe/meta-llama/Llama-3.3-70B-Instruct")
|
|
assert provider == "crusoe"
|
|
assert model == "meta-llama/Llama-3.3-70B-Instruct"
|
|
|
|
|
|
def test_crusoe_model_list_populated(monkeypatch):
|
|
"""Test Crusoe models are present in model_prices_and_context_window.json"""
|
|
import litellm
|
|
|
|
original_model_cost = litellm.model_cost
|
|
original_env = os.environ.get("LITELLM_LOCAL_MODEL_COST_MAP")
|
|
try:
|
|
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", "True")
|
|
litellm.model_cost = litellm.get_model_cost_map(url="")
|
|
|
|
expected = [
|
|
"crusoe/meta-llama/Llama-3.3-70B-Instruct",
|
|
"crusoe/deepseek-ai/DeepSeek-R1-0528",
|
|
"crusoe/deepseek-ai/DeepSeek-V3-0324",
|
|
"crusoe/Qwen/Qwen3-235B-A22B-Instruct-2507",
|
|
"crusoe/moonshotai/Kimi-K2-Thinking",
|
|
"crusoe/openai/gpt-oss-120b",
|
|
"crusoe/google/gemma-3-12b-it",
|
|
]
|
|
for model in expected:
|
|
assert model in litellm.model_cost, f"{model} not found in model_cost"
|
|
assert litellm.model_cost[model].get("litellm_provider") == "crusoe"
|
|
finally:
|
|
litellm.model_cost = original_model_cost
|
|
if original_env is None:
|
|
os.environ.pop("LITELLM_LOCAL_MODEL_COST_MAP", None)
|
|
else:
|
|
monkeypatch.setenv("LITELLM_LOCAL_MODEL_COST_MAP", original_env)
|