93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
import type { ProviderAccount, ProviderVendorInfo } from '@/lib/providers';
|
|
import type { ConversationThinkingLevel, ProductModelRef } from '@/types/coding-conversation';
|
|
import { getImportedModelProfile } from '../../shared/imported-model-profile';
|
|
|
|
export interface CodingModelOption {
|
|
key: string;
|
|
accountId: string;
|
|
modelId: string;
|
|
label: string;
|
|
availableThinkingLevels: ConversationThinkingLevel[] | null;
|
|
}
|
|
|
|
const STANDARD_THINKING_LEVELS: readonly ConversationThinkingLevel[] = [
|
|
'off',
|
|
'minimal',
|
|
'low',
|
|
'medium',
|
|
'high',
|
|
];
|
|
const SERVER_REASONING_LEVELS = ['low', 'high', 'max'] as const;
|
|
|
|
function availableThinkingLevels(
|
|
account: ProviderAccount,
|
|
modelId: string,
|
|
): ConversationThinkingLevel[] | null {
|
|
const serverCapability = account.metadata?.worksSquareModelCapabilities?.[modelId];
|
|
if (serverCapability) {
|
|
return [
|
|
...(serverCapability.reasoningCanDisable ? ['off' as const] : []),
|
|
...SERVER_REASONING_LEVELS.filter((level) => serverCapability.reasoningEfforts.includes(level)),
|
|
];
|
|
}
|
|
|
|
const profile = getImportedModelProfile(modelId)?.pi;
|
|
if (!profile) return null;
|
|
if (!profile.reasoning) return ['off'];
|
|
if (!profile.thinkingLevelMap) return [...STANDARD_THINKING_LEVELS];
|
|
return [
|
|
...STANDARD_THINKING_LEVELS.filter((level) => profile.thinkingLevelMap?.[level] !== null),
|
|
...(typeof profile.thinkingLevelMap.max === 'string' ? ['max' as const] : []),
|
|
];
|
|
}
|
|
|
|
export function codingModelKey(model: Pick<ProductModelRef, 'accountId' | 'modelId'>): string {
|
|
return JSON.stringify([model.accountId, model.modelId]);
|
|
}
|
|
|
|
export function parseCodingModelKey(key: string): Pick<ProductModelRef, 'accountId' | 'modelId'> | null {
|
|
try {
|
|
const value = JSON.parse(key) as unknown;
|
|
if (!Array.isArray(value) || value.length !== 2) return null;
|
|
const [accountId, modelId] = value;
|
|
if (typeof accountId !== 'string' || !accountId.trim()
|
|
|| typeof modelId !== 'string' || !modelId.trim()) return null;
|
|
return { accountId: accountId.trim(), modelId: modelId.trim() };
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function buildCodingModelOptions(
|
|
accounts: ProviderAccount[],
|
|
vendors: ProviderVendorInfo[],
|
|
): CodingModelOption[] {
|
|
const vendorNames = new Map(vendors.map((vendor) => [vendor.id, vendor.name]));
|
|
const seen = new Set<string>();
|
|
const options: CodingModelOption[] = [];
|
|
for (const account of accounts) {
|
|
if (!account.enabled) continue;
|
|
const modelIds = [
|
|
account.model,
|
|
...(account.fallbackModels ?? []),
|
|
...(account.metadata?.customModels ?? []),
|
|
];
|
|
for (const value of modelIds) {
|
|
const modelId = value?.trim();
|
|
if (!modelId) continue;
|
|
const key = codingModelKey({ accountId: account.id, modelId });
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
const vendor = vendorNames.get(account.vendorId);
|
|
options.push({
|
|
key,
|
|
accountId: account.id,
|
|
modelId,
|
|
label: `${account.label}${vendor && vendor !== account.label ? ` · ${vendor}` : ''} / ${modelId}`,
|
|
availableThinkingLevels: availableThinkingLevels(account, modelId),
|
|
});
|
|
}
|
|
}
|
|
return options;
|
|
}
|