mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-08-09 02:54:37 +00:00
Merge branch 'fix-too-low-request-costs' into token-storage-and-rate-limit
This commit is contained in:
@@ -374,16 +374,38 @@ def _calculate_from_usd_cost(
|
||||
input_msats = math.floor(cost_in_msats * input_usd / component_usd)
|
||||
output_msats = cost_in_msats - input_msats
|
||||
else:
|
||||
effective_input_tokens = (
|
||||
input_tokens + cache_read_tokens + cache_creation_tokens
|
||||
# Providers often report only a total USD cost. Derive the visible
|
||||
# input/output split from the model's relative token prices; raw token
|
||||
# counts alone are misleading when completion tokens cost more.
|
||||
try:
|
||||
pricing_rates = _get_pricing_rates(response_data)
|
||||
except ValueError:
|
||||
pricing_rates = None
|
||||
|
||||
if pricing_rates is None:
|
||||
input_rate = float(settings.fixed_per_1k_input_tokens) * 1000.0
|
||||
output_rate = float(settings.fixed_per_1k_output_tokens) * 1000.0
|
||||
cache_read_rate = input_rate
|
||||
cache_creation_rate = input_rate
|
||||
else:
|
||||
input_rate, output_rate, cache_read_rate, cache_creation_rate = (
|
||||
pricing_rates
|
||||
)
|
||||
|
||||
input_weight = (
|
||||
input_tokens * input_rate
|
||||
+ cache_read_tokens * cache_read_rate
|
||||
+ cache_creation_tokens * cache_creation_rate
|
||||
)
|
||||
total_tokens = effective_input_tokens + output_tokens
|
||||
input_msats = (
|
||||
int(cost_in_msats * effective_input_tokens / total_tokens)
|
||||
if total_tokens > 0
|
||||
else 0
|
||||
)
|
||||
output_msats = cost_in_msats - input_msats
|
||||
output_weight = output_tokens * output_rate
|
||||
total_weight = input_weight + output_weight
|
||||
|
||||
if total_weight > 0:
|
||||
input_msats = math.floor(cost_in_msats * input_weight / total_weight)
|
||||
output_msats = cost_in_msats - input_msats
|
||||
else:
|
||||
input_msats = 0
|
||||
output_msats = cost_in_msats
|
||||
|
||||
logger.info(
|
||||
"Using cost from usage data/details",
|
||||
|
||||
@@ -5,7 +5,7 @@ edge cases, and billing accuracy.
|
||||
"""
|
||||
|
||||
import os
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -500,6 +500,38 @@ async def test_small_usd_cost_components_sum_to_rounded_total(
|
||||
assert result.input_msats + result.output_msats == result.total_msats
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_total_only_usd_cost_uses_model_prices_for_component_split(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
"""A reported total is split by priced tokens, not raw token counts."""
|
||||
monkeypatch.setattr(settings, "fixed_pricing", False)
|
||||
response = {
|
||||
"model": "z-ai/glm-5.2-20260616",
|
||||
"usage": {
|
||||
"prompt_tokens": 375,
|
||||
"completion_tokens": 218,
|
||||
"total_cost": 0.00039088,
|
||||
},
|
||||
}
|
||||
pricing = Mock(
|
||||
prompt=0.0001,
|
||||
completion=0.001,
|
||||
input_cache_read=0.0001,
|
||||
input_cache_write=0.0001,
|
||||
)
|
||||
model = Mock(sats_pricing=pricing)
|
||||
|
||||
with patch("routstr.proxy.get_model_instance", return_value=model):
|
||||
result = await calculate_cost(response, max_cost=100000)
|
||||
|
||||
assert isinstance(result, CostData)
|
||||
assert result.total_msats == 7818
|
||||
assert result.input_msats + result.output_msats == result.total_msats
|
||||
assert result.input_msats == 1147
|
||||
assert result.output_msats == 6671
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Test 13: Missing Usage Block
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user