fixes to website - #11
Conversation
Reviewer's GuideUpdates models listing behavior, filters, and query sync on the website, improves pricing handling and sorting UX, extends input-modality detection for embeddings, and adds a new script to generate OpenRouter–Vercel model mapping reports. Class diagram for updated model listing filters and sort optionsclassDiagram
class SortOption {
<<type>>
name_asc
name_desc
pricing_low
pricing_high
context_high
max_output_tokens_high
}
class ModelRangeLimits {
+number~2~ contextLength
+number~2~ maxTokens
+number~2~ inputPricing
+number~2~ outputPricing
}
class FilterFeatures {
<<type>>
bool? reasoning
bool? toolCall
}
class StrictFeatures {
<<type>>
bool reasoning
bool toolCall
}
class FilterState {
<<type>>
string[] inputModalities
string[] outputModalities
string[] providers
FilterFeatures features
string[] series
string[] categories
string[] supportedParameters
number~2~ contextLength
number~2~ inputPricing
number~2~ outputPricing
number~2~ maxTokens
}
class ModelsStoreFeatures {
<<type>>
bool reasoning
bool toolCall
}
class ModelsStore {
<<store>>
ModelData[] allModels
string searchQuery
SortOption sortBy
FilterState filters
ModelsStoreFeatures features
string[] series
string[] categories
string[] providers
string[] supportedParameters
number activeFiltersCount
bool hasActiveFilters
}
class ModelsQueryParsers {
<<nuqs_parsers>>
string q
SortOption sort
string[] im
string[] om
string[] prov
string[] s
string[] c
string[] params
bool rz
bool tc
number cmin
number cmax
number tmin
number tmax
number ipmin
number ipmax
number opmin
number opmax
}
ModelsStore --> FilterState : uses
FilterState --> FilterFeatures : has
ModelsStore --> ModelsStoreFeatures : has
ModelsStore --> ModelRangeLimits : uses
ModelsQueryParsers --> SortOption : uses
ModelsQueryParsers --> FilterState : populates
Class diagram for OpenRouter mapping report script typesclassDiagram
class VercelModel {
+string id
+string name
+string description
+number created
+number context_window
+string type
}
class OpenRouterModel {
+string id
+string name
+string description
+number created
+number context_length
}
class SafeMapping {
+string vercelId
+string openrouterId
+string kind exact|provider_alias
}
class CuratedMapping {
+string vercelId
+string kind provider_alias_plus_suffix|snapshot_suffix|renamed_family|variant_collapse|ambiguous_family_match
+string[] candidates
+string notes
}
class CuratedReview {
+string vercelId
+string review high_confidence|risky|should_be_unsupported
+string reason
}
class UnsupportedMapping {
+string vercelId
+string reason
}
class MappingSummaryCounts {
+number vercelModels
+number openrouterModels
+number safe
+number curated
+number unsupported
}
class MappingSummary {
+string generatedAt
+string vercelSource
+string openrouterSource
+MappingSummaryCounts counts
+object providerAliases
}
class MappingReportPipeline {
<<script_main>>
+main()
+buildCandidateList(model, openRouterModels)
+classifyCuratedKind(model, candidates)
+classifyCuratedNotes(model, candidates)
+reviewCuratedMapping(mapping)
+buildUnsupportedReason(model)
}
MappingReportPipeline --> VercelModel : reads
MappingReportPipeline --> OpenRouterModel : reads
MappingReportPipeline --> SafeMapping : produces
MappingReportPipeline --> CuratedMapping : produces
MappingReportPipeline --> CuratedReview : produces
MappingReportPipeline --> UnsupportedMapping : produces
MappingReportPipeline --> MappingSummary : produces
MappingSummary --> MappingSummaryCounts : has
Flow diagram for OpenRouter–Vercel mapping report generation scriptflowchart TD
A[Start mapping report script] --> B[Fetch Vercel models JSON from VERCEL_URL]
B --> C[Fetch OpenRouter models JSON from OPENROUTER_URL]
C --> D[Iterate over each VercelModel]
D --> E{Exact id match in OpenRouter?}
E -->|yes| F[Add SafeMapping kind exact]
E -->|no| G{Provider alias id match?}
G -->|yes| H[Add SafeMapping kind provider_alias]
G -->|no| I[Build candidate list using buildCandidateList]
I --> J{Candidates found?}
J -->|yes| K[Create CuratedMapping and notes]
K --> L[Review mapping with reviewCuratedMapping]
L --> M[Add CuratedMapping and CuratedReview]
J -->|no| N[BuildUnsupportedReason and add UnsupportedMapping]
F --> O[Next VercelModel]
H --> O
M --> O
N --> O
O -->|all processed| P[Sort arrays and assemble MappingSummary]
P --> Q[Write summary.json]
P --> R[Write safe-map.json]
P --> S[Write curated-map.json]
P --> T[Write curated-review.json]
P --> U[Write unsupported.json]
Q --> V[Log counts and output directory]
R --> V
S --> V
T --> V
U --> V
V --> W[End script]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughReplaces "newest" sorting with "name-asc"/"name-desc"; removes temperatureControl from model filters and sync; centralizes USD-per-M-token pricing formatting and changes missing-price handling; adds a script to generate OpenRouter model ID mappings; updates related UI, store, and parsers. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 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.
Hey - I've found 1 issue, and left some high level feedback:
- The price parsing logic (
parsePriceincomputeResultsandtoPerMillionPriceincomputeModelRangeLimits) plusformatUsdPerMTokensusage are now spread across multiple files with slightly different behaviors; consider centralizing price parsing/formatting helpers to guarantee consistent handling of undefined/invalid values. - The
SortOptionunion is now defined both inmodels-types.tsand locally inmodels-toolbar.tsx; it would be more robust to import and reuse the shared type to avoid divergence when sort options change. - The helper functions
isDefaultRangeandparsePriceare recreated on everycomputeResultscall; consider hoisting them to module scope to avoid per-call allocations and keep the function body smaller.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The price parsing logic (`parsePrice` in `computeResults` and `toPerMillionPrice` in `computeModelRangeLimits`) plus `formatUsdPerMTokens` usage are now spread across multiple files with slightly different behaviors; consider centralizing price parsing/formatting helpers to guarantee consistent handling of undefined/invalid values.
- The `SortOption` union is now defined both in `models-types.ts` and locally in `models-toolbar.tsx`; it would be more robust to import and reuse the shared type to avoid divergence when sort options change.
- The helper functions `isDefaultRange` and `parsePrice` are recreated on every `computeResults` call; consider hoisting them to module scope to avoid per-call allocations and keep the function body smaller.
## Individual Comments
### Comment 1
<location path="apps/website/app/(models)/models/models-store-context.tsx" line_range="166-167" />
<code_context>
searchQuery: string,
filters: FilterState,
- sortBy: SortOption
+ sortBy: SortOption,
+ rangeLimits: ModelRangeLimits
): ModelData[] => {
+ const isDefaultRange = (
</code_context>
<issue_to_address>
**suggestion:** The local `parsePrice` helper duplicates logic from `computeModelRangeLimits`; consider extracting a shared utility.
`computeResults` and `computeModelRangeLimits` now both convert a string to a per-million price (`parsePrice` vs `toPerMillionPrice`). Duplicating this logic (especially around empty strings and invalid numbers) risks the two paths drifting over time. Please extract a shared helper (or small pricing util) and have both call sites reuse it.
Suggested implementation:
```typescript
count += rangeEquals(f.outputPricing, defaults.outputPricing) ? 0 : 1;
const toPerMillionPrice = (value?: string | null): number | null => {
if (value == null) {
return null;
}
const trimmed = value.trim();
if (trimmed === "") {
return null;
}
const price = Number.parseFloat(trimmed);
if (!Number.isFinite(price)) {
return null;
}
return price * 1_000_000;
};
allModels: ModelData[],
```
To fully eliminate the duplicated price-parsing logic and have both call sites reuse the same helper:
1. Locate `computeModelRangeLimits` in this file. It currently defines or uses a local `toPerMillionPrice` helper. Remove that local definition and instead use the new top-level `toPerMillionPrice` defined above.
2. Update any usages of the old local `toPerMillionPrice` in `computeModelRangeLimits` (or nearby helpers) to reference the shared top-level function (no call-site changes needed if the name is the same and now resolves to the shared one).
3. In this `computeResults` function, replace any direct price parsing logic (e.g. calls to a local `parsePrice`) with calls to the shared `toPerMillionPrice`. If `parsePrice` is referenced elsewhere in `computeResults`, either:
- Inline `toPerMillionPrice` at those call sites, or
- Keep a thin alias like `const parsePrice = toPerMillionPrice;` instead of the custom implementation that was removed in the SEARCH block.
4. If there are any unit tests that depended on the previous local helpers, add or update tests to cover `toPerMillionPrice`’s behavior (handling of `undefined`, `null`, empty strings, and invalid numbers) to ensure both `computeResults` and `computeModelRangeLimits` share the exact same semantics.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| sortBy: SortOption, | ||
| rangeLimits: ModelRangeLimits |
There was a problem hiding this comment.
suggestion: The local parsePrice helper duplicates logic from computeModelRangeLimits; consider extracting a shared utility.
computeResults and computeModelRangeLimits now both convert a string to a per-million price (parsePrice vs toPerMillionPrice). Duplicating this logic (especially around empty strings and invalid numbers) risks the two paths drifting over time. Please extract a shared helper (or small pricing util) and have both call sites reuse it.
Suggested implementation:
count += rangeEquals(f.outputPricing, defaults.outputPricing) ? 0 : 1;
const toPerMillionPrice = (value?: string | null): number | null => {
if (value == null) {
return null;
}
const trimmed = value.trim();
if (trimmed === "") {
return null;
}
const price = Number.parseFloat(trimmed);
if (!Number.isFinite(price)) {
return null;
}
return price * 1_000_000;
};
allModels: ModelData[],To fully eliminate the duplicated price-parsing logic and have both call sites reuse the same helper:
- Locate
computeModelRangeLimitsin this file. It currently defines or uses a localtoPerMillionPricehelper. Remove that local definition and instead use the new top-leveltoPerMillionPricedefined above. - Update any usages of the old local
toPerMillionPriceincomputeModelRangeLimits(or nearby helpers) to reference the shared top-level function (no call-site changes needed if the name is the same and now resolves to the shared one). - In this
computeResultsfunction, replace any direct price parsing logic (e.g. calls to a localparsePrice) with calls to the sharedtoPerMillionPrice. IfparsePriceis referenced elsewhere incomputeResults, either:- Inline
toPerMillionPriceat those call sites, or - Keep a thin alias like
const parsePrice = toPerMillionPrice;instead of the custom implementation that was removed in the SEARCH block.
- Inline
- If there are any unit tests that depended on the previous local helpers, add or update tests to cover
toPerMillionPrice’s behavior (handling ofundefined,null, empty strings, and invalid numbers) to ensure bothcomputeResultsandcomputeModelRangeLimitsshare the exact same semantics.
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 (1)
apps/website/app/(models)/models/components/models-toolbar.tsx (1)
15-21:⚠️ Potential issue | 🟠 MajorSync this toolbar with the canonical sort contract.
This local
SortOptionhas drifted from the shared models-page sort type: it still allows"context-low"and omits"max-output-tokens-high". As a result, the toolbar can emit a sort value the parser/store no longer accept, and it cannot render a valid sort state if"max-output-tokens-high"comes from the URL or store.Suggested fix
+import type { SortOption } from "@/app/(models)/models/models-types"; import { RotateCcw, Search, X } from "lucide-react"; import { memo } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; @@ -type SortOption = - | "name-asc" - | "name-desc" - | "pricing-low" - | "pricing-high" - | "context-high" - | "context-low"; - export const PureModelsToolbar = memo(function PureModelsToolbar({ @@ <SelectItem value="name-asc">A-Z</SelectItem> <SelectItem value="name-desc">Z-A</SelectItem> <SelectItem value="pricing-low">$ Low → High</SelectItem> <SelectItem value="pricing-high">$ High → Low</SelectItem> <SelectItem value="context-high">Context High → Low</SelectItem> - <SelectItem value="context-low">Context Low → High</SelectItem> + <SelectItem value="max-output-tokens-high"> + Max Output High → Low + </SelectItem>Also applies to: 67-72
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/website/app/`(models)/models/components/models-toolbar.tsx around lines 15 - 21, The local SortOption union in models-toolbar is out of sync with the canonical models-page sort contract: remove the deprecated "context-low" option and add "max-output-tokens-high" (and any other missing canonical variants), and update any usage sites in the models-toolbar component to accept/emit the canonical values (including parsing URL/store values and rendering the active sort). Ensure the SortOption type definition and all references in the models-toolbar component (including the other occurrence referenced in the review) match the shared sort type exactly so the toolbar never emits or fails to render unsupported sort strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/website/app/`(models)/models/models-store-context.tsx:
- Around line 174-180: parsePrice correctly returns null for unknown prices, but
the pricing sort branches still coerce null to 0 causing unknowns to sort as
free; update the sort comparator(s) that handle the "pricing-low" and
"pricing-high" branches to treat nulls specially instead of coercing to 0: for
ascending ("pricing-low") map null -> Infinity so unknown prices appear last,
and for descending ("pricing-high") map null -> -Infinity so unknowns appear
last there too; replace any usage like (priceA ?? 0) / (priceB ?? 0) with a
small helper or inline mapping that converts parsePrice(...) nulls as described
and then compare the numeric values.
---
Outside diff comments:
In `@apps/website/app/`(models)/models/components/models-toolbar.tsx:
- Around line 15-21: The local SortOption union in models-toolbar is out of sync
with the canonical models-page sort contract: remove the deprecated
"context-low" option and add "max-output-tokens-high" (and any other missing
canonical variants), and update any usage sites in the models-toolbar component
to accept/emit the canonical values (including parsing URL/store values and
rendering the active sort). Ensure the SortOption type definition and all
references in the models-toolbar component (including the other occurrence
referenced in the review) match the shared sort type exactly so the toolbar
never emits or fails to render unsupported sort strings.
🪄 Autofix (Beta)
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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 93b6696f-dedc-4a41-ae5e-a8c5aa5188a1
📒 Files selected for processing (13)
apps/website/app/(models)/compare/model-details-card.tsxapps/website/app/(models)/models/components/models-toolbar.tsxapps/website/app/(models)/models/gateway-model-card.tsxapps/website/app/(models)/models/model-filters.tsxapps/website/app/(models)/models/model-query-parsers.tsapps/website/app/(models)/models/models-constants.tsapps/website/app/(models)/models/models-store-context.tsxapps/website/app/(models)/models/models-types.tsapps/website/app/(models)/models/sort-select.tsxapps/website/app/(models)/models/use-models-nuqs-sync.tsapps/website/app/(models)/models/wide-model-details.tsxapps/website/lib/ai/to-model-data.tsapps/website/scripts/report-openrouter-mappings.ts
There was a problem hiding this comment.
2 issues found across 13 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/website/app/(models)/models/models-store-context.tsx">
<violation number="1" location="apps/website/app/(models)/models/models-store-context.tsx:174">
P2: Models with unknown pricing now remain in search results (good), but the `pricing-low` / `pricing-high` sort branches still coerce missing prices to `"0"`. This causes models with no pricing data to rank as if they are free. The `parsePrice` helper defined here correctly returns `null` for missing values—reuse it in the sort comparator and push `null`-priced models to the end of the list instead of treating them as `$0`.</violation>
</file>
<file name="apps/website/scripts/report-openrouter-mappings.ts">
<violation number="1" location="apps/website/scripts/report-openrouter-mappings.ts:172">
P2: Vision-family scoring omits `r2v`/`wan` for OpenRouter candidates, causing false mismatches and lower candidate scores for valid mappings.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| candidateTokens.includes("image") || | ||
| candidateTokens.includes("video") || | ||
| candidateTokens.includes("i2v") || | ||
| candidateTokens.includes("t2v") || | ||
| candidateTokens.includes("veo") || | ||
| candidateTokens.includes("imagen") || | ||
| compact(candidate.name).includes("image") || | ||
| compact(candidate.name).includes("video"); | ||
| if (isVisionFamily !== candidateIsVisionFamily) { |
There was a problem hiding this comment.
P2: Vision-family scoring omits r2v/wan for OpenRouter candidates, causing false mismatches and lower candidate scores for valid mappings.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/website/scripts/report-openrouter-mappings.ts, line 172:
<comment>Vision-family scoring omits `r2v`/`wan` for OpenRouter candidates, causing false mismatches and lower candidate scores for valid mappings.</comment>
<file context>
@@ -0,0 +1,458 @@
+ vercelTokens.includes("imagen") ||
+ vercelTokens.includes("wan");
+ const candidateIsVisionFamily =
+ candidateTokens.includes("image") ||
+ candidateTokens.includes("video") ||
+ candidateTokens.includes("i2v") ||
</file context>
| candidateTokens.includes("image") || | |
| candidateTokens.includes("video") || | |
| candidateTokens.includes("i2v") || | |
| candidateTokens.includes("t2v") || | |
| candidateTokens.includes("veo") || | |
| candidateTokens.includes("imagen") || | |
| compact(candidate.name).includes("image") || | |
| compact(candidate.name).includes("video"); | |
| if (isVisionFamily !== candidateIsVisionFamily) { | |
| const candidateIsVisionFamily = | |
| candidateTokens.includes("image") || | |
| candidateTokens.includes("video") || | |
| candidateTokens.includes("i2v") || | |
| candidateTokens.includes("t2v") || | |
| candidateTokens.includes("r2v") || | |
| candidateTokens.includes("veo") || | |
| candidateTokens.includes("imagen") || | |
| candidateTokens.includes("wan") || | |
| compact(candidate.name).includes("image") || | |
| compact(candidate.name).includes("video"); |
Summary by Sourcery
Update models listing UX, pricing handling, and add an OpenRouter mapping report script.
New Features:
Bug Fixes:
Enhancements:
Build:
Summary by cubic
Improved the models explorer with A–Z/Z–A default sorting, smarter pricing display/sorting/filtering, and a leaner features filter. Added an OpenRouter mapping report script that outputs safe, curated, review, and unsupported mappings.
Bug Fixes
formatUsdPerMTokens; ignore missing/NaN in range limits; keep models with missing prices unless a range is set; sort missing prices last.New Features
scripts/report-openrouter-mappings.tsto writesummary.json,safe-map.json,curated-map.json,curated-review.json, andunsupported.json.Written for commit db75fd8. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes
Style
Refactor
Chores