feat(cost): persist reasoning tokens and the peak/off-peak tier
CI / lint / typecheck / import-linter / tests / secrets (push) Canceled after 0s

ob_ai_call is the only input to V2 pricing, and it was dropping the two
numbers needed to interpret its own cost column.

tokens_reasoning was captured from the provider response but never written to
the table, so the cost of thinking mode was invisible once it left the
process. Folding it into tokens_out was not an option either -- it is billed
at the output rate, and mixing the two hides exactly the difference we now
have to watch.

rate_tier records which price list applied. Without it a row's cost_cents
cannot be rechecked: DeepSeek's peak rate is double off-peak from 2026-08-17,
so recomputing an old row at "the current rate" silently produces a different
answer than what was actually billed.

Migration 0002 adds both columns. tokens_reasoning defaults to 0 rather than
NULL so cost aggregates do not collapse to NULL on historical rows;
rate_tier stays nullable because rows written before this change genuinely
have an unknown tier, and NULL says that more honestly than a guessed 'flat'.

The smoke script now asserts both: reasoning tokens must be 0 (a non-zero
value means a provider default re-enabled thinking mode behind our backs) and
rate_tier must be present. Verified against the real API -- tokens_reasoning=0,
rate_tier=off_peak, 3.9s round trip.
This commit is contained in:
xsl
2026-08-14 01:02:13 +08:00
parent f46ecc5eb0
commit c97f8ae3e1
9 changed files with 119 additions and 2 deletions
+4
View File
@@ -254,6 +254,7 @@ class LLMGateway:
user_id=user_id,
prompt_id=prompt_id,
prompt_version=prompt_version,
rate_tier=self._rates.current_tier(),
error_code=exc.code,
error_message=str(exc)[:1000],
ref_type=ref_type,
@@ -263,6 +264,8 @@ class LLMGateway:
raise
latency_ms = self._elapsed_ms(started)
# 单价与档位在同一刻取,成本落库后不再回算 —— 跨时段重算会改掉历史成本。
rate_tier = self._rates.current_tier()
cost = compute_cost_cents(response.usage, self._rates.for_model(response.model))
parsed: BaseModel | None = None
@@ -289,6 +292,7 @@ class LLMGateway:
user_id=user_id,
prompt_id=prompt_id,
prompt_version=prompt_version,
rate_tier=rate_tier,
error_code=error_code,
error_message=last_detail if not success else None,
ref_type=ref_type,