refactor(open-sse): P2 alias single-source — derive OAuth alias→id from OAUTH_ALIASES
- Export OAUTH_ALIASES as canonical id→alias source - model.js derives 16 OAuth alias→id pairs instead of hardcoding (mmf kept out to preserve behavior) - Add verify-alias.mjs byte-for-byte guard (133 tokens), all equal Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -896,7 +896,8 @@ export function getModelQuotaFamily(aliasOrId, modelId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OAuth providers that use short aliases (everything else: alias = id)
|
// OAuth providers that use short aliases (everything else: alias = id)
|
||||||
const OAUTH_ALIASES = {
|
// Single source of canonical id→alias; services/model.js derives the reverse.
|
||||||
|
export const OAUTH_ALIASES = {
|
||||||
claude: "cc",
|
claude: "cc",
|
||||||
codex: "cx",
|
codex: "cx",
|
||||||
"gemini-cli": "gc",
|
"gemini-cli": "gc",
|
||||||
|
|||||||
@@ -1,20 +1,16 @@
|
|||||||
// Provider alias to ID mapping
|
import { OAUTH_ALIASES } from "../config/providerModels.js";
|
||||||
|
|
||||||
|
// Reverse OAuth aliases (id→alias) into alias→id; "mmf" intentionally excluded to preserve behavior
|
||||||
|
const OAUTH_ALIAS_TO_ID = Object.fromEntries(
|
||||||
|
Object.entries(OAUTH_ALIASES)
|
||||||
|
.filter(([id]) => id !== "mimo-free")
|
||||||
|
.map(([id, alias]) => [alias, id])
|
||||||
|
);
|
||||||
|
|
||||||
|
// Provider alias to ID mapping (OAuth short aliases derived above)
|
||||||
const ALIAS_TO_PROVIDER_ID = {
|
const ALIAS_TO_PROVIDER_ID = {
|
||||||
cc: "claude",
|
...OAUTH_ALIAS_TO_ID,
|
||||||
cx: "codex",
|
|
||||||
gc: "gemini-cli",
|
|
||||||
qw: "qwen",
|
|
||||||
if: "iflow",
|
|
||||||
ag: "antigravity",
|
|
||||||
gh: "github",
|
|
||||||
kr: "kiro",
|
|
||||||
cu: "cursor",
|
|
||||||
kc: "kilocode",
|
|
||||||
kmc: "kimi-coding",
|
|
||||||
cl: "cline",
|
|
||||||
oc: "opencode",
|
|
||||||
ocg: "opencode-go",
|
ocg: "opencode-go",
|
||||||
qd: "qoder",
|
|
||||||
qoder: "qoder",
|
qoder: "qoder",
|
||||||
// TTS providers
|
// TTS providers
|
||||||
el: "elevenlabs",
|
el: "elevenlabs",
|
||||||
|
|||||||
335
tests/__baseline__/alias-baseline.json
Normal file
335
tests/__baseline__/alias-baseline.json
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
{
|
||||||
|
"aliasToId": {
|
||||||
|
"cc": "claude",
|
||||||
|
"cx": "codex",
|
||||||
|
"gc": "gemini-cli",
|
||||||
|
"qw": "qwen",
|
||||||
|
"if": "iflow",
|
||||||
|
"ag": "antigravity",
|
||||||
|
"gh": "github",
|
||||||
|
"kr": "kiro",
|
||||||
|
"cu": "cursor",
|
||||||
|
"kc": "kilocode",
|
||||||
|
"kmc": "kimi-coding",
|
||||||
|
"cl": "cline",
|
||||||
|
"oc": "opencode",
|
||||||
|
"ocg": "opencode-go",
|
||||||
|
"qd": "qoder",
|
||||||
|
"qoder": "qoder",
|
||||||
|
"el": "elevenlabs",
|
||||||
|
"openai": "openai",
|
||||||
|
"vercel": "vercel-ai-gateway",
|
||||||
|
"vercel-ai-gateway": "vercel-ai-gateway",
|
||||||
|
"anthropic": "anthropic",
|
||||||
|
"gemini": "gemini",
|
||||||
|
"openrouter": "openrouter",
|
||||||
|
"glm": "glm",
|
||||||
|
"kimi": "kimi",
|
||||||
|
"minimax": "minimax",
|
||||||
|
"minimax-cn": "minimax-cn",
|
||||||
|
"hf": "huggingface",
|
||||||
|
"huggingface": "huggingface",
|
||||||
|
"ds": "deepseek",
|
||||||
|
"deepseek": "deepseek",
|
||||||
|
"cmc": "commandcode",
|
||||||
|
"commandcode": "commandcode",
|
||||||
|
"groq": "groq",
|
||||||
|
"xai": "xai",
|
||||||
|
"mistral": "mistral",
|
||||||
|
"pplx": "perplexity",
|
||||||
|
"perplexity": "perplexity",
|
||||||
|
"together": "together",
|
||||||
|
"fireworks": "fireworks",
|
||||||
|
"cerebras": "cerebras",
|
||||||
|
"cohere": "cohere",
|
||||||
|
"nvidia": "nvidia",
|
||||||
|
"nebius": "nebius",
|
||||||
|
"siliconflow": "siliconflow",
|
||||||
|
"hyp": "hyperbolic",
|
||||||
|
"hyperbolic": "hyperbolic",
|
||||||
|
"dg": "deepgram",
|
||||||
|
"deepgram": "deepgram",
|
||||||
|
"aai": "assemblyai",
|
||||||
|
"assemblyai": "assemblyai",
|
||||||
|
"nb": "nanobanana",
|
||||||
|
"nanobanana": "nanobanana",
|
||||||
|
"ch": "chutes",
|
||||||
|
"chutes": "chutes",
|
||||||
|
"ark": "volcengine-ark",
|
||||||
|
"volcengine-ark": "volcengine-ark",
|
||||||
|
"byteplus": "byteplus",
|
||||||
|
"bpm": "byteplus",
|
||||||
|
"cursor": "cursor",
|
||||||
|
"vx": "vertex",
|
||||||
|
"vertex": "vertex",
|
||||||
|
"vxp": "vertex-partner",
|
||||||
|
"vertex-partner": "vertex-partner",
|
||||||
|
"gw": "grok-web",
|
||||||
|
"grok-web": "grok-web",
|
||||||
|
"pw": "perplexity-web",
|
||||||
|
"perplexity-web": "perplexity-web",
|
||||||
|
"mimo": "xiaomi-mimo",
|
||||||
|
"xiaomi-mimo": "xiaomi-mimo",
|
||||||
|
"xmtp": "xiaomi-tokenplan",
|
||||||
|
"xiaomi-tokenplan": "xiaomi-tokenplan",
|
||||||
|
"cf": "cloudflare-ai",
|
||||||
|
"cloudflare-ai": "cloudflare-ai",
|
||||||
|
"fal": "fal-ai",
|
||||||
|
"fal-ai": "fal-ai",
|
||||||
|
"stability": "stability-ai",
|
||||||
|
"stability-ai": "stability-ai",
|
||||||
|
"bfl": "black-forest-labs",
|
||||||
|
"black-forest-labs": "black-forest-labs",
|
||||||
|
"recraft": "recraft",
|
||||||
|
"topaz": "topaz",
|
||||||
|
"runway": "runwayml",
|
||||||
|
"runwayml": "runwayml",
|
||||||
|
"jina": "jina-ai",
|
||||||
|
"jina-ai": "jina-ai",
|
||||||
|
"polly": "aws-polly",
|
||||||
|
"aws-polly": "aws-polly",
|
||||||
|
"agentrouter": "agentrouter",
|
||||||
|
"aimlapi": "aimlapi",
|
||||||
|
"aiml": "aimlapi",
|
||||||
|
"novita": "novita",
|
||||||
|
"modal": "modal",
|
||||||
|
"mdl": "modal",
|
||||||
|
"reka": "reka",
|
||||||
|
"nlpcloud": "nlpcloud",
|
||||||
|
"nlpc": "nlpcloud",
|
||||||
|
"bazaarlink": "bazaarlink",
|
||||||
|
"bzl": "bazaarlink",
|
||||||
|
"completions": "completions",
|
||||||
|
"cpl": "completions",
|
||||||
|
"enally": "enally",
|
||||||
|
"enly": "enally",
|
||||||
|
"freetheai": "freetheai",
|
||||||
|
"fta": "freetheai",
|
||||||
|
"llm7": "llm7",
|
||||||
|
"lepton": "lepton",
|
||||||
|
"kluster": "kluster",
|
||||||
|
"ai21": "ai21",
|
||||||
|
"inference-net": "inference-net",
|
||||||
|
"inet": "inference-net",
|
||||||
|
"predibase": "predibase",
|
||||||
|
"bytez": "bytez",
|
||||||
|
"morph": "morph",
|
||||||
|
"longcat": "longcat",
|
||||||
|
"lc": "longcat",
|
||||||
|
"puter": "puter",
|
||||||
|
"pu": "puter",
|
||||||
|
"uncloseai": "uncloseai",
|
||||||
|
"unc": "uncloseai",
|
||||||
|
"scaleway": "scaleway",
|
||||||
|
"scw": "scaleway",
|
||||||
|
"deepinfra": "deepinfra",
|
||||||
|
"sambanova": "sambanova",
|
||||||
|
"samba": "sambanova",
|
||||||
|
"nscale": "nscale",
|
||||||
|
"baseten": "baseten",
|
||||||
|
"publicai": "publicai",
|
||||||
|
"nous-research": "nous-research",
|
||||||
|
"nous": "nous-research",
|
||||||
|
"glhf": "glhf",
|
||||||
|
"bb": "blackbox",
|
||||||
|
"blackbox": "blackbox"
|
||||||
|
},
|
||||||
|
"idToAlias": {
|
||||||
|
"claude": "cc",
|
||||||
|
"gemini": "gemini",
|
||||||
|
"gemini-cli": "gc",
|
||||||
|
"codex": "cx",
|
||||||
|
"qwen": "qw",
|
||||||
|
"iflow": "if",
|
||||||
|
"qoder": "qd",
|
||||||
|
"antigravity": "ag",
|
||||||
|
"openrouter": "openrouter",
|
||||||
|
"openai": "openai",
|
||||||
|
"vercel-ai-gateway": "vercel-ai-gateway",
|
||||||
|
"glm": "glm",
|
||||||
|
"glm-cn": "glm-cn",
|
||||||
|
"kimi": "kimi",
|
||||||
|
"minimax": "minimax",
|
||||||
|
"minimax-cn": "minimax-cn",
|
||||||
|
"alicode": "alicode",
|
||||||
|
"alicode-intl": "alicode-intl",
|
||||||
|
"volcengine-ark": "volcengine-ark",
|
||||||
|
"byteplus": "byteplus",
|
||||||
|
"github": "gh",
|
||||||
|
"kiro": "kr",
|
||||||
|
"cursor": "cu",
|
||||||
|
"kimi-coding": "kmc",
|
||||||
|
"kilocode": "kc",
|
||||||
|
"cline": "cl",
|
||||||
|
"nvidia": "nvidia",
|
||||||
|
"anthropic": "anthropic",
|
||||||
|
"deepseek": "deepseek",
|
||||||
|
"commandcode": "commandcode",
|
||||||
|
"groq": "groq",
|
||||||
|
"xai": "xai",
|
||||||
|
"mistral": "mistral",
|
||||||
|
"perplexity": "perplexity",
|
||||||
|
"together": "together",
|
||||||
|
"fireworks": "fireworks",
|
||||||
|
"cerebras": "cerebras",
|
||||||
|
"cohere": "cohere",
|
||||||
|
"nebius": "nebius",
|
||||||
|
"siliconflow": "siliconflow",
|
||||||
|
"hyperbolic": "hyperbolic",
|
||||||
|
"deepgram": "deepgram",
|
||||||
|
"assemblyai": "assemblyai",
|
||||||
|
"nanobanana": "nanobanana",
|
||||||
|
"chutes": "chutes",
|
||||||
|
"ollama": "ollama",
|
||||||
|
"ollama-local": "ollama-local",
|
||||||
|
"vertex": "vertex",
|
||||||
|
"vertex-partner": "vertex-partner",
|
||||||
|
"gitlab": "gitlab",
|
||||||
|
"codebuddy": "codebuddy",
|
||||||
|
"opencode": "oc",
|
||||||
|
"opencode-go": "opencode-go",
|
||||||
|
"grok-web": "grok-web",
|
||||||
|
"perplexity-web": "perplexity-web",
|
||||||
|
"azure": "azure",
|
||||||
|
"cloudflare-ai": "cloudflare-ai",
|
||||||
|
"xiaomi-mimo": "xiaomi-mimo",
|
||||||
|
"mimo-free": "mmf",
|
||||||
|
"mmf": "mmf",
|
||||||
|
"xiaomi-tokenplan": "xiaomi-tokenplan",
|
||||||
|
"agentrouter": "agentrouter",
|
||||||
|
"aimlapi": "aimlapi",
|
||||||
|
"novita": "novita",
|
||||||
|
"modal": "modal",
|
||||||
|
"reka": "reka",
|
||||||
|
"nlpcloud": "nlpcloud",
|
||||||
|
"bazaarlink": "bazaarlink",
|
||||||
|
"completions": "completions",
|
||||||
|
"enally": "enally",
|
||||||
|
"freetheai": "freetheai",
|
||||||
|
"llm7": "llm7",
|
||||||
|
"lepton": "lepton",
|
||||||
|
"kluster": "kluster",
|
||||||
|
"ai21": "ai21",
|
||||||
|
"inference-net": "inference-net",
|
||||||
|
"predibase": "predibase",
|
||||||
|
"bytez": "bytez",
|
||||||
|
"morph": "morph",
|
||||||
|
"longcat": "longcat",
|
||||||
|
"puter": "puter",
|
||||||
|
"uncloseai": "uncloseai",
|
||||||
|
"scaleway": "scaleway",
|
||||||
|
"deepinfra": "deepinfra",
|
||||||
|
"sambanova": "sambanova",
|
||||||
|
"nscale": "nscale",
|
||||||
|
"baseten": "baseten",
|
||||||
|
"publicai": "publicai",
|
||||||
|
"nous-research": "nous-research",
|
||||||
|
"glhf": "glhf",
|
||||||
|
"blackbox": "blackbox"
|
||||||
|
},
|
||||||
|
"modelKeys": [
|
||||||
|
"ag",
|
||||||
|
"agentrouter",
|
||||||
|
"ai21",
|
||||||
|
"aimlapi",
|
||||||
|
"alicode",
|
||||||
|
"alicode-intl",
|
||||||
|
"anthropic",
|
||||||
|
"assemblyai",
|
||||||
|
"baseten",
|
||||||
|
"bazaarlink",
|
||||||
|
"black-forest-labs",
|
||||||
|
"blackbox",
|
||||||
|
"byteplus",
|
||||||
|
"bytez",
|
||||||
|
"cc",
|
||||||
|
"cerebras",
|
||||||
|
"cl",
|
||||||
|
"cloudflare-ai",
|
||||||
|
"cohere",
|
||||||
|
"comfyui",
|
||||||
|
"commandcode",
|
||||||
|
"completions",
|
||||||
|
"cu",
|
||||||
|
"cx",
|
||||||
|
"deepgram",
|
||||||
|
"deepinfra",
|
||||||
|
"deepseek",
|
||||||
|
"edge-tts",
|
||||||
|
"elevenlabs-tts-models",
|
||||||
|
"enally",
|
||||||
|
"fal-ai",
|
||||||
|
"fireworks",
|
||||||
|
"freetheai",
|
||||||
|
"gc",
|
||||||
|
"gemini",
|
||||||
|
"gemini-tts-models",
|
||||||
|
"gemini-tts-voices",
|
||||||
|
"gh",
|
||||||
|
"glhf",
|
||||||
|
"glm",
|
||||||
|
"glm-cn",
|
||||||
|
"google-tts",
|
||||||
|
"grok-web",
|
||||||
|
"groq",
|
||||||
|
"huggingface",
|
||||||
|
"hyperbolic",
|
||||||
|
"if",
|
||||||
|
"inference-net",
|
||||||
|
"kc",
|
||||||
|
"kimi",
|
||||||
|
"kluster",
|
||||||
|
"kmc",
|
||||||
|
"kr",
|
||||||
|
"lepton",
|
||||||
|
"llm7",
|
||||||
|
"local-device",
|
||||||
|
"longcat",
|
||||||
|
"minimax",
|
||||||
|
"minimax-cn",
|
||||||
|
"mistral",
|
||||||
|
"mmf",
|
||||||
|
"modal",
|
||||||
|
"morph",
|
||||||
|
"nanobanana",
|
||||||
|
"nebius",
|
||||||
|
"nlpcloud",
|
||||||
|
"nous-research",
|
||||||
|
"novita",
|
||||||
|
"nscale",
|
||||||
|
"nvidia",
|
||||||
|
"oc",
|
||||||
|
"ollama",
|
||||||
|
"openai",
|
||||||
|
"openai-tts-models",
|
||||||
|
"openai-tts-voices",
|
||||||
|
"opencode-go",
|
||||||
|
"openrouter",
|
||||||
|
"openrouter-tts-models",
|
||||||
|
"openrouter-tts-voices",
|
||||||
|
"perplexity",
|
||||||
|
"perplexity-web",
|
||||||
|
"predibase",
|
||||||
|
"publicai",
|
||||||
|
"puter",
|
||||||
|
"qd",
|
||||||
|
"qw",
|
||||||
|
"recraft",
|
||||||
|
"reka",
|
||||||
|
"runwayml",
|
||||||
|
"sambanova",
|
||||||
|
"scaleway",
|
||||||
|
"sdwebui",
|
||||||
|
"siliconflow",
|
||||||
|
"stability-ai",
|
||||||
|
"together",
|
||||||
|
"uncloseai",
|
||||||
|
"vertex",
|
||||||
|
"vertex-partner",
|
||||||
|
"volcengine-ark",
|
||||||
|
"voyage-ai",
|
||||||
|
"xai",
|
||||||
|
"xiaomi-mimo",
|
||||||
|
"xiaomi-tokenplan"
|
||||||
|
]
|
||||||
|
}
|
||||||
54
tests/__baseline__/verify-alias.mjs
Normal file
54
tests/__baseline__/verify-alias.mjs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
// Verify alias resolution is byte-for-byte stable (both directions, all sources).
|
||||||
|
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { dirname, join } from "node:path";
|
||||||
|
import { resolveProviderAlias } from "../../open-sse/services/model.js";
|
||||||
|
import { PROVIDER_ID_TO_ALIAS, PROVIDER_MODELS } from "../../open-sse/config/providerModels.js";
|
||||||
|
|
||||||
|
const here = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const snapPath = join(here, "alias-baseline.json");
|
||||||
|
|
||||||
|
// All known alias tokens to probe (collected from both maps' historical keys)
|
||||||
|
const ALIAS_TOKENS = [
|
||||||
|
"cc","cx","gc","qw","if","ag","gh","kr","cu","kc","kmc","cl","oc","ocg","qd","qoder",
|
||||||
|
"el","openai","vercel","vercel-ai-gateway","anthropic","gemini","openrouter","glm","kimi",
|
||||||
|
"minimax","minimax-cn","hf","huggingface","ds","deepseek","cmc","commandcode","groq","xai",
|
||||||
|
"mistral","pplx","perplexity","together","fireworks","cerebras","cohere","nvidia","nebius",
|
||||||
|
"siliconflow","hyp","hyperbolic","dg","deepgram","aai","assemblyai","nb","nanobanana","ch",
|
||||||
|
"chutes","ark","volcengine-ark","byteplus","bpm","cursor","vx","vertex","vxp","vertex-partner",
|
||||||
|
"gw","grok-web","pw","perplexity-web","mimo","xiaomi-mimo","xmtp","xiaomi-tokenplan","cf",
|
||||||
|
"cloudflare-ai","fal","fal-ai","stability","stability-ai","bfl","black-forest-labs","recraft",
|
||||||
|
"topaz","runway","runwayml","jina","jina-ai","polly","aws-polly","agentrouter","aimlapi","aiml",
|
||||||
|
"novita","modal","mdl","reka","nlpcloud","nlpc","bazaarlink","bzl","completions","cpl","enally",
|
||||||
|
"enly","freetheai","fta","llm7","lepton","kluster","ai21","inference-net","inet","predibase",
|
||||||
|
"bytez","morph","longcat","lc","puter","pu","uncloseai","unc","scaleway","scw","deepinfra",
|
||||||
|
"sambanova","samba","nscale","baseten","publicai","nous-research","nous","glhf","bb","blackbox",
|
||||||
|
];
|
||||||
|
|
||||||
|
const resolved = {
|
||||||
|
aliasToId: Object.fromEntries(ALIAS_TOKENS.map(a => [a, resolveProviderAlias(a)])),
|
||||||
|
idToAlias: PROVIDER_ID_TO_ALIAS,
|
||||||
|
modelKeys: Object.keys(PROVIDER_MODELS).sort(),
|
||||||
|
};
|
||||||
|
const current = JSON.parse(JSON.stringify(resolved));
|
||||||
|
|
||||||
|
if (process.argv[2] === "--snapshot") {
|
||||||
|
writeFileSync(snapPath, JSON.stringify(current, null, 2));
|
||||||
|
console.log(`Snapshot alias resolution → ${snapPath}`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
if (!existsSync(snapPath)) { console.error("No baseline. Run --snapshot first."); process.exit(1); }
|
||||||
|
const baseline = JSON.parse(readFileSync(snapPath, "utf8"));
|
||||||
|
if (JSON.stringify(baseline) === JSON.stringify(current)) {
|
||||||
|
console.log(`✅ Alias resolution byte-for-byte equal (${ALIAS_TOKENS.length} tokens).`);
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
// Diff
|
||||||
|
for (const a of ALIAS_TOKENS) {
|
||||||
|
if (baseline.aliasToId[a] !== current.aliasToId[a]) {
|
||||||
|
console.error(`~ alias ${a}: ${baseline.aliasToId[a]} -> ${current.aliasToId[a]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (JSON.stringify(baseline.idToAlias) !== JSON.stringify(current.idToAlias)) console.error("~ idToAlias changed");
|
||||||
|
if (JSON.stringify(baseline.modelKeys) !== JSON.stringify(current.modelKeys)) console.error("~ modelKeys changed");
|
||||||
|
process.exit(1);
|
||||||
Reference in New Issue
Block a user