Files
makelore/src/lib/model-options.ts
T
brother7 5a275b93a7 feat: remove legacy OpenCode runtime
Cut product flows over to Coding/Pi and retain only the migration-owned v1 boundary. Promote supported native optional packages because electron-builder omitted pnpm transitive optional closure from the packaged ASAR.
2026-08-24 12:17:43 +08:00

177 lines
6.2 KiB
TypeScript

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<string, ProviderWithKeyInfo>,
): 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<string>();
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<string, ProviderVendorInfo>(safeVendors.map((vendor) => [vendor.id, vendor]));
const statusById = new Map<string, ProviderWithKeyInfo>(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<string, RuntimeProviderOption>();
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<string, ProviderWithKeyInfo>(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<string, ConfiguredModelOption>();
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()];
}