Skip to content

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

Merged
dingyi merged 1 commit into
mainfrom
feature/upstream-ratio-sync-ui-port
May 1, 2026
Merged

feat(ratio-sync): port upstream pricing-endpoint sync UI to HeroUI#20
dingyi merged 1 commit into
mainfrom
feature/upstream-ratio-sync-ui-port

Conversation

@dingyi

@dingyi dingyi commented May 1, 2026

Copy link
Copy Markdown
Owner

Summary

Re-target of #19 onto main. The original PR landed on
feature/sync-upstream-v1.0, but #17 was merged before #19 made it
into v1.0, so when v1.0 was merged into main it carried only #17's
content — leaving the single commit
a3a49d41d feat(ratio-sync): port upstream pricing-endpoint sync UI to HeroUI
stranded on the now-defunct sync branch.

This PR brings exactly that one commit (already reviewed in #19)
onto main. No additional changes — pure rebase-by-targeting.

Diff

Single commit, +278/-53 across:

  • web/classic/src/pages/Setting/Ratio/UpstreamRatioSync.jsx
    (the actual UI port)
  • web/classic/src/i18n/locales/*.json (4 new keys: 创建缓存倍率,
    图像倍率, 音频倍率, 阶梯计费表达式)
  • TODO.md (mark f424f906d follow-up as done)

Why it landed this way

When I opened #19 the v1.0 sync PR (#17) was still open, so #19
based on feature/sync-upstream-v1.0 made sense — it kept #19's
diff scoped to just the UI changes (instead of also re-displaying
the entire +171k merge). The plan was to retarget after #17 merged.
GitHub's PR retargeting + auto-merge ordering didn't catch the
sequencing, so #19 quietly merged into v1.0 milliseconds before v1.0
itself merged into main, missing the train.

Net effect on review surface area: zero — this is the same content
already reviewed in #19, presented against main so it can actually
land.

Verification

Related


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

@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 505
CreateCacheRatio: { ...currentRatios.CreateCacheRatio },
ImageRatio: { ...currentRatios.ImageRatio },
AudioRatio: { ...currentRatios.AudioRatio },
AudioCompletionRatio: { ...currentRatios.AudioCompletionRatio },
ModelPrice: { ...currentRatios.ModelPrice },

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 extended ratio maps when building sync payload

performSync now always writes CreateCacheRatio/ImageRatio/AudioRatio/AudioCompletionRatio (and tiered maps) from currentRatios, but the conflict-confirmation path still calls it with only the legacy 4 fields. In that flow, these spreads resolve to empty objects and the subsequent PUT /api/option/ calls overwrite existing option JSON with {}, so confirming a price-vs-ratio conflict can silently erase previously configured ratio/tiered data.

Useful? React with 👍 / 👎.

if (rtCategory !== category) delete newModelRes[rt];
}
});
newModelRes[preferredType] = preferredValue;

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 checkbox state keyed to the rendered ratio type

This stores selections under preferredType (e.g. auto-upgrading a model_ratio click to billing_expr), but table rendering and bulk stats still read by record.ratioType. As a result, the clicked row can remain visually unchecked and bulk unselect can miss the stored entry, so users can unintentionally apply hidden tiered selections they cannot clear from the same row.

Useful? React with 👍 / 👎.

@dingyi
dingyi merged commit ab5aa2a into main May 1, 2026
2 checks passed
@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