import type { Model } from "./workspace/types"; export type ModelIdentity = { provider: string; modelId: string }; export type ThinkingLevelOption = { value: string; label: string }; const DEFAULT_THINKING_LEVELS = [ "off", "minimal", "low", "medium", "high", "xhigh", ] as const; const OFF_THINKING_LEVEL: ThinkingLevelOption = { value: "off", label: "Off" }; function thinkingLevelLabel(level: string) { if (level === "xhigh") return "XHigh"; return level[0].toUpperCase() + level.slice(1); } export function modelIdentity(model: Model): ModelIdentity | undefined { if (!model.provider || !model.id) return undefined; return { provider: model.provider, modelId: model.id }; } export function modelIdentityValue(identity: ModelIdentity) { return JSON.stringify([identity.provider, identity.modelId]); } export function modelIdentityFromValue( value: string, ): ModelIdentity | undefined { try { const parsed: unknown = JSON.parse(value); if ( !Array.isArray(parsed) || parsed.length !== 2 || !parsed.every((part) => typeof part === "string") ) return undefined; return { provider: parsed[0], modelId: parsed[1] }; } catch { return undefined; } } export function reconcileModelIdentity( models: Model[], preferred: ModelIdentity, ): ModelIdentity | undefined { const identities = models .map(modelIdentity) .filter((identity): identity is ModelIdentity => !!identity); const preferredValue = modelIdentityValue(preferred); return ( identities.find( (identity) => modelIdentityValue(identity) === preferredValue, ) ?? identities.find((identity) => identity.provider === preferred.provider) ?? identities[0] ); } export function thinkingLevelOptions(model?: Model): ThinkingLevelOption[] { if (!model || model.reasoning === false) return [OFF_THINKING_LEVEL]; const map = model.thinkingLevelMap; const levels = [ ...DEFAULT_THINKING_LEVELS.filter((level) => { if (level === "xhigh") return map?.xhigh !== undefined && map.xhigh !== null; return map?.[level] !== null; }), ...(map?.max == null ? [] : ["max"]), ]; return levels.length ? levels.map((value) => ({ value, label: thinkingLevelLabel(value) })) : [OFF_THINKING_LEVEL]; } export function reconcileThinkingLevel( model: Model | undefined, level: string, ) { const options = thinkingLevelOptions(model); return options.some((option) => option.value === level) ? level : (options.find((option) => option.value === "off")?.value ?? options[0].value); } export function catalogModels( models: Model[], configured: ModelIdentity[], ): Model[] { const available = models.filter((model) => model.provider && model.id); const keys = new Set( available .map(modelIdentity) .filter((identity): identity is ModelIdentity => !!identity) .map(modelIdentityValue), ); for (const identity of configured) { if (!identity.provider || !identity.modelId) continue; const key = modelIdentityValue(identity); if (keys.has(key)) continue; available.unshift({ provider: identity.provider, id: identity.modelId, }); keys.add(key); } return available; }