import type { ClientModel } from "@opengeni/sdk"; import { modelDisplayName } from "@opengeni/sdk/model-display"; import { ChevronDownIcon } from "lucide-react"; import { useId, useMemo } from "react"; import { groupPickerRowsByBillingClass, type PickerModelRow } from "../model-policy"; import { cn } from "../lib/cn"; import { useOpenGeniText, type OpenGeniText } from "../messages/runtime"; export type ModelPickerProps = { /** Legacy host config models grouped by provider. */ models?: ClientModel[] | undefined; /** Catalog-backed rows grouped by billing class. Preferred when available. */ rows?: PickerModelRow[] | undefined; value?: string | undefined; onChange: (modelId: string) => void; disabled?: boolean | undefined; className?: string | undefined; label?: string | undefined; /** * When true (remote compaction v2), non-Codex models remain listed but are * disabled. Same picker chrome — only selectability changes. */ codexOnly?: boolean | undefined; }; const EMPTY_CLIENT_MODELS: ClientModel[] = []; function isCodexClientModel(model: ClientModel): boolean { return model.id.startsWith("codex/") || model.source === "codex"; } function isCodexPickerRow(row: PickerModelRow): boolean { return row.id.startsWith("codex/") || row.catalog.source === "codex"; } function applyCodexOnlyRows( t: OpenGeniText, rows: PickerModelRow[], codexOnly: boolean, ): PickerModelRow[] { if (!codexOnly) return rows; return rows.map((row) => { if (isCodexPickerRow(row)) return row; return { ...row, selectable: false, unavailableReason: row.unavailableReason ?? t("modelPicker.codexOnlySession"), }; }); } function providerGroups(models: ClientModel[], codexOnly: boolean) { const byProvider = new Map< string, { label: string; models: Array } >(); for (const model of models) { let group = byProvider.get(model.provider); if (!group) { group = { label: model.providerLabel, models: [] }; byProvider.set(model.provider, group); } group.models.push({ ...model, optionDisabled: codexOnly && !isCodexClientModel(model), }); } const groups = [...byProvider.values()]; if (codexOnly) { const codexIndex = groups.findIndex((group) => group.models.some((model) => !model.optionDisabled && isCodexClientModel(model)), ); if (codexIndex > 0) groups.unshift(...groups.splice(codexIndex, 1)); } return groups; } export function ModelPicker({ models = EMPTY_CLIENT_MODELS, rows, value, onChange, disabled, className, label: labelProp, codexOnly = false, }: ModelPickerProps) { const t = useOpenGeniText(); const label = labelProp === undefined ? t("modelPicker.model") : labelProp; const selectId = useId(); const effectiveRows = useMemo( () => (rows && rows.length > 0 ? applyCodexOnlyRows(t, rows, codexOnly) : []), [rows, codexOnly, t], ); const billingGroups = useMemo( () => effectiveRows.length > 0 ? groupPickerRowsByBillingClass(t, effectiveRows, { codexOnly, selectedId: value }) : [], [effectiveRows, codexOnly, value, t], ); const providerGroupList = useMemo(() => providerGroups(models, codexOnly), [models, codexOnly]); const useBillingGroups = Boolean(effectiveRows.length); if (models.length === 0 && effectiveRows.length === 0) { return null; } return ( ); }