feat(usage): support importing model pricing from models.dev#4079
Conversation
Add an "Import from models.dev" button to the Add Pricing panel that fetches https://models.dev/api.json, lists priced models sorted by release date (newest 50 by default, full-text search across ~4800), and bulk-imports the selected entries through the same update_model_pricing command as manual entry. - Normalize imported model IDs to match the backend's clean_model_id_for_pricing rules (strip vendor prefix, lowercase, truncate ':' suffix, map '@' to '-', drop the [1m] marker) so the stored rows actually match cost-attribution lookups - Dedupe selections that collapse to the same model_id and report skipped duplicates in the success toast - Invalidate usage queries on settled (not just success) so partial import failures still refresh the pricing list - Keep ESC inside the picker's search input from closing the dialog and discarding the selection - Add i18n keys for zh/en/zh-TW/ja and unit tests for the normalization, price formatting and flattening logic Fixes farion1231#4017 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 78ca0380aa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The scoped backfill selected zero-cost rows via exact SQL string match, but log columns store raw model strings (route prefixes, :free variants, date suffixes), so alias rows were skipped until the next full backfill on startup. Filter rows in Rust with the same model_pricing_candidates normalization used by the pricing lookup; pricing decision logic is untouched. Pre-existing gap from schema v11, surfaced by bulk import. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@codex review |
|
To use Codex here, create a Codex account and connect to github. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 936a776b0f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Each update_model_pricing call triggers a backfill pass that loads every zero-cost usage row before filtering by model, so bulk-importing N entries scaled as selectedModels x allZeroCostLogs full scans. Re-importing pricing is rare, so drop the batch path instead of optimizing it: the picker is now single-select, one import runs exactly one update_model_pricing call and one backfill pass. This also removes the normalized-ID dedup logic and the useImportModelPricing hook in favor of the existing useUpdateModelPricing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
目前来看 批量导入模型定价的场景还是比较少的,如果为了支持批量导入而去更改rust 接口定义的话 改动面就大了 收益不太高,所以暂时还是改回只导入单条模型定价 |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6ce263528c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
i think it's ready for merging into master ,kindly ping @farion1231 |
…231#4079) * feat(usage): support importing model pricing from models.dev Add an "Import from models.dev" button to the Add Pricing panel that fetches https://models.dev/api.json, lists priced models sorted by release date (newest 50 by default, full-text search across ~4800), and bulk-imports the selected entries through the same update_model_pricing command as manual entry. - Normalize imported model IDs to match the backend's clean_model_id_for_pricing rules (strip vendor prefix, lowercase, truncate ':' suffix, map '@' to '-', drop the [1m] marker) so the stored rows actually match cost-attribution lookups - Dedupe selections that collapse to the same model_id and report skipped duplicates in the success toast - Invalidate usage queries on settled (not just success) so partial import failures still refresh the pricing list - Keep ESC inside the picker's search input from closing the dialog and discarding the selection - Add i18n keys for zh/en/zh-TW/ja and unit tests for the normalization, price formatting and flattening logic Fixes farion1231#4017 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(usage): match scoped cost backfill against raw model aliases The scoped backfill selected zero-cost rows via exact SQL string match, but log columns store raw model strings (route prefixes, :free variants, date suffixes), so alias rows were skipped until the next full backfill on startup. Filter rows in Rust with the same model_pricing_candidates normalization used by the pricing lookup; pricing decision logic is untouched. Pre-existing gap from schema v11, surfaced by bulk import. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * refactor(usage): restrict models.dev pricing import to a single model Each update_model_pricing call triggers a backfill pass that loads every zero-cost usage row before filtering by model, so bulk-importing N entries scaled as selectedModels x allZeroCostLogs full scans. Re-importing pricing is rare, so drop the batch path instead of optimizing it: the picker is now single-select, one import runs exactly one update_model_pricing call and one backfill pass. This also removes the normalized-ID dedup logic and the useImportModelPricing hook in favor of the existing useUpdateModelPricing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary / 概述
在「使用统计 → 成本定价 → 添加」面板新增 从 models.dev 导入 入口:拉取 https://models.dev/api.json 的模型定价数据(约 4800 个带定价的模型),按发布时间倒序默认展示最新 50 个,支持全量搜索与按供应商筛选,选择单个模型导入。导入复用与手动添加完全相同的
update_model_pricing命令。Adds an "Import from models.dev" entry to the Add Pricing panel: fetches model pricing from https://models.dev/api.json (~4800 priced models), shows the 50 most recently released by default, supports full-text search and provider filtering, and imports the selected model through the exact same
update_model_pricingcommand as manual entry.实现要点 / Key implementation details:
clean_model_id_for_pricing的同等规则归一化(去掉xx/厂商前缀、转小写、截断:后缀、@→-、去[1m]标记)。models.dev 约 60% 的原始 ID 含这些字符,原样入库将永远无法被成本归因查询匹配到。Model IDs are normalized to mirror the backend's
clean_model_id_for_pricingrules before storing; ~60% of raw models.dev IDs would otherwise never match cost-attribution lookups.openrouter/anthropic/claude-sonnet-4.5:free),别名形式的历史行会被漏掉。改为在内存中按归一化候选集合匹配model/request_model/pricing_model三列。Scoped backfill (Rust, addressing the first review comment) now matches log rows against normalized candidates of all three model columns in memory, instead of exact SQL string equality that skipped alias-form historical rows.
update_model_pricing每次写入后都会触发一次零成本历史回填,该回填会先加载全部零成本日志行再按模型过滤,批量导入 N 个模型会产生selectedModels × allZeroCostLogs的重复全表扫描。重新导入定价的场景不多,因此直接收窄为单选,同时也消除了同一归一化 ID 在多个供应商条目间静默互相覆盖的问题。Single-select import (addressing the second review comment) — each
update_model_pricingcall triggers a backfill pass that loads all zero-cost rows before filtering, so bulk import scaled asselectedModels × allZeroCostLogsfull scans. Since re-importing pricing is rare, the picker is restricted to one model per import, which also removes the silent last-write-wins risk between provider entries sharing a normalized ID.FullScreenPanel的isTextEditableTarget约定一致);供应商下拉为 135 项加了 max-height;价格字符串格式化保证不输出科学计数法(后端Decimal::from_str不接受)。ESC inside the search input no longer discards the selection; the 135-item provider dropdown gets a max-height; price strings are formatted to never produce exponent notation (rejected by
Decimal::from_str).Adds i18n strings for all four locales and unit tests for the normalization, price formatting and flattening logic.
Related Issue / 关联 Issue
Fixes #4017
Screenshots / 截图
Checklist / 检查清单
pnpm typecheckpasses / 通过 TypeScript 类型检查pnpm format:checkpasses / 通过代码格式检查cargo clippypasses (if Rust code changed) / 通过 Clippy 检查(如修改了 Rust 代码)🤖 Generated with Claude Code