Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 27 additions & 12 deletions apps/web/src/components/settings/ProviderModelsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type ProviderInstanceId,
type ServerProviderModel,
} from "@t3tools/contracts";
import { REASONING_SELECT_DESCRIPTOR_IDS } from "@t3tools/client-runtime/model-traits";
import { type CustomModelDefinition, normalizeCustomModelSlug } from "@t3tools/shared/model";

import { useStableCallback } from "../../hooks/useStableCallback";
Expand All @@ -27,7 +28,21 @@ const CUSTOM_MODEL_PLACEHOLDER_BY_KIND: Partial<Record<ProviderDriverKind, strin

const FILTER_THRESHOLD = 8;

function describeModelCapabilities(model: ServerProviderModel): string[] {
const PROVIDER_ALIAS_SLUGS = new Set(["default", "auto"]);

function hasReasoningControl(model: ServerProviderModel): boolean {
return (model.capabilities?.optionDescriptors ?? []).some((descriptor) =>
REASONING_SELECT_DESCRIPTOR_IDS.has(descriptor.id),
);
}

// Reasoning is the norm, so only its absence is labeled, and only when other models have it.
// Custom and null capabilities are fallbacks or user-written, not discovered, so they prove nothing.
// Alias slugs ("default", Cursor's "auto") defer to the provider's own model choice, so reasoning is unknown.
function describeModelCapabilities(
model: ServerProviderModel,
flagMissingReasoning: boolean,
): string[] {
const descriptors = model.capabilities?.optionDescriptors ?? [];
const labels: string[] = [];
const hasFastMode = descriptors.some(
Expand All @@ -38,18 +53,14 @@ function describeModelCapabilities(model: ServerProviderModel): string[] {
descriptor.options.some((option) => option.id === "fast" || option.label === "Fast")),
);
if (hasFastMode) labels.push("Fast mode");
if (descriptors.some((descriptor) => descriptor.id === "thinking")) labels.push("Thinking");
if (
descriptors.some(
(descriptor) =>
descriptor.type === "select" &&
(descriptor.id === "reasoningEffort" ||
descriptor.id === "effort" ||
descriptor.id === "reasoning" ||
descriptor.id === "variant"),
)
flagMissingReasoning &&
!model.isCustom &&
model.capabilities &&
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
!PROVIDER_ALIAS_SLUGS.has(model.slug) &&
!hasReasoningControl(model)
) {
labels.push("Reasoning");
labels.push("No reasoning");
}
return labels;
}
Expand Down Expand Up @@ -144,6 +155,7 @@ export function ProviderModelsSection({
const hiddenCount = displayModels.filter(
(model) => !model.isCustom && hiddenModelSet.has(model.slug),
).length;
const flagMissingReasoning = models.some(hasReasoningControl);
const showFilter = models.length > FILTER_THRESHOLD;
const normalizedFilter = filter.trim().toLowerCase();
const isFiltering = showFilter && normalizedFilter.length > 0;
Expand Down Expand Up @@ -278,6 +290,7 @@ export function ProviderModelsSection({
canMoveUp={!isFiltering && previousModel !== undefined && groupOf(previousModel) === group}
canMoveDown={!isFiltering && nextModel !== undefined && groupOf(nextModel) === group}
isFiltering={isFiltering}
flagMissingReasoning={flagMissingReasoning}
canWritePreferences={canWritePreferences}
canManageCustomModels={canManageCustomModels}
{...rowHandlers}
Expand Down Expand Up @@ -437,6 +450,7 @@ function ProviderModelRow({
canMoveUp,
canMoveDown,
isFiltering,
flagMissingReasoning,
canWritePreferences,
canManageCustomModels,
onToggleFavorite,
Expand All @@ -451,6 +465,7 @@ function ProviderModelRow({
readonly canMoveUp: boolean;
readonly canMoveDown: boolean;
readonly isFiltering: boolean;
readonly flagMissingReasoning: boolean;
readonly canWritePreferences: boolean;
readonly canManageCustomModels: boolean;
readonly onToggleFavorite: (slug: string) => void;
Expand All @@ -459,7 +474,7 @@ function ProviderModelRow({
readonly onToggleEdit: (slug: string) => void;
readonly onRemove: (slug: string) => void;
}) {
const capLabels = describeModelCapabilities(model);
const capLabels = describeModelCapabilities(model, flagMissingReasoning);
const nameClassName = cn("text-xs", isHidden ? "text-muted-foreground" : "text-foreground/90");
const pickerTooltip = model.isCustom
? "Custom models are always shown in the picker"
Expand Down
Loading