|
# retoor <retoor@molodetz.nl>
|
|
|
|
from dataclasses import replace
|
|
from datetime import datetime, timezone
|
|
|
|
from devplacepy.services.openai_gateway.usage import (
|
|
USAGE_FIELDS,
|
|
Pricing,
|
|
accumulate_usage,
|
|
compute_cost,
|
|
new_usage_totals,
|
|
normalize_usage,
|
|
parse_usage_headers,
|
|
usage_metric_cards,
|
|
)
|
|
|
|
BASE_PRICING = Pricing(
|
|
chat_cache_hit_per_m=0.0028,
|
|
chat_cache_miss_per_m=0.14,
|
|
chat_output_per_m=0.28,
|
|
vision_input_per_m=0.0,
|
|
vision_output_per_m=0.0,
|
|
embed_input_per_m=0.01,
|
|
)
|
|
|
|
GATEWAY_HEADERS = {
|
|
"X-Gateway-Cost-USD": "0.00010000",
|
|
"X-Gateway-Model": "molodetz",
|
|
"X-Gateway-Prompt-Tokens": "500",
|
|
"X-Gateway-Completion-Tokens": "100",
|
|
"X-Gateway-Total-Tokens": "600",
|
|
"X-Gateway-Upstream-Latency-Ms": "400",
|
|
"X-Gateway-Total-Latency-Ms": "450",
|
|
}
|
|
|
|
|
|
class FakeResp:
|
|
def __init__(self, headers=None):
|
|
self.headers = headers if headers is not None else {}
|
|
|
|
|
|
def test_new_usage_totals_is_zeroed():
|
|
totals = new_usage_totals()
|
|
assert set(totals) == set(USAGE_FIELDS)
|
|
assert all(value == 0 for value in totals.values())
|
|
|
|
|
|
def test_accumulate_usage_sums_headers():
|
|
totals = new_usage_totals()
|
|
accumulate_usage(totals, FakeResp(GATEWAY_HEADERS))
|
|
accumulate_usage(totals, FakeResp(GATEWAY_HEADERS))
|
|
assert totals["calls"] == 2
|
|
assert totals["prompt_tokens"] == 1000
|
|
assert totals["completion_tokens"] == 200
|
|
assert totals["total_tokens"] == 1200
|
|
assert abs(totals["cost_usd"] - 0.0002) < 1e-9
|
|
assert totals["upstream_latency_ms"] == 800.0
|
|
assert totals["total_latency_ms"] == 900.0
|
|
|
|
|
|
def test_accumulate_usage_is_noop_without_headers_or_totals():
|
|
totals = new_usage_totals()
|
|
accumulate_usage(None, FakeResp(GATEWAY_HEADERS))
|
|
accumulate_usage(totals, FakeResp({}))
|
|
accumulate_usage(totals, object())
|
|
assert totals["calls"] == 0
|
|
|
|
|
|
def test_parse_usage_headers_requires_cost_header():
|
|
assert parse_usage_headers({}) is None
|
|
assert parse_usage_headers(None) is None
|
|
parsed = parse_usage_headers(GATEWAY_HEADERS)
|
|
assert parsed["calls"] == 1
|
|
assert parsed["total_tokens"] == 600
|
|
|
|
|
|
def test_usage_metric_cards_labels_and_formatting():
|
|
usage = {
|
|
"calls": 4,
|
|
"total_tokens": 9200,
|
|
"prompt_tokens": 6000,
|
|
"completion_tokens": 3200,
|
|
"cost_usd": 0.0492,
|
|
"avg_tokens": 2300.0,
|
|
"avg_cost_usd": 0.0123,
|
|
"avg_upstream_latency_ms": 4200.0,
|
|
"avg_tokens_per_second": 190.5,
|
|
}
|
|
cards = usage_metric_cards(usage)
|
|
labels = [card["label"] for card in cards]
|
|
assert labels == [
|
|
"AI calls",
|
|
"Total tokens",
|
|
"Prompt tokens",
|
|
"Completion tokens",
|
|
"Total cost",
|
|
"Avg tokens/call",
|
|
"Avg cost/call",
|
|
"Avg latency",
|
|
"Avg tokens/sec",
|
|
]
|
|
by_label = {card["label"]: card["value"] for card in cards}
|
|
assert by_label["AI calls"] == 4
|
|
assert by_label["Total cost"] == "$0.0492"
|
|
assert by_label["Avg cost/call"] == "$0.012300"
|
|
assert by_label["Avg latency"] == "4200ms"
|
|
|
|
|
|
def test_compute_cost_matches_deepseek_flat_rates_when_no_tier_configured():
|
|
norm = normalize_usage(
|
|
{
|
|
"prompt_tokens": 2000,
|
|
"completion_tokens": 100,
|
|
"prompt_cache_hit_tokens": 500,
|
|
"prompt_cache_miss_tokens": 1500,
|
|
}
|
|
)
|
|
total, input_cost, output_cost, native = compute_cost({}, norm, BASE_PRICING, "chat")
|
|
assert native is False
|
|
assert abs(input_cost - (500 / 1e6 * 0.0028 + 1500 / 1e6 * 0.14)) < 1e-12
|
|
assert abs(output_cost - (100 / 1e6 * 0.28)) < 1e-12
|
|
assert abs(total - (input_cost + output_cost)) < 1e-12
|
|
|
|
|
|
def test_compute_cost_switches_to_tier2_above_threshold():
|
|
tiered = replace(
|
|
BASE_PRICING,
|
|
chat_cache_hit_per_m_tier2=0.005,
|
|
chat_cache_miss_per_m_tier2=0.25,
|
|
chat_output_per_m_tier2=0.5,
|
|
context_tier_threshold_tokens=1000,
|
|
)
|
|
below = normalize_usage({"prompt_tokens": 900, "completion_tokens": 50})
|
|
above = normalize_usage({"prompt_tokens": 1001, "completion_tokens": 50})
|
|
_, below_input, below_output, _ = compute_cost({}, below, tiered, "chat")
|
|
_, above_input, above_output, _ = compute_cost({}, above, tiered, "chat")
|
|
assert abs(below_output - (50 / 1e6 * 0.28)) < 1e-12
|
|
assert abs(above_output - (50 / 1e6 * 0.5)) < 1e-12
|
|
assert below_input != above_input
|
|
|
|
|
|
def test_compute_cost_tier2_leaves_unset_component_at_tier1():
|
|
tiered = replace(
|
|
BASE_PRICING,
|
|
chat_output_per_m_tier2=0.5,
|
|
context_tier_threshold_tokens=100,
|
|
)
|
|
norm = normalize_usage({"prompt_tokens": 200, "completion_tokens": 10})
|
|
_, input_cost, output_cost, _ = compute_cost({}, norm, tiered, "chat")
|
|
assert abs(output_cost - (10 / 1e6 * 0.5)) < 1e-12
|
|
assert abs(input_cost - (200 / 1e6 * 0.14)) < 1e-12
|
|
|
|
|
|
def test_compute_cost_applies_off_peak_discount():
|
|
discounted = replace(
|
|
BASE_PRICING,
|
|
off_peak_start_minute=60,
|
|
off_peak_end_minute=120,
|
|
off_peak_discount_pct=50.0,
|
|
)
|
|
norm = normalize_usage({"prompt_tokens": 1000, "completion_tokens": 100})
|
|
in_window = datetime(2026, 1, 1, 1, 30, tzinfo=timezone.utc)
|
|
outside_window = datetime(2026, 1, 1, 10, 0, tzinfo=timezone.utc)
|
|
total_in, _, _, _ = compute_cost({}, norm, discounted, "chat", now=in_window)
|
|
total_out, _, _, _ = compute_cost({}, norm, discounted, "chat", now=outside_window)
|
|
total_flat, _, _, _ = compute_cost({}, norm, BASE_PRICING, "chat")
|
|
assert abs(total_out - total_flat) < 1e-12
|
|
assert abs(total_in - total_flat / 2) < 1e-12
|
|
|
|
|
|
def test_compute_cost_off_peak_wraps_past_midnight():
|
|
wrapped = replace(
|
|
BASE_PRICING,
|
|
off_peak_start_minute=23 * 60,
|
|
off_peak_end_minute=60,
|
|
off_peak_discount_pct=100.0,
|
|
)
|
|
norm = normalize_usage({"prompt_tokens": 1000, "completion_tokens": 100})
|
|
just_after_midnight = datetime(2026, 1, 1, 0, 30, tzinfo=timezone.utc)
|
|
midday = datetime(2026, 1, 1, 12, 0, tzinfo=timezone.utc)
|
|
total_wrapped, _, _, _ = compute_cost(
|
|
{}, norm, wrapped, "chat", now=just_after_midnight
|
|
)
|
|
total_midday, _, _, _ = compute_cost({}, norm, wrapped, "chat", now=midday)
|
|
assert total_wrapped == 0.0
|
|
assert total_midday > 0.0
|
|
|
|
|
|
def test_compute_cost_image_branch_flat_per_call():
|
|
image_pricing = replace(BASE_PRICING, image_per_call=0.04)
|
|
norm = normalize_usage({})
|
|
total, input_cost, output_cost, native = compute_cost(
|
|
{}, norm, image_pricing, "image"
|
|
)
|
|
assert native is False
|
|
assert output_cost == 0.0
|
|
assert abs(total - 0.04) < 1e-9
|
|
assert abs(input_cost - 0.04) < 1e-9
|
|
|
|
|
|
def test_compute_cost_image_native_cost_preferred():
|
|
from devplacepy.services.openai_gateway.usage import extract_image_usage
|
|
|
|
image_pricing = replace(BASE_PRICING, image_per_call=0.04)
|
|
norm = normalize_usage({})
|
|
usage = extract_image_usage({"usage": {"cost": 0.055}})
|
|
total, _, _, native = compute_cost(usage, norm, image_pricing, "image")
|
|
assert native is True
|
|
assert total == 0.055
|
|
|
|
|
|
def test_compute_cost_native_upstream_cost_unaffected_by_tiering():
|
|
tiered = replace(
|
|
BASE_PRICING,
|
|
chat_output_per_m_tier2=100.0,
|
|
context_tier_threshold_tokens=1,
|
|
)
|
|
norm = normalize_usage({"prompt_tokens": 1000, "completion_tokens": 100})
|
|
total, _, _, native = compute_cost({"cost": 0.05}, norm, tiered, "chat")
|
|
assert native is True
|
|
assert total == 0.05
|
|
|
|
|
|
def test_validate_app_reference_accepts_valid_slugs():
|
|
from devplacepy.services.openai_gateway.service import _validate_app_reference
|
|
|
|
assert _validate_app_reference("devplace-devii-v-1-0-0") == "devplace-devii-v-1-0-0"
|
|
assert _validate_app_reference("my-app") == "my-app"
|
|
assert _validate_app_reference("a") == "a"
|
|
assert _validate_app_reference("abcdefghijklmnopqrstuvwxyz0123") == "abcdefghijklmnopqrstuvwxyz0123"
|
|
assert _validate_app_reference("test_app.release-2") == "test_app.release-2"
|
|
|
|
|
|
def test_validate_app_reference_rejects_invalid():
|
|
from devplacepy.services.openai_gateway.service import _validate_app_reference
|
|
|
|
assert _validate_app_reference("") == "default"
|
|
assert _validate_app_reference(" ") == "default"
|
|
assert _validate_app_reference("hello world") == "default"
|
|
assert _validate_app_reference("name@domain") == "default"
|
|
assert _validate_app_reference("name/domain") == "default"
|
|
assert _validate_app_reference(None) == "default"
|
|
assert _validate_app_reference("abcdefghijklmnopqrstuvwxyz01234") == "default"
|
|
|
|
|
|
def test_validate_app_reference_strips_whitespace():
|
|
from devplacepy.services.openai_gateway.service import _validate_app_reference
|
|
|
|
assert _validate_app_reference(" my-app ") == "my-app"
|
|
assert _validate_app_reference("\tdevplace\t") == "devplace"
|