From 7134dd71dbbb5b53c7447dacd63628939501f7cc Mon Sep 17 00:00:00 2001 From: decolua Date: Sat, 13 Jun 2026 20:48:11 +0700 Subject: [PATCH] =?UTF-8?q?refactor(open-sse):=20P2=20alias=20single-sourc?= =?UTF-8?q?e=20=E2=80=94=20derive=20OAuth=20alias=E2=86=92id=20from=20OAUT?= =?UTF-8?q?H=5FALIASES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- open-sse/config/providerModels.js | 3 +- open-sse/services/model.js | 26 +- tests/__baseline__/alias-baseline.json | 335 +++++++++++++++++++++++++ tests/__baseline__/verify-alias.mjs | 54 ++++ 4 files changed, 402 insertions(+), 16 deletions(-) create mode 100644 tests/__baseline__/alias-baseline.json create mode 100644 tests/__baseline__/verify-alias.mjs diff --git a/open-sse/config/providerModels.js b/open-sse/config/providerModels.js index 68285d36..46b92e02 100644 --- a/open-sse/config/providerModels.js +++ b/open-sse/config/providerModels.js @@ -896,7 +896,8 @@ export function getModelQuotaFamily(aliasOrId, modelId) { } // 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", codex: "cx", "gemini-cli": "gc", diff --git a/open-sse/services/model.js b/open-sse/services/model.js index d2dd3c81..6e528abb 100644 --- a/open-sse/services/model.js +++ b/open-sse/services/model.js @@ -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 = { - 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", + ...OAUTH_ALIAS_TO_ID, ocg: "opencode-go", - qd: "qoder", qoder: "qoder", // TTS providers el: "elevenlabs", diff --git a/tests/__baseline__/alias-baseline.json b/tests/__baseline__/alias-baseline.json new file mode 100644 index 00000000..f4786df7 --- /dev/null +++ b/tests/__baseline__/alias-baseline.json @@ -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" + ] +} \ No newline at end of file diff --git a/tests/__baseline__/verify-alias.mjs b/tests/__baseline__/verify-alias.mjs new file mode 100644 index 00000000..d7d7bdd9 --- /dev/null +++ b/tests/__baseline__/verify-alias.mjs @@ -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);