feat(usage): user-configurable per-model cost overlay (providers.<name>.modelCosts) - #1010
Conversation
|
Important Review skippedNo new commits to review since the last review. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds ChangesProvider cost overlay
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant ConfigLoader
participant OverlayReconciler
participant UserCostOverlays
participant CostEstimator
participant UsageSummaryCache
participant Logs
ConfigLoader->>OverlayReconciler: detect config.json changes
OverlayReconciler->>UserCostOverlays: refresh modelCosts
UserCostOverlays-->>CostEstimator: active overlays and version
CostEstimator->>CostEstimator: resolve user price before built-in prices
CostEstimator-->>UsageSummaryCache: store estimate with overlay version
UsageSummaryCache->>UsageSummaryCache: reject stale overlay versions
CostEstimator-->>Logs: estimate with source user
Logs-->>Logs: localize provider_cost_overlay
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 `@docs-site/src/content/docs/reference/configuration/providers.md`:
- Line 69: Update the `modelCosts` documentation on line 69 to explicitly
clarify that custom or internal provider model IDs—even those absent from
built-in catalogs—are valid overlay keys for cost configuration. Add a note
stating that users can define custom prices for any model identifier regardless
of catalog presence, making clear that the feature supports providers beyond the
standard built-in offerings.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: d0d55d83-385c-4beb-b349-119980acd24b
📒 Files selected for processing (16)
docs-site/src/content/docs/reference/configuration/providers.mdgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/zh.tsgui/src/pages/Logs.tsxsrc/config.tssrc/server/auth-cors.tssrc/server/management/shared.tssrc/types.tssrc/usage/cost.tssrc/usage/user-cost-overlays.tstests/provider-cost-overlay-config.test.tstests/usage-cost.test.ts
afc131d to
180b8b4
Compare
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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 `@docs-site/src/content/docs/reference/configuration/providers.md`:
- Line 75: Update the modelCosts documentation to state the complete
display-price fallback order: user configuration, jawcode catalog,
expected-price overlay, then model-level vendor fallback. Clarify that an
all-zero user entry falls through to the next source in that sequence, while
preserving the existing display-only and non-billing behavior.
- Line 75: Add the modelCosts configuration row after modelMaxOutputTokens in
the Japanese, Korean, Russian, and Simplified Chinese provider pages,
documenting exact model-ID keys, all-zero entries falling back to built-in
catalogs, and display-only estimates that never affect billing.
In `@gui/src/i18n/de.ts`:
- Line 574: Update the German translation value for
logs.detail.estimate.provider_cost_overlay to “Ein vom Anbieter konfiguriertes
Preis-Overlay wurde verwendet.”, preserving alignment with the English catalog
entry.
In `@gui/src/pages/Logs.tsx`:
- Around line 45-49: Extend the GUI price-source contract used by
MatchedPriceInfo to include "user", then add the logs.detail.source.user
translation key to every locale alongside the existing source labels so
provider-configured overlay-priced rows display a localized source.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a4b0f2de-a70c-4109-a5d9-f87dcbe73c8d
📒 Files selected for processing (8)
docs-site/src/content/docs/reference/configuration/providers.mdgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/zh.tsgui/src/pages/Logs.tsx
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 180b8b4326
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Thanks for the review — addressed in 7a4ce62:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@docs-site/src/content/docs/ja/reference/configuration/providers.md`:
- Line 68: Update the Cost4 documentation row in
docs-site/src/content/docs/ja/reference/configuration/providers.md:68-68 to list
the supported input, output, cacheRead, and cacheWrite rate fields. Apply the
equivalent field list or example to
docs-site/src/content/docs/ko/reference/configuration/providers.md:68-68,
docs-site/src/content/docs/ru/reference/configuration/providers.md:78-78, and
docs-site/src/content/docs/zh-cn/reference/configuration/providers.md:67-67,
preserving each locale’s language and existing pricing guidance.
In `@gui/src/i18n/ru.ts`:
- Line 566: Update the Russian translation for
logs.detail.estimate.provider_cost_overlay at line 592 to reuse the same
terminology from logs.detail.source.user at line 566. Replace the current
"настроенная пользователем цена провайдера" phrasing with a sentence form that
incorporates "Ценовой оверлей провайдера" (keeping the "оверлей" term
consistent). This aligns the Russian translations with every other locale in the
PR, which reuses an identical term across both the price-source label and the
estimate-reason explanation.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a3d12d7c-bcdf-4344-b9a0-61f4e65190d7
📒 Files selected for processing (18)
docs-site/src/content/docs/ja/reference/configuration/providers.mddocs-site/src/content/docs/ko/reference/configuration/providers.mddocs-site/src/content/docs/reference/configuration/providers.mddocs-site/src/content/docs/ru/reference/configuration/providers.mddocs-site/src/content/docs/zh-cn/reference/configuration/providers.mdgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/zh.tsgui/src/pages/Logs.tsxsrc/config.tssrc/server/auth-cors.tssrc/server/management/shared.tssrc/usage/cost.tssrc/usage/user-cost-overlays.tstests/provider-cost-overlay-config.test.ts
|
✅ Deterministic PR hygiene checks passed. |
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@src/server/auth-cors.ts`:
- Around line 525-539: Update sanitizeModelCosts to create out with a null
prototype so assigning the "__proto__" model ID creates an own property and
Object.keys includes it. Add a regression test using JSON.parse() to construct
the "__proto__" key and verify safeConfigDTO exposes its valid cost row.
In `@tests/usage-cost.test.ts`:
- Around line 769-812: Move the empty-provider cleanup for user cost overlays
out of the test body and into an afterEach hook scoped to the surrounding
describe block. Ensure the hook calls refreshUserCostOverlays with an empty
providers object so the blsc overlay is reset even when assertions in “registry
refresh replaces rows, bumps the version, and invalidates the memo” fail, and
update the bun:test imports to include afterEach.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: d8697a9c-f003-4c14-ae6a-70292ae50aef
📒 Files selected for processing (20)
docs-site/src/content/docs/ja/reference/configuration/providers.mddocs-site/src/content/docs/ko/reference/configuration/providers.mddocs-site/src/content/docs/reference/configuration/providers.mddocs-site/src/content/docs/ru/reference/configuration/providers.mddocs-site/src/content/docs/zh-cn/reference/configuration/providers.mdgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/zh.tsgui/src/pages/Logs.tsxsrc/config.tssrc/server/auth-cors.tssrc/server/management/shared.tssrc/types.tssrc/usage/cost.tssrc/usage/user-cost-overlays.tstests/provider-cost-overlay-config.test.tstests/usage-cost.test.ts
|
@coderabbitai resume — the dev merge (f620f7f) resolved the config.ts conflict (persistConfigUnlocked now returns boolean for generation bumping while still refreshing the cost-overlay registry). Please run the full review on the merged state. |
|
✅ Action performedReviews resumed. Full review finished. |
|
Heads-up for review: the hygiene gate flags |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/server/auth-cors.ts (1)
525-539: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winPreserve the valid
__proto__model ID.Line 527 creates a normal object.
providerModelCostsConfigErroraccepts"__proto__"because it is a nonblank model ID. For a row parsed from JSON, Line 536 changesout's prototype instead of adding an own property.Object.keys(out)then omits the row, sosafeConfigDTOdoes not expose a valid configured price.Create
outwith a null prototype. Add a regression test that builds the key throughJSON.parse().Proposed fix
- const out: Record<string, ProviderCostOverlay> = {}; + const out: Record<string, ProviderCostOverlay> = Object.create(null);🤖 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 `@src/server/auth-cors.ts` around lines 525 - 539, Update sanitizeModelCosts to create out with a null prototype so assigning the valid "__proto__" model ID produces an own property and remains included in the returned configuration. Add a regression test that obtains this key via JSON.parse() and verifies the sanitized costs preserve the valid entry.
🤖 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 `@tests/provider-cost-overlay-config.test.ts`:
- Around line 26-30: Reset the user overlay registry in both affected test
sites: in tests/provider-cost-overlay-config.test.ts lines 26-30, call
refreshUserCostOverlays({ providers: {} } as OcxConfig) in the existing
afterEach before deleting OPENCODEX_HOME; in tests/usage-cost.test.ts lines
769-812, add a scoped afterEach that performs the same reset and remove the
test-body cleanup near line 807.
---
Duplicate comments:
In `@src/server/auth-cors.ts`:
- Around line 525-539: Update sanitizeModelCosts to create out with a null
prototype so assigning the valid "__proto__" model ID produces an own property
and remains included in the returned configuration. Add a regression test that
obtains this key via JSON.parse() and verifies the sanitized costs preserve the
valid entry.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a262235e-fcc4-491e-a174-d1c80bdad0fb
📒 Files selected for processing (20)
docs-site/src/content/docs/ja/reference/configuration/providers.mddocs-site/src/content/docs/ko/reference/configuration/providers.mddocs-site/src/content/docs/reference/configuration/providers.mddocs-site/src/content/docs/ru/reference/configuration/providers.mddocs-site/src/content/docs/zh-cn/reference/configuration/providers.mdgui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/zh.tsgui/src/pages/Logs.tsxsrc/config.tssrc/server/auth-cors.tssrc/server/management/shared.tssrc/types.tssrc/usage/cost.tssrc/usage/user-cost-overlays.tstests/provider-cost-overlay-config.test.tstests/usage-cost.test.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c5db9f883f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Line 231 in 50cc147
When a valid custom provider name happens to end with the Codex account-label suffix pattern, such as blsc-pabcdef, resolveMatchedPrice() has already collapsed it to blsc before this new user-overlay lookup runs, while refreshUserCostOverlays() stores the configured row under the exact provider name. In that scenario a valid providers.blsc-pabcdef.modelCosts.custom-model entry is never matched, so Logs/usage estimates stay unpriced despite the explicit override; try the exact provider against userOverlays before applying baseProviderLabel, or register both exact and collapsed keys.
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
The module-level poller started by startServer was only stopped in the process-wide shutdown path; server.stop() (used by embedded callers and tests) left the interval running with the stopped server's live config. Stop it in the server.stop shutdown steps so no timer keeps updating process-global overlay state after the listener is gone.
CodeRabbit follow-ups after rebasing onto the refcounted background lifecycle: - start the cost-overlay reconciler inside the guarded startup transaction so a listener bind failure releases its lease alongside backgroundLifecycle - make the reconciler owner-scoped: each startServer registers its own live config and the shared timer stops only when the LAST owner releases, so stopping one of several servers cannot kill reconciliation for the others - keep the process-wide stop in runListenerShutdown for full shutdown
…r stop Wibias re-review (REQUEST CHANGES) found two reachable data-integrity failures in the preservation lifecycle: - With multiple servers, a newer owner can own a provider that an older owner still treats as disk-only; when the newer owner stops, the older owner's next unrelated save could erase the provider from disk. stop() now re-reads config and recomputes disk-only preservation against the remaining owners. - When the final owner stops (or the process-wide stop runs), preserved disk-only providers stayed in the module cache, so a later save could resurrect an externally deleted provider. Final-owner/process-wide stop now clears the cache. Also, reconcileUserCostOverlaysFromDisk no longer preserves every disk provider when no live routing config is available (CodeRabbit outside-diff finding): an overlay-only refresh must not turn later intentional provider deletions into resurrected rows. Regression coverage: A-lacks-beta/B-has-beta/B-stops/A-saves survives; final-owner stop -> delete beta -> save does not resurrect; process-wide stop -> delete beta -> save does not resurrect; overlay-only refresh with no live config does not preserve disk providers.
- drainAndShutdown now releases only this server's reconciler lease via the startServer stop override; the process-wide stop stays test/teardown-only and is documented as such. - startUserCostOverlayReconciler restarts the shared timer when a later owner requests a smaller interval and relaxes the cadence when it stops. - key-login live-update test asserts the running proxy's live DTO retains the overlay, not just disk. - reconcile tests wait on observed conditions instead of fixed sleeps and drop an ineffective in-memory mutation. - usage-cost fall-through test no longer pins the vendor catalog rate.
… docs - validLegacyOverlayCosts requires exactly the four Cost4 own fields, so a malformed legacy row with an extra field (e.g. apiKey) collides instead of being carried into canonical openai config. - reconcileUserCostOverlaysFromDisk without a live config now mirrors the owners path: registered owners keep protecting disk-only rows, and with no live owners the stale preservation registry is cleared so a deleted provider cannot be resurrected by the next saveConfig. - config-user-edits afterEach resets the module-level overlay registry. - cadence regression test drops the timing-fragile negative assertion. - providers docs (en/ko/ru/zh-cn) state custom providers may target any OpenAI-compatible endpoint through the openai-chat adapter.
- Capture the overlay version before reading/computing the summary and stamp that captured version; if the version changed mid-read, serve the summary uncached so a mixed-price entry is never accepted as current. - Add a deterministic regression covering an overlay change during the read/summary lifecycle. - Add the two new locale keys to the Turkish catalog after rebasing onto dev.
- configStamp() now includes ctimeMs and ino so a same-size edit inside one coarse mtime tick (or an atomic-rename replacement) still triggers a reconcile. - usage-cost fall-through test no longer pins the expected-overlay rate. - reconcile tests assert the child writer exit status and bound the first wait to 200ms so only the 20ms fast owner can satisfy it.
…r overlay wording - Every provider validation issue path now passes the provider key through redactSecretString(name), matching the modelCosts path, so token-shaped provider keys cannot leak through ocx config validate/import diagnostics. - Turkish overlay strings now say the price tier is user-configured (Kullanıcı tarafından yapılandırılan ...), consistent with ko/ru/zh.
rememberDiskOnlyProviders now preserves a disk provider whenever at least one live owner lacks it, instead of dropping it as soon as any owner owns it. One server's older live projection can no longer erase a provider owned by another active server: an unrelated saveConfig from the older owner re-adds the row through the shared preservation registry. Regression added: A lacks beta -> B has beta -> both remain alive -> A saves -> beta survives on disk.
Track provider ownership across live config projections so stale-owner preservation still protects externally added rows, while an owner that previously owned a provider can intentionally delete it. Successful deletion is propagated to all active preservation owners so a later stale save cannot resurrect the provider. Regression covers A lacking beta, B/C owning beta, an unrelated A save preserving beta, a reconcile tick during B's delete gap, and B's explicit deletion surviving a later C save.
|
✅ Action performedReview finished.
|
|
|
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
gui/src/i18n/ko.ts (1)
1936-1983: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTranslate the new
lab.*Compatibility Lab strings.Lines 1936–1983 add the Compatibility Lab keys, but every value is untranslated English (e.g.
"lab.title": "Compatibility Lab","lab.verdict.UNKNOWN": "Unknown").models.tab.compatibilityandmodels.subtitle.compatibility, added by the same PR just above (lines 448-452), are correctly localized to Korean. Translate thelab.*block for consistency.🤖 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 `@gui/src/i18n/ko.ts` around lines 1936 - 1983, Translate every newly added lab.* value in the Korean locale, including titles, labels, statuses, verdicts, layers, and messages, replacing the English text with natural Korean while preserving all keys and interpolation or punctuation semantics.gui/src/i18n/ja.ts (1)
1936-1983: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTranslate the new
lab.*Compatibility Lab strings.Lines 1936–1983 add roughly 30 new keys, but every value is the untranslated English source text (for example,
"lab.title": "Compatibility Lab","lab.loadMore": "Load more","lab.detailTitle": "Verdict detail"). Compare this tomodels.tab.compatibilityandmodels.subtitle.compatibility, added in the same PR a few lines above, which are correctly localized to Japanese ("互換性","ラボ投影証拠の読み取り専用互換性判定マトリクス。").A Japanese-reading user opening the Compatibility Lab page sees a mix of Japanese chrome and English content strings. Translate these keys to match the rest of the catalog.
🤖 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 `@gui/src/i18n/ja.ts` around lines 1936 - 1983, Translate every newly added lab.* value in the Japanese catalog from English into natural Japanese, including titles, subtitles, statuses, filters, column labels, empty/loading/error messages, detail actions, verdict labels, and evidence-layer labels. Preserve all existing keys and interpolation or formatting while matching the terminology used by models.tab.compatibility and models.subtitle.compatibility.gui/src/i18n/ru.ts (1)
1938-1985: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTranslate the new
lab.*Compatibility Lab strings.Lines 1938–1985 add the Compatibility Lab keys with untranslated English values (e.g.
"lab.subtitle": "Read-only compatibility verdict matrix from lab projection evidence."), whilemodels.tab.compatibility/models.subtitle.compatibilityadded by the same PR a few lines above are correctly localized to Russian. Translate thelab.*block for consistency.🤖 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 `@gui/src/i18n/ru.ts` around lines 1938 - 1985, Translate every newly added lab.* value in the Russian locale from English into natural Russian, including titles, status labels, filters, column names, verdicts, layers, and messages. Preserve all lab.* keys and placeholders while matching the existing Russian terminology used by models.tab.compatibility and models.subtitle.compatibility.gui/src/i18n/tr.ts (1)
1938-1984: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTranslate the new
lab.*Compatibility Lab strings.Lines 1938–1984 add the Compatibility Lab keys with untranslated English values (e.g.
"lab.title": "Compatibility Lab","lab.col.protocol": "Protocol conformance").models.tab.compatibility/models.subtitle.compatibility, added by the same PR at lines 453/457, are correctly localized to Turkish ("Uyumluluk","Lab projeksiyon kanıtından salt okunur uyumluluk matrisi."). Translate thelab.*block for consistency.🤖 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 `@gui/src/i18n/tr.ts` around lines 1938 - 1984, Translate every newly added lab.* value in the Turkish locale, including titles, statuses, filters, column labels, empty/loading/error messages, verdicts, and evidence layers, while preserving all keys and interpolation or punctuation semantics. Use consistent Turkish terminology with the existing models.tab.compatibility and models.subtitle.compatibility translations.gui/src/i18n/de.ts (1)
1943-1950: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winFinish translating the Compatibility Lab detail-view strings.
Lines 1943–1950 leave
lab.loadMore("Load more"),lab.detailTitle("Verdict detail"),lab.detailClose("Close"),lab.detailSubject("Subject"),lab.detailObservations("Observations"),lab.detailEvents("Contributing events"),lab.detailArtifacts("Artifact metadata"), andlab.detailLoadFailed("Could not load verdict detail") as untranslated English, while the surroundinglab.*keys in the same block (lines 1915-1942, 1951-1961) are correctly translated to German. A German-reading user who opens a verdict detail row sees English labels mixed into an otherwise German page.Translate these eight strings to match the rest of the block.
🤖 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 `@gui/src/i18n/de.ts` around lines 1943 - 1950, Translate the eight English values for lab.loadMore, lab.detailTitle, lab.detailClose, lab.detailSubject, lab.detailObservations, lab.detailEvents, lab.detailArtifacts, and lab.detailLoadFailed into German, matching the terminology and style used by the surrounding lab.* translations while preserving all keys.gui/src/i18n/zh.ts (1)
1936-1983: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winTranslate the new
lab.*Compatibility Lab strings.Lines 1936–1983 add the Compatibility Lab keys with untranslated English values (e.g.
"lab.title": "Compatibility Lab","lab.empty": "No compatibility verdicts in the projection yet."), whilemodels.tab.compatibility/models.subtitle.compatibilityadded by the same PR a few lines above are correctly localized to Chinese ("兼容性","来自实验室投影证据的只读兼容性判定矩阵。"). Translate thelab.*block for consistency.🤖 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 `@gui/src/i18n/zh.ts` around lines 1936 - 1983, Translate every newly added lab.* value in the zh locale, including titles, status and filter labels, column headings, messages, verdicts, and evidence-layer names, into natural Chinese. Preserve all existing keys and interpolation or punctuation structure, and align terminology with the localized models.tab.compatibility and models.subtitle.compatibility entries.
🤖 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 `@gui/src/i18n/de.ts`:
- Around line 1943-1950: Translate the eight English values for lab.loadMore,
lab.detailTitle, lab.detailClose, lab.detailSubject, lab.detailObservations,
lab.detailEvents, lab.detailArtifacts, and lab.detailLoadFailed into German,
matching the terminology and style used by the surrounding lab.* translations
while preserving all keys.
In `@gui/src/i18n/ja.ts`:
- Around line 1936-1983: Translate every newly added lab.* value in the Japanese
catalog from English into natural Japanese, including titles, subtitles,
statuses, filters, column labels, empty/loading/error messages, detail actions,
verdict labels, and evidence-layer labels. Preserve all existing keys and
interpolation or formatting while matching the terminology used by
models.tab.compatibility and models.subtitle.compatibility.
In `@gui/src/i18n/ko.ts`:
- Around line 1936-1983: Translate every newly added lab.* value in the Korean
locale, including titles, labels, statuses, verdicts, layers, and messages,
replacing the English text with natural Korean while preserving all keys and
interpolation or punctuation semantics.
In `@gui/src/i18n/ru.ts`:
- Around line 1938-1985: Translate every newly added lab.* value in the Russian
locale from English into natural Russian, including titles, status labels,
filters, column names, verdicts, layers, and messages. Preserve all lab.* keys
and placeholders while matching the existing Russian terminology used by
models.tab.compatibility and models.subtitle.compatibility.
In `@gui/src/i18n/tr.ts`:
- Around line 1938-1984: Translate every newly added lab.* value in the Turkish
locale, including titles, statuses, filters, column labels, empty/loading/error
messages, verdicts, and evidence layers, while preserving all keys and
interpolation or punctuation semantics. Use consistent Turkish terminology with
the existing models.tab.compatibility and models.subtitle.compatibility
translations.
In `@gui/src/i18n/zh.ts`:
- Around line 1936-1983: Translate every newly added lab.* value in the zh
locale, including titles, status and filter labels, column headings, messages,
verdicts, and evidence-layer names, into natural Chinese. Preserve all existing
keys and interpolation or punctuation structure, and align terminology with the
localized models.tab.compatibility and models.subtitle.compatibility entries.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c1c3a675-ec67-4335-8164-afc0763459ee
📒 Files selected for processing (7)
gui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/tr.tsgui/src/i18n/zh.ts
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
gui/src/i18n/de.ts (1)
1943-1945: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winTranslate the remaining Compatibility Lab UI strings.
The German catalog still contains English controls, labels, and error text at Lines 1943-1945 and 1947-1950. This produces a mixed-language German UI.
Use German values such as
Mehr laden,Urteilsdetails,Schließen,Beobachtungen,Beteiligte Ereignisse,Artefakt-Metadaten, andDetails zum Urteil konnten nicht geladen werden.As per path instructions, keep user-visible GUI strings in the i18n locale file and provide localized values for the German UI.
Proposed translation update
- "lab.loadMore": "Load more", - "lab.detailTitle": "Verdict detail", - "lab.detailClose": "Close", + "lab.loadMore": "Mehr laden", + "lab.detailTitle": "Urteilsdetails", + "lab.detailClose": "Schließen", - "lab.detailObservations": "Observations", - "lab.detailArtifacts": "Artifact metadata", - "lab.detailLoadFailed": "Could not load verdict detail", + "lab.detailObservations": "Beobachtungen", + "lab.detailArtifacts": "Artefakt-Metadaten", + "lab.detailLoadFailed": "Details zum Urteil konnten nicht geladen werden",Also applies to: 1947-1950
🤖 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 `@gui/src/i18n/de.ts` around lines 1943 - 1945, Update the German i18n entries lab.loadMore, lab.detailTitle, lab.detailClose, and the adjacent Compatibility Lab strings through the error message to use German translations, including Mehr laden, Urteilsdetails, Schließen, Beobachtungen, Beteiligte Ereignisse, Artefakt-Metadaten, and Details zum Urteil konnten nicht geladen werden.Source: Path instructions
gui/src/i18n/ja.ts (1)
1936-1982: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy liftTranslate the Compatibility Lab (
lab.*) block instead of copying the English source into every locale. Each locale file below declares itself a translated catalog for its language, but the entirelab.*block (lab.titlethroughlab.layer.task_effectiveness, about 46 keys covering projection status, evidence filters, verdict records, detail views, verdict states, and evidence layers) is an exact copy of the English text fromgui/src/i18n/en.ts. This compiles because everyTKeyis present, but it leaves the Compatibility Lab UI entirely in English for non-English users, and it contradicts each file's own AI-generated summary, which claims these strings were localized.
gui/src/i18n/ja.ts#L1936-L1982: translate thelab.*block into Japanese.gui/src/i18n/ko.ts#L1936-L1983: translate thelab.*block into Korean.gui/src/i18n/ru.ts#L1938-L1985: translate thelab.*block into Russian.gui/src/i18n/tr.ts#L1938-L1985: translate thelab.*block into Turkish.gui/src/i18n/zh.ts#L1936-L1983: translate thelab.*block into Chinese.
As per path instructions, "user-visible strings go through the i18n locale files rather than hardcoded text" — the keys satisfy that mechanically, but the actual localized content is missing for this feature in every non-English locale.🤖 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 `@gui/src/i18n/ja.ts` around lines 1936 - 1982, Translate the complete lab.* Compatibility Lab block instead of retaining English source text: update gui/src/i18n/ja.ts lines 1936-1982 in Japanese, gui/src/i18n/ko.ts lines 1936-1983 in Korean, gui/src/i18n/ru.ts lines 1938-1985 in Russian, gui/src/i18n/tr.ts lines 1938-1985 in Turkish, and gui/src/i18n/zh.ts lines 1936-1983 in Chinese. Preserve every existing key from lab.title through lab.layer.task_effectiveness, including status, filters, verdicts, details, and evidence-layer labels.Source: Path instructions
🤖 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 `@gui/src/i18n/ja.ts`:
- Around line 605-612: Update the Japanese translations for
logs.detail.source.user and its corresponding “was used” message around
logs.detail.source.user so both explicitly state that the provider price overlay
was configured by the user, not supplied by the provider. Preserve the existing
meaning and apply the wording consistently to both strings.
---
Outside diff comments:
In `@gui/src/i18n/de.ts`:
- Around line 1943-1945: Update the German i18n entries lab.loadMore,
lab.detailTitle, lab.detailClose, and the adjacent Compatibility Lab strings
through the error message to use German translations, including Mehr laden,
Urteilsdetails, Schließen, Beobachtungen, Beteiligte Ereignisse,
Artefakt-Metadaten, and Details zum Urteil konnten nicht geladen werden.
In `@gui/src/i18n/ja.ts`:
- Around line 1936-1982: Translate the complete lab.* Compatibility Lab block
instead of retaining English source text: update gui/src/i18n/ja.ts lines
1936-1982 in Japanese, gui/src/i18n/ko.ts lines 1936-1983 in Korean,
gui/src/i18n/ru.ts lines 1938-1985 in Russian, gui/src/i18n/tr.ts lines
1938-1985 in Turkish, and gui/src/i18n/zh.ts lines 1936-1983 in Chinese.
Preserve every existing key from lab.title through lab.layer.task_effectiveness,
including status, filters, verdicts, details, and evidence-layer labels.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2dfe5393-1ffe-417a-9c1c-7ac2062daf48
📒 Files selected for processing (7)
gui/src/i18n/de.tsgui/src/i18n/en.tsgui/src/i18n/ja.tsgui/src/i18n/ko.tsgui/src/i18n/ru.tsgui/src/i18n/tr.tsgui/src/i18n/zh.ts
Stop the user cost overlay reconciler in api-usage afterEach so isolate workers do not keep a poll timer alive into later shard files. Harden cli-restore-back with spawn timeouts and isolated HOME for the help test, matching cli-provider budgets.
Exclude tests/api-usage.test.ts from sharded Linux test legs and run it in a fresh Bun process, matching the storage-policy isolation pattern. Prevents startServer overlay reconciler cycles from wedging shard 4 after cli-restore-back.
|
✅ Action performedReview finished.
|
|
|
|
|
Wibias
left a comment
There was a problem hiding this comment.
Reviewed current head c840973 against current dev. The previous runtime and data-integrity blockers are fixed: cross-process edits reconcile live, disk-only providers survive unrelated saves across multiple owners, usage pricing invalidates safely, and the overlay remains display-only. I found no remaining runtime, security, or data-integrity blocker. One CodeRabbit thread is still a valid non-blocking test-strength nit: the invalid-config test should wait for the reconciler observation counter rather than readConfigDiagnostics() directly. Approval is for this exact head; merge should still require the in-progress exact-head macOS CI job to finish green.
|
Tip For best results, initiate chat on the files or code changes.
You are interacting with an AI system. |
|
✅ Action performedReview finished.
|
|
|
Wibias
left a comment
There was a problem hiding this comment.
Reviewed the current exact head. The final live-reconcile regression now waits for the reconciler observation counter, all review threads are resolved, exact-head CI is green, and the current GitHub merge ref cleanly integrates with dev. The display-only pricing contract, live config reconciliation, cache invalidation, multi-owner preservation, and redaction boundaries all hold. Approved for merge.
|
Merged. Thanks @harryzhou2000 for sticking with this through the review iterations. 🙏 This is a genuinely useful addition: |
|
@Wibias — thanks for the thorough review and the smooth merge! Your feedback made the overlay reconciliation much more robust. 🙏 |
Closes #1009
Summary
Adds
providers.<name>.modelCosts— a per-model display price overlay (USD per1M tokens) so operators can price internal/custom providers whose ids do not
match the compiled catalogs, or whose actual costs vary from list prices.
Resolution order: user
modelCosts→ jawcode catalog → expected-price overlay →model-level vendor fallback. All-zero entries fall through. Display-time
estimation only; never billing.
Changes
src/usage/user-cost-overlays.ts(new): registry liftingproviders.<name>.modelCostsinto price-overlay rows; refreshed atloadConfigand every persist path; versioned so the estimator memo neverserves stale rows and edits apply without a restart.
src/usage/cost.ts: user overlay wins inresolveMatchedPrice(exactprovider/model match,
source: "user"); memo key now includes the overlayversion. Also replaces a stray NUL byte in the memo cache-key template
literal with a space separator.
src/config.ts:providerModelCostsConfigErrorvalidation (non-negativefinite 4-tuples) wired into the load-time schema refine.
src/server/auth-cors.ts: management API rejects malformedmodelCosts;safeConfigDTOexposes the field.src/server/management/shared.ts: newprovider_cost_overlayestimatereason.
gui/: reason label + i18n strings (en/de/ja/ko/ru/zh).UI change
Tests
via overlay, all-zero fall-through, combo fail-closed, registry refresh/memo
invalidation.
validation, DTO passthrough.
bun run typecheck, GUIeslint, and the touched suites pass (91 tests).Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Localization
Review readiness checklist
This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:
All CI tests are green on my local testing.
I pushed my PR to the latest dev commit.
I resolved all correct Codex and CodeRabbit findings.
My PR is ready for review.