Skip to content

chore: 移除 seed 价格表并强制云端同步#584

Merged
ding113 merged 3 commits intodevfrom
chore/force-cloud-price-table
Jan 10, 2026
Merged

chore: 移除 seed 价格表并强制云端同步#584
ding113 merged 3 commits intodevfrom
chore/force-cloud-price-table

Conversation

@ding113
Copy link
Owner

@ding113 ding113 commented Jan 10, 2026

  • 删除仓库内置 seed 价格表文件 public/seed/litellm-prices.json\n- 启动初始化不再依赖本地 seed,数据库为空时直接拉取云端价格表写入\n- 清理 release workflow 中的 seed 价格表下载/更新步骤,避免 CI 生成/提交本地价格表\n\n验证:bun run lint / typecheck / build / test:coverage

Greptile Overview

Greptile Summary

This PR successfully removes the local seed price table in favor of a cloud-first approach, significantly reducing repository size while maintaining price table functionality through network sync.

What Changed

Removed Local Seed Infrastructure:

  • Deleted 32,348-line public/seed/litellm-prices.json file
  • Removed CI workflow steps that downloaded and committed price table updates
  • Simplified seed-initializer.ts to call syncCloudPriceTableToDatabase() directly

New Initialization Flow:

  1. On first startup, ensurePriceTable() checks if database has any price records
  2. If empty, directly fetches from cloud endpoint (https://claude-code-hub.app/config/prices-base.toml)
  3. Parses TOML and writes to database using existing processPriceTableInternal()
  4. Failures are logged but don't block application startup

Architecture Benefits:

  • Removes 32K+ lines from version control
  • Eliminates CI-generated commits for price table updates
  • Always uses latest cloud price data (via 30-minute scheduler in instrumentation.ts)
  • Maintains existing error handling and graceful degradation

Trade-offs

Network Dependency: The application now requires network connectivity during first initialization to populate the price table. If the cloud endpoint is unreachable, the app starts with zero price data (though it continues to function). The existing scheduler will retry every 30 minutes.

No Offline Fallback: Previously, fresh deployments had a bundled seed file as a fallback. Now, if the cloud endpoint is down during first startup, users must wait for the endpoint to become available or manually upload a price table.

Risk Assessment

The changes are well-implemented with proper error handling. The cloud sync mechanism is already battle-tested (used by the scheduler), and the simplified initialization just reuses that code path. However, the removal of the local fallback increases dependency on external infrastructure during critical first-startup scenarios.

Confidence Score: 4/5

  • This PR is safe to merge with minor considerations around network dependency
  • The implementation is solid and well-tested, with proper error handling throughout. The main concern is the increased dependency on network connectivity during first startup - if the cloud endpoint is down, new deployments start with no price data. However, this is mitigated by: (1) graceful degradation that doesn't block startup, (2) automatic retry via 30-minute scheduler, (3) manual price table upload option. The only issue found is a stale .prettierignore entry (cosmetic).
  • The .prettierignore file contains a stale reference to the deleted seed file (line 51) that should be cleaned up

Important Files Changed

File Analysis

Filename Score Overview
src/lib/price-sync/seed-initializer.ts 4/5 Simplified to cloud-only initialization, removing local seed fallback. Network failures gracefully degrade but leave app with no price data.
.github/workflows/release.yml 5/5 Removed 29 lines of seed price table download/update logic from CI. Clean removal with no issues.
public/seed/litellm-prices.json 4/5 32K+ line seed file deleted. Creates network dependency for initialization but reduces repository size significantly.

Sequence Diagram

sequenceDiagram
    participant App as Application Startup
    participant Init as seed-initializer.ts
    participant DB as Database
    participant Cloud as cloud-price-updater.ts
    participant Endpoint as Cloud Price API
    participant Process as processPriceTableInternal

    App->>Init: ensurePriceTable()
    Init->>DB: hasAnyPriceRecords()
    
    alt Database has prices
        DB-->>Init: true
        Init-->>App: Skip initialization (prices exist)
    else Database is empty
        DB-->>Init: false
        Init->>Cloud: syncCloudPriceTableToDatabase()
        Cloud->>Endpoint: fetchCloudPriceTableToml()
        
        alt Network success
            Endpoint-->>Cloud: TOML data
            Cloud->>Cloud: parseCloudPriceTableToml()
            Cloud->>Process: processPriceTableInternal(jsonContent)
            Process->>DB: Insert/update price records
            DB-->>Process: Success
            Process-->>Cloud: {ok: true, data: {...}}
            Cloud-->>Init: Success with stats
            Init-->>App: Initialization complete
        else Network failure or parse error
            Endpoint-->>Cloud: Error (timeout/404/500)
            Cloud-->>Init: {ok: false, error: "..."}
            Init->>Init: Log warning
            Init-->>App: Continue startup (no prices)
        end
    end
    
    Note over App,Endpoint: Scheduler retries every 30 minutes via instrumentation.ts
Loading

@coderabbitai
Copy link

coderabbitai bot commented Jan 10, 2026

📝 Walkthrough

概述

此更改从发布工作流程中移除了种子价格表更新步骤,并将本地种子文件初始化流程替换为云优先同步方案。提交范围现已限制为 VERSION 和格式化代码,不再包含价格表相关文件。

变更详情

文件群组 / 文件 变更摘要
发布流程更新
.github/workflows/release.yml
移除了"Update seed price table"步骤(下载、验证和日志记录远程 JSON 价格表),简化了版本更新后的提交范围,现仅包含 VERSION 和格式化代码。
价格同步初始化迁移
src/lib/price-sync/seed-initializer.ts
将本地种子文件读取和验证流程替换为云同步方案;ensurePriceTable 入口点现调用 syncCloudPriceTableToDatabase 而非读取本地种子文件;删除遗留的文件 I/O 工具函数;更新日志前缀为 [PriceSync];公开 API 接口保持不变。

预计代码审查工作量

🎯 3 (中等) | ⏱️ ~20 分钟

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR标题清晰准确地反映了主要变更:删除本地seed价格表并强制使用云端同步,与提交的代码改动完全对应。
Description check ✅ Passed PR描述详细说明了变更内容、动机和风险影响,与代码改动高度相关,包含充分的背景和验证信息。
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/force-cloud-price-table

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

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @ding113, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

此拉取请求旨在将价格表的初始化策略从依赖本地种子文件转变为优先从云端同步。这一改变简化了部署流程,确保了价格数据的实时性和一致性,并减少了仓库的维护负担,因为它不再需要管理一个可能过时的本地价格表文件。

Highlights

  • 移除本地种子价格表文件: 删除了仓库中内置的 public/seed/litellm-prices.json 文件。
  • 强制云端同步: 应用启动时,如果数据库中没有价格数据,将直接从云端拉取价格表并写入数据库,不再依赖本地种子文件。
  • 清理 CI/CD 流程: 移除了 release workflow 中与种子价格表下载/更新相关的步骤,以避免 CI 生成或提交本地价格表。
Ignored Files
  • Ignored by pattern: .github/workflows/** (1)
    • .github/workflows/release.yml
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions bot added enhancement New feature or request area:deployment labels Jan 10, 2026
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

本次 PR 移除了本地 seed 价格表,改为在应用启动时强制从云端同步,简化了初始化流程,方向正确。代码改动清晰,日志格式也更统一。

主要建议是为 src/lib/price-sync/seed-initializer.ts 中被修改的核心逻辑 ensurePriceTable 函数补充单元测试,以确保其在各种场景下的行为符合预期。此外,我对错误日志记录提出了一点改进建议,以便在出现问题时能保留更完整的调试信息。

@github-actions github-actions bot added the size/XL Extra Large PR (> 1000 lines) label Jan 10, 2026
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@github-actions
Copy link
Contributor

🧪 测试结果

测试类型 状态
代码质量
单元测试
集成测试
API 测试

总体结果: ✅ 所有测试通过

@ding113 ding113 merged commit 11b2a01 into dev Jan 10, 2026
1 check was pending
@github-project-automation github-project-automation bot moved this from Backlog to Done in Claude Code Hub Roadmap Jan 10, 2026
Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, no comments

Edit Code Review Agent Settings | Greptile

logger.info("ℹ️ No price data found in database, initializing from seed...");
logger.info("[PriceSync] No price data found in database, syncing from cloud price table...");

const result = await syncCloudPriceTableToDatabase();
Copy link
Contributor

@github-actions github-actions bot Jan 10, 2026

Choose a reason for hiding this comment

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

[HIGH] [TEST-MISSING-CRITICAL] New startup initialization path lacks unit tests

Location: src/lib/price-sync/seed-initializer.ts:34

Evidence:

  • New behavior: const result = await syncCloudPriceTableToDatabase();
  • Project rule (CLAUDE.md): "Test Coverage - All new features must have unit test coverage of at least 80%"

Why this is a problem: ensurePriceTable() now depends on syncCloudPriceTableToDatabase() when the DB is empty. This is a startup-critical branch, and regressions here can leave the price table empty without being caught by CI.

Suggested fix:

// tests/unit/price-sync/seed-initializer.test.ts
import { describe, expect, it, vi } from "vitest";
import { ensurePriceTable } from "@/lib/price-sync/seed-initializer";
import { hasAnyPriceRecords } from "@/repository/model-price";
import { syncCloudPriceTableToDatabase } from "@/lib/price-sync/cloud-price-updater";

vi.mock("@/lib/logger", () => ({
  logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn(), trace: vi.fn() },
}));
vi.mock("@/repository/model-price", () => ({ hasAnyPriceRecords: vi.fn() }));
vi.mock("@/lib/price-sync/cloud-price-updater", () => ({ syncCloudPriceTableToDatabase: vi.fn() }));

describe("ensurePriceTable", () => {
  it("skips sync when prices already exist", async () => {
    vi.mocked(hasAnyPriceRecords).mockResolvedValue(true);
    await ensurePriceTable();
    expect(syncCloudPriceTableToDatabase).not.toHaveBeenCalled();
  });

  it("attempts cloud sync when DB is empty", async () => {
    vi.mocked(hasAnyPriceRecords).mockResolvedValue(false);
    vi.mocked(syncCloudPriceTableToDatabase).mockResolvedValue({ ok: false, error: "sync failed" });
    await ensurePriceTable();
    expect(syncCloudPriceTableToDatabase).toHaveBeenCalledTimes(1);
  });
});

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Code Review Summary

This PR removes the embedded seed price table and switches startup initialization to a cloud-sync path when the DB is empty. The functional change is small, but the diff is XL due to deleting a 32k-line JSON file, which increases review noise and makes future history harder to navigate. The main issue is missing unit tests for the new initialization branch.

PR Size: XL

  • Lines changed: 32481
  • Files changed: 3
  • Split suggestion: Separate the large data-file deletion (public/seed/litellm-prices.json) into its own PR (or at least its own commit) from the runtime/workflow changes to simplify review, cherry-picks, and potential reverts.

Issues Found

Category Critical High Medium Low
Logic/Bugs 0 0 0 0
Security 0 0 0 0
Error Handling 0 0 0 0
Types 0 0 0 0
Comments/Docs 0 0 0 0
Tests 0 1 0 0
Simplification 0 0 0 0

Critical Issues (Must Fix)

  • None

High Priority Issues (Should Fix)

  • [TEST-MISSING-CRITICAL] Missing unit tests for new startup init path (src/lib/price-sync/seed-initializer.ts:34, confidence 80)

Review Coverage

  • Logic and correctness
  • Security (OWASP Top 10)
  • Error handling
  • Type safety
  • Documentation accuracy
  • Test coverage
  • Code clarity

Automated review by Codex AI

Copy link
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

  • Identified PR #584 and applied the size label size/XL.
  • Posted 1 inline review comment (High): [TEST-MISSING-CRITICAL] missing unit tests for the new startup initialization path at src/lib/price-sync/seed-initializer.ts:34.
  • Submitted the required PR review summary (includes XL split suggestion).

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
.github/workflows/release.yml (1)

154-159: git add -A 与注释“仅 VERSION + 格式化代码”不一致,可能误提交非预期文件

当前做法会把除 .github/ 外的所有变更都纳入提交(例如 lockfile、生成文件或其它意外改动),与 Line 154 的注释语义不一致,且有把不该由 CI 自动落盘的内容推到 main 的风险。建议收敛 staging 范围。

建议改法(收敛提交范围)
-          # 添加所有更改(VERSION文件 + 格式化后的代码)
-          git add -A
-
-          # 排除 .github/ 目录的更改(workflow 文件不需要自动提交,且需要特殊权限)
-          git restore --staged .github/ 2>/dev/null || true
+          # 仅提交 VERSION + 已跟踪文件的格式化变更(避免误提交新文件/生成物/lockfile 漂移)
+          git add VERSION
+          git add -u
+          # 如仍需排除 workflow 变更,可保留:
+          git restore --staged .github/ 2>/dev/null || true
src/lib/price-sync/seed-initializer.ts (1)

22-46: 日志消息必须使用 i18n,不能硬编码字符串

Lines 28, 32, 36, 42, 50 中的日志消息都使用了硬编码的英文字符串,违反了编码指南。所有用户面向的字符串必须使用 i18n(支持 5 种语言:zh-CN、en、ja、ko、de),使用 next-intl 进行国际化处理。

logger.info("[PriceSync] Price table already exists, skipping initialization");
logger.info("[PriceSync] No price data found in database, syncing from cloud price table...");
logger.warn("[PriceSync] Failed to sync cloud price table for initialization", ...);
logger.info("[PriceSync] Cloud price table synced for initialization", ...);
logger.error("[PriceSync] Failed to ensure price table", ...);

并发场景需要额外的保护

processPriceTableInternal 中的 createModelPrice() 调用没有使用数据库事务进行包装。在多个实例同时启动时,若数据库为空,它们可能同时尝试插入相同的模型记录。由于 model_prices 表在 modelName 字段上没有 UNIQUE 约束,这会导致重复记录的产生。建议:

  1. createModelPrice() 添加事务包装,或
  2. model_prices 表的 modelName 字段添加 UNIQUE 约束以确保原子性
🧹 Nitpick comments (1)
src/lib/price-sync/seed-initializer.ts (1)

47-52: 错误处理得当,建议优化日志参数命名。

catch 块正确地记录错误但不阻塞启动,符合降级策略。

可选优化: Line 50 的 error: error 参数命名有些冗余。可考虑改为更清晰的命名:

♻️ 建议的重构
  } catch (error) {
    // 不阻塞应用启动,用户仍可通过手动同步/更新来添加价格表
    logger.error("[PriceSync] Failed to ensure price table", {
-     error: error,
+     error,
    });
  }

或者使用更具描述性的键名:

  } catch (error) {
    // 不阻塞应用启动,用户仍可通过手动同步/更新来添加价格表
    logger.error("[PriceSync] Failed to ensure price table", {
-     error: error,
+     cause: error,
    });
  }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Cache: Disabled due to Reviews > Disable Cache setting

📥 Commits

Reviewing files that changed from the base of the PR and between d0cb42b and 48b9df6.

📒 Files selected for processing (3)
  • .github/workflows/release.yml
  • public/seed/litellm-prices.json
  • src/lib/price-sync/seed-initializer.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

No emoji characters in any code, comments, or string literals

Files:

  • src/lib/price-sync/seed-initializer.ts
**/*.{ts,tsx,jsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,jsx,js}: All user-facing strings must use i18n (5 languages supported: zh-CN, en, ja, ko, de). Never hardcode display text
Use path alias @/ to map to ./src/
Use Biome for code formatting with configuration: double quotes, trailing commas, 2-space indent, 100 character line width
Prefer named exports over default exports
Use next-intl for internationalization
Use Next.js 16 App Router with Hono for API routes

Files:

  • src/lib/price-sync/seed-initializer.ts
🧠 Learnings (2)
📚 Learning: 2026-01-05T03:01:39.354Z
Learnt from: NieiR
Repo: ding113/claude-code-hub PR: 539
File: src/types/user.ts:158-170
Timestamp: 2026-01-05T03:01:39.354Z
Learning: In TypeScript interfaces, explicitly document and enforce distinct meanings for null and undefined. Example: for numeric limits like limitTotalUsd, use 'number | null | undefined' when null signifies explicitly unlimited (e.g., matches DB schema or special UI logic) and undefined signifies 'inherit default'. This pattern should be consistently reflected in type definitions across related fields to preserve semantic clarity between database constraints and UI behavior.

Applied to files:

  • src/lib/price-sync/seed-initializer.ts
📚 Learning: 2026-01-07T17:05:37.171Z
Learnt from: CR
Repo: ding113/claude-code-hub PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-07T17:05:37.171Z
Learning: Before committing, run: bun run build, bun run lint, bun run lint:fix, bun run typecheck, and bun run test

Applied to files:

  • .github/workflows/release.yml
🧬 Code graph analysis (1)
src/lib/price-sync/seed-initializer.ts (2)
src/lib/logger.ts (1)
  • logger (168-187)
src/lib/price-sync/cloud-price-updater.ts (1)
  • syncCloudPriceTableToDatabase (17-47)
🔇 Additional comments (3)
src/lib/price-sync/seed-initializer.ts (3)

1-8: 文档更新准确,清晰描述了云端优先的初始化策略。

头部注释准确反映了重构后的实现逻辑,包括云端拉取和降级策略。


10-12: 导入模块正确,成功移除了种子文件依赖。

引入 syncCloudPriceTableToDatabase 替代本地种子文件处理,符合 PR 目标。


14-21: 函数文档清晰描述了云端同步策略。

文档准确反映了新的实现流程和非阻塞的错误处理机制。

@ding113 ding113 deleted the chore/force-cloud-price-table branch January 27, 2026 09:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:deployment enhancement New feature or request size/XL Extra Large PR (> 1000 lines)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant