import { hostApiFetch } from '@/lib/host-api'; import type { ProviderAccount, ProviderType, ProviderVendorInfo, ProviderWithKeyInfo, } from '@/lib/providers'; export interface ProviderSnapshot { accounts: ProviderAccount[]; statuses: ProviderWithKeyInfo[]; vendors: ProviderVendorInfo[]; defaultAccountId: string | null; } export interface ProviderListItem { account: ProviderAccount; vendor?: ProviderVendorInfo; status?: ProviderWithKeyInfo; } export async function fetchProviderSnapshot(): Promise { const [accounts, statuses, vendors, defaultInfo] = await Promise.all([ hostApiFetch('/api/provider-accounts'), hostApiFetch('/api/providers'), hostApiFetch('/api/provider-vendors'), hostApiFetch<{ accountId: string | null }>('/api/provider-accounts/default'), ]); return { accounts, statuses, vendors, defaultAccountId: defaultInfo.accountId, }; } export function hasConfiguredCredentials( account: ProviderAccount, status?: ProviderWithKeyInfo, ): boolean { if (account.authMode === 'oauth_device' || account.authMode === 'oauth_browser' || account.authMode === 'local') { return true; } return status?.hasKey ?? false; } export function pickPreferredAccount( accounts: ProviderAccount[], defaultAccountId: string | null, vendorId: ProviderType | string, statusMap: Map, ): ProviderAccount | null { const sameVendor = accounts.filter((account) => account.vendorId === vendorId); if (sameVendor.length === 0) return null; return ( (defaultAccountId ? sameVendor.find((account) => account.id === defaultAccountId) : undefined) || sameVendor.find((account) => hasConfiguredCredentials(account, statusMap.get(account.id))) || sameVendor[0] ); } export function buildProviderAccountId( vendorId: ProviderType, existingAccountId: string | null, vendors: ProviderVendorInfo[], ): string { if (existingAccountId) { return existingAccountId; } const vendor = vendors.find((candidate) => candidate.id === vendorId); return vendor?.supportsMultipleAccounts ? `${vendorId}-${crypto.randomUUID()}` : vendorId; } export function legacyProviderToAccount(provider: ProviderWithKeyInfo): ProviderAccount { return { id: provider.id, vendorId: provider.type, label: provider.name, authMode: provider.type === 'ollama' ? 'local' : 'api_key', baseUrl: provider.baseUrl, headers: provider.headers, model: provider.model, fallbackModels: provider.fallbackModels, fallbackAccountIds: provider.fallbackProviderIds, enabled: provider.enabled, isDefault: false, createdAt: provider.createdAt, updatedAt: provider.updatedAt, }; } export function buildProviderListItems( accounts: ProviderAccount[], statuses: ProviderWithKeyInfo[], vendors: ProviderVendorInfo[], defaultAccountId: string | null, ): ProviderListItem[] { const safeAccounts = accounts ?? []; const safeStatuses = statuses ?? []; const safeVendors = vendors ?? []; const vendorMap = new Map(safeVendors.map((vendor) => [vendor.id, vendor])); const statusMap = new Map(safeStatuses.map((status) => [status.id, status])); if (safeAccounts.length > 0) { return safeAccounts .map((account) => ({ account, vendor: vendorMap.get(account.vendorId), status: statusMap.get(account.id), })) .sort((left, right) => { if (left.account.id === defaultAccountId) return -1; if (right.account.id === defaultAccountId) return 1; return right.account.updatedAt.localeCompare(left.account.updatedAt); }); } return safeStatuses.map((status) => ({ account: legacyProviderToAccount(status), vendor: vendorMap.get(status.type), status, })); }