Skip to content

feat(radar): show the rate limits and training disclosure the feed already sends - #12320

Merged
diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.51from
maxmad64bis:feat/radar-catalog-limits-training
Sep 1, 2026
Merged

diegosouzapw merged 1 commit into
cheaper-inference:release/v3.8.51from
maxmad64bis:feat/radar-catalog-limits-training

Conversation

@maxmad64bis

@maxmad64bis maxmad64bis commented Sep 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The Radar feed reports per-model rate limits and whether a provider says it may train on your prompts. The catalog table renders neither. Both fields are on RadarMergedEntry; grep them in RadarCatalogTable.tsx and the only hits are the type declaration.

The training flag is the one that stings. freeModelCatalog.ts documents it as "Provider states it may train on user prompts. Surfaced in the UI so the..." — a promise the UI doesn't keep. Thirteen catalog entries carry it today.

So: a Rate limits column, and a badge in the ToS cell when a provider discloses training. No new data, no request, no API change.

Two decisions in there. A limit of zero renders as 0/min, not rate-only: formatTokens maps 0 to "rate-only", which is right for a monthly budget and nonsense for a ceiling, where zero is a real and alarming fact. And a model with no training statement gets no badge, with the tooltip saying why — models without it either state they don't train, or don't document it. An absent statement isn't a guarantee, so the check is === true.

The cost of this one is the i18n pass: three English keys plus the sentinel in forty-two locales.

Related Issues

Validation

  • Change type: UI / i18n
  • Focused tests and category gates from the golden path
  • npm run lint
  • Reconciled with the current active release base; focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR

Red first: drop the badge condition and the flag test fails; formatLimits didn't exist, so its cases couldn't compile. After, 14/14.

i18n gates: check-ui-coverage PASS, check-glossary PASS, and check-value-drift PASS with BASE_REF=upstream/release/v3.8.51 — without that variable it resolves nothing and its green means nothing.

npm run lint exits 2 here and on an untouched checkout of the base alike (suppressions left that do not occur anymore, zero reported errors).

Tests Added Or Updated

  • tests/unit/dashboard/radar-catalog-table-limits-training.test.tsx (new, 14 cases)

Coverage Notes

Two exported pure formatters and two render sites. The formatters are covered directly — every combination of present and absent limits, zero, locale-independence of the compaction, and non-numeric input. The badge is covered by rendering the table and finding the element rather than looking for a substring: the next-intl mock returns a distinguishable string, so a stray key elsewhere in the DOM can't make the test pass for the wrong reason. One case asserts every row has as many cells as the header has columns.

Reviewer Notes

  • The 42 locale files got the three keys as __MISSING__ sentinels, matching what i18n:sync-ui produces. I inserted only those three rather than running the script: a full run would also sweep in seven unrelated kilo* keys per locale already missing on the base — 274 extra lines belonging to whichever PR added them.
  • compactCount guards non-finite and negative input. The feed is JSON off the network, so the declared number is a hope; without the guard a bad value renders as InfinityM or NaN/min.
  • The badge sits in the existing ToS cell rather than a new column, since it qualifies the same "should I use this provider" judgement.

@maxmad64bis
maxmad64bis force-pushed the feat/radar-catalog-limits-training branch from bbe2f47 to cfc2e85 Compare September 1, 2026 14:19
@diegosouzapw
diegosouzapw merged commit ad4b67d into cheaper-inference:release/v3.8.51 Sep 1, 2026
14 of 16 checks passed
maxmad64bis pushed a commit to maxmad64bis/OmniRouteFork that referenced this pull request Sep 1, 2026
… + stream gates

