113 lines
3.0 KiB
TypeScript
113 lines
3.0 KiB
TypeScript
import type { Model } from "./workspace/types";
|
|
|
|
export type ModelIdentity = { provider: string; modelId: string };
|
|
export type ThinkingLevelOption = { value: string; label: string };
|
|
|
|
const DEFAULT_THINKING_LEVELS = [
|
|
"off",
|
|
"minimal",
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
"xhigh",
|
|
] as const;
|
|
const OFF_THINKING_LEVEL: ThinkingLevelOption = { value: "off", label: "Off" };
|
|
|
|
function thinkingLevelLabel(level: string) {
|
|
if (level === "xhigh") return "XHigh";
|
|
return level[0].toUpperCase() + level.slice(1);
|
|
}
|
|
|
|
export function modelIdentity(model: Model): ModelIdentity | undefined {
|
|
if (!model.provider || !model.id) return undefined;
|
|
return { provider: model.provider, modelId: model.id };
|
|
}
|
|
|
|
export function modelIdentityValue(identity: ModelIdentity) {
|
|
return JSON.stringify([identity.provider, identity.modelId]);
|
|
}
|
|
|
|
export function modelIdentityFromValue(
|
|
value: string,
|
|
): ModelIdentity | undefined {
|
|
try {
|
|
const parsed: unknown = JSON.parse(value);
|
|
if (
|
|
!Array.isArray(parsed) ||
|
|
parsed.length !== 2 ||
|
|
!parsed.every((part) => typeof part === "string")
|
|
)
|
|
return undefined;
|
|
return { provider: parsed[0], modelId: parsed[1] };
|
|
} catch {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
export function reconcileModelIdentity(
|
|
models: Model[],
|
|
preferred: ModelIdentity,
|
|
): ModelIdentity | undefined {
|
|
const identities = models
|
|
.map(modelIdentity)
|
|
.filter((identity): identity is ModelIdentity => !!identity);
|
|
const preferredValue = modelIdentityValue(preferred);
|
|
return (
|
|
identities.find(
|
|
(identity) => modelIdentityValue(identity) === preferredValue,
|
|
) ??
|
|
identities.find((identity) => identity.provider === preferred.provider) ??
|
|
identities[0]
|
|
);
|
|
}
|
|
|
|
export function thinkingLevelOptions(model?: Model): ThinkingLevelOption[] {
|
|
if (!model || model.reasoning === false) return [OFF_THINKING_LEVEL];
|
|
const map = model.thinkingLevelMap;
|
|
const levels = [
|
|
...DEFAULT_THINKING_LEVELS.filter((level) => {
|
|
if (level === "xhigh") return map?.xhigh !== undefined && map.xhigh !== null;
|
|
return map?.[level] !== null;
|
|
}),
|
|
...(map?.max == null ? [] : ["max"]),
|
|
];
|
|
return levels.length
|
|
? levels.map((value) => ({ value, label: thinkingLevelLabel(value) }))
|
|
: [OFF_THINKING_LEVEL];
|
|
}
|
|
|
|
export function reconcileThinkingLevel(
|
|
model: Model | undefined,
|
|
level: string,
|
|
) {
|
|
const options = thinkingLevelOptions(model);
|
|
return options.some((option) => option.value === level)
|
|
? level
|
|
: (options.find((option) => option.value === "off")?.value ??
|
|
options[0].value);
|
|
}
|
|
|
|
export function catalogModels(
|
|
models: Model[],
|
|
configured: ModelIdentity[],
|
|
): Model[] {
|
|
const available = models.filter((model) => model.provider && model.id);
|
|
const keys = new Set(
|
|
available
|
|
.map(modelIdentity)
|
|
.filter((identity): identity is ModelIdentity => !!identity)
|
|
.map(modelIdentityValue),
|
|
);
|
|
for (const identity of configured) {
|
|
if (!identity.provider || !identity.modelId) continue;
|
|
const key = modelIdentityValue(identity);
|
|
if (keys.has(key)) continue;
|
|
available.unshift({
|
|
provider: identity.provider,
|
|
id: identity.modelId,
|
|
});
|
|
keys.add(key);
|
|
}
|
|
return available;
|
|
}
|