mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 22:02:08 +03:00
chore(duplication): share vscode metadata helpers (#5471)
This commit is contained in:
@@ -1,85 +1,5 @@
|
||||
const FAMILY_FIRST_MODEL_PATTERN = /^((?:gpt-[a-z0-9._-]+|claude[a-z0-9._-]*))(?:__provider_([a-z0-9-]+))(?:__tier_(priority|flex))?$/i;
|
||||
const TIER_SUFFIX_PATTERN = /(__tier_(?:priority|flex))$/i;
|
||||
|
||||
function normalizeFamily(value: string | null | undefined) {
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function splitActualModelId(modelId: string) {
|
||||
const trimmedModelId = modelId.trim();
|
||||
const slashIndex = trimmedModelId.indexOf("/");
|
||||
if (slashIndex <= 0 || slashIndex === trimmedModelId.length - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
providerPrefix: trimmedModelId.slice(0, slashIndex),
|
||||
providerModelId: trimmedModelId.slice(slashIndex + 1),
|
||||
};
|
||||
}
|
||||
|
||||
function extractTierSuffix(modelId: string) {
|
||||
const match = modelId.match(TIER_SUFFIX_PATTERN);
|
||||
return match?.[1] || "";
|
||||
}
|
||||
|
||||
function stripTierSuffix(modelId: string) {
|
||||
return modelId.replace(TIER_SUFFIX_PATTERN, "");
|
||||
}
|
||||
|
||||
function isFamilyFirstEligibleFamily(family: string) {
|
||||
const normalized = family.toLowerCase();
|
||||
return normalized.startsWith("gpt-") || normalized.startsWith("claude");
|
||||
}
|
||||
|
||||
export function getFamilyFirstPublishedModelId(actualModelId: string, family: string | null | undefined) {
|
||||
const normalizedFamily = normalizeFamily(family);
|
||||
if (!normalizedFamily || !isFamilyFirstEligibleFamily(normalizedFamily)) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
const parts = splitActualModelId(actualModelId);
|
||||
if (!parts) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
const tierSuffix = extractTierSuffix(parts.providerModelId);
|
||||
const providerModelBase = stripTierSuffix(parts.providerModelId);
|
||||
if (providerModelBase !== normalizedFamily) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
return `${normalizedFamily}__provider_${parts.providerPrefix}${tierSuffix}`;
|
||||
}
|
||||
|
||||
export function resolveFamilyFirstPublishedModelId(modelId: string | null | undefined) {
|
||||
const trimmedModelId = typeof modelId === "string" ? modelId.trim() : "";
|
||||
if (!trimmedModelId) {
|
||||
return trimmedModelId;
|
||||
}
|
||||
|
||||
const match = trimmedModelId.match(FAMILY_FIRST_MODEL_PATTERN);
|
||||
if (!match) {
|
||||
return trimmedModelId;
|
||||
}
|
||||
|
||||
const [, family, providerPrefix, serviceTier] = match;
|
||||
const tierSuffix = serviceTier ? `__tier_${serviceTier.toLowerCase()}` : "";
|
||||
return `${providerPrefix}/${family}${tierSuffix}`;
|
||||
}
|
||||
|
||||
export function getFamilyFirstModelCandidates(actualModelId: string, family: string | null | undefined) {
|
||||
const normalizedFamily = normalizeFamily(family);
|
||||
const candidates = new Set<string>([actualModelId]);
|
||||
const publishedModelId = getFamilyFirstPublishedModelId(actualModelId, normalizedFamily);
|
||||
if (publishedModelId !== actualModelId) {
|
||||
candidates.add(publishedModelId);
|
||||
}
|
||||
|
||||
if (normalizedFamily && isFamilyFirstEligibleFamily(normalizedFamily)) {
|
||||
const tierSuffix = extractTierSuffix(actualModelId);
|
||||
candidates.add(`${normalizedFamily}${tierSuffix}`);
|
||||
}
|
||||
|
||||
return [...candidates];
|
||||
}
|
||||
export {
|
||||
getFamilyFirstModelCandidates,
|
||||
getFamilyFirstPublishedModelId,
|
||||
resolveFamilyFirstPublishedModelId,
|
||||
} from "@/lib/vscode/familyFirstModelIds";
|
||||
|
||||
@@ -1,177 +1,5 @@
|
||||
import { parseModel } from "@omniroute/open-sse/services/model";
|
||||
import {
|
||||
getCanonicalModelMetadata,
|
||||
type CanonicalModelMetadata,
|
||||
} from "@/lib/modelMetadataRegistry";
|
||||
import {
|
||||
getVscodeServiceTierVariantSuffix,
|
||||
parseVscodeServiceTierVariantModelId,
|
||||
supportsVscodeServiceTierVariants,
|
||||
} from "@/app/api/v1/vscode/[token]/serviceTierVariants";
|
||||
import { getReasoningVariantBaseModelId } from "@/app/api/v1/vscode/[token]/reasoningMetadata";
|
||||
import { resolveFamilyFirstPublishedModelId } from "@/app/api/v1/vscode/[token]/familyFirstModelIds";
|
||||
type VscodeCatalogModel = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
};
|
||||
|
||||
const PROVIDER_NAME_OVERRIDES: Record<string, string> = {
|
||||
codex: "Codex",
|
||||
cx: "Codex",
|
||||
github: "GitHub",
|
||||
gh: "GitHub",
|
||||
gemini: "Gemini",
|
||||
};
|
||||
|
||||
function tokenize(value: string) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.split(/[^a-z0-9]+/i)
|
||||
.map((part) => part.trim())
|
||||
.filter((part) => part.length >= 4);
|
||||
}
|
||||
|
||||
function getProviderPrefix(metadata: CanonicalModelMetadata | null) {
|
||||
const providerKey = metadata?.providerAlias || metadata?.provider || "";
|
||||
if (providerKey && PROVIDER_NAME_OVERRIDES[providerKey]) {
|
||||
return PROVIDER_NAME_OVERRIDES[providerKey];
|
||||
}
|
||||
|
||||
const providerLabel = metadata?.providerLabel?.trim() || null;
|
||||
if (!providerLabel) {
|
||||
return null;
|
||||
}
|
||||
if (/codex/i.test(providerLabel)) {
|
||||
return "Codex";
|
||||
}
|
||||
if (/github/i.test(providerLabel)) {
|
||||
return "GitHub";
|
||||
}
|
||||
if (/gemini/i.test(providerLabel)) {
|
||||
return "Gemini";
|
||||
}
|
||||
|
||||
return providerLabel;
|
||||
}
|
||||
|
||||
function normalizeDisplayNameBranding(displayName: string) {
|
||||
return displayName
|
||||
.replace(/^OpenAI\s+Codex\b/i, "Codex")
|
||||
.replace(/^GitHub\s+Copilot\b/i, "GitHub")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function stripLeadingProviderPrefix(displayName: string, providerPrefix: string | null) {
|
||||
if (!providerPrefix) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
const escapedProviderPrefix = providerPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
return displayName.replace(new RegExp(`^${escapedProviderPrefix}\\s+`, "i"), "").trim();
|
||||
}
|
||||
|
||||
function looksLikeTechnicalModelName(value: string) {
|
||||
return /\/|__provider_|__tier_|^[a-z0-9-]+\/[a-z0-9._-]+$/i.test(value);
|
||||
}
|
||||
|
||||
function humanizeModelIdentifier(modelId: string) {
|
||||
const identifier = (modelId.split("/").pop() || modelId).trim();
|
||||
if (!identifier) {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
return identifier
|
||||
.split(/[-_]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => {
|
||||
if (/^gpt$/i.test(part)) return "GPT";
|
||||
if (/^[0-9]+(?:\.[0-9]+)*$/.test(part)) return part;
|
||||
if (/^[a-z][0-9]$/i.test(part)) return part.toUpperCase();
|
||||
return part.charAt(0).toUpperCase() + part.slice(1);
|
||||
})
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function resolveFriendlyBaseDisplayName(
|
||||
rawModelId: string,
|
||||
metadata: CanonicalModelMetadata | null,
|
||||
fallbackValue: string
|
||||
) {
|
||||
const normalizedFallback = normalizeDisplayNameBranding(fallbackValue.trim());
|
||||
if (normalizedFallback && !looksLikeTechnicalModelName(normalizedFallback)) {
|
||||
return normalizedFallback;
|
||||
}
|
||||
|
||||
const normalizedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
const parsedTierModel = parseVscodeServiceTierVariantModelId(normalizedModelId);
|
||||
const canonicalBaseModelId = getReasoningVariantBaseModelId(parsedTierModel.baseModelId);
|
||||
const parsed = parseModel(canonicalBaseModelId, "");
|
||||
const providerModelId =
|
||||
parsed.model ||
|
||||
(canonicalBaseModelId.includes("/")
|
||||
? canonicalBaseModelId.split("/").slice(1).join("/")
|
||||
: canonicalBaseModelId) ||
|
||||
fallbackValue;
|
||||
|
||||
return humanizeModelIdentifier(providerModelId);
|
||||
}
|
||||
|
||||
function prefixDisplayName(displayName: string, providerPrefix: string | null) {
|
||||
const normalizedProviderPrefix = providerPrefix?.trim() || null;
|
||||
const normalizedDisplayName = normalizeDisplayNameBranding(displayName);
|
||||
|
||||
if (!normalizedProviderPrefix) return normalizedDisplayName;
|
||||
|
||||
const providerTokens = tokenize(normalizedProviderPrefix);
|
||||
if (providerTokens.length === 0) return normalizedDisplayName;
|
||||
|
||||
const displayNameLower = normalizedDisplayName.toLowerCase();
|
||||
if (providerTokens.some((token) => displayNameLower.includes(token))) {
|
||||
return normalizedDisplayName;
|
||||
}
|
||||
|
||||
return `${normalizedProviderPrefix} ${stripLeadingProviderPrefix(normalizedDisplayName, normalizedProviderPrefix)}`.trim();
|
||||
}
|
||||
|
||||
export function resolveVscodeModelMetadata(model: VscodeCatalogModel) {
|
||||
const rawModelId = model.id || model.root || model.name || "";
|
||||
const normalizedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
const parsedTierModel = parseVscodeServiceTierVariantModelId(normalizedModelId);
|
||||
const canonicalBaseModelId = getReasoningVariantBaseModelId(parsedTierModel.baseModelId);
|
||||
const parsed = parseModel(canonicalBaseModelId, "");
|
||||
const provider = parsed.provider || model.owned_by || undefined;
|
||||
const providerModel =
|
||||
parsed.model ||
|
||||
(canonicalBaseModelId.includes("/")
|
||||
? canonicalBaseModelId.split("/").slice(1).join("/")
|
||||
: canonicalBaseModelId) ||
|
||||
model.root ||
|
||||
model.id ||
|
||||
model.name ||
|
||||
undefined;
|
||||
|
||||
return providerModel && provider
|
||||
? getCanonicalModelMetadata({ provider, model: providerModel })
|
||||
: providerModel
|
||||
? getCanonicalModelMetadata({ model: providerModel })
|
||||
: null;
|
||||
}
|
||||
|
||||
export function getVscodeModelDisplayName(model: VscodeCatalogModel) {
|
||||
const rawModelId = model.id || model.root || model.name || "";
|
||||
const { serviceTier } = parseVscodeServiceTierVariantModelId(rawModelId);
|
||||
const metadata = resolveVscodeModelMetadata(model);
|
||||
const displayName = metadata?.displayName || model.name || model.id || model.root || "unknown";
|
||||
const prefixedDisplayName = prefixDisplayName(displayName, getProviderPrefix(metadata));
|
||||
const shouldShowTierSuffix = Boolean(serviceTier) || supportsVscodeServiceTierVariants(model);
|
||||
return shouldShowTierSuffix
|
||||
? `${prefixedDisplayName} (${getVscodeServiceTierVariantSuffix(serviceTier)})`
|
||||
: prefixedDisplayName;
|
||||
}
|
||||
|
||||
export function getVscodeModelGroupingKey(model: VscodeCatalogModel) {
|
||||
const metadata = resolveVscodeModelMetadata(model);
|
||||
return metadata?.qualifiedId || metadata?.model || model.id || model.name || model.root || "";
|
||||
}
|
||||
export {
|
||||
getVscodeModelDisplayName,
|
||||
getVscodeModelGroupingKey,
|
||||
resolveVscodeModelMetadata,
|
||||
} from "@/lib/vscode/modelPresentation";
|
||||
|
||||
@@ -1,175 +1,13 @@
|
||||
import { parseModel } from "@omniroute/open-sse/services/model";
|
||||
import { supportsXHighEffort } from "@omniroute/open-sse/config/providerModels";
|
||||
import { stripVscodeServiceTierVariantModelId } from "@/app/api/v1/vscode/[token]/serviceTierVariants";
|
||||
|
||||
export type VscodeCatalogModel = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
capabilities?: Record<string, boolean>;
|
||||
supportsReasoningEffort?: string[];
|
||||
supportedReasoningEfforts?: string[];
|
||||
supports_reasoning_effort?: string[];
|
||||
defaultReasoningEffort?: string;
|
||||
default_reasoning_effort?: string;
|
||||
};
|
||||
|
||||
const EFFORT_SUFFIX_PATTERN = /-(xhigh|high|medium|low|none)$/i;
|
||||
const DEFAULT_REASONING_EFFORT = "none";
|
||||
const KNOWN_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh"]);
|
||||
|
||||
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 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.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 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 the maximum available reasoning effort.";
|
||||
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 = modelId.match(EFFORT_SUFFIX_PATTERN);
|
||||
if (!match) return undefined;
|
||||
|
||||
const selected = match[1]?.toLowerCase();
|
||||
if (!selected) return undefined;
|
||||
if (Array.isArray(supportedValues) && supportedValues.length > 0 && !supportedValues.includes(selected)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
export function getReasoningVariantBaseModelId(modelId: string) {
|
||||
return modelId.replace(EFFORT_SUFFIX_PATTERN, "");
|
||||
}
|
||||
|
||||
export function getDefaultReasoningEffort(
|
||||
model: VscodeCatalogModel,
|
||||
supportedValues?: string[]
|
||||
) {
|
||||
return inferSelectedReasoningEffort(model, supportedValues) || 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),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
export {
|
||||
buildReasoningConfigSchema,
|
||||
buildSupportedReasoningEfforts,
|
||||
formatReasoningEffortLabel,
|
||||
getCatalogModelName,
|
||||
getDefaultReasoningEffort,
|
||||
getReasoningEffortValues,
|
||||
getReasoningVariantBaseModelId,
|
||||
inferSelectedReasoningEffort,
|
||||
isReasoningCapableModel,
|
||||
type VscodeCatalogModel,
|
||||
type VscodeModelConfigSchema,
|
||||
} from "@/lib/vscode/reasoningMetadata";
|
||||
|
||||
@@ -1,190 +1,12 @@
|
||||
import { CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS } from "@/lib/providers/codexFastTier";
|
||||
import { normalizeServiceTierId, type ServiceTierId } from "@/shared/utils/serviceTierLabels";
|
||||
import { resolveFamilyFirstPublishedModelId } from "@/app/api/v1/vscode/[token]/familyFirstModelIds";
|
||||
|
||||
const SERVICE_TIER_VARIANT_PATTERN = /__tier_(priority|flex)$/i;
|
||||
const SUPPORTED_VSCODE_SERVICE_TIERS: readonly ServiceTierId[] = ["priority", "flex"];
|
||||
|
||||
export type VscodeServiceTierModelLike = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
};
|
||||
|
||||
export function parseVscodeServiceTierVariantModelId(modelId: string | null | undefined): {
|
||||
baseModelId: string;
|
||||
serviceTier?: ServiceTierId;
|
||||
} {
|
||||
const rawModelId = typeof modelId === "string" ? modelId.trim() : "";
|
||||
if (!rawModelId) {
|
||||
return { baseModelId: "" };
|
||||
}
|
||||
|
||||
const match = rawModelId.match(SERVICE_TIER_VARIANT_PATTERN);
|
||||
if (!match) {
|
||||
return { baseModelId: rawModelId };
|
||||
}
|
||||
|
||||
const baseModelId = rawModelId.replace(SERVICE_TIER_VARIANT_PATTERN, "");
|
||||
const serviceTier = normalizeServiceTierId(match[1]);
|
||||
return serviceTier === "standard" ? { baseModelId } : { baseModelId, serviceTier };
|
||||
}
|
||||
|
||||
export function stripVscodeServiceTierVariantModelId(modelId: string | null | undefined): string {
|
||||
return parseVscodeServiceTierVariantModelId(modelId).baseModelId;
|
||||
}
|
||||
|
||||
export function isVscodeServiceTierVariantModelId(modelId: string | null | undefined): boolean {
|
||||
return Boolean(parseVscodeServiceTierVariantModelId(modelId).serviceTier);
|
||||
}
|
||||
|
||||
export function getVscodeServiceTierVariantModelId(
|
||||
baseModelId: string,
|
||||
serviceTier: ServiceTierId
|
||||
): string {
|
||||
if (serviceTier === "standard") {
|
||||
return baseModelId;
|
||||
}
|
||||
return `${baseModelId}__tier_${serviceTier}`;
|
||||
}
|
||||
|
||||
function getRawModelId(model: VscodeServiceTierModelLike): string {
|
||||
return (model.id || model.name || model.root || "").trim();
|
||||
}
|
||||
|
||||
function getModelProvider(model: VscodeServiceTierModelLike, baseModelId: string): string {
|
||||
const owner = typeof model.owned_by === "string" ? model.owned_by.trim().toLowerCase() : "";
|
||||
if (owner) {
|
||||
return owner;
|
||||
}
|
||||
const prefix = baseModelId.split("/")[0]?.trim().toLowerCase() || "";
|
||||
return prefix;
|
||||
}
|
||||
|
||||
function supportsCodexServiceTierModel(baseModelId: string): boolean {
|
||||
const normalizedModel = (baseModelId.split("/").pop() || baseModelId).trim().toLowerCase();
|
||||
if (!normalizedModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS.some((candidate) => {
|
||||
const normalizedCandidate = candidate.trim().toLowerCase();
|
||||
return normalizedModel === normalizedCandidate || normalizedModel.startsWith(normalizedCandidate);
|
||||
});
|
||||
}
|
||||
|
||||
export function supportsVscodeServiceTierVariants(model: VscodeServiceTierModelLike): boolean {
|
||||
const rawModelId = getRawModelId(model);
|
||||
if (!rawModelId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const baseModelId = stripVscodeServiceTierVariantModelId(rawModelId);
|
||||
const provider = getModelProvider(model, baseModelId);
|
||||
if (provider !== "codex" && provider !== "cx") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return supportsCodexServiceTierModel(baseModelId);
|
||||
}
|
||||
|
||||
function cloneModelIdentifiers<T extends VscodeServiceTierModelLike>(
|
||||
model: T,
|
||||
modelId: string
|
||||
): T {
|
||||
return {
|
||||
...model,
|
||||
...(model.id ? { id: modelId } : {}),
|
||||
...(model.name ? { name: modelId } : {}),
|
||||
...(model.root ? { root: modelId } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function expandVscodeServiceTierModels<T extends VscodeServiceTierModelLike>(models: T[]): T[] {
|
||||
const expanded: T[] = [];
|
||||
|
||||
for (const model of models) {
|
||||
const rawModelId = getRawModelId(model);
|
||||
if (!rawModelId) {
|
||||
expanded.push(model);
|
||||
continue;
|
||||
}
|
||||
|
||||
const baseModelId = stripVscodeServiceTierVariantModelId(rawModelId);
|
||||
const baseModel = rawModelId === baseModelId ? model : cloneModelIdentifiers(model, baseModelId);
|
||||
expanded.push(baseModel as T);
|
||||
|
||||
if (!supportsVscodeServiceTierVariants(model)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const serviceTier of SUPPORTED_VSCODE_SERVICE_TIERS) {
|
||||
expanded.push(cloneModelIdentifiers(baseModel as T, getVscodeServiceTierVariantModelId(baseModelId, serviceTier)));
|
||||
}
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
export function getVscodeServiceTierVariantSuffix(serviceTier: ServiceTierId | undefined): string {
|
||||
if (serviceTier === "priority") {
|
||||
return "Fast";
|
||||
}
|
||||
if (serviceTier === "flex") {
|
||||
return "Flex";
|
||||
}
|
||||
return "Default";
|
||||
}
|
||||
|
||||
export function resolveVscodeServiceTierRequest(body: Record<string, unknown>): Record<string, unknown> {
|
||||
const rawModelId = typeof body.model === "string" ? body.model.trim() : "";
|
||||
if (!rawModelId) {
|
||||
return body;
|
||||
}
|
||||
|
||||
const resolvedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
|
||||
const { baseModelId, serviceTier } = parseVscodeServiceTierVariantModelId(resolvedModelId);
|
||||
if (!serviceTier) {
|
||||
if (resolvedModelId === rawModelId) {
|
||||
return body;
|
||||
}
|
||||
|
||||
return {
|
||||
...body,
|
||||
model: resolvedModelId,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...body,
|
||||
model: baseModelId,
|
||||
...(body.service_tier === undefined ? { service_tier: serviceTier } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function rewriteVscodeServiceTierRequest(request: Request): Promise<Request> {
|
||||
if (request.method !== "POST") {
|
||||
return request;
|
||||
}
|
||||
|
||||
const body = await request.clone().json().catch(() => null);
|
||||
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const rewrittenBody = resolveVscodeServiceTierRequest(body as Record<string, unknown>);
|
||||
if (rewrittenBody === body) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const headers = new Headers(request.headers);
|
||||
headers.delete("content-length");
|
||||
|
||||
return new Request(request.url, {
|
||||
method: request.method,
|
||||
headers,
|
||||
body: JSON.stringify(rewrittenBody),
|
||||
});
|
||||
}
|
||||
export {
|
||||
expandVscodeServiceTierModels,
|
||||
getVscodeServiceTierVariantModelId,
|
||||
getVscodeServiceTierVariantSuffix,
|
||||
isVscodeServiceTierVariantModelId,
|
||||
parseVscodeServiceTierVariantModelId,
|
||||
resolveVscodeServiceTierRequest,
|
||||
rewriteVscodeServiceTierRequest,
|
||||
stripVscodeServiceTierVariantModelId,
|
||||
supportsVscodeServiceTierVariants,
|
||||
type VscodeServiceTierModelLike,
|
||||
} from "@/lib/vscode/serviceTierVariants";
|
||||
|
||||
@@ -1,85 +1,5 @@
|
||||
const FAMILY_FIRST_MODEL_PATTERN = /^((?:gpt-[a-z0-9._-]+|claude[a-z0-9._-]*))(?:__provider_([a-z0-9-]+))(?:__tier_(priority|flex))?$/i;
|
||||
const TIER_SUFFIX_PATTERN = /(__tier_(?:priority|flex))$/i;
|
||||
|
||||
function normalizeFamily(value: string | null | undefined) {
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function splitActualModelId(modelId: string) {
|
||||
const trimmedModelId = modelId.trim();
|
||||
const slashIndex = trimmedModelId.indexOf("/");
|
||||
if (slashIndex <= 0 || slashIndex === trimmedModelId.length - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
providerPrefix: trimmedModelId.slice(0, slashIndex),
|
||||
providerModelId: trimmedModelId.slice(slashIndex + 1),
|
||||
};
|
||||
}
|
||||
|
||||
function extractTierSuffix(modelId: string) {
|
||||
const match = modelId.match(TIER_SUFFIX_PATTERN);
|
||||
return match?.[1] || "";
|
||||
}
|
||||
|
||||
function stripTierSuffix(modelId: string) {
|
||||
return modelId.replace(TIER_SUFFIX_PATTERN, "");
|
||||
}
|
||||
|
||||
function isFamilyFirstEligibleFamily(family: string) {
|
||||
const normalized = family.toLowerCase();
|
||||
return normalized.startsWith("gpt-") || normalized.startsWith("claude");
|
||||
}
|
||||
|
||||
export function getFamilyFirstPublishedModelId(actualModelId: string, family: string | null | undefined) {
|
||||
const normalizedFamily = normalizeFamily(family);
|
||||
if (!normalizedFamily || !isFamilyFirstEligibleFamily(normalizedFamily)) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
const parts = splitActualModelId(actualModelId);
|
||||
if (!parts) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
const tierSuffix = extractTierSuffix(parts.providerModelId);
|
||||
const providerModelBase = stripTierSuffix(parts.providerModelId);
|
||||
if (providerModelBase !== normalizedFamily) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
return `${normalizedFamily}__provider_${parts.providerPrefix}${tierSuffix}`;
|
||||
}
|
||||
|
||||
export function resolveFamilyFirstPublishedModelId(modelId: string | null | undefined) {
|
||||
const trimmedModelId = typeof modelId === "string" ? modelId.trim() : "";
|
||||
if (!trimmedModelId) {
|
||||
return trimmedModelId;
|
||||
}
|
||||
|
||||
const match = trimmedModelId.match(FAMILY_FIRST_MODEL_PATTERN);
|
||||
if (!match) {
|
||||
return trimmedModelId;
|
||||
}
|
||||
|
||||
const [, family, providerPrefix, serviceTier] = match;
|
||||
const tierSuffix = serviceTier ? `__tier_${serviceTier.toLowerCase()}` : "";
|
||||
return `${providerPrefix}/${family}${tierSuffix}`;
|
||||
}
|
||||
|
||||
export function getFamilyFirstModelCandidates(actualModelId: string, family: string | null | undefined) {
|
||||
const normalizedFamily = normalizeFamily(family);
|
||||
const candidates = new Set<string>([actualModelId]);
|
||||
const publishedModelId = getFamilyFirstPublishedModelId(actualModelId, normalizedFamily);
|
||||
if (publishedModelId !== actualModelId) {
|
||||
candidates.add(publishedModelId);
|
||||
}
|
||||
|
||||
if (normalizedFamily && isFamilyFirstEligibleFamily(normalizedFamily)) {
|
||||
const tierSuffix = extractTierSuffix(actualModelId);
|
||||
candidates.add(`${normalizedFamily}${tierSuffix}`);
|
||||
}
|
||||
|
||||
return [...candidates];
|
||||
}
|
||||
export {
|
||||
getFamilyFirstModelCandidates,
|
||||
getFamilyFirstPublishedModelId,
|
||||
resolveFamilyFirstPublishedModelId,
|
||||
} from "@/lib/vscode/familyFirstModelIds";
|
||||
|
||||
@@ -1,177 +1,5 @@
|
||||
import { parseModel } from "@omniroute/open-sse/services/model";
|
||||
import {
|
||||
getCanonicalModelMetadata,
|
||||
type CanonicalModelMetadata,
|
||||
} from "@/lib/modelMetadataRegistry";
|
||||
import {
|
||||
getVscodeServiceTierVariantSuffix,
|
||||
parseVscodeServiceTierVariantModelId,
|
||||
supportsVscodeServiceTierVariants,
|
||||
} from "@/app/api/v1/vscode/raw/[token]/serviceTierVariants";
|
||||
import { getReasoningVariantBaseModelId } from "@/app/api/v1/vscode/raw/[token]/reasoningMetadata";
|
||||
import { resolveFamilyFirstPublishedModelId } from "@/app/api/v1/vscode/raw/[token]/familyFirstModelIds";
|
||||
type VscodeCatalogModel = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
};
|
||||
|
||||
const PROVIDER_NAME_OVERRIDES: Record<string, string> = {
|
||||
codex: "Codex",
|
||||
cx: "Codex",
|
||||
github: "GitHub",
|
||||
gh: "GitHub",
|
||||
gemini: "Gemini",
|
||||
};
|
||||
|
||||
function tokenize(value: string) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.split(/[^a-z0-9]+/i)
|
||||
.map((part) => part.trim())
|
||||
.filter((part) => part.length >= 4);
|
||||
}
|
||||
|
||||
function getProviderPrefix(metadata: CanonicalModelMetadata | null) {
|
||||
const providerKey = metadata?.providerAlias || metadata?.provider || "";
|
||||
if (providerKey && PROVIDER_NAME_OVERRIDES[providerKey]) {
|
||||
return PROVIDER_NAME_OVERRIDES[providerKey];
|
||||
}
|
||||
|
||||
const providerLabel = metadata?.providerLabel?.trim() || null;
|
||||
if (!providerLabel) {
|
||||
return null;
|
||||
}
|
||||
if (/codex/i.test(providerLabel)) {
|
||||
return "Codex";
|
||||
}
|
||||
if (/github/i.test(providerLabel)) {
|
||||
return "GitHub";
|
||||
}
|
||||
if (/gemini/i.test(providerLabel)) {
|
||||
return "Gemini";
|
||||
}
|
||||
|
||||
return providerLabel;
|
||||
}
|
||||
|
||||
function normalizeDisplayNameBranding(displayName: string) {
|
||||
return displayName
|
||||
.replace(/^OpenAI\s+Codex\b/i, "Codex")
|
||||
.replace(/^GitHub\s+Copilot\b/i, "GitHub")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function stripLeadingProviderPrefix(displayName: string, providerPrefix: string | null) {
|
||||
if (!providerPrefix) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
const escapedProviderPrefix = providerPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
return displayName.replace(new RegExp(`^${escapedProviderPrefix}\\s+`, "i"), "").trim();
|
||||
}
|
||||
|
||||
function looksLikeTechnicalModelName(value: string) {
|
||||
return /\/|__provider_|__tier_|^[a-z0-9-]+\/[a-z0-9._-]+$/i.test(value);
|
||||
}
|
||||
|
||||
function humanizeModelIdentifier(modelId: string) {
|
||||
const identifier = (modelId.split("/").pop() || modelId).trim();
|
||||
if (!identifier) {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
return identifier
|
||||
.split(/[-_]+/)
|
||||
.filter(Boolean)
|
||||
.map((part) => {
|
||||
if (/^gpt$/i.test(part)) return "GPT";
|
||||
if (/^[0-9]+(?:\.[0-9]+)*$/.test(part)) return part;
|
||||
if (/^[a-z][0-9]$/i.test(part)) return part.toUpperCase();
|
||||
return part.charAt(0).toUpperCase() + part.slice(1);
|
||||
})
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
function resolveFriendlyBaseDisplayName(
|
||||
rawModelId: string,
|
||||
metadata: CanonicalModelMetadata | null,
|
||||
fallbackValue: string
|
||||
) {
|
||||
const normalizedFallback = normalizeDisplayNameBranding(fallbackValue.trim());
|
||||
if (normalizedFallback && !looksLikeTechnicalModelName(normalizedFallback)) {
|
||||
return normalizedFallback;
|
||||
}
|
||||
|
||||
const normalizedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
const parsedTierModel = parseVscodeServiceTierVariantModelId(normalizedModelId);
|
||||
const canonicalBaseModelId = getReasoningVariantBaseModelId(parsedTierModel.baseModelId);
|
||||
const parsed = parseModel(canonicalBaseModelId, "");
|
||||
const providerModelId =
|
||||
parsed.model ||
|
||||
(canonicalBaseModelId.includes("/")
|
||||
? canonicalBaseModelId.split("/").slice(1).join("/")
|
||||
: canonicalBaseModelId) ||
|
||||
fallbackValue;
|
||||
|
||||
return humanizeModelIdentifier(providerModelId);
|
||||
}
|
||||
|
||||
function prefixDisplayName(displayName: string, providerPrefix: string | null) {
|
||||
const normalizedProviderPrefix = providerPrefix?.trim() || null;
|
||||
const normalizedDisplayName = normalizeDisplayNameBranding(displayName);
|
||||
|
||||
if (!normalizedProviderPrefix) return normalizedDisplayName;
|
||||
|
||||
const providerTokens = tokenize(normalizedProviderPrefix);
|
||||
if (providerTokens.length === 0) return normalizedDisplayName;
|
||||
|
||||
const displayNameLower = normalizedDisplayName.toLowerCase();
|
||||
if (providerTokens.some((token) => displayNameLower.includes(token))) {
|
||||
return normalizedDisplayName;
|
||||
}
|
||||
|
||||
return `${normalizedProviderPrefix} ${stripLeadingProviderPrefix(normalizedDisplayName, normalizedProviderPrefix)}`.trim();
|
||||
}
|
||||
|
||||
export function resolveVscodeModelMetadata(model: VscodeCatalogModel) {
|
||||
const rawModelId = model.id || model.root || model.name || "";
|
||||
const normalizedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
const parsedTierModel = parseVscodeServiceTierVariantModelId(normalizedModelId);
|
||||
const canonicalBaseModelId = getReasoningVariantBaseModelId(parsedTierModel.baseModelId);
|
||||
const parsed = parseModel(canonicalBaseModelId, "");
|
||||
const provider = parsed.provider || model.owned_by || undefined;
|
||||
const providerModel =
|
||||
parsed.model ||
|
||||
(canonicalBaseModelId.includes("/")
|
||||
? canonicalBaseModelId.split("/").slice(1).join("/")
|
||||
: canonicalBaseModelId) ||
|
||||
model.root ||
|
||||
model.id ||
|
||||
model.name ||
|
||||
undefined;
|
||||
|
||||
return providerModel && provider
|
||||
? getCanonicalModelMetadata({ provider, model: providerModel })
|
||||
: providerModel
|
||||
? getCanonicalModelMetadata({ model: providerModel })
|
||||
: null;
|
||||
}
|
||||
|
||||
export function getVscodeModelDisplayName(model: VscodeCatalogModel) {
|
||||
const rawModelId = model.id || model.root || model.name || "";
|
||||
const { serviceTier } = parseVscodeServiceTierVariantModelId(rawModelId);
|
||||
const metadata = resolveVscodeModelMetadata(model);
|
||||
const displayName = metadata?.displayName || model.name || model.id || model.root || "unknown";
|
||||
const prefixedDisplayName = prefixDisplayName(displayName, getProviderPrefix(metadata));
|
||||
const shouldShowTierSuffix = Boolean(serviceTier) || supportsVscodeServiceTierVariants(model);
|
||||
return shouldShowTierSuffix
|
||||
? `${prefixedDisplayName} (${getVscodeServiceTierVariantSuffix(serviceTier)})`
|
||||
: prefixedDisplayName;
|
||||
}
|
||||
|
||||
export function getVscodeModelGroupingKey(model: VscodeCatalogModel) {
|
||||
const metadata = resolveVscodeModelMetadata(model);
|
||||
return metadata?.qualifiedId || metadata?.model || model.id || model.name || model.root || "";
|
||||
}
|
||||
export {
|
||||
getVscodeModelDisplayName,
|
||||
getVscodeModelGroupingKey,
|
||||
resolveVscodeModelMetadata,
|
||||
} from "@/lib/vscode/modelPresentation";
|
||||
|
||||
@@ -1,175 +1,13 @@
|
||||
import { parseModel } from "@omniroute/open-sse/services/model";
|
||||
import { supportsXHighEffort } from "@omniroute/open-sse/config/providerModels";
|
||||
import { stripVscodeServiceTierVariantModelId } from "@/app/api/v1/vscode/raw/[token]/serviceTierVariants";
|
||||
|
||||
export type VscodeCatalogModel = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
capabilities?: Record<string, boolean>;
|
||||
supportsReasoningEffort?: string[];
|
||||
supportedReasoningEfforts?: string[];
|
||||
supports_reasoning_effort?: string[];
|
||||
defaultReasoningEffort?: string;
|
||||
default_reasoning_effort?: string;
|
||||
};
|
||||
|
||||
const EFFORT_SUFFIX_PATTERN = /-(xhigh|high|medium|low|none)$/i;
|
||||
const DEFAULT_REASONING_EFFORT = "none";
|
||||
const KNOWN_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh"]);
|
||||
|
||||
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 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.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 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 the maximum available reasoning effort.";
|
||||
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 = modelId.match(EFFORT_SUFFIX_PATTERN);
|
||||
if (!match) return undefined;
|
||||
|
||||
const selected = match[1]?.toLowerCase();
|
||||
if (!selected) return undefined;
|
||||
if (Array.isArray(supportedValues) && supportedValues.length > 0 && !supportedValues.includes(selected)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
export function getReasoningVariantBaseModelId(modelId: string) {
|
||||
return modelId.replace(EFFORT_SUFFIX_PATTERN, "");
|
||||
}
|
||||
|
||||
export function getDefaultReasoningEffort(
|
||||
model: VscodeCatalogModel,
|
||||
supportedValues?: string[]
|
||||
) {
|
||||
return inferSelectedReasoningEffort(model, supportedValues) || 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),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
export {
|
||||
buildReasoningConfigSchema,
|
||||
buildSupportedReasoningEfforts,
|
||||
formatReasoningEffortLabel,
|
||||
getCatalogModelName,
|
||||
getDefaultReasoningEffort,
|
||||
getReasoningEffortValues,
|
||||
getReasoningVariantBaseModelId,
|
||||
inferSelectedReasoningEffort,
|
||||
isReasoningCapableModel,
|
||||
type VscodeCatalogModel,
|
||||
type VscodeModelConfigSchema,
|
||||
} from "@/lib/vscode/reasoningMetadata";
|
||||
|
||||
@@ -1,190 +1,12 @@
|
||||
import { CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS } from "@/lib/providers/codexFastTier";
|
||||
import { normalizeServiceTierId, type ServiceTierId } from "@/shared/utils/serviceTierLabels";
|
||||
import { resolveFamilyFirstPublishedModelId } from "@/app/api/v1/vscode/raw/[token]/familyFirstModelIds";
|
||||
|
||||
const SERVICE_TIER_VARIANT_PATTERN = /__tier_(priority|flex)$/i;
|
||||
const SUPPORTED_VSCODE_SERVICE_TIERS: readonly ServiceTierId[] = ["priority", "flex"];
|
||||
|
||||
export type VscodeServiceTierModelLike = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
};
|
||||
|
||||
export function parseVscodeServiceTierVariantModelId(modelId: string | null | undefined): {
|
||||
baseModelId: string;
|
||||
serviceTier?: ServiceTierId;
|
||||
} {
|
||||
const rawModelId = typeof modelId === "string" ? modelId.trim() : "";
|
||||
if (!rawModelId) {
|
||||
return { baseModelId: "" };
|
||||
}
|
||||
|
||||
const match = rawModelId.match(SERVICE_TIER_VARIANT_PATTERN);
|
||||
if (!match) {
|
||||
return { baseModelId: rawModelId };
|
||||
}
|
||||
|
||||
const baseModelId = rawModelId.replace(SERVICE_TIER_VARIANT_PATTERN, "");
|
||||
const serviceTier = normalizeServiceTierId(match[1]);
|
||||
return serviceTier === "standard" ? { baseModelId } : { baseModelId, serviceTier };
|
||||
}
|
||||
|
||||
export function stripVscodeServiceTierVariantModelId(modelId: string | null | undefined): string {
|
||||
return parseVscodeServiceTierVariantModelId(modelId).baseModelId;
|
||||
}
|
||||
|
||||
export function isVscodeServiceTierVariantModelId(modelId: string | null | undefined): boolean {
|
||||
return Boolean(parseVscodeServiceTierVariantModelId(modelId).serviceTier);
|
||||
}
|
||||
|
||||
export function getVscodeServiceTierVariantModelId(
|
||||
baseModelId: string,
|
||||
serviceTier: ServiceTierId
|
||||
): string {
|
||||
if (serviceTier === "standard") {
|
||||
return baseModelId;
|
||||
}
|
||||
return `${baseModelId}__tier_${serviceTier}`;
|
||||
}
|
||||
|
||||
function getRawModelId(model: VscodeServiceTierModelLike): string {
|
||||
return (model.id || model.name || model.root || "").trim();
|
||||
}
|
||||
|
||||
function getModelProvider(model: VscodeServiceTierModelLike, baseModelId: string): string {
|
||||
const owner = typeof model.owned_by === "string" ? model.owned_by.trim().toLowerCase() : "";
|
||||
if (owner) {
|
||||
return owner;
|
||||
}
|
||||
const prefix = baseModelId.split("/")[0]?.trim().toLowerCase() || "";
|
||||
return prefix;
|
||||
}
|
||||
|
||||
function supportsCodexServiceTierModel(baseModelId: string): boolean {
|
||||
const normalizedModel = (baseModelId.split("/").pop() || baseModelId).trim().toLowerCase();
|
||||
if (!normalizedModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS.some((candidate) => {
|
||||
const normalizedCandidate = candidate.trim().toLowerCase();
|
||||
return normalizedModel === normalizedCandidate || normalizedModel.startsWith(normalizedCandidate);
|
||||
});
|
||||
}
|
||||
|
||||
export function supportsVscodeServiceTierVariants(model: VscodeServiceTierModelLike): boolean {
|
||||
const rawModelId = getRawModelId(model);
|
||||
if (!rawModelId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const baseModelId = stripVscodeServiceTierVariantModelId(rawModelId);
|
||||
const provider = getModelProvider(model, baseModelId);
|
||||
if (provider !== "codex" && provider !== "cx") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return supportsCodexServiceTierModel(baseModelId);
|
||||
}
|
||||
|
||||
function cloneModelIdentifiers<T extends VscodeServiceTierModelLike>(
|
||||
model: T,
|
||||
modelId: string
|
||||
): T {
|
||||
return {
|
||||
...model,
|
||||
...(model.id ? { id: modelId } : {}),
|
||||
...(model.name ? { name: modelId } : {}),
|
||||
...(model.root ? { root: modelId } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function expandVscodeServiceTierModels<T extends VscodeServiceTierModelLike>(models: T[]): T[] {
|
||||
const expanded: T[] = [];
|
||||
|
||||
for (const model of models) {
|
||||
const rawModelId = getRawModelId(model);
|
||||
if (!rawModelId) {
|
||||
expanded.push(model);
|
||||
continue;
|
||||
}
|
||||
|
||||
const baseModelId = stripVscodeServiceTierVariantModelId(rawModelId);
|
||||
const baseModel = rawModelId === baseModelId ? model : cloneModelIdentifiers(model, baseModelId);
|
||||
expanded.push(baseModel as T);
|
||||
|
||||
if (!supportsVscodeServiceTierVariants(model)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const serviceTier of SUPPORTED_VSCODE_SERVICE_TIERS) {
|
||||
expanded.push(cloneModelIdentifiers(baseModel as T, getVscodeServiceTierVariantModelId(baseModelId, serviceTier)));
|
||||
}
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
export function getVscodeServiceTierVariantSuffix(serviceTier: ServiceTierId | undefined): string {
|
||||
if (serviceTier === "priority") {
|
||||
return "Fast";
|
||||
}
|
||||
if (serviceTier === "flex") {
|
||||
return "Flex";
|
||||
}
|
||||
return "Default";
|
||||
}
|
||||
|
||||
export function resolveVscodeServiceTierRequest(body: Record<string, unknown>): Record<string, unknown> {
|
||||
const rawModelId = typeof body.model === "string" ? body.model.trim() : "";
|
||||
if (!rawModelId) {
|
||||
return body;
|
||||
}
|
||||
|
||||
const resolvedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
|
||||
const { baseModelId, serviceTier } = parseVscodeServiceTierVariantModelId(resolvedModelId);
|
||||
if (!serviceTier) {
|
||||
if (resolvedModelId === rawModelId) {
|
||||
return body;
|
||||
}
|
||||
|
||||
return {
|
||||
...body,
|
||||
model: resolvedModelId,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...body,
|
||||
model: baseModelId,
|
||||
...(body.service_tier === undefined ? { service_tier: serviceTier } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function rewriteVscodeServiceTierRequest(request: Request): Promise<Request> {
|
||||
if (request.method !== "POST") {
|
||||
return request;
|
||||
}
|
||||
|
||||
const body = await request.clone().json().catch(() => null);
|
||||
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const rewrittenBody = resolveVscodeServiceTierRequest(body as Record<string, unknown>);
|
||||
if (rewrittenBody === body) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const headers = new Headers(request.headers);
|
||||
headers.delete("content-length");
|
||||
|
||||
return new Request(request.url, {
|
||||
method: request.method,
|
||||
headers,
|
||||
body: JSON.stringify(rewrittenBody),
|
||||
});
|
||||
}
|
||||
export {
|
||||
expandVscodeServiceTierModels,
|
||||
getVscodeServiceTierVariantModelId,
|
||||
getVscodeServiceTierVariantSuffix,
|
||||
isVscodeServiceTierVariantModelId,
|
||||
parseVscodeServiceTierVariantModelId,
|
||||
resolveVscodeServiceTierRequest,
|
||||
rewriteVscodeServiceTierRequest,
|
||||
stripVscodeServiceTierVariantModelId,
|
||||
supportsVscodeServiceTierVariants,
|
||||
type VscodeServiceTierModelLike,
|
||||
} from "@/lib/vscode/serviceTierVariants";
|
||||
|
||||
92
src/lib/vscode/familyFirstModelIds.ts
Normal file
92
src/lib/vscode/familyFirstModelIds.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
const FAMILY_FIRST_MODEL_PATTERN =
|
||||
/^((?:gpt-[a-z0-9._-]+|claude[a-z0-9._-]*))(?:__provider_([a-z0-9-]+))(?:__tier_(priority|flex))?$/i;
|
||||
const TIER_SUFFIX_PATTERN = /(__tier_(?:priority|flex))$/i;
|
||||
|
||||
function normalizeFamily(value: string | null | undefined) {
|
||||
return typeof value === "string" ? value.trim() : "";
|
||||
}
|
||||
|
||||
function splitActualModelId(modelId: string) {
|
||||
const trimmedModelId = modelId.trim();
|
||||
const slashIndex = trimmedModelId.indexOf("/");
|
||||
if (slashIndex <= 0 || slashIndex === trimmedModelId.length - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
providerPrefix: trimmedModelId.slice(0, slashIndex),
|
||||
providerModelId: trimmedModelId.slice(slashIndex + 1),
|
||||
};
|
||||
}
|
||||
|
||||
function extractTierSuffix(modelId: string) {
|
||||
const match = modelId.match(TIER_SUFFIX_PATTERN);
|
||||
return match?.[1] || "";
|
||||
}
|
||||
|
||||
function stripTierSuffix(modelId: string) {
|
||||
return modelId.replace(TIER_SUFFIX_PATTERN, "");
|
||||
}
|
||||
|
||||
function isFamilyFirstEligibleFamily(family: string) {
|
||||
const normalized = family.toLowerCase();
|
||||
return normalized.startsWith("gpt-") || normalized.startsWith("claude");
|
||||
}
|
||||
|
||||
export function getFamilyFirstPublishedModelId(
|
||||
actualModelId: string,
|
||||
family: string | null | undefined
|
||||
) {
|
||||
const normalizedFamily = normalizeFamily(family);
|
||||
if (!normalizedFamily || !isFamilyFirstEligibleFamily(normalizedFamily)) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
const parts = splitActualModelId(actualModelId);
|
||||
if (!parts) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
const tierSuffix = extractTierSuffix(parts.providerModelId);
|
||||
const providerModelBase = stripTierSuffix(parts.providerModelId);
|
||||
if (providerModelBase !== normalizedFamily) {
|
||||
return actualModelId;
|
||||
}
|
||||
|
||||
return `${normalizedFamily}__provider_${parts.providerPrefix}${tierSuffix}`;
|
||||
}
|
||||
|
||||
export function resolveFamilyFirstPublishedModelId(modelId: string | null | undefined) {
|
||||
const trimmedModelId = typeof modelId === "string" ? modelId.trim() : "";
|
||||
if (!trimmedModelId) {
|
||||
return trimmedModelId;
|
||||
}
|
||||
|
||||
const match = trimmedModelId.match(FAMILY_FIRST_MODEL_PATTERN);
|
||||
if (!match) {
|
||||
return trimmedModelId;
|
||||
}
|
||||
|
||||
const [, family, providerPrefix, serviceTier] = match;
|
||||
const tierSuffix = serviceTier ? `__tier_${serviceTier.toLowerCase()}` : "";
|
||||
return `${providerPrefix}/${family}${tierSuffix}`;
|
||||
}
|
||||
|
||||
export function getFamilyFirstModelCandidates(
|
||||
actualModelId: string,
|
||||
family: string | null | undefined
|
||||
) {
|
||||
const normalizedFamily = normalizeFamily(family);
|
||||
const candidates = new Set<string>([actualModelId]);
|
||||
const publishedModelId = getFamilyFirstPublishedModelId(actualModelId, normalizedFamily);
|
||||
if (publishedModelId !== actualModelId) {
|
||||
candidates.add(publishedModelId);
|
||||
}
|
||||
|
||||
if (normalizedFamily && isFamilyFirstEligibleFamily(normalizedFamily)) {
|
||||
const tierSuffix = extractTierSuffix(actualModelId);
|
||||
candidates.add(`${normalizedFamily}${tierSuffix}`);
|
||||
}
|
||||
|
||||
return [...candidates];
|
||||
}
|
||||
132
src/lib/vscode/modelPresentation.ts
Normal file
132
src/lib/vscode/modelPresentation.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { parseModel } from "@omniroute/open-sse/services/model";
|
||||
import {
|
||||
getCanonicalModelMetadata,
|
||||
type CanonicalModelMetadata,
|
||||
} from "@/lib/modelMetadataRegistry";
|
||||
import { resolveFamilyFirstPublishedModelId } from "@/lib/vscode/familyFirstModelIds";
|
||||
import { getReasoningVariantBaseModelId } from "@/lib/vscode/reasoningMetadata";
|
||||
import {
|
||||
getVscodeServiceTierVariantSuffix,
|
||||
parseVscodeServiceTierVariantModelId,
|
||||
supportsVscodeServiceTierVariants,
|
||||
} from "@/lib/vscode/serviceTierVariants";
|
||||
|
||||
type VscodeCatalogModel = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
};
|
||||
|
||||
const PROVIDER_NAME_OVERRIDES: Record<string, string> = {
|
||||
codex: "Codex",
|
||||
cx: "Codex",
|
||||
github: "GitHub",
|
||||
gh: "GitHub",
|
||||
gemini: "Gemini",
|
||||
};
|
||||
|
||||
function tokenize(value: string) {
|
||||
return value
|
||||
.toLowerCase()
|
||||
.split(/[^a-z0-9]+/i)
|
||||
.map((part) => part.trim())
|
||||
.filter((part) => part.length >= 4);
|
||||
}
|
||||
|
||||
function getProviderPrefix(metadata: CanonicalModelMetadata | null) {
|
||||
const providerKey = metadata?.providerAlias || metadata?.provider || "";
|
||||
if (providerKey && PROVIDER_NAME_OVERRIDES[providerKey]) {
|
||||
return PROVIDER_NAME_OVERRIDES[providerKey];
|
||||
}
|
||||
|
||||
const providerLabel = metadata?.providerLabel?.trim() || null;
|
||||
if (!providerLabel) {
|
||||
return null;
|
||||
}
|
||||
if (/codex/i.test(providerLabel)) {
|
||||
return "Codex";
|
||||
}
|
||||
if (/github/i.test(providerLabel)) {
|
||||
return "GitHub";
|
||||
}
|
||||
if (/gemini/i.test(providerLabel)) {
|
||||
return "Gemini";
|
||||
}
|
||||
|
||||
return providerLabel;
|
||||
}
|
||||
|
||||
function normalizeDisplayNameBranding(displayName: string) {
|
||||
return displayName
|
||||
.replace(/^OpenAI\s+Codex\b/i, "Codex")
|
||||
.replace(/^GitHub\s+Copilot\b/i, "GitHub")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function stripLeadingProviderPrefix(displayName: string, providerPrefix: string | null) {
|
||||
if (!providerPrefix) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
const escapedProviderPrefix = providerPrefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||
return displayName.replace(new RegExp(`^${escapedProviderPrefix}\\s+`, "i"), "").trim();
|
||||
}
|
||||
|
||||
function prefixDisplayName(displayName: string, providerPrefix: string | null) {
|
||||
const normalizedProviderPrefix = providerPrefix?.trim() || null;
|
||||
const normalizedDisplayName = normalizeDisplayNameBranding(displayName);
|
||||
|
||||
if (!normalizedProviderPrefix) return normalizedDisplayName;
|
||||
|
||||
const providerTokens = tokenize(normalizedProviderPrefix);
|
||||
if (providerTokens.length === 0) return normalizedDisplayName;
|
||||
|
||||
const displayNameLower = normalizedDisplayName.toLowerCase();
|
||||
if (providerTokens.some((token) => displayNameLower.includes(token))) {
|
||||
return normalizedDisplayName;
|
||||
}
|
||||
|
||||
return `${normalizedProviderPrefix} ${stripLeadingProviderPrefix(normalizedDisplayName, normalizedProviderPrefix)}`.trim();
|
||||
}
|
||||
|
||||
export function resolveVscodeModelMetadata(model: VscodeCatalogModel) {
|
||||
const rawModelId = model.id || model.root || model.name || "";
|
||||
const normalizedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
const parsedTierModel = parseVscodeServiceTierVariantModelId(normalizedModelId);
|
||||
const canonicalBaseModelId = getReasoningVariantBaseModelId(parsedTierModel.baseModelId);
|
||||
const parsed = parseModel(canonicalBaseModelId, "");
|
||||
const provider = parsed.provider || model.owned_by || undefined;
|
||||
const providerModel =
|
||||
parsed.model ||
|
||||
(canonicalBaseModelId.includes("/")
|
||||
? canonicalBaseModelId.split("/").slice(1).join("/")
|
||||
: canonicalBaseModelId) ||
|
||||
model.root ||
|
||||
model.id ||
|
||||
model.name ||
|
||||
undefined;
|
||||
|
||||
return providerModel && provider
|
||||
? getCanonicalModelMetadata({ provider, model: providerModel })
|
||||
: providerModel
|
||||
? getCanonicalModelMetadata({ model: providerModel })
|
||||
: null;
|
||||
}
|
||||
|
||||
export function getVscodeModelDisplayName(model: VscodeCatalogModel) {
|
||||
const rawModelId = model.id || model.root || model.name || "";
|
||||
const { serviceTier } = parseVscodeServiceTierVariantModelId(rawModelId);
|
||||
const metadata = resolveVscodeModelMetadata(model);
|
||||
const displayName = metadata?.displayName || model.name || model.id || model.root || "unknown";
|
||||
const prefixedDisplayName = prefixDisplayName(displayName, getProviderPrefix(metadata));
|
||||
const shouldShowTierSuffix = Boolean(serviceTier) || supportsVscodeServiceTierVariants(model);
|
||||
return shouldShowTierSuffix
|
||||
? `${prefixedDisplayName} (${getVscodeServiceTierVariantSuffix(serviceTier)})`
|
||||
: prefixedDisplayName;
|
||||
}
|
||||
|
||||
export function getVscodeModelGroupingKey(model: VscodeCatalogModel) {
|
||||
const metadata = resolveVscodeModelMetadata(model);
|
||||
return metadata?.qualifiedId || metadata?.model || model.id || model.name || model.root || "";
|
||||
}
|
||||
183
src/lib/vscode/reasoningMetadata.ts
Normal file
183
src/lib/vscode/reasoningMetadata.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import { supportsXHighEffort } from "@omniroute/open-sse/config/providerModels";
|
||||
import { parseModel } from "@omniroute/open-sse/services/model";
|
||||
import { stripVscodeServiceTierVariantModelId } from "@/lib/vscode/serviceTierVariants";
|
||||
|
||||
export type VscodeCatalogModel = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
capabilities?: Record<string, boolean>;
|
||||
supportsReasoningEffort?: string[];
|
||||
supportedReasoningEfforts?: string[];
|
||||
supports_reasoning_effort?: string[];
|
||||
defaultReasoningEffort?: string;
|
||||
default_reasoning_effort?: string;
|
||||
};
|
||||
|
||||
const EFFORT_SUFFIX_PATTERN = /-(xhigh|high|medium|low|none)$/i;
|
||||
const DEFAULT_REASONING_EFFORT = "none";
|
||||
const KNOWN_REASONING_EFFORTS = new Set(["none", "low", "medium", "high", "xhigh"]);
|
||||
|
||||
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 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.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 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 the maximum available reasoning effort.";
|
||||
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 = modelId.match(EFFORT_SUFFIX_PATTERN);
|
||||
if (!match) return undefined;
|
||||
|
||||
const selected = match[1]?.toLowerCase();
|
||||
if (!selected) return undefined;
|
||||
if (
|
||||
Array.isArray(supportedValues) &&
|
||||
supportedValues.length > 0 &&
|
||||
!supportedValues.includes(selected)
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
export function getReasoningVariantBaseModelId(modelId: string) {
|
||||
return modelId.replace(EFFORT_SUFFIX_PATTERN, "");
|
||||
}
|
||||
|
||||
export function getDefaultReasoningEffort(model: VscodeCatalogModel, supportedValues?: string[]) {
|
||||
return inferSelectedReasoningEffort(model, supportedValues) || 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),
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
202
src/lib/vscode/serviceTierVariants.ts
Normal file
202
src/lib/vscode/serviceTierVariants.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
import { CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS } from "@/lib/providers/codexFastTier";
|
||||
import { resolveFamilyFirstPublishedModelId } from "@/lib/vscode/familyFirstModelIds";
|
||||
import { normalizeServiceTierId, type ServiceTierId } from "@/shared/utils/serviceTierLabels";
|
||||
|
||||
const SERVICE_TIER_VARIANT_PATTERN = /__tier_(priority|flex)$/i;
|
||||
const SUPPORTED_VSCODE_SERVICE_TIERS: readonly ServiceTierId[] = ["priority", "flex"];
|
||||
|
||||
export type VscodeServiceTierModelLike = {
|
||||
id?: string;
|
||||
name?: string;
|
||||
root?: string;
|
||||
owned_by?: string;
|
||||
};
|
||||
|
||||
export function parseVscodeServiceTierVariantModelId(modelId: string | null | undefined): {
|
||||
baseModelId: string;
|
||||
serviceTier?: ServiceTierId;
|
||||
} {
|
||||
const rawModelId = typeof modelId === "string" ? modelId.trim() : "";
|
||||
if (!rawModelId) {
|
||||
return { baseModelId: "" };
|
||||
}
|
||||
|
||||
const match = rawModelId.match(SERVICE_TIER_VARIANT_PATTERN);
|
||||
if (!match) {
|
||||
return { baseModelId: rawModelId };
|
||||
}
|
||||
|
||||
const baseModelId = rawModelId.replace(SERVICE_TIER_VARIANT_PATTERN, "");
|
||||
const serviceTier = normalizeServiceTierId(match[1]);
|
||||
return serviceTier === "standard" ? { baseModelId } : { baseModelId, serviceTier };
|
||||
}
|
||||
|
||||
export function stripVscodeServiceTierVariantModelId(modelId: string | null | undefined): string {
|
||||
return parseVscodeServiceTierVariantModelId(modelId).baseModelId;
|
||||
}
|
||||
|
||||
export function isVscodeServiceTierVariantModelId(modelId: string | null | undefined): boolean {
|
||||
return Boolean(parseVscodeServiceTierVariantModelId(modelId).serviceTier);
|
||||
}
|
||||
|
||||
export function getVscodeServiceTierVariantModelId(
|
||||
baseModelId: string,
|
||||
serviceTier: ServiceTierId
|
||||
): string {
|
||||
if (serviceTier === "standard") {
|
||||
return baseModelId;
|
||||
}
|
||||
return `${baseModelId}__tier_${serviceTier}`;
|
||||
}
|
||||
|
||||
function getRawModelId(model: VscodeServiceTierModelLike): string {
|
||||
return (model.id || model.name || model.root || "").trim();
|
||||
}
|
||||
|
||||
function getModelProvider(model: VscodeServiceTierModelLike, baseModelId: string): string {
|
||||
const owner = typeof model.owned_by === "string" ? model.owned_by.trim().toLowerCase() : "";
|
||||
if (owner) {
|
||||
return owner;
|
||||
}
|
||||
const prefix = baseModelId.split("/")[0]?.trim().toLowerCase() || "";
|
||||
return prefix;
|
||||
}
|
||||
|
||||
function supportsCodexServiceTierModel(baseModelId: string): boolean {
|
||||
const normalizedModel = (baseModelId.split("/").pop() || baseModelId).trim().toLowerCase();
|
||||
if (!normalizedModel) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return CODEX_FAST_TIER_DEFAULT_SUPPORTED_MODELS.some((candidate) => {
|
||||
const normalizedCandidate = candidate.trim().toLowerCase();
|
||||
return (
|
||||
normalizedModel === normalizedCandidate || normalizedModel.startsWith(normalizedCandidate)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function supportsVscodeServiceTierVariants(model: VscodeServiceTierModelLike): boolean {
|
||||
const rawModelId = getRawModelId(model);
|
||||
if (!rawModelId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const baseModelId = stripVscodeServiceTierVariantModelId(rawModelId);
|
||||
const provider = getModelProvider(model, baseModelId);
|
||||
if (provider !== "codex" && provider !== "cx") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return supportsCodexServiceTierModel(baseModelId);
|
||||
}
|
||||
|
||||
function cloneModelIdentifiers<T extends VscodeServiceTierModelLike>(model: T, modelId: string): T {
|
||||
return {
|
||||
...model,
|
||||
...(model.id ? { id: modelId } : {}),
|
||||
...(model.name ? { name: modelId } : {}),
|
||||
...(model.root ? { root: modelId } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function expandVscodeServiceTierModels<T extends VscodeServiceTierModelLike>(
|
||||
models: T[]
|
||||
): T[] {
|
||||
const expanded: T[] = [];
|
||||
|
||||
for (const model of models) {
|
||||
const rawModelId = getRawModelId(model);
|
||||
if (!rawModelId) {
|
||||
expanded.push(model);
|
||||
continue;
|
||||
}
|
||||
|
||||
const baseModelId = stripVscodeServiceTierVariantModelId(rawModelId);
|
||||
const baseModel =
|
||||
rawModelId === baseModelId ? model : cloneModelIdentifiers(model, baseModelId);
|
||||
expanded.push(baseModel as T);
|
||||
|
||||
if (!supportsVscodeServiceTierVariants(model)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const serviceTier of SUPPORTED_VSCODE_SERVICE_TIERS) {
|
||||
expanded.push(
|
||||
cloneModelIdentifiers(
|
||||
baseModel as T,
|
||||
getVscodeServiceTierVariantModelId(baseModelId, serviceTier)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return expanded;
|
||||
}
|
||||
|
||||
export function getVscodeServiceTierVariantSuffix(serviceTier: ServiceTierId | undefined): string {
|
||||
if (serviceTier === "priority") {
|
||||
return "Fast";
|
||||
}
|
||||
if (serviceTier === "flex") {
|
||||
return "Flex";
|
||||
}
|
||||
return "Default";
|
||||
}
|
||||
|
||||
export function resolveVscodeServiceTierRequest(
|
||||
body: Record<string, unknown>
|
||||
): Record<string, unknown> {
|
||||
const rawModelId = typeof body.model === "string" ? body.model.trim() : "";
|
||||
if (!rawModelId) {
|
||||
return body;
|
||||
}
|
||||
|
||||
const resolvedModelId = resolveFamilyFirstPublishedModelId(rawModelId);
|
||||
|
||||
const { baseModelId, serviceTier } = parseVscodeServiceTierVariantModelId(resolvedModelId);
|
||||
if (!serviceTier) {
|
||||
if (resolvedModelId === rawModelId) {
|
||||
return body;
|
||||
}
|
||||
|
||||
return {
|
||||
...body,
|
||||
model: resolvedModelId,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...body,
|
||||
model: baseModelId,
|
||||
...(body.service_tier === undefined ? { service_tier: serviceTier } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function rewriteVscodeServiceTierRequest(request: Request): Promise<Request> {
|
||||
if (request.method !== "POST") {
|
||||
return request;
|
||||
}
|
||||
|
||||
const body = await request
|
||||
.clone()
|
||||
.json()
|
||||
.catch(() => null);
|
||||
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const rewrittenBody = resolveVscodeServiceTierRequest(body as Record<string, unknown>);
|
||||
if (rewrittenBody === body) {
|
||||
return request;
|
||||
}
|
||||
|
||||
const headers = new Headers(request.headers);
|
||||
headers.delete("content-length");
|
||||
|
||||
return new Request(request.url, {
|
||||
method: request.method,
|
||||
headers,
|
||||
body: JSON.stringify(rewrittenBody),
|
||||
});
|
||||
}
|
||||
63
tests/unit/vscode-shared-metadata.test.ts
Normal file
63
tests/unit/vscode-shared-metadata.test.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
const familyFirstModelIds =
|
||||
await import("../../src/app/api/v1/vscode/[token]/familyFirstModelIds.ts");
|
||||
const rawFamilyFirstModelIds =
|
||||
await import("../../src/app/api/v1/vscode/raw/[token]/familyFirstModelIds.ts");
|
||||
const serviceTierVariants =
|
||||
await import("../../src/app/api/v1/vscode/[token]/serviceTierVariants.ts");
|
||||
const rawServiceTierVariants =
|
||||
await import("../../src/app/api/v1/vscode/raw/[token]/serviceTierVariants.ts");
|
||||
const reasoningMetadata = await import("../../src/app/api/v1/vscode/[token]/reasoningMetadata.ts");
|
||||
const rawReasoningMetadata =
|
||||
await import("../../src/app/api/v1/vscode/raw/[token]/reasoningMetadata.ts");
|
||||
|
||||
test("vscode raw and tokenized family-first helpers share behavior", () => {
|
||||
assert.equal(
|
||||
familyFirstModelIds.resolveFamilyFirstPublishedModelId("gpt-5.4__provider_cx__tier_priority"),
|
||||
"cx/gpt-5.4__tier_priority"
|
||||
);
|
||||
assert.deepEqual(
|
||||
rawFamilyFirstModelIds.getFamilyFirstModelCandidates("cx/gpt-5.4__tier_flex", "gpt-5.4"),
|
||||
familyFirstModelIds.getFamilyFirstModelCandidates("cx/gpt-5.4__tier_flex", "gpt-5.4")
|
||||
);
|
||||
});
|
||||
|
||||
test("vscode raw and tokenized service tier helpers share behavior", () => {
|
||||
const tokenizedPayload = serviceTierVariants.resolveVscodeServiceTierRequest({
|
||||
model: "gpt-5.4__provider_cx__tier_flex",
|
||||
});
|
||||
const rawPayload = rawServiceTierVariants.resolveVscodeServiceTierRequest({
|
||||
model: "gpt-5.4__provider_cx__tier_flex",
|
||||
});
|
||||
|
||||
assert.deepEqual(rawPayload, tokenizedPayload);
|
||||
assert.deepEqual(
|
||||
serviceTierVariants.expandVscodeServiceTierModels([
|
||||
{ id: "cx/gpt-5.4", name: "cx/gpt-5.4", owned_by: "codex" },
|
||||
]),
|
||||
rawServiceTierVariants.expandVscodeServiceTierModels([
|
||||
{ id: "cx/gpt-5.4", name: "cx/gpt-5.4", owned_by: "codex" },
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
test("vscode raw and tokenized reasoning helpers share behavior", () => {
|
||||
const reasoningModel = {
|
||||
id: "openai/gpt-5-high",
|
||||
owned_by: "openai",
|
||||
capabilities: { reasoning: true },
|
||||
};
|
||||
const supportedValues = reasoningMetadata.getReasoningEffortValues(reasoningModel);
|
||||
|
||||
assert.deepEqual(supportedValues, rawReasoningMetadata.getReasoningEffortValues(reasoningModel));
|
||||
assert.equal(
|
||||
reasoningMetadata.inferSelectedReasoningEffort(reasoningModel, supportedValues),
|
||||
"high"
|
||||
);
|
||||
assert.deepEqual(
|
||||
reasoningMetadata.buildReasoningConfigSchema(["none", "high"], "high"),
|
||||
rawReasoningMetadata.buildReasoningConfigSchema(["none", "high"], "high")
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user