import type { ProviderAccount, ProviderVendorInfo, ProviderWithKeyInfo } from '@/lib/providers'; import { getKnownModelCapabilityKind, getModelCapabilityLabel, type ModelCapabilityKind, } from '../../shared/model-capabilities'; import { NIANCODE_USER_MODEL_ACCOUNT_ID, normalizeImportedUserModelId, selectUserModelRuntimeAccounts, } from '../../shared/user-model-config'; export interface ConfiguredModelOption { modelRef: string; label: string; runtimeProviderKey: string; accountId: string; capability: ModelCapabilityKind; } export interface RuntimeProviderOption { runtimeProviderKey: string; accountId: string; label: string; modelIdPlaceholder?: string; configuredModelId?: string; } export function resolveRuntimeProviderKey(account: ProviderAccount): string { return account.id; } export function splitModelRef(modelRef: string | null | undefined): { providerKey: string; modelId: string } | null { const value = (modelRef || '').trim(); if (!value) return null; const separatorIndex = value.indexOf('/'); if (separatorIndex <= 0 || separatorIndex >= value.length - 1) return null; return { providerKey: value.slice(0, separatorIndex), modelId: value.slice(separatorIndex + 1), }; } export function formatModelRefLabel(modelRef: string | null | undefined): string { const parsed = splitModelRef(modelRef); return parsed?.modelId || (modelRef || '').trim() || 'Model'; } export function toModelOptionTestId(label: string): string { return label.replace(/[^a-zA-Z0-9_-]+/g, '-'); } export function getConfiguredModelCapability( option: ConfiguredModelOption, ): ModelCapabilityKind { return option.capability; } export function getConfiguredModelCapabilityLabel( option: ConfiguredModelOption, ): string { return getModelCapabilityLabel(getConfiguredModelCapability(option)); } export function hasConfiguredProviderCredentials( account: ProviderAccount, statusById: Map, ): boolean { if (account.authMode === 'oauth_device' || account.authMode === 'oauth_browser' || account.authMode === 'local') { return true; } return statusById.get(account.id)?.hasKey ?? false; } function normalizeModelIdForRuntimeKey( rawModel: string | undefined, runtimeProviderKey: string, account: ProviderAccount, ): string | null { const trimmed = rawModel?.trim(); if (!trimmed) return null; const modelId = trimmed.startsWith(`${runtimeProviderKey}/`) ? trimmed.slice(runtimeProviderKey.length + 1) : trimmed; return account.id === NIANCODE_USER_MODEL_ACCOUNT_ID ? normalizeImportedUserModelId(modelId) : modelId; } function modelIdsForAccount(account: ProviderAccount, runtimeProviderKey: string): string[] { const seen = new Set(); const modelIds: string[] = []; for (const rawModel of [account.model, ...(account.fallbackModels ?? [])]) { const modelId = normalizeModelIdForRuntimeKey(rawModel, runtimeProviderKey, account); if (!modelId || seen.has(modelId)) continue; seen.add(modelId); modelIds.push(modelId); } return modelIds; } export function buildRuntimeProviderOptions( providerAccounts: ProviderAccount[], providerStatuses: ProviderWithKeyInfo[], providerVendors: ProviderVendorInfo[], providerDefaultAccountId: string | null, ): RuntimeProviderOption[] { const safeAccounts = Array.isArray(providerAccounts) ? providerAccounts : []; const safeStatuses = Array.isArray(providerStatuses) ? providerStatuses : []; const safeVendors = Array.isArray(providerVendors) ? providerVendors : []; const vendorMap = new Map(safeVendors.map((vendor) => [vendor.id, vendor])); const statusById = new Map(safeStatuses.map((status) => [status.id, status])); const entries = selectUserModelRuntimeAccounts(safeAccounts) .filter((account) => account.enabled && hasConfiguredProviderCredentials(account, statusById)) .sort((left, right) => { if (left.id === providerDefaultAccountId) return -1; if (right.id === providerDefaultAccountId) return 1; return right.updatedAt.localeCompare(left.updatedAt); }); const deduped = new Map(); for (const account of entries) { const runtimeProviderKey = resolveRuntimeProviderKey(account); if (!runtimeProviderKey || deduped.has(runtimeProviderKey)) continue; const vendor = vendorMap.get(account.vendorId); const label = `${account.label} (${vendor?.name || account.vendorId})`; const configuredModelId = account.model ? normalizeModelIdForRuntimeKey(account.model, runtimeProviderKey, account) ?? undefined : undefined; deduped.set(runtimeProviderKey, { runtimeProviderKey, accountId: account.id, label, modelIdPlaceholder: vendor?.modelIdPlaceholder, configuredModelId, }); } return [...deduped.values()]; } export function buildConfiguredModelOptions( providerAccounts: ProviderAccount[], providerStatuses: ProviderWithKeyInfo[], providerDefaultAccountId: string | null, ): ConfiguredModelOption[] { const safeAccounts = Array.isArray(providerAccounts) ? providerAccounts : []; const safeStatuses = Array.isArray(providerStatuses) ? providerStatuses : []; const statusById = new Map(safeStatuses.map((status) => [status.id, status])); const entries = selectUserModelRuntimeAccounts(safeAccounts) .filter((account) => account.enabled && hasConfiguredProviderCredentials(account, statusById)) .sort((left, right) => { if (left.id === providerDefaultAccountId) return -1; if (right.id === providerDefaultAccountId) return 1; return right.updatedAt.localeCompare(left.updatedAt); }); const deduped = new Map(); for (const account of entries) { const runtimeProviderKey = resolveRuntimeProviderKey(account); for (const modelId of modelIdsForAccount(account, runtimeProviderKey)) { const modelRef = `${runtimeProviderKey}/${modelId}`; if (deduped.has(modelRef)) continue; deduped.set(modelRef, { modelRef, label: modelId, runtimeProviderKey, accountId: account.id, capability: getKnownModelCapabilityKind(modelId), }); } } return [...deduped.values()]; }