127 lines
3.8 KiB
TypeScript
127 lines
3.8 KiB
TypeScript
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<ProviderSnapshot> {
|
|
const [accounts, statuses, vendors, defaultInfo] = await Promise.all([
|
|
hostApiFetch<ProviderAccount[]>('/api/provider-accounts'),
|
|
hostApiFetch<ProviderWithKeyInfo[]>('/api/providers'),
|
|
hostApiFetch<ProviderVendorInfo[]>('/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<string, ProviderWithKeyInfo>,
|
|
): 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,
|
|
}));
|
|
}
|