From d0ac49414432522192b80faac1eaffd6c9b49197 Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 3 Sep 2026 13:31:56 -0700 Subject: [PATCH 1/3] fix(cost): honor off_peak_pricing in the fireworks_ai and perplexity cost calculators --- .../litellm_core_utils/llm_cost_calc/utils.py | 4 +- litellm/llms/fireworks_ai/cost_calculator.py | 50 +++++----- litellm/llms/perplexity/cost_calculator.py | 17 +++- .../test_fireworks_ai_cost_calculator.py | 75 +++++++++++++++ .../test_perplexity_cost_calculator.py | 92 +++++++++++++++++++ 5 files changed, 209 insertions(+), 29 deletions(-) diff --git a/litellm/litellm_core_utils/llm_cost_calc/utils.py b/litellm/litellm_core_utils/llm_cost_calc/utils.py index b34c416cd40..21587af73aa 100644 --- a/litellm/litellm_core_utils/llm_cost_calc/utils.py +++ b/litellm/litellm_core_utils/llm_cost_calc/utils.py @@ -428,7 +428,7 @@ def _coerce_off_peak_rate(value: object, default: float) -> float: return default -def _apply_off_peak_pricing( +def apply_off_peak_pricing( model_info: ModelInfo, current_time: datetime | None, prompt_base_cost: float, @@ -462,7 +462,7 @@ def _apply_off_peak_to_base_costs( has no field for them. """ prompt, completion, cache_creation, cache_creation_above_1hr, cache_read = base_costs - off_peak_prompt, off_peak_completion, off_peak_cache_read = _apply_off_peak_pricing( + off_peak_prompt, off_peak_completion, off_peak_cache_read = apply_off_peak_pricing( model_info, current_time, prompt, completion, cache_read ) return (off_peak_prompt, off_peak_completion, cache_creation, cache_creation_above_1hr, off_peak_cache_read) diff --git a/litellm/llms/fireworks_ai/cost_calculator.py b/litellm/llms/fireworks_ai/cost_calculator.py index 08e6f009010..df47d3546ca 100644 --- a/litellm/llms/fireworks_ai/cost_calculator.py +++ b/litellm/llms/fireworks_ai/cost_calculator.py @@ -2,6 +2,7 @@ For calculating cost of fireworks ai serverless inference models. """ +from datetime import datetime from typing import Final from litellm.constants import ( @@ -10,7 +11,8 @@ from litellm.constants import ( FIREWORKS_AI_56_B_MOE, FIREWORKS_AI_176_B_MOE, ) -from litellm.types.utils import Usage +from litellm.litellm_core_utils.llm_cost_calc.utils import apply_off_peak_pricing +from litellm.types.utils import ModelInfo, Usage from litellm.utils import get_model_info @@ -54,44 +56,46 @@ def get_base_model_for_pricing(model_name: str) -> str: return "fireworks-ai-default" -def cost_per_token(model: str, usage: Usage) -> tuple[float, float]: +def _resolve_model_info(model: str) -> ModelInfo: + try: + return get_model_info(model=model, custom_llm_provider="fireworks_ai") + except Exception: + base_model: Final = get_base_model_for_pricing(model_name=model) + return get_model_info(model=base_model, custom_llm_provider="fireworks_ai") + + +def cost_per_token(model: str, usage: Usage, current_time: datetime | None = None) -> tuple[float, float]: """ - Calculates the cost per token for a given model, prompt tokens, and completion tokens. + Calculates the cost per token for a given model, prompt tokens, and completion tokens, + swapping in the model's off_peak_pricing rates while one of its windows is open. Input: - model: str, the model name without provider prefix - usage: LiteLLM Usage block, containing anthropic caching information + - current_time: the moment the request is billed at; defaults to now, UTC Returns: Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd """ - ## check if model mapped, else use default pricing - try: - model_info = get_model_info(model=model, custom_llm_provider="fireworks_ai") - except Exception: - base_model: Final = get_base_model_for_pricing(model_name=model) + model_info: Final = _resolve_model_info(model) + standard_input_rate: Final[float] = model_info["input_cost_per_token"] or 0.0 + standard_cache_read_rate: Final = model_info.get("cache_read_input_token_cost") + input_rate, output_rate, cache_read_rate = apply_off_peak_pricing( + model_info, + current_time, + standard_input_rate, + model_info["output_cost_per_token"] or 0.0, + standard_cache_read_rate if standard_cache_read_rate is not None else standard_input_rate, + ) - ## GET MODEL INFO - model_info = get_model_info(model=base_model, custom_llm_provider="fireworks_ai") - - ## CALCULATE INPUT COST prompt_tokens_details: Final = usage.prompt_tokens_details cached_tokens: Final[int] = ( prompt_tokens_details.cached_tokens if prompt_tokens_details is not None and prompt_tokens_details.cached_tokens is not None else 0 ) - input_cost_per_token: Final[float] = model_info["input_cost_per_token"] or 0.0 - cache_read_input_token_cost: Final = model_info.get("cache_read_input_token_cost") - cache_read_cost_per_token: Final[float] = ( - cache_read_input_token_cost if cache_read_input_token_cost is not None else input_cost_per_token - ) non_cached_prompt_tokens: Final[int] = max(usage.prompt_tokens - cached_tokens, 0) - - prompt_cost: float = non_cached_prompt_tokens * input_cost_per_token + cached_tokens * cache_read_cost_per_token - - ## CALCULATE OUTPUT COST - output_cost_per_token: Final[float] = model_info["output_cost_per_token"] or 0.0 - completion_cost: Final[float] = usage.completion_tokens * output_cost_per_token + prompt_cost: Final[float] = non_cached_prompt_tokens * input_rate + cached_tokens * cache_read_rate + completion_cost: Final[float] = usage.completion_tokens * output_rate return prompt_cost, completion_cost diff --git a/litellm/llms/perplexity/cost_calculator.py b/litellm/llms/perplexity/cost_calculator.py index 27835ecbfe8..67949e850f0 100644 --- a/litellm/llms/perplexity/cost_calculator.py +++ b/litellm/llms/perplexity/cost_calculator.py @@ -3,19 +3,23 @@ Helper util for handling perplexity-specific cost calculation - e.g.: citation tokens, search queries """ +from datetime import datetime from typing import Final +from litellm.litellm_core_utils.llm_cost_calc.utils import apply_off_peak_pricing from litellm.types.utils import Usage from litellm.utils import get_model_info -def cost_per_token(model: str, usage: Usage) -> tuple[float, float]: +def cost_per_token(model: str, usage: Usage, current_time: datetime | None = None) -> tuple[float, float]: """ Calculates the cost per token for a given model, prompt tokens, and completion tokens. + The manual fallback swaps in the model's off_peak_pricing rates while one of its windows is open. Input: - model: str, the model name without provider prefix - usage: LiteLLM Usage block, containing perplexity-specific usage information + - current_time: the moment the request is billed at; defaults to now, UTC Returns: Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd @@ -48,8 +52,15 @@ def cost_per_token(model: str, usage: Usage) -> tuple[float, float]: except (ValueError, TypeError): return default + input_cost_per_token, output_cost_per_token, _ = apply_off_peak_pricing( + model_info, + current_time, + _safe_float_cast(model_info.get("input_cost_per_token")), + _safe_float_cast(model_info.get("output_cost_per_token")), + 0.0, + ) + ## CALCULATE INPUT COST - input_cost_per_token: Final = _safe_float_cast(model_info.get("input_cost_per_token")) prompt_cost: float = (usage.prompt_tokens or 0) * input_cost_per_token ## ADD CITATION TOKENS COST (if present) @@ -60,8 +71,6 @@ def cost_per_token(model: str, usage: Usage) -> tuple[float, float]: prompt_cost += citation_tokens * citation_cost_per_token ## CALCULATE OUTPUT COST - output_cost_per_token: Final = _safe_float_cast(model_info.get("output_cost_per_token")) - reasoning_tokens = getattr(usage, "reasoning_tokens", 0) or 0 if reasoning_tokens == 0 and hasattr(usage, "completion_tokens_details") and usage.completion_tokens_details: reasoning_tokens = getattr(usage.completion_tokens_details, "reasoning_tokens", 0) or 0 diff --git a/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py b/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py index f1664dabf48..21ee56a7873 100644 --- a/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py +++ b/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py @@ -1,4 +1,7 @@ +import math +from datetime import datetime, timezone + import pytest @@ -64,3 +67,75 @@ def test_no_cached_tokens_matches_full_input_rate(): assert prompt_cost == pytest.approx(prompt_tokens * INPUT_COST) assert completion_cost == pytest.approx(completion_tokens * OUTPUT_COST) + + +OFF_PEAK_MODEL = "accounts/fireworks/models/off-peak-test" +OFF_PEAK_WINDOW = "14:00-00:00" +INSIDE_WINDOW = datetime(2026, 9, 3, 17, 25, tzinfo=timezone.utc) +OUTSIDE_WINDOW = datetime(2026, 9, 3, 9, 0, tzinfo=timezone.utc) +STANDARD_INPUT_COST = 1.5e-07 +STANDARD_OUTPUT_COST = 6e-07 +STANDARD_CACHE_READ_COST = 1.5e-08 + + +def _register_off_peak_model(off_peak_pricing: dict) -> None: + litellm.model_cost[f"fireworks_ai/{OFF_PEAK_MODEL}"] = { + "litellm_provider": "fireworks_ai", + "mode": "chat", + "input_cost_per_token": STANDARD_INPUT_COST, + "output_cost_per_token": STANDARD_OUTPUT_COST, + "cache_read_input_token_cost": STANDARD_CACHE_READ_COST, + "off_peak_pricing": off_peak_pricing, + } + + +def test_off_peak_window_swaps_in_the_off_peak_rates(): + """ + Regression (LIT-6874): a deployment configured with off_peak_pricing kept billing the + standard fireworks_ai rates inside its window, while the same block on a deepseek + deployment billed the off-peak rates. + """ + _register_off_peak_model( + { + "hours_utc": OFF_PEAK_WINDOW, + "input_cost_per_token": 1e-08, + "output_cost_per_token": 2e-08, + "cache_read_input_token_cost": 1e-09, + } + ) + usage = _usage(prompt_tokens=1000, cached_tokens=300, completion_tokens=200) + + prompt_cost, completion_cost = cost_per_token(model=OFF_PEAK_MODEL, usage=usage, current_time=INSIDE_WINDOW) + + assert math.isclose(prompt_cost, (700 * 1e-08) + (300 * 1e-09), rel_tol=1e-10) + assert math.isclose(completion_cost, 200 * 2e-08, rel_tol=1e-10) + + peak_prompt_cost, peak_completion_cost = cost_per_token( + model=OFF_PEAK_MODEL, usage=usage, current_time=OUTSIDE_WINDOW + ) + + assert math.isclose(peak_prompt_cost, (700 * STANDARD_INPUT_COST) + (300 * STANDARD_CACHE_READ_COST), rel_tol=1e-10) + assert math.isclose(peak_completion_cost, 200 * STANDARD_OUTPUT_COST, rel_tol=1e-10) + + +def test_off_peak_rates_left_unset_keep_the_standard_rates(): + """A block that only overrides the input rate leaves output and cache reads on the standard rates.""" + _register_off_peak_model({"hours_utc": OFF_PEAK_WINDOW, "input_cost_per_token": 1e-08}) + usage = _usage(prompt_tokens=1000, cached_tokens=300, completion_tokens=200) + + prompt_cost, completion_cost = cost_per_token(model=OFF_PEAK_MODEL, usage=usage, current_time=INSIDE_WINDOW) + + assert math.isclose(prompt_cost, (700 * 1e-08) + (300 * STANDARD_CACHE_READ_COST), rel_tol=1e-10) + assert math.isclose(completion_cost, 200 * STANDARD_OUTPUT_COST, rel_tol=1e-10) + + +def test_off_peak_defaults_to_the_current_time(): + """The proxy's cost dispatch passes no clock, so an all-day window has to apply on the + default current time.""" + _register_off_peak_model({"hours_utc": "00:00-00:00", "input_cost_per_token": 1e-08, "output_cost_per_token": 2e-08}) + usage = _usage(prompt_tokens=1000, cached_tokens=0, completion_tokens=200) + + prompt_cost, completion_cost = cost_per_token(model=OFF_PEAK_MODEL, usage=usage) + + assert math.isclose(prompt_cost, 1000 * 1e-08, rel_tol=1e-10) + assert math.isclose(completion_cost, 200 * 2e-08, rel_tol=1e-10) diff --git a/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py b/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py index 117379c331a..be338bd3dfa 100644 --- a/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py +++ b/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py @@ -8,6 +8,7 @@ search queries, and reasoning tokens. import json import math import os +from datetime import datetime, timezone from unittest.mock import patch import pytest @@ -523,3 +524,94 @@ class TestPerplexityCostCalculator: ) assert math.isclose(total_cost, 1000 * 1.4e-06 + 500 * 4.4e-06, rel_tol=1e-9) + + OFF_PEAK_MODEL = "sonar-off-peak-test" + OFF_PEAK_WINDOW = "14:00-00:00" + INSIDE_WINDOW = datetime(2026, 9, 3, 17, 25, tzinfo=timezone.utc) + OUTSIDE_WINDOW = datetime(2026, 9, 3, 9, 0, tzinfo=timezone.utc) + + def _register_off_peak_model(self, off_peak_pricing: dict) -> None: + litellm.model_cost[f"perplexity/{self.OFF_PEAK_MODEL}"] = { + "litellm_provider": "perplexity", + "mode": "chat", + "input_cost_per_token": 1e-06, + "output_cost_per_token": 1e-06, + "output_cost_per_reasoning_token": 3e-06, + "citation_cost_per_token": 2e-06, + "search_context_cost_per_query": {"search_context_size_low": 0.005}, + "off_peak_pricing": off_peak_pricing, + } + + def test_off_peak_window_swaps_in_the_off_peak_rates(self): + """ + Regression (LIT-6874): a deployment configured with off_peak_pricing kept billing the + standard perplexity rates inside its window, while the same block on a deepseek + deployment billed the off-peak rates. + """ + self._register_off_peak_model( + {"hours_utc": self.OFF_PEAK_WINDOW, "input_cost_per_token": 1e-07, "output_cost_per_token": 2e-07} + ) + usage = Usage(prompt_tokens=1000, completion_tokens=200, total_tokens=1200) + + prompt_cost, completion_cost = perplexity_cost_per_token( + model=self.OFF_PEAK_MODEL, usage=usage, current_time=self.INSIDE_WINDOW + ) + + assert math.isclose(prompt_cost, 1000 * 1e-07, rel_tol=1e-10) + assert math.isclose(completion_cost, 200 * 2e-07, rel_tol=1e-10) + + peak_prompt_cost, peak_completion_cost = perplexity_cost_per_token( + model=self.OFF_PEAK_MODEL, usage=usage, current_time=self.OUTSIDE_WINDOW + ) + + assert math.isclose(peak_prompt_cost, 1000 * 1e-06, rel_tol=1e-10) + assert math.isclose(peak_completion_cost, 200 * 1e-06, rel_tol=1e-10) + + def test_off_peak_rates_leave_citation_search_and_reasoning_fees_alone(self): + """Inside the window only the plain input and output rates change: citation tokens, the + per-request search fee, and a dedicated reasoning rate keep billing as published.""" + self._register_off_peak_model( + {"hours_utc": self.OFF_PEAK_WINDOW, "input_cost_per_token": 1e-07, "output_cost_per_token": 2e-07} + ) + usage = Usage( + prompt_tokens=1000, + completion_tokens=200, + total_tokens=1200, + prompt_tokens_details=PromptTokensDetailsWrapper(web_search_requests=1), + completion_tokens_details=CompletionTokensDetailsWrapper(reasoning_tokens=50), + ) + usage.citation_tokens = 100 + + prompt_cost, completion_cost = perplexity_cost_per_token( + model=self.OFF_PEAK_MODEL, usage=usage, current_time=self.INSIDE_WINDOW + ) + + assert math.isclose(prompt_cost, (1000 * 1e-07) + (100 * 2e-06), rel_tol=1e-10) + assert math.isclose(completion_cost, (150 * 2e-07) + (50 * 3e-06) + 0.005, rel_tol=1e-10) + + def test_off_peak_defaults_to_the_current_time(self): + """The proxy's cost dispatch passes no clock, so an all-day window has to apply on the + default current time.""" + self._register_off_peak_model( + {"hours_utc": "00:00-00:00", "input_cost_per_token": 1e-07, "output_cost_per_token": 2e-07} + ) + usage = Usage(prompt_tokens=1000, completion_tokens=200, total_tokens=1200) + + prompt_cost, completion_cost = perplexity_cost_per_token(model=self.OFF_PEAK_MODEL, usage=usage) + + assert math.isclose(prompt_cost, 1000 * 1e-07, rel_tol=1e-10) + assert math.isclose(completion_cost, 200 * 2e-07, rel_tol=1e-10) + + def test_provider_stated_cost_still_wins_inside_an_off_peak_window(self): + """A response that carries Perplexity's own metered cost bills that cost whatever the + window says; the caller strips it when the deployment carries custom pricing.""" + self._register_off_peak_model( + {"hours_utc": "00:00-00:00", "input_cost_per_token": 1e-07, "output_cost_per_token": 2e-07} + ) + usage = Usage(prompt_tokens=1000, completion_tokens=200, total_tokens=1200) + usage.cost = {"total_cost": 0.00501} + + prompt_cost, completion_cost = perplexity_cost_per_token(model=self.OFF_PEAK_MODEL, usage=usage) + + assert prompt_cost == 0.0 + assert completion_cost == 0.00501 From e65e3d0e2b89becc8fb55268ff18a8141e9ddf4b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 3 Sep 2026 13:45:17 -0700 Subject: [PATCH 2/3] fix(cost): bill fireworks cached tokens at the off-peak input rate when no cache-read rate exists --- litellm/llms/fireworks_ai/cost_calculator.py | 11 +++++---- .../test_fireworks_ai_cost_calculator.py | 23 +++++++++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/litellm/llms/fireworks_ai/cost_calculator.py b/litellm/llms/fireworks_ai/cost_calculator.py index df47d3546ca..3843bad6d8f 100644 --- a/litellm/llms/fireworks_ai/cost_calculator.py +++ b/litellm/llms/fireworks_ai/cost_calculator.py @@ -2,6 +2,7 @@ For calculating cost of fireworks ai serverless inference models. """ +import math from datetime import datetime from typing import Final @@ -15,6 +16,8 @@ from litellm.litellm_core_utils.llm_cost_calc.utils import apply_off_peak_pricin from litellm.types.utils import ModelInfo, Usage from litellm.utils import get_model_info +NO_CACHE_READ_RATE: Final = float("nan") + # Extract the number of billion parameters from the model name # only used for together_computer LLMs @@ -78,15 +81,15 @@ def cost_per_token(model: str, usage: Usage, current_time: datetime | None = Non Tuple[float, float] - prompt_cost_in_usd, completion_cost_in_usd """ model_info: Final = _resolve_model_info(model) - standard_input_rate: Final[float] = model_info["input_cost_per_token"] or 0.0 standard_cache_read_rate: Final = model_info.get("cache_read_input_token_cost") - input_rate, output_rate, cache_read_rate = apply_off_peak_pricing( + input_rate, output_rate, cache_read_rate_or_unset = apply_off_peak_pricing( model_info, current_time, - standard_input_rate, + model_info["input_cost_per_token"] or 0.0, model_info["output_cost_per_token"] or 0.0, - standard_cache_read_rate if standard_cache_read_rate is not None else standard_input_rate, + standard_cache_read_rate if standard_cache_read_rate is not None else NO_CACHE_READ_RATE, ) + cache_read_rate: Final[float] = input_rate if math.isnan(cache_read_rate_or_unset) else cache_read_rate_or_unset prompt_tokens_details: Final = usage.prompt_tokens_details cached_tokens: Final[int] = ( diff --git a/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py b/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py index 21ee56a7873..555fdf7e11d 100644 --- a/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py +++ b/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py @@ -78,14 +78,14 @@ STANDARD_OUTPUT_COST = 6e-07 STANDARD_CACHE_READ_COST = 1.5e-08 -def _register_off_peak_model(off_peak_pricing: dict) -> None: +def _register_off_peak_model(off_peak_pricing: dict, cache_read_cost: float | None = STANDARD_CACHE_READ_COST) -> None: litellm.model_cost[f"fireworks_ai/{OFF_PEAK_MODEL}"] = { "litellm_provider": "fireworks_ai", "mode": "chat", "input_cost_per_token": STANDARD_INPUT_COST, "output_cost_per_token": STANDARD_OUTPUT_COST, - "cache_read_input_token_cost": STANDARD_CACHE_READ_COST, "off_peak_pricing": off_peak_pricing, + **({} if cache_read_cost is None else {"cache_read_input_token_cost": cache_read_cost}), } @@ -129,6 +129,25 @@ def test_off_peak_rates_left_unset_keep_the_standard_rates(): assert math.isclose(completion_cost, 200 * STANDARD_OUTPUT_COST, rel_tol=1e-10) +def test_off_peak_window_bills_cached_tokens_at_the_off_peak_input_rate_without_a_cache_read_rate(): + """Most fireworks_ai price-map entries carry no cache_read_input_token_cost, so cached tokens + fall back to the input rate, and inside the window that has to be the off-peak one.""" + _register_off_peak_model( + {"hours_utc": OFF_PEAK_WINDOW, "input_cost_per_token": 1e-08, "output_cost_per_token": 2e-08}, + cache_read_cost=None, + ) + usage = _usage(prompt_tokens=1000, cached_tokens=300, completion_tokens=200) + + prompt_cost, completion_cost = cost_per_token(model=OFF_PEAK_MODEL, usage=usage, current_time=INSIDE_WINDOW) + + assert math.isclose(prompt_cost, 1000 * 1e-08, rel_tol=1e-10) + assert math.isclose(completion_cost, 200 * 2e-08, rel_tol=1e-10) + + peak_prompt_cost, _ = cost_per_token(model=OFF_PEAK_MODEL, usage=usage, current_time=OUTSIDE_WINDOW) + + assert math.isclose(peak_prompt_cost, 1000 * STANDARD_INPUT_COST, rel_tol=1e-10) + + def test_off_peak_defaults_to_the_current_time(): """The proxy's cost dispatch passes no clock, so an all-day window has to apply on the default current time.""" From f9e41470d68fa2be215290300c4977890bab0d9b Mon Sep 17 00:00:00 2001 From: mateo-berri <277851410+mateo-berri@users.noreply.github.com> Date: Thu, 3 Sep 2026 15:03:10 -0700 Subject: [PATCH 3/3] test(cost): type the off-peak fixture helpers with OffPeakPricing --- .../llms/fireworks_ai/test_fireworks_ai_cost_calculator.py | 4 ++-- .../llms/perplexity/test_perplexity_cost_calculator.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py b/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py index 555fdf7e11d..c2e42da1b4c 100644 --- a/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py +++ b/tests/test_litellm/llms/fireworks_ai/test_fireworks_ai_cost_calculator.py @@ -7,7 +7,7 @@ import pytest import litellm from litellm.llms.fireworks_ai.cost_calculator import cost_per_token -from litellm.types.utils import PromptTokensDetailsWrapper, Usage +from litellm.types.utils import OffPeakPricing, PromptTokensDetailsWrapper, Usage MODEL = "accounts/fireworks/models/glm-5p2" INPUT_COST = 1.4e-06 @@ -78,7 +78,7 @@ STANDARD_OUTPUT_COST = 6e-07 STANDARD_CACHE_READ_COST = 1.5e-08 -def _register_off_peak_model(off_peak_pricing: dict, cache_read_cost: float | None = STANDARD_CACHE_READ_COST) -> None: +def _register_off_peak_model(off_peak_pricing: OffPeakPricing, cache_read_cost: float | None = STANDARD_CACHE_READ_COST) -> None: litellm.model_cost[f"fireworks_ai/{OFF_PEAK_MODEL}"] = { "litellm_provider": "fireworks_ai", "mode": "chat", diff --git a/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py b/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py index be338bd3dfa..6630039e92e 100644 --- a/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py +++ b/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py @@ -22,6 +22,7 @@ from litellm.llms.perplexity.cost_calculator import ( ) from litellm.types.utils import ( CompletionTokensDetailsWrapper, + OffPeakPricing, Usage, PromptTokensDetailsWrapper, ) @@ -530,7 +531,7 @@ class TestPerplexityCostCalculator: INSIDE_WINDOW = datetime(2026, 9, 3, 17, 25, tzinfo=timezone.utc) OUTSIDE_WINDOW = datetime(2026, 9, 3, 9, 0, tzinfo=timezone.utc) - def _register_off_peak_model(self, off_peak_pricing: dict) -> None: + def _register_off_peak_model(self, off_peak_pricing: OffPeakPricing) -> None: litellm.model_cost[f"perplexity/{self.OFF_PEAK_MODEL}"] = { "litellm_provider": "perplexity", "mode": "chat",