Files
2026-07-13 13:12:33 +08:00

373 lines
12 KiB
Python

from collections.abc import Iterator
from unittest.mock import AsyncMock, patch
import pytest
from opensquilla.engine.pricing import (
PriceEntry,
PricingCache,
lookup_price,
reset_live_price_cache_for_tests,
seed_live_price_cache_for_tests,
)
@pytest.fixture(autouse=True)
def reset_pricing_cache() -> Iterator[None]:
reset_live_price_cache_for_tests()
yield
reset_live_price_cache_for_tests()
def test_deepseek_v4_pro_static_price_matches_current_official(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Official price since 2026-05-31: $0.435/M in (miss), $0.87/M out, $0.003625/M cache hit."""
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price("deepseek/deepseek-v4-pro")
assert price.input_per_m == pytest.approx(0.435)
assert price.output_per_m == pytest.approx(0.87)
assert price.cache_read_per_m == pytest.approx(0.003625)
@pytest.mark.asyncio
async def test_pricing_cache_refresh_adds_openrouter_app_attribution() -> None:
import httpx as _httpx
cache = PricingCache(api_key="test-key", ttl_seconds=60)
captured: dict[str, object] = {}
mock_response = _httpx.Response(
200,
json={
"data": [
{
"id": "openai/gpt-4o",
"pricing": {"prompt": "0.0000025", "completion": "0.00001"},
}
]
},
request=_httpx.Request("GET", "https://openrouter.ai/api/v1/models"),
)
with patch("opensquilla.engine.pricing.httpx.AsyncClient") as mock_client:
mock_instance = AsyncMock()
async def capture_get(url, *, headers):
captured["url"] = url
captured["headers"] = headers
return mock_response
mock_instance.get = AsyncMock(side_effect=capture_get)
mock_client.return_value.__aenter__ = AsyncMock(return_value=mock_instance)
mock_client.return_value.__aexit__ = AsyncMock(return_value=False)
await cache.refresh()
assert captured["url"] == "https://openrouter.ai/api/v1/models"
assert captured["headers"] == {
"Authorization": "Bearer test-key",
"Content-Type": "application/json",
"HTTP-Referer": "https://opensquilla.ai",
"X-Title": "OpenSquilla",
}
price = cache.get_price_sync("openai/gpt-4o")
assert price is not None
assert price.input_per_token == 0.0000025
assert price.output_per_token == 0.00001
def test_deepseek_v4_pro_live_price_wins_over_static(
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""The hardcoded pin must no longer block live-price self-correction."""
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "1")
reset_live_price_cache_for_tests()
seed_live_price_cache_for_tests("deepseek/deepseek-v4-pro", PriceEntry(0.5, 1.0))
price = lookup_price("deepseek/deepseek-v4-pro")
assert price.input_per_m == pytest.approx(0.5)
assert price.output_per_m == pytest.approx(1.0)
def test_versioned_deepseek_id_prefix_matches_static_entry(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price("deepseek/deepseek-v4-pro-20260423")
assert price.input_per_m == pytest.approx(0.435)
def test_price_entry_cache_fields_default_none() -> None:
entry = PriceEntry(3.0, 15.0)
assert entry.cache_read_per_m is None
assert entry.cache_write_per_m is None
@pytest.mark.parametrize(
("model", "input_per_m", "output_per_m"),
[
("moonshotai/kimi-k2.7-code-20260612", 0.95, 4.0),
("kimi-k2.7-code", 0.95, 4.0),
("claude-haiku-4-5-20251001", 1.0, 5.0),
("deepseek-chat", 0.14, 0.28),
("deepseek-reasoner", 0.26, 0.38),
("glm-5.2", 1.40, 4.40),
("qwen3.7-max", 1.25, 3.75),
("qwen3.7-plus", 0.40, 1.60),
],
)
def test_previously_missing_ids_now_have_static_entries(
monkeypatch: pytest.MonkeyPatch,
model: str,
input_per_m: float,
output_per_m: float,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price(model)
assert price.input_per_m == pytest.approx(input_per_m)
assert price.output_per_m == pytest.approx(output_per_m)
@pytest.mark.parametrize("model", ["z-ai/glm-5.1", "z-ai/glm-5.2"])
def test_glm_5_static_price_matches_openrouter_native_provider(
monkeypatch: pytest.MonkeyPatch,
model: str,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price(model)
assert price.input_per_m == pytest.approx(1.40)
assert price.output_per_m == pytest.approx(4.40)
@pytest.mark.parametrize(
("model", "input_per_m", "output_per_m"),
[
("qwen/qwen3.7-plus-20260602", 0.40, 1.60),
("qwen/qwen3.7-max", 1.25, 3.75),
("google/gemini-3-flash-preview-20251217", 0.50, 3.0),
("mistralai/mistral-large-2512", 0.50, 1.50),
("meta-llama/llama-4-maverick", 0.15, 0.60),
],
)
def test_g8_ensemble_static_fallback_prices_do_not_use_generic_default(
monkeypatch: pytest.MonkeyPatch,
model: str,
input_per_m: float,
output_per_m: float,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price(model)
assert price.input_per_m == pytest.approx(input_per_m)
assert price.output_per_m == pytest.approx(output_per_m)
def test_claude_opus_4_8_static_price_matches_openrouter_model_catalog(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price("anthropic/claude-opus-4.8")
assert price.input_per_m == pytest.approx(5.0)
assert price.output_per_m == pytest.approx(25.0)
@pytest.mark.parametrize(
("model", "input_per_m", "output_per_m"),
[
("qwen-plus", 0.115, 0.287),
("qwen-flash", 0.022, 0.216),
("qwen-turbo", 0.044, 0.087),
("qwen-max", 0.345, 1.377),
],
)
def test_dashscope_beijing_qwen_static_prices_match_official_model_studio_pricing(
monkeypatch: pytest.MonkeyPatch,
model: str,
input_per_m: float,
output_per_m: float,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price(model)
assert price.input_per_m == pytest.approx(input_per_m)
assert price.output_per_m == pytest.approx(output_per_m)
def test_dashscope_beijing_qwen_plus_smoke_usage_estimates_cost_from_tokens(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price("qwen-plus")
estimated_cost = (31 * price.input_per_m + 6 * price.output_per_m) / 1_000_000
assert estimated_cost == pytest.approx(0.000005287)
def test_provider_profile_models_do_not_use_default_pricing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
default = PriceEntry(3.0, 15.0)
models = [
"qwen3.6-flash",
"qwen3.7-plus",
"qwen3.7-max",
"deepseek-v4-flash",
"deepseek-v4-pro",
"gemini-3.1-flash-lite",
"gemini-3.5-flash",
"gemini-3.1-pro-preview",
"gpt-4.1-nano",
"gpt-4.1-mini",
"gpt-4.1",
"glm-5-turbo",
"glm-5.2",
"kimi-k2.6",
"kimi-k2.7-code",
"doubao-seed-1-6-flash-250828",
"doubao-seed-1-6-251015",
"doubao-seed-1-6-thinking-250715",
"doubao-seed-2-0-mini-260215",
"doubao-seed-2-0-lite-260215",
"doubao-seed-2-0-pro-260215",
"doubao-seed-2-0-code-preview-260215",
]
for model in models:
assert lookup_price(model) != default, model
def test_local_embedding_model_does_not_fetch_openrouter_pricing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "1")
def fail_fetch(*_args, **_kwargs):
raise AssertionError("local embedding models should not hit OpenRouter pricing")
monkeypatch.setattr("opensquilla.engine.pricing._fetch_openrouter_json_sync", fail_fetch)
price = lookup_price("BAAI/bge-small-zh-v1.5")
assert price.input_per_m == 0
assert price.output_per_m == 0
@pytest.mark.parametrize(
("model", "input_per_m", "output_per_m"),
[
("gpt-4.1-nano", 0.10, 0.40),
("gpt-4.1-mini", 0.40, 1.60),
("gpt-4.1", 2.0, 8.0),
("gpt-5.4-nano", 0.20, 1.25),
("gpt-5.4-mini", 0.75, 4.50),
("gpt-5.5", 5.0, 30.0),
("glm-5", 1.0, 3.20),
("glm-5-turbo", 1.20, 4.0),
("glm-5.1", 1.40, 4.40),
("kimi-k2.6", 0.95, 4.0),
("kimi-k2.7-code", 0.95, 4.0),
("gemini-3.1-flash-lite", 0.25, 1.50),
("gemini-3.5-flash", 1.50, 9.0),
("gemini-3.1-pro-preview", 2.0, 12.0),
],
)
def test_direct_provider_profile_estimate_prices_match_approved_static_entries(
monkeypatch: pytest.MonkeyPatch,
model: str,
input_per_m: float,
output_per_m: float,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price(model)
assert price.input_per_m == pytest.approx(input_per_m)
assert price.output_per_m == pytest.approx(output_per_m)
@pytest.mark.parametrize(
("model", "input_per_m", "output_per_m"),
[
("doubao-seed-2-0-mini-260215", 0.029, 0.287),
("doubao-seed-2-0-lite-260215", 0.086, 0.516),
("doubao-seed-2-0-pro-260215", 0.459, 2.294),
("doubao-seed-2-0-code-preview-260215", 0.459, 2.294),
],
)
def test_volcengine_seed_2_static_prices_match_under_32k_online_inference_pricing(
monkeypatch: pytest.MonkeyPatch,
model: str,
input_per_m: float,
output_per_m: float,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price(model)
assert price.input_per_m == pytest.approx(input_per_m)
assert price.output_per_m == pytest.approx(output_per_m)
@pytest.mark.parametrize(
("model", "input_per_m", "output_per_m"),
[
("gpt-4.1", 2.0, 8.0),
("glm-4.5", 0.115, 0.287),
("kimi-k2.6", 0.95, 4.0),
("MiniMax-M2.7", 0.118, 0.99),
],
)
def test_direct_openai_zhipu_kimi_and_minimax_prices_do_not_fall_back_to_default(
monkeypatch: pytest.MonkeyPatch,
model: str,
input_per_m: float,
output_per_m: float,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price(model)
assert price.input_per_m == pytest.approx(input_per_m)
assert price.output_per_m == pytest.approx(output_per_m)
def test_local_provider_is_free_even_for_unqualified_model(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
# Bare ollama model id misses the "ollama/" table entry -> cloud default.
assert lookup_price("qwen3:4b").input_per_m == pytest.approx(3.0)
# Passing the local provider makes it free regardless of the model id.
free = lookup_price("qwen3:4b", provider="ollama")
assert free.input_per_m == 0.0
assert free.output_per_m == 0.0
def test_local_provider_case_insensitive_and_covers_local_runtimes() -> None:
for prov in ("ollama", "OLLAMA", "lm_studio", "vllm", "ovms", "local"):
price = lookup_price("some-model", provider=prov)
assert (price.input_per_m, price.output_per_m) == (0.0, 0.0)
def test_cloud_provider_arg_does_not_zero_priced_model(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0")
price = lookup_price("claude-sonnet-4", provider="anthropic")
assert price.input_per_m == pytest.approx(3.0)