213 lines
6.9 KiB
TypeScript
213 lines
6.9 KiB
TypeScript
import type {
|
|
ThinkingCapability,
|
|
ThinkingConfig,
|
|
ThinkingEffort,
|
|
ThinkingLevel,
|
|
ThinkingMode,
|
|
} from '@/lib/types/provider';
|
|
|
|
export function getThinkingConfigKey(providerId: string, modelId: string): string {
|
|
return `${providerId}:${modelId}`;
|
|
}
|
|
|
|
export function supportsConfigurableThinking(
|
|
thinking?: ThinkingCapability,
|
|
): thinking is ThinkingCapability {
|
|
return !!thinking?.control && thinking.control !== 'none' && !!thinking.requestAdapter;
|
|
}
|
|
|
|
export function clampBudgetForCapability(
|
|
thinking: ThinkingCapability,
|
|
budgetTokens?: number,
|
|
): number | undefined {
|
|
const range = thinking.budgetRange;
|
|
if (!range || typeof budgetTokens !== 'number' || Number.isNaN(budgetTokens)) {
|
|
return undefined;
|
|
}
|
|
if (budgetTokens === -1 && range.allowDynamic) return -1;
|
|
return Math.max(range.min, Math.min(range.max, Math.round(budgetTokens)));
|
|
}
|
|
|
|
export function getThinkingMode(
|
|
config?: ThinkingConfig,
|
|
): 'disabled' | 'enabled' | 'auto' | undefined {
|
|
if (!config) return undefined;
|
|
if (config.mode && config.mode !== 'default') return config.mode;
|
|
if (config.enabled === false) return 'disabled';
|
|
if (config.enabled === true) return 'enabled';
|
|
return undefined;
|
|
}
|
|
|
|
export function pickThinkingEffort(
|
|
thinking: ThinkingCapability,
|
|
config: ThinkingConfig,
|
|
): ThinkingEffort | undefined {
|
|
const allowed = thinking.effortValues;
|
|
if (!allowed?.length) return undefined;
|
|
if (config.effort && allowed.includes(config.effort)) return config.effort;
|
|
|
|
const mode = getThinkingMode(config);
|
|
if (mode === 'disabled') {
|
|
return (
|
|
(allowed.includes('none') && 'none') ||
|
|
(allowed.includes('minimal') && 'minimal') ||
|
|
(allowed.includes('low') && 'low') ||
|
|
thinking.defaultEffort
|
|
);
|
|
}
|
|
if (mode === 'enabled') return thinking.defaultEffort;
|
|
return undefined;
|
|
}
|
|
|
|
export function pickThinkingLevel(
|
|
thinking: ThinkingCapability,
|
|
config: ThinkingConfig,
|
|
): ThinkingLevel | undefined {
|
|
const allowed = thinking.levelValues;
|
|
if (!allowed?.length) return undefined;
|
|
if (config.level && allowed.includes(config.level)) return config.level;
|
|
|
|
const mode = getThinkingMode(config);
|
|
if (mode === 'disabled') {
|
|
return (
|
|
(allowed.includes('minimal') && 'minimal') ||
|
|
(allowed.includes('low') && 'low') ||
|
|
thinking.defaultLevel
|
|
);
|
|
}
|
|
if (mode === 'enabled') return thinking.defaultLevel;
|
|
return undefined;
|
|
}
|
|
|
|
export function pickThinkingBudget(
|
|
thinking: ThinkingCapability,
|
|
config: ThinkingConfig,
|
|
): number | undefined {
|
|
const range = thinking.budgetRange;
|
|
if (!range) return undefined;
|
|
|
|
const mode = getThinkingMode(config);
|
|
if (mode === 'disabled' && range.disableValue !== undefined) {
|
|
return range.disableValue;
|
|
}
|
|
|
|
const rawBudget =
|
|
typeof config.budgetTokens === 'number' ? config.budgetTokens : thinking.defaultBudgetTokens;
|
|
return clampBudgetForCapability(thinking, rawBudget);
|
|
}
|
|
|
|
function defaultModeForCapability(thinking: ThinkingCapability): ThinkingMode {
|
|
return thinking.defaultMode ?? (thinking.defaultEnabled === false ? 'disabled' : 'enabled');
|
|
}
|
|
|
|
function defaultEffortForCapability(thinking: ThinkingCapability): ThinkingEffort | undefined {
|
|
return thinking.defaultEffort ?? thinking.effortValues?.[0];
|
|
}
|
|
|
|
function defaultLevelForCapability(thinking: ThinkingCapability): ThinkingLevel | undefined {
|
|
return thinking.defaultLevel ?? thinking.levelValues?.[0];
|
|
}
|
|
|
|
export function getDefaultThinkingConfig(
|
|
thinking?: ThinkingCapability,
|
|
): ThinkingConfig | undefined {
|
|
if (!supportsConfigurableThinking(thinking)) return undefined;
|
|
|
|
switch (thinking.control) {
|
|
case 'effort': {
|
|
const effort = defaultEffortForCapability(thinking);
|
|
return effort
|
|
? { mode: effort === 'none' ? 'disabled' : 'enabled', effort }
|
|
: { mode: defaultModeForCapability(thinking) };
|
|
}
|
|
case 'level': {
|
|
const level = defaultLevelForCapability(thinking);
|
|
return level ? { mode: 'enabled', level } : { mode: defaultModeForCapability(thinking) };
|
|
}
|
|
case 'toggle':
|
|
return { mode: defaultModeForCapability(thinking) };
|
|
case 'toggle-budget':
|
|
return {
|
|
mode: defaultModeForCapability(thinking),
|
|
budgetTokens: thinking.defaultBudgetTokens,
|
|
};
|
|
case 'budget-only':
|
|
return { mode: 'enabled', budgetTokens: thinking.defaultBudgetTokens };
|
|
case 'mode':
|
|
return { mode: defaultModeForCapability(thinking) };
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function normalizeThinkingConfig(
|
|
thinking: ThinkingCapability | undefined,
|
|
config: ThinkingConfig | undefined,
|
|
): ThinkingConfig | undefined {
|
|
if (!supportsConfigurableThinking(thinking)) return undefined;
|
|
|
|
const mode = getThinkingMode(config);
|
|
|
|
switch (thinking.control) {
|
|
case 'effort': {
|
|
const effort =
|
|
(config ? pickThinkingEffort(thinking, config) : undefined) ??
|
|
defaultEffortForCapability(thinking);
|
|
if (!effort) return undefined;
|
|
return { mode: effort === 'none' ? 'disabled' : 'enabled', effort };
|
|
}
|
|
case 'level': {
|
|
const level =
|
|
(config ? pickThinkingLevel(thinking, config) : undefined) ??
|
|
defaultLevelForCapability(thinking);
|
|
if (!level) return undefined;
|
|
return { mode: 'enabled', level };
|
|
}
|
|
case 'toggle':
|
|
return { mode: mode ?? defaultModeForCapability(thinking) };
|
|
case 'toggle-budget': {
|
|
const normalizedMode = mode ?? defaultModeForCapability(thinking);
|
|
const budget = config ? pickThinkingBudget(thinking, config) : undefined;
|
|
return {
|
|
mode: normalizedMode,
|
|
budgetTokens: budget ?? thinking.defaultBudgetTokens,
|
|
};
|
|
}
|
|
case 'budget-only': {
|
|
const budget = config ? pickThinkingBudget(thinking, config) : undefined;
|
|
return {
|
|
mode: 'enabled',
|
|
budgetTokens: budget ?? thinking.defaultBudgetTokens,
|
|
};
|
|
}
|
|
case 'mode':
|
|
return { mode: mode ?? defaultModeForCapability(thinking) };
|
|
default:
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function getThinkingDisplayValue(
|
|
thinking: ThinkingCapability | undefined,
|
|
config: ThinkingConfig | undefined,
|
|
): string | undefined {
|
|
const normalized =
|
|
normalizeThinkingConfig(thinking, config) ?? getDefaultThinkingConfig(thinking);
|
|
if (!supportsConfigurableThinking(thinking) || !normalized) return undefined;
|
|
|
|
if (thinking.control === 'effort') return normalized.effort;
|
|
if (thinking.control === 'level') return normalized.level;
|
|
if (thinking.control === 'budget-only') {
|
|
return normalized.budgetTokens === -1 ? 'dynamic' : `${normalized.budgetTokens ?? ''}`;
|
|
}
|
|
if (thinking.control === 'toggle-budget') {
|
|
if (normalized.mode === 'disabled') return 'off';
|
|
if (normalized.budgetTokens === -1 && thinking.budgetRange?.allowDynamic) return 'auto';
|
|
return normalized.budgetTokens && normalized.budgetTokens !== -1
|
|
? `${normalized.budgetTokens}`
|
|
: 'on';
|
|
}
|
|
if (thinking.control === 'mode') return normalized.mode;
|
|
return normalized.mode === 'disabled' ? 'off' : 'on';
|
|
}
|