mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-15 09:12:18 +00:00
fix(cost_map): fill missing Claude capability fields from the family baseline
Mapped Claude entries on Anthropic, Azure Foundry, Bedrock and Vertex now get supports_parallel_function_calling and supports_system_messages from the claude-family-baseline rule when the entry leaves them out. Values set on an entry still win
This commit is contained in:
parent
4573fa1f31
commit
203f692b0b
3 changed files with 60 additions and 5 deletions
|
|
@ -63749,7 +63749,8 @@
|
|||
{
|
||||
"name": "claude-family-baseline",
|
||||
"pattern": "claude-[a-z]+-\\d+(?:[-.]\\d+)?(?:-\\d{8})?",
|
||||
"description": "Any Claude family-major id with an optional minor and an optional 8-digit date suffix, under any provider namespace (bare, bedrock-dotted, vertex, databricks, ...), so bare majors like claude-newfamily-5 get the same baseline as claude-newfamily-5-1. Carries the model-family facts every Claude shares; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number.",
|
||||
"fill_missing_for_providers": ["anthropic", "azure_ai", "bedrock", "bedrock_converse", "vertex_ai-anthropic_models"],
|
||||
"description": "Any Claude family-major id with an optional minor and an optional 8-digit date suffix, under any provider namespace (bare, bedrock-dotted, vertex, databricks, ...), so bare majors like claude-newfamily-5 get the same baseline as claude-newfamily-5-1. Carries the model-family facts every Claude shares; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number. On mapped Claude entries from Anthropic, Azure Foundry, Bedrock and Vertex it only fills the keys an entry leaves out, so the entry's own values always win.",
|
||||
"model_info": {
|
||||
"mode": "chat",
|
||||
"max_input_tokens": 200000,
|
||||
|
|
|
|||
|
|
@ -63749,7 +63749,8 @@
|
|||
{
|
||||
"name": "claude-family-baseline",
|
||||
"pattern": "claude-[a-z]+-\\d+(?:[-.]\\d+)?(?:-\\d{8})?",
|
||||
"description": "Any Claude family-major id with an optional minor and an optional 8-digit date suffix, under any provider namespace (bare, bedrock-dotted, vertex, databricks, ...), so bare majors like claude-newfamily-5 get the same baseline as claude-newfamily-5-1. Carries the model-family facts every Claude shares; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number.",
|
||||
"fill_missing_for_providers": ["anthropic", "azure_ai", "bedrock", "bedrock_converse", "vertex_ai-anthropic_models"],
|
||||
"description": "Any Claude family-major id with an optional minor and an optional 8-digit date suffix, under any provider namespace (bare, bedrock-dotted, vertex, databricks, ...), so bare majors like claude-newfamily-5 get the same baseline as claude-newfamily-5-1. Carries the model-family facts every Claude shares; it carries no pricing, so cost stays on the standard unpriced behavior rather than a guessed number. On mapped Claude entries from Anthropic, Azure Foundry, Bedrock and Vertex it only fills the keys an entry leaves out, so the entry's own values always win.",
|
||||
"model_info": {
|
||||
"mode": "chat",
|
||||
"max_input_tokens": 200000,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ resolution (get_model_info) including the shipped rules in the bundled cost map.
|
|||
|
||||
import json
|
||||
import logging
|
||||
from collections.abc import Callable
|
||||
import re
|
||||
from collections.abc import Callable, Mapping
|
||||
from pathlib import Path
|
||||
from typing import Final
|
||||
|
||||
|
|
@ -914,11 +915,11 @@ def test_shipped_claude_thinking_rules_backfill_only_anthropic(shipped_cost_map)
|
|||
assert info.get("supports_adaptive_thinking") is None
|
||||
assert info.get("supports_legacy_thinking") is None
|
||||
assert info.get("max_input_tokens") is None
|
||||
assert match_fill_missing_generalizations("claude-sonnet-4-6", "anthropic") == {
|
||||
assert match_fill_missing_generalizations("claude-sonnet-4-6", "anthropic").items() >= {
|
||||
"supports_adaptive_thinking": True,
|
||||
"supports_legacy_thinking": True,
|
||||
"supports_tool_search": True,
|
||||
}
|
||||
}.items()
|
||||
assert match_fill_missing_generalizations("claude-sonnet-4-6", "perplexity") is None
|
||||
|
||||
|
||||
|
|
@ -961,3 +962,55 @@ def test_shipped_tool_search_rule_fills_mapped_claude_entries_without_flag(shipp
|
|||
assert match_fill_missing_generalizations("claude-opus-5", "bedrock")["supports_tool_search"] is True
|
||||
assert match_fill_missing_generalizations("claude-opus-5", "azure_ai")["supports_tool_search"] is True
|
||||
assert match_fill_missing_generalizations("claude-opus-5", "perplexity") is None
|
||||
|
||||
|
||||
CLAUDE_BASELINE_FILL_PROVIDERS: Final = ("anthropic", "azure_ai", "bedrock", "bedrock_converse", "vertex_ai-anthropic_models")
|
||||
|
||||
|
||||
def _claude_baseline() -> Mapping[str, object]:
|
||||
return next(rule for rule in get_fallback_generalization_rules() if rule["name"] == "claude-family-baseline")
|
||||
|
||||
|
||||
def _mapped_claude_chat_entries() -> tuple[tuple[str, Mapping[str, object]], ...]:
|
||||
pattern: Final = re.compile(_claude_baseline()["pattern"], re.IGNORECASE)
|
||||
return tuple(
|
||||
(key, entry)
|
||||
for key, entry in litellm.model_cost.items()
|
||||
if isinstance(entry, dict) and entry.get("mode") == "chat" and pattern.search(key) is not None
|
||||
)
|
||||
|
||||
|
||||
def _fields_overriding_baseline(entry: Mapping[str, object], baseline: Mapping[str, object]) -> frozenset[str]:
|
||||
return frozenset(field for field in entry.keys() & baseline.keys() if entry[field] != baseline[field])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("provider", CLAUDE_BASELINE_FILL_PROVIDERS)
|
||||
def test_shipped_claude_baseline_fills_only_missing_keys_on_mapped_entries(shipped_cost_map, provider):
|
||||
"""A mapped Claude entry from a first-party host gets each baseline key it leaves out,
|
||||
while every baseline key the entry sets keeps the entry's own value."""
|
||||
baseline: Final = _claude_baseline()["model_info"]
|
||||
entries: Final = tuple((key, entry) for key, entry in _mapped_claude_chat_entries() if entry["litellm_provider"] == provider)
|
||||
assert any(baseline.keys() - entry.keys() for _, entry in entries)
|
||||
assert any(_fields_overriding_baseline(entry, baseline) for _, entry in entries)
|
||||
|
||||
for key, entry in entries:
|
||||
info = litellm.get_model_info(key)
|
||||
assert info["key"] == key
|
||||
assert {field: info[field] for field in baseline} == {
|
||||
field: entry.get(field, value) for field, value in baseline.items()
|
||||
}, key
|
||||
|
||||
|
||||
def test_shipped_claude_baseline_leaves_other_providers_entries_unfilled(shipped_cost_map):
|
||||
baseline: Final = _claude_baseline()["model_info"]
|
||||
entries: Final = tuple(
|
||||
(key, entry)
|
||||
for key, entry in _mapped_claude_chat_entries()
|
||||
if entry["litellm_provider"] not in CLAUDE_BASELINE_FILL_PROVIDERS
|
||||
)
|
||||
assert any("supports_parallel_function_calling" not in entry for _, entry in entries)
|
||||
|
||||
for key, entry in entries:
|
||||
info = litellm.get_model_info(key)
|
||||
missing = baseline.keys() - entry.keys()
|
||||
assert {field: info.get(field) for field in missing} == dict.fromkeys(missing), key
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue