Skip to content

fixes to website - #11

Merged
FranciscoMoretti merged 2 commits into
mainfrom
on-demand-model-loading-follow-up
Apr 11, 2026
Merged

fixes to website#11
FranciscoMoretti merged 2 commits into
mainfrom
on-demand-model-loading-follow-up

Conversation

@FranciscoMoretti

@FranciscoMoretti FranciscoMoretti commented Apr 11, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Update models listing UX, pricing handling, and add an OpenRouter mapping report script.

New Features:

  • Add a script to generate safe, curated, and unsupported mappings between Vercel AI Gateway models and OpenRouter models.

Bug Fixes:

  • Treat models without explicit pricing as matching default pricing filters instead of forcing them out of search results.
  • Correctly classify embedding models as supporting text input.

Enhancements:

  • Change default model sort and UI options from newest-first to name-based A–Z/Z–A sorting.
  • Remove the unused temperature-control feature flag and its query parameter from model filters and state synchronization.
  • Improve pricing range calculations and comparisons by safely handling missing or non-finite price values.
  • Centralize and reuse pricing display formatting for model details and comparison views.
  • Adjust sort select layout to be full-width on small screens and align dropdown width with the trigger.

Build:

  • Add a Node script under scripts/ to fetch live Vercel/OpenRouter model lists and emit mapping JSON reports for downstream use.

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

    • Pricing: unified formatting with formatUsdPerMTokens; ignore missing/NaN in range limits; keep models with missing prices unless a range is set; sort missing prices last.
    • Filters/UI: removed Temperature control; embeddings count as text input; aligned sort dropdown width to its trigger.
  • New Features

    • Sorting: replaced “Newest” with “A–Z” (default) and “Z–A”; added “Max Output High → Low”; removed “Context Low → High”; updated query parsing and defaults.
    • Tooling: added scripts/report-openrouter-mappings.ts to write summary.json, safe-map.json, curated-map.json, curated-review.json, and unsupported.json.

Written for commit db75fd8. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added A–Z and Z–A model sorting and made A–Z the new default.
    • Embedding models now expose text input support.
  • Bug Fixes

    • More accurate and consistent pricing display, including better handling of missing prices.
  • Style

    • Sort selector now responsive for improved mobile/layout behavior.
  • Refactor

    • Pricing formatting centralized for consistent displays.
  • Chores

    • Removed the Temperature Control filter option.

@sourcery-ai

sourcery-ai Bot commented Apr 11, 2026

Copy link
Copy Markdown

Reviewer's Guide

Updates 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 options

classDiagram
  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
Loading

Class diagram for OpenRouter mapping report script types

classDiagram
  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
Loading

Flow diagram for OpenRouter–Vercel mapping report generation script

flowchart 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]
Loading

File-Level Changes

Change Details Files
Change default and available sort options from newest-first to name-based A–Z/Z–A across models views and query parsing.
  • Update SortOption type to replace 'newest' with 'name-asc' and 'name-desc' variants across models-related modules.
  • Change defaultSortBy and query parser defaults from newest to name-asc.
  • Adjust computeResults sorting logic to sort by model.name for name-asc/name-desc cases.
  • Update SortSelect and toolbar select components to present A–Z/Z–A labels and ensure dropdown width matches trigger.
apps/website/app/(models)/models/models-store-context.tsx
apps/website/app/(models)/models/sort-select.tsx
apps/website/app/(models)/models/model-query-parsers.ts
apps/website/app/(models)/models/components/models-toolbar.tsx
apps/website/app/(models)/models/models-types.ts
Remove the temperatureControl feature flag from model filters and URL sync, leaving only reasoning and toolCall.
  • Drop temperatureControl from ModelsStore.features, StrictFeatures, FilterState, and related equality/normalization helpers.
  • Update computeActiveFiltersCount and state updaters to no longer count or propagate temperatureControl.
  • Simplify FeaturesFilter UI to only handle reasoning and toolCall and adjust toggle helper signatures.
  • Remove tctl query parameter handling from query parsers and nuqs sync logic.
apps/website/app/(models)/models/models-store-context.tsx
apps/website/app/(models)/models/model-filters.tsx
apps/website/app/(models)/models/model-query-parsers.ts
apps/website/app/(models)/models/use-models-nuqs-sync.ts
Make pricing filters and range computations robust to missing/invalid pricing and align UI with shared formatting helpers.
  • Introduce helper functions in computeResults to detect default ranges and safely parse per-million prices, returning null for undefined/invalid values.
  • Ensure models without pricing only pass filters when the corresponding price range is at its default limit, otherwise they are excluded.
  • Refactor ModelRangeLimits computation to reuse a safe toPerMillionPrice helper and ignore nulls.
  • Switch pricing display in model details and comparison views to use formatUsdPerMTokens and accept possibly undefined pricing strings.
  • Pass rangeLimits into computeResults so pricing-default checks are correct.
apps/website/app/(models)/models/models-store-context.tsx
apps/website/app/(models)/models/models-constants.ts
apps/website/app/(models)/compare/model-details-card.tsx
apps/website/app/(models)/models/wide-model-details.tsx
apps/website/app/(models)/models/gateway-model-card.tsx
Broaden model data to treat embeddings as supporting text input in the UI.
  • Update toModelData to set input.text true for models whose type is 'language' or 'embedding' so embeddings appear as text-capable where relevant.
apps/website/lib/ai/to-model-data.ts
Add a script to generate and classify mappings between Vercel AI Gateway models and OpenRouter models.
  • Create report-openrouter-mappings script that fetches live model lists from both APIs, computes safe exact/provider-alias mappings, curated candidate mappings, and unsupported models.
  • Implement scoring-based candidate selection, family- and provider-aware heuristics, and classification of curated mappings into kinds and review levels.
  • Write mapping outputs and summary statistics into structured JSON files under scripts/outputs/openrouter-mapping and log a short console summary.
apps/website/scripts/report-openrouter-mappings.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@vercel

vercel Bot commented Apr 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ai-registry-website Ready Ready Preview, Comment Apr 11, 2026 8:33am

@coderabbitai

coderabbitai Bot commented Apr 11, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2cb53693-9f06-4e5c-a5fb-44ed8f720172

📥 Commits

Reviewing files that changed from the base of the PR and between 902d560 and db75fd8.

📒 Files selected for processing (2)
  • apps/website/app/(models)/models/components/models-toolbar.tsx
  • apps/website/app/(models)/models/models-store-context.tsx

📝 Walkthrough

Walkthrough

Replaces "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

Cohort / File(s) Summary
Sort Option Refactor
apps/website/app/(models)/models-types.ts, apps/website/app/(models)/models/components/models-toolbar.tsx, apps/website/app/(models)/models/model-query-parsers.ts, apps/website/app/(models)/models/sort-select.tsx
Removes "newest" sort, adds "name-asc"/"name-desc", updates type, dropdown items, query parsing defaults, and select trigger/content sizing.
Pricing Format Centralization
apps/website/app/(models)/compare/model-details-card.tsx, apps/website/app/(models)/models/gateway-model-card.tsx, apps/website/app/(models)/models/wide-model-details.tsx, apps/website/app/(models)/models/models-constants.ts
Delegates per‑M‑token formatting to formatUsdPerMTokens; stops defaulting undefined pricing to "0", introduces parsing helper to exclude undefined values from ranges.
Filter & Store: temperatureControl Removal
apps/website/app/(models)/models/model-filters.tsx, apps/website/app/(models)/models/models-store-context.tsx, apps/website/app/(models)/models/use-models-nuqs-sync.ts, apps/website/app/(models)/models/use-models-nuqs-sync.ts
Removes temperatureControl from filter state, store features, sync logic, equality/normalization, and UI; updates feature defaults and active-filter counting.
Store Pricing & Compute Logic
apps/website/app/(models)/models/models-store-context.tsx, apps/website/app/(models)/models/models-constants.ts, apps/website/app/(models)/models/model-query-parsers.ts
Changes default sort to name-asc; adjusts computeResults to accept rangeLimits, uses robust parsePrice handling, treats missing/invalid prices as non-filtering when range is default, and stabilizes pricing sort by using nullable totalPrice with infinities for ordering.
UI Components & Small Behavior
apps/website/app/(models)/models/components/models-toolbar.tsx, apps/website/app/(models)/models/sort-select.tsx, apps/website/app/(models)/models/model-filters.tsx, apps/website/app/(models)/models/model-query-parsers.ts
Updates toolbar/sort UI to reflect new sort options and responsive trigger sizing; removes obsolete options and query param (tctl).
Model Data Mapping
apps/website/lib/ai/to-model-data.ts
Sets input.text = true for both language and embedding model types.
OpenRouter Mapping Script
apps/website/scripts/report-openrouter-mappings.ts
Adds new script that fetches Vercel/OpenRouter models, computes safe/curated/unsupported mappings using similarity heuristics, and writes JSON outputs to scripts/outputs/openrouter-mapping/.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • fixes to website #11: Mirrors the same code-level changes—replaces inline per‑M pricing formatting with formatUsdPerMTokens, updates sort options (newestname-asc/name-desc), and removes temperatureControl.
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'fixes to website' is vague and generic, lacking specificity about which fixes or what aspects of the website are being addressed. Replace with a more descriptive title that identifies the primary change, such as 'Replace newest sort with name-based sorting and refactor pricing display' or 'Update model sorting and consolidate pricing formatting logic'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch on-demand-model-loading-follow-up

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • 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.
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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +166 to +167
sortBy: SortOption,
rangeLimits: ModelRangeLimits

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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:

  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.

@coderabbitai coderabbitai 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.

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 | 🟠 Major

Sync this toolbar with the canonical sort contract.

This local SortOption has 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

📥 Commits

Reviewing files that changed from the base of the PR and between 564646c and 902d560.

📒 Files selected for processing (13)
  • apps/website/app/(models)/compare/model-details-card.tsx
  • apps/website/app/(models)/models/components/models-toolbar.tsx
  • apps/website/app/(models)/models/gateway-model-card.tsx
  • apps/website/app/(models)/models/model-filters.tsx
  • apps/website/app/(models)/models/model-query-parsers.ts
  • apps/website/app/(models)/models/models-constants.ts
  • apps/website/app/(models)/models/models-store-context.tsx
  • apps/website/app/(models)/models/models-types.ts
  • apps/website/app/(models)/models/sort-select.tsx
  • apps/website/app/(models)/models/use-models-nuqs-sync.ts
  • apps/website/app/(models)/models/wide-model-details.tsx
  • apps/website/lib/ai/to-model-data.ts
  • apps/website/scripts/report-openrouter-mappings.ts

Comment thread apps/website/app/(models)/models/models-store-context.tsx

@cubic-dev-ai cubic-dev-ai 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.

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.

Comment thread apps/website/app/(models)/models/models-store-context.tsx
Comment on lines +172 to +180
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) {

@cubic-dev-ai cubic-dev-ai Bot Apr 11, 2026

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: 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>
Suggested change
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");
Fix with Cubic

@FranciscoMoretti
FranciscoMoretti merged commit 1bdab42 into main Apr 11, 2026
3 of 5 checks passed
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