Files
meida_backend/migrations
xsl c97f8ae3e1
CI / lint / typecheck / import-linter / tests / secrets (push) Canceled after 0s
feat(cost): persist reasoning tokens and the peak/off-peak tier
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.
2026-08-14 01:02:13 +08:00
..

migrations/ — Alembic

异步引擎(asyncpg)+ pgvector。Base.metadata 来自 packages/shared/models.py。

make migrate                          # upgrade head
make makemigration m="add kb tables"  # autogenerate
uv run alembic downgrade -1           # 回滚一步

规矩

  1. 迁移必须向后兼容(架构 §14.3):加列不删列、先双写后切读。发布流程是 「先迁移,后发布」,所以新迁移必须能和旧版应用代码共存,否则回滚时数据 不兼容。
  2. 必须可逆,或在文件头明确标注不可逆并说明原因(§16.2 迁移检查清单)。 CI 会跑 upgrade → downgrade base → upgrade。
  3. 表名必须带域前缀:id_ / pf_ / rp_ / kb_ / ag_ / tr_ / bl_ / tk_ / cp_ / ob_。单库多域,前缀就是将来拆库的边界(§6.1)。
  4. 主键用雪花 ID(bigint),不用自增(枚举攻击 + 分库冲突),不用 UUID 作主键(索引局部性差)。
  5. 不要存人脸原图,也不要存可反推生物特征的数据(TD-20 / CR-02)。诊断结果 只存分类标签与色值等派生量。
  6. 需要过滤的字段一律提升为列;JSONB 只用于「结构会演进且不需要索引全部字段」 的场景(§6.1 原则 6)。
  7. 配置类数据(配额规则、色板、路由)用迁移种子进表,不要硬编码进代码 (NFR-MAINT-05)。

已有迁移

版本 内容
0001 pgvector 扩展;id_user id_user_identity id_consent;bl_quota_rule bl_quota bl_ledger;tk_task;ob_ai_call;配额规则种子数据

§6.2 里其余的表(pf_ 画像与记忆、rp_ 报告、kb_ 知识库、ag_ 会话、 tr_ 换装、cp_ 合规)尚未创建,属于 M2~M4。