Skip to content

feat(ratio-sync): port upstream pricing-endpoint sync UI to HeroUI#19

Merged
dingyi merged 1 commit into
feature/sync-upstream-v1.0from
feature/upstream-ratio-sync-ui-port
May 1, 2026
Merged

feat(ratio-sync): port upstream pricing-endpoint sync UI to HeroUI#19
dingyi merged 1 commit into
feature/sync-upstream-v1.0from
feature/upstream-ratio-sync-ui-port

Conversation

@dingyi

@dingyi dingyi commented May 1, 2026

Copy link
Copy Markdown
Owner

Summary

Re-implements the frontend half of upstream commits
f424f906d (sync upstream pricing from pricing endpoint) and
cc4ad6c39 (load model mapping during upstream model checks)
on top of this fork's HeroUI-native UpstreamRatioSync.

The backend half was already absorbed by the v1.0 sync merge in #17.
Only the web/classic/src/pages/Setting/Ratio/UpstreamRatioSync.jsx UI
was left on the old per-row-per-ratio-type table that didn't surface
the new sync fields.

Why a separate PR

PR #17 was already +171k/-2.4k from the upstream merge — adding 250+
more lines of feature work would push it past reviewable. Splitting
out the UI port gives a focused diff (+278/-53) where each hunk maps
1:1 to a documented behaviour change.

Base

