mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-11 08:38:24 +00:00
fix(lite): round Codex's default level down and describe a team's shared-name model from its own deployments
Move the reasoning strength helper to litellm_core_utils so lite and the Codex catalog import it without the SDK or the router, and let make lint-format-check-changed diff the working tree so a staged rename passes.
This commit is contained in:
parent
88f935b19e
commit
8b51849002
13 changed files with 146 additions and 73 deletions
2
Makefile
2
Makefile
|
|
@ -153,7 +153,7 @@ lint-install:
|
|||
# the same set CI's ':(glob)litellm/**/*.py' selects.
|
||||
lint-format-check-changed: $(LINT_DEP_INSTALL) $(LINT_DEP_BASE)
|
||||
@base_ref=$$($(RESOLVE_BASE)) && \
|
||||
changed=$$(git diff --name-only --diff-filter=ACMR "$$base_ref...HEAD" -- 'litellm/*.py') && \
|
||||
changed=$$(git diff --name-only --diff-filter=ACMR --merge-base "$$base_ref" -- 'litellm/*.py') && \
|
||||
files=$$(printf '%s\n' "$$changed" | grep -v '^litellm/enterprise/' || true) || exit $$?; \
|
||||
if [ -z "$$files" ]; then \
|
||||
echo "No changed litellm Python files to format-check."; \
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ from packaging.version import InvalidVersion, Version
|
|||
from pydantic import ConfigDict, Field, StringConstraints, TypeAdapter, ValidationError
|
||||
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.router_utils.reasoning_effort_strength import nearest_declared_reasoning_effort
|
||||
from litellm.litellm_core_utils.reasoning_effort_strength import nearest_declared_reasoning_effort
|
||||
from litellm.types.llms.base_model import LiteLLMBaseModel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -282,12 +282,13 @@ def codex_reasoning_levels(
|
|||
a version that does not parse, only the efforts every release knows are offered. The default is
|
||||
set whenever a level is offered, because Codex reads a missing one as `none`: `none` itself when the
|
||||
group accepts it, so a user who picks no level sends what Codex sent before, else medium, else the
|
||||
nearest stronger declared level, else the first one."""
|
||||
nearest weaker declared level, so picking no level never costs more reasoning than Codex's usual medium,
|
||||
else the weakest declared level, else the first one."""
|
||||
parses_any: Final = _parses_custom_efforts(client_version)
|
||||
offered: Final = tuple(effort for effort in efforts if parses_any or effort in _EFFORTS_EVERY_CODEX_PARSES)
|
||||
if not offered:
|
||||
return (), None
|
||||
nearest: Final = nearest_declared_reasoning_effort(_PREFERRED_DEFAULT_EFFORT, offered)
|
||||
nearest: Final = nearest_declared_reasoning_effort(_PREFERRED_DEFAULT_EFFORT, offered, "down")
|
||||
levels: Final = tuple(
|
||||
CodexReasoningLevel(effort=effort, description=f"Sends reasoning_effort={effort} upstream")
|
||||
for effort in offered
|
||||
|
|
|
|||
|
|
@ -17986,7 +17986,7 @@ def get_model_group_info(
|
|||
**_model_group_info.model_dump(),
|
||||
**model_group_custom_fields(
|
||||
llm_router.get_model_list_of_routed_group(
|
||||
resolve_model_group_alias(llm_router.model_group_alias, model) or model
|
||||
resolve_model_group_alias(llm_router.model_group_alias, model) or model, team_id
|
||||
)
|
||||
),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12635,11 +12635,7 @@ class Router:
|
|||
serves_team_deployments: Final = team_id is not None and bool(
|
||||
self._team_deployments_named(team_id, model_group)
|
||||
)
|
||||
model_list: Final = (
|
||||
self.get_all_deployments(model_name=model_group, team_id=team_id)
|
||||
if serves_team_deployments
|
||||
else self.get_model_list_of_routed_group(model_group)
|
||||
)
|
||||
model_list: Final = self.get_model_list_of_routed_group(model_group, team_id)
|
||||
model_group_description: Final[str | None] = next(
|
||||
(d for d in (_configured_model_info(m).get("description") for m in model_list) if isinstance(d, str)),
|
||||
None,
|
||||
|
|
@ -13997,17 +13993,20 @@ class Router:
|
|||
lambda name: self.wildcard_route_deployments(name, team_id),
|
||||
)
|
||||
|
||||
def get_model_list_of_routed_group(self, model_group: str) -> list[DeploymentTypedDict]:
|
||||
def get_model_list_of_routed_group(self, model_group: str, team_id: str | None = None) -> list[DeploymentTypedDict]:
|
||||
"""
|
||||
The deployments a request the router has already resolved to model_group is
|
||||
served from: the ones named model_group, the routing group of that name, or
|
||||
the wildcard route matching it when neither exists.
|
||||
served from: team_id's own deployments under that public name when the team has
|
||||
any, else the ones named model_group, the routing group of that name, or the
|
||||
wildcard route matching it when neither exists.
|
||||
|
||||
Unlike get_model_list, model_group's own model_group_alias entry is not
|
||||
followed. The router resolves an alias exactly once, so a group reached as
|
||||
an alias target is served by its own deployments, never by a second hop:
|
||||
in the chain X -> T -> U a request to X is served from T's deployments.
|
||||
"""
|
||||
if team_id is not None and self._team_deployments_named(team_id, model_group):
|
||||
return self.get_all_deployments(model_name=model_group, team_id=team_id)
|
||||
named: Final = [
|
||||
*self._get_all_deployments(model_name=model_group),
|
||||
*self.get_model_list_from_routing_groups(model_name=model_group),
|
||||
|
|
|
|||
|
|
@ -33,13 +33,13 @@ import litellm
|
|||
from litellm._logging import verbose_logger
|
||||
from litellm.litellm_core_utils.core_helpers import as_str_mapping
|
||||
from litellm.litellm_core_utils.get_supported_openai_params import get_supported_openai_params
|
||||
from litellm.litellm_core_utils.reasoning_effort_utils import reasoning_effort_from_thinking
|
||||
from litellm.llms.anthropic.common_utils import AnthropicModelInfo, validated_object_mapping
|
||||
from litellm.router_utils.reasoning_effort_strength import (
|
||||
from litellm.litellm_core_utils.reasoning_effort_strength import (
|
||||
REASONING_EFFORT_STRENGTH_RANK,
|
||||
ReasoningEffortRounding,
|
||||
nearest_declared_reasoning_effort,
|
||||
)
|
||||
from litellm.litellm_core_utils.reasoning_effort_utils import reasoning_effort_from_thinking
|
||||
from litellm.llms.anthropic.common_utils import AnthropicModelInfo, validated_object_mapping
|
||||
from litellm.types.llms.anthropic import AnthropicThinkingParam
|
||||
from litellm.types.llms.openai import REASONING_EFFORT
|
||||
|
||||
|
|
|
|||
|
|
@ -868,7 +868,7 @@ class TestCodexModelSync:
|
|||
"effort": "low",
|
||||
"description": "Sends reasoning_effort=low upstream",
|
||||
}
|
||||
assert entry["default_reasoning_level"] == "high"
|
||||
assert entry["default_reasoning_level"] == "low"
|
||||
|
||||
def test_known_models_and_unknown_or_empty_groups_keep_their_entries(self, tmp_path):
|
||||
listing = self._listing(self._row("gpt-5.5"), self._row("unknown"), self._row("empty"))
|
||||
|
|
|
|||
|
|
@ -139,7 +139,8 @@ _ABSENT = object()
|
|||
@pytest.mark.parametrize(
|
||||
("efforts", "client_version", "levels", "default"),
|
||||
[
|
||||
(("low", "high", "max"), "0.165.0", ["low", "high", "max"], "high"),
|
||||
(("low", "high", "max"), "0.165.0", ["low", "high", "max"], "low"),
|
||||
(("high", "xhigh", "max"), "0.165.0", ["high", "xhigh", "max"], "high"),
|
||||
(
|
||||
("none", "low", "medium", "high", "xhigh", "max"),
|
||||
"0.165.0",
|
||||
|
|
@ -150,9 +151,9 @@ _ABSENT = object()
|
|||
(("none", "low", "max"), "0.137.0", ["none", "low"], "none"),
|
||||
(("none",), "0.165.0", ["none"], "none"),
|
||||
(("low", "turbo"), "0.165.0", ["low", "turbo"], "low"),
|
||||
(("low", "high", "max"), "0.138.0", ["low", "high", "max"], "high"),
|
||||
(("low", "high", "max"), "0.137.9", ["low", "high"], "high"),
|
||||
(("low", "high", "max"), "0.138.0-alpha.2", ["low", "high"], "high"),
|
||||
(("low", "high", "max"), "0.138.0", ["low", "high", "max"], "low"),
|
||||
(("low", "high", "max"), "0.137.9", ["low", "high"], "low"),
|
||||
(("low", "high", "max"), "0.138.0-alpha.2", ["low", "high"], "low"),
|
||||
(("minimal", "max"), "not-a-version", ["minimal"], "minimal"),
|
||||
(("low", "turbo"), None, ["low"], "low"),
|
||||
(("max",), "0.137.0", [], _ABSENT),
|
||||
|
|
@ -652,12 +653,12 @@ def test_model_list_body_offers_what_model_group_info_reports_for_each_listed_mo
|
|||
|
||||
assert router.get_model_group_info("declared").supported_reasoning_efforts == ("low", "high", "max")
|
||||
assert offered("0.165.0") == {
|
||||
"declared": (["low", "high", "max"], "high"),
|
||||
"declared": (["low", "high", "max"], "low"),
|
||||
"unknown": ([], None),
|
||||
"non-reasoning": ([], None),
|
||||
"hidden-declared": (["low", "high", "max"], "high"),
|
||||
"hidden-declared": (["low", "high", "max"], "low"),
|
||||
}
|
||||
assert offered("0.137.0")["declared"] == (["low", "high"], "high")
|
||||
assert offered("0.137.0")["declared"] == (["low", "high"], "low")
|
||||
|
||||
|
||||
def test_catalog_rows_without_a_router_carry_only_the_listing():
|
||||
|
|
|
|||
|
|
@ -221,57 +221,39 @@ def test_deleted_model_params_stay_absent_after_reload_and_another_save(
|
|||
from litellm.types.router import Deployment, LiteLLM_Params, ModelInfo, updateDeployment, updateLiteLLMParams
|
||||
|
||||
configured: Final = {
|
||||
"model": "openai/test-model",
|
||||
"api_key": "synthetic-runtime-secret",
|
||||
"use_litellm_proxy": True,
|
||||
"use_in_pass_through": True,
|
||||
"use_chat_completions_api": False,
|
||||
"litellm_image_enabled": True,
|
||||
"timeout": 30,
|
||||
"input_cost_per_token": 0.003,
|
||||
"team_id": "param-only-team",
|
||||
"temperature": 0,
|
||||
"metadata": {},
|
||||
"model": "openai/test-model", "api_key": "synthetic-runtime-secret",
|
||||
"use_litellm_proxy": True, "use_in_pass_through": True, "use_chat_completions_api": False,
|
||||
"litellm_image_enabled": True, "timeout": 30, "input_cost_per_token": 0.003,
|
||||
"team_id": "param-only-team", "temperature": 0, "metadata": {},
|
||||
}
|
||||
original: Final = Deployment(
|
||||
model_name="editor-model",
|
||||
litellm_params=LiteLLM_Params.model_validate(configured),
|
||||
model_name="editor-model", litellm_params=LiteLLM_Params.model_validate(configured),
|
||||
model_info=ModelInfo(id="editor-model", team_id="owner-team", access_groups=["retained-group"]),
|
||||
)
|
||||
removals: Final = ("litellm_image_enabled", "use_litellm_proxy", "input_cost_per_token", "team_id")
|
||||
deleted: Final = update_db_model(
|
||||
db_model=original,
|
||||
updated_patch=PatchModelRequest(
|
||||
litellm_params=updateLiteLLMParams(max_retries=0),
|
||||
litellm_params_to_remove=removals,
|
||||
litellm_params=updateLiteLLMParams(max_retries=0), litellm_params_to_remove=removals,
|
||||
model_info=ModelInfo(id="editor-model", input_cost_per_token=0.003),
|
||||
),
|
||||
)
|
||||
saved_params: Final = json.loads(deleted["litellm_params"])
|
||||
saved_info: Final = json.loads(deleted["model_info"])
|
||||
assert saved_params == {
|
||||
**{key: value for key, value in configured.items() if key not in removals},
|
||||
"max_retries": 0,
|
||||
}
|
||||
assert saved_params == {**{key: value for key, value in configured.items() if key not in removals}, "max_retries": 0}
|
||||
assert "input_cost_per_token" not in saved_info
|
||||
assert saved_info["team_id"] == "owner-team"
|
||||
assert saved_info["access_groups"] == ["retained-group"]
|
||||
reloaded: Final = Deployment.model_validate(
|
||||
{"model_name": original.model_name, "litellm_params": saved_params, "model_info": saved_info}
|
||||
)
|
||||
router: Final = litellm.Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": "editor-model",
|
||||
"litellm_params": saved_params,
|
||||
"model_info": {"id": "editor-model", "db_model": True},
|
||||
}
|
||||
]
|
||||
)
|
||||
router: Final = litellm.Router(model_list=[{
|
||||
"model_name": "editor-model", "litellm_params": saved_params,
|
||||
"model_info": {"id": "editor-model", "db_model": True},
|
||||
}])
|
||||
stored_params: Final = {**saved_params, "api_key": "synthetic-stored-secret"}
|
||||
mock_prisma.db.litellm_proxymodeltable.find_unique.return_value = LiteLLM_ProxyModelTable(
|
||||
model_id="editor-model",
|
||||
model_name="editor-model",
|
||||
model_id="editor-model", model_name="editor-model",
|
||||
litellm_params=json.dumps(stored_params) if encoded else stored_params,
|
||||
model_info={"id": "editor-model"},
|
||||
)
|
||||
|
|
@ -1051,6 +1033,67 @@ def test_model_group_info_describes_a_team_model_under_the_public_name_its_team_
|
|||
assert groups[_BYOK_PUBLIC_NAME]["supported_reasoning_efforts"] == efforts
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("team_id", "providers", "efforts", "custom_tags"),
|
||||
[
|
||||
(_BYOK_TEAM_ID, ["fireworks_ai"], ["low", "high", "max"], ["team-tag"]),
|
||||
("team-other", ["openai"], [], ["global-tag"]),
|
||||
],
|
||||
)
|
||||
def test_model_group_info_describes_the_deployments_a_shared_name_routes_the_team_to(
|
||||
client, auth_as, monkeypatch, team_id: str, providers: list[str], efforts: list[str], custom_tags: list[str]
|
||||
):
|
||||
"""A team model whose public name is also a global model's name routes the team's requests to the
|
||||
team's deployment, so the team's row must describe that deployment in every field, while another
|
||||
team still sees the global model."""
|
||||
from litellm.proxy._types import LitellmUserRoles
|
||||
from litellm.proxy.agent_endpoints import model_list_helpers
|
||||
|
||||
router: Final = litellm.Router(
|
||||
model_list=[
|
||||
{
|
||||
"model_name": _BYOK_PUBLIC_NAME,
|
||||
"litellm_params": {"model": "openai/gpt-4.1-mini"},
|
||||
"model_info": {"supports_reasoning": False, "custom_tags": ["global-tag"]},
|
||||
},
|
||||
{
|
||||
"model_name": _BYOK_INTERNAL_NAME,
|
||||
"litellm_params": {"model": "fireworks_ai/accounts/fireworks/models/kimi-k3"},
|
||||
"model_info": {
|
||||
"team_id": _BYOK_TEAM_ID,
|
||||
"team_public_model_name": _BYOK_PUBLIC_NAME,
|
||||
"supports_reasoning": True,
|
||||
"reasoning_effort_levels": ["low", "high", "max"],
|
||||
"custom_tags": ["team-tag"],
|
||||
},
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
async def append_agents_to_model_group(*, model_groups, user_api_key_dict):
|
||||
return model_groups
|
||||
|
||||
monkeypatch.setattr(proxy_server, "llm_router", router)
|
||||
monkeypatch.setattr(proxy_server, "llm_model_list", router.model_list)
|
||||
monkeypatch.setattr(proxy_server, "user_model", None)
|
||||
monkeypatch.setattr(proxy_server, "general_settings", {})
|
||||
monkeypatch.setattr(proxy_server, "prisma_client", None)
|
||||
monkeypatch.setattr(
|
||||
model_list_helpers, "append_agents_to_model_group", AsyncMock(side_effect=append_agents_to_model_group)
|
||||
)
|
||||
|
||||
with auth_as(LitellmUserRoles.INTERNAL_USER, user_id=None, team_id=team_id, team_models=[_BYOK_PUBLIC_NAME]):
|
||||
response: Final = client.get("/model_group/info")
|
||||
|
||||
assert response.status_code == 200
|
||||
group: Final = {group["model_group"]: group for group in response.json()["data"]}[_BYOK_PUBLIC_NAME]
|
||||
assert (group["providers"], group["supported_reasoning_efforts"], group["custom_tags"]) == (
|
||||
providers,
|
||||
efforts,
|
||||
custom_tags,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /v2/model/info?exclude_auto_routers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -22,7 +22,9 @@ from litellm.types.router import DeploymentModelListingInfo
|
|||
from .conftest import normalize # type: ignore[import-not-found]
|
||||
|
||||
|
||||
def _stub_model_info_response(model_id: str = "gpt-4", provider: str = "openai") -> dict:
|
||||
def _stub_model_info_response(
|
||||
model_id: str = "gpt-4", provider: str = "openai"
|
||||
) -> dict:
|
||||
return {
|
||||
"id": model_id,
|
||||
"object": "model",
|
||||
|
|
@ -64,7 +66,9 @@ def patched_models(monkeypatch):
|
|||
def _fake_create_model_info_response(model_id, provider="openai", **kwargs):
|
||||
return _stub_model_info_response(model_id=model_id, provider=provider)
|
||||
|
||||
monkeypatch.setattr(proxy_utils, "create_model_info_response", _fake_create_model_info_response)
|
||||
monkeypatch.setattr(
|
||||
proxy_utils, "create_model_info_response", _fake_create_model_info_response
|
||||
)
|
||||
|
||||
monkeypatch.setattr(proxy_utils, "validate_model_access", lambda **kwargs: None)
|
||||
|
||||
|
|
@ -103,7 +107,9 @@ def test_get_models_happy_path(client, auth_as, patched_models, path):
|
|||
|
||||
|
||||
@pytest.mark.parametrize("path", ["/v1/models", "/models"])
|
||||
def test_get_models_anthropic_format_when_header_present(client, auth_as, patched_models, path):
|
||||
def test_get_models_anthropic_format_when_header_present(
|
||||
client, auth_as, patched_models, path
|
||||
):
|
||||
"""Pins: ``GET /v1/models`` returns the Anthropic-native models shape when
|
||||
the caller sends an ``anthropic-version`` header (Claude Code gateway
|
||||
discovery), while the default OpenAI shape is unchanged without it."""
|
||||
|
|
@ -123,7 +129,9 @@ def test_get_models_anthropic_format_when_header_present(client, auth_as, patche
|
|||
|
||||
|
||||
@pytest.mark.parametrize("path", ["/v1/models", "/models"])
|
||||
def test_anthropic_format_exposes_token_limits(client, auth_as, patched_models, monkeypatch, path):
|
||||
def test_anthropic_format_exposes_token_limits(
|
||||
client, auth_as, patched_models, monkeypatch, path
|
||||
):
|
||||
"""Claude Code sizes requests off the listing, so the Anthropic-native entries
|
||||
carry the same token limits the OpenAI listing resolves, with the output budget
|
||||
named max_tokens as the Messages API names it."""
|
||||
|
|
@ -138,7 +146,9 @@ def test_anthropic_format_exposes_token_limits(client, auth_as, patched_models,
|
|||
"max_output_tokens": 64000,
|
||||
}
|
||||
|
||||
monkeypatch.setattr(proxy_utils, "create_model_info_response", _create_model_info_response)
|
||||
monkeypatch.setattr(
|
||||
proxy_utils, "create_model_info_response", _create_model_info_response
|
||||
)
|
||||
|
||||
with auth_as():
|
||||
response = client.get(path, headers={"anthropic-version": "2023-06-01"})
|
||||
|
|
@ -213,7 +223,9 @@ def test_anthropic_format_uses_configured_display_name(client, auth_as, patched_
|
|||
|
||||
|
||||
@pytest.mark.parametrize("params", [{}, {"scope": "expand"}])
|
||||
def test_anthropic_display_name_resolved_via_internal_team_key(client, auth_as, patched_models, monkeypatch, params):
|
||||
def test_anthropic_display_name_resolved_via_internal_team_key(
|
||||
client, auth_as, patched_models, monkeypatch, params
|
||||
):
|
||||
"""For a team-scoped row the configured display name must be looked up by the
|
||||
internal routing key while the entry itself is keyed by the public name, so
|
||||
the clean name lands on the id the client actually sees."""
|
||||
|
|
@ -246,10 +258,14 @@ def test_anthropic_display_name_resolved_via_internal_team_key(client, auth_as,
|
|||
"get_available_models_for_user",
|
||||
_fake_get_available_models_for_user,
|
||||
)
|
||||
monkeypatch.setattr(model_checks, "get_complete_model_list", lambda **kwargs: [internal_name])
|
||||
monkeypatch.setattr(
|
||||
model_checks, "get_complete_model_list", lambda **kwargs: [internal_name]
|
||||
)
|
||||
|
||||
with auth_as():
|
||||
response = client.get("/v1/models", params=params, headers={"anthropic-version": "2023-06-01"})
|
||||
response = client.get(
|
||||
"/v1/models", params=params, headers={"anthropic-version": "2023-06-01"}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
(entry,) = response.json()["data"]
|
||||
|
|
@ -290,7 +306,9 @@ def test_get_model_by_id_not_found(client, auth_as, patched_models, path):
|
|||
|
||||
|
||||
@pytest.mark.parametrize("params", [{}, {"scope": "expand"}])
|
||||
def test_anthropic_format_returns_public_team_model_name(client, auth_as, patched_models, monkeypatch, params):
|
||||
def test_anthropic_format_returns_public_team_model_name(
|
||||
client, auth_as, patched_models, monkeypatch, params
|
||||
):
|
||||
"""Regression: the Anthropic-native listing must go through the same team
|
||||
name translation as the OpenAI listing, so a caller never sees the internal
|
||||
``model_name_{team_id}_{uuid}`` routing key."""
|
||||
|
|
@ -320,10 +338,14 @@ def test_anthropic_format_returns_public_team_model_name(client, auth_as, patche
|
|||
"get_available_models_for_user",
|
||||
_fake_get_available_models_for_user,
|
||||
)
|
||||
monkeypatch.setattr(model_checks, "get_complete_model_list", lambda **kwargs: [internal_name])
|
||||
monkeypatch.setattr(
|
||||
model_checks, "get_complete_model_list", lambda **kwargs: [internal_name]
|
||||
)
|
||||
|
||||
with auth_as():
|
||||
response = client.get("/v1/models", params=params, headers={"anthropic-version": "2023-06-01"})
|
||||
response = client.get(
|
||||
"/v1/models", params=params, headers={"anthropic-version": "2023-06-01"}
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert [m["id"] for m in response.json()["data"]] == ["gpt-4-team"]
|
||||
|
|
@ -354,9 +376,7 @@ def test_anthropic_format_lists_claude_code_view_ids_for_claude_code(
|
|||
|
||||
patched_models.model_group_alias = {}
|
||||
patched_models.has_model_id.return_value = False
|
||||
patched_models.get_candidate_model_ids_for_route.side_effect = lambda name, team_id=None: (
|
||||
frozenset({name}) if name in ("gpt-4", "claude-sonnet") else frozenset()
|
||||
)
|
||||
patched_models.get_candidate_model_ids_for_route.side_effect = lambda name, team_id=None: frozenset({name}) if name in ("gpt-4", "claude-sonnet") else frozenset()
|
||||
monkeypatch.setattr(proxy_utils, "create_model_info_response", _create_model_info_response)
|
||||
|
||||
with auth_as():
|
||||
|
|
@ -376,9 +396,7 @@ def test_anthropic_format_lists_claude_code_view_ids_for_claude_code(
|
|||
def test_anthropic_format_keeps_served_ids_for_other_anthropic_clients(client, auth_as, patched_models, path):
|
||||
"""An Anthropic SDK asking for the vendor shape gets the served ids: the view is Claude Code's alone."""
|
||||
with auth_as():
|
||||
response = client.get(
|
||||
path, headers={"anthropic-version": "2023-06-01", "user-agent": "anthropic-sdk-python/0.40"}
|
||||
)
|
||||
response = client.get(path, headers={"anthropic-version": "2023-06-01", "user-agent": "anthropic-sdk-python/0.40"})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert [m["id"] for m in response.json()["data"]] == ["gpt-4", "claude-sonnet"]
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ class _FakeRouter:
|
|||
def get_model_group_info(self, model_group: str, team_id: str | None = None) -> ModelGroupInfo | None:
|
||||
return self.infos.get(model_group)
|
||||
|
||||
def get_model_list_of_routed_group(self, model_group: str) -> list[DeploymentTypedDict]:
|
||||
def get_model_list_of_routed_group(self, model_group: str, team_id: str | None = None) -> list[DeploymentTypedDict]:
|
||||
return []
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from litellm.router_utils.reasoning_effort_capability import (
|
|||
resolve_supported_reasoning_efforts,
|
||||
thinking_budget_effort,
|
||||
)
|
||||
from litellm.router_utils.reasoning_effort_strength import REASONING_EFFORT_STRENGTH_ORDER
|
||||
from litellm.litellm_core_utils.reasoning_effort_strength import REASONING_EFFORT_STRENGTH_ORDER
|
||||
|
||||
|
||||
class TestDeploymentIsCatalogMapped:
|
||||
|
|
|
|||
|
|
@ -119,6 +119,17 @@ def test_explicit_base_works_without_remote_access(
|
|||
assert "No changed litellm Python files" in checked.stdout
|
||||
|
||||
|
||||
def test_format_check_reads_staged_renames(remote_and_clone: tuple[Path, Path]) -> None:
|
||||
_, repo = remote_and_clone
|
||||
(repo / "litellm" / "before_move.py").write_text("value = 2\n")
|
||||
_commit(repo, "add module")
|
||||
_git(repo, "mv", "litellm/before_move.py", "litellm/after_move.py")
|
||||
checked: Final = _make(repo, "lint-format-check-changed", "BASE_REF=origin/release_branch", "UV_RUN=echo")
|
||||
assert checked.returncode == 0, checked.stdout + checked.stderr
|
||||
assert "litellm/after_move.py" in checked.stdout
|
||||
assert "before_move.py" not in checked.stdout
|
||||
|
||||
|
||||
def _freshness(repo: Path, *args: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue