mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
Merge pull request #36215 from BerriAI/litellm_declare_model_info_pricing
refactor(types): declare mirrored pricing fields on ModelInfo
This commit is contained in:
commit
d0758a291c
4 changed files with 108 additions and 16 deletions
|
|
@ -17,7 +17,12 @@ from .completion import CompletionRequest
|
|||
from .embedding import EmbeddingRequest
|
||||
from .llms.openai import OpenAIFileObject
|
||||
from .search import SearchProvider
|
||||
from .utils import CustomPricingLiteLLMParams, ModelResponse, StandardLoggingRoutingDecision
|
||||
from .utils import (
|
||||
CustomPricingLiteLLMParams,
|
||||
MirroredPricingParams,
|
||||
ModelResponse,
|
||||
StandardLoggingRoutingDecision,
|
||||
)
|
||||
|
||||
|
||||
class ConfigurableClientsideParamsCustomAuth(TypedDict):
|
||||
|
|
@ -122,7 +127,7 @@ class UpdateRouterConfig(BaseModel):
|
|||
model_config = ConfigDict(protected_namespaces=())
|
||||
|
||||
|
||||
class ModelInfo(BaseModel):
|
||||
class ModelInfo(MirroredPricingParams):
|
||||
id: str | None # Allow id to be optional on input, but it will always be present as a str in the model instance
|
||||
db_model: bool = False # used for proxy - to separate models which are stored in the db vs. config.
|
||||
updated_at: datetime.datetime | None = None
|
||||
|
|
@ -424,14 +429,7 @@ class DeploymentTypedDict(TypedDict, total=False):
|
|||
model_info: dict
|
||||
|
||||
|
||||
SPECIAL_MODEL_INFO_PARAMS = [
|
||||
"input_cost_per_token",
|
||||
"output_cost_per_token",
|
||||
"input_cost_per_character",
|
||||
"output_cost_per_character",
|
||||
"cache_read_input_token_cost",
|
||||
"cache_creation_input_token_cost",
|
||||
]
|
||||
SPECIAL_MODEL_INFO_PARAMS = tuple(MirroredPricingParams.model_fields)
|
||||
|
||||
|
||||
class Deployment(BaseModel):
|
||||
|
|
|
|||
|
|
@ -3245,10 +3245,23 @@ class StandardCallbackDynamicParams(TypedDict, total=False):
|
|||
litellm_disabled_callbacks: list[str] | None
|
||||
|
||||
|
||||
class CustomPricingLiteLLMParams(BaseModel):
|
||||
## CUSTOM PRICING ##
|
||||
class MirroredPricingParams(BaseModel):
|
||||
"""Pricing overrides that ``Deployment.__init__`` mirrors from ``litellm_params``
|
||||
onto ``model_info``, so both blobs hold the same rate.
|
||||
|
||||
Declared once and inherited by both sides of that mirror, so the two can't drift.
|
||||
"""
|
||||
|
||||
input_cost_per_token: float | None = None
|
||||
output_cost_per_token: float | None = None
|
||||
input_cost_per_character: float | None = None
|
||||
output_cost_per_character: float | None = None
|
||||
cache_read_input_token_cost: float | None = None
|
||||
cache_creation_input_token_cost: float | None = None
|
||||
|
||||
|
||||
class CustomPricingLiteLLMParams(MirroredPricingParams):
|
||||
## CUSTOM PRICING ##
|
||||
input_cost_per_second: float | None = None
|
||||
output_cost_per_second: float | None = None
|
||||
output_cost_per_second_1080p: float | None = None
|
||||
|
|
@ -3259,7 +3272,6 @@ class CustomPricingLiteLLMParams(BaseModel):
|
|||
# This allows any model_info parameter to be set in litellm_params
|
||||
input_cost_per_token_flex: float | None = None
|
||||
input_cost_per_token_priority: float | None = None
|
||||
cache_creation_input_token_cost: float | None = None
|
||||
cache_creation_input_token_cost_above_1hr: float | None = None
|
||||
cache_creation_input_token_cost_above_200k_tokens: float | None = None
|
||||
cache_creation_input_token_cost_above_272k_tokens: float | None = None
|
||||
|
|
@ -3268,7 +3280,6 @@ class CustomPricingLiteLLMParams(BaseModel):
|
|||
cache_creation_input_token_cost_flex: float | None = None
|
||||
cache_creation_input_token_cost_priority: float | None = None
|
||||
cache_creation_input_audio_token_cost: float | None = None
|
||||
cache_read_input_token_cost: float | None = None
|
||||
cache_read_input_token_cost_flex: float | None = None
|
||||
cache_read_input_token_cost_priority: float | None = None
|
||||
cache_read_input_token_cost_above_200k_tokens: float | None = None
|
||||
|
|
@ -3276,7 +3287,6 @@ class CustomPricingLiteLLMParams(BaseModel):
|
|||
cache_read_input_token_cost_above_272k_tokens_priority: float | None = None
|
||||
cache_read_input_token_cost_above_272k_tokens_flex: float | None = None
|
||||
cache_read_input_audio_token_cost: float | None = None
|
||||
input_cost_per_character: float | None = None
|
||||
input_cost_per_character_above_128k_tokens: float | None = None
|
||||
input_cost_per_audio_token: float | None = None
|
||||
input_cost_per_token_cache_hit: float | None = None
|
||||
|
|
@ -3298,7 +3308,6 @@ class CustomPricingLiteLLMParams(BaseModel):
|
|||
output_cost_per_token_batches: float | None = None
|
||||
output_cost_per_token_flex: float | None = None
|
||||
output_cost_per_token_priority: float | None = None
|
||||
output_cost_per_character: float | None = None
|
||||
output_cost_per_audio_token: float | None = None
|
||||
output_cost_per_token_above_128k_tokens: float | None = None
|
||||
output_cost_per_token_above_200k_tokens: float | None = None
|
||||
|
|
|
|||
73
tests/test_litellm/types/test_router.py
Normal file
73
tests/test_litellm/types/test_router.py
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
import pytest
|
||||
|
||||
from litellm.types.router import (
|
||||
SPECIAL_MODEL_INFO_PARAMS,
|
||||
Deployment,
|
||||
LiteLLM_Params,
|
||||
ModelInfo,
|
||||
)
|
||||
from litellm.types.utils import CustomPricingLiteLLMParams, MirroredPricingParams
|
||||
|
||||
|
||||
def test_model_info_declares_mirrored_pricing_fields():
|
||||
"""The pricing keys Deployment mirrors onto model_info must be declared fields, not
|
||||
extras that only survive because ModelInfo sets extra="allow"."""
|
||||
for field in SPECIAL_MODEL_INFO_PARAMS:
|
||||
assert field in ModelInfo.model_fields
|
||||
|
||||
info = ModelInfo(id="x", input_cost_per_token=1e-06)
|
||||
assert info.__pydantic_extra__ == {}
|
||||
assert info.input_cost_per_token == 1e-06
|
||||
|
||||
|
||||
def test_special_model_info_params_cannot_drift_from_the_mirror():
|
||||
assert SPECIAL_MODEL_INFO_PARAMS == tuple(MirroredPricingParams.model_fields)
|
||||
assert set(SPECIAL_MODEL_INFO_PARAMS) <= set(CustomPricingLiteLLMParams.model_fields)
|
||||
assert set(SPECIAL_MODEL_INFO_PARAMS) <= set(LiteLLM_Params.model_fields)
|
||||
|
||||
|
||||
def test_custom_pricing_params_keeps_every_field_it_had():
|
||||
"""The mirrored fields moved to a base class; none of them may go missing from
|
||||
CustomPricingLiteLLMParams, whose model_fields drive custom-pricing detection."""
|
||||
for field in (
|
||||
"input_cost_per_token",
|
||||
"output_cost_per_token",
|
||||
"input_cost_per_character",
|
||||
"output_cost_per_character",
|
||||
"cache_read_input_token_cost",
|
||||
"cache_creation_input_token_cost",
|
||||
"input_cost_per_second",
|
||||
"cache_read_input_token_cost_flex",
|
||||
"input_cost_per_character_above_128k_tokens",
|
||||
"output_cost_per_audio_token",
|
||||
):
|
||||
assert field in CustomPricingLiteLLMParams.model_fields
|
||||
|
||||
|
||||
@pytest.mark.parametrize("field", SPECIAL_MODEL_INFO_PARAMS)
|
||||
def test_deployment_mirrors_pricing_from_litellm_params_onto_model_info(field):
|
||||
deployment = Deployment(
|
||||
model_name="my-model",
|
||||
litellm_params=LiteLLM_Params(model="gpt-4o", **{field: 3e-06}),
|
||||
)
|
||||
assert getattr(deployment.model_info, field) == 3e-06
|
||||
assert deployment.model_info.model_dump(exclude_none=True)[field] == 3e-06
|
||||
|
||||
|
||||
def test_unset_pricing_is_still_absent_from_dumps():
|
||||
"""/model/info responses and DB writes dump model_info with exclude_none=True, so
|
||||
declaring the pricing fields must not start emitting ~6 null keys per deployment."""
|
||||
dumped = ModelInfo(id="x").model_dump(exclude_none=True)
|
||||
assert [field for field in SPECIAL_MODEL_INFO_PARAMS if field in dumped] == []
|
||||
|
||||
|
||||
def test_pricing_strings_are_coerced_to_float():
|
||||
"""Cost values arrive from the DB and the Admin UI as strings; they must land as
|
||||
floats so cost calculation doesn't multiply a str."""
|
||||
info = ModelInfo(id="x", output_cost_per_token="0.000002")
|
||||
assert info.output_cost_per_token == 2e-06
|
||||
|
||||
|
||||
def test_invalid_pricing_is_rejected():
|
||||
with pytest.raises(ValueError):
|
||||
ModelInfo(id="x", input_cost_per_token="free")
|
||||
12
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
12
ui/litellm-dashboard/src/lib/http/schema.d.ts
generated
vendored
|
|
@ -35294,6 +35294,10 @@ export interface components {
|
|||
base_model?: string | null;
|
||||
/** Blocked */
|
||||
blocked?: boolean | null;
|
||||
/** Cache Creation Input Token Cost */
|
||||
cache_creation_input_token_cost?: number | null;
|
||||
/** Cache Read Input Token Cost */
|
||||
cache_read_input_token_cost?: number | null;
|
||||
/** Created At */
|
||||
created_at?: string | null;
|
||||
/** Created By */
|
||||
|
|
@ -35305,6 +35309,14 @@ export interface components {
|
|||
db_model: boolean;
|
||||
/** Id */
|
||||
id: string | null;
|
||||
/** Input Cost Per Character */
|
||||
input_cost_per_character?: number | null;
|
||||
/** Input Cost Per Token */
|
||||
input_cost_per_token?: number | null;
|
||||
/** Output Cost Per Character */
|
||||
output_cost_per_character?: number | null;
|
||||
/** Output Cost Per Token */
|
||||
output_cost_per_token?: number | null;
|
||||
/** Team Id */
|
||||
team_id?: string | null;
|
||||
/** Team Public Model Name */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue