import { DEFAULT_MODEL_BY_PROVIDER, type ClaudeModelOptions, type CodexModelOptions, type ModelCapabilities, type ProviderKind, type ServerProvider, type ServerProviderModel, } from "@t3tools/contracts"; import { getDefaultEffort, hasEffortLevel, normalizeModelSlug, trimOrNull, } from "@t3tools/shared/model"; const EMPTY_CAPABILITIES: ModelCapabilities = { reasoningEffortLevels: [], supportsFastMode: false, supportsThinkingToggle: false, promptInjectedEffortLevels: [], }; export function getProviderModels( providers: ReadonlyArray, provider: ProviderKind, ): ReadonlyArray { return providers.find((candidate) => candidate.provider === provider)?.models ?? []; } export function getProviderSnapshot( providers: ReadonlyArray, provider: ProviderKind, ): ServerProvider | undefined { return providers.find((candidate) => candidate.provider === provider); } export function isProviderEnabled( providers: ReadonlyArray, provider: ProviderKind, ): boolean { return getProviderSnapshot(providers, provider)?.enabled ?? true; } export function resolveSelectableProvider( providers: ReadonlyArray, provider: ProviderKind | null | undefined, ): ProviderKind { const requested = provider ?? "codex"; if (isProviderEnabled(providers, requested)) { return requested; } return providers.find((candidate) => candidate.enabled)?.provider ?? requested; } export function getProviderModelCapabilities( models: ReadonlyArray, model: string | null | undefined, provider: ProviderKind, ): ModelCapabilities { const slug = normalizeModelSlug(model, provider); return models.find((candidate) => candidate.slug === slug)?.capabilities ?? EMPTY_CAPABILITIES; } export function getDefaultServerModel( providers: ReadonlyArray, provider: ProviderKind, ): string { const models = getProviderModels(providers, provider); return ( models.find((model) => !model.isCustom)?.slug ?? models[0]?.slug ?? DEFAULT_MODEL_BY_PROVIDER[provider] ); } export function normalizeCodexModelOptionsWithCapabilities( caps: ModelCapabilities, modelOptions: CodexModelOptions | null | undefined, ): CodexModelOptions | undefined { const defaultReasoningEffort = getDefaultEffort(caps); const reasoningEffort = trimOrNull(modelOptions?.reasoningEffort) ?? defaultReasoningEffort; const fastModeEnabled = modelOptions?.fastMode === true; const nextOptions: CodexModelOptions = { ...(reasoningEffort && reasoningEffort !== defaultReasoningEffort ? { reasoningEffort: reasoningEffort as CodexModelOptions["reasoningEffort"] } : {}), ...(fastModeEnabled ? { fastMode: true } : {}), }; return Object.keys(nextOptions).length > 0 ? nextOptions : undefined; } export function normalizeClaudeModelOptionsWithCapabilities( caps: ModelCapabilities, modelOptions: ClaudeModelOptions | null | undefined, ): ClaudeModelOptions | undefined { const defaultReasoningEffort = getDefaultEffort(caps); const resolvedEffort = trimOrNull(modelOptions?.effort); const isPromptInjected = caps.promptInjectedEffortLevels.includes(resolvedEffort ?? ""); const effort = resolvedEffort && !isPromptInjected && hasEffortLevel(caps, resolvedEffort) && resolvedEffort !== defaultReasoningEffort ? resolvedEffort : undefined; const thinking = caps.supportsThinkingToggle && modelOptions?.thinking === false ? false : undefined; const fastMode = caps.supportsFastMode && modelOptions?.fastMode === true ? true : undefined; const nextOptions: ClaudeModelOptions = { ...(thinking === false ? { thinking: false } : {}), ...(effort ? { effort } : {}), ...(fastMode ? { fastMode: true } : {}), }; return Object.keys(nextOptions).length > 0 ? nextOptions : undefined; }