This commit is contained in:
James Liounis 2026-08-26 08:09:03 -04:00 committed by GitHub
commit dc24f285c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 124 additions and 0 deletions

View file

@ -655,6 +655,7 @@ def cost_per_token(
if (
(model_info.get("input_cost_per_token") or 0.0) > 0
or (model_info.get("output_cost_per_token") or 0.0) > 0
or (model_info.get("input_cost_per_request") or 0.0) > 0
or model_info.get("tiered_pricing") is not None
):
return generic_cost_per_token(

View file

@ -918,6 +918,11 @@ def generic_cost_per_token(
service_tier=service_tier,
)
## FLAT PER-REQUEST COST
input_cost_per_request: Final = model_info.get("input_cost_per_request")
if input_cost_per_request:
prompt_cost += input_cost_per_request
## CALCULATE OUTPUT COST
text_tokens = 0
audio_tokens = 0

View file

@ -234,6 +234,7 @@ class ModelInfoBase(ProviderSpecificModelInfo, total=False):
input_cost_per_token_above_512k_tokens: float | None # MiniMax-M3: prompts >512K priced at 2x input
input_cost_per_character_above_128k_tokens: float | None # only for vertex ai models
input_cost_per_query: float | None # only for rerank models
input_cost_per_request: ReadOnly[float | None] # flat per-request pricing
input_cost_per_image: float | None # only for vertex ai models
input_cost_per_image_token: float | None # for gpt-image-1 and similar models
input_cost_per_video_token: float | None # for gemini omni models with video input
@ -3338,6 +3339,7 @@ class MirroredPricingParams(BaseModel):
class CustomPricingLiteLLMParams(MirroredPricingParams):
## CUSTOM PRICING ##
input_cost_per_second: float | None = None
input_cost_per_request: float | None = None
output_cost_per_second: float | None = None
output_cost_per_second_1080p: float | None = None
output_cost_per_second_480p: float | None = None

View file

@ -5781,6 +5781,7 @@ def _get_model_info_helper(
),
input_cost_per_token_above_512k_tokens=_model_info.get("input_cost_per_token_above_512k_tokens", None),
input_cost_per_query=_model_info.get("input_cost_per_query", None),
input_cost_per_request=_model_info.get("input_cost_per_request", None),
input_cost_per_second=_model_info.get("input_cost_per_second", None),
input_cost_per_audio_token=_model_info.get("input_cost_per_audio_token", None),
input_cost_per_image_token=_model_info.get("input_cost_per_image_token", None),

View file

@ -3517,3 +3517,114 @@ def test_generic_cost_per_token_grok_46_long_context(_local_model_cost_map):
)
assert prompt_cost == pytest.approx(200_000 * 4e-06 + 50_000 * 1e-06)
assert completion_cost == pytest.approx(1_000 * 1.2e-05)
@pytest.fixture
def flat_per_request_model():
"""Register a synthetic model whose only price is a flat per-request rate."""
from litellm.utils import _invalidate_model_cost_lowercase_map
model = "openai/flat-rate-test-model"
prev = litellm.model_cost.get(model)
litellm.model_cost[model] = {
"input_cost_per_request": 0.05,
"input_cost_per_token": 0.0,
"output_cost_per_token": 0.0,
"litellm_provider": "openai",
"mode": "chat",
}
_invalidate_model_cost_lowercase_map()
try:
yield model
finally:
if prev is None:
litellm.model_cost.pop(model, None)
else:
litellm.model_cost[model] = prev
_invalidate_model_cost_lowercase_map()
def test_flat_per_request_cost_applies_once(flat_per_request_model):
"""A flat per-request price bills the same regardless of token counts."""
from litellm.types.utils import Usage
small_usage = Usage(prompt_tokens=10, completion_tokens=5, total_tokens=15)
large_usage = Usage(prompt_tokens=10_000, completion_tokens=5_000, total_tokens=15_000)
small_prompt_cost, small_completion_cost = generic_cost_per_token(
model=flat_per_request_model,
usage=small_usage,
custom_llm_provider="openai",
)
large_prompt_cost, large_completion_cost = generic_cost_per_token(
model=flat_per_request_model,
usage=large_usage,
custom_llm_provider="openai",
)
assert small_prompt_cost == pytest.approx(0.05)
assert small_completion_cost == 0.0
assert large_prompt_cost == pytest.approx(0.05)
assert large_completion_cost == 0.0
def test_flat_per_request_cost_is_added_to_token_cost():
"""The flat rate stacks on top of token pricing rather than replacing it."""
from litellm.utils import _invalidate_model_cost_lowercase_map
from litellm.types.utils import Usage
model = "openai/flat-plus-tokens-test-model"
litellm.model_cost[model] = {
"input_cost_per_request": 0.05,
"input_cost_per_token": 1e-05,
"output_cost_per_token": 2e-05,
"litellm_provider": "openai",
"mode": "chat",
}
_invalidate_model_cost_lowercase_map()
try:
usage = Usage(prompt_tokens=100, completion_tokens=50, total_tokens=150)
prompt_cost, completion_cost = generic_cost_per_token(
model=model,
usage=usage,
custom_llm_provider="openai",
)
assert prompt_cost == pytest.approx(0.05 + 100 * 1e-05)
assert completion_cost == pytest.approx(50 * 2e-05)
finally:
litellm.model_cost.pop(model, None)
_invalidate_model_cost_lowercase_map()
def test_flat_per_request_cost_routes_through_cost_per_token():
"""The generic cost_per_token dispatch must not skip a model whose only
pricing is a flat per-request rate.
Uses a provider with no dedicated cost_per_token branch, so the model
genuinely reaches the generic fallback whose guard is under test.
"""
from litellm.cost_calculator import cost_per_token
from litellm.types.utils import Usage
from litellm.utils import _invalidate_model_cost_lowercase_map
model = "groq/flat-rate-only-test-model"
litellm.model_cost[model] = {
"input_cost_per_request": 0.05,
"input_cost_per_token": 0.0,
"output_cost_per_token": 0.0,
"litellm_provider": "groq",
"mode": "chat",
}
_invalidate_model_cost_lowercase_map()
try:
usage = Usage(prompt_tokens=10, completion_tokens=5, total_tokens=15)
prompt_cost, completion_cost = cost_per_token(
model=model,
custom_llm_provider="groq",
usage_object=usage,
)
assert prompt_cost == pytest.approx(0.05)
assert completion_cost == 0.0
finally:
litellm.model_cost.pop(model, None)
_invalidate_model_cost_lowercase_map()

View file

@ -27572,6 +27572,8 @@ export interface components {
input_cost_per_pixel?: number | null;
/** Input Cost Per Query */
input_cost_per_query?: number | null;
/** Input Cost Per Request */
input_cost_per_request?: number | null;
/** Input Cost Per Second */
input_cost_per_second?: number | null;
/** Input Cost Per Token */
@ -36792,6 +36794,8 @@ export interface components {
input_cost_per_pixel?: number | null;
/** Input Cost Per Query */
input_cost_per_query?: number | null;
/** Input Cost Per Request */
input_cost_per_request?: number | null;
/** Input Cost Per Second */
input_cost_per_second?: number | null;
/** Input Cost Per Token */