mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
refactor(open-sse): registry consolidation + DRY media/oauth/adhoc cleanup
- Single-source registry: oauth clientId/tokenUrl, usage URLs, image/embed configs, search defaultModel, codex fixedPort, google token url derive. - Remove 29 unused OmniRoute providers (registry 100→71); media intact. - De-adhoc: codex literals → registry format/oauth flags; reasoningInject, image/embed openrouter headers + xai bodyFields config-driven. - Add REGISTRY_TEMPLATE.js + expand PROVIDER_DEFAULTS/schema JSDoc. - Baselines updated; PROVIDERS 62 + alias 90 byte-for-byte, golden snapshots. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -88,67 +88,18 @@
|
||||
"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": {
|
||||
"agentrouter": "agentrouter",
|
||||
"ai21": "ai21",
|
||||
"aimlapi": "aimlapi",
|
||||
"alicode": "alicode",
|
||||
"alicode-intl": "alicode-intl",
|
||||
"anthropic": "anthropic",
|
||||
"antigravity": "ag",
|
||||
"assemblyai": "assemblyai",
|
||||
"azure": "azure",
|
||||
"baseten": "baseten",
|
||||
"bazaarlink": "bazaarlink",
|
||||
"blackbox": "blackbox",
|
||||
"byteplus": "byteplus",
|
||||
"bytez": "bytez",
|
||||
"cerebras": "cerebras",
|
||||
"chutes": "chutes",
|
||||
"claude": "cc",
|
||||
@@ -158,47 +109,31 @@
|
||||
"codex": "cx",
|
||||
"cohere": "cohere",
|
||||
"commandcode": "commandcode",
|
||||
"completions": "completions",
|
||||
"cursor": "cu",
|
||||
"deepgram": "deepgram",
|
||||
"deepinfra": "deepinfra",
|
||||
"deepseek": "deepseek",
|
||||
"enally": "enally",
|
||||
"fireworks": "fireworks",
|
||||
"freetheai": "freetheai",
|
||||
"gemini": "gemini",
|
||||
"gemini-cli": "gc",
|
||||
"github": "gh",
|
||||
"gitlab": "gitlab",
|
||||
"glhf": "glhf",
|
||||
"glm": "glm",
|
||||
"glm-cn": "glm-cn",
|
||||
"grok-web": "grok-web",
|
||||
"groq": "groq",
|
||||
"hyperbolic": "hyperbolic",
|
||||
"iflow": "if",
|
||||
"inference-net": "inference-net",
|
||||
"kilocode": "kc",
|
||||
"kimi": "kimi",
|
||||
"kimi-coding": "kmc",
|
||||
"kiro": "kr",
|
||||
"kluster": "kluster",
|
||||
"lepton": "lepton",
|
||||
"llm7": "llm7",
|
||||
"longcat": "longcat",
|
||||
"mimo-free": "mmf",
|
||||
"minimax": "minimax",
|
||||
"minimax-cn": "minimax-cn",
|
||||
"mistral": "mistral",
|
||||
"mmf": "mmf",
|
||||
"modal": "modal",
|
||||
"morph": "morph",
|
||||
"nanobanana": "nanobanana",
|
||||
"nebius": "nebius",
|
||||
"nlpcloud": "nlpcloud",
|
||||
"nous-research": "nous-research",
|
||||
"novita": "novita",
|
||||
"nscale": "nscale",
|
||||
"nvidia": "nvidia",
|
||||
"ollama": "ollama",
|
||||
"ollama-local": "ollama-local",
|
||||
@@ -208,17 +143,10 @@
|
||||
"openrouter": "openrouter",
|
||||
"perplexity": "perplexity",
|
||||
"perplexity-web": "perplexity-web",
|
||||
"predibase": "predibase",
|
||||
"publicai": "publicai",
|
||||
"puter": "puter",
|
||||
"qoder": "qd",
|
||||
"qwen": "qw",
|
||||
"reka": "reka",
|
||||
"sambanova": "sambanova",
|
||||
"scaleway": "scaleway",
|
||||
"siliconflow": "siliconflow",
|
||||
"together": "together",
|
||||
"uncloseai": "uncloseai",
|
||||
"vercel-ai-gateway": "vercel-ai-gateway",
|
||||
"vertex": "vertex",
|
||||
"vertex-partner": "vertex-partner",
|
||||
@@ -229,19 +157,13 @@
|
||||
},
|
||||
"modelKeys": [
|
||||
"ag",
|
||||
"agentrouter",
|
||||
"ai21",
|
||||
"aimlapi",
|
||||
"alicode",
|
||||
"alicode-intl",
|
||||
"anthropic",
|
||||
"assemblyai",
|
||||
"baseten",
|
||||
"bazaarlink",
|
||||
"black-forest-labs",
|
||||
"blackbox",
|
||||
"byteplus",
|
||||
"bytez",
|
||||
"cc",
|
||||
"cerebras",
|
||||
"cl",
|
||||
@@ -249,24 +171,19 @@
|
||||
"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",
|
||||
@@ -275,28 +192,17 @@
|
||||
"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",
|
||||
@@ -309,21 +215,14 @@
|
||||
"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",
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -395,142 +395,8 @@
|
||||
"baseUrl": "https://token-plan-sgp.xiaomimimo.com/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"agentrouter": {
|
||||
"baseUrl": "https://agentrouter.org/v1/messages",
|
||||
"format": "claude",
|
||||
"headers": {
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Anthropic-Beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24,structured-outputs-2025-12-15,fast-mode-2026-02-01,redact-thinking-2026-02-12,token-efficient-tools-2026-03-28",
|
||||
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
||||
"User-Agent": "claude-cli/2.1.92 (external, sdk-cli)",
|
||||
"X-App": "cli",
|
||||
"X-Stainless-Helper-Method": "stream",
|
||||
"X-Stainless-Retry-Count": "0",
|
||||
"X-Stainless-Runtime-Version": "v24.14.0",
|
||||
"X-Stainless-Package-Version": "0.80.0",
|
||||
"X-Stainless-Runtime": "node",
|
||||
"X-Stainless-Lang": "js",
|
||||
"X-Stainless-Arch": "arm64",
|
||||
"X-Stainless-Os": "MacOS",
|
||||
"X-Stainless-Timeout": "600"
|
||||
}
|
||||
},
|
||||
"aimlapi": {
|
||||
"baseUrl": "https://api.aimlapi.com/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"novita": {
|
||||
"baseUrl": "https://api.novita.ai/v3/openai/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"modal": {
|
||||
"baseUrl": "https://api.modal.com/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"reka": {
|
||||
"baseUrl": "https://api.reka.ai/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"nlpcloud": {
|
||||
"baseUrl": "https://api.nlpcloud.io/v1/gpu/chatbot",
|
||||
"format": "openai"
|
||||
},
|
||||
"bazaarlink": {
|
||||
"baseUrl": "https://bazaarlink.ai/api/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"completions": {
|
||||
"baseUrl": "https://completions.me/api/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"enally": {
|
||||
"baseUrl": "https://ai.enally.in/v1/chat/completions",
|
||||
"format": "openai",
|
||||
"authHeader": "x-api-key"
|
||||
},
|
||||
"freetheai": {
|
||||
"baseUrl": "https://api.freetheai.xyz/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"llm7": {
|
||||
"baseUrl": "https://api.llm7.io/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"lepton": {
|
||||
"baseUrl": "https://api.lepton.ai/api/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"kluster": {
|
||||
"baseUrl": "https://api.kluster.ai/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"ai21": {
|
||||
"baseUrl": "https://api.ai21.com/studio/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"inference-net": {
|
||||
"baseUrl": "https://api.inference.net/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"predibase": {
|
||||
"baseUrl": "https://serving.app.predibase.com/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"bytez": {
|
||||
"baseUrl": "https://api.bytez.com/models/v2",
|
||||
"format": "openai"
|
||||
},
|
||||
"morph": {
|
||||
"baseUrl": "https://api.morphllm.com/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"longcat": {
|
||||
"baseUrl": "https://api.longcat.chat/openai/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"puter": {
|
||||
"baseUrl": "https://api.puter.com/puterai/openai/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"uncloseai": {
|
||||
"baseUrl": "https://hermes.ai.unturf.com/v1/chat/completions",
|
||||
"format": "openai",
|
||||
"noAuth": true
|
||||
},
|
||||
"scaleway": {
|
||||
"baseUrl": "https://api.scaleway.ai/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"deepinfra": {
|
||||
"baseUrl": "https://api.deepinfra.com/v1/openai/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"sambanova": {
|
||||
"baseUrl": "https://api.sambanova.ai/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"nscale": {
|
||||
"baseUrl": "https://inference.api.nscale.com/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"baseten": {
|
||||
"baseUrl": "https://inference.baseten.co/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"publicai": {
|
||||
"baseUrl": "https://api.publicai.co/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"nous-research": {
|
||||
"baseUrl": "https://inference-api.nousresearch.com/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"glhf": {
|
||||
"baseUrl": "https://glhf.chat/api/openai/v1/chat/completions",
|
||||
"format": "openai"
|
||||
},
|
||||
"blackbox": {
|
||||
"baseUrl": "https://api.blackbox.ai/chat/completions",
|
||||
"format": "openai"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,7 @@ const ALIAS_TOKENS = [
|
||||
"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",
|
||||
"topaz","runway","runwayml","jina","jina-ai","polly","aws-polly","bb","blackbox",
|
||||
];
|
||||
|
||||
// Sort idToAlias by key — runtime accesses by key, order is irrelevant (content-based)
|
||||
|
||||
@@ -9,12 +9,15 @@ const here = dirname(fileURLToPath(import.meta.url));
|
||||
const baseline = JSON.parse(readFileSync(join(here, "providers-baseline.json"), "utf8"));
|
||||
|
||||
// Fields intentionally added during refactor (verified by dedicated runtime tests, not byte-baseline).
|
||||
const ADDED_FIELDS = new Set(["forceStream", "urlSuffix"]);
|
||||
// authUrl: removed dead field (qwen/iflow) — no consumer reads config.authUrl (oauth block has authorize/deviceCode)
|
||||
const ADDED_FIELDS = new Set(["forceStream", "urlSuffix", "retry", "quirks", "auth", "validateUrl", "usage", "clientId", "clientSecret", "tokenUrl", "cliVersion", "apiClient", "copilot", "authorizeUrl", "authUrl", "regions", "defaultRegion", "reasoningInject"]);
|
||||
|
||||
// Normalize via JSON roundtrip so function/undefined are dropped identically; drop added fields.
|
||||
// Normalize via JSON roundtrip so function/undefined are dropped identically; drop added/removed fields.
|
||||
// ADDED_FIELDS are verified by dedicated runtime tests, so drop them from BOTH sides (added or intentionally removed).
|
||||
const current = JSON.parse(JSON.stringify(PROVIDERS));
|
||||
for (const id of Object.keys(current)) {
|
||||
for (const f of ADDED_FIELDS) delete current[id][f];
|
||||
for (const f of ADDED_FIELDS) {
|
||||
for (const id of Object.keys(current)) delete current[id][f];
|
||||
for (const id of Object.keys(baseline)) delete baseline[id][f];
|
||||
}
|
||||
|
||||
const diffs = [];
|
||||
|
||||
@@ -1,107 +1,5 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > agentrouter → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Anthropic-Beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24,structured-outputs-2025-12-15,fast-mode-2026-02-01,redact-thinking-2026-02-12,token-efficient-tools-2026-03-28",
|
||||
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "claude-cli/2.1.92 (external, sdk-cli)",
|
||||
"X-App": "cli",
|
||||
"X-Stainless-Arch": "arm64",
|
||||
"X-Stainless-Helper-Method": "stream",
|
||||
"X-Stainless-Lang": "js",
|
||||
"X-Stainless-Os": "MacOS",
|
||||
"X-Stainless-Package-Version": "0.80.0",
|
||||
"X-Stainless-Retry-Count": "0",
|
||||
"X-Stainless-Runtime": "node",
|
||||
"X-Stainless-Runtime-Version": "v24.14.0",
|
||||
"X-Stainless-Timeout": "600",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"x-api-key": "<CRED>",
|
||||
},
|
||||
"nonStream": {
|
||||
"Anthropic-Beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24,structured-outputs-2025-12-15,fast-mode-2026-02-01,redact-thinking-2026-02-12,token-efficient-tools-2026-03-28",
|
||||
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "claude-cli/2.1.92 (external, sdk-cli)",
|
||||
"X-App": "cli",
|
||||
"X-Stainless-Arch": "arm64",
|
||||
"X-Stainless-Helper-Method": "stream",
|
||||
"X-Stainless-Lang": "js",
|
||||
"X-Stainless-Os": "MacOS",
|
||||
"X-Stainless-Package-Version": "0.80.0",
|
||||
"X-Stainless-Retry-Count": "0",
|
||||
"X-Stainless-Runtime": "node",
|
||||
"X-Stainless-Runtime-Version": "v24.14.0",
|
||||
"X-Stainless-Timeout": "600",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"x-api-key": "<CRED>",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Anthropic-Beta": "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,context-management-2025-06-27,prompt-caching-scope-2026-01-05,advanced-tool-use-2025-11-20,effort-2025-11-24,structured-outputs-2025-12-15,fast-mode-2026-02-01,redact-thinking-2026-02-12,token-efficient-tools-2026-03-28",
|
||||
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "claude-cli/2.1.92 (external, sdk-cli)",
|
||||
"X-App": "cli",
|
||||
"X-Stainless-Arch": "arm64",
|
||||
"X-Stainless-Helper-Method": "stream",
|
||||
"X-Stainless-Lang": "js",
|
||||
"X-Stainless-Os": "MacOS",
|
||||
"X-Stainless-Package-Version": "0.80.0",
|
||||
"X-Stainless-Retry-Count": "0",
|
||||
"X-Stainless-Runtime": "node",
|
||||
"X-Stainless-Runtime-Version": "v24.14.0",
|
||||
"X-Stainless-Timeout": "600",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"x-api-key": "<CRED>",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > ai21 → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > aimlapi → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > alicode → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -187,44 +85,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > assemblyai → heade
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > baseten → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > bazaarlink → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > blackbox → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -263,25 +123,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > byteplus → headers
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > bytez → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > cerebras → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -484,25 +325,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > cohere → headers (
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > completions → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > deepgram → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -522,25 +344,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > deepgram → headers
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > deepinfra → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > deepseek → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -560,25 +363,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > deepseek → headers
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > enally → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > fireworks → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -598,25 +382,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > fireworks → header
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > freetheai → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > gemini → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -655,25 +420,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > gitlab → headers (
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > glhf → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > glm → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -756,25 +502,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > hyperbolic → heade
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > inference-net → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > kilocode → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -856,82 +583,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > kimi-coding → head
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > kluster → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > lepton → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > llm7 → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > longcat → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > minimax → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -1020,44 +671,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > mmf → headers (api
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > modal → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > morph → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > nanobanana → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -1096,82 +709,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > nebius → headers (
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > nlpcloud → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > nous-research → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > novita → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > nscale → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > nvidia → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -1273,120 +810,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > perplexity → heade
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > predibase → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > publicai → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > puter → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > reka → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > sambanova → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > scaleway → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > siliconflow → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -1425,25 +848,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > together → headers
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > uncloseai → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"nonStream": {
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
"oauth": {
|
||||
"Accept": "text/event-stream",
|
||||
"Authorization": "Bearer <TOK>",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildHeaders (default executor providers) > vercel-ai-gateway → headers (apiKey / oauth) 1`] = `
|
||||
{
|
||||
"apiKey": {
|
||||
@@ -1520,27 +924,6 @@ exports[`GOLDEN buildHeaders (default executor providers) > xiaomi-mimo → head
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > agentrouter → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://agentrouter.org/v1/messages",
|
||||
"stream": "https://agentrouter.org/v1/messages",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > ai21 → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.ai21.com/studio/v1/chat/completions",
|
||||
"stream": "https://api.ai21.com/studio/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > aimlapi → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.aimlapi.com/v1/chat/completions",
|
||||
"stream": "https://api.aimlapi.com/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > alicode → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://coding.dashscope.aliyuncs.com/v1/chat/completions",
|
||||
@@ -1569,20 +952,6 @@ exports[`GOLDEN buildUrl (default executor providers) > assemblyai → url (stre
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > baseten → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://inference.baseten.co/v1/chat/completions",
|
||||
"stream": "https://inference.baseten.co/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > bazaarlink → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://bazaarlink.ai/api/v1/chat/completions",
|
||||
"stream": "https://bazaarlink.ai/api/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > blackbox → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.blackbox.ai/chat/completions",
|
||||
@@ -1597,13 +966,6 @@ exports[`GOLDEN buildUrl (default executor providers) > byteplus → url (stream
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > bytez → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.bytez.com/models/v2",
|
||||
"stream": "https://api.bytez.com/models/v2",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > cerebras → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.cerebras.ai/v1/chat/completions",
|
||||
@@ -1653,13 +1015,6 @@ exports[`GOLDEN buildUrl (default executor providers) > cohere → url (stream +
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > completions → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://completions.me/api/v1/chat/completions",
|
||||
"stream": "https://completions.me/api/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > deepgram → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.deepgram.com/v1/listen",
|
||||
@@ -1667,13 +1022,6 @@ exports[`GOLDEN buildUrl (default executor providers) > deepgram → url (stream
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > deepinfra → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.deepinfra.com/v1/openai/chat/completions",
|
||||
"stream": "https://api.deepinfra.com/v1/openai/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > deepseek → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.deepseek.com/chat/completions",
|
||||
@@ -1681,13 +1029,6 @@ exports[`GOLDEN buildUrl (default executor providers) > deepseek → url (stream
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > enally → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://ai.enally.in/v1/chat/completions",
|
||||
"stream": "https://ai.enally.in/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > fireworks → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.fireworks.ai/inference/v1/chat/completions",
|
||||
@@ -1695,13 +1036,6 @@ exports[`GOLDEN buildUrl (default executor providers) > fireworks → url (strea
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > freetheai → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.freetheai.xyz/v1/chat/completions",
|
||||
"stream": "https://api.freetheai.xyz/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > gemini → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://generativelanguage.googleapis.com/v1beta/models/test-model:generateContent",
|
||||
@@ -1716,13 +1050,6 @@ exports[`GOLDEN buildUrl (default executor providers) > gitlab → url (stream +
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > glhf → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://glhf.chat/api/openai/v1/chat/completions",
|
||||
"stream": "https://glhf.chat/api/openai/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > glm → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.z.ai/api/anthropic/v1/messages?beta=true",
|
||||
@@ -1751,13 +1078,6 @@ exports[`GOLDEN buildUrl (default executor providers) > hyperbolic → url (stre
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > inference-net → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.inference.net/v1/chat/completions",
|
||||
"stream": "https://api.inference.net/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > kilocode → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.kilo.ai/api/openrouter/chat/completions",
|
||||
@@ -1779,34 +1099,6 @@ exports[`GOLDEN buildUrl (default executor providers) > kimi-coding → url (str
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > kluster → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.kluster.ai/v1/chat/completions",
|
||||
"stream": "https://api.kluster.ai/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > lepton → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.lepton.ai/api/v1/chat/completions",
|
||||
"stream": "https://api.lepton.ai/api/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > llm7 → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.llm7.io/v1/chat/completions",
|
||||
"stream": "https://api.llm7.io/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > longcat → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.longcat.chat/openai/v1/chat/completions",
|
||||
"stream": "https://api.longcat.chat/openai/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > minimax → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.minimax.io/anthropic/v1/messages?beta=true",
|
||||
@@ -1835,20 +1127,6 @@ exports[`GOLDEN buildUrl (default executor providers) > mmf → url (stream + no
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > modal → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.modal.com/v1/chat/completions",
|
||||
"stream": "https://api.modal.com/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > morph → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.morphllm.com/v1/chat/completions",
|
||||
"stream": "https://api.morphllm.com/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > nanobanana → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.nanobananaapi.ai/v1/chat/completions",
|
||||
@@ -1863,34 +1141,6 @@ exports[`GOLDEN buildUrl (default executor providers) > nebius → url (stream +
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > nlpcloud → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.nlpcloud.io/v1/gpu/chatbot",
|
||||
"stream": "https://api.nlpcloud.io/v1/gpu/chatbot",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > nous-research → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://inference-api.nousresearch.com/v1/chat/completions",
|
||||
"stream": "https://inference-api.nousresearch.com/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > novita → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.novita.ai/v3/openai/chat/completions",
|
||||
"stream": "https://api.novita.ai/v3/openai/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > nscale → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://inference.api.nscale.com/v1/chat/completions",
|
||||
"stream": "https://inference.api.nscale.com/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > nvidia → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://integrate.api.nvidia.com/v1/chat/completions",
|
||||
@@ -1926,48 +1176,6 @@ exports[`GOLDEN buildUrl (default executor providers) > perplexity → url (stre
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > predibase → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://serving.app.predibase.com/v1/chat/completions",
|
||||
"stream": "https://serving.app.predibase.com/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > publicai → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.publicai.co/v1/chat/completions",
|
||||
"stream": "https://api.publicai.co/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > puter → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.puter.com/puterai/openai/v1/chat/completions",
|
||||
"stream": "https://api.puter.com/puterai/openai/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > reka → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.reka.ai/v1/chat/completions",
|
||||
"stream": "https://api.reka.ai/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > sambanova → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.sambanova.ai/v1/chat/completions",
|
||||
"stream": "https://api.sambanova.ai/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > scaleway → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.scaleway.ai/v1/chat/completions",
|
||||
"stream": "https://api.scaleway.ai/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > siliconflow → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://api.siliconflow.com/v1/chat/completions",
|
||||
@@ -1982,13 +1190,6 @@ exports[`GOLDEN buildUrl (default executor providers) > together → url (stream
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > uncloseai → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://hermes.ai.unturf.com/v1/chat/completions",
|
||||
"stream": "https://hermes.ai.unturf.com/v1/chat/completions",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`GOLDEN buildUrl (default executor providers) > vercel-ai-gateway → url (stream + non-stream) 1`] = `
|
||||
{
|
||||
"nonStream": "https://ai-gateway.vercel.sh/v1/chat/completions",
|
||||
|
||||
@@ -31,8 +31,7 @@ describe("antigravity oauth client (deduped)", () => {
|
||||
expect(gc.transport.clientSecret).toBe(GOOGLE.clientSecret);
|
||||
});
|
||||
|
||||
// Vitest can't resolve the `@/` alias used inside oauth.js (pre-existing infra gap),
|
||||
// so guard the refactor textually: it must spread the shared client + keep other fields.
|
||||
// Guard: oauth.js must spread shared clients + derive from registry (PROVIDER_OAUTH).
|
||||
it("src oauth.js imports shared client + keeps full shape", async () => {
|
||||
const { readFileSync } = await import("node:fs");
|
||||
const { fileURLToPath } = await import("node:url");
|
||||
@@ -42,7 +41,9 @@ describe("antigravity oauth client (deduped)", () => {
|
||||
expect(src).toContain('import { ANTIGRAVITY_OAUTH_CLIENT, GOOGLE_OAUTH_CLIENT } from "open-sse/providers/shared.js"');
|
||||
expect(src).toContain("...ANTIGRAVITY_OAUTH_CLIENT");
|
||||
expect(src).toContain("...GOOGLE_OAUTH_CLIENT");
|
||||
expect(src).toContain('authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth"');
|
||||
// authorizeUrl now lives in registry; oauth.js derives via PROVIDER_OAUTH spread
|
||||
expect(src).toContain('PROVIDER_OAUTH["antigravity"]');
|
||||
expect(src).toContain('PROVIDER_OAUTH["gemini-cli"]');
|
||||
expect(src).not.toContain(EXPECTED.clientSecret); // antigravity secret no longer hardcoded here
|
||||
expect(src).not.toContain(GOOGLE.clientSecret); // gemini secret no longer hardcoded here
|
||||
});
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// Guards D3: antigravity 429/503 retry merged into base via computeRetryDelay hook.
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { AntigravityExecutor } from "../../open-sse/executors/antigravity.js";
|
||||
|
||||
const MAX = 10000;
|
||||
function res(status, headers = {}, body = null) {
|
||||
return {
|
||||
status,
|
||||
headers: { get: (k) => headers[k.toLowerCase()] ?? null },
|
||||
clone: () => ({ text: async () => (body == null ? "" : JSON.stringify(body)) }),
|
||||
};
|
||||
}
|
||||
|
||||
describe("antigravity computeRetryDelay hook (D3)", () => {
|
||||
const ag = new AntigravityExecutor();
|
||||
|
||||
it("uses Retry-After header (seconds → ms) when within cap", async () => {
|
||||
expect(await ag.computeRetryDelay(res(429, { "retry-after": "5" }), 1)).toBe(5000);
|
||||
});
|
||||
|
||||
it("vetoes (false) when Retry-After exceeds cap", async () => {
|
||||
expect(await ag.computeRetryDelay(res(429, { "retry-after": "60" }), 1)).toBe(false);
|
||||
});
|
||||
|
||||
it("parses retry time from error body when no header", async () => {
|
||||
const r = res(429, {}, { error: { message: "quota will reset after 3s" } });
|
||||
expect(await ag.computeRetryDelay(r, 1)).toBe(3000);
|
||||
});
|
||||
|
||||
it("exponential backoff for 429 when no retry info", async () => {
|
||||
expect(await ag.computeRetryDelay(res(429), 1)).toBe(Math.min(1000 * 2 ** 1, MAX));
|
||||
expect(await ag.computeRetryDelay(res(429), 3)).toBe(Math.min(1000 * 2 ** 3, MAX));
|
||||
});
|
||||
|
||||
it("503 without retry info → veto (no auto backoff)", async () => {
|
||||
expect(await ag.computeRetryDelay(res(503), 1)).toBe(false);
|
||||
});
|
||||
|
||||
it("buildHeaders includes cached session id after transformRequest", () => {
|
||||
ag._lastSessionId = "sess-123";
|
||||
const h = ag.buildHeaders({ accessToken: "tok" }, true);
|
||||
expect(h["X-Machine-Session-Id"]).toBe("sess-123");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,97 @@
|
||||
// Locks BaseExecutor.execute retry/fallback behavior (docs 04 GAP #1, docs 11 §7).
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
|
||||
// Mock the network layer so we can script upstream responses.
|
||||
const fetchMock = vi.fn();
|
||||
vi.mock("../../open-sse/utils/proxyFetch.js", () => ({
|
||||
proxyAwareFetch: (...args) => fetchMock(...args),
|
||||
}));
|
||||
|
||||
const { BaseExecutor } = await import("../../open-sse/executors/base.js");
|
||||
|
||||
function res(status) {
|
||||
return { status, headers: { get: () => "" } };
|
||||
}
|
||||
|
||||
function makeExec(config) {
|
||||
const ex = new BaseExecutor("test", config);
|
||||
// make headers trivial; credentials empty
|
||||
return ex;
|
||||
}
|
||||
|
||||
const creds = { apiKey: "k" };
|
||||
|
||||
beforeEach(() => fetchMock.mockReset());
|
||||
|
||||
describe("BaseExecutor.execute — retry by status (config-driven)", () => {
|
||||
it("retries 502 `attempts` times then succeeds", async () => {
|
||||
const ex = makeExec({ baseUrl: "https://x/api", retry: { 502: { attempts: 3, delayMs: 0 } } });
|
||||
fetchMock
|
||||
.mockResolvedValueOnce(res(502))
|
||||
.mockResolvedValueOnce(res(502))
|
||||
.mockResolvedValueOnce(res(200));
|
||||
const out = await ex.execute({ model: "m", body: {}, stream: false, credentials: creds });
|
||||
expect(out.response.status).toBe(200);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it("stops after exhausting 502 attempts on a single url and throws", async () => {
|
||||
const ex = makeExec({ baseUrl: "https://x/api", retry: { 502: { attempts: 2, delayMs: 0 } } });
|
||||
fetchMock.mockResolvedValue(res(502));
|
||||
// single url: 1 initial + 2 retries = 3 calls, then returns the 502 response (no fallback url)
|
||||
const out = await ex.execute({ model: "m", body: {}, stream: false, credentials: creds });
|
||||
expect(out.response.status).toBe(502);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe("BaseExecutor.execute — baseUrls fallback", () => {
|
||||
it("falls over to the next url on 429 (shouldRetry)", async () => {
|
||||
const ex = makeExec({ baseUrls: ["https://a/api", "https://b/api"], retry: { 429: { attempts: 0 } } });
|
||||
fetchMock
|
||||
.mockResolvedValueOnce(res(429)) // url[0] → fallback
|
||||
.mockResolvedValueOnce(res(200)); // url[1] ok
|
||||
const out = await ex.execute({ model: "m", body: {}, stream: false, credentials: creds });
|
||||
expect(out.response.status).toBe(200);
|
||||
expect(out.url).toBe("https://b/api");
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("BaseExecutor.execute — network error retry/fallback", () => {
|
||||
it("maps network exception to 502 retry config", async () => {
|
||||
const ex = makeExec({ baseUrl: "https://x/api", retry: { 502: { attempts: 1, delayMs: 0 } } });
|
||||
fetchMock
|
||||
.mockImplementationOnce(async () => { throw new Error("ECONNRESET"); })
|
||||
.mockResolvedValueOnce(res(200));
|
||||
const out = await ex.execute({ model: "m", body: {}, stream: false, credentials: creds });
|
||||
expect(out.response.status).toBe(200);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("throws when the only url fails with network error and no retries left", async () => {
|
||||
const ex = makeExec({ baseUrl: "https://x/api", retry: { 502: { attempts: 0 } } });
|
||||
// mockImplementationOnce (not persistent) avoids vitest flagging a reused rejection.
|
||||
fetchMock.mockImplementationOnce(async () => { throw new Error("boom"); });
|
||||
let thrown = null;
|
||||
try {
|
||||
await ex.execute({ model: "m", body: {}, stream: false, credentials: creds });
|
||||
} catch (e) {
|
||||
thrown = e;
|
||||
}
|
||||
expect(thrown?.message).toBe("boom");
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("BaseExecutor.execute — computeRetryDelay hook veto", () => {
|
||||
it("hook returning false skips retry (uses fallback path)", async () => {
|
||||
const ex = makeExec({ baseUrl: "https://x/api", retry: { 429: { attempts: 5, delayMs: 0 } } });
|
||||
ex.computeRetryDelay = vi.fn().mockResolvedValue(false);
|
||||
fetchMock.mockResolvedValueOnce(res(429));
|
||||
const out = await ex.execute({ model: "m", body: {}, stream: false, credentials: creds });
|
||||
// hook vetoes retry → no fallback url → returns the 429 response as-is
|
||||
expect(out.response.status).toBe(429);
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
// Guards C2: regex name fallback (no catalog). Terse entries derive name; existing names untouched.
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { deriveModelName } from "../../open-sse/providers/models/namePatterns.js";
|
||||
import { normalizeModel } from "../../open-sse/providers/models/schema.js";
|
||||
|
||||
describe("model name regex fallback (C2)", () => {
|
||||
it("derives display name from id per family", () => {
|
||||
expect(deriveModelName("kimi-k2.5")).toBe("Kimi K2.5");
|
||||
expect(deriveModelName("glm-4.6v")).toBe("GLM 4.6V (Vision)");
|
||||
expect(deriveModelName("minimax-m2.7")).toBe("MiniMax M2.7");
|
||||
expect(deriveModelName("gpt-5.4-mini")).toBe("GPT 5.4 Mini");
|
||||
expect(deriveModelName("grok-4")).toBe("Grok 4");
|
||||
});
|
||||
|
||||
it("falls back to id verbatim when no pattern matches", () => {
|
||||
expect(deriveModelName("some-unknown-model")).toBe("some-unknown-model");
|
||||
});
|
||||
|
||||
it("normalizeModel: explicit name always wins over regex", () => {
|
||||
expect(normalizeModel({ id: "kimi-k2.5", name: "Custom" }).name).toBe("Custom");
|
||||
});
|
||||
|
||||
it("normalizeModel: terse string id becomes object with derived name", () => {
|
||||
const m = normalizeModel("glm-5");
|
||||
expect(m.id).toBe("glm-5");
|
||||
expect(m.name).toBe("GLM 5");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,58 @@
|
||||
// Locks multimodal quirks flagged in docs 11 §4: image_url.detail drop + input_audio per-format.
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { openaiToClaudeRequest } from "../../open-sse/translator/request/openai-to-claude.js";
|
||||
import { convertOpenAIContentToParts } from "../../open-sse/translator/helpers/geminiHelper.js";
|
||||
|
||||
function userImage(detail) {
|
||||
return {
|
||||
model: "claude-sonnet-4-6",
|
||||
messages: [{
|
||||
role: "user",
|
||||
content: [
|
||||
{ type: "text", text: "look" },
|
||||
{ type: "image_url", image_url: { url: "data:image/png;base64,AAAB", detail } },
|
||||
],
|
||||
}],
|
||||
};
|
||||
}
|
||||
|
||||
describe("openai→claude: image_url.detail is dropped (docs 11 §4)", () => {
|
||||
it("converts image to base64 source WITHOUT a detail field", () => {
|
||||
const out = openaiToClaudeRequest("claude-sonnet-4-6", userImage("high"), false);
|
||||
const imgBlock = out.messages[0].content.find((b) => b.type === "image");
|
||||
expect(imgBlock).toBeTruthy();
|
||||
expect(imgBlock.source).toEqual({ type: "base64", media_type: "image/png", data: "AAAB" });
|
||||
expect("detail" in imgBlock).toBe(false);
|
||||
expect("detail" in imgBlock.source).toBe(false);
|
||||
});
|
||||
|
||||
it("drops input_audio entirely (claude has no audio block)", () => {
|
||||
const body = {
|
||||
model: "claude-sonnet-4-6",
|
||||
messages: [{ role: "user", content: [
|
||||
{ type: "text", text: "hi" },
|
||||
{ type: "input_audio", input_audio: { data: "ZZZ", format: "wav" } },
|
||||
] }],
|
||||
};
|
||||
const out = openaiToClaudeRequest("claude-sonnet-4-6", body, false);
|
||||
const blocks = out.messages[0].content;
|
||||
expect(blocks.some((b) => b.type === "audio" || b.type === "input_audio")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("openai→gemini: input_audio is mapped to inlineData (docs 11 §4)", () => {
|
||||
it("maps wav → audio/wav inlineData", () => {
|
||||
const parts = convertOpenAIContentToParts([{ type: "input_audio", input_audio: { data: "ZZZ", format: "wav" } }]);
|
||||
expect(parts).toEqual([{ inlineData: { mime_type: "audio/wav", data: "ZZZ" } }]);
|
||||
});
|
||||
|
||||
it("maps mp3 → audio/mpeg inlineData", () => {
|
||||
const parts = convertOpenAIContentToParts([{ type: "input_audio", input_audio: { data: "ZZZ", format: "mp3" } }]);
|
||||
expect(parts[0].inlineData.mime_type).toBe("audio/mpeg");
|
||||
});
|
||||
|
||||
it("drops image_url.detail (not carried into inlineData)", () => {
|
||||
const parts = convertOpenAIContentToParts([{ type: "image_url", image_url: { url: "data:image/png;base64,AAAB", detail: "high" } }]);
|
||||
expect(parts).toEqual([{ inlineData: { mime_type: "image/png", data: "AAAB" } }]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
// Guards E1: display fields live in providersDisplay.js, merged back into AI_PROVIDERS (shape unchanged).
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
const DISPLAY_FIELDS = ["name", "icon", "color"];
|
||||
|
||||
describe("provider display split (E1)", () => {
|
||||
it("AI_PROVIDERS entries still carry merged display + transport", async () => {
|
||||
const { AI_PROVIDERS } = await import("../../src/shared/constants/providers.js");
|
||||
const kiro = AI_PROVIDERS.kiro;
|
||||
// display merged
|
||||
expect(kiro.name).toBe("Kiro AI");
|
||||
expect(kiro.icon).toBe("psychology_alt");
|
||||
// transport kept
|
||||
expect(kiro.id).toBe("kiro");
|
||||
expect(kiro.alias).toBe("kr");
|
||||
// transport-heavy provider keeps its config
|
||||
expect(AI_PROVIDERS.gemini.serviceKinds).toContain("tts");
|
||||
expect(AI_PROVIDERS.gemini.ttsConfig).toBeTruthy();
|
||||
});
|
||||
|
||||
it("display fields source from providersDisplay.js", async () => {
|
||||
const { PROVIDER_DISPLAY } = await import("../../src/shared/constants/providersDisplay.js");
|
||||
const { AI_PROVIDERS } = await import("../../src/shared/constants/providers.js");
|
||||
for (const f of DISPLAY_FIELDS) {
|
||||
expect(PROVIDER_DISPLAY.kiro[f]).toBe(AI_PROVIDERS.kiro[f]);
|
||||
}
|
||||
});
|
||||
|
||||
it("helpers still work after split", async () => {
|
||||
const m = await import("../../src/shared/constants/providers.js");
|
||||
expect(m.ALIAS_TO_ID.kr).toBe("kiro");
|
||||
expect(m.getProvidersByKind("tts").length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
// Locks edge cases flagged in docs 11 §1/§4 that were only covered indirectly.
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { normalizeClaudePassthrough } from "../../open-sse/translator/helpers/claudeHelper.js";
|
||||
import { parseDataUri, encodeDataUri } from "../../open-sse/translator/helpers/imageHelper.js";
|
||||
|
||||
describe("normalizeClaudePassthrough — haiku adaptive thinking (docs 11 §1)", () => {
|
||||
it("downgrades adaptive thinking to enabled+budget for haiku models", () => {
|
||||
const out = normalizeClaudePassthrough({ thinking: { type: "adaptive" } }, "claude-haiku-4-5");
|
||||
expect(out.thinking).toEqual({ type: "enabled", budget_tokens: 10000 });
|
||||
});
|
||||
|
||||
it("keeps adaptive thinking for sonnet/opus", () => {
|
||||
const out = normalizeClaudePassthrough({ thinking: { type: "adaptive" } }, "claude-sonnet-4-6");
|
||||
expect(out.thinking).toEqual({ type: "adaptive" });
|
||||
});
|
||||
|
||||
it("hoists mid-conversation system messages into top-level system", () => {
|
||||
const out = normalizeClaudePassthrough({
|
||||
messages: [
|
||||
{ role: "user", content: "hi" },
|
||||
{ role: "system", content: "be brief" },
|
||||
],
|
||||
});
|
||||
expect(out.system).toEqual([{ type: "text", text: "be brief" }]);
|
||||
expect(out.messages.every((m) => m.role !== "system")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseDataUri / encodeDataUri (docs 11 §4)", () => {
|
||||
it("parses a base64 data uri", () => {
|
||||
expect(parseDataUri("data:image/png;base64,AAAB")).toEqual({ mimeType: "image/png", base64: "AAAB" });
|
||||
});
|
||||
|
||||
it("tolerates newlines inside base64 payload", () => {
|
||||
expect(parseDataUri("data:image/jpeg;base64,AA\nBB")?.base64).toBe("AA\nBB");
|
||||
});
|
||||
|
||||
it("returns null for http urls and non-strings", () => {
|
||||
expect(parseDataUri("https://x/y.png")).toBeNull();
|
||||
expect(parseDataUri(null)).toBeNull();
|
||||
});
|
||||
|
||||
it("encode/parse roundtrip", () => {
|
||||
const uri = encodeDataUri("image/webp", "ZZZ");
|
||||
expect(parseDataUri(uri)).toEqual({ mimeType: "image/webp", base64: "ZZZ" });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user