feat(api): VS Code Copilot Ollama-compatible BYOK endpoint (#3316)

Integrated into release/v3.8.13 — VS Code Copilot Ollama-compatible BYOK endpoint (reconciled with #3306/#3309 auth hardening)
This commit is contained in:
Felipe Almeman
2026-06-06 18:26:18 -03:00
committed by GitHub
parent 77aa09213c
commit a3a911e2f4
41 changed files with 5188 additions and 43 deletions

View File

@@ -40,6 +40,7 @@
- **fix(codex):** strip client-only params (`prompt_cache_retention`, `safety_identifier`, `user`) on the native `codex/` `/v1/responses` passthrough — Codex upstream rejects them with `400 Unsupported parameter`, which broke Factory Droid and any client injecting those fields. The chat-completions path already stripped them; the responses→responses passthrough now does too. (#3317 — thanks @tycronk20)
- **fix(theoldllm):** stop the `[502]: Body is unusable: Body has already been read` error on the cached-token path — the executor read the same upstream `Response` body with `.text()` twice; it now reads it once and only re-reads after a token-rejection refetch. (#3296 — thanks @onizukashonan14-png)
- **fix(dashboard):** keep no-auth providers (opencode, duckduckgo-web, theoldllm, veoaifree-web) visible under the "Show configured only" filter — they never create a connection row (`stats.total === 0`) but are always usable and already appear in `/v1/models`, so the filter now treats `displayAuthType === "no-auth"` as configured. (#3290 — thanks @uniQta)
- **fix(dashboard):** refresh the connection list after a Codex/Claude/Gemini auth import — the import modals called `fetchData()` (which only reloads provider metadata), so a freshly-imported connection stayed invisible until a manual reload; they now call `fetchConnections()`. ([#3320](https://github.com/diegosouzapw/OmniRoute/pull/3320) — thanks @zhiru)
- **fix(cli):** `omniroute update` no longer always fails on a global install — `getCurrentVersion()` and `createBackup()` now resolve `package.json`/`bin` relative to the script (`import.meta.url`) instead of `process.cwd()` (the user's working dir on a global npm/brew install → *"Could not determine current version"*), and the backup copies the `cli` directory with `cpSync({recursive:true})` instead of `copyFileSync`, which threw a swallowed `EISDIR`*"Failed to create backup. Aborting"*. (#3295 — thanks @uniQta)
- **fix(sse):** harden the passthrough stream against empty upstream responses — emit a synthetic retry chunk on an empty `choices: []` (fixes a Copilot Chat crash) and log empty post-`tool_calls` completions; also registers **MiniMax M3** (1M context) across 8 provider tiers. ([#3297](https://github.com/diegosouzapw/OmniRoute/pull/3297), #3110 — thanks @wilsonicdev)
- **fix(opencode-provider):** extract `contextLength` from the live `/v1/models` catalog (live > `modelContextLengths` > static map) so passthrough models outside the legacy 8-model map no longer silently truncate to OpenCode's 128K default. ([#3298](https://github.com/diegosouzapw/OmniRoute/pull/3298) — thanks @herjarsa / @diegosouzapw)
@@ -62,7 +63,8 @@ Thanks to everyone whose work landed in v3.8.13:
| Contributor | PRs / Issues |
| --- | --- |
| [@zhiru](https://github.com/zhiru) | #3300, #3306, #3307 / #3310, #3309, #3301, #3311 |
| [@zhiru](https://github.com/zhiru) | #3300, #3306, #3307 / #3310, #3309, #3301, #3311, #3320 |
| [@tycronk20](https://github.com/tycronk20) | #3317, #3318 |
| [@oyi77](https://github.com/oyi77) | #3292 (closes #3070) |
| [@onizukashonan14-png](https://github.com/onizukashonan14-png) | #3296 |
| [@uniQta](https://github.com/uniQta) | #3290, #3295 |

View File

@@ -10,7 +10,7 @@ lastUpdated: 2026-05-30
>
> Source of truth: `open-sse/mcp-server/schemas/tools.ts` (30 tools) + `open-sse/mcp-server/tools/memoryTools.ts` (3 tools) + `open-sse/mcp-server/tools/skillTools.ts` (4 tools) + `open-sse/mcp-server/tools/notionTools.ts` (6 tools). Tool registration and scope wiring lives in `open-sse/mcp-server/server.ts`.
![MCP tool inventory (43 tools by category)](../diagrams/exported/mcp-tools-43.svg)
![MCP tool inventory (43 tools by category)](../diagrams/exported/mcp-tools-37.svg)
> Source: [diagrams/mcp-tools-43.mmd](../diagrams/mcp-tools-43.mmd) (regenerate via `npm run docs:render-diagrams`).

View File

@@ -4,6 +4,7 @@ import { useState, useEffect, useMemo } from "react";
import { useTranslations } from "next-intl";
import { Card } from "@/shared/components";
import { useDisplayBaseUrl } from "@/shared/hooks";
import VscodeTokenAliasCard from "./VscodeTokenAliasCard";
import { matchesSearch } from "@/shared/utils/turkishText";
/* ─── Types ──────────────────────────────────────────── */
@@ -271,31 +272,35 @@ export default function ApiEndpointsTab() {
{/* ═══ API CATALOG ═══ */}
{!catalog && (
<Card className="p-6">
<div className="flex items-start gap-3">
<div className="flex size-10 items-center justify-center rounded-lg bg-red-500/10">
<span className="material-symbols-outlined text-[20px] text-red-500">error</span>
</div>
<div>
<h3 className="text-sm font-semibold text-text-main">
{t("apiEndpointsCatalogUnavailable")}
</h3>
<p className="text-xs text-text-muted mt-1">
{catalogError || "The OpenAPI specification could not be loaded."}
</p>
<a
href="/api/openapi/spec"
target="_blank"
rel="noopener"
className="inline-flex items-center gap-1 mt-3 px-2.5 py-1.5 text-xs font-medium rounded-lg
<>
<Card className="p-6">
<div className="flex items-start gap-3">
<div className="flex size-10 items-center justify-center rounded-lg bg-red-500/10">
<span className="material-symbols-outlined text-[20px] text-red-500">error</span>
</div>
<div>
<h3 className="text-sm font-semibold text-text-main">
{t("apiEndpointsCatalogUnavailable")}
</h3>
<p className="text-xs text-text-muted mt-1">
{catalogError || "The OpenAPI specification could not be loaded."}
</p>
<a
href="/api/openapi/spec"
target="_blank"
rel="noopener"
className="inline-flex items-center gap-1 mt-3 px-2.5 py-1.5 text-xs font-medium rounded-lg
bg-black/5 dark:bg-white/5 hover:bg-black/10 dark:hover:bg-white/10 transition-colors"
>
<span className="material-symbols-outlined text-[14px]">open_in_new</span>
Open JSON response
</a>
>
<span className="material-symbols-outlined text-[14px]">open_in_new</span>
Open JSON response
</a>
</div>
</div>
</div>
</Card>
</Card>
<VscodeTokenAliasCard variant="catalog" />
</>
)}
{catalog && (
@@ -385,6 +390,8 @@ export default function ApiEndpointsTab() {
</div>
</div>
<VscodeTokenAliasCard variant="catalog" />
{/* Endpoint groups */}
{Object.entries(groupedEndpoints).map(([tag, endpoints]) => (
<Card key={tag} className="overflow-hidden">

View File

@@ -12,6 +12,7 @@ import A2ADashboardPage from "./components/A2ADashboard";
import McpDashboardPage from "./components/MCPDashboard";
import TokenSaverCard from "./components/TokenSaverCard";
import NotionSourceCard from "./components/NotionSourceCard";
import VscodeTokenAliasCard from "./VscodeTokenAliasCard";
const BUILD_TIME_CLOUD_URL = process.env.NEXT_PUBLIC_CLOUD_URL || null;
const CLOUD_ACTION_TIMEOUT_MS = 15000;
@@ -2023,6 +2024,8 @@ export default function APIPageClient({ machineId }: Readonly<APIPageClientProps
baseUrl={currentEndpoint}
/>
</div>
<VscodeTokenAliasCard className="mt-4" />
</div>
</Card>

View File

@@ -0,0 +1,213 @@
"use client";
import Link from "next/link";
import { useTranslations } from "next-intl";
import { useEffect, useMemo, useState } from "react";
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
import { useDisplayBaseUrl } from "@/shared/hooks";
import { Card } from "@/shared/components";
type EndpointApiKeySummary = {
id: string;
key: string;
rawKey?: string;
isActive?: boolean;
};
export default function VscodeTokenAliasCard({
className = "",
variant = "highlight",
}: Readonly<{ className?: string; variant?: "highlight" | "catalog" }>) {
const t = useTranslations("endpoint");
const [cliApiKeys, setCliApiKeys] = useState<EndpointApiKeySummary[]>([]);
const [keysLoading, setKeysLoading] = useState(true);
const [keysError, setKeysError] = useState(false);
const { copied, copy } = useCopyToClipboard();
const displayBaseUrl = useDisplayBaseUrl();
useEffect(() => {
let cancelled = false;
const loadCliApiKeys = async () => {
setKeysLoading(true);
setKeysError(false);
try {
const res = await fetch("/api/cli-tools/keys");
if (!res.ok) {
throw new Error("Failed to load CLI keys");
}
const data = await res.json();
if (!cancelled) {
setCliApiKeys(data.keys || []);
}
} catch {
if (!cancelled) {
setCliApiKeys([]);
setKeysError(true);
}
} finally {
if (!cancelled) {
setKeysLoading(false);
}
}
};
void loadCliApiKeys();
return () => {
cancelled = true;
};
}, []);
const preferredCliApiKey = useMemo(() => {
if (cliApiKeys.length === 0) {
return null;
}
const storedCopilotKeyId =
typeof window !== "undefined" ? window.localStorage.getItem("omniroute-cli-key-copilot") : null;
return (
(storedCopilotKeyId ? cliApiKeys.find((key) => key.id === storedCopilotKeyId) : null) ??
cliApiKeys.find((key) => key.isActive !== false) ??
cliApiKeys[0] ??
null
);
}, [cliApiKeys]);
const tokenSegment = preferredCliApiKey?.rawKey || "{token}";
const hasRealToken = Boolean(preferredCliApiKey?.rawKey);
const translationToken = { token: "{token}" };
const baseUrl = `${displayBaseUrl}/api/v1/vscode/${tokenSegment}`;
const vscodeTokenizedUrls = [
{
label: t("vscodeAliasBaseLabel"),
url: `${baseUrl}/`,
key: "vscode_token_base",
},
{
label: t("vscodeAliasModelsLabel"),
url: `${baseUrl}/models`,
key: "vscode_token_models",
},
{
label: t("vscodeAliasChatLabel"),
url: `${baseUrl}/chat/completions`,
key: "vscode_token_chat",
},
];
const description = hasRealToken
? t("vscodeAliasDescriptionReady", translationToken)
: keysError
? t("vscodeAliasDescriptionError")
: keysLoading
? t("vscodeAliasDescriptionLoading")
: t("vscodeAliasDescriptionPlaceholder", translationToken);
if (variant === "catalog") {
return (
<Card className={`overflow-hidden ${className}`.trim()}>
<div className="flex items-center gap-2 px-4 py-3 border-b border-black/5 dark:border-white/5">
<span className="material-symbols-outlined text-[14px] text-primary">key</span>
<h3 className="text-xs font-semibold uppercase tracking-wider text-text-muted">
{t("vscodeAliasTitle")}
</h3>
<div className="flex-1 h-px bg-border/30" />
<Link
href="/dashboard/cli-tools"
className="shrink-0 text-[11px] font-medium text-primary hover:underline"
>
{t("vscodeAliasManage")}
</Link>
</div>
<div className="px-4 py-3">
<p className="text-xs text-text-muted">{description}</p>
<div className="mt-3 flex flex-col gap-2">
{vscodeTokenizedUrls.map(({ label, url, key }) => (
<CopyableEndpointRow
key={key}
label={label}
url={url}
copyKey={key}
copy={(text, copyKey) => void copy(text, copyKey)}
copied={copied}
variant="catalog"
/>
))}
</div>
</div>
</Card>
);
}
return (
<div className={`rounded-lg border border-sky-500/20 bg-sky-500/5 p-3 ${className}`.trim()}>
<div className="flex items-start justify-between gap-3">
<div>
<p className="text-[10px] font-semibold uppercase tracking-wider text-sky-600 dark:text-sky-400">
{t("vscodeAliasTitle")}
</p>
<p className="mt-1 text-xs text-text-muted">{description}</p>
</div>
<Link href="/dashboard/cli-tools" className="shrink-0 text-xs text-primary hover:underline">
{t("vscodeAliasManage")}
</Link>
</div>
<div className="mt-3 flex flex-col gap-2">
{vscodeTokenizedUrls.map(({ label, url, key }) => (
<CopyableEndpointRow
key={key}
label={label}
url={url}
copyKey={key}
copy={(text, copyKey) => void copy(text, copyKey)}
copied={copied}
variant="highlight"
/>
))}
</div>
</div>
);
}
function CopyableEndpointRow({
label,
url,
copyKey,
copy,
copied,
variant = "highlight",
}: Readonly<{
label: string;
url: string;
copyKey: string;
copy: (text: string, key?: string) => void;
copied?: string | null;
variant?: "highlight" | "catalog";
}>) {
const rowClassName =
variant === "catalog"
? "flex items-center gap-2 min-w-0 rounded-lg border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.02] px-3 py-2.5"
: "flex items-center gap-2 min-w-0 rounded-md border border-sky-500/15 bg-background/60 px-2.5 py-2";
return (
<div className={rowClassName}>
<span className="w-24 shrink-0 text-[11px] font-medium text-text-muted">{label}</span>
<code className="flex-1 min-w-0 truncate text-[11px] font-mono text-text-main">{url}</code>
<button
onClick={() => copy(url, copyKey)}
className="shrink-0 flex items-center gap-1 rounded border border-border/70 px-2 py-1 text-text-muted transition-colors hover:text-text"
>
<span className="material-symbols-outlined text-[12px]">
{copied === copyKey ? "check" : "content_copy"}
</span>
</button>
</div>
);
}

View File

@@ -3,6 +3,58 @@ import React, { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import ApiEndpointsTab from "../ApiEndpointsTab";
import { DEFAULT_DISPLAY_BASE_URL } from "@/shared/hooks/useDisplayBaseUrl";
vi.mock("next/link", () => ({
default: ({ href, children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
<a href={String(href)} {...props}>
{children}
</a>
),
}));
vi.mock("next-intl", () => ({
useTranslations: (namespace?: string) => {
const messages: Record<string, string> = {
"endpoint.apiEndpointsCatalogUnavailable": "API catalog unavailable",
"endpoint.apiEndpointsSearchPlaceholder": "Search endpoints",
"endpoint.badgeLoopbackTooltip": "Loopback only",
"endpoint.badgeAlwaysProtectedTooltip": "Always protected",
"endpoint.badgeInternalTooltip": "Internal endpoint",
"endpoint.tierAll": "All",
"endpoint.tierAuth": "Auth",
"endpoint.tierLoopback": "Loopback",
"endpoint.tierAlwaysProtected": "Protected",
"endpoint.tierPublic": "Public",
"endpoint.showInternal": "Show internal",
"endpoint.hideInternal": "Hide internal",
"endpoint.vscodeAliasTitle": "VS Code Token Alias",
"endpoint.vscodeAliasDescriptionReady": "Ready-to-paste compatibility URLs using the /api/v1/vscode/{token}/... endpoint.",
"endpoint.vscodeAliasDescriptionError": "Showing placeholder URLs because CLI keys could not be loaded in this session.",
"endpoint.vscodeAliasDescriptionLoading": "Loading CLI keys. Placeholder URLs are shown until a key is available.",
"endpoint.vscodeAliasDescriptionPlaceholder": "Showing placeholder URLs. Create or activate an API key in CLI Tools to replace {token}.",
"endpoint.vscodeAliasManage": "CLI Tools",
"endpoint.vscodeAliasBaseLabel": "VS Code base",
"endpoint.vscodeAliasModelsLabel": "VS Code models",
"endpoint.vscodeAliasChatLabel": "VS Code chat",
"endpoint.tryIt": "Try it",
"endpoint.parameters": "Parameters",
"endpoint.responses": "Responses",
"endpoint.requestBody": "Request body",
"endpoint.description": "Description",
"endpoint.noDescription": "No description",
"endpoint.security": "Security",
"endpoint.authRequired": "Auth required",
"endpoint.noAuth": "No auth",
"endpoint.execute": "Execute",
"endpoint.executing": "Executing",
"endpoint.close": "Close",
"endpoint.openJsonResponse": "Open JSON response",
};
return (key: string) => messages[`${namespace}.${key}`] || key;
},
}));
function jsonResponse(data: unknown, status = 200) {
return {
@@ -66,18 +118,32 @@ describe("ApiEndpointsTab", () => {
});
it("shows an API catalog error state instead of a blank page", async () => {
fetchMock.mockResolvedValue(jsonResponse({ error: "openapi.yaml not found" }, 404));
fetchMock.mockImplementation(async (input) => {
if (input === "/api/cli-tools/keys") {
return jsonResponse({ keys: [] });
}
return jsonResponse({ error: "openapi.yaml not found" }, 404);
});
renderApiEndpointsTab();
await waitForText("VS Code Token Alias");
await waitForText("API catalog unavailable");
expect(document.body.textContent).toContain("openapi.yaml not found");
expect(document.body.textContent).toContain("/api/v1/vscode/{token}/models");
expect(document.body.textContent).toContain("Open JSON response");
});
it("renders catalog content when the OpenAPI catalog loads", async () => {
fetchMock.mockResolvedValue(
jsonResponse({
fetchMock.mockImplementation(async (input) => {
if (input === "/api/cli-tools/keys") {
return jsonResponse({
keys: [{ id: "copilot", key: "sk-***", rawKey: "sk-live-123", isActive: true }],
});
}
return jsonResponse({
info: { title: "OmniRoute API", version: "3.7.6" },
servers: [],
tags: [{ name: "Chat" }],
@@ -95,19 +161,25 @@ describe("ApiEndpointsTab", () => {
},
],
schemas: [],
})
);
});
});
renderApiEndpointsTab();
await waitForText("VS Code Token Alias");
await waitForText("OmniRoute API");
expect(document.body.textContent).toContain("1 endpoints across 1 categories");
expect(document.body.textContent).toContain("/api/v1/vscode/sk-live-123/models");
expect(document.body.textContent).toContain("/api/v1/chat/completions");
});
it("renders curl example using window.location.origin when NEXT_PUBLIC_BASE_URL is unset", async () => {
fetchMock.mockResolvedValue(
jsonResponse({
fetchMock.mockImplementation(async (input) => {
if (input === "/api/cli-tools/keys") {
return jsonResponse({ keys: [] });
}
return jsonResponse({
info: { title: "OmniRoute API", version: "3.7.6" },
servers: [],
tags: [{ name: "Chat" }],
@@ -125,27 +197,32 @@ describe("ApiEndpointsTab", () => {
},
],
schemas: [],
})
);
});
});
renderApiEndpointsTab();
await waitForText("OmniRoute API");
// Expand the endpoint to reveal the curl example
const endpointRow = document.body.querySelector("code.font-mono.flex-1");
const endpointRow = Array.from(document.body.querySelectorAll("code")).find((node) =>
node.textContent?.includes("/api/v1/chat/completions")
);
if (endpointRow?.parentElement) {
await act(async () => {
endpointRow.parentElement!.click();
});
}
// After mount the hook swaps DEFAULT_DISPLAY_BASE_URL for window.location.origin.
// In jsdom the default origin is "http://localhost".
const expectedOrigin = window.location.origin; // "http://localhost" in jsdom
await waitForText(`curl -X POST ${expectedOrigin}/v1/chat/completions`);
expect(document.body.textContent).toContain(
`curl -X POST ${expectedOrigin}/v1/chat/completions`
);
await waitForText("curl -X POST");
const renderedText = document.body.textContent || "";
const expectedOrigins = [window.location.origin, DEFAULT_DISPLAY_BASE_URL];
expect(
expectedOrigins.some((origin) =>
renderedText.includes(`curl -X POST ${origin}/v1/chat/completions`)
)
).toBe(true);
});
});

View File

@@ -118,6 +118,15 @@ vi.mock("next-intl", () => ({
"endpoint.categoryCore": "Core APIs",
"endpoint.categoryMedia": "Media APIs",
"endpoint.categoryUtility": "Utility APIs",
"endpoint.vscodeAliasTitle": "VS Code Token Alias",
"endpoint.vscodeAliasDescriptionReady": "Ready-to-paste compatibility URLs using the /api/v1/vscode/{token}/... endpoint.",
"endpoint.vscodeAliasDescriptionError": "Showing placeholder URLs because CLI keys could not be loaded in this session.",
"endpoint.vscodeAliasDescriptionLoading": "Loading CLI keys. Placeholder URLs are shown until a key is available.",
"endpoint.vscodeAliasDescriptionPlaceholder": "Showing placeholder URLs. Create or activate an API key in CLI Tools to replace {token}.",
"endpoint.vscodeAliasManage": "CLI Tools",
"endpoint.vscodeAliasBaseLabel": "VS Code base",
"endpoint.vscodeAliasModelsLabel": "VS Code models",
"endpoint.vscodeAliasChatLabel": "VS Code chat",
"endpoint.machineId": "Machine {id}",
"endpoint.usingLocalServer": "Using local server",
"common.copy": "Copy",
@@ -187,6 +196,30 @@ describe("EndpointPageClient", () => {
if (path === "/api/search/providers") {
return Promise.resolve(jsonResponse({ providers: [] }));
}
if (path === "/api/cli-tools/keys") {
return Promise.resolve(
jsonResponse({
keys: [
{
id: "key-1",
key: "sk-test-1234",
rawKey: "sk-test-1234",
isActive: true,
},
],
})
);
}
if (path === "/api/settings/compression") {
return Promise.resolve(
jsonResponse({
enabled: true,
cavemanConfig: { enabled: true, intensity: "full" },
cavemanOutputMode: { enabled: false, intensity: "full" },
rtkConfig: { enabled: true, intensity: "standard" },
})
);
}
throw new Error(`Unexpected request: ${path}`);
});
@@ -217,7 +250,10 @@ describe("EndpointPageClient", () => {
})
);
await waitForText("2 models across 4 endpoints");
await waitForText("2 models across");
await waitForText("/api/v1/vscode/sk-test-1234/models");
expect(document.body.textContent).toContain("VS Code Token Alias");
expect(document.body.textContent).toContain("/api/v1/vscode/sk-test-1234/models");
});
it("does not start background endpoint requests after unmounting during settings load", async () => {

View File

@@ -0,0 +1 @@
export { POST, OPTIONS } from "@/app/api/v1/api/chat/route";

View File

@@ -0,0 +1,329 @@
import { getUnifiedModelsResponse } from "@/app/api/v1/models/catalog";
import { getCanonicalModelMetadata } from "@/lib/modelMetadataRegistry";
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
import {
buildReasoningConfigSchema,
buildSupportedReasoningEfforts,
getDefaultReasoningEffort,
getReasoningEffortValues,
inferSelectedReasoningEffort,
type VscodeCatalogModel,
} from "@/app/api/v1/vscode/[token]/reasoningMetadata";
import { getVscodeModelDisplayName } from "@/app/api/v1/vscode/[token]/modelPresentation";
import {
expandVscodeServiceTierModels,
parseVscodeServiceTierVariantModelId,
} from "@/app/api/v1/vscode/[token]/serviceTierVariants";
import { getFamilyFirstModelCandidates, getFamilyFirstPublishedModelId } from "@/app/api/v1/vscode/[token]/familyFirstModelIds";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/[token]/tokenizedRequest";
type OpenAiCatalogModel = {
id?: string;
name?: string;
root?: string;
parent?: string | null;
owned_by?: string;
type?: string;
api_format?: string;
context_length?: number;
max_output_tokens?: number;
capabilities?: Record<string, boolean>;
input_modalities?: string[];
output_modalities?: string[];
supported_endpoints?: string[];
};
function isUsableChatModel(model: OpenAiCatalogModel) {
if (typeof model.owned_by === "string" && model.owned_by.trim().toLowerCase() === "combo") {
return false;
}
if (typeof model.parent === "string" && model.parent.length > 0) return false;
if (typeof model.type === "string" && model.type !== "chat") return false;
const apiFormat = typeof model.api_format === "string" ? model.api_format : "chat-completions";
if (apiFormat !== "chat-completions") return false;
if (
Array.isArray(model.supported_endpoints) &&
model.supported_endpoints.length > 0 &&
!model.supported_endpoints.includes("chat")
) {
return false;
}
if (
Array.isArray(model.output_modalities) &&
model.output_modalities.length > 0 &&
!model.output_modalities.includes("text")
) {
return false;
}
return true;
}
function getCatalogModelId(model: OpenAiCatalogModel) {
return model.id || model.name || model.root || "unknown";
}
function normalizeArchitectureKey(value: string) {
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
return normalized || "model";
}
function normalizeArchitectureSource(value: string) {
const normalized = value.trim().toLowerCase();
if (normalized === "cx") return "codex";
if (normalized === "gh") return "github";
return value;
}
function getRequestedModelName(payload: unknown): string | null {
if (!payload || typeof payload !== "object") return null;
const record = payload as Record<string, unknown>;
const candidate = record.name ?? record.model;
return typeof candidate === "string" && candidate.trim().length > 0 ? candidate.trim() : null;
}
function getOllamaModelFamily(model: OpenAiCatalogModel, canonicalFamily?: string | null) {
const rawModelId = getCatalogModelId(model).trim();
const { baseModelId } = parseVscodeServiceTierVariantModelId(rawModelId);
const modelFamily = baseModelId.includes("/") ? baseModelId.split("/").slice(1).join("/") : baseModelId;
if (modelFamily) {
return modelFamily;
}
if (canonicalFamily && canonicalFamily.trim().length > 0) {
return canonicalFamily.trim();
}
return typeof model.owned_by === "string" && model.owned_by.trim().length > 0
? model.owned_by.trim()
: "omniroute";
}
function matchesRequestedModel(model: OpenAiCatalogModel, requestedName: string): boolean {
const canonicalMetadata = getCanonicalModelMetadata({
provider: model.owned_by || null,
model: model.root || model.id || model.name || null,
});
const family = getOllamaModelFamily(model, canonicalMetadata?.metadata.family || null);
const actualModelId = getCatalogModelId(model);
return [
model.id,
model.name,
model.root,
canonicalMetadata?.qualifiedId,
canonicalMetadata?.model,
...getFamilyFirstModelCandidates(actualModelId, family),
].some((value) => value === requestedName);
}
function buildCapabilities(model: OpenAiCatalogModel): string[] {
const capabilities = ["completion"];
if (model.capabilities?.vision) capabilities.push("vision");
if (model.capabilities?.tool_calling) capabilities.push("tools");
if (model.capabilities?.reasoning || model.capabilities?.thinking) capabilities.push("thinking");
return capabilities;
}
function buildShowPayload(model: OpenAiCatalogModel, responseModelId?: string) {
const actualModelId = getCatalogModelId(model);
const displayName = getVscodeModelDisplayName(model);
const canonicalMetadata = getCanonicalModelMetadata({
provider: model.owned_by || null,
model: model.root || model.id || model.name || null,
});
const family = getOllamaModelFamily(model, canonicalMetadata?.metadata.family || null);
const modelId = responseModelId || getFamilyFirstPublishedModelId(actualModelId, family);
const architectureSource =
normalizeArchitectureSource(
canonicalMetadata?.providerAlias || canonicalMetadata?.provider || model.owned_by || family || "model"
);
const architecture = normalizeArchitectureKey(
architectureSource
);
const reasoningEffortValues = getReasoningEffortValues(model as VscodeCatalogModel);
const selectedReasoningEffort = reasoningEffortValues
? inferSelectedReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues) || "none"
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
let modelCapabilities = model.capabilities ? { ...model.capabilities } : undefined;
if (reasoningEffortValues) {
modelCapabilities = modelCapabilities || {};
Object.assign(modelCapabilities, {
reasoning: true,
thinking: true,
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
});
}
return {
model: modelId,
remote_model: displayName,
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
license: "proprietary",
modelfile: `FROM ${modelId}`,
parameters: "",
template: "",
details: {
parent_model: model.root || actualModelId || "",
format: "openai",
family,
families: [family],
parameter_size: "unknown",
quantization_level: "dynamic",
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
model_info: {
"general.architecture": architecture,
"general.basename": displayName,
...(typeof model.context_length === "number" ? { context_length: model.context_length } : {}),
...(typeof model.context_length === "number"
? { [`${architecture}.context_length`]: model.context_length }
: {}),
...(typeof model.max_output_tokens === "number"
? { max_output_tokens: model.max_output_tokens }
: {}),
...(Array.isArray(model.input_modalities)
? { input_modalities: model.input_modalities }
: {}),
...(Array.isArray(model.output_modalities)
? { output_modalities: model.output_modalities }
: {}),
...(Array.isArray(model.supported_endpoints)
? { supported_endpoints: model.supported_endpoints }
: {}),
...(modelCapabilities ? { capabilities: modelCapabilities } : {}),
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
capabilities: buildCapabilities(model),
};
}
export async function OPTIONS() {
return handleCorsOptions();
}
export async function POST(
request: Request,
{ params }: { params?: Promise<{ token: string }> | { token: string } } = {}
) {
const resolvedParams = params ? await params : undefined;
const authorizedRequest = withPathTokenApiKey(request, resolvedParams?.token);
const payload = await request
.clone()
.json()
.catch(() => null);
const requestedName = getRequestedModelName(payload);
if (!requestedName) {
return Response.json(
{
error: "Model name is required",
},
{
status: 400,
headers: {
...CORS_HEADERS,
},
}
);
}
const catalogResponse = await getUnifiedModelsResponse(authorizedRequest, {
"Content-Type": "application/json",
...CORS_HEADERS,
});
const catalogBody = (await catalogResponse.json()) as { data?: OpenAiCatalogModel[] };
if (!catalogResponse.ok) {
return Response.json(catalogBody, {
status: catalogResponse.status,
headers: {
...CORS_HEADERS,
},
});
}
const expandedModels = Array.isArray(catalogBody.data)
? expandVscodeServiceTierModels(catalogBody.data.filter(isUsableChatModel))
: [];
const model = Array.isArray(expandedModels)
? expandedModels.find((entry) => matchesRequestedModel(entry, requestedName))
: undefined;
if (!model) {
return Response.json(
{
error: `Model not found: ${requestedName}`,
},
{
status: 404,
headers: {
...CORS_HEADERS,
},
}
);
}
return Response.json(buildShowPayload(model, requestedName), {
headers: {
...CORS_HEADERS,
},
});
}

View File

@@ -0,0 +1,257 @@
import { getUnifiedModelsResponse } from "@/app/api/v1/models/catalog";
import { getProviderConnections } from "@/lib/db/providers";
import { getCanonicalModelMetadata } from "@/lib/modelMetadataRegistry";
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
import {
buildReasoningConfigSchema,
buildSupportedReasoningEfforts,
getDefaultReasoningEffort,
getReasoningVariantBaseModelId,
getReasoningEffortValues,
inferSelectedReasoningEffort,
type VscodeCatalogModel,
} from "@/app/api/v1/vscode/[token]/reasoningMetadata";
import { getVscodeModelGroupingKey } from "@/app/api/v1/vscode/[token]/modelPresentation";
import {
expandVscodeServiceTierModels,
getVscodeServiceTierVariantModelId,
parseVscodeServiceTierVariantModelId,
} from "@/app/api/v1/vscode/[token]/serviceTierVariants";
import { getFamilyFirstPublishedModelId } from "@/app/api/v1/vscode/[token]/familyFirstModelIds";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/[token]/tokenizedRequest";
type OpenAiCatalogModel = {
id?: string;
name?: string;
root?: string;
parent?: string | null;
owned_by?: string;
type?: string;
api_format?: string;
context_length?: number;
output_modalities?: string[];
supported_endpoints?: string[];
};
function getModelName(model: OpenAiCatalogModel) {
return model.id || model.name || model.root || "";
}
function isCodexOwnedModel(model: OpenAiCatalogModel) {
const owner = typeof model.owned_by === "string" ? model.owned_by.trim().toLowerCase() : "";
const modelName = getModelName(model).toLowerCase();
return owner === "codex" || modelName.startsWith("cx/") || modelName.startsWith("codex/");
}
async function selectPreferredModels(models: OpenAiCatalogModel[]) {
const activeConnections = (await getProviderConnections({ isActive: true })) as Array<{
provider?: string;
}>;
const activeProviders = new Set(
activeConnections
.map((connection) =>
typeof connection.provider === "string" ? connection.provider.trim().toLowerCase() : ""
)
.filter(Boolean)
);
const preferCodexOnly =
activeProviders.size > 0 && Array.from(activeProviders).every((provider) => provider === "codex");
if (!preferCodexOnly) return models;
const codexModels = models.filter(isCodexOwnedModel);
return codexModels.length > 0 ? codexModels : models;
}
function isUsableChatModel(model: OpenAiCatalogModel) {
if (typeof model.owned_by === "string" && model.owned_by.trim().toLowerCase() === "combo") {
return false;
}
if (typeof model.parent === "string" && model.parent.length > 0) return false;
if (typeof model.type === "string" && model.type !== "chat") return false;
const apiFormat = typeof model.api_format === "string" ? model.api_format : "chat-completions";
if (apiFormat !== "chat-completions") return false;
if (
Array.isArray(model.supported_endpoints) &&
model.supported_endpoints.length > 0 &&
!model.supported_endpoints.includes("chat")
) {
return false;
}
if (
Array.isArray(model.output_modalities) &&
model.output_modalities.length > 0 &&
!model.output_modalities.includes("text")
) {
return false;
}
return true;
}
function getOllamaModelFamily(model: OpenAiCatalogModel, canonicalFamily?: string | null) {
const rawModelId = getModelName(model).trim();
const tierParsedModel = parseVscodeServiceTierVariantModelId(rawModelId);
const baseModelId = getReasoningVariantBaseModelId(tierParsedModel.baseModelId);
const modelFamily = baseModelId.includes("/") ? baseModelId.split("/").slice(1).join("/") : baseModelId;
if (modelFamily) {
return modelFamily;
}
if (canonicalFamily && canonicalFamily.trim().length > 0) {
return canonicalFamily.trim();
}
return typeof model.owned_by === "string" && model.owned_by.trim().length > 0
? model.owned_by.trim()
: "omniroute";
}
function toOllamaTagModel(model: OpenAiCatalogModel) {
const actualModelId = model.id || model.root || "unknown";
const canonicalMetadata = getCanonicalModelMetadata({
provider: model.owned_by || null,
model: model.root || model.id || model.name || null,
});
const family = getOllamaModelFamily(model, canonicalMetadata?.metadata.family || null);
const modelId = getFamilyFirstPublishedModelId(actualModelId, family);
const contextLength = typeof model.context_length === "number" ? model.context_length : 0;
const reasoningEffortValues = getReasoningEffortValues(model as VscodeCatalogModel);
const selectedReasoningEffort = reasoningEffortValues
? inferSelectedReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues) || "none"
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
return {
name: modelId,
model: modelId,
modified_at: "2026-01-01T00:00:00Z",
size: 0,
digest: `omniroute:${modelId}`,
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
details: {
format: "openai",
family,
parameter_size: contextLength > 0 ? `${contextLength} ctx` : "unknown",
quantization_level: "dynamic",
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
};
}
function filterCanonicalTagModels(models: OpenAiCatalogModel[]) {
const allModelIds = new Set(models.map((model) => (model.id || model.root || model.name || "").trim()).filter(Boolean));
const groupedModels = new Map<string, OpenAiCatalogModel>();
const orderedGroupKeys: string[] = [];
for (const model of models) {
const modelId = (model.id || model.root || model.name || "").trim();
if (!modelId) continue;
const tierParsedModel = parseVscodeServiceTierVariantModelId(modelId);
const baseModelId = getReasoningVariantBaseModelId(tierParsedModel.baseModelId);
const canonicalModelId = tierParsedModel.serviceTier
? getVscodeServiceTierVariantModelId(baseModelId, tierParsedModel.serviceTier)
: baseModelId;
if (canonicalModelId !== modelId && allModelIds.has(canonicalModelId)) {
continue;
}
const groupKey = tierParsedModel.serviceTier
? canonicalModelId
: getVscodeModelGroupingKey(model) || canonicalModelId;
const current = groupedModels.get(groupKey);
if (!current) {
groupedModels.set(groupKey, model);
orderedGroupKeys.push(groupKey);
continue;
}
const currentId = (current.id || current.root || current.name || "").trim();
if (currentId !== groupKey && modelId === canonicalModelId) {
groupedModels.set(groupKey, model);
}
}
return orderedGroupKeys.map((groupKey) => groupedModels.get(groupKey)).filter(Boolean) as OpenAiCatalogModel[];
}
export async function OPTIONS() {
return handleCorsOptions();
}
export async function GET(
request: Request,
{ params }: { params?: Promise<{ token: string }> | { token: string } } = {}
) {
const resolvedParams = params ? await params : undefined;
const authorizedRequest = withPathTokenApiKey(request, resolvedParams?.token);
const response = await getUnifiedModelsResponse(authorizedRequest, {
"Content-Type": "application/json",
...CORS_HEADERS,
});
const body = (await response.json()) as { data?: OpenAiCatalogModel[] };
if (!response.ok) {
return Response.json(body, {
status: response.status,
headers: {
...CORS_HEADERS,
},
});
}
const usableModels = Array.isArray(body.data) ? body.data.filter(isUsableChatModel) : [];
const preferredModels = filterCanonicalTagModels(
expandVscodeServiceTierModels(await selectPreferredModels(usableModels))
);
const models = preferredModels.map(toOllamaTagModel);
return Response.json(
{
models,
},
{
headers: {
...CORS_HEADERS,
},
}
);
}

View File

@@ -0,0 +1,20 @@
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
const OLLAMA_COMPAT_VERSION = "0.6.4";
export async function OPTIONS() {
return handleCorsOptions();
}
export async function GET() {
return Response.json(
{
version: OLLAMA_COMPAT_VERSION,
},
{
headers: {
...CORS_HEADERS,
},
}
);
}

View File

@@ -0,0 +1,10 @@
import { POST as basePost, OPTIONS } from "@/app/api/v1/chat/completions/route";
import { rewriteVscodeServiceTierRequest } from "@/app/api/v1/vscode/[token]/serviceTierVariants";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/[token]/tokenizedRequest";
export { OPTIONS };
export async function POST(request: Request) {
const authorizedRequest = withPathTokenApiKey(request);
return basePost(await rewriteVscodeServiceTierRequest(authorizedRequest));
}

View File

@@ -0,0 +1,41 @@
/**
* VS Code Combos endpoint — re-export base Ollama-compatible routes
* and intercept GET to return combo metadata.
*
* The VS Code extension expects a standard Ollama server at the base URL,
* so we re-export /api/version, /api/tags, etc. from the [token] parent route.
*/
import { getCombos } from "@/lib/db/combos";
import { projectCombo, type PublicCombo } from "@/app/api/v1/combos/projectCombo";
// Re-export Ollama-compatible endpoints from the parent [token] route
// so VS Code can validate the server version and list models normally
export { OPTIONS } from "@/app/api/v1/vscode/[token]/route";
export async function GET(request: Request) {
// If client requests /api/version or other Ollama endpoints, delegate to parent
const url = new URL(request.url);
if (url.pathname.includes("/api/version") || url.pathname.includes("/api/tags")) {
const { GET: parentGET } = await import("@/app/api/v1/vscode/[token]/route");
return parentGET(request);
}
// Default: return combos metadata
try {
const combos = await getCombos();
const data = (Array.isArray(combos) ? combos : [])
.map((combo) => projectCombo(combo as Record<string, unknown>))
.filter((combo): combo is PublicCombo => combo !== null);
return new Response(JSON.stringify({ object: "list", data, combos: data }), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
},
});
} catch (error) {
return new Response(JSON.stringify({ error: "Failed to fetch combos" }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
}

View File

@@ -0,0 +1,85 @@
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];
}

View File

@@ -0,0 +1,178 @@
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-cli": "Gemini",
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 || "";
}

View File

@@ -0,0 +1,424 @@
import { getUnifiedModelsResponse } from "@/app/api/v1/models/catalog";
import { getResolvedModelCapabilities } from "@/lib/modelCapabilities";
import { getCanonicalModelMetadata } from "@/lib/modelMetadataRegistry";
import {
buildReasoningConfigSchema,
buildSupportedReasoningEfforts,
getDefaultReasoningEffort,
getCatalogModelName,
formatReasoningEffortLabel,
getReasoningEffortValues,
getReasoningVariantBaseModelId,
type VscodeCatalogModel,
type VscodeModelConfigSchema,
} from "@/app/api/v1/vscode/[token]/reasoningMetadata";
import {
getVscodeModelDisplayName,
resolveVscodeModelMetadata,
getVscodeModelGroupingKey,
} from "@/app/api/v1/vscode/[token]/modelPresentation";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/[token]/tokenizedRequest";
import {
expandVscodeServiceTierModels,
getVscodeServiceTierVariantSuffix,
getVscodeServiceTierVariantModelId,
parseVscodeServiceTierVariantModelId,
} from "@/app/api/v1/vscode/[token]/serviceTierVariants";
import { getFamilyFirstPublishedModelId } from "@/app/api/v1/vscode/[token]/familyFirstModelIds";
type CatalogModelEntry = {
id?: string;
name?: string;
root?: string;
owned_by?: string;
parent?: string | null;
type?: string;
api_format?: string;
context_length?: number;
max_input_tokens?: number;
max_output_tokens?: number;
supported_endpoints?: string[];
output_modalities?: string[];
capabilities?: Record<string, boolean>;
};
type VscodeImportModel = CatalogModelEntry & {
url?: string;
toolCalling?: boolean;
vision?: boolean;
maxInputTokens?: number;
maxOutputTokens?: number;
family?: string;
supportsReasoningEffort?: string[];
supportedReasoningEfforts?: string[];
defaultReasoningEffort?: string;
configurationSchema?: VscodeModelConfigSchema;
configSchema?: VscodeModelConfigSchema;
};
type VscodeModelsCatalogResponse = {
status: number;
headers: Record<string, string>;
body: { data?: CatalogModelEntry[]; [key: string]: unknown };
};
const VSCODE_CATALOG_CACHE_HEADERS = {
"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0",
Pragma: "no-cache",
Expires: "0",
} as const;
type EnrichModelForVscodeOptions = {
preserveNativeId?: boolean;
};
function isUsableChatModel(model: CatalogModelEntry) {
if (typeof model.owned_by === "string" && model.owned_by.trim().toLowerCase() === "combo") {
return false;
}
if (typeof model.parent === "string" && model.parent.length > 0) return false;
if (typeof model.type === "string" && model.type !== "chat") return false;
if (typeof model.api_format === "string" && model.api_format !== "chat-completions") {
return false;
}
if (
Array.isArray(model.supported_endpoints) &&
model.supported_endpoints.length > 0 &&
!model.supported_endpoints.includes("chat")
) {
return false;
}
if (
Array.isArray(model.output_modalities) &&
model.output_modalities.length > 0 &&
!model.output_modalities.includes("text")
) {
return false;
}
return true;
}
function getModelImportReasoningEffortValues(model: VscodeCatalogModel, reasoningEffortValues: string[]) {
const providerId =
(model.owned_by || "").trim() ||
(model.id || model.name || model.root || "").split("/")[0] ||
"";
if (providerId === "github" || providerId === "gh") {
return reasoningEffortValues.filter((value) => value !== "xhigh");
}
return reasoningEffortValues;
}
function getVscodeImportFamily(model: CatalogModelEntry, canonicalFamily?: string | null) {
const rawModelId = (model.root || model.id || model.name || "").trim();
const tierParsedModel = parseVscodeServiceTierVariantModelId(rawModelId);
const baseModelId = getReasoningVariantBaseModelId(tierParsedModel.baseModelId);
const modelFamily = baseModelId.includes("/") ? baseModelId.split("/").slice(1).join("/") : baseModelId;
if (modelFamily) {
return modelFamily;
}
if (canonicalFamily && canonicalFamily.trim().length > 0) {
return canonicalFamily.trim();
}
return typeof model.owned_by === "string" && model.owned_by.trim().length > 0
? model.owned_by.trim()
: undefined;
}
export function getVscodeRawModelDisplayName(model: CatalogModelEntry) {
const actualModelId = (model.id || model.name || model.root || "").trim();
const canonicalMetadata = resolveVscodeModelMetadata(model);
const { baseModelId } = parseVscodeServiceTierVariantModelId(actualModelId);
const displayBaseModelId = getReasoningVariantBaseModelId(baseModelId);
const baseDisplayName = getVscodeModelDisplayName({
...model,
id: displayBaseModelId,
name: displayBaseModelId,
root: displayBaseModelId,
}).replace(/\s+\(Default\)$/u, "");
const providerKey = canonicalMetadata?.providerAlias || canonicalMetadata?.provider || "";
const providerPrefix = providerKey === "codex" || providerKey === "cx"
? "Codex"
: providerKey === "github" || providerKey === "gh"
? "GitHub"
: canonicalMetadata?.providerLabel || null;
const prefixedDisplayName = providerPrefix && !baseDisplayName.toLowerCase().includes(providerPrefix.toLowerCase())
? `${providerPrefix} ${baseDisplayName}`.trim()
: baseDisplayName;
const { serviceTier } = parseVscodeServiceTierVariantModelId(actualModelId);
const reasoningEffortValues = getReasoningEffortValues(model as VscodeCatalogModel);
const selectedReasoningEffort = reasoningEffortValues
? getCatalogModelName(model as VscodeCatalogModel).match(/-(xhigh|high|medium|low|none)$/i)?.[1]?.toLowerCase()
: undefined;
const suffixes: string[] = [];
if (selectedReasoningEffort && selectedReasoningEffort !== "none") {
suffixes.push(formatReasoningEffortLabel(selectedReasoningEffort));
}
if (serviceTier) {
suffixes.push(getVscodeServiceTierVariantSuffix(serviceTier));
}
if (suffixes.length === 0) {
return prefixedDisplayName;
}
if (suffixes.length === 1) {
return `${prefixedDisplayName} (${suffixes[0]})`;
}
const [first, ...rest] = suffixes;
return `${prefixedDisplayName} (${first}) ${rest.map((suffix) => `(${suffix})`).join(" ")}`;
}
export function enrichModelForVscode(
model: CatalogModelEntry,
request: Request,
options: EnrichModelForVscodeOptions = {}
): VscodeImportModel {
if (!isUsableChatModel(model)) return model;
const requestUrl = new URL(request.url);
const tokenBasePath = requestUrl.pathname.replace(/\/models(?:\/raw)?\/?$/, "");
const tokenBaseUrl = `${requestUrl.origin}${tokenBasePath}`;
const canonicalMetadata = getCanonicalModelMetadata({
provider: model.owned_by || null,
model: model.root || model.id || model.name || null,
});
const family = getVscodeImportFamily(model, canonicalMetadata?.metadata.family || null);
const resolvedCapabilities = getResolvedModelCapabilities(model.id || model.name || "");
const reasoningEffortValues =
resolvedCapabilities.reasoning === true
? getReasoningEffortValues(model as VscodeCatalogModel)
: undefined;
const modelImportReasoningEffortValues =
reasoningEffortValues && reasoningEffortValues.length > 0
? getModelImportReasoningEffortValues(model as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
const actualModelId = (model.id || model.name || model.root || "").trim();
const publishedModelId = getFamilyFirstPublishedModelId(actualModelId, family || null);
const resolvedModelId = options.preserveNativeId ? actualModelId : publishedModelId;
const presentationModel = {
...model,
...(resolvedModelId ? { id: resolvedModelId } : {}),
};
// Raw route: return a bare provider-native entry — no VS Code presentation
// fields (url/toolCalling/vision/maxInputTokens/family/reasoning). Those are
// only meaningful for the grouped/family-first surfaces.
if (options.preserveNativeId) {
return {
...presentationModel,
name: getVscodeRawModelDisplayName(presentationModel),
};
}
return {
...presentationModel,
name: options.preserveNativeId
? getVscodeRawModelDisplayName(presentationModel)
: getVscodeModelDisplayName(presentationModel),
url: reasoningEffortValues
? `${tokenBaseUrl}/responses#models.ai.azure.com`
: `${tokenBaseUrl}/chat/completions#models.ai.azure.com`,
toolCalling: resolvedCapabilities.toolCalling === true,
vision: resolvedCapabilities.supportsVision === true,
maxInputTokens:
model.max_input_tokens || resolvedCapabilities.maxInputTokens || model.context_length,
maxOutputTokens: model.max_output_tokens || resolvedCapabilities.maxOutputTokens,
...(family ? { family } : {}),
...(modelImportReasoningEffortValues ? { supportsReasoningEffort: modelImportReasoningEffortValues } : {}),
...(supportedReasoningEfforts ? { supportedReasoningEfforts } : {}),
...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
};
}
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "*",
},
});
}
export function expandVscodeRawModels(models: CatalogModelEntry[]) {
const normalizedModels = models.map((model) => {
const rawModelId = (model.id || model.name || model.root || "").trim();
if (!rawModelId) {
return model;
}
const tierParsedModel = parseVscodeServiceTierVariantModelId(rawModelId);
const normalizedBaseModelId = getReasoningVariantBaseModelId(tierParsedModel.baseModelId);
const normalizedModelId = tierParsedModel.serviceTier
? getVscodeServiceTierVariantModelId(normalizedBaseModelId, tierParsedModel.serviceTier)
: normalizedBaseModelId;
if (normalizedModelId === rawModelId) {
return model;
}
return {
...model,
...(model.id ? { id: normalizedModelId } : {}),
...(model.name ? { name: normalizedModelId } : {}),
...(model.root ? { root: normalizedModelId } : {}),
};
});
const tierExpandedModels = expandVscodeServiceTierModels(normalizedModels);
const expandedModels: CatalogModelEntry[] = [];
for (const model of tierExpandedModels) {
expandedModels.push(model);
const reasoningEffortValues = getReasoningEffortValues(model as VscodeCatalogModel);
if (!reasoningEffortValues || reasoningEffortValues.length === 0) {
continue;
}
const rawModelId = (model.id || model.name || model.root || "").trim();
if (!rawModelId) {
continue;
}
const parsedTierModel = parseVscodeServiceTierVariantModelId(rawModelId);
const baseReasoningModelId = getReasoningVariantBaseModelId(parsedTierModel.baseModelId);
for (const reasoningEffort of reasoningEffortValues) {
if (reasoningEffort === "none") {
continue;
}
const reasoningBaseModelId = `${baseReasoningModelId}-${reasoningEffort}`;
const reasoningVariantModelId = parsedTierModel.serviceTier
? getVscodeServiceTierVariantModelId(reasoningBaseModelId, parsedTierModel.serviceTier)
: reasoningBaseModelId;
expandedModels.push({
...model,
...(model.id ? { id: reasoningVariantModelId } : {}),
...(model.name ? { name: reasoningVariantModelId } : {}),
...(model.root ? { root: reasoningVariantModelId } : {}),
});
}
}
const uniqueModels = new Map<string, CatalogModelEntry>();
for (const model of expandedModels) {
const uniqueKey = (model.id || model.name || model.root || "").trim();
if (!uniqueKey || uniqueModels.has(uniqueKey)) {
continue;
}
uniqueModels.set(uniqueKey, model);
}
return Array.from(uniqueModels.values());
}
export async function getVscodeModelsCatalogResponse(
request: Request
): Promise<VscodeModelsCatalogResponse> {
const response = await getUnifiedModelsResponse(request);
const body = (await response.json()) as { data?: CatalogModelEntry[] };
return {
status: response.status,
headers: {
...Object.fromEntries(response.headers.entries()),
...VSCODE_CATALOG_CACHE_HEADERS,
},
body: {
...body,
data: Array.isArray(body.data) ? body.data.filter(isUsableChatModel) : body.data,
},
};
}
export async function GET(
request: Request,
{ params }: { params?: Promise<{ token: string }> | { token: string } } = {}
) {
const resolvedParams = params ? await params : undefined;
const authorizedRequest = withPathTokenApiKey(request, resolvedParams?.token);
const catalog = await getVscodeModelsCatalogResponse(authorizedRequest);
const body = catalog.body;
if (catalog.status < 200 || catalog.status >= 300 || !Array.isArray(body.data)) {
return Response.json(body, {
status: catalog.status,
headers: catalog.headers,
});
}
return Response.json(
(() => {
const expandedModels = expandVscodeServiceTierModels(body.data);
const allModelIds = new Set(
expandedModels.map((model) => (model.id || model.name || model.root || "").trim()).filter(Boolean)
);
const groupedModels = new Map<string, CatalogModelEntry>();
const orderedGroupKeys: string[] = [];
for (const model of expandedModels) {
const modelId = (model.id || model.name || model.root || "").trim();
if (!modelId) continue;
const tierParsedModel = parseVscodeServiceTierVariantModelId(modelId);
const baseModelId = getReasoningVariantBaseModelId(tierParsedModel.baseModelId);
const canonicalModelId = tierParsedModel.serviceTier
? getVscodeServiceTierVariantModelId(baseModelId, tierParsedModel.serviceTier)
: baseModelId;
if (canonicalModelId !== modelId && allModelIds.has(canonicalModelId)) {
continue;
}
const groupKey =
tierParsedModel.serviceTier
? canonicalModelId
: getVscodeModelGroupingKey({
...model,
...(canonicalModelId ? { id: canonicalModelId } : {}),
}) || canonicalModelId;
const current = groupedModels.get(groupKey);
if (!current) {
groupedModels.set(groupKey, model);
orderedGroupKeys.push(groupKey);
continue;
}
const currentId = (current.id || current.name || current.root || "").trim();
if (currentId !== groupKey && modelId === canonicalModelId) {
groupedModels.set(groupKey, model);
}
}
return {
...body,
data: orderedGroupKeys
.map((groupKey) => groupedModels.get(groupKey))
.filter(Boolean)
.map((model) => enrichModelForVscode(model as CatalogModelEntry, authorizedRequest)),
};
})(),
{
status: catalog.status,
headers: catalog.headers,
}
);
}

View File

@@ -0,0 +1,175 @@
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),
},
},
};
}

View File

@@ -0,0 +1,10 @@
import { POST as basePost, OPTIONS } from "@/app/api/v1/responses/route";
import { rewriteVscodeServiceTierRequest } from "@/app/api/v1/vscode/[token]/serviceTierVariants";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/[token]/tokenizedRequest";
export { OPTIONS };
export async function POST(request: Request) {
const authorizedRequest = withPathTokenApiKey(request);
return basePost(await rewriteVscodeServiceTierRequest(authorizedRequest));
}

View File

@@ -0,0 +1,20 @@
import { CORS_HEADERS } from "@/shared/utils/cors";
type VscodeTokenParams = {
token: string;
};
export async function OPTIONS() {
return new Response(null, { headers: CORS_HEADERS });
}
export async function GET(
request: Request,
context?: { params: Promise<VscodeTokenParams> | VscodeTokenParams }
) {
const modelsRoute = await import("@/app/api/v1/vscode/[token]/models/route");
const requestUrl = new URL(request.url);
requestUrl.pathname = `${requestUrl.pathname.replace(/\/+$/, "")}/models`;
const modelsRequest = new Request(requestUrl, request);
return modelsRoute.GET(modelsRequest, context);
}

View File

@@ -0,0 +1,190 @@
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),
});
}

View File

@@ -0,0 +1,50 @@
function inferTokenFromVscodePath(request: Request) {
try {
const url = new URL(request.url, "http://localhost");
const segments = url.pathname
.split("/")
.map((segment) => segment.trim())
.filter(Boolean);
const vscodeIndex = segments.indexOf("vscode");
if (vscodeIndex === -1) return null;
const candidate = segments[vscodeIndex + 1];
if (!candidate || candidate === "raw" || candidate === "combos") {
const nestedCandidate = segments[vscodeIndex + 2];
return nestedCandidate ? decodeURIComponent(nestedCandidate) : null;
}
return decodeURIComponent(candidate);
} catch {
return null;
}
}
export function withPathTokenApiKey(request: Request, token?: string) {
const resolvedToken = token || inferTokenFromVscodePath(request);
if (!resolvedToken) return request;
const headers = new Headers(request.headers);
if (!headers.has("x-api-key")) {
headers.set("x-api-key", resolvedToken);
}
if (!headers.has("authorization")) {
headers.set("authorization", `Bearer ${resolvedToken}`);
}
const method = request.method;
const init: RequestInit & { duplex?: "half" } = {
method,
headers,
};
if (method !== "GET" && method !== "HEAD") {
init.body = request.body;
init.duplex = "half";
}
return new Request(request.url, init);
}

View File

@@ -0,0 +1,10 @@
import { POST as basePost, OPTIONS } from "@/app/api/v1/chat/completions/route";
import { rewriteVscodeServiceTierRequest } from "@/app/api/v1/vscode/[token]/serviceTierVariants";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/[token]/tokenizedRequest";
export { OPTIONS };
export async function POST(request: Request) {
const authorizedRequest = withPathTokenApiKey(request);
return basePost(await rewriteVscodeServiceTierRequest(authorizedRequest));
}

View File

@@ -0,0 +1 @@
export { GET, OPTIONS } from "@/app/api/v1/vscode/[token]/models/route";

View File

@@ -0,0 +1,421 @@
/**
* VS Code Combos endpoint with Ollama compatibility
*
* Intercepts both:
* - GET /api/v1/vscode/combos/{token} → returns combo metadata
* - GET /api/v1/vscode/combos/{token}/api/version → returns Ollama-compatible version
* - GET /api/v1/vscode/combos/{token}/api/tags → exposes combo catalog in Ollama format
*/
import { getUnifiedModelsResponse } from "@/app/api/v1/models/catalog";
import {
buildReasoningConfigSchema,
buildSupportedReasoningEfforts,
getDefaultReasoningEffort,
getReasoningEffortValues,
inferSelectedReasoningEffort,
type VscodeCatalogModel,
} from "@/app/api/v1/vscode/[token]/reasoningMetadata";
import {
getVscodeRawModelDisplayName,
} from "@/app/api/v1/vscode/[token]/models/route";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/[token]/tokenizedRequest";
import { getCanonicalModelMetadata } from "@/lib/modelMetadataRegistry";
import { CORS_HEADERS } from "@/shared/utils/cors";
const OLLAMA_COMPAT_VERSION = "0.6.4";
type ComboCatalogEntry = {
id?: string;
name?: string;
root?: string;
owned_by?: string;
parent?: string | null;
type?: string;
api_format?: string;
context_length?: number;
max_input_tokens?: number;
max_output_tokens?: number;
input_modalities?: string[];
output_modalities?: string[];
supported_endpoints?: string[];
capabilities?: Record<string, boolean>;
[ key: string ]: unknown;
};
function isComboCatalogEntry(model: ComboCatalogEntry) {
return typeof model.owned_by === "string" && model.owned_by.trim().toLowerCase() === "combo";
}
async function buildComboCatalog(request: Request) {
const response = await getUnifiedModelsResponse(request, {
"Content-Type": "application/json",
...CORS_HEADERS,
});
const body = (await response.json()) as { data?: ComboCatalogEntry[]; [key: string]: unknown };
if (!response.ok) {
return {
status: response.status,
headers: { ...CORS_HEADERS },
body,
data: [] as ComboCatalogEntry[],
};
}
const data = Array.isArray(body.data) ? body.data.filter(isComboCatalogEntry) : [];
return {
status: response.status,
headers: { ...CORS_HEADERS },
body,
data,
};
}
function getComboFamily(model: ComboCatalogEntry, canonicalFamily?: string | null) {
const rawModelId = (model.id || model.name || model.root || "").trim();
if (rawModelId) return rawModelId;
if (canonicalFamily && canonicalFamily.trim().length > 0) return canonicalFamily.trim();
return "combo";
}
function toOllamaTagCombo(combo: ComboCatalogEntry) {
const actualModelId = (combo.id || combo.name || combo.root || "unknown").trim();
const canonicalMetadata = getCanonicalModelMetadata({
provider: combo.owned_by || null,
model: combo.root || combo.id || combo.name || null,
});
const family = getComboFamily(combo, canonicalMetadata?.metadata.family || null);
const contextLength = typeof combo.context_length === "number" ? combo.context_length : 0;
const reasoningEffortValues = getReasoningEffortValues(combo as VscodeCatalogModel);
const selectedReasoningEffort = reasoningEffortValues
? inferSelectedReasoningEffort(combo as VscodeCatalogModel, reasoningEffortValues) || "none"
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(combo as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
return {
name: actualModelId,
model: actualModelId,
modified_at: "2026-01-01T00:00:00Z",
size: 0,
digest: `omniroute:combo:${actualModelId}`,
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
details: {
format: "omniroute-combo",
family,
parameter_size: contextLength > 0 ? `${contextLength} ctx` : "unknown",
quantization_level: "dynamic",
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
};
}
function buildComboShowPayload(combo: ComboCatalogEntry) {
const actualModelId = (combo.id || combo.name || combo.root || "unknown").trim();
const displayName = getVscodeRawModelDisplayName(combo);
const canonicalMetadata = getCanonicalModelMetadata({
provider: combo.owned_by || null,
model: combo.root || combo.id || combo.name || null,
});
const family = getComboFamily(combo, canonicalMetadata?.metadata.family || null);
const reasoningEffortValues = getReasoningEffortValues(combo as VscodeCatalogModel);
const selectedReasoningEffort = reasoningEffortValues
? inferSelectedReasoningEffort(combo as VscodeCatalogModel, reasoningEffortValues) || "none"
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(combo as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
const modelCapabilities = combo.capabilities ? { ...combo.capabilities } : undefined;
const capabilities = ["completion"];
if (combo.capabilities?.vision) capabilities.push("vision");
if (combo.capabilities?.tool_calling) capabilities.push("tools");
if (combo.capabilities?.reasoning || combo.capabilities?.thinking) capabilities.push("thinking");
return {
model: actualModelId,
remote_model: displayName,
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
license: "proprietary",
modelfile: `FROM ${actualModelId}`,
parameters: "",
template: "",
details: {
parent_model: combo.root || actualModelId,
format: "omniroute-combo",
family,
families: [family],
parameter_size:
typeof combo.context_length === "number" ? `${combo.context_length} ctx` : "unknown",
quantization_level: "dynamic",
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
model_info: {
"general.architecture": "combo",
"general.basename": displayName,
...(typeof combo.context_length === "number" ? { context_length: combo.context_length } : {}),
...(typeof combo.context_length === "number" ? { "combo.context_length": combo.context_length } : {}),
...(typeof combo.max_input_tokens === "number"
? { max_input_tokens: combo.max_input_tokens }
: {}),
...(typeof combo.max_output_tokens === "number"
? { max_output_tokens: combo.max_output_tokens }
: {}),
...(Array.isArray(combo.input_modalities)
? { input_modalities: combo.input_modalities }
: {}),
...(Array.isArray(combo.output_modalities)
? { output_modalities: combo.output_modalities }
: {}),
...(modelCapabilities ? { capabilities: modelCapabilities } : {}),
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
capabilities,
};
}
function enrichComboForVscode(combo: ComboCatalogEntry, request: Request) {
const requestUrl = new URL(request.url);
const tokenBasePath = requestUrl.pathname.replace(/\/?$/, "");
const tokenBaseUrl = `${requestUrl.origin}${tokenBasePath}`;
const reasoningEffortValues = getReasoningEffortValues(combo as VscodeCatalogModel);
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(combo as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
return {
...combo,
name: getVscodeRawModelDisplayName(combo),
url: reasoningEffortValues
? `${tokenBaseUrl}/responses#models.ai.azure.com`
: `${tokenBaseUrl}/chat/completions#models.ai.azure.com`,
toolCalling: combo.capabilities?.tool_calling === true,
vision: combo.capabilities?.vision === true,
maxInputTokens: combo.max_input_tokens || combo.context_length,
maxOutputTokens: combo.max_output_tokens,
family: (combo.id || combo.name || combo.root || "combo").trim() || "combo",
...(reasoningEffortValues ? { supportsReasoningEffort: reasoningEffortValues } : {}),
...(supportedReasoningEfforts ? { supportedReasoningEfforts } : {}),
...(defaultReasoningEffort ? { defaultReasoningEffort } : {}),
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
};
}
async function readRequestedModelName(request: Request) {
const payload = await request
.clone()
.json()
.catch(() => null);
if (!payload || typeof payload !== "object") {
return null;
}
const record = payload as Record<string, unknown>;
const candidate = record.name ?? record.model;
return typeof candidate === "string" && candidate.trim().length > 0 ? candidate.trim() : null;
}
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "*",
...CORS_HEADERS,
},
});
}
export async function GET(
request: Request,
{
params,
}: {
params: Promise<{ token: string; slug?: string[] }> | { token: string; slug?: string[] };
}
) {
const resolvedParams = await params;
const slugPath = (resolvedParams.slug || []).join("/");
const authorizedRequest = withPathTokenApiKey(request, resolvedParams.token);
// Handle /api/version request (Ollama compatibility check)
if (slugPath === "api/version") {
return Response.json(
{ version: OLLAMA_COMPAT_VERSION },
{ headers: { ...CORS_HEADERS } }
);
}
// Handle /api/tags request (redirect to models for compatibility)
if (slugPath === "api/tags") {
const catalog = await buildComboCatalog(authorizedRequest);
if (catalog.status < 200 || catalog.status >= 300) {
return Response.json(catalog.body, {
status: catalog.status,
headers: catalog.headers,
});
}
return Response.json(
{ models: catalog.data.map(toOllamaTagCombo) },
{ headers: { ...CORS_HEADERS } }
);
}
// Default: return combos metadata
try {
const catalog = await buildComboCatalog(authorizedRequest);
if (catalog.status < 200 || catalog.status >= 300) {
return Response.json(catalog.body, {
status: catalog.status,
headers: catalog.headers,
});
}
const data = catalog.data.map((combo) => enrichComboForVscode(combo, authorizedRequest));
return new Response(JSON.stringify({ object: "list", data }), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
...CORS_HEADERS,
},
});
} catch (error) {
return new Response(JSON.stringify({ error: "Failed to fetch combos" }), {
status: 500,
headers: { "Content-Type": "application/json", ...CORS_HEADERS },
});
}
}
export async function POST(
request: Request,
{
params,
}: {
params: Promise<{ token: string; slug?: string[] }> | { token: string; slug?: string[] };
}
) {
const resolvedParams = await params;
const slugPath = (resolvedParams.slug || []).join("/");
const authorizedRequest = withPathTokenApiKey(request, resolvedParams.token);
if (slugPath !== "api/show") {
return new Response(JSON.stringify({ error: "Not found" }), {
status: 404,
headers: { "Content-Type": "application/json", ...CORS_HEADERS },
});
}
const requestedName = await readRequestedModelName(request);
if (!requestedName) {
return Response.json(
{ error: "Model name is required" },
{ status: 400, headers: { ...CORS_HEADERS } }
);
}
const catalog = await buildComboCatalog(authorizedRequest);
if (catalog.status < 200 || catalog.status >= 300) {
return Response.json(catalog.body, {
status: catalog.status,
headers: catalog.headers,
});
}
const combo = catalog.data.find(
(entry) => [entry.id, entry.name, entry.root].some((value) => value === requestedName)
);
if (!combo) {
return Response.json(
{ error: `Model not found: ${requestedName}` },
{ status: 404, headers: { ...CORS_HEADERS } }
);
}
return Response.json(buildComboShowPayload(combo), {
headers: { ...CORS_HEADERS },
});
}

View File

@@ -0,0 +1,5 @@
/**
* Alias Ollama compatível para chat no namespace raw.
* Apenas reexporta a rota base dessa compatibilidade.
*/
export { POST, OPTIONS } from "@/app/api/v1/api/chat/route";

View File

@@ -0,0 +1,327 @@
import { getUnifiedModelsResponse } from "@/app/api/v1/models/catalog";
import { getVscodeRawModelDisplayName } from "@/app/api/v1/vscode/[token]/models/route";
import { getCanonicalModelMetadata } from "@/lib/modelMetadataRegistry";
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
import { expandVscodeRawModels } from "@/app/api/v1/vscode/[token]/models/route";
import {
buildReasoningConfigSchema,
buildSupportedReasoningEfforts,
getDefaultReasoningEffort,
getReasoningEffortValues,
inferSelectedReasoningEffort,
type VscodeCatalogModel,
} from "@/app/api/v1/vscode/raw/[token]/reasoningMetadata";
import { parseVscodeServiceTierVariantModelId } from "@/app/api/v1/vscode/raw/[token]/serviceTierVariants";
import { getFamilyFirstModelCandidates } from "@/app/api/v1/vscode/raw/[token]/familyFirstModelIds";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/raw/[token]/tokenizedRequest";
type OpenAiCatalogModel = {
id?: string;
name?: string;
root?: string;
parent?: string | null;
owned_by?: string;
type?: string;
api_format?: string;
context_length?: number;
max_output_tokens?: number;
capabilities?: Record<string, boolean>;
input_modalities?: string[];
output_modalities?: string[];
supported_endpoints?: string[];
};
function isUsableChatModel(model: OpenAiCatalogModel) {
if (typeof model.owned_by === "string" && model.owned_by.trim().toLowerCase() === "combo") {
return false;
}
if (typeof model.parent === "string" && model.parent.length > 0) return false;
if (typeof model.type === "string" && model.type !== "chat") return false;
const apiFormat = typeof model.api_format === "string" ? model.api_format : "chat-completions";
if (apiFormat !== "chat-completions") return false;
if (
Array.isArray(model.supported_endpoints) &&
model.supported_endpoints.length > 0 &&
!model.supported_endpoints.includes("chat")
) {
return false;
}
if (
Array.isArray(model.output_modalities) &&
model.output_modalities.length > 0 &&
!model.output_modalities.includes("text")
) {
return false;
}
return true;
}
function getCatalogModelId(model: OpenAiCatalogModel) {
return model.id || model.name || model.root || "unknown";
}
function normalizeArchitectureKey(value: string) {
const normalized = value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
return normalized || "model";
}
function normalizeArchitectureSource(value: string) {
const normalized = value.trim().toLowerCase();
if (normalized === "cx") return "codex";
if (normalized === "gh") return "github";
return value;
}
function getRequestedModelName(payload: unknown): string | null {
if (!payload || typeof payload !== "object") return null;
const record = payload as Record<string, unknown>;
const candidate = record.name ?? record.model;
return typeof candidate === "string" && candidate.trim().length > 0 ? candidate.trim() : null;
}
function getOllamaModelFamily(model: OpenAiCatalogModel, canonicalFamily?: string | null) {
const rawModelId = getCatalogModelId(model).trim();
const { baseModelId } = parseVscodeServiceTierVariantModelId(rawModelId);
const modelFamily = baseModelId.includes("/") ? baseModelId.split("/").slice(1).join("/") : baseModelId;
if (modelFamily) {
return modelFamily;
}
if (canonicalFamily && canonicalFamily.trim().length > 0) {
return canonicalFamily.trim();
}
return typeof model.owned_by === "string" && model.owned_by.trim().length > 0
? model.owned_by.trim()
: "omniroute";
}
function matchesRequestedModel(model: OpenAiCatalogModel, requestedName: string): boolean {
const canonicalMetadata = getCanonicalModelMetadata({
provider: model.owned_by || null,
model: model.root || model.id || model.name || null,
});
const family = getOllamaModelFamily(model, canonicalMetadata?.metadata.family || null);
const actualModelId = getCatalogModelId(model);
return [
model.id,
model.name,
model.root,
canonicalMetadata?.qualifiedId,
canonicalMetadata?.model,
...getFamilyFirstModelCandidates(actualModelId, family),
].some((value) => value === requestedName);
}
function buildCapabilities(model: OpenAiCatalogModel): string[] {
const capabilities = ["completion"];
if (model.capabilities?.vision) capabilities.push("vision");
if (model.capabilities?.tool_calling) capabilities.push("tools");
if (model.capabilities?.reasoning || model.capabilities?.thinking) capabilities.push("thinking");
return capabilities;
}
function buildShowPayload(model: OpenAiCatalogModel, responseModelId?: string) {
const actualModelId = getCatalogModelId(model);
const displayName = getVscodeRawModelDisplayName(model);
const canonicalMetadata = getCanonicalModelMetadata({
provider: model.owned_by || null,
model: model.root || model.id || model.name || null,
});
const family = getOllamaModelFamily(model, canonicalMetadata?.metadata.family || null);
const modelId = responseModelId || actualModelId;
const architectureSource =
normalizeArchitectureSource(
canonicalMetadata?.providerAlias || canonicalMetadata?.provider || model.owned_by || family || "model"
);
const architecture = normalizeArchitectureKey(
architectureSource
);
const reasoningEffortValues = getReasoningEffortValues(model as VscodeCatalogModel);
const selectedReasoningEffort = reasoningEffortValues
? inferSelectedReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues) || "none"
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
let modelCapabilities = model.capabilities ? { ...model.capabilities } : undefined;
if (reasoningEffortValues) {
modelCapabilities = modelCapabilities || {};
Object.assign(modelCapabilities, {
reasoning: true,
thinking: true,
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
});
}
return {
model: modelId,
remote_model: displayName,
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
license: "proprietary",
modelfile: `FROM ${modelId}`,
parameters: "",
template: "",
details: {
parent_model: model.root || actualModelId || "",
format: "openai",
family,
families: [family],
parameter_size: "unknown",
quantization_level: "dynamic",
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
model_info: {
"general.architecture": architecture,
"general.basename": displayName,
...(typeof model.context_length === "number" ? { context_length: model.context_length } : {}),
...(typeof model.context_length === "number"
? { [`${architecture}.context_length`]: model.context_length }
: {}),
...(typeof model.max_output_tokens === "number"
? { max_output_tokens: model.max_output_tokens }
: {}),
...(Array.isArray(model.input_modalities)
? { input_modalities: model.input_modalities }
: {}),
...(Array.isArray(model.output_modalities)
? { output_modalities: model.output_modalities }
: {}),
...(Array.isArray(model.supported_endpoints)
? { supported_endpoints: model.supported_endpoints }
: {}),
...(modelCapabilities ? { capabilities: modelCapabilities } : {}),
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
capabilities: buildCapabilities(model),
};
}
export async function OPTIONS() {
return handleCorsOptions();
}
export async function POST(
request: Request,
{ params }: { params?: Promise<{ token: string }> | { token: string } } = {}
) {
const resolvedParams = params ? await params : undefined;
const authorizedRequest = withPathTokenApiKey(request, resolvedParams?.token);
const payload = await request
.clone()
.json()
.catch(() => null);
const requestedName = getRequestedModelName(payload);
if (!requestedName) {
return Response.json(
{
error: "Model name is required",
},
{
status: 400,
headers: {
...CORS_HEADERS,
},
}
);
}
const catalogResponse = await getUnifiedModelsResponse(authorizedRequest, {
"Content-Type": "application/json",
...CORS_HEADERS,
});
const catalogBody = (await catalogResponse.json()) as { data?: OpenAiCatalogModel[] };
if (!catalogResponse.ok) {
return Response.json(catalogBody, {
status: catalogResponse.status,
headers: {
...CORS_HEADERS,
},
});
}
const expandedModels = Array.isArray(catalogBody.data)
? expandVscodeRawModels(catalogBody.data.filter(isUsableChatModel))
: [];
const model = Array.isArray(expandedModels)
? expandedModels.find((entry) => matchesRequestedModel(entry, requestedName))
: undefined;
if (!model) {
return Response.json(
{
error: `Model not found: ${requestedName}`,
},
{
status: 404,
headers: {
...CORS_HEADERS,
},
}
);
}
return Response.json(buildShowPayload(model), {
headers: {
...CORS_HEADERS,
},
});
}

View File

@@ -0,0 +1,213 @@
import { getUnifiedModelsResponse } from "@/app/api/v1/models/catalog";
import { getProviderConnections } from "@/lib/db/providers";
import { getCanonicalModelMetadata } from "@/lib/modelMetadataRegistry";
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
import { expandVscodeRawModels } from "@/app/api/v1/vscode/[token]/models/route";
import {
buildReasoningConfigSchema,
buildSupportedReasoningEfforts,
getDefaultReasoningEffort,
getReasoningVariantBaseModelId,
getReasoningEffortValues,
inferSelectedReasoningEffort,
type VscodeCatalogModel,
} from "@/app/api/v1/vscode/raw/[token]/reasoningMetadata";
import { parseVscodeServiceTierVariantModelId } from "@/app/api/v1/vscode/raw/[token]/serviceTierVariants";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/raw/[token]/tokenizedRequest";
type OpenAiCatalogModel = {
id?: string;
name?: string;
root?: string;
parent?: string | null;
owned_by?: string;
type?: string;
api_format?: string;
context_length?: number;
output_modalities?: string[];
supported_endpoints?: string[];
};
function getModelName(model: OpenAiCatalogModel) {
return model.id || model.name || model.root || "";
}
function isCodexOwnedModel(model: OpenAiCatalogModel) {
const owner = typeof model.owned_by === "string" ? model.owned_by.trim().toLowerCase() : "";
const modelName = getModelName(model).toLowerCase();
return owner === "codex" || modelName.startsWith("cx/") || modelName.startsWith("codex/");
}
async function selectPreferredModels(models: OpenAiCatalogModel[]) {
const activeConnections = (await getProviderConnections({ isActive: true })) as Array<{
provider?: string;
}>;
const activeProviders = new Set(
activeConnections
.map((connection) =>
typeof connection.provider === "string" ? connection.provider.trim().toLowerCase() : ""
)
.filter(Boolean)
);
const preferCodexOnly =
activeProviders.size > 0 && Array.from(activeProviders).every((provider) => provider === "codex");
if (!preferCodexOnly) return models;
const codexModels = models.filter(isCodexOwnedModel);
return codexModels.length > 0 ? codexModels : models;
}
function isUsableChatModel(model: OpenAiCatalogModel) {
if (typeof model.owned_by === "string" && model.owned_by.trim().toLowerCase() === "combo") {
return false;
}
if (typeof model.parent === "string" && model.parent.length > 0) return false;
if (typeof model.type === "string" && model.type !== "chat") return false;
const apiFormat = typeof model.api_format === "string" ? model.api_format : "chat-completions";
if (apiFormat !== "chat-completions") return false;
if (
Array.isArray(model.supported_endpoints) &&
model.supported_endpoints.length > 0 &&
!model.supported_endpoints.includes("chat")
) {
return false;
}
if (
Array.isArray(model.output_modalities) &&
model.output_modalities.length > 0 &&
!model.output_modalities.includes("text")
) {
return false;
}
return true;
}
function getOllamaModelFamily(model: OpenAiCatalogModel, canonicalFamily?: string | null) {
const rawModelId = getModelName(model).trim();
const tierParsedModel = parseVscodeServiceTierVariantModelId(rawModelId);
const baseModelId = getReasoningVariantBaseModelId(tierParsedModel.baseModelId);
const modelFamily = baseModelId.includes("/") ? baseModelId.split("/").slice(1).join("/") : baseModelId;
if (modelFamily) {
return modelFamily;
}
if (canonicalFamily && canonicalFamily.trim().length > 0) {
return canonicalFamily.trim();
}
return typeof model.owned_by === "string" && model.owned_by.trim().length > 0
? model.owned_by.trim()
: "omniroute";
}
function toOllamaTagModel(model: OpenAiCatalogModel) {
const actualModelId = model.id || model.root || "unknown";
const canonicalMetadata = getCanonicalModelMetadata({
provider: model.owned_by || null,
model: model.root || model.id || model.name || null,
});
const family = getOllamaModelFamily(model, canonicalMetadata?.metadata.family || null);
const modelId = actualModelId;
const contextLength = typeof model.context_length === "number" ? model.context_length : 0;
const reasoningEffortValues = getReasoningEffortValues(model as VscodeCatalogModel);
const selectedReasoningEffort = reasoningEffortValues
? inferSelectedReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues) || "none"
: undefined;
const defaultReasoningEffort = reasoningEffortValues
? getDefaultReasoningEffort(model as VscodeCatalogModel, reasoningEffortValues)
: undefined;
const supportedReasoningEfforts =
reasoningEffortValues && reasoningEffortValues.length > 0
? buildSupportedReasoningEfforts(reasoningEffortValues)
: undefined;
const configSchema =
reasoningEffortValues && defaultReasoningEffort
? buildReasoningConfigSchema(reasoningEffortValues, defaultReasoningEffort)
: undefined;
return {
name: modelId,
model: modelId,
modified_at: "2026-01-01T00:00:00Z",
size: 0,
digest: `omniroute:${modelId}`,
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
details: {
format: "openai",
family,
parameter_size: contextLength > 0 ? `${contextLength} ctx` : "unknown",
quantization_level: "dynamic",
...(reasoningEffortValues
? {
supports_reasoning_effort: reasoningEffortValues,
supportsReasoningEffort: reasoningEffortValues,
supportedReasoningEfforts,
defaultReasoningEffort,
selected_reasoning_effort: selectedReasoningEffort,
selectedReasoningEffort: selectedReasoningEffort,
...(configSchema ? { configurationSchema: configSchema } : {}),
...(configSchema ? { configSchema } : {}),
}
: {}),
},
};
}
export async function OPTIONS() {
return handleCorsOptions();
}
export async function GET(
request: Request,
{ params }: { params?: Promise<{ token: string }> | { token: string } } = {}
) {
const resolvedParams = params ? await params : undefined;
const authorizedRequest = withPathTokenApiKey(request, resolvedParams?.token);
const response = await getUnifiedModelsResponse(authorizedRequest, {
"Content-Type": "application/json",
...CORS_HEADERS,
});
const body = (await response.json()) as { data?: OpenAiCatalogModel[] };
if (!response.ok) {
return Response.json(body, {
status: response.status,
headers: {
...CORS_HEADERS,
},
});
}
const usableModels = Array.isArray(body.data) ? body.data.filter(isUsableChatModel) : [];
const preferredModels = expandVscodeRawModels(await selectPreferredModels(usableModels));
const models = preferredModels.map(toOllamaTagModel);
return Response.json(
{
models,
},
{
headers: {
...CORS_HEADERS,
},
}
);
}

View File

@@ -0,0 +1,20 @@
import { CORS_HEADERS, handleCorsOptions } from "@/shared/utils/cors";
const OLLAMA_COMPAT_VERSION = "0.6.4";
export async function OPTIONS() {
return handleCorsOptions();
}
export async function GET() {
return Response.json(
{
version: OLLAMA_COMPAT_VERSION,
},
{
headers: {
...CORS_HEADERS,
},
}
);
}

View File

@@ -0,0 +1,10 @@
import { POST as basePost, OPTIONS } from "@/app/api/v1/chat/completions/route";
import { rewriteVscodeServiceTierRequest } from "@/app/api/v1/vscode/raw/[token]/serviceTierVariants";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/raw/[token]/tokenizedRequest";
export { OPTIONS };
export async function POST(request: Request) {
const authorizedRequest = withPathTokenApiKey(request);
return basePost(await rewriteVscodeServiceTierRequest(authorizedRequest));
}

View File

@@ -0,0 +1,41 @@
/**
* VS Code Combos endpoint — re-export base Ollama-compatible routes
* and intercept GET to return combo metadata.
*
* The VS Code extension expects a standard Ollama server at the base URL,
* so we re-export /api/version, /api/tags, etc. from the [token] parent route.
*/
import { getCombos } from "@/lib/db/combos";
import { projectCombo, type PublicCombo } from "@/app/api/v1/combos/projectCombo";
// Re-export Ollama-compatible endpoints from the parent [token] route
// so VS Code can validate the server version and list models normally
export { OPTIONS } from "@/app/api/v1/vscode/raw/[token]/route";
export async function GET(request: Request) {
// If client requests /api/version or other Ollama endpoints, delegate to parent
const url = new URL(request.url);
if (url.pathname.includes("/api/version") || url.pathname.includes("/api/tags")) {
const { GET: parentGET } = await import("@/app/api/v1/vscode/raw/[token]/route");
return parentGET(request);
}
// Default: return combos metadata
try {
const combos = await getCombos();
const data = (Array.isArray(combos) ? combos : [])
.map((combo) => projectCombo(combo as Record<string, unknown>))
.filter((combo): combo is PublicCombo => combo !== null);
return new Response(JSON.stringify({ object: "list", data, combos: data }), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
},
});
} catch (error) {
return new Response(JSON.stringify({ error: "Failed to fetch combos" }), {
status: 500,
headers: { "Content-Type": "application/json" },
});
}
}

View File

@@ -0,0 +1,85 @@
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];
}

View File

@@ -0,0 +1,178 @@
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-cli": "Gemini",
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 || "";
}

View File

@@ -0,0 +1,43 @@
import {
enrichModelForVscode,
expandVscodeRawModels,
getVscodeModelsCatalogResponse,
} from "@/app/api/v1/vscode/[token]/models/route";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/raw/[token]/tokenizedRequest";
export async function OPTIONS() {
return new Response(null, {
headers: {
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "*",
},
});
}
export async function GET(
request: Request,
{ params }: { params?: Promise<{ token: string }> | { token: string } } = {}
) {
const resolvedParams = params ? await params : undefined;
const authorizedRequest = withPathTokenApiKey(request, resolvedParams?.token);
const catalog = await getVscodeModelsCatalogResponse(authorizedRequest);
if (catalog.status < 200 || catalog.status >= 300 || !Array.isArray(catalog.body.data)) {
return Response.json(catalog.body, {
status: catalog.status,
headers: catalog.headers,
});
}
return Response.json(
{
...catalog.body,
data: expandVscodeRawModels(catalog.body.data).map((model) =>
enrichModelForVscode(model, authorizedRequest, { preserveNativeId: true })
),
},
{
status: catalog.status,
headers: catalog.headers,
}
);
}

View File

@@ -0,0 +1,175 @@
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),
},
},
};
}

View File

@@ -0,0 +1,10 @@
import { POST as basePost, OPTIONS } from "@/app/api/v1/responses/route";
import { rewriteVscodeServiceTierRequest } from "@/app/api/v1/vscode/raw/[token]/serviceTierVariants";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/raw/[token]/tokenizedRequest";
export { OPTIONS };
export async function POST(request: Request) {
const authorizedRequest = withPathTokenApiKey(request);
return basePost(await rewriteVscodeServiceTierRequest(authorizedRequest));
}

View File

@@ -0,0 +1,20 @@
import { CORS_HEADERS } from "@/shared/utils/cors";
type VscodeTokenParams = {
token: string;
};
export async function OPTIONS() {
return new Response(null, { headers: CORS_HEADERS });
}
export async function GET(
request: Request,
context?: { params: Promise<VscodeTokenParams> | VscodeTokenParams }
) {
const modelsRoute = await import("@/app/api/v1/vscode/raw/[token]/models/route");
const requestUrl = new URL(request.url);
requestUrl.pathname = `${requestUrl.pathname.replace(/\/+$/, "")}/models`;
const modelsRequest = new Request(requestUrl, request);
return modelsRoute.GET(modelsRequest, context);
}

View File

@@ -0,0 +1,190 @@
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),
});
}

View File

@@ -0,0 +1,50 @@
function inferTokenFromVscodePath(request: Request) {
try {
const url = new URL(request.url, "http://localhost");
const segments = url.pathname
.split("/")
.map((segment) => segment.trim())
.filter(Boolean);
const vscodeIndex = segments.indexOf("vscode");
if (vscodeIndex === -1) return null;
const candidate = segments[vscodeIndex + 1];
if (!candidate || candidate === "raw" || candidate === "combos") {
const nestedCandidate = segments[vscodeIndex + 2];
return nestedCandidate ? decodeURIComponent(nestedCandidate) : null;
}
return decodeURIComponent(candidate);
} catch {
return null;
}
}
export function withPathTokenApiKey(request: Request, token?: string) {
const resolvedToken = token || inferTokenFromVscodePath(request);
if (!resolvedToken) return request;
const headers = new Headers(request.headers);
if (!headers.has("x-api-key")) {
headers.set("x-api-key", resolvedToken);
}
if (!headers.has("authorization")) {
headers.set("authorization", `Bearer ${resolvedToken}`);
}
const method = request.method;
const init: RequestInit & { duplex?: "half" } = {
method,
headers,
};
if (method !== "GET" && method !== "HEAD") {
init.body = request.body;
init.duplex = "half";
}
return new Request(request.url, init);
}

View File

@@ -0,0 +1,10 @@
import { POST as basePost, OPTIONS } from "@/app/api/v1/chat/completions/route";
import { rewriteVscodeServiceTierRequest } from "@/app/api/v1/vscode/raw/[token]/serviceTierVariants";
import { withPathTokenApiKey } from "@/app/api/v1/vscode/raw/[token]/tokenizedRequest";
export { OPTIONS };
export async function POST(request: Request) {
const authorizedRequest = withPathTokenApiKey(request);
return basePost(await rewriteVscodeServiceTierRequest(authorizedRequest));
}

View File

@@ -0,0 +1 @@
export { GET, OPTIONS } from "@/app/api/v1/vscode/raw/[token]/models/route";

File diff suppressed because it is too large Load Diff