"""峰谷定价(DeepSeek 自 2026-08-17 起高峰单价翻倍)。 保护的是:同一次调用在高峰与空闲时段必须记出不同的成本。若只配一套单价, ``ob_ai_call`` 会与真实账单差出接近 2 倍,而这张表是 V2 定价的唯一依据。 """ from __future__ import annotations from datetime import UTC, datetime, timedelta, timezone from decimal import Decimal import pytest from packages.llm.cost import ( TIER_FLAT, TIER_OFF_PEAK, TIER_PEAK, ModelRate, PeakAwareRateTable, RateTable, compute_cost_cents, is_peak, ) from packages.llm.types import Usage BEIJING = timezone(timedelta(hours=8)) OFF_PEAK = ModelRate(Decimal("150"), Decimal("5"), Decimal("450")) PEAK = ModelRate(Decimal("300"), Decimal("10"), Decimal("900")) MODEL = "deepseek-v4-flash" def _table(*, enabled: bool = True) -> PeakAwareRateTable: return PeakAwareRateTable( RateTable({MODEL: OFF_PEAK}, default=OFF_PEAK), RateTable({MODEL: PEAK}, default=PEAK), enabled=enabled, ) @pytest.mark.parametrize( ("hour", "expected"), [ (0, False), (8, False), (9, True), # 高峰左闭 (11, True), (12, False), # 高峰右开 (13, False), (14, True), (17, True), (18, False), (23, False), ], ) def test_peak_windows_follow_the_published_schedule(hour: int, expected: bool) -> None: assert is_peak(datetime(2026, 8, 20, hour, 30, tzinfo=BEIJING)) is expected def test_utc_input_is_converted_to_beijing_before_judging() -> None: # 02:00 UTC = 10:00 北京时间,属高峰。按 UTC 判定会把高峰算成空闲、成本少记一半 assert is_peak(datetime(2026, 8, 20, 2, 0, tzinfo=UTC)) is True def test_peak_rate_is_double_the_off_peak_rate() -> None: table = _table() usage = Usage(tokens_in=10_000, tokens_out=2_000, tokens_cached=0) off_peak_cost = compute_cost_cents( usage, table.for_model(MODEL, datetime(2026, 8, 20, 13, 0, tzinfo=BEIJING)) ) peak_cost = compute_cost_cents( usage, table.for_model(MODEL, datetime(2026, 8, 20, 10, 0, tzinfo=BEIJING)) ) assert peak_cost == off_peak_cost * 2 def test_disabling_peak_pricing_keeps_one_rate() -> None: # 供应商若取消峰谷定价,改一个开关即可,不必回滚代码 table = _table(enabled=False) assert table.for_model(MODEL, datetime(2026, 8, 20, 10, 0, tzinfo=BEIJING)) is OFF_PEAK def test_unknown_model_still_costs_money_in_both_tiers() -> None: table = _table() for hour in (10, 13): rate = table.for_model("some-new-model", datetime(2026, 8, 20, hour, tzinfo=BEIJING)) assert rate.input_cents_per_mtok > 0 def test_tier_is_reported_so_cost_can_be_rechecked_later() -> None: # 没有档位,事后就无法判断某行 cost_cents 是按哪套单价算的 table = _table() assert table.current_tier(datetime(2026, 8, 20, 10, 0, tzinfo=BEIJING)) == TIER_PEAK assert table.current_tier(datetime(2026, 8, 20, 13, 0, tzinfo=BEIJING)) == TIER_OFF_PEAK def test_single_rate_tables_report_a_flat_tier() -> None: assert RateTable({MODEL: OFF_PEAK}, default=OFF_PEAK).current_tier() == TIER_FLAT assert _table(enabled=False).current_tier() == TIER_FLAT