Docs cheaper-inference#12320 added radarPage.colLimits / trainsOnPrompts /
trainsOnPromptsHelp as __MISSING__ in 42 locales but never added the
EN source. That broke two gates: i18n-missing-placeholder-fallback
(cheaper-inference#7258, "__MISSING__ after EN fallback") and settings-i18n-keys
("direct translation calls have English messages"). Add the three EN
keys (values are the sentinels' English text).

cheaper-inference#12151 changed passthrough to estimate usage on finish without usage
when content exists and to drop the trailing choices:[] valid usage
once estimated. tests/unit/stream-utils "forwards OpenAI usage-only
empty choices chunks" still expected the old forward. Update it to
assert estimated:true and no choices:[] (same tradeoff documented in
stream-passthrough-usage-estimation).

Also drops the dead collectSSE helper in stream-passthrough-usage-
estimation.test.ts (3 unused identifiers) that reds No new ESLint
warnings — same as already-merged fix/release-v3.8.51-eslint-unused-
collectsse.

Reproduced on pure tip 438db55: 4 RED (ESLint, i18n-missing-fallback,
settings-i18n-keys, stream-utils ×2 via TIA + shard 4/4). After:
4 suites pass locally (i18n-missing, settings-i18n-keys, stream-
passthrough, stream-utils 40/40).
diegosouzapw added a commit that referenced this pull request Sep 1, 2026
…e regression + radarPage i18n keys (#12327)

* fix(sse): forward the upstream's real trailing usage in passthrough; estimate only at flush

#12151 injected estimated usage into the finish chunk and dropped the real
trailing usage block that genuine OpenAI upstreams send afterwards — metered
clients got estimates instead of real token counts. The estimate now leaves
via a canonical usage-only chunk at flush, only when the upstream stayed
silent; a real trailing block is forwarded verbatim and wins. The tool_calls
finish_reason normalization now materializes its own rewrite (it piggybacked
on the removed finish-time rewrite), and the dead collectSSE helper goes with
it (subsumes #12324).

* fix(i18n): seed the radarPage limits/training keys the #12320 UI already consumes

RadarCatalogTable.tsx references radarPage.colLimits / trainsOnPrompts /
trainsOnPromptsHelp but #12320 never added them to en.json, so the EN
fallback could not resolve the __MISSING__ markers across 42 locales.
Real translations for pt-BR and vi; the rest resolve via the EN fallback.

* fix(sse): carry the chat stream id into flush-time synthetic chunks

The estimated usage-only chunk emitted at flush used passthroughResponsesId,
which is only ever set on the Responses path — on the chat path the synthetic
chunk shipped id: null, breaking the string-id invariant pinned by
stream-numeric-ids.test.ts. Track the upstream chat-completion id in the
passthrough loop and reuse it (falling back to the Responses id, then a
generated one). Sibling sweep: 74 files importing utils/stream — 603/603.
@maxmad64bis
maxmad64bis deleted the feat/radar-catalog-limits-training branch September 24, 2026 21:11
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…ready sends (cheaper-inference#12320)

The Radar feed reports per-model rate limits and whether a provider says it may train on your prompts. Both fields are on RadarMergedEntry and the catalog table rendered neither — grep them in RadarCatalogTable.tsx and the only hits were the type declaration. The training flag is the one that stings: freeModelCatalog.ts documents it as "Surfaced in the UI", a promise the UI did not keep, and thirteen catalog entries carry it today.

Adds a Rate limits column and a badge in the ToS cell when a provider discloses training. No new data, no request, no API change.

Two judgement calls worth keeping: a limit of zero renders as 0/min rather than formatTokens' "rate-only" (right for a monthly budget, nonsense for a ceiling where zero is a real and alarming fact), and the badge condition is === true, since an absent training statement is not a guarantee.

Validation note — read before trusting the green: this PR's own suite (tests/unit/dashboard/radar-catalog-table-limits-training.test.tsx, 14 cases) could NOT be run locally. Vitest fails to resolve react18-json-view, which is declared in package.json and package-lock.json but is not present in this machine's node_modules; two pre-existing .test.tsx files in the same directory fail identically, so the cause is environmental and not this PR. The blocking test-vitest CI job runs npm ci and will execute it.

What was verified locally, in a combined batch worktree with all 11 PRs of this batch: 174/174 node-runner focused tests, typecheck:core clean, complexity 2706/3218, cognitive-complexity 1221/1437, check:cycles and check:docs-counts green, plus both CI i18n gates for the 42-locale pass — check-ui-keys-coverage PASS (all 42 locales at or above 65%) and check-ui-value-drift PASS against the release tip.

Thanks @maxmad64bis.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…e regression + radarPage i18n keys (cheaper-inference#12327)

* fix(sse): forward the upstream's real trailing usage in passthrough; estimate only at flush

cheaper-inference#12151 injected estimated usage into the finish chunk and dropped the real
trailing usage block that genuine OpenAI upstreams send afterwards — metered
clients got estimates instead of real token counts. The estimate now leaves
via a canonical usage-only chunk at flush, only when the upstream stayed
silent; a real trailing block is forwarded verbatim and wins. The tool_calls
finish_reason normalization now materializes its own rewrite (it piggybacked
on the removed finish-time rewrite), and the dead collectSSE helper goes with
it (subsumes cheaper-inference#12324).

* fix(i18n): seed the radarPage limits/training keys the cheaper-inference#12320 UI already consumes

RadarCatalogTable.tsx references radarPage.colLimits / trainsOnPrompts /
trainsOnPromptsHelp but cheaper-inference#12320 never added them to en.json, so the EN
fallback could not resolve the __MISSING__ markers across 42 locales.
Real translations for pt-BR and vi; the rest resolve via the EN fallback.

* fix(sse): carry the chat stream id into flush-time synthetic chunks

The estimated usage-only chunk emitted at flush used passthroughResponsesId,
which is only ever set on the Responses path — on the chat path the synthetic
chunk shipped id: null, breaking the string-id invariant pinned by
stream-numeric-ids.test.ts. Track the upstream chat-completion id in the
passthrough loop and reuse it (falling back to the Responses id, then a
generated one). Sibling sweep: 74 files importing utils/stream — 603/603.
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.

2 participants