mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-20 22:02:19 +03:00
Merged. Root cause first: `publishers.models.list` was called with `pageSize=1000` against Google's hard maximum of 300, so every publisher answered 400 and discovery silently fell back to the stale static catalog. On top of that the PR separates API-key from Service-Account capabilities correctly (keys cannot list Model Garden — project-scoped curated catalog; SA tokens can — live catalog), stops treating the expected generativelanguage rejection of a Service Account as a discovery failure, replaces the speculative partner IDs with documented MaaS IDs, and rejects OAuth client-config JSON with a clear message instead of a misleading one. The 5 ESLint errors flagged during the earlier fix sweep were fixed in your own follow-up commits; the branch was reconciled with the release tip and the 42 locale files were checked for lost keys before this merge. Validated as a combined board first (this PR merged with the 4 siblings of the JxnLexn wave on the release tip): eslint with the frozen suppressions, typecheck:core, check:open-sse-typecheck, complexity, cognitive-complexity, changelog-integrity, docs-sync, migration-numbering, provider-consistency and a duplicate-identifier audit all green, plus 77 passing / 0 failing focused node:test cases across the test files the wave touches. The wave's i18n fill (new keys carried to all 66 locales), free-tier doc counts and file-size rebaseline land in one follow-up PR right after the wave, as with #13904. Thank you — the credential-capability distinction and the retired/non-chat filtering are what make the Vertex listing trustworthy.
107 lines
4.1 KiB
TypeScript
107 lines
4.1 KiB
TypeScript
import type { RegistryModel } from "./providers/shared.ts";
|
|
|
|
const VERTEX_PUBLISHER_RESOURCE_PATTERN =
|
|
/^(?:(?:projects\/[^/]+\/locations\/[^/]+\/)?publishers\/([^/]+)\/models\/|([^/]+)\/models\/)(.+)$/i;
|
|
const NATIVE_PUBLISHERS = new Set(["google", "anthropic", "mistralai"]);
|
|
|
|
const LEGACY_OPENAI_MAAS_PUBLISHERS = [
|
|
{ prefix: "deepseek-", publisher: "deepseek-ai" },
|
|
{ prefix: "qwen", publisher: "qwen" },
|
|
{ prefix: "llama-", publisher: "meta" },
|
|
{ prefix: "glm-", publisher: "zai-org" },
|
|
{ prefix: "gpt-oss-", publisher: "openai" },
|
|
{ prefix: "kimi-", publisher: "moonshotai" },
|
|
{ prefix: "minimax-", publisher: "minimaxai" },
|
|
] as const;
|
|
|
|
export type VertexModelTransport = "gemini" | "anthropic" | "mistral" | "openai";
|
|
|
|
function normalizePublisherModel(publisher: string, model: string): string {
|
|
const normalizedPublisher = publisher.toLowerCase();
|
|
if (NATIVE_PUBLISHERS.has(normalizedPublisher)) return model;
|
|
return `${normalizedPublisher}/${model}`;
|
|
}
|
|
|
|
/**
|
|
* Convert Model Garden resource/version names to the model value accepted by its inference API.
|
|
* Native Google, Anthropic, and Mistral endpoints encode the publisher in the URL and take a bare
|
|
* model id. OpenAI-compatible MaaS requests keep the publisher namespace in the request body.
|
|
*/
|
|
export function normalizeVertexModelId(model: string): string {
|
|
const trimmed = model.trim().replace(/^(?:vertex|vp)\//i, "");
|
|
const resourceMatch = trimmed.match(VERTEX_PUBLISHER_RESOURCE_PATTERN);
|
|
if (resourceMatch) {
|
|
const publisher = resourceMatch[1] || resourceMatch[2];
|
|
const modelId = resourceMatch[3];
|
|
if (publisher && modelId) return normalizePublisherModel(publisher, modelId);
|
|
}
|
|
|
|
const namespacedMatch = trimmed.match(/^([^/]+)\/(.+)$/);
|
|
if (namespacedMatch && NATIVE_PUBLISHERS.has(namespacedMatch[1].toLowerCase())) {
|
|
return namespacedMatch[2];
|
|
}
|
|
|
|
// Keep manually-added historical Grok ids working while emitting Google's documented MaaS
|
|
// request form (`xai/grok-*`) for the OpenAI-compatible endpoint.
|
|
if (/^grok-/i.test(trimmed)) return `xai/${trimmed}`;
|
|
const legacyMaaS = LEGACY_OPENAI_MAAS_PUBLISHERS.find(({ prefix }) =>
|
|
trimmed.toLowerCase().startsWith(prefix)
|
|
);
|
|
if (legacyMaaS) return `${legacyMaaS.publisher}/${trimmed}`;
|
|
return trimmed;
|
|
}
|
|
|
|
/** Resolve the Vertex transport from either a request id or a Model Garden resource name. */
|
|
export function getVertexModelTransport(model: string): VertexModelTransport {
|
|
const normalized = normalizeVertexModelId(model).toLowerCase();
|
|
if (normalized.startsWith("claude-")) return "anthropic";
|
|
if (normalized.startsWith("mistral-")) return "mistral";
|
|
if (normalized.includes("/")) return "openai";
|
|
return "gemini";
|
|
}
|
|
|
|
/** Wire format used before the request reaches the provider-specific Vertex executor. */
|
|
export function getVertexModelTargetFormat(model: string): "claude" | "openai" | null {
|
|
const transport = getVertexModelTransport(model);
|
|
if (transport === "anthropic") return "claude";
|
|
if (transport === "mistral" || transport === "openai") return "openai";
|
|
return null;
|
|
}
|
|
|
|
/** True for xAI models served through Vertex's OpenAI-compatible MaaS endpoint. */
|
|
export function isVertexXaiModel(model: string): boolean {
|
|
return /^xai\/grok-/i.test(normalizeVertexModelId(model));
|
|
}
|
|
|
|
/** True only for models exposed by the project-less Vertex Express API. */
|
|
export function isVertexExpressModel(model: string): boolean {
|
|
return (
|
|
getVertexModelTransport(model) === "gemini" && /^gemini-/i.test(normalizeVertexModelId(model))
|
|
);
|
|
}
|
|
|
|
/** Curated fallback metadata for Vertex xAI MaaS models. */
|
|
export const VERTEX_XAI_MODELS = [
|
|
{
|
|
id: "xai/grok-4.6",
|
|
name: "Grok 4.6",
|
|
contextLength: 524288,
|
|
supportsReasoning: true,
|
|
supportsVision: true,
|
|
toolCalling: true,
|
|
targetFormat: "openai",
|
|
},
|
|
{ id: "xai/grok-4.3", name: "Grok 4.3", targetFormat: "openai" },
|
|
{
|
|
id: "xai/grok-4.20-reasoning",
|
|
name: "Grok 4.20 Reasoning",
|
|
supportsReasoning: true,
|
|
targetFormat: "openai",
|
|
},
|
|
{
|
|
id: "xai/grok-4.20-non-reasoning",
|
|
name: "Grok 4.20 Non-Reasoning",
|
|
targetFormat: "openai",
|
|
},
|
|
] as const satisfies readonly RegistryModel[];
|