diff --git a/litellm/llms/perplexity/cost_calculator.py b/litellm/llms/perplexity/cost_calculator.py index bf055f91aa0..ec7ec397ea6 100644 --- a/litellm/llms/perplexity/cost_calculator.py +++ b/litellm/llms/perplexity/cost_calculator.py @@ -98,10 +98,11 @@ def _safe_float_cast( if num_search_queries > 0 and search_cost_value is not None: # Handle both dict and float formats if isinstance(search_cost_value, dict): - # Use the "low" size as default - tests expect 0.005 / 1000 - search_cost_per_query = ( - _safe_float_cast(search_cost_value.get("search_context_size_low", 0)) - / 1000 + # search_context_cost_per_query stores the per-request price in USD + # (e.g. sonar low = $0.005/request). Use it directly, matching the + # gemini cost calculator which reads the same field per request. + search_cost_per_query = _safe_float_cast( + search_cost_value.get("search_context_size_low", 0) ) else: search_cost_per_query = _safe_float_cast(search_cost_value) 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 e2d1ab72c5e..46c1e457d7c 100644 --- a/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py +++ b/tests/test_litellm/llms/perplexity/test_perplexity_cost_calculator.py @@ -9,7 +9,7 @@ import math import os import sys -from unittest.mock import Mock, patch +from unittest.mock import patch import pytest @@ -120,10 +120,10 @@ def test_search_queries_cost_calculation(self): # Expected costs: # Input: 100 tokens * $2e-6 = $0.0002 # Output: 50 tokens * $8e-6 = $0.0004 - # Search: 3 queries * ($0.005 / 1000) = $0.000015 - # Total completion cost: $0.000415 + # Search: 3 queries * $0.005 per request = $0.015 + # Total completion cost: $0.0154 expected_prompt_cost = 100 * 2e-6 - expected_completion_cost = (50 * 8e-6) + (3 / 1000 * 0.005) + expected_completion_cost = (50 * 8e-6) + (3 * 0.005) assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-6) assert math.isclose(completion_cost, expected_completion_cost, rel_tol=1e-6) @@ -195,10 +195,10 @@ def test_comprehensive_cost_calculation(self): # Total prompt cost = $0.00026 # Output (text): (50 - 15) tokens * $8e-6 = $0.00028 # Reasoning: 15 tokens * $3e-6 = $0.000045 - # Search: 2 queries * ($0.005 / 1000) = $0.00001 - # Total completion cost = $0.000335 + # Search: 2 queries * $0.005 per request = $0.01 + # Total completion cost = $0.010325 expected_prompt_cost = (100 * 2e-6) + (30 * 2e-6) - expected_completion_cost = ((50 - 15) * 8e-6) + (15 * 3e-6) + (2 / 1000 * 0.005) + expected_completion_cost = ((50 - 15) * 8e-6) + (15 * 3e-6) + (2 * 0.005) assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-6) assert math.isclose(completion_cost, expected_completion_cost, rel_tol=1e-6) @@ -311,7 +311,7 @@ def test_integration_with_completion_cost_function(self): # Calculate expected total cost (reasoning is a subset of completion_tokens) expected_prompt_cost = (100 * 2e-6) + (15 * 2e-6) # Input + citation expected_completion_cost = ( - ((50 - 10) * 8e-6) + (10 * 3e-6) + (1 / 1000 * 0.005) + ((50 - 10) * 8e-6) + (10 * 3e-6) + (1 * 0.005) ) # Output (text) + reasoning + search expected_total = expected_prompt_cost + expected_completion_cost @@ -361,7 +361,7 @@ def test_cost_calculation_combinations( expected_completion_cost = ( ((50 - reasoning_tokens) * 8e-6) + (reasoning_tokens * 3e-6) - + (search_queries / 1000 * 0.005) + + (search_queries * 0.005) ) assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-6) diff --git a/tests/test_litellm/llms/perplexity/test_perplexity_integration.py b/tests/test_litellm/llms/perplexity/test_perplexity_integration.py index e59fbc9f272..8691e6a1ee5 100644 --- a/tests/test_litellm/llms/perplexity/test_perplexity_integration.py +++ b/tests/test_litellm/llms/perplexity/test_perplexity_integration.py @@ -9,7 +9,6 @@ import math import os import sys -from unittest.mock import Mock, patch import pytest @@ -106,8 +105,8 @@ def test_end_to_end_cost_calculation_with_transformation(self): expected_prompt_cost = (100 * 2e-6) + (citation_tokens * 2e-6) expected_completion_cost = ( - ((50 - 10) * 8e-6) + (10 * 3e-6) + (2 / 1000 * 0.005) - ) + ((50 - 10) * 8e-6) + (10 * 3e-6) + (2 * 0.005) + ) # Output (text) + reasoning + search expected_total = expected_prompt_cost + expected_completion_cost assert math.isclose(total_cost, expected_total, rel_tol=1e-6) @@ -152,8 +151,8 @@ def test_main_cost_calculator_integration(self): expected_prompt_cost = (200 * 2e-6) + (40 * 2e-6) expected_completion_cost = ( - ((100 - 25) * 8e-6) + (25 * 3e-6) + (3 / 1000 * 0.005) - ) + ((100 - 25) * 8e-6) + (25 * 3e-6) + (3 * 0.005) + ) # Output (text) + reasoning + search assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-6) assert math.isclose(completion_cost_val, expected_completion_cost, rel_tol=1e-6) @@ -262,9 +261,9 @@ def test_high_volume_cost_calculation(self): expected_prompt_cost = (50000 * 2e-6) + (5000 * 2e-6) expected_completion_cost = ( - ((25000 - 10000) * 8e-6) + (10000 * 3e-6) + (100 / 1000 * 0.005) - ) - expected_total = expected_prompt_cost + expected_completion_cost + ((25000 - 10000) * 8e-6) + (10000 * 3e-6) + (100 * 0.005) + ) # $0.65 + expected_total = expected_prompt_cost + expected_completion_cost # $0.76 assert math.isclose(total_cost, expected_total, rel_tol=1e-6) assert total_cost > 0.25 @@ -326,7 +325,7 @@ def test_case_insensitive_provider_matching(self, provider_name): # Should calculate costs correctly expected_prompt_cost = (100 * 2e-6) + (10 * 2e-6) - expected_completion_cost = (50 * 8e-6) + (1 / 1000 * 0.005) + expected_completion_cost = (50 * 8e-6) + (1 * 0.005) assert math.isclose(prompt_cost, expected_prompt_cost, rel_tol=1e-6) assert math.isclose(completion_cost_val, expected_completion_cost, rel_tol=1e-6)