feat(models): add stability flags with ui - #681
Conversation
- 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>
WalkthroughAdds a StabilityLevel type and optional Changes
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 💡 Knowledge Base configuration:
You can enable these sources in your CodeRabbit configuration. 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ 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)
✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (9)
packages/models/src/models.ts (2)
89-90: Single source of truth for stability valuesTo 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 enforcedDocs 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 schemaLeverage 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 testsImport 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
getStabilityBadgePropsreturnscolor, 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 removedOr apply the color in the render (see suggestion on Lines 189-201).
81-84: Return a boolean explicitlyCurrent 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 iconAdd 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 itemsSame 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 BadgeUse the
colorreturned bygetStabilityBadgePropsso 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.
📒 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.tsapps/gateway/src/models/models.spec.tspackages/models/src/models.tsapps/ui/src/components/playground/model-selector.tsxpackages/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 usedb().query.<table>.findMany()ordb().query.<table>.findFirst()
Files:
apps/gateway/src/models/models.tsapps/gateway/src/models/models.spec.tspackages/models/src/models.tspackages/models/src/models/deepseek.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
Never use
as anyor: anyin TypeScript files.
Files:
apps/gateway/src/models/models.tsapps/gateway/src/models/models.spec.tspackages/models/src/models.tsapps/ui/src/components/playground/model-selector.tsxpackages/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.findMany() or db().query.
apps/{api,gateway}/**/*.{ts,tsx}: Use Drizzle with the latest object syntax for database operations
For read queries, use db().query..findFirst() Files:
apps/gateway/src/models/models.tsapps/gateway/src/models/models.spec.tsapps/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.tsapps/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.tsapps/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.tsxapps/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 frontendFiles:
apps/ui/src/components/playground/model-selector.tsxapps/{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.tsxapps/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 navigationFiles:
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-wiseForwarding
model.stabilityas optional matches tests and UI behavior. If you later want the API to always surface an explicit value, switch tomodel.stability ?? "stable"and update tests accordingly.apps/gateway/src/models/models.spec.ts (1)
130-177: Solid targeted assertions for DeepSeek modelsGood coverage verifying presence and expected flags; aligns with current model definitions.
apps/ui/src/components/playground/model-selector.tsx (3)
1-7: Imports look goodType import for StabilityLevel and AlertTriangle usage are appropriate.
23-28: LocalModel extended correctlyAdding
stability?: StabilityLevelto the local shape keeps UI typed.
102-103: Propagation looks correctForwarding
typedModel.stabilitypreserves typing.packages/models/src/models/deepseek.ts (5)
10-10: DeepSeek V3 marked unstable — OKFlag aligns with PR intent to surface caution on V3.
45-45: DeepSeek R1 marked unstable — OKConsistent with instability designation.
92-92: DeepSeek R1-0528 marked unstable — OKMatches the status communicated in the PR.
118-129: Nebius provider re-enabled for R1-0528Provider block looks complete (pricing, context, streaming). Good addition.
139-139: R1 Distill set to beta — OKAppropriate mid-tier stability signal.
- 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>
There was a problem hiding this comment.
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 providersMath.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 fieldsThis 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: EliminateanyingetCapabilityIcons’smodelparam.
Conform to the TS guideline. The function only readsoutput, 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/UIProvide 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 returnCurrent 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 logicThis 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 booleanSame 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 helpersgetStabilityBadgeProps/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 ZodAvoid 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 PromiseAlign 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.
📒 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.tsxapps/ui/src/components/models/all-models.tsxapps/gateway/src/models/models.tsapps/ui/src/app/models/[name]/page.tsxapps/ui/src/components/playground/model-selector.tsxpackages/models/src/models.tspackages/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.tsxapps/ui/src/components/models/all-models.tsxapps/ui/src/app/models/[name]/page.tsxapps/ui/src/components/playground/model-selector.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
Never use
as anyor: anyin TypeScript files.
Files:
apps/ui/src/components/models/models-list.tsxapps/ui/src/components/models/all-models.tsxapps/gateway/src/models/models.tsapps/ui/src/app/models/[name]/page.tsxapps/ui/src/components/playground/model-selector.tsxpackages/models/src/models.tspackages/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.tsxapps/ui/src/components/models/all-models.tsxapps/ui/src/app/models/[name]/page.tsxapps/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.tsxapps/ui/src/components/models/all-models.tsxapps/ui/src/app/models/[name]/page.tsxapps/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.tsxapps/ui/src/components/models/all-models.tsxapps/ui/src/app/models/[name]/page.tsxapps/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 usedb().query.<table>.findMany()ordb().query.<table>.findFirst()
Files:
apps/gateway/src/models/models.tspackages/models/src/models.tspackages/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.findMany() or db().query.
apps/{api,gateway}/**/*.{ts,tsx}: Use Drizzle with the latest object syntax for database operations
For read queries, use db().query..findFirst() Files:
apps/gateway/src/models/models.tsapps/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 linksApplied 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 navigationApplied 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).
| const modelDef = modelDefinitions.find( | ||
| (m) => m.id === decodedName, | ||
| ) as ModelDefinition; | ||
|
|
There was a problem hiding this comment.
🛠️ 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.
| 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.
| {(() => { | ||
| 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> |
There was a problem hiding this comment.
🛠️ 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.
| {(() => { | |
| 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.
| 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; | ||
| }; |
There was a problem hiding this comment.
🛠️ 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.
| 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.
| <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> | ||
|
|
There was a problem hiding this comment.
🛠️ 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.
| <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.
| <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> |
There was a problem hiding this comment.
🛠️ 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.
| <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.
| 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> |
There was a problem hiding this comment.
🛠️ 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.
| 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.
| {(() => { | ||
| 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; | ||
| })()} |
There was a problem hiding this comment.
🛠️ 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.
| {(() => { | |
| 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>
There was a problem hiding this comment.
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
getStabilityBadgePropsisn’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.
getStabilityBadgePropsreturns acolorhint 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 | undefinedinference.-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(andshouldShowStabilityWarning) 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.
getStabilityBadgePropsandshouldShowStabilityWarningmirror 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.
📒 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.tsxapps/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.tsxapps/ui/src/components/models/models-list.tsx
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/general.mdc)
Never use
as anyor: anyin TypeScript files.
Files:
apps/ui/src/components/models/provider-card.tsxapps/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.tsxapps/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.tsxapps/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.tsxapps/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 || modelStabilityensures 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>
There was a problem hiding this comment.
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.
📒 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 anyor: anyin 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.
| 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; | ||
| } | ||
| }; |
There was a problem hiding this comment.
🛠️ 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.
| 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.
adds stability flags; only use for UI purposes now, we can use this for routing decisions as well
Summary
Changes Made
Schema Updates
StabilityLeveltype with four levelsModelDefinitionandProviderModelMappinginterfaces with optionalstabilityfieldModel Configuration
unstableunstablebeta(updated from unstable)betastable(no explicit flag needed)UI Enhancements
API Updates
Testing
Test Plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
UI Changes
Tests
🌿 Generated by Terry
ℹ️ Tag @terragon-labs to ask questions and address PR feedback
📎 Task: https://www.terragonlabs.com/task/accc24dc-1c04-43e4-8d8e-c67c3fad4f10