from __future__ import annotations import pytest from opensquilla.engine.pricing import PriceEntry, _endpoint_price, resolve_model_price from opensquilla.provider.model_catalog import ModelCatalog, set_shared_catalog def test_local_provider_resolves_free(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0") r = resolve_model_price("qwen3:4b", provider="lm_studio") assert r.source == "local_free" assert r.entry.input_per_m == 0.0 and r.entry.cache_read_per_m == 0.0 def test_user_catalog_override_wins_over_static(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0") catalog = ModelCatalog() catalog.set_user_overrides( { "deepseek/deepseek-v4-pro": { "input_cost_per_mtok": 0.2, "output_cost_per_mtok": 0.4, "cache_read_cost_per_mtok": 0.002, } } ) set_shared_catalog(catalog) try: r = resolve_model_price("deepseek/deepseek-v4-pro", provider="deepseek") assert r.source == "user_override" assert r.entry.input_per_m == pytest.approx(0.2) assert r.entry.cache_read_per_m == pytest.approx(0.002) finally: set_shared_catalog(None) def test_catalog_snapshot_wins_over_static_table_with_source( monkeypatch: pytest.MonkeyPatch, ) -> None: """The refreshed models.dev snapshot now vendors deepseek-v4-pro's own cost keys, so a provider-qualified lookup resolves through the catalog layer (matching the static table's official rate) instead of falling all the way to the static table.""" monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0") r = resolve_model_price("deepseek/deepseek-v4-pro", provider="deepseek") assert r.source == "catalog" assert r.entry.input_per_m == pytest.approx(0.435) assert r.entry.cache_read_per_m == pytest.approx(0.003625) def test_static_table_fallback_with_source(monkeypatch: pytest.MonkeyPatch) -> None: """A model+provider pair verified ABSENT from the snapshot (a dated suffix models.dev does not carry) still falls through to the static table — the catalog-first change above only wins when the snapshot actually knows the exact (provider, model).""" monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0") r = resolve_model_price("deepseek/deepseek-v4-pro-20260423", provider="deepseek") assert r.source == "static_table" assert r.entry.input_per_m == pytest.approx(0.435) def test_unknown_model_resolves_default_source(monkeypatch: pytest.MonkeyPatch) -> None: monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "0") r = resolve_model_price("totally-unknown-model-xyz", provider="mistral") assert r.source == "default" assert r.entry == PriceEntry(3.0, 15.0) def test_live_fetch_skipped_for_non_openrouter_provider(monkeypatch: pytest.MonkeyPatch) -> None: """A siliconflow 'deepseek-ai/...' id must not query the OpenRouter marketplace.""" monkeypatch.setenv("OPENSQUILLA_OPENROUTER_LIVE_PRICING", "1") called: list[object] = [] monkeypatch.setattr( "opensquilla.engine.pricing._fetch_live_openrouter_price", lambda *a, **k: called.append(a) or None, ) resolve_model_price("deepseek-ai/DeepSeek-V3.2", provider="siliconflow") assert called == [] def test_endpoint_price_parses_cache_rates_without_discount_inverse() -> None: """Prompt/completion take the discount-inverse; cache rates are passed through.""" price = _endpoint_price( { "pricing": { "prompt": "0.000001", "completion": "0.000002", "input_cache_read": "0.0000001", "input_cache_write": "0.00000125", "discount": 0.5, } } ) assert price is not None # 1/(1 - 0.5) = 2x on prompt/completion; no inverse on the cache rates. assert price.input_per_m == pytest.approx(2.0) assert price.output_per_m == pytest.approx(4.0) assert price.cache_read_per_m == pytest.approx(0.1) assert price.cache_write_per_m == pytest.approx(1.25) def test_endpoint_price_cache_rates_none_when_absent() -> None: price = _endpoint_price({"pricing": {"prompt": "0.000001", "completion": "0.000002"}}) assert price is not None assert price.cache_read_per_m is None assert price.cache_write_per_m is None