refactor(open-sse): translator DRY + schema enums, bug fixes, dead code cleanup
- Bug B1-B7: media UI m.kind||m.type, serviceKinds, gemini mediaPriority, schema kind, models/info lookup by kind - Dead code D1-D6: safeParseJSON, drop PROVIDER_ENDPOINTS, orphan fetcher, GITHUB_CONFIG derive, getProviderConfig internal, legacy kiro file - Translator concerns: toOpenAIUsage, toOpenAIFinish (gemini/kiro/ollama + fix kiro tool finish), thinking effort maps - Reorg helpers/ → concerns/ (logic) + formats/ (per-format) + schema/ (pure enums: roles/blocks/finishReasons/defaults) - Wire ~280 hardcoded role/block/finish/default literals to schema enums across 20+ files - collapseTextParts + extractTextContent dedup - Normalize translator fn names to openaiToXRequest / xToOpenAIResponse - Golden tests lock behavior; 0 regression (byte-for-byte providers/alias, 26=26 known fails) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "alicode-intl",
|
||||
priority: 10,
|
||||
alias: "alicode-intl",
|
||||
display: {
|
||||
name: "Alibaba Intl",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "alicode",
|
||||
priority: 20,
|
||||
alias: "alicode",
|
||||
display: {
|
||||
name: "Alibaba",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "anthropic",
|
||||
priority: 30,
|
||||
alias: "anthropic",
|
||||
display: {
|
||||
name: "Anthropic",
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ANTIGRAVITY_OAUTH_CLIENT } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "antigravity",
|
||||
priority: 20,
|
||||
alias: "ag",
|
||||
uiAlias: "ag",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "assemblyai",
|
||||
priority: 30,
|
||||
alias: "assemblyai",
|
||||
aliases: [
|
||||
"aai",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "azure",
|
||||
priority: 40,
|
||||
alias: "azure",
|
||||
display: {
|
||||
name: "Azure OpenAI",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "black-forest-labs",
|
||||
priority: 50,
|
||||
alias: "black-forest-labs",
|
||||
aliases: [
|
||||
"bfl",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "blackbox",
|
||||
priority: 50,
|
||||
alias: "blackbox",
|
||||
aliases: [
|
||||
"bb",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "byteplus",
|
||||
priority: 150,
|
||||
alias: "byteplus",
|
||||
aliases: [
|
||||
"bpm",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "cerebras",
|
||||
priority: 60,
|
||||
alias: "cerebras",
|
||||
display: {
|
||||
name: "Cerebras",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "chutes",
|
||||
priority: 70,
|
||||
alias: "chutes",
|
||||
aliases: [
|
||||
"ch",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CLAUDE_CLI_SPOOF_HEADERS } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "claude",
|
||||
priority: 10,
|
||||
alias: "cc",
|
||||
uiAlias: "cc",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "cline",
|
||||
priority: 70,
|
||||
alias: "cl",
|
||||
uiAlias: "cl",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "cloudflare-ai",
|
||||
priority: 20,
|
||||
hasFree: true,
|
||||
alias: "cloudflare-ai",
|
||||
aliases: [
|
||||
"cf",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "codebuddy",
|
||||
priority: 80,
|
||||
display: {
|
||||
name: "CodeBuddy",
|
||||
icon: "smart_toy",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { withCodexReviewModels } from "../models/helpers.js";
|
||||
|
||||
export default {
|
||||
id: "codex",
|
||||
priority: 30,
|
||||
alias: "cx",
|
||||
uiAlias: "cx",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "cohere",
|
||||
priority: 90,
|
||||
alias: "cohere",
|
||||
display: {
|
||||
name: "Cohere",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "comfyui",
|
||||
priority: 120,
|
||||
alias: "comfyui",
|
||||
display: {
|
||||
name: "ComfyUI",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "commandcode",
|
||||
priority: 100,
|
||||
alias: "commandcode",
|
||||
aliases: [
|
||||
"cmc",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "cursor",
|
||||
priority: 40,
|
||||
alias: "cu",
|
||||
uiAlias: "cu",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "deepgram",
|
||||
priority: 20,
|
||||
alias: "deepgram",
|
||||
aliases: [
|
||||
"dg",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "deepseek",
|
||||
priority: 110,
|
||||
alias: "deepseek",
|
||||
aliases: [
|
||||
"ds",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "fal-ai",
|
||||
priority: 90,
|
||||
hasFree: true,
|
||||
alias: "fal-ai",
|
||||
aliases: [
|
||||
"fal",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "fireworks",
|
||||
priority: 50,
|
||||
alias: "fireworks",
|
||||
display: {
|
||||
name: "Fireworks AI",
|
||||
@@ -23,5 +24,6 @@ export default {
|
||||
{ id: "accounts/fireworks/models/qwen3-235b-a22b", name: "Qwen3 235B" },
|
||||
{ id: "nomic-ai/nomic-embed-text-v1.5", name: "Nomic Embed Text v1.5", kind: "embedding" },
|
||||
],
|
||||
serviceKinds: ["llm", "embedding"],
|
||||
embeddingConfig: { baseUrl: "https://api.fireworks.ai/inference/v1/embeddings" },
|
||||
};
|
||||
|
||||
@@ -2,6 +2,8 @@ import { GOOGLE_OAUTH_CLIENT } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "gemini-cli",
|
||||
priority: 130,
|
||||
hasFree: true,
|
||||
alias: "gc",
|
||||
uiAlias: "gc",
|
||||
display: {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { GOOGLE_OAUTH_CLIENT } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "gemini",
|
||||
priority: 10,
|
||||
hasFree: true,
|
||||
alias: "gemini",
|
||||
display: {
|
||||
name: "Gemini",
|
||||
@@ -12,9 +14,9 @@ export default {
|
||||
notice: {
|
||||
apiKeyUrl: "https://aistudio.google.com/app/apikey",
|
||||
},
|
||||
mediaPriority: 1,
|
||||
},
|
||||
category: "freeTier",
|
||||
mediaPriority: 1,
|
||||
transport: {
|
||||
baseUrl: "https://generativelanguage.googleapis.com/v1beta/models",
|
||||
format: "gemini",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "github",
|
||||
priority: 50,
|
||||
alias: "gh",
|
||||
uiAlias: "gh",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "gitlab",
|
||||
priority: 120,
|
||||
display: {
|
||||
name: "GitLab Duo",
|
||||
icon: "code",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "glm-cn",
|
||||
priority: 130,
|
||||
alias: "glm-cn",
|
||||
display: {
|
||||
name: "GLM (China)",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "glm",
|
||||
priority: 140,
|
||||
alias: "glm",
|
||||
display: {
|
||||
name: "GLM Coding",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "grok-web",
|
||||
priority: 150,
|
||||
alias: "grok-web",
|
||||
aliases: [
|
||||
"gw",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "groq",
|
||||
priority: 60,
|
||||
hasFree: true,
|
||||
alias: "groq",
|
||||
display: {
|
||||
name: "Groq",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "huggingface",
|
||||
priority: 70,
|
||||
hasFree: true,
|
||||
alias: "huggingface",
|
||||
aliases: [
|
||||
"hf",
|
||||
@@ -27,5 +29,6 @@ export default {
|
||||
{ id: "openai/whisper-large-v3", name: "Whisper Large v3 (HF)", params: ["language"], kind: "stt" },
|
||||
{ id: "openai/whisper-small", name: "Whisper Small (HF)", params: ["language"], kind: "stt" },
|
||||
],
|
||||
serviceKinds: ["image", "stt"],
|
||||
imageConfig: { baseUrl: "https://api-inference.huggingface.co/models" },
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "hyperbolic",
|
||||
priority: 160,
|
||||
alias: "hyperbolic",
|
||||
aliases: [
|
||||
"hyp",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "iflow",
|
||||
priority: 170,
|
||||
alias: "if",
|
||||
display: {
|
||||
name: "iFlow AI",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "kilocode",
|
||||
priority: 60,
|
||||
alias: "kc",
|
||||
uiAlias: "kc",
|
||||
display: {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CLAUDE_API_HEADERS, KIMI_CODING_BASE_URL } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "kimi-coding",
|
||||
priority: 180,
|
||||
alias: "kmc",
|
||||
display: {
|
||||
name: "Kimi Coding",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CLAUDE_API_HEADERS, KIMI_CODING_BASE_URL } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "kimi",
|
||||
priority: 170,
|
||||
alias: "kimi",
|
||||
display: {
|
||||
name: "Kimi",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "kiro",
|
||||
priority: 80,
|
||||
alias: "kr",
|
||||
uiAlias: "kr",
|
||||
display: {
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
/**
|
||||
* migrate-registry.mjs
|
||||
* Migrates all registry files to Model-A schema:
|
||||
* - models[] = ALL models (chat + media), field `kind` (default "llm")
|
||||
* - media wrapper removed → fields promoted top-level
|
||||
* - *Config.models removed (data merged into models[])
|
||||
* - format: terse, consistent indent
|
||||
*
|
||||
* Run: node --experimental-vm-modules migrate-registry.mjs [--dry]
|
||||
*/
|
||||
import { readFileSync, writeFileSync, readdirSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const REGISTRY_DIR = __dirname; // script lives in registry/
|
||||
const DRY = process.argv.includes("--dry");
|
||||
|
||||
// *Config.models field → kind value
|
||||
const CFG_KIND = {
|
||||
ttsConfig: "tts",
|
||||
sttConfig: "stt",
|
||||
embeddingConfig: "embedding",
|
||||
imageConfig: "image",
|
||||
imageToTextConfig: "imageToText",
|
||||
videoConfig: "video",
|
||||
musicConfig: "music",
|
||||
};
|
||||
|
||||
// Fields in *Config that are NOT models (keep on config)
|
||||
const MODEL_ONLY_KEY = "models";
|
||||
|
||||
// Top-level registry fields that are NOT media-config (don't flatten these from media)
|
||||
// serviceKinds + *Config + searchViaChat + mediaConfig + passthroughModels are media fields
|
||||
// Everything else is already top-level
|
||||
const MEDIA_WHITELIST = new Set([
|
||||
"serviceKinds",
|
||||
"ttsConfig", "sttConfig", "embeddingConfig",
|
||||
"imageConfig", "imageToTextConfig", "videoConfig", "musicConfig",
|
||||
"searchViaChat", "searchConfig", "fetchConfig",
|
||||
"modelsFetcher", "hasProviderSpecificData", "passthroughModels",
|
||||
"mediaPriority", "hiddenKinds",
|
||||
]);
|
||||
|
||||
function migrateEntry(entry, filename) {
|
||||
const out = {};
|
||||
|
||||
// 1. Top-level identity/transport fields (preserve order)
|
||||
const TRANSPORT_KEYS = ["id", "alias", "aliases", "uiAlias", "display", "category",
|
||||
"authType", "authHint", "authModes", "hasOAuth", "noAuth",
|
||||
"hasProviderSpecificData", "thinkingConfig", "hiddenKinds",
|
||||
"regions", "defaultRegion", "passthroughModels", "transport"];
|
||||
for (const k of TRANSPORT_KEYS) {
|
||||
if (entry[k] !== undefined) out[k] = entry[k];
|
||||
}
|
||||
|
||||
// 2. Collect existing models[] (convert type→kind, skip if kind already set)
|
||||
const existingModels = (entry.models || []).map(m => {
|
||||
const { type, ...rest } = m;
|
||||
const kind = m.kind ?? (type && type !== "llm" ? type : undefined);
|
||||
return kind ? { ...rest, kind } : rest;
|
||||
});
|
||||
const existingIds = new Set(existingModels.map(m => m.id));
|
||||
|
||||
// 3. Extract models from *Config.models (merge into models[])
|
||||
const mediaModels = [];
|
||||
const media = entry.media || {};
|
||||
for (const [cfgKey, kind] of Object.entries(CFG_KIND)) {
|
||||
const cfg = media[cfgKey];
|
||||
if (!cfg?.models) continue;
|
||||
for (const m of cfg.models) {
|
||||
// Check if same id+kind combo already exists to avoid true duplicates
|
||||
const dup = existingModels.find(x => x.id === m.id && (x.kind ?? "llm") === kind);
|
||||
if (dup) continue;
|
||||
const { ...mClean } = m;
|
||||
mediaModels.push({ ...mClean, kind });
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Merge models (existing first, then media additions)
|
||||
const allModels = [...existingModels, ...mediaModels];
|
||||
// Only include models key if non-empty or explicitly defined
|
||||
if (allModels.length > 0 || entry.models !== undefined) {
|
||||
out.models = allModels;
|
||||
}
|
||||
|
||||
// 5. Flatten media fields (without .models sub-arrays)
|
||||
for (const [k, v] of Object.entries(media)) {
|
||||
if (!MEDIA_WHITELIST.has(k)) continue;
|
||||
if (CFG_KIND[k]) {
|
||||
// Strip .models from config, keep rest
|
||||
const { models: _m, ...cfgRest } = (v || {});
|
||||
if (Object.keys(cfgRest).length > 0) out[k] = cfgRest;
|
||||
} else {
|
||||
out[k] = v;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Other top-level fields not in TRANSPORT_KEYS and not media (e.g. features, oauth, usage in transport)
|
||||
const SKIP = new Set([...TRANSPORT_KEYS, "models", "media", ...Object.keys(CFG_KIND),
|
||||
"serviceKinds", "searchViaChat", "searchConfig", "fetchConfig",
|
||||
"modelsFetcher", "passthroughModels", "mediaPriority"]);
|
||||
for (const [k, v] of Object.entries(entry)) {
|
||||
if (!SKIP.has(k)) out[k] = v;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
// Format a registry entry as clean JS (no JSON.stringify — write proper ES module)
|
||||
function formatValue(v, indent = 0) {
|
||||
const pad = " ".repeat(indent);
|
||||
const pad1 = " ".repeat(indent + 1);
|
||||
|
||||
if (v === null || v === undefined) return String(v);
|
||||
if (typeof v === "boolean" || typeof v === "number") return String(v);
|
||||
if (typeof v === "string") return JSON.stringify(v);
|
||||
|
||||
if (Array.isArray(v)) {
|
||||
if (v.length === 0) return "[]";
|
||||
// Model arrays: 1 model per line (compact inline object)
|
||||
const items = v.map(item => {
|
||||
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
||||
return `${pad1}${formatInlineObject(item)}`;
|
||||
}
|
||||
return `${pad1}${formatValue(item, indent + 1)}`;
|
||||
});
|
||||
return `[\n${items.join(",\n")},\n${pad}]`;
|
||||
}
|
||||
|
||||
if (typeof v === "object") {
|
||||
const keys = Object.keys(v);
|
||||
if (keys.length === 0) return "{}";
|
||||
const lines = keys.map(k => {
|
||||
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
|
||||
return `${pad1}${key}: ${formatValue(v[k], indent + 1)}`;
|
||||
});
|
||||
return `{\n${lines.join(",\n")},\n${pad}}`;
|
||||
}
|
||||
|
||||
return JSON.stringify(v);
|
||||
}
|
||||
|
||||
// Inline compact object: { id: "x", name: "y", kind: "tts", dimensions: 1536 }
|
||||
function formatInlineObject(obj) {
|
||||
const parts = Object.entries(obj).map(([k, v]) => {
|
||||
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
|
||||
return `${key}: ${JSON.stringify(v)}`;
|
||||
});
|
||||
return `{ ${parts.join(", ")} }`;
|
||||
}
|
||||
|
||||
// Config objects (ttsConfig etc) — inline single line if short, else multi-line
|
||||
function formatConfig(cfg) {
|
||||
const line = `{ ${Object.entries(cfg).map(([k,v])=>`${k}: ${JSON.stringify(v)}`).join(", ")} }`;
|
||||
if (line.length <= 120) return line;
|
||||
const pad1 = " ".repeat(2);
|
||||
const lines = Object.entries(cfg).map(([k,v]) => `${pad1}${k}: ${JSON.stringify(v)}`);
|
||||
return `{\n${lines.join(",\n")},\n }`;
|
||||
}
|
||||
|
||||
// Top-level registry entry formatter
|
||||
function formatEntry(entry, imports = "") {
|
||||
const lines = [];
|
||||
if (imports) lines.push(imports, "");
|
||||
lines.push("export default {");
|
||||
|
||||
const TOP_ORDER = [
|
||||
"id", "alias", "aliases", "uiAlias", "display", "category",
|
||||
"authType", "authHint", "authModes", "hasOAuth", "noAuth",
|
||||
"hasProviderSpecificData", "thinkingConfig", "hiddenKinds",
|
||||
"regions", "defaultRegion", "transport",
|
||||
"models",
|
||||
// media fields
|
||||
"serviceKinds",
|
||||
"ttsConfig", "sttConfig", "embeddingConfig",
|
||||
"imageConfig", "imageToTextConfig", "videoConfig", "musicConfig",
|
||||
"searchViaChat", "searchConfig", "fetchConfig", "modelsFetcher",
|
||||
"passthroughModels", "mediaPriority",
|
||||
// other
|
||||
"oauth", "features",
|
||||
];
|
||||
|
||||
const emitted = new Set();
|
||||
|
||||
function emitKey(k) {
|
||||
if (!(k in entry) || emitted.has(k)) return;
|
||||
emitted.add(k);
|
||||
const v = entry[k];
|
||||
const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(k) ? k : JSON.stringify(k);
|
||||
|
||||
// Config objects (xConfig) — special inline format
|
||||
if (CFG_KIND[k] || k === "searchViaChat" || k === "searchConfig" || k === "fetchConfig" || k === "modelsFetcher") {
|
||||
lines.push(` ${key}: ${formatConfig(v)},`);
|
||||
return;
|
||||
}
|
||||
|
||||
// models[] — terse per-line
|
||||
if (k === "models" && Array.isArray(v)) {
|
||||
if (v.length === 0) { lines.push(` models: [],`); return; }
|
||||
lines.push(` models: [`);
|
||||
for (const m of v) lines.push(` ${formatInlineObject(m)},`);
|
||||
lines.push(` ],`);
|
||||
return;
|
||||
}
|
||||
|
||||
// serviceKinds — inline array
|
||||
if (k === "serviceKinds") {
|
||||
lines.push(` serviceKinds: ${JSON.stringify(v)},`);
|
||||
return;
|
||||
}
|
||||
|
||||
// display — multi-line
|
||||
if (k === "display") {
|
||||
lines.push(` display: ${formatValue(v, 1)},`);
|
||||
return;
|
||||
}
|
||||
|
||||
// transport — multi-line
|
||||
if (k === "transport") {
|
||||
lines.push(` transport: ${formatValue(v, 1)},`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Everything else
|
||||
lines.push(` ${key}: ${formatValue(v, 1)},`);
|
||||
}
|
||||
|
||||
for (const k of TOP_ORDER) emitKey(k);
|
||||
// Emit any remaining keys not in TOP_ORDER
|
||||
for (const k of Object.keys(entry)) emitKey(k);
|
||||
|
||||
lines.push("};");
|
||||
return lines.join("\n") + "\n";
|
||||
}
|
||||
|
||||
// --- Main ---
|
||||
const files = readdirSync(REGISTRY_DIR).filter(f => f.endsWith(".js") && f !== "index.js");
|
||||
let count = 0;
|
||||
|
||||
for (const file of files) {
|
||||
const path = join(REGISTRY_DIR, file);
|
||||
const src = readFileSync(path, "utf8");
|
||||
|
||||
// Extract import lines (for files that import shared constants)
|
||||
const importLines = src.split("\n").filter(l => l.startsWith("import "));
|
||||
const importSrc = importLines.join("\n");
|
||||
|
||||
// Dynamic import to get entry
|
||||
let entry;
|
||||
try {
|
||||
const mod = await import(`${join(REGISTRY_DIR, file)}?t=${Date.now()}`);
|
||||
entry = mod.default;
|
||||
} catch (e) {
|
||||
console.error(`SKIP ${file}: ${e.message}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const migrated = migrateEntry(entry, file);
|
||||
const output = formatEntry(migrated, importSrc);
|
||||
|
||||
if (DRY) {
|
||||
console.log(`\n=== ${file} ===\n${output}`);
|
||||
} else {
|
||||
writeFileSync(path, output, "utf8");
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(DRY ? `[DRY] Would migrate ${files.length} files` : `✅ Migrated ${count} files`);
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "mimo-free",
|
||||
priority: 120,
|
||||
hasFree: true,
|
||||
alias: "mmf",
|
||||
uiAlias: "mmf",
|
||||
display: {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "minimax-cn",
|
||||
priority: 190,
|
||||
alias: "minimax-cn",
|
||||
display: {
|
||||
name: "Minimax (China)",
|
||||
|
||||
@@ -2,6 +2,7 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "minimax",
|
||||
priority: 90,
|
||||
alias: "minimax",
|
||||
display: {
|
||||
name: "Minimax Coding",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "mistral",
|
||||
priority: 80,
|
||||
alias: "mistral",
|
||||
display: {
|
||||
name: "Mistral",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "mmf",
|
||||
priority: 200,
|
||||
display: {
|
||||
name: "MMF",
|
||||
icon: "hub",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "nanobanana",
|
||||
priority: 80,
|
||||
hasFree: true,
|
||||
alias: "nanobanana",
|
||||
aliases: [
|
||||
"nb",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "nebius",
|
||||
priority: 70,
|
||||
alias: "nebius",
|
||||
display: {
|
||||
name: "Nebius AI",
|
||||
@@ -21,5 +22,6 @@ export default {
|
||||
{ id: "meta-llama/Llama-3.3-70B-Instruct", name: "Llama 3.3 70B Instruct" },
|
||||
{ id: "Qwen/Qwen3-Embedding-8B", name: "Qwen3 Embedding 8B", kind: "embedding" },
|
||||
],
|
||||
serviceKinds: ["llm", "embedding"],
|
||||
embeddingConfig: { baseUrl: "https://api.tokenfactory.nebius.com/v1/embeddings" },
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "nvidia",
|
||||
priority: 100,
|
||||
hasFree: true,
|
||||
alias: "nvidia",
|
||||
display: {
|
||||
name: "NVIDIA NIM",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "ollama-local",
|
||||
priority: 50,
|
||||
hasFree: true,
|
||||
alias: "ollama-local",
|
||||
display: {
|
||||
name: "Ollama Local",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "ollama",
|
||||
priority: 40,
|
||||
hasFree: true,
|
||||
alias: "ollama",
|
||||
display: {
|
||||
name: "Ollama Cloud",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "openai",
|
||||
priority: 30,
|
||||
alias: "openai",
|
||||
display: {
|
||||
name: "OpenAI",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "opencode-go",
|
||||
priority: 210,
|
||||
alias: "opencode-go",
|
||||
aliases: [
|
||||
"ocg",
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "opencode",
|
||||
priority: 110,
|
||||
hasFree: true,
|
||||
alias: "oc",
|
||||
uiAlias: "oc",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export default {
|
||||
id: "openrouter",
|
||||
priority: 30,
|
||||
hasFree: true,
|
||||
alias: "openrouter",
|
||||
display: {
|
||||
name: "OpenRouter",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "perplexity-web",
|
||||
priority: 220,
|
||||
alias: "perplexity-web",
|
||||
aliases: [
|
||||
"pw",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "perplexity",
|
||||
priority: 180,
|
||||
alias: "perplexity",
|
||||
aliases: [
|
||||
"pplx",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "qoder",
|
||||
priority: 230,
|
||||
alias: "qd",
|
||||
uiAlias: "qd",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "qwen",
|
||||
priority: 240,
|
||||
alias: "qw",
|
||||
display: {
|
||||
name: "Qwen Code",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "recraft",
|
||||
priority: 70,
|
||||
alias: "recraft",
|
||||
display: {
|
||||
name: "Recraft",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "runwayml",
|
||||
priority: 80,
|
||||
alias: "runwayml",
|
||||
aliases: [
|
||||
"runway",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "sdwebui",
|
||||
priority: 110,
|
||||
alias: "sdwebui",
|
||||
display: {
|
||||
name: "SD WebUI",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "siliconflow",
|
||||
priority: 250,
|
||||
alias: "siliconflow",
|
||||
display: {
|
||||
name: "SiliconFlow",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "stability-ai",
|
||||
priority: 60,
|
||||
alias: "stability-ai",
|
||||
aliases: [
|
||||
"stability",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "together",
|
||||
priority: 60,
|
||||
alias: "together",
|
||||
display: {
|
||||
name: "Together AI",
|
||||
@@ -25,5 +26,6 @@ export default {
|
||||
{ id: "BAAI/bge-large-en-v1.5", name: "BGE Large EN v1.5", kind: "embedding" },
|
||||
{ id: "togethercomputer/m2-bert-80M-8k-retrieval", name: "M2 BERT 80M 8K", kind: "embedding" },
|
||||
],
|
||||
serviceKinds: ["llm", "embedding"],
|
||||
embeddingConfig: { baseUrl: "https://api.together.xyz/v1/embeddings" },
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "vercel-ai-gateway",
|
||||
priority: 160,
|
||||
alias: "vercel-ai-gateway",
|
||||
aliases: [
|
||||
"vercel",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "vertex-partner",
|
||||
priority: 260,
|
||||
alias: "vertex-partner",
|
||||
aliases: [
|
||||
"vxp",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "vertex",
|
||||
priority: 140,
|
||||
alias: "vertex",
|
||||
aliases: [
|
||||
"vx",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "volcengine-ark",
|
||||
priority: 270,
|
||||
alias: "volcengine-ark",
|
||||
aliases: [
|
||||
"ark",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "voyage-ai",
|
||||
priority: 40,
|
||||
alias: "voyage-ai",
|
||||
uiAlias: "voyage",
|
||||
display: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "xai",
|
||||
priority: 280,
|
||||
alias: "xai",
|
||||
display: {
|
||||
name: "xAI (Grok)",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "xiaomi-mimo",
|
||||
priority: 290,
|
||||
alias: "xiaomi-mimo",
|
||||
aliases: [
|
||||
"mimo",
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export default {
|
||||
id: "xiaomi-tokenplan",
|
||||
priority: 300,
|
||||
alias: "xiaomi-tokenplan",
|
||||
aliases: [
|
||||
"xmtp",
|
||||
|
||||
Reference in New Issue
Block a user