Built on top of `feature/sync-upstream-v1.0` (PR #17). Will
re-target `main` once #17 merges; rebase is trivial because no
files outside the ones I touched in #17 are affected.

What ships

  • 5 new ratio columns: `create_cache_ratio`, `image_ratio`,
    `audio_ratio`, `audio_completion_ratio`, plus the existing
    `cache_ratio`. Module-level `RATIO_FIELDS` mirrors backend
    `controller/ratio_sync.go pricingSyncFields`.
  • Tiered billing as a third category (`billing_mode` +
    `billing_expr`). Coexists with ratio/price billing on the same
    model — backend chooses at billing time. Mutually-exclusive
    rules:
    • `tiered` ↔ `tiered`: paired (auto-populate partner)
    • `ratio` ↔ `price`: still mutually exclusive
    • `tiered` ↔ `{ratio, price}`: NOT exclusive (overlay)
  • Expression-priority resolution. New `getPreferredSyncField`
    upgrades a non-expr selection to `billing_expr` whenever the
    same source exposes a non-empty expression — one click captures
    the full tiered config.
  • Auto-pair tiered fields. Selecting `billing_expr` auto-fills
    `billing_mode` (= `tiered_expr` if source didn't ship a mode);
    selecting `billing_mode` auto-fills `billing_expr` if present.
  • Apply-time conflict check tightened. Price-vs-ratio swap
    warning runs against `RATIO_FIELDS` (not 3 hard-coded fields)
    and skips tiered-only selections.
  • Numeric normalisation hardened. `parseFloat` → `Number(...)`
    so explicit `0` (legal ratio for free models) survives round-trip;
    tiered string fields never get NaN-ified. `applySync` best-effort-parses
    each option to `{}` instead of throwing on a malformed JSON.
  • Caller plumbing: `selectValue(model, ratioType, value, sourceName)`
    — `handleBulkSelect` / `handleRowSelect` / in-cell CompactCheckbox
    thread the upstream name through so expression-priority resolution
    can pick the right source.
  • Option-key mapping helper: `optionKeyFor` handles the dotted
    Option-store keys upstream introduced (`billing_setting.billing_mode`,
    `billing_setting.billing_expr`).

What deliberately doesn't ship

  • The upstream `web/default` version split the monolith into
    4 files (main / table / columns / helpers) AND moved to a
    per-model-row layout where each row holds a vector of ratio types.
    That's a divergent UI choice this fork would re-conflict on every
    future sync. Kept the existing per-(model, ratioType) row layout —
    same data, denser table.

i18n

4 new keys across all 8 locale files:

  • `创建缓存倍率` / Create cache ratio
  • `图像倍率` / Image ratio
  • `音频倍率` / Audio ratio
  • `阶梯计费表达式` / Tiered billing expression

`音频补全倍率` and `计费模式` were already present from the v1.0
sync merge.

Verification

  • `bun run build` clean (`built in 16.52s`)
  • All 8 locale JSONs parse (`python3 -c json.load`)
  • Manual smoke test in dev — pending reviewer:
    • `/console/setting/ratio` → "上游价格同步" tab → click 选择同步渠道
    • Pick channel using `/api/pricing` endpoint with `billing_expr` for some model → confirm clicking any non-expr cell upgrades to expr
    • Pick channel with non-overlapping ratio types → confirm new columns (image/audio/etc.) appear
    • Pick model that locally has `ModelPrice` set + try to apply `model_ratio` from upstream → confirm conflict modal still triggers
    • Apply with mixed tiered + ratio resolutions → confirm both `billing_setting.billing_mode` and `ModelRatio` get persisted

Backend dependencies

All shipped via PR #17:

  • `controller/ratio_sync.go` (`FetchUpstreamRatios`, `GetSyncableChannels`)
  • `setting/billing_setting/tiered_billing.go`
    (`GetPricingSyncData` exposes `billing_mode` + `billing_expr`)
  • `setting/ratio_setting/model_ratio.go` (extended sync fields)

This PR was generated automatically by Cursor

Made with Cursor

Re-implements the frontend half of upstream commits f424f90
("feat: sync upstream pricing from pricing endpoint") and cc4ad6c
("fix(channel): load model mapping during upstream model checks")
on top of this fork's HeroUI-native UpstreamRatioSync. The backend
half was already absorbed by the v1.0 sync merge; only the
`web/classic/src/pages/Setting/Ratio/UpstreamRatioSync.jsx` UI was
left on the old per-row-per-ratio-type table that didn't surface the
new fields.

What ships:

- **Five new ratio columns**: `create_cache_ratio`, `image_ratio`,
  `audio_ratio`, `audio_completion_ratio` join the existing
  `cache_ratio`. `RATIO_FIELDS` / `SYNC_FIELD_ORDER` are now a
  module-level source of truth that mirrors backend
  `controller/ratio_sync.go pricingSyncFields`.

- **Tiered billing as a third category**. `getBillingCategory` now
  returns `'tiered'` for `billing_mode` / `billing_expr`. Tiered
  selections coexist with ratio/price billing on the same model
  (backend chooses based on `billing_mode` at billing time) so
  picking tiered does NOT clear an existing price/ratio resolution
  and vice-versa.

- **Expression-priority resolution**. New `getPreferredSyncField`
  upgrades a non-expr selection to `billing_expr` whenever the same
  source exposes a non-empty expression for the model — clicking
  any cell from that source captures the full tiered config in one
  go. Mirrors upstream's `web/default` reference implementation.

- **Auto-pair tiered fields**. Selecting a tiered field also
  populates the partner field from the same source: picking
  `billing_expr` auto-fills `billing_mode` (= `tiered_expr` if
  source didn't ship a mode), and picking `billing_mode` auto-fills
  `billing_expr` if available.

- **Apply-time conflict check tightened**. The price-vs-ratio swap
  warning now runs against `RATIO_FIELDS` (not just three of them)
  and skips tiered-only selections — tiered is a billing overlay,
  not a category swap, so it shouldn't trigger the modal.

- **Numeric normalisation hardened**. `parseFloat` swapped for
  `Number(...)` in `performSync` so explicit `0` (a legal ratio for
  free models) survives the round-trip and tiered string fields
  never get NaN-ified. `applySync` now best-effort-parses every
  option to `{}` instead of throwing on a single malformed JSON.

- **Caller plumbing**: `selectValue(model, ratioType, value, sourceName)`
  — `handleBulkSelect`, `handleRowSelect`, and the in-cell
  CompactCheckbox onChange now thread the upstream name through so
  expression-priority resolution can ask the right source.

Backend `optionKeyFor` mapping handles the dotted Option-store keys
upstream introduced (`billing_setting.billing_mode`,
`billing_setting.billing_expr`) without leaking the convention into
the rest of the file.

i18n: adds 4 new keys (`创建缓存倍率`, `图像倍率`, `音频倍率`,
`阶梯计费表达式`) across all 8 locale files. `音频补全倍率` and
`计费模式` were already present from the v1.0 sync merge.

Build clean. UI fits the existing per-(model, ratioType) row table
shape so no architectural restructure was needed; the upstream
default version's column-collapsed layout is intentionally not
mirrored — it's a divergent design choice that would re-conflict
on every future sync.

Made-with: Cursor
@dingyi
dingyi merged commit 34c0f21 into feature/sync-upstream-v1.0 May 1, 2026
1 check passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a3a49d41de

ℹ️ 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".

Comment on lines +501 to +504
CreateCacheRatio: { ...currentRatios.CreateCacheRatio },
ImageRatio: { ...currentRatios.ImageRatio },
AudioRatio: { ...currentRatios.AudioRatio },
AudioCompletionRatio: { ...currentRatios.AudioCompletionRatio },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve full ratio maps when confirming conflict sync

performSync now always writes CreateCacheRatio, ImageRatio, AudioRatio, AudioCompletionRatio, and both billing_setting.* option blobs, but the conflict-confirm path still calls it with only the legacy four maps (ModelRatio, CompletionRatio, CacheRatio, ModelPrice). When a conflict modal is confirmed, these newly added maps are initialized from missing input and get saved as empty objects, which can wipe existing per-model values for those options during an otherwise unrelated sync.

Useful? React with 👍 / 👎.

Comment on lines +456 to +460
if (category === 'tiered') {
if (rtCategory !== 'tiered') delete newModelRes[rt];
} else {
if (rtCategory !== category) delete newModelRes[rt];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep tiered selections when adding price/ratio selections

This exclusivity branch deletes any previously selected fields from other billing categories, so selecting billing_expr and then selecting model_price/ratio (or vice versa) for the same model drops the earlier selection. That prevents applying tiered + ratio/price together in one sync for a model, even though later logic in this file treats tiered as an overlay and intentionally avoids price-vs-ratio conflict handling for tiered selections.

Useful? React with 👍 / 👎.

@dingyi
dingyi deleted the feature/upstream-ratio-sync-ui-port branch May 1, 2026 06:58
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