Skip to content

feat: import formatted model prices from upstream - #799

Open
awsl233777 wants to merge 19 commits into
mainfrom
feature/litellm-model-price-sync
Open

feat: import formatted model prices from upstream#799
awsl233777 wants to merge 19 commits into
mainfrom
feature/litellm-model-price-sync

Conversation

@awsl233777

@awsl233777 awsl233777 commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • add POST /model-prices/upstream/prices to fetch formatted upstream model prices
  • backend only normalizes upstream data into domain.ModelPrice; it does not diff, compare, sync, or apply
  • keep existing files minimally touched:
    • internal/handler/admin.go: route dispatch only
    • internal/service/admin.go: thin delegation only
    • web/src/pages/model-prices/index.tsx: imports/renders the upstream import component only
  • put new backend logic in internal/modelpriceupstream and internal/handler/model_price_upstream.go
  • put new frontend UI/orchestration in upstream-prices-import.tsx and upstream-prices-dialog.tsx

Apply behavior

  • no separate apply endpoint and no SyncModelPricesFromExternalSource path
  • frontend derives selectable create/update rows against the current price list
  • selected rows apply through existing generic endpoints:
    • create → POST /model-prices
    • update → PUT /model-prices/{id}
  • database prices absent from the selected upstream are preserved

Extensibility

  • omitted source defaults to litellm
  • future upstreams implement modelpriceupstream.Source and register with modelpriceupstream.Register
  • LiteLLM is one source implementation, not hardcoded into admin/service/page logic

Review hardening

  • unknown upstream source returns HTTP 400
  • empty chunked request bodies are accepted as empty fetch requests
  • LiteLLM fetch uses request context cancellation and a bounded response body reader
  • upstream source registry is guarded by a mutex
  • e2e test asserts filtered sample rows and avoids unsafe total assertion

Test plan

  • go test ./internal/modelpriceupstream ./internal/service ./internal/handler ./tests/e2e -run 'ModelPrice|ModelPrices|Resolve|Convert|Register|Fetch|List'
  • pnpm -C web typecheck

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@awsl233777, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b089d991-8167-4f0e-896f-573fa11d0750

📥 Commits

Reviewing files that changed from the base of the PR and between 9b1bd27 and f1506b0.

📒 Files selected for processing (14)
  • internal/handler/admin.go
  • internal/handler/model_price_upstream.go
  • internal/modelpriceupstream/litellm.go
  • internal/modelpriceupstream/prices.go
  • internal/modelpriceupstream/source.go
  • internal/modelpriceupstream/source_test.go
  • internal/service/admin.go
  • tests/e2e/model_prices_upstream_test.go
  • web/src/lib/transport/types.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/model-prices/index.tsx
  • web/src/pages/model-prices/upstream-prices-dialog.tsx
  • web/src/pages/model-prices/upstream-prices-import.tsx
📝 Walkthrough

Walkthrough

新增 LiteLLM 外部模型价格获取接口。系统支持来源解析、价格标准化、前端差异预览和选择性应用。获取阶段不写入数据库,应用阶段使用现有创建和更新操作。

Changes

外部价格同步

Layer / File(s) Summary
价格源注册与转换
internal/modelpriceupstream/*
新增价格源接口和 LiteLLM 实现。系统解析外部价格,处理缓存、图片及高上下文价格,并过滤无效记录。
管理员 API 与端到端验证
internal/handler/admin.go, internal/service/admin.go, tests/e2e/model_prices_test.go
新增 POST /model-prices/upstream/prices。接口支持可选 source,并在获取阶段返回标准化价格。端到端测试覆盖后续创建和更新。
前端传输契约与查询 Hooks
web/src/lib/transport/*, web/src/hooks/queries/*
新增外部价格结果、变更类型、Transport 方法和 React Query mutation hook。
模型价格预览与应用界面
web/src/pages/model-prices/index.tsx, web/src/locales/*.json
新增来源选择、差异预览、逐项或全选应用、结果统计及中英文文案。

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Admin
  participant ModelPricesPage
  participant HttpTransport
  participant AdminHandler
  participant AdminService
  participant LiteLLM
  Admin->>ModelPricesPage: 选择来源并开始预览
  ModelPricesPage->>HttpTransport: fetchExternalModelPrices(source)
  HttpTransport->>AdminHandler: POST /model-prices/upstream/prices
  AdminHandler->>AdminService: ListModelPricesFromExternalSource(source)
  AdminService->>LiteLLM: 获取模型价格
  LiteLLM-->>AdminService: 返回价格数据
  AdminService-->>HttpTransport: 返回标准化价格结果
  HttpTransport-->>ModelPricesPage: 展示创建和更新差异
  Admin->>ModelPricesPage: 选择变更并应用
  ModelPricesPage->>ModelPricesPage: 调用现有创建或更新操作
Loading

Possibly related PRs

  • awsl-project/maxx#192:同样涉及模型价格功能,但该 PR 处理备份导入、导出和恢复。

Poem

我是小兔,价格数据跳进来,
LiteLLM 清单排成行。
先预览,再挑选,
创建更新不慌张。
自定义价格稳稳留,
同步完成,胡萝卜香!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.10% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 标题准确概括了从上游导入规范化模型价格这一主要变更,内容简洁且与变更范围一致。
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/litellm-model-price-sync

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (4)
internal/handler/admin.go (1)

2533-2561: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

错误一律返回 500,客户端无法区分错误类型。

服务层会返回两类不同性质的错误:unsupported model price sync source %q 属于客户端输入错误,抓取或解码失败属于上游源故障。当前两者都映射为 500。建议对不支持的来源返回 400,对外部抓取失败返回 502。前端可据此显示不同提示。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/handler/admin.go` around lines 2533 - 2561, Update
handleModelPricesSyncExternalPreview and handleModelPricesSyncExternalApply to
distinguish service errors: return HTTP 400 for unsupported model price sync
sources and HTTP 502 for external source fetch or decode failures. Reuse the
existing error text or error classification mechanism, preserving the current
JSON error response format and successful response paths.
internal/service/admin.go (2)

2289-2337: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy lift

同步写入没有事务保护。

循环内逐条调用 CreateUpdate。如果中途某次写入失败,函数直接返回错误,此前已写入的记录会保留。数据库处于部分同步状态,且返回给调用方的 result 被丢弃,管理员无法得知已应用了哪些变更。建议将应用阶段包裹在一个事务中,或在返回错误时同时返回已完成的变更统计。

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/admin.go` around lines 2289 - 2337, Update syncModelPrices
to protect the apply phase from partial writes by executing the loop’s Create
and Update operations within a single repository transaction, committing only
after all writes succeed and rolling back on any error. Preserve dry-run
behavior and ensure result statistics are returned only for a successfully
applied synchronization.

2341-2376: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

外部响应体没有大小上限,并且请求缺少 context。

json.NewDecoder(resp.Body) 直接解码整个响应。如果配置的源 URL 返回超大响应,服务进程内存会被放大占用。建议用 io.LimitReader 限制读取长度。另外建议改用 http.NewRequestWithContext,使调用方可以取消请求。

🛠️ 建议的修复
-	var raw map[string]liteLLMModelPrice
-	if err := json.NewDecoder(resp.Body).Decode(&raw); err != nil {
+	const maxModelPriceBodyBytes = 32 << 20
+	var raw map[string]liteLLMModelPrice
+	if err := json.NewDecoder(io.LimitReader(resp.Body, maxModelPriceBodyBytes)).Decode(&raw); err != nil {
 		return nil, source, sourceURL, fmt.Errorf("decode model price source %q: %w", source.Code, err)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/service/admin.go` around lines 2341 - 2376, Update
fetchExternalModelPrices to accept and propagate a caller context, create the
request with http.NewRequestWithContext instead of client.Get, and execute it
through the HTTP client so cancellation is honored. Bound response decoding with
io.LimitReader using an appropriate maximum response size while preserving the
existing error handling and price conversion flow.
tests/e2e/model_prices_test.go (1)

265-306: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

建议补充对 sample_ 过滤和 total 的断言,并使用安全的类型断言。

两点建议:

  1. 测试数据包含 sample_spec,转换函数会过滤它。当前测试没有断言该条目被排除。建议断言 total 为 2,或断言结果中不存在 sample_spec。这样可以覆盖过滤逻辑。
  2. preview["created"].(float64) 使用非安全类型断言。如果字段缺失或类型变化,测试会 panic,失败信息不清晰。建议使用双返回值形式。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/e2e/model_prices_test.go` around lines 265 - 306, 补充同步预览/应用结果的断言:验证
total 为 2 或确认返回结果中不存在被过滤的 sample_spec 条目,以覆盖 sample_ 过滤逻辑;同时将 preview 和 result 中
created、updated 的直接类型断言改为带 ok 检查的安全断言,并在字段缺失或类型错误时输出明确失败信息。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/handler/admin.go`:
- Around line 2567-2577: Update decodeModelPriceSyncRequest to treat
json.Decoder.Decode returning io.EOF as an empty request body, returning req,
true instead of writing a 400 response; continue returning 400 for all other
decoding errors.

In `@web/src/hooks/queries/use-model-prices.ts`:
- Around line 106-108: Update the onSuccess handler to also invalidate cached
model price detail queries by invalidating modelPriceKeys.details() (or
modelPriceKeys.all), while preserving the existing list and pricing
invalidations.

In `@web/src/locales/en.json`:
- Line 1588: Update the syncExternalPreviewDesc translation in
web/src/locales/en.json at lines 1588-1588 and web/src/locales/zh.json at lines
1585-1585 to state that only database prices for modelId records absent from the
selected external source are preserved; clarify that matching upstream records
with changed prices may be updated.

In `@web/src/pages/model-prices/index.tsx`:
- Around line 202-204: Bind the apply operation to the reviewed external-price
snapshot instead of re-fetching by source. In
web/src/pages/model-prices/index.tsx:202-204, pass the snapshot identifier; add
a usable expiry, version, or content hash to the preview result in
web/src/lib/transport/types.ts:1674-1683; accept it in
web/src/lib/transport/interface.ts:370-371 and send it from
web/src/lib/transport/http-transport.ts:1342-1355. Update
handleModelPricesSyncExternalPreview and handleModelPricesSyncExternalApply to
create, receive, and validate the snapshot before applying it.

---

Nitpick comments:
In `@internal/handler/admin.go`:
- Around line 2533-2561: Update handleModelPricesSyncExternalPreview and
handleModelPricesSyncExternalApply to distinguish service errors: return HTTP
400 for unsupported model price sync sources and HTTP 502 for external source
fetch or decode failures. Reuse the existing error text or error classification
mechanism, preserving the current JSON error response format and successful
response paths.

In `@internal/service/admin.go`:
- Around line 2289-2337: Update syncModelPrices to protect the apply phase from
partial writes by executing the loop’s Create and Update operations within a
single repository transaction, committing only after all writes succeed and
rolling back on any error. Preserve dry-run behavior and ensure result
statistics are returned only for a successfully applied synchronization.
- Around line 2341-2376: Update fetchExternalModelPrices to accept and propagate
a caller context, create the request with http.NewRequestWithContext instead of
client.Get, and execute it through the HTTP client so cancellation is honored.
Bound response decoding with io.LimitReader using an appropriate maximum
response size while preserving the existing error handling and price conversion
flow.

In `@tests/e2e/model_prices_test.go`:
- Around line 265-306: 补充同步预览/应用结果的断言:验证 total 为 2 或确认返回结果中不存在被过滤的 sample_spec
条目,以覆盖 sample_ 过滤逻辑;同时将 preview 和 result 中 created、updated 的直接类型断言改为带 ok
检查的安全断言,并在字段缺失或类型错误时输出明确失败信息。
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f62e58e6-e58c-42ba-a69c-cbd41628e5ca

📥 Commits

Reviewing files that changed from the base of the PR and between 48271f5 and 31890c8.

📒 Files selected for processing (12)
  • internal/handler/admin.go
  • internal/service/admin.go
  • tests/e2e/model_prices_test.go
  • web/src/hooks/queries/index.ts
  • web/src/hooks/queries/use-model-prices.ts
  • web/src/lib/transport/http-transport.ts
  • web/src/lib/transport/index.ts
  • web/src/lib/transport/interface.ts
  • web/src/lib/transport/types.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/model-prices/index.tsx
📜 Review details
⏰ Context from checks skipped due to timeout. (4)
  • GitHub Check: Backend Checks
  • GitHub Check: Frontend Checks
  • GitHub Check: playwright
  • GitHub Check: e2e
🔇 Additional comments (11)
internal/service/admin.go (5)

7-14: LGTM!

Also applies to: 31-52, 2004-2038


2378-2388: LGTM!


2429-2473: LGTM!


2320-2328: 🗄️ Data Integrity & Integration

无需修改:ModelPriceRepository.Update 不会用传入的 CreatedAt 覆盖当前行;no-op 分支会回填当前行的 CreatedAt,变更分支会插入新行并回填新行的 CreatedAt

			> Likely an incorrect or invalid review comment.

2390-2427: 🗄️ Data Integrity & Integration

无需修改。 Context1MThreshold 使用数值判定阈值,默认值为 200_000Has1MContext 仅表示启用长上下文溢价,不是 1M 阈值。Context1MThreshold=200000 与默认行为一致。

internal/handler/admin.go (1)

2443-2450: LGTM!

tests/e2e/model_prices_test.go (2)

6-6: LGTM!

Also applies to: 215-263


308-335: LGTM!

web/src/lib/transport/index.ts (1)

162-162: LGTM!

web/src/hooks/queries/index.ts (1)

198-199: LGTM!

web/src/pages/model-prices/index.tsx (1)

14-18: LGTM!

Also applies to: 37-40, 72-73, 137-150, 191-200, 205-222, 238-261, 284-286, 588-662

Comment thread internal/handler/admin.go Outdated
Comment thread web/src/hooks/queries/use-model-prices.ts Outdated
Comment thread web/src/locales/en.json Outdated
Comment thread web/src/pages/model-prices/index.tsx Outdated
@awsl233777 awsl233777 changed the title feat: sync model prices from LiteLLM feat: preview and import model prices from upstream sources Aug 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
web/src/pages/model-prices/index.tsx (2)

696-708: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low value

Set 替代数组查找。

第 705 行在渲染循环中对 selectedSyncChangeKeysincludes。选中集合可能包含全部变更,渲染成本为 O(n×m)。当前上限是 50 行,影响有限。

可把选中状态改为 Set<string>,或在渲染前构造一次 Set

♻️ 建议的修改
+  const selectedKeySet = new Set(selectedSyncChangeKeys);
-                          checked={selectedSyncChangeKeys.includes(key)}
+                          checked={selectedKeySet.has(key)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/model-prices/index.tsx` around lines 696 - 708, 在同步预览渲染逻辑中优化
selectedSyncChangeKeys 的查找:在 map 循环前基于该数组构造一次 Set,并在 checkbox 的 checked 判断中使用
Set.has,避免每行调用 includes;保持现有选中状态与 handleToggleSyncChange 行为不变。

253-259: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

显式判断 update 分支。

第 256 行使用 else 兜底。如果后端后续新增 action 取值(例如 skipdelete),该分支会用 change.after.id 调用更新接口。对于没有 id 的变更,请求会带上 undefined

请改为显式判断 change.action === 'update',并忽略未知取值。

♻️ 建议的修改
       if (change.action === 'create') {
         await createPrice.mutateAsync(input);
         created++;
-      } else {
+      } else if (change.action === 'update') {
         await updatePrice.mutateAsync({ id: change.after.id, data: input });
         updated++;
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/pages/model-prices/index.tsx` around lines 253 - 259, 在处理变更的分支中,将
`else` 替换为显式判断 `change.action === 'update'`,仅对更新操作调用 `updatePrice.mutateAsync`
并递增 `updated`;对未知 action 直接忽略,保留 `create` 分支行为不变。
internal/modelpricesync/source.go (1)

28-41: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

为 source 注册表添加并发保护。

如果服务启动后调用 Register,Line 41 的 map 写入可能与 Line 51 的读取并发执行。Go 会因并发 map 读写而 panic。使用 sync.RWMutex 保护 sources 的读取和写入,或明确限制 Register 只能在服务启动前调用。

建议修改
 import (
 	"fmt"
 	"sort"
 	"strings"
+	"sync"
@@
-var sources = map[string]Source{
-	DefaultSourceCode: NewLiteLLMSource(),
-}
+var (
+	sources = map[string]Source{
+		DefaultSourceCode: NewLiteLLMSource(),
+	}
+	sourcesMu sync.RWMutex
+)
@@
+	sourcesMu.Lock()
+	defer sourcesMu.Unlock()
 	sources[code] = source
@@
+	sourcesMu.RLock()
 	source, ok := sources[code]
+	sourcesMu.RUnlock()

Also applies to: 45-55

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/modelpricesync/source.go` around lines 28 - 41, 为 sources
注册表增加并发保护:定义并使用 sync.RWMutex,在 Register 中写入 sources 前加写锁并确保释放,同时在涉及 sources
读取的查找函数中使用读锁。保留现有 source 校验与规范化逻辑,确保注册和读取不会并发访问 map。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/modelpricesync/source_test.go`:
- Around line 27-30: 在 TestRegisterSupportsIndependentSourceImplementations 中注册
fakeSource 后增加 t.Cleanup,测试结束时恢复注册表中原有值或删除本次新增的 fake source,确保不会影响同包后续测试。

In `@web/src/pages/model-prices/index.tsx`:
- Around line 233-240: Update handleToggleAllSyncChanges to select only the
syncPreview.changes entries currently rendered in the dialog’s visible 50-item
list, rather than the full change set; keep clearing the selection when
unchecked and preserve the existing syncChangeKey generation.
- Around line 242-270: Update handleApplyExternalSync to catch errors from each
createPrice.mutateAsync or updatePrice.mutateAsync operation, continue
processing remaining selected changes, and track failed operations alongside
created and updated counts. Include failed in the syncExternalResult translation
parameters, and ensure the result text is set and the sync preview is closed
even when some changes fail.

---

Nitpick comments:
In `@internal/modelpricesync/source.go`:
- Around line 28-41: 为 sources 注册表增加并发保护:定义并使用 sync.RWMutex,在 Register 中写入
sources 前加写锁并确保释放,同时在涉及 sources 读取的查找函数中使用读锁。保留现有 source 校验与规范化逻辑,确保注册和读取不会并发访问
map。

In `@web/src/pages/model-prices/index.tsx`:
- Around line 696-708: 在同步预览渲染逻辑中优化 selectedSyncChangeKeys 的查找:在 map
循环前基于该数组构造一次 Set,并在 checkbox 的 checked 判断中使用 Set.has,避免每行调用 includes;保持现有选中状态与
handleToggleSyncChange 行为不变。
- Around line 253-259: 在处理变更的分支中,将 `else` 替换为显式判断 `change.action ===
'update'`,仅对更新操作调用 `updatePrice.mutateAsync` 并递增 `updated`;对未知 action 直接忽略,保留
`create` 分支行为不变。
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 411d209e-3e94-4460-9dfe-7bf1e7ba0e48

📥 Commits

Reviewing files that changed from the base of the PR and between 31890c8 and d65209f.

📒 Files selected for processing (14)
  • internal/handler/admin.go
  • internal/modelpricesync/litellm.go
  • internal/modelpricesync/source.go
  • internal/modelpricesync/source_test.go
  • internal/modelpricesync/sync.go
  • internal/service/admin.go
  • tests/e2e/model_prices_test.go
  • web/src/hooks/queries/index.ts
  • web/src/hooks/queries/use-model-prices.ts
  • web/src/lib/transport/http-transport.ts
  • web/src/lib/transport/interface.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/model-prices/index.tsx
💤 Files with no reviewable changes (5)
  • web/src/hooks/queries/index.ts
  • web/src/hooks/queries/use-model-prices.ts
  • web/src/lib/transport/http-transport.ts
  • internal/handler/admin.go
  • web/src/lib/transport/interface.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • tests/e2e/model_prices_test.go
  • internal/service/admin.go
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: Backend Checks
  • GitHub Check: playwright
  • GitHub Check: e2e
🔇 Additional comments (1)
web/src/pages/model-prices/index.tsx (1)

109-130: LGTM!

Also applies to: 159-172, 272-277, 293-316, 339-341

Comment thread internal/modelpriceupstream/source_test.go
Comment thread web/src/pages/model-prices/index.tsx Outdated
Comment thread web/src/pages/model-prices/index.tsx Outdated
@awsl233777 awsl233777 changed the title feat: preview and import model prices from upstream sources feat: import model prices from upstream diff Aug 10, 2026
@awsl233777 awsl233777 changed the title feat: import model prices from upstream diff feat: compare and import model prices from upstream Aug 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/modelpricesync/sync.go (1)

62-64: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

不要修改 sourcePrices 中的对象。

Line 62 修改了调用方传入的 priceCompareRows 只应生成比较结果,不应改变来源价格数据。请复制 price 后再设置数据库 ID,并将该副本写入 After

建议修复
-		price.ID = current.ID
+		after := cloneModelPrice(price)
+		after.ID = current.ID
 		result.Updated++
-		result.Changes = append(result.Changes, Change{Action: "update", Before: cloneModelPrice(current), After: cloneModelPrice(price)})
+		result.Changes = append(result.Changes, Change{Action: "update", Before: cloneModelPrice(current), After: after})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/modelpricesync/sync.go` around lines 62 - 64, 在 CompareRows
的更新分支中不要直接修改调用方传入的 price;先复制 price,向副本设置 current.ID,并将该副本用于 After 和后续数据库更新,同时保持
sourcePrices 中的原对象不变。
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/modelpricesync/sync.go`:
- Around line 62-64: 在 CompareRows 的更新分支中不要直接修改调用方传入的 price;先复制 price,向副本设置
current.ID,并将该副本用于 After 和后续数据库更新,同时保持 sourcePrices 中的原对象不变。

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6526e350-8492-43a8-96b8-ea077d221194

📥 Commits

Reviewing files that changed from the base of the PR and between d65209f and df08880.

📒 Files selected for processing (12)
  • internal/handler/admin.go
  • internal/modelpricesync/source.go
  • internal/modelpricesync/sync.go
  • internal/service/admin.go
  • tests/e2e/model_prices_test.go
  • web/src/hooks/queries/index.ts
  • web/src/hooks/queries/use-model-prices.ts
  • web/src/lib/transport/http-transport.ts
  • web/src/lib/transport/interface.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/model-prices/index.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • web/src/locales/en.json
  • web/src/hooks/queries/use-model-prices.ts
  • web/src/locales/zh.json
  • web/src/pages/model-prices/index.tsx
  • internal/modelpricesync/source.go
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: e2e
  • GitHub Check: playwright
🔇 Additional comments (6)
internal/service/admin.go (1)

2201-2204: LGTM!

internal/handler/admin.go (1)

2443-2445: LGTM!

Also applies to: 2529-2540

tests/e2e/model_prices_test.go (1)

215-341: LGTM!

web/src/lib/transport/interface.ts (1)

370-370: LGTM!

web/src/lib/transport/http-transport.ts (1)

1342-1348: LGTM!

web/src/hooks/queries/index.ts (1)

189-199: LGTM!

@awsl233777 awsl233777 changed the title feat: compare and import model prices from upstream feat: import formatted model prices from upstream Aug 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/handler/admin.go`:
- Around line 2535-2538: 更新 ListModelPricesFromExternalSource 及其调用处理逻辑,为未知
source 返回可识别的专用错误,并在对应 handler 中将该错误映射为 http.StatusBadRequest;其他上游请求失败仍保持返回
http.StatusInternalServerError。
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06c60c71-0a6e-45d9-898e-484cfc9cfe73

📥 Commits

Reviewing files that changed from the base of the PR and between df08880 and 9b1bd27.

📒 Files selected for processing (16)
  • internal/handler/admin.go
  • internal/modelpriceupstream/litellm.go
  • internal/modelpriceupstream/prices.go
  • internal/modelpriceupstream/source.go
  • internal/modelpriceupstream/source_test.go
  • internal/service/admin.go
  • tests/e2e/model_prices_test.go
  • web/src/hooks/queries/index.ts
  • web/src/hooks/queries/use-model-prices.ts
  • web/src/lib/transport/http-transport.ts
  • web/src/lib/transport/index.ts
  • web/src/lib/transport/interface.ts
  • web/src/lib/transport/types.ts
  • web/src/locales/en.json
  • web/src/locales/zh.json
  • web/src/pages/model-prices/index.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • web/src/locales/zh.json
  • web/src/locales/en.json
  • web/src/pages/model-prices/index.tsx
  • web/src/lib/transport/index.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (3)
  • GitHub Check: playwright
  • GitHub Check: Backend Checks
  • GitHub Check: e2e
🔇 Additional comments (12)
internal/modelpriceupstream/source.go (1)

1-1: LGTM!

Also applies to: 13-13, 32-53

internal/modelpriceupstream/litellm.go (1)

1-1: LGTM!

internal/modelpriceupstream/prices.go (1)

1-20: LGTM!

internal/modelpriceupstream/source_test.go (1)

1-1: LGTM!

Also applies to: 52-63

internal/service/admin.go (1)

22-22: LGTM!

Also applies to: 2201-2204

internal/handler/admin.go (1)

2443-2445: LGTM!

tests/e2e/model_prices_test.go (1)

215-339: LGTM!

web/src/lib/transport/types.ts (1)

588-589: LGTM!

Also applies to: 677-678, 1665-1669

web/src/lib/transport/interface.ts (1)

106-106: LGTM!

Also applies to: 370-370

web/src/lib/transport/http-transport.ts (1)

107-107: LGTM!

Also applies to: 1342-1347

web/src/hooks/queries/use-model-prices.ts (1)

94-97: LGTM!

web/src/hooks/queries/index.ts (1)

198-198: LGTM!

Comment thread internal/handler/admin.go Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant