Skip to content

feat(models): add stability flags with ui - #681

Merged
steebchen merged 9 commits into
mainfrom
feat/deepseek-stability-flags
Aug 31, 2025
Merged

steebchen merged 9 commits into
mainfrom
feat/deepseek-stability-flags

Conversation

@steebchen

@steebchen steebchen commented Aug 30, 2025

Copy link
Copy Markdown
Member

adds stability flags; only use for UI purposes now, we can use this for routing decisions as well

image image

Summary

  • Add comprehensive stability flag system for model quality indicators
  • Implement stability levels: stable, beta, unstable, experimental
  • Mark DeepSeek V3, R1, and R1-0528 as unstable due to their experimental nature
  • Mark DeepSeek R1 Distill as beta (more stable than base models but not fully stable)
  • Add UI warnings and badges for unstable models in ModelSelector, AllModels, ProviderCard, and ModelPage
  • Include stability information in API responses
  • Enable previously disabled nebius provider for DeepSeek R1-0528
  • Add stability badges and warning icons consistently across multiple UI components

Changes Made

Schema Updates

  • Added StabilityLevel type with four levels
  • Extended ModelDefinition and ProviderModelMapping interfaces with optional stability field
  • Updated API schema to include stability information

Model Configuration

  • DeepSeek V3: marked as unstable
  • DeepSeek R1: marked as unstable
  • DeepSeek R1-0528: marked as beta (updated from unstable)
  • DeepSeek R1 Distill: marked as beta
  • DeepSeek V3.1: defaults to stable (no explicit flag needed)

UI Enhancements

  • Added warning triangles for unstable/experimental models in multiple UI components
  • Added stability badges (BETA, UNSTABLE, EXPERIMENTAL) in model selector, all models list, provider cards, and model detail page
  • Color-coded badges: blue for beta, red for unstable, orange for experimental
  • Show stability badges for providers as well as models

API Updates

  • Models endpoint now returns stability information
  • Proper validation of stability field values

Testing

  • Added comprehensive tests for stability flag handling
  • Validated API responses include stability information
  • Ensured only valid stability values are returned

Test Plan

  • Unit tests pass for models API
  • UI displays stability warnings and badges correctly
  • API responses include stability field
  • DeepSeek models show correct stability levels
  • Linting and formatting passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Models API exposes a stability level (stable, beta, unstable, experimental) with provider→model fallbacks. DeepSeek metadata updated (some models marked beta/unstable) and an additional provider activated for DeepSeek R1-0528.
  • UI Changes

    • Playground, model pages, list/grid, cards and provider views show stability badges, a Stability column/section, and warning icons for unstable/experimental entries.
  • Tests

    • Added tests validating stability metadata is present and uses allowed values in models API responses.

🌿 Generated by Terry


ℹ️ Tag @terragon-labs to ask questions and address PR feedback

📎 Task: https://www.terragonlabs.com/task/accc24dc-1c04-43e4-8d8e-c67c3fad4f10

- Add StabilityLevel type with stable, beta, unstable, experimental options
- Add stability field to ModelDefinition interface
- Mark DeepSeek V3, R1, and R1-0528 as unstable
- Mark DeepSeek R1 Distill as beta
- Update UI ModelSelector to show stability warnings and badges
- Add stability information to API responses
- Add comprehensive tests for stability flag handling
- Enable previously commented nebius provider for DeepSeek R1-0528

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 30, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a StabilityLevel type and optional stability fields to model and provider metadata; surfaces stability in the gateway /v1/models response; annotates DeepSeek models/providers with stability values (and activates a NEBIUS provider); updates UI to render stability badges and warnings; and adds tests validating /v1/models stability semantics.

Changes

Cohort / File(s) Summary
Model typing: stability type & fields
packages/models/src/models.ts
Add `export type StabilityLevel = "stable"
DeepSeek model metadata & providers
packages/models/src/models/deepseek.ts
Add top-level stability to DeepSeek models and stability to provider entries; activate previously-commented NEBIUS provider for deepseek-r1-0528.
Gateway schema & response mapping
apps/gateway/src/models/models.ts
Extend model schema with optional stability; include stability on model objects and on provider items (`provider.stability
API tests for stability
apps/gateway/src/models/models.spec.ts
Add tests asserting /v1/models returns stability metadata, values are within allowed enum, and specific DeepSeek model stability expectations.
UI: pages, lists, selector, provider card
apps/ui/src/components/playground/model-selector.tsx, apps/ui/src/components/models/all-models.tsx, apps/ui/src/components/models/models-list.tsx, apps/ui/src/app/models/[name]/page.tsx, apps/ui/src/components/models/provider-card.tsx
Propagate stability into local types/props, add helpers to map stability → badge props and warning visibility, compute most-unstable across model/providers, and render stability badges and AlertTriangle warnings across dropdowns, lists, cards, provider cards, and model pages.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Web UI
  participant GW as Gateway (/v1/models)
  participant REG as Models registry

  User->>UI: open model list / selector / model page
  UI->>GW: GET /v1/models
  GW->>REG: read model definitions (includes optional `stability`)
  REG-->>GW: return models + providers (stability fields)
  GW-->>UI: 200 OK with models[].stability and providers[].stability
  UI->>UI: compute most-unstable stability per model/provider
  UI->>User: render badges and warning icons
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • smakosh

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 34a1403 and 6463994.

📒 Files selected for processing (2)
  • apps/gateway/src/models/models.spec.ts (1 hunks)
  • packages/models/src/models/deepseek.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/models/src/models/deepseek.ts
  • apps/gateway/src/models/models.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build / run
  • GitHub Check: e2e / run
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/deepseek-stability-flags

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (9)
packages/models/src/models.ts (2)

89-90: Single source of truth for stability values

To avoid duplicating literal strings across packages (e.g., Zod enums in gateway), export the values as a const tuple and derive the type from it.

+export const stabilityLevels = ["stable", "beta", "unstable", "experimental"] as const;
-export type StabilityLevel = "stable" | "beta" | "unstable" | "experimental";
+export type StabilityLevel = (typeof stabilityLevels)[number];

132-140: Default is documented, not enforced

Docs say default = "stable" when unspecified. That’s fine, but consider a small helper in the shared package to compute effective stability to keep callers consistent.

Example:

export const getEffectiveStability = (stability?: StabilityLevel): StabilityLevel =>
  stability ?? "stable";
apps/gateway/src/models/models.ts (1)

65-65: Use shared stability tuple in Zod schema

Leverage a shared exported tuple to prevent drift between the TS type and the OpenAPI schema.

-  stability: z.enum(["stable", "beta", "unstable", "experimental"]).optional(),
+  stability: z.enum(stabilityLevels).optional(),

Add import near the existing models import:

import { stabilityLevels } from "@llmgateway/models";
apps/gateway/src/models/models.spec.ts (1)

178-199: Reuse shared stability tuple in tests

Import the shared stability list to avoid hardcoding values in tests.

-    const validStabilityValues = [
-      "stable","beta","unstable","experimental",undefined,
-    ];
+    const validStabilityValues = [...stabilityLevels, undefined];

Add:

import { stabilityLevels } from "@llmgateway/models";
apps/ui/src/components/playground/model-selector.tsx (5)

56-80: Computed color is never applied

getStabilityBadgeProps returns color, but it’s not used when rendering the Badge. Either remove it or apply it to className.

No-op removal:

-  color: "text-blue-600",
+  // color removed

Or apply the color in the render (see suggestion on Lines 189-201).


81-84: Return a boolean explicitly

Current expression yields string | false. Make it a strict boolean for clarity.

-const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
-  return stability && ["unstable", "experimental"].includes(stability);
-};
+const shouldShowStabilityWarning = (stability?: StabilityLevel): boolean => {
+  return stability === "unstable" || stability === "experimental";
+};

129-132: Minor a11y: label the warning icon

Add aria-label/title so screen readers convey the warning.

-  <AlertTriangle className="h-4 w-4 text-orange-500" />
+  <AlertTriangle
+    className="h-4 w-4 text-orange-500"
+    aria-label="Model marked unstable or experimental"
+    title="Model marked unstable or experimental"
+  />

155-158: Repeat a11y improvement for list items

Same accessibility suggestion for the dropdown items.

-  <AlertTriangle className="h-4 w-4 text-orange-500" />
+  <AlertTriangle
+    className="h-4 w-4 text-orange-500"
+    aria-label="Model marked unstable or experimental"
+    title="Model marked unstable or experimental"
+  />

189-201: Apply the computed color to the Badge

Use the color returned by getStabilityBadgeProps so badges render with the intended styling.

-  <Badge
-    variant={stabilityProps.variant}
-    className="text-xs px-1 py-0"
-  >
+  <Badge
+    variant={stabilityProps.variant}
+    className={`text-xs px-1 py-0 ${stabilityProps.color}`}
+  >
     {stabilityProps.label}
   </Badge>

Optionally, avoid the IIFE for readability:

- {(() => {
-   const stabilityProps = getStabilityBadgeProps(model.stability);
-   return stabilityProps ? ( ... ) : null;
- })()}
+ {(() => {
+   const props = getStabilityBadgeProps(model.stability);
+   return props ? ( ...use props... ) : null;
+ })()}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between aa54870 and 3180ebc.

📒 Files selected for processing (5)
  • apps/gateway/src/models/models.spec.ts (1 hunks)
  • apps/gateway/src/models/models.ts (2 hunks)
  • apps/ui/src/components/playground/model-selector.tsx (7 hunks)
  • packages/models/src/models.ts (2 hunks)
  • packages/models/src/models/deepseek.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/gateway/src/models/models.ts
  • apps/gateway/src/models/models.spec.ts
  • packages/models/src/models.ts
  • apps/ui/src/components/playground/model-selector.tsx
  • packages/models/src/models/deepseek.ts
**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts}: Use drizzle with the latest object syntax for database operations
For read queries, always use db().query.<table>.findMany() or db().query.<table>.findFirst()

Files:

  • apps/gateway/src/models/models.ts
  • apps/gateway/src/models/models.spec.ts
  • packages/models/src/models.ts
  • packages/models/src/models/deepseek.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use as any or : any in TypeScript files.

Files:

  • apps/gateway/src/models/models.ts
  • apps/gateway/src/models/models.spec.ts
  • packages/models/src/models.ts
  • apps/ui/src/components/playground/model-selector.tsx
  • packages/models/src/models/deepseek.ts
apps/{api,gateway}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{api,gateway}/**/*.{ts,tsx}: Use Zod schemas for validation in backend services
Use Hono as the web framework for backend routes and middleware

apps/{api,gateway}/**/*.{ts,tsx}: Use Drizzle with the latest object syntax for database operations
For read queries, use db().query.

.findMany() or db().query.
.findFirst()

Files:

  • apps/gateway/src/models/models.ts
  • apps/gateway/src/models/models.spec.ts
apps/gateway/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

In apps/gateway (Hono), always use Hono + Zod + OpenAPI for validation and typesafety

Files:

  • apps/gateway/src/models/models.ts
  • apps/gateway/src/models/models.spec.ts
**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Name unit test files with the .spec.ts suffix

Files:

  • apps/gateway/src/models/models.spec.ts
apps/ui/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In apps/ui (a tanstack router project), always use navigate() for navigation

Files:

  • apps/ui/src/components/playground/model-selector.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation’s router for programmatic navigation
Use localStorage (not cookies) for client-side data persistence in the UI
Use TanStack Query for client-side state management in the UI
Use Radix UI with Tailwind CSS for UI components and styling in the frontend

Files:

  • apps/ui/src/components/playground/model-selector.tsx
apps/{ui,docs}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/ui/src/components/playground/model-selector.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/ui/**/*.{ts,tsx,js,jsx}: In apps/ui (Next.js App Router), use next/link for links
In apps/ui, use next/navigation router.push/replace or redirect() for programmatic navigation
In apps/ui, never use window.location for navigation

Files:

  • apps/ui/src/components/playground/model-selector.tsx
🧬 Code graph analysis (2)
apps/gateway/src/models/models.spec.ts (1)
apps/gateway/src/index.ts (1)
  • app (43-43)
apps/ui/src/components/playground/model-selector.tsx (1)
packages/models/src/models.ts (1)
  • StabilityLevel (89-89)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build / run
  • GitHub Check: e2e / run
🔇 Additional comments (10)
apps/gateway/src/models/models.ts (1)

224-225: Pass-through is correct; consider explicit default only if desired API-wise

Forwarding model.stability as optional matches tests and UI behavior. If you later want the API to always surface an explicit value, switch to model.stability ?? "stable" and update tests accordingly.

apps/gateway/src/models/models.spec.ts (1)

130-177: Solid targeted assertions for DeepSeek models

Good coverage verifying presence and expected flags; aligns with current model definitions.

apps/ui/src/components/playground/model-selector.tsx (3)

1-7: Imports look good

Type import for StabilityLevel and AlertTriangle usage are appropriate.


23-28: LocalModel extended correctly

Adding stability?: StabilityLevel to the local shape keeps UI typed.


102-103: Propagation looks correct

Forwarding typedModel.stability preserves typing.

packages/models/src/models/deepseek.ts (5)

10-10: DeepSeek V3 marked unstable — OK

Flag aligns with PR intent to surface caution on V3.


45-45: DeepSeek R1 marked unstable — OK

Consistent with instability designation.


92-92: DeepSeek R1-0528 marked unstable — OK

Matches the status communicated in the PR.


118-129: Nebius provider re-enabled for R1-0528

Provider block looks complete (pricing, context, streaming). Good addition.


139-139: R1 Distill set to beta — OK

Appropriate mid-tier stability signal.

steebchen and others added 2 commits August 30, 2025 23:46
- Add stability badges to models table view (new Stability column)
- Add stability badges to models grid view
- Add stability warnings (triangle icons) for unstable/experimental models
- Add stability information to individual model detail pages
- Display warning banners on unstable model detail pages
- Update ModelSelector, AllModels, ModelsList, and model detail page components
- Consistent color coding: blue for beta, red for unstable, orange for experimental

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Introduce optional stability enum (stable, beta, unstable, experimental) to model and provider schemas
- Update deepseek models with stability levels
- Enhance UI components (AllModels, ModelSelector) to show stability badges and warnings based on most unstable stability level
- Add utility function to determine most unstable stability level from model and its providers
- Improve model API to include stability information

This feature enables better visibility of model stability status across the system, helping users make informed decisions.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
apps/gateway/src/models/models.ts (2)

218-221: Guard against -Infinity when no providers

Math.max(...[]) yields -Infinity; current OR-coalescing won’t catch it.

- context_length:
-   Math.max(...model.providers.map((p) => p.contextSize || 0)) ||
-   undefined,
+ const maxContext = Math.max(0, ...model.providers.map((p) => p.contextSize || 0));
+ context_length: maxContext || undefined,

254-276: Remove any-casts (violates “no any” guideline) and use typed fields

This keeps strong typing and matches ProviderModelMapping.

-function getSupportedParametersFromModel(model: ModelDefinition): string[] {
+function getSupportedParametersFromModel(model: ModelDefinition): string[] {
@@
-  for (const provider of model.providers) {
-    const supportedParameters = (provider as any)?.supportedParameters as
-      | string[]
-      | undefined;
+  for (const provider of model.providers) {
+    const supportedParameters = provider.supportedParameters;
     if (supportedParameters && supportedParameters.length > 0) {
       const params = [...supportedParameters];
-      // If any provider supports reasoning, expose the reasoning parameter
-      if (model.providers.some((p: any) => p?.reasoning)) {
+      // If any provider supports reasoning, expose the reasoning parameter
+      if (model.providers.some((p) => p.reasoning)) {
         if (!params.includes("reasoning")) {
           params.push("reasoning");
         }
       }
       return params;
     }
   }
@@
-  if (model.providers.some((p: any) => p?.reasoning)) {
+  if (model.providers.some((p) => p.reasoning)) {
     params.push("reasoning");
   }
   return params;
 }
apps/ui/src/components/models/all-models.tsx (1)

446-485: Eliminate any in getCapabilityIcons’s model param.
Conform to the TS guideline. The function only reads output, so narrow the type.

-const getCapabilityIcons = (provider: ProviderModelMapping, model?: any) => {
+const getCapabilityIcons = (
+  provider: ProviderModelMapping,
+  model?: Pick<ModelDefinition, "output">,
+) => {
🧹 Nitpick comments (10)
packages/models/src/models.ts (1)

97-98: Export shared stability constants to prevent drift across API/UI

Provide a single source of truth and reuse in Zod enums and UI helpers.

Apply:

-export type StabilityLevel = "stable" | "beta" | "unstable" | "experimental";
+export const STABILITY_LEVELS = [
+  "stable",
+  "beta",
+  "unstable",
+  "experimental",
+] as const;
+export type StabilityLevel = (typeof STABILITY_LEVELS)[number];
+// For UI "most-unstable" computations
+export const STABILITY_ORDER = [
+  "experimental",
+  "unstable",
+  "beta",
+  "stable",
+] as const;
apps/ui/src/components/playground/model-selector.tsx (2)

82-84: Ensure boolean return

Current expression infers string | false. Make it a strict boolean.

-const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
-  return stability && ["unstable", "experimental"].includes(stability);
-};
+const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
+  return !!stability && (stability === "unstable" || stability === "experimental");
+};

86-95: De-duplicate stability ordering logic

This ordering is repeated across UI files. Import a shared STABILITY_ORDER (or helper) from models to avoid divergence.

Would you like me to extract a small UI util (e.g., apps/ui/src/lib/stability.ts) and update all three sites?

Also applies to: 96-111

apps/ui/src/components/models/models-list.tsx (2)

38-40: Return a boolean

Same minor typing nit as elsewhere.

-const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
-  return stability && ["unstable", "experimental"].includes(stability);
-};
+const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
+  return !!stability && (stability === "unstable" || stability === "experimental");
+};

13-36: Extract shared helpers

getStabilityBadgeProps/shouldShowStabilityWarning appear in multiple components; consider a single shared helper to keep UX consistent.

Also applies to: 46-51, 58-60

apps/gateway/src/models/models.ts (1)

3-7: Reuse shared enum values for Zod

Avoid string-literal duplication by importing STABILITY_LEVELS from models.

-import {
-  models as modelsList,
-  providers,
-  type ProviderModelMapping,
-  type ModelDefinition,
-} from "@llmgateway/models";
+import {
+  models as modelsList,
+  providers,
+  STABILITY_LEVELS,
+  type ProviderModelMapping,
+  type ModelDefinition,
+} from "@llmgateway/models";
@@
-      stability: z
-        .enum(["stable", "beta", "unstable", "experimental"])
-        .optional(),
+      stability: z.enum(STABILITY_LEVELS).optional(),
@@
-  stability: z.enum(["stable", "beta", "unstable", "experimental"]).optional(),
+  stability: z.enum(STABILITY_LEVELS).optional(),

Also applies to: 46-49, 68-69

apps/ui/src/app/models/[name]/page.tsx (3)

16-23: Fix PageProps typing; params is not a Promise

Align with Next.js App Router conventions.

-interface PageProps {
-  params: Promise<{ name: string }>;
-}
+interface PageProps {
+  params: { name: string };
+}
@@
-export default async function ModelPage({ params }: PageProps) {
-  const { name } = await params;
+export default async function ModelPage({ params }: PageProps) {
+  const { name } = params;

57-59: Return a boolean

-const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
-  return stability && ["unstable", "experimental"].includes(stability);
-};
+const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
+  return !!stability && (stability === "unstable" || stability === "experimental");
+};

205-212: Fix generateMetadata params typing; remove unnecessary await

-export async function generateMetadata({ params }: PageProps) {
-  const { name } = await params;
+export async function generateMetadata({ params }: { params: { name: string } }) {
+  const { name } = params;
apps/ui/src/components/models/all-models.tsx (1)

1087-1087: Remove stray console.log from UI.
Avoid noisy logs in production.

-                                                    console.log(provider.providerId, ProviderIcon);
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3180ebc and 7fc7e16.

📒 Files selected for processing (7)
  • apps/gateway/src/models/models.ts (4 hunks)
  • apps/ui/src/app/models/[name]/page.tsx (4 hunks)
  • apps/ui/src/components/models/all-models.tsx (8 hunks)
  • apps/ui/src/components/models/models-list.tsx (1 hunks)
  • apps/ui/src/components/playground/model-selector.tsx (8 hunks)
  • packages/models/src/models.ts (2 hunks)
  • packages/models/src/models/deepseek.ts (13 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/ui/src/components/models/models-list.tsx
  • apps/ui/src/components/models/all-models.tsx
  • apps/gateway/src/models/models.ts
  • apps/ui/src/app/models/[name]/page.tsx
  • apps/ui/src/components/playground/model-selector.tsx
  • packages/models/src/models.ts
  • packages/models/src/models/deepseek.ts
apps/ui/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In apps/ui (a tanstack router project), always use navigate() for navigation

Files:

  • apps/ui/src/components/models/models-list.tsx
  • apps/ui/src/components/models/all-models.tsx
  • apps/ui/src/app/models/[name]/page.tsx
  • apps/ui/src/components/playground/model-selector.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use as any or : any in TypeScript files.

Files:

  • apps/ui/src/components/models/models-list.tsx
  • apps/ui/src/components/models/all-models.tsx
  • apps/gateway/src/models/models.ts
  • apps/ui/src/app/models/[name]/page.tsx
  • apps/ui/src/components/playground/model-selector.tsx
  • packages/models/src/models.ts
  • packages/models/src/models/deepseek.ts
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation’s router for programmatic navigation
Use localStorage (not cookies) for client-side data persistence in the UI
Use TanStack Query for client-side state management in the UI
Use Radix UI with Tailwind CSS for UI components and styling in the frontend

Files:

  • apps/ui/src/components/models/models-list.tsx
  • apps/ui/src/components/models/all-models.tsx
  • apps/ui/src/app/models/[name]/page.tsx
  • apps/ui/src/components/playground/model-selector.tsx
apps/{ui,docs}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/ui/src/components/models/models-list.tsx
  • apps/ui/src/components/models/all-models.tsx
  • apps/ui/src/app/models/[name]/page.tsx
  • apps/ui/src/components/playground/model-selector.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/ui/**/*.{ts,tsx,js,jsx}: In apps/ui (Next.js App Router), use next/link for links
In apps/ui, use next/navigation router.push/replace or redirect() for programmatic navigation
In apps/ui, never use window.location for navigation

Files:

  • apps/ui/src/components/models/models-list.tsx
  • apps/ui/src/components/models/all-models.tsx
  • apps/ui/src/app/models/[name]/page.tsx
  • apps/ui/src/components/playground/model-selector.tsx
**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts}: Use drizzle with the latest object syntax for database operations
For read queries, always use db().query.<table>.findMany() or db().query.<table>.findFirst()

Files:

  • apps/gateway/src/models/models.ts
  • packages/models/src/models.ts
  • packages/models/src/models/deepseek.ts
apps/{api,gateway}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{api,gateway}/**/*.{ts,tsx}: Use Zod schemas for validation in backend services
Use Hono as the web framework for backend routes and middleware

apps/{api,gateway}/**/*.{ts,tsx}: Use Drizzle with the latest object syntax for database operations
For read queries, use db().query.

.findMany() or db().query.
.findFirst()

Files:

  • apps/gateway/src/models/models.ts
apps/gateway/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

In apps/gateway (Hono), always use Hono + Zod + OpenAPI for validation and typesafety

Files:

  • apps/gateway/src/models/models.ts
🧠 Learnings (2)
📚 Learning: 2025-08-29T15:31:07.044Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:31:07.044Z
Learning: Applies to apps/ui/**/*.{ts,tsx,js,jsx} : In apps/ui (Next.js App Router), use next/link for links

Applied to files:

  • apps/ui/src/components/models/all-models.tsx
📚 Learning: 2025-08-29T02:12:34.132Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-29T02:12:34.132Z
Learning: Applies to apps/ui/**/*.{ts,tsx} : In the Next.js UI, use next/link for links and next/navigation’s router for programmatic navigation

Applied to files:

  • apps/ui/src/components/models/all-models.tsx
🧬 Code graph analysis (4)
apps/ui/src/components/models/models-list.tsx (1)
packages/models/src/models.ts (3)
  • StabilityLevel (97-97)
  • models (150-165)
  • ModelDefinition (99-148)
apps/ui/src/components/models/all-models.tsx (1)
packages/models/src/models.ts (1)
  • StabilityLevel (97-97)
apps/ui/src/app/models/[name]/page.tsx (1)
packages/models/src/models.ts (2)
  • ModelDefinition (99-148)
  • StabilityLevel (97-97)
apps/ui/src/components/playground/model-selector.tsx (1)
packages/models/src/models.ts (1)
  • StabilityLevel (97-97)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: e2e / run
  • GitHub Check: build / run
🔇 Additional comments (10)
apps/ui/src/components/models/all-models.tsx (4)

22-23: LGTM: new icon import is correct and used.


64-69: LGTM: type-only imports are correct and match packages/models.


813-814: LGTM: Stability column addition.


824-827: LGTM: warning icon uses the most-unstable value (model+providers).

packages/models/src/models/deepseek.ts (6)

10-10: LGTM: model-level stability set to “unstable” for DeepSeek V3.


23-37: LGTM: provider-level stability reflects cloudrift “unstable” and nebius “beta”.


47-87: LGTM: R1 marked unstable overall; provider stabilities differentiate deepseek (stable) vs others.


97-137: LGTM: R1-0528 unstable overall; Nebius provider re-enabled with beta stability.


147-174: LGTM: Distill model set to beta; provider stabilities align (groq stable, nebius beta).


179-201: LGTM: V3.1 left without a stability field (treated as stable by default).

Comment on lines +24 to 27
const modelDef = modelDefinitions.find(
(m) => m.id === decodedName,
) as ModelDefinition;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid unsafe cast; let notFound() narrow the type

-const modelDef = modelDefinitions.find(
-  (m) => m.id === decodedName,
-) as ModelDefinition;
+const modelDef = modelDefinitions.find((m) => m.id === decodedName);

Type narrows after notFound().

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const modelDef = modelDefinitions.find(
(m) => m.id === decodedName,
) as ModelDefinition;
const modelDef = modelDefinitions.find((m) => m.id === decodedName);
🤖 Prompt for AI Agents
In apps/ui/src/app/models/[name]/page.tsx around lines 24 to 27, remove the
unsafe "as ModelDefinition" cast on modelDef and instead let TypeScript narrow
the type by checking for a falsy value and calling notFound() when modelDef is
undefined; i.e., assign modelDef directly from modelDefinitions.find(...), then
immediately if (!modelDef) notFound(), so subsequent code treats modelDef as a
ModelDefinition without an explicit cast.

Comment on lines +87 to 104
{(() => {
const stabilityProps = getStabilityBadgeProps(
modelDef.stability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-sm px-3 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-sm px-3 py-1">
STABLE
</Badge>
);
})()}
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Apply badge color classes

-<Badge
-  variant={stabilityProps.variant}
-  className="text-sm px-3 py-1"
->
+<Badge
+  variant={stabilityProps.variant}
+  className={`text-sm px-3 py-1 ${stabilityProps.color ?? ""}`}
+>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{(() => {
const stabilityProps = getStabilityBadgeProps(
modelDef.stability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-sm px-3 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-sm px-3 py-1">
STABLE
</Badge>
);
})()}
</div>
{(() => {
const stabilityProps = getStabilityBadgeProps(
modelDef.stability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className={`text-sm px-3 py-1 ${stabilityProps.color ?? ""}`}
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-sm px-3 py-1">
STABLE
</Badge>
);
})()}
🤖 Prompt for AI Agents
In apps/ui/src/app/models/[name]/page.tsx around lines 87 to 104, the badge
currently always uses the same styling and doesn't apply the color classes
provided by the stability metadata; update the JSX so the Badge receives the
stability color classes by merging the existing "text-sm px-3 py-1" with the
stabilityProps.className (or stabilityProps.colorClass) when stabilityProps
exists, and when falling back to the default branch provide the appropriate
default color class(es) instead of only "outline" so the badge shows the
intended color; ensure you keep the variant prop and properly concatenate class
strings safely (e.g., conditional join) so absent fields don't break rendering.

Comment thread apps/ui/src/components/models/all-models.tsx
Comment on lines +405 to +427
const getMostUnstableStability = (model: any): StabilityLevel | undefined => {
const stabilityLevels: StabilityLevel[] = [
"experimental",
"unstable",
"beta",
"stable",
];

// Get all stability levels (model-level and provider-level)
const allStabilities = [
model.stability,
...model.providers.map((p: any) => p.stability || model.stability),
].filter(Boolean) as StabilityLevel[];

// Return the most unstable level
for (const level of stabilityLevels) {
if (allStabilities.includes(level)) {
return level;
}
}

return undefined;
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid any and tighten types for stability computation.
Follow the repo guideline “never use any in TS”. Also, no need to duplicate model-level stability for each provider.

-const getMostUnstableStability = (model: any): StabilityLevel | undefined => {
+const getMostUnstableStability = (
+  model: ModelWithProviders,
+): StabilityLevel | undefined => {
   const stabilityLevels: StabilityLevel[] = [
     "experimental",
     "unstable",
     "beta",
     "stable",
   ];
 
-  // Get all stability levels (model-level and provider-level)
-  const allStabilities = [
-    model.stability,
-    ...model.providers.map((p: any) => p.stability || model.stability),
-  ].filter(Boolean) as StabilityLevel[];
+  // Collect model- and provider-level stabilities
+  const allStabilities = [
+    model.stability,
+    ...model.providers.map((p) => p.stability),
+  ].filter((s): s is StabilityLevel => Boolean(s));
 
   // Return the most unstable level
   for (const level of stabilityLevels) {
     if (allStabilities.includes(level)) {
       return level;
     }
   }
 
   return undefined;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getMostUnstableStability = (model: any): StabilityLevel | undefined => {
const stabilityLevels: StabilityLevel[] = [
"experimental",
"unstable",
"beta",
"stable",
];
// Get all stability levels (model-level and provider-level)
const allStabilities = [
model.stability,
...model.providers.map((p: any) => p.stability || model.stability),
].filter(Boolean) as StabilityLevel[];
// Return the most unstable level
for (const level of stabilityLevels) {
if (allStabilities.includes(level)) {
return level;
}
}
return undefined;
};
const getMostUnstableStability = (
model: ModelWithProviders,
): StabilityLevel | undefined => {
const stabilityLevels: StabilityLevel[] = [
"experimental",
"unstable",
"beta",
"stable",
];
// Collect model- and provider-level stabilities
const allStabilities = [
model.stability,
...model.providers.map((p) => p.stability),
].filter((s): s is StabilityLevel => Boolean(s));
// Return the most unstable level
for (const level of stabilityLevels) {
if (allStabilities.includes(level)) {
return level;
}
}
return undefined;
};
🤖 Prompt for AI Agents
In apps/ui/src/components/models/all-models.tsx around lines 405 to 427, tighten
types by replacing all `any` with concrete types: define or import a
StabilityLevel union type (e.g. "experimental" | "unstable" | "beta" |
"stable"), type the model parameter as an interface with stability?:
StabilityLevel and providers: { stability?: StabilityLevel }[], and update the
function signature accordingly; compute provider stabilities without duplicating
the model stability for each provider by mapping providers to (p.stability ??
model.stability), filter out undefined, and iterate the predefined stability
priority to return the first match or undefined.

Comment on lines +979 to +999
<TableCell className="text-center">
{(() => {
const mostUnstableStability = getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-2 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</TableCell>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Apply computed color to the Stability badge in the table.
Currently the color hint isn’t used; this implements the PR’s “blue/red/orange” requirement.

-<Badge
-  variant={stabilityProps.variant}
-  className="text-xs px-2 py-1"
->
+<Badge
+  variant={stabilityProps.variant}
+  className={cn("text-xs px-2 py-1", stabilityProps.color)}
+>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<TableCell className="text-center">
{(() => {
const mostUnstableStability = getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-2 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</TableCell>
<TableCell className="text-center">
{(() => {
const mostUnstableStability = getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className={cn("text-xs px-2 py-1", stabilityProps.color)}
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</TableCell>
🤖 Prompt for AI Agents
In apps/ui/src/components/models/all-models.tsx around lines 979 to 999, the
computed stability color (from getStabilityBadgeProps / mostUnstableStability)
is not being applied to the Badge; update the JSX so the Badge uses the
stabilityProps color (e.g., pass stabilityProps.color to the Badge via its color
prop or append a corresponding color className) for the non-empty branch and
ensure the fallback badge uses a neutral/outline color; keep variant and
existing sizing classes intact.

Comment thread apps/ui/src/components/models/all-models.tsx
Comment on lines +1182 to +1202
<div>
<div className="font-medium mb-2 text-sm">Stability:</div>
{(() => {
const mostUnstableStability = getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-2 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Apply computed color to the Stability badge in the grid.
Same issue as the table badge.

-<Badge
-  variant={stabilityProps.variant}
-  className="text-xs px-2 py-1"
->
+<Badge
+  variant={stabilityProps.variant}
+  className={cn("text-xs px-2 py-1", stabilityProps.color)}
+>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div>
<div className="font-medium mb-2 text-sm">Stability:</div>
{(() => {
const mostUnstableStability = getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-2 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</div>
<div>
<div className="font-medium mb-2 text-sm">Stability:</div>
{(() => {
const mostUnstableStability = getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className={cn("text-xs px-2 py-1", stabilityProps.color)}
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</div>
🤖 Prompt for AI Agents
In apps/ui/src/components/models/all-models.tsx around lines 1182 to 1202, the
Stability badge rendered in the grid does not apply the computed color/variant
from getStabilityBadgeProps like the table badge does; update the JSX so when
stabilityProps exists you pass stabilityProps.variant (and any color/class names
it carries) to the Badge component and apply the same text/px/py classes, and
when stabilityProps is missing keep the fallback Badge with variant="outline";
ensure you reuse the same badge-props extraction (mostUnstableStability ->
getStabilityBadgeProps) and consistently apply variant/className so the computed
color shows in the grid.

Comment on lines +61 to +75
const stabilityProps = getStabilityBadgeProps(model.stability);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-2 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Apply badge color classes

Color hint is computed but not applied; STABLE fallback is fine.

-<Badge
-  variant={stabilityProps.variant}
-  className="text-xs px-2 py-1"
->
+<Badge
+  variant={stabilityProps.variant}
+  className={`text-xs px-2 py-1 ${stabilityProps.color ?? ""}`}
+>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const stabilityProps = getStabilityBadgeProps(model.stability);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-2 py-1"
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
})()}
</div>
const stabilityProps = getStabilityBadgeProps(model.stability);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className={`text-xs px-2 py-1 ${stabilityProps.color ?? ""}`}
>
{stabilityProps.label}
</Badge>
) : (
<Badge variant="outline" className="text-xs px-2 py-1">
STABLE
</Badge>
);
🤖 Prompt for AI Agents
In apps/ui/src/components/models/models-list.tsx around lines 61 to 75, the
computed badge color hint from getStabilityBadgeProps is not being applied to
the Badge components; update the JSX so the Badge receives the computed
className (e.g., stabilityProps.className or a dedicated color property) in
addition to existing classes when stabilityProps is present, and apply the
corresponding STABLE fallback color class to the else branch instead of only
using variant="outline", ensuring you merge the computed color classes with
"text-xs px-2 py-1" rather than overwriting them.

Comment on lines +217 to +231
{(() => {
const mostUnstableStability =
getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-1 py-0"
>
{stabilityProps.label}
</Badge>
) : null;
})()}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Badge color prop is computed but not applied

The returned color class isn’t used; experimental won’t show orange, beta won’t show blue.

-const stabilityProps = getStabilityBadgeProps(
-  mostUnstableStability,
-);
+const stabilityProps = getStabilityBadgeProps(mostUnstableStability);
 return stabilityProps ? (
   <Badge
     variant={stabilityProps.variant}
-    className="text-xs px-1 py-0"
+    className={`text-xs px-1 py-0 ${stabilityProps.color ?? ""}`}
   >
     {stabilityProps.label}
   </Badge>
 ) : null;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{(() => {
const mostUnstableStability =
getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(
mostUnstableStability,
);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className="text-xs px-1 py-0"
>
{stabilityProps.label}
</Badge>
) : null;
})()}
{(() => {
const mostUnstableStability = getMostUnstableStability(model);
const stabilityProps = getStabilityBadgeProps(mostUnstableStability);
return stabilityProps ? (
<Badge
variant={stabilityProps.variant}
className={`text-xs px-1 py-0 ${stabilityProps.color ?? ""}`}
>
{stabilityProps.label}
</Badge>
) : null;
})()}
🤖 Prompt for AI Agents
In apps/ui/src/components/playground/model-selector.tsx around lines 217-231,
the stability badge's computed color class from getStabilityBadgeProps is not
included in the rendered Badge, so experimental/beta colors won't appear; update
the Badge to include the color class (e.g. stabilityProps.color or
stabilityProps.colorClass) merged with the existing className ("text-xs px-1
py-0") so the variant/label remain and the correct color styling is applied.

- Display stability badges for providers in the models list with appropriate styling.
- Show stability badges and warning icons (for unstable and experimental) in provider cards.
- Pass model stability info down to provider cards for consistent display.
- Enhance UI to better communicate provider stability levels to users.

Co-authored-by: terragon-labs[bot] <terragon-labs[bot]@users.noreply.github.com>
@steebchen steebchen changed the title feat(models): add stability flags for DeepSeek models feat(models): add stability flags for DeepSeek models with UI and API integration Aug 31, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/ui/src/components/models/models-list.tsx (2)

65-71: Apply computed color classes to provider stability badges.

Color hint from getStabilityBadgeProps isn’t applied, so badges won’t show blue/red/orange.

{stabilityProps && (
  <Badge
    variant={stabilityProps.variant}
-   className="text-xs px-1 py-0.5"
+   className={`text-xs px-1 py-0.5 ${stabilityProps.color ?? ""}`}
  >
    {stabilityProps.label}
  </Badge>
)}

81-90: Apply computed color classes to model-level stability badge; consider a subtle color for STABLE.

Same as provider badge; also optional: add a neutral/green tone for STABLE if desired.

return stabilityProps ? (
  <Badge
    variant={stabilityProps.variant}
-   className="text-xs px-2 py-1"
+   className={`text-xs px-2 py-1 ${stabilityProps.color ?? ""}`}
  >
    {stabilityProps.label}
  </Badge>
) : (
-  <Badge variant="outline" className="text-xs px-2 py-1">
+  <Badge variant="outline" className="text-xs px-2 py-1 text-emerald-600">
    STABLE
  </Badge>
);
🧹 Nitpick comments (7)
apps/ui/src/components/models/provider-card.tsx (4)

101-106: Apply computed color classes to Badge.

getStabilityBadgeProps returns a color hint that isn’t applied, so badges won’t reflect blue/red/orange hues.

Apply the color class:

-<Badge
-  variant={stabilityProps.variant}
-  className="text-xs px-2 py-0.5"
->
+<Badge
+  variant={stabilityProps.variant}
+  className={`text-xs px-2 py-0.5 ${stabilityProps.color ?? ""}`}
+>

94-96: Match warning icon color to stability (red for unstable, orange for experimental).

The icon is always orange; this mismatches the red branding for “unstable.”

-{shouldShowStabilityWarning(providerStability) && (
-  <AlertTriangle className="h-4 w-4 text-orange-500" />
-)}
+{shouldShowStabilityWarning(providerStability) && (
+  <AlertTriangle
+    className={`h-4 w-4 ${
+      providerStability === "unstable" ? "text-red-500" : "text-orange-500"
+    }`}
+  />
+)}

63-65: Return a strict boolean from helper.

Minor: coerce to boolean to avoid boolean | undefined inference.

-const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
-  return stability && ["unstable", "experimental"].includes(stability);
-};
+const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
+  return !!stability && ["unstable", "experimental"].includes(stability);
+};

38-61: Deduplicate stability helpers across components.

getStabilityBadgeProps (and shouldShowStabilityWarning) are duplicated in multiple files. Consider a shared utility (e.g., components/models/stability.ts) to keep variants/labels/colors consistent.

apps/ui/src/components/models/models-list.tsx (3)

48-50: Match warning icon color to stability (red for unstable, orange for experimental).

The alert icon is always orange; align with badge colors.

-{shouldShowStabilityWarning(model.stability) && (
-  <AlertTriangle className="h-4 w-4 text-orange-500" />
-)}
+{shouldShowStabilityWarning(model.stability) && (
+  <AlertTriangle
+    className={`h-4 w-4 ${
+      model.stability === "unstable" ? "text-red-500" : "text-orange-500"
+    }`}
+  />
+)}

13-40: Centralize stability helpers to avoid drift across files.

getStabilityBadgeProps and shouldShowStabilityWarning mirror logic in other components. Extract to a shared utility for consistency.


38-40: Return a strict boolean from helper.

Minor boolean coercion for clarity.

-const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
-  return stability && ["unstable", "experimental"].includes(stability);
-};
+const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
+  return !!stability && ["unstable", "experimental"].includes(stability);
+};
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc7e16 and 98fa9b8.

📒 Files selected for processing (3)
  • apps/ui/src/app/models/[name]/page.tsx (5 hunks)
  • apps/ui/src/components/models/models-list.tsx (1 hunks)
  • apps/ui/src/components/models/provider-card.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/ui/src/app/models/[name]/page.tsx
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/ui/src/components/models/provider-card.tsx
  • apps/ui/src/components/models/models-list.tsx
apps/ui/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In apps/ui (a tanstack router project), always use navigate() for navigation

Files:

  • apps/ui/src/components/models/provider-card.tsx
  • apps/ui/src/components/models/models-list.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use as any or : any in TypeScript files.

Files:

  • apps/ui/src/components/models/provider-card.tsx
  • apps/ui/src/components/models/models-list.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation’s router for programmatic navigation
Use localStorage (not cookies) for client-side data persistence in the UI
Use TanStack Query for client-side state management in the UI
Use Radix UI with Tailwind CSS for UI components and styling in the frontend

Files:

  • apps/ui/src/components/models/provider-card.tsx
  • apps/ui/src/components/models/models-list.tsx
apps/{ui,docs}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/ui/src/components/models/provider-card.tsx
  • apps/ui/src/components/models/models-list.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/ui/**/*.{ts,tsx,js,jsx}: In apps/ui (Next.js App Router), use next/link for links
In apps/ui, use next/navigation router.push/replace or redirect() for programmatic navigation
In apps/ui, never use window.location for navigation

Files:

  • apps/ui/src/components/models/provider-card.tsx
  • apps/ui/src/components/models/models-list.tsx
🧬 Code graph analysis (2)
apps/ui/src/components/models/provider-card.tsx (2)
packages/models/src/models.ts (1)
  • StabilityLevel (97-97)
apps/ui/src/components/ui/providers-icons.tsx (1)
  • getProviderIcon (426-439)
apps/ui/src/components/models/models-list.tsx (1)
packages/models/src/models.ts (3)
  • StabilityLevel (97-97)
  • models (150-165)
  • ModelDefinition (99-148)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: e2e / run
  • GitHub Check: build / run
🔇 Additional comments (1)
apps/ui/src/components/models/provider-card.tsx (1)

36-36: Good precedence: provider overrides model stability.

Using provider.stability || modelStability ensures provider-level flags take precedence with a sensible model-level fallback.

Resolved conflict in packages/models/src/models.ts by including both:
- stability field for model stability levels
- supportsSystemRole field for system role message support

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@steebchen steebchen changed the title feat(models): add stability flags for DeepSeek models with UI and API integration feat(models): add stability flags for DeepSeek models with UI and API integration and comprehensive stability badge and warning support Aug 31, 2025
@steebchen steebchen changed the title feat(models): add stability flags for DeepSeek models with UI and API integration and comprehensive stability badge and warning support feat(models): add stability flags with ui Aug 31, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
apps/ui/src/components/models/all-models.tsx (5)

402-404: Return a boolean explicitly from shouldShowStabilityWarning.
Current expression yields boolean | StabilityLevel.

Apply:

-const shouldShowStabilityWarning = (stability?: StabilityLevel) => {
-  return stability && ["unstable", "experimental"].includes(stability);
-};
+const shouldShowStabilityWarning = (stability?: StabilityLevel): boolean => {
+  return stability === "unstable" || stability === "experimental";
+};

996-1016: Apply the computed color class to the table Stability badge.
Color hint isn’t used; badges look identical.

Apply:

 return stabilityProps ? (
   <Badge
     variant={stabilityProps.variant}
-    className="text-xs px-2 py-1"
+    className={cn("text-xs px-2 py-1", stabilityProps.color)}
   >
     {stabilityProps.label}
   </Badge>
 ) : (

1055-1057: Unify grid warning logic with table (consider provider stabilities).
Grid checks only model.stability; should use most-unstable across model+providers.

Apply:

-{shouldShowStabilityWarning(model.stability) && (
+{shouldShowStabilityWarning(getMostUnstableStability(model)) && (
   <AlertTriangle className="h-4 w-4 text-orange-500" />
 )}

1209-1229: Apply the computed color class to the grid Stability badge.
Same as table; also benefits from EXPERIMENTAL variant fix above.

Apply:

 return stabilityProps ? (
   <Badge
     variant={stabilityProps.variant}
-    className="text-xs px-2 py-1"
+    className={cn("text-xs px-2 py-1", stabilityProps.color)}
   >
     {stabilityProps.label}
   </Badge>
 ) : (

406-429: Avoid any and don’t duplicate model stability for providers.
Type the arg and only collect defined provider stabilities; no “any”.

Apply:

-const getMostUnstableStability = (model: any): StabilityLevel | undefined => {
+const getMostUnstableStability = (
+  model: ModelWithProviders,
+): StabilityLevel | undefined => {
   const stabilityLevels: StabilityLevel[] = [
     "experimental",
     "unstable",
     "beta",
     "stable",
   ];

-  // Get all stability levels (model-level and provider-level)
-  const allStabilities = [
-    model.stability,
-    ...model.providers.map((p: any) => p.stability || model.stability),
-  ].filter(Boolean) as StabilityLevel[];
+  // Collect model- and provider-level stabilities
+  const allStabilities = [
+    model.stability,
+    ...model.providers.map((p) => p.stability),
+  ].filter((s): s is StabilityLevel => Boolean(s));
🧹 Nitpick comments (4)
apps/ui/src/components/models/all-models.tsx (4)

817-817: Make the Stability header sticky like the others.
Consistency with adjacent sticky heads.

Apply:

-<TableHead className="text-center">Stability</TableHead>
+<TableHead className="text-center bg-background/95 backdrop-blur-sm border-b">Stability</TableHead>

1114-1114: Remove stray console.log from provider icon render.
Avoid console noise in production UI.

Apply:

-                                                  console.log(provider.providerId, ProviderIcon);

447-449: Avoid any in getCapabilityIcons signature.
Keep types consistent with shared models package.

Apply:

-const getCapabilityIcons = (provider: ProviderModelMapping, model?: any) => {
+const getCapabilityIcons = (
+  provider: ProviderModelMapping,
+  model?: ModelDefinition,
+) => {

1426-1427: Remove “as any” when counting free models.
free exists on ModelDefinition; no cast needed.

Apply:

-{
-  modelsWithProviders.filter((m) => (m as any).free).length
-}
+{modelsWithProviders.filter((m) => m.free).length}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 241265a and 34a1403.

📒 Files selected for processing (2)
  • apps/gateway/src/models/models.ts (4 hunks)
  • apps/ui/src/components/models/all-models.tsx (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/gateway/src/models/models.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/ui/src/components/models/all-models.tsx
apps/ui/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

In apps/ui (a tanstack router project), always use navigate() for navigation

Files:

  • apps/ui/src/components/models/all-models.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/general.mdc)

Never use as any or : any in TypeScript files.

Files:

  • apps/ui/src/components/models/all-models.tsx
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/ui/**/*.{ts,tsx}: In the Next.js UI, use next/link for links and next/navigation’s router for programmatic navigation
Use localStorage (not cookies) for client-side data persistence in the UI
Use TanStack Query for client-side state management in the UI
Use Radix UI with Tailwind CSS for UI components and styling in the frontend

Files:

  • apps/ui/src/components/models/all-models.tsx
apps/{ui,docs}/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use localStorage instead of cookies for client-side data persistence

Files:

  • apps/ui/src/components/models/all-models.tsx
apps/ui/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

apps/ui/**/*.{ts,tsx,js,jsx}: In apps/ui (Next.js App Router), use next/link for links
In apps/ui, use next/navigation router.push/replace or redirect() for programmatic navigation
In apps/ui, never use window.location for navigation

Files:

  • apps/ui/src/components/models/all-models.tsx
🧠 Learnings (1)
📚 Learning: 2025-08-29T15:31:07.044Z
Learnt from: CR
PR: theopenco/llmgateway#0
File: AGENTS.md:0-0
Timestamp: 2025-08-29T15:31:07.044Z
Learning: Applies to apps/ui/**/*.{ts,tsx,js,jsx} : In apps/ui (Next.js App Router), use next/link for links

Applied to files:

  • apps/ui/src/components/models/all-models.tsx
🧬 Code graph analysis (1)
apps/ui/src/components/models/all-models.tsx (1)
packages/models/src/models.ts (1)
  • StabilityLevel (101-101)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: build / run
  • GitHub Check: e2e / run
🔇 Additional comments (3)
apps/ui/src/components/models/all-models.tsx (3)

22-24: LGTM: Alert icon import is appropriate.
Importing AlertTriangle is consistent with the new stability warning UI.


65-69: LGTM: Typed imports.
Good to pull StabilityLevel and mapping types from the shared package.


836-841: LGTM: Warning icon uses most-unstable stability.
Correctly factors provider-level instability for the table row indicator.

Comment on lines +377 to +400
const getStabilityBadgeProps = (stability?: StabilityLevel) => {
switch (stability) {
case "beta":
return {
variant: "secondary" as const,
color: "text-blue-600",
label: "BETA",
};
case "unstable":
return {
variant: "destructive" as const,
color: "text-red-600",
label: "UNSTABLE",
};
case "experimental":
return {
variant: "destructive" as const,
color: "text-orange-600",
label: "EXPERIMENTAL",
};
default:
return null;
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Use neutral variant for EXPERIMENTAL (orange text), not “destructive” (red).
PR requires blue=beta, red=unstable, orange=experimental. “destructive” will look red; keep it neutral and color text orange.

Apply:

 case "experimental":
   return {
-    variant: "destructive" as const,
+    variant: "secondary" as const,
     color: "text-orange-600",
     label: "EXPERIMENTAL",
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const getStabilityBadgeProps = (stability?: StabilityLevel) => {
switch (stability) {
case "beta":
return {
variant: "secondary" as const,
color: "text-blue-600",
label: "BETA",
};
case "unstable":
return {
variant: "destructive" as const,
color: "text-red-600",
label: "UNSTABLE",
};
case "experimental":
return {
variant: "destructive" as const,
color: "text-orange-600",
label: "EXPERIMENTAL",
};
default:
return null;
}
};
const getStabilityBadgeProps = (stability?: StabilityLevel) => {
switch (stability) {
case "beta":
return {
variant: "secondary" as const,
color: "text-blue-600",
label: "BETA",
};
case "unstable":
return {
variant: "destructive" as const,
color: "text-red-600",
label: "UNSTABLE",
};
case "experimental":
return {
variant: "secondary" as const,
color: "text-orange-600",
label: "EXPERIMENTAL",
};
default:
return null;
}
};
🤖 Prompt for AI Agents
In apps/ui/src/components/models/all-models.tsx around lines 377 to 400, the
EXPERIMENTAL case currently returns variant: "destructive" which renders as red;
change it to a neutral variant (match beta's neutral style) by returning
variant: "secondary" as const while keeping color: "text-orange-600" and label:
"EXPERIMENTAL" so the badge appears orange but not destructive.

Simplifies deepseek models by removing unnecessary stability fields.
@steebchen
steebchen enabled auto-merge August 31, 2025 17:26
@steebchen
steebchen added this pull request to the merge queue Aug 31, 2025
Merged via the queue into main with commit ecf5107 Aug 31, 2025
11 checks passed
@steebchen
steebchen deleted the feat/deepseek-stability-flags branch August 31, 2025 18:04
@coderabbitai coderabbitai Bot mentioned this pull request Jan 11, 2026
6 tasks
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