mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-11 17:52:31 +03:00
235 lines
7.3 KiB
TypeScript
235 lines
7.3 KiB
TypeScript
import { supportsXHighEffort } from "@omniroute/open-sse/config/providerModels";
|
|
import { parseModel } from "@omniroute/open-sse/services/model";
|
|
import { stripVscodeServiceTierVariantModelId } from "@/lib/vscode/serviceTierVariants";
|
|
import { extendCodexGpt56EffortValues } from "@/shared/reasoning/effortStandardization";
|
|
|
|
export type VscodeCatalogModel = {
|
|
id?: string;
|
|
name?: string;
|
|
root?: string;
|
|
owned_by?: string;
|
|
capabilities?: Record<string, boolean | string[]>;
|
|
supportsReasoningEffort?: string[];
|
|
supportedReasoningEfforts?: string[];
|
|
supports_reasoning_effort?: string[];
|
|
defaultReasoningEffort?: string;
|
|
default_reasoning_effort?: string;
|
|
};
|
|
|
|
const STANDARD_EFFORT_SUFFIX_PATTERN = /-(xhigh|high|medium|low|none)$/i;
|
|
const GPT_5_6_EXTENDED_EFFORT_SUFFIX_PATTERN = /^(.*gpt-5\.6-(?:sol|terra|luna))-(max|ultra)$/i;
|
|
const DEFAULT_REASONING_EFFORT = "none";
|
|
const KNOWN_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh", "max", "ultra"]);
|
|
|
|
export type VscodeModelConfigSchema = {
|
|
type: "object";
|
|
properties: {
|
|
reasoningEffort: {
|
|
type: "string";
|
|
title: string;
|
|
description: string;
|
|
default: string;
|
|
enum: string[];
|
|
enumLabels: string[];
|
|
enumDescriptions: string[];
|
|
};
|
|
};
|
|
};
|
|
|
|
export function getCatalogModelName(model: VscodeCatalogModel) {
|
|
return stripVscodeServiceTierVariantModelId(model.id || model.name || model.root || "");
|
|
}
|
|
|
|
function matchReasoningEffortSuffix(modelId: string) {
|
|
const extendedMatch = modelId.match(GPT_5_6_EXTENDED_EFFORT_SUFFIX_PATTERN);
|
|
if (extendedMatch?.[1] && extendedMatch[2]) {
|
|
return { baseModelId: extendedMatch[1], effort: extendedMatch[2].toLowerCase() };
|
|
}
|
|
|
|
const standardMatch = modelId.match(STANDARD_EFFORT_SUFFIX_PATTERN);
|
|
if (!standardMatch?.[1]) return undefined;
|
|
return {
|
|
baseModelId: modelId.slice(0, -standardMatch[0].length),
|
|
effort: standardMatch[1].toLowerCase(),
|
|
};
|
|
}
|
|
|
|
function normalizeReasoningEffortValue(value: string) {
|
|
const normalized = value
|
|
.trim()
|
|
.toLowerCase()
|
|
.replace(/[_\s-]+/g, "");
|
|
if (normalized === "xhigh") return "xhigh";
|
|
if (KNOWN_REASONING_EFFORTS.has(normalized)) return normalized;
|
|
return undefined;
|
|
}
|
|
|
|
function getNativeReasoningEffortValues(model: VscodeCatalogModel) {
|
|
const candidates = [
|
|
model.owned_by !== "combo" && Array.isArray(model.capabilities?.effort_tiers)
|
|
? model.capabilities.effort_tiers
|
|
: undefined,
|
|
model.supportsReasoningEffort,
|
|
model.supportedReasoningEfforts,
|
|
model.supports_reasoning_effort,
|
|
];
|
|
|
|
for (const candidate of candidates) {
|
|
if (!Array.isArray(candidate) || candidate.length === 0) {
|
|
continue;
|
|
}
|
|
|
|
const normalized = Array.from(
|
|
new Set(
|
|
candidate
|
|
.map((value) =>
|
|
typeof value === "string" ? normalizeReasoningEffortValue(value) : undefined
|
|
)
|
|
.filter(Boolean)
|
|
)
|
|
) as string[];
|
|
|
|
if (normalized.length > 0) {
|
|
return normalized;
|
|
}
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
export function isReasoningCapableModel(model: VscodeCatalogModel) {
|
|
return (
|
|
model.capabilities?.reasoning === true ||
|
|
model.capabilities?.thinking === true ||
|
|
(getNativeReasoningEffortValues(model)?.length || 0) > 0
|
|
);
|
|
}
|
|
|
|
export function getReasoningEffortValues(model: VscodeCatalogModel) {
|
|
const nativeReasoningEffortValues = getNativeReasoningEffortValues(model);
|
|
if (nativeReasoningEffortValues && nativeReasoningEffortValues.length > 0) {
|
|
return nativeReasoningEffortValues;
|
|
}
|
|
|
|
if (!isReasoningCapableModel(model)) return undefined;
|
|
|
|
const modelId = getCatalogModelName(model);
|
|
const parsed = parseModel(modelId);
|
|
const providerId = parsed.provider || model.owned_by || "";
|
|
const providerModelId = parsed.model || model.root || modelId.split("/").pop() || modelId;
|
|
const values = ["none", "low", "medium", "high"];
|
|
|
|
if (providerId && providerModelId && supportsXHighEffort(providerId, providerModelId)) {
|
|
values.push("xhigh");
|
|
}
|
|
|
|
return extendCodexGpt56EffortValues(providerId, providerModelId, values);
|
|
}
|
|
|
|
export function formatReasoningEffortLabel(level: string) {
|
|
if (level === "xhigh") return "XHigh";
|
|
return level.charAt(0).toUpperCase() + level.slice(1);
|
|
}
|
|
|
|
function describeReasoningEffort(level: string) {
|
|
switch (level) {
|
|
case "none":
|
|
return "Disables extra reasoning effort.";
|
|
case "low":
|
|
return "Uses a light amount of reasoning.";
|
|
case "medium":
|
|
return "Uses a balanced amount of reasoning.";
|
|
case "high":
|
|
return "Uses an extended amount of reasoning.";
|
|
case "xhigh":
|
|
return "Uses extra-high reasoning effort.";
|
|
case "max":
|
|
return "Uses the maximum available reasoning effort.";
|
|
case "ultra":
|
|
return "Uses the Ultra reasoning preset.";
|
|
default:
|
|
return `Uses ${formatReasoningEffortLabel(level)} reasoning effort.`;
|
|
}
|
|
}
|
|
|
|
export function buildSupportedReasoningEfforts(supportedValues: string[]): string[] {
|
|
return [...supportedValues];
|
|
}
|
|
|
|
export function inferSelectedReasoningEffort(
|
|
model: VscodeCatalogModel,
|
|
supportedValues?: string[]
|
|
) {
|
|
const modelId = getCatalogModelName(model);
|
|
const match = matchReasoningEffortSuffix(modelId);
|
|
if (!match) return undefined;
|
|
|
|
const selected = match.effort;
|
|
if (
|
|
Array.isArray(supportedValues) &&
|
|
supportedValues.length > 0 &&
|
|
!supportedValues.includes(selected)
|
|
) {
|
|
return undefined;
|
|
}
|
|
|
|
return selected;
|
|
}
|
|
|
|
export function getReasoningVariantBaseModelId(modelId: string) {
|
|
return matchReasoningEffortSuffix(modelId)?.baseModelId || modelId;
|
|
}
|
|
|
|
function getCodexGpt56DefaultReasoningEffort(model: VscodeCatalogModel) {
|
|
const modelId = getCatalogModelName(model);
|
|
const parsed = parseModel(modelId);
|
|
const providerId = (parsed.provider || model.owned_by || "").trim().toLowerCase();
|
|
if (providerId !== "codex" && providerId !== "cx") return undefined;
|
|
|
|
const providerModelId = (parsed.model || model.root || modelId.split("/").pop() || modelId)
|
|
.trim()
|
|
.toLowerCase();
|
|
const match = providerModelId.match(
|
|
/^gpt-5\.6-(sol|terra|luna)(?:-(?:none|low|medium|high|xhigh|max|ultra))?$/
|
|
);
|
|
if (!match) return undefined;
|
|
return match[1] === "sol" ? "low" : "medium";
|
|
}
|
|
|
|
export function getDefaultReasoningEffort(model: VscodeCatalogModel, supportedValues?: string[]) {
|
|
const nativeDefault = normalizeReasoningEffortValue(
|
|
model.defaultReasoningEffort || model.default_reasoning_effort || ""
|
|
);
|
|
return (
|
|
inferSelectedReasoningEffort(model, supportedValues) ||
|
|
(nativeDefault && (!supportedValues?.length || supportedValues.includes(nativeDefault))
|
|
? nativeDefault
|
|
: undefined) ||
|
|
getCodexGpt56DefaultReasoningEffort(model) ||
|
|
(supportedValues?.includes(DEFAULT_REASONING_EFFORT)
|
|
? DEFAULT_REASONING_EFFORT
|
|
: supportedValues?.[0]) ||
|
|
DEFAULT_REASONING_EFFORT
|
|
);
|
|
}
|
|
|
|
export function buildReasoningConfigSchema(
|
|
supportedValues: string[],
|
|
defaultReasoningEffort: string
|
|
): VscodeModelConfigSchema {
|
|
return {
|
|
type: "object",
|
|
properties: {
|
|
reasoningEffort: {
|
|
type: "string",
|
|
title: "Reasoning effort",
|
|
description: "Controls how much reasoning effort the model uses.",
|
|
default: defaultReasoningEffort,
|
|
enum: supportedValues,
|
|
enumLabels: supportedValues.map(formatReasoningEffortLabel),
|
|
enumDescriptions: supportedValues.map(describeReasoningEffort),
|
|
},
|
|
},
|
|
};
|
|
}
|