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:
@@ -1,9 +1,9 @@
|
||||
import { platform, arch } from "os";
|
||||
import { PROVIDERS } from "./providers.js";
|
||||
import { PROVIDERS, PROVIDER_OAUTH } from "./providers.js";
|
||||
|
||||
// === Gemini CLI ===
|
||||
export const GEMINI_CLI_VERSION = "0.34.0";
|
||||
export const GEMINI_CLI_API_CLIENT = "google-genai-sdk/1.41.0 gl-node/v22.19.0";
|
||||
// === Gemini CLI === derive từ registry gemini-cli.transport
|
||||
export const GEMINI_CLI_VERSION = PROVIDERS["gemini-cli"]?.cliVersion;
|
||||
export const GEMINI_CLI_API_CLIENT = PROVIDERS["gemini-cli"]?.apiClient;
|
||||
|
||||
// Map Node arch to Gemini CLI arch string (x64/x86/arm64/...)
|
||||
function geminiCLIArch() {
|
||||
@@ -17,11 +17,13 @@ export function geminiCLIUserAgent(model = "unknown") {
|
||||
}
|
||||
|
||||
// === GitHub Copilot ===
|
||||
// Derive từ registry github.transport.copilot
|
||||
const _ghCopilot = PROVIDERS.github?.copilot || {};
|
||||
export const GITHUB_COPILOT = {
|
||||
VSCODE_VERSION: "1.110.0",
|
||||
COPILOT_CHAT_VERSION: "0.38.0",
|
||||
USER_AGENT: "GitHubCopilotChat/0.38.0",
|
||||
API_VERSION: "2025-04-01",
|
||||
VSCODE_VERSION: _ghCopilot.vscodeVersion,
|
||||
COPILOT_CHAT_VERSION: _ghCopilot.chatVersion,
|
||||
USER_AGENT: _ghCopilot.userAgent,
|
||||
API_VERSION: _ghCopilot.apiVersion,
|
||||
};
|
||||
|
||||
// === Antigravity enums ===
|
||||
@@ -153,43 +155,19 @@ export const LOAD_CODE_ASSIST_METADATA = {
|
||||
export const CLAUDE_SYSTEM_PROMPT = "You are Claude Code, Anthropic's official CLI for Claude.";
|
||||
export const ANTIGRAVITY_DEFAULT_SYSTEM = "You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Absolute paths only****Proactiveness**";
|
||||
|
||||
// Proactive token refresh lead times per provider (ms)
|
||||
export const REFRESH_LEAD_MS = {
|
||||
codex: 5 * 24 * 60 * 60 * 1000, // 5 days
|
||||
claude: 4 * 60 * 60 * 1000, // 4 hours
|
||||
iflow: 24 * 60 * 60 * 1000, // 24 hours
|
||||
qwen: 20 * 60 * 1000, // 20 minutes
|
||||
"kimi-coding": 5 * 60 * 1000, // 5 minutes
|
||||
antigravity: 5 * 60 * 1000, // 5 minutes
|
||||
};
|
||||
// Derive từ registry oauth.refreshLeadMs
|
||||
export const REFRESH_LEAD_MS = Object.fromEntries(
|
||||
Object.entries(PROVIDER_OAUTH).filter(([, o]) => o.refreshLeadMs).map(([id, o]) => [id, o.refreshLeadMs])
|
||||
);
|
||||
|
||||
// OAuth endpoints
|
||||
export const OAUTH_ENDPOINTS = {
|
||||
google: {
|
||||
token: "https://oauth2.googleapis.com/token",
|
||||
auth: "https://accounts.google.com/o/oauth2/auth"
|
||||
},
|
||||
openai: {
|
||||
token: PROVIDERS.codex.tokenUrl,
|
||||
auth: "https://auth.openai.com/oauth/authorize"
|
||||
},
|
||||
anthropic: {
|
||||
token: PROVIDERS.claude.tokenUrl,
|
||||
auth: "https://api.anthropic.com/v1/oauth/authorize"
|
||||
},
|
||||
qwen: {
|
||||
token: "https://qwen.ai/api/v1/oauth2/token",
|
||||
auth: "https://qwen.ai/api/v1/oauth2/device/code"
|
||||
},
|
||||
iflow: {
|
||||
token: PROVIDERS.iflow.tokenUrl,
|
||||
auth: "https://iflow.cn/oauth"
|
||||
},
|
||||
github: {
|
||||
token: "https://github.com/login/oauth/access_token",
|
||||
auth: "https://github.com/login/oauth/authorize",
|
||||
deviceCode: "https://github.com/login/device/code"
|
||||
}
|
||||
google: { token: "https://oauth2.googleapis.com/token", auth: "https://accounts.google.com/o/oauth2/auth" },
|
||||
openai: { token: PROVIDER_OAUTH["codex"]?.tokenUrl, auth: PROVIDER_OAUTH["codex"]?.authorizeUrl },
|
||||
anthropic: { token: PROVIDER_OAUTH["claude"]?.tokenUrl, auth: "https://api.anthropic.com/v1/oauth/authorize" }, // ≠ claude.authorizeUrl (claude.ai login) — keep
|
||||
qwen: { token: PROVIDER_OAUTH["qwen"]?.tokenUrl, auth: PROVIDER_OAUTH["qwen"]?.deviceCodeUrl },
|
||||
iflow: { token: PROVIDER_OAUTH["iflow"]?.tokenUrl, auth: PROVIDER_OAUTH["iflow"]?.authorizeUrl },
|
||||
github: { token: PROVIDER_OAUTH["github"]?.tokenUrl, auth: PROVIDER_OAUTH["github"]?.authorizeUrl, deviceCode: PROVIDER_OAUTH["github"]?.deviceCodeUrl },
|
||||
};
|
||||
|
||||
// Generate Kimi OAuth custom headers
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { PROVIDERS } from "./providers.js";
|
||||
import REGISTRY from "../providers/registry/index.js";
|
||||
// PROVIDER_MODELS now built from providers/registry (transport + models co-located)
|
||||
import { PROVIDER_MODELS } from "../providers/index.js";
|
||||
import { modelQuotaFamily, modelStrip, modelTargetFormat } from "../providers/models/schema.js";
|
||||
import { CODEX_REVIEW_SUFFIX } from "../providers/models/helpers.js";
|
||||
|
||||
export { PROVIDER_MODELS };
|
||||
|
||||
const CODEX_REVIEW_SUFFIX = "-review";
|
||||
|
||||
|
||||
// Helper functions
|
||||
export function getProviderModels(aliasOrId) {
|
||||
@@ -60,27 +60,11 @@ export function getModelQuotaFamily(aliasOrId, modelId) {
|
||||
return modelQuotaFamily(models?.find(m => m.id === modelId));
|
||||
}
|
||||
|
||||
// OAuth providers that use short aliases (everything else: alias = id)
|
||||
// Single source of canonical id→alias; services/model.js derives the reverse.
|
||||
export const OAUTH_ALIASES = {
|
||||
claude: "cc",
|
||||
codex: "cx",
|
||||
"gemini-cli": "gc",
|
||||
qwen: "qw",
|
||||
iflow: "if",
|
||||
antigravity: "ag",
|
||||
github: "gh",
|
||||
kiro: "kr",
|
||||
cursor: "cu",
|
||||
"kimi-coding": "kmc",
|
||||
kilocode: "kc",
|
||||
cline: "cl",
|
||||
opencode: "oc",
|
||||
qoder: "qd",
|
||||
"mimo-free": "mmf",
|
||||
vertex: "vertex",
|
||||
"vertex-partner": "vertex-partner",
|
||||
};
|
||||
// OAuth short aliases — derived from registry `alias` (single source). everything else: alias = id.
|
||||
// vertex/vertex-partner keep alias=id (kept via the `|| id` fallback in consumers).
|
||||
export const OAUTH_ALIASES = Object.fromEntries(
|
||||
REGISTRY.filter(r => r.alias && r.alias !== r.id).map(r => [r.id, r.alias])
|
||||
);
|
||||
|
||||
// Derived from PROVIDERS — no need to maintain manually
|
||||
export const PROVIDER_ID_TO_ALIAS = Object.fromEntries(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Barrel: PROVIDERS now built from providers/registry (transport co-located with models)
|
||||
export { PROVIDERS } from "../providers/index.js";
|
||||
import { PROVIDERS } from "../providers/index.js";
|
||||
export { PROVIDERS, PROVIDER_OAUTH } from "../providers/index.js";
|
||||
|
||||
export const OLLAMA_LOCAL_DEFAULT_HOST = "http://localhost:11434";
|
||||
|
||||
@@ -8,12 +9,9 @@ export function resolveOllamaLocalHost(credentials) {
|
||||
return (raw || OLLAMA_LOCAL_DEFAULT_HOST).replace(/\/$/, "");
|
||||
}
|
||||
|
||||
export const XIAOMI_TOKENPLAN_REGIONS = {
|
||||
sgp: "https://token-plan-sgp.xiaomimimo.com/v1",
|
||||
cn: "https://token-plan-cn.xiaomimimo.com/v1",
|
||||
ams: "https://token-plan-ams.xiaomimimo.com/v1"
|
||||
};
|
||||
export const XIAOMI_TOKENPLAN_DEFAULT_REGION = "sgp";
|
||||
// Region URLs single-source from registry xiaomi-tokenplan.transport
|
||||
export const XIAOMI_TOKENPLAN_REGIONS = PROVIDERS["xiaomi-tokenplan"]?.regions || {};
|
||||
export const XIAOMI_TOKENPLAN_DEFAULT_REGION = PROVIDERS["xiaomi-tokenplan"]?.defaultRegion;
|
||||
|
||||
export function resolveXiaomiTokenplanBaseUrl(credentials) {
|
||||
const region = credentials?.providerSpecificData?.region;
|
||||
|
||||
@@ -30,13 +30,16 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
return `${baseUrl}/v1internal:${action}`;
|
||||
}
|
||||
|
||||
// sessionId comes from transformRequest output; base.execute runs transformRequest before
|
||||
// buildHeaders, so we read it from instance state cached there (fallback: explicit arg).
|
||||
buildHeaders(credentials, stream = true, sessionId = null) {
|
||||
const sid = sessionId || this._lastSessionId;
|
||||
return {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${credentials.accessToken}`,
|
||||
"User-Agent": this.config.headers?.["User-Agent"] || ANTIGRAVITY_HEADERS["User-Agent"],
|
||||
[INTERNAL_REQUEST_HEADER.name]: INTERNAL_REQUEST_HEADER.value,
|
||||
...(sessionId && { "X-Machine-Session-Id": sessionId }),
|
||||
...(sid && { "X-Machine-Session-Id": sid }),
|
||||
"Accept": stream ? "text/event-stream" : "application/json"
|
||||
};
|
||||
}
|
||||
@@ -96,6 +99,8 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
...(tools?.length > 0 && { toolConfig: { functionCallingConfig: { mode: "VALIDATED" } } })
|
||||
};
|
||||
|
||||
this._lastSessionId = transformedRequest.sessionId; // cached for buildHeaders (base.execute order)
|
||||
|
||||
return {
|
||||
...body,
|
||||
project: projectId,
|
||||
@@ -196,98 +201,23 @@ export class AntigravityExecutor extends BaseExecutor {
|
||||
return totalMs > 0 ? totalMs : null;
|
||||
}
|
||||
|
||||
async execute({ model, body, stream, credentials, signal, log, proxyOptions = null }) {
|
||||
const fallbackCount = this.getFallbackCount();
|
||||
let lastError = null;
|
||||
let lastStatus = 0;
|
||||
const MAX_AUTO_RETRIES = 3;
|
||||
const MAX_RETRY_AFTER_RETRIES = 3;
|
||||
const retryAttemptsByUrl = {}; // Track retry attempts per URL
|
||||
const retryAfterAttemptsByUrl = {}; // Track Retry-After retries per URL
|
||||
|
||||
for (let urlIndex = 0; urlIndex < fallbackCount; urlIndex++) {
|
||||
const url = this.buildUrl(model, stream, urlIndex);
|
||||
const transformedBody = this.transformRequest(model, body, stream, credentials);
|
||||
const sessionId = transformedBody.request?.sessionId;
|
||||
const headers = this.buildHeaders(credentials, stream, sessionId);
|
||||
|
||||
// Initialize retry counters for this URL
|
||||
if (!retryAttemptsByUrl[urlIndex]) {
|
||||
retryAttemptsByUrl[urlIndex] = 0;
|
||||
}
|
||||
if (!retryAfterAttemptsByUrl[urlIndex]) {
|
||||
retryAfterAttemptsByUrl[urlIndex] = 0;
|
||||
}
|
||||
|
||||
// Hook called by BaseExecutor.tryRetry: derive delay from Retry-After (header → body),
|
||||
// cap at MAX_RETRY_AFTER_MS, else exponential backoff for 429. Return false to veto (fallback URL).
|
||||
async computeRetryDelay(response, attempt) {
|
||||
let retryMs = this.parseRetryHeaders(response.headers);
|
||||
if (!retryMs) {
|
||||
try {
|
||||
const response = await proxyAwareFetch(url, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: JSON.stringify(transformedBody),
|
||||
signal
|
||||
}, proxyOptions);
|
||||
|
||||
if (response.status === HTTP_STATUS.RATE_LIMITED || response.status === HTTP_STATUS.SERVICE_UNAVAILABLE) {
|
||||
// Try to get retry time from headers first
|
||||
let retryMs = this.parseRetryHeaders(response.headers);
|
||||
|
||||
// If no retry time in headers, try to parse from error message body
|
||||
if (!retryMs) {
|
||||
try {
|
||||
const errorBody = await response.clone().text();
|
||||
const errorJson = JSON.parse(errorBody);
|
||||
const errorMessage = errorJson?.error?.message || errorJson?.message || "";
|
||||
retryMs = this.parseRetryFromErrorMessage(errorMessage);
|
||||
} catch (e) {
|
||||
// Ignore parse errors, will fall back to exponential backoff
|
||||
}
|
||||
}
|
||||
|
||||
if (retryMs && retryMs <= MAX_RETRY_AFTER_MS && retryAfterAttemptsByUrl[urlIndex] < MAX_RETRY_AFTER_RETRIES) {
|
||||
retryAfterAttemptsByUrl[urlIndex]++;
|
||||
log?.debug?.("RETRY", `${response.status} with Retry-After: ${Math.ceil(retryMs / 1000)}s, waiting... (${retryAfterAttemptsByUrl[urlIndex]}/${MAX_RETRY_AFTER_RETRIES})`);
|
||||
await new Promise(resolve => setTimeout(resolve, retryMs));
|
||||
urlIndex--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Auto retry only for 429 when retryMs is 0 or undefined
|
||||
if (response.status === HTTP_STATUS.RATE_LIMITED && (!retryMs || retryMs === 0) && retryAttemptsByUrl[urlIndex] < MAX_AUTO_RETRIES) {
|
||||
retryAttemptsByUrl[urlIndex]++;
|
||||
// Exponential backoff: 2s, 4s, 8s...
|
||||
const backoffMs = Math.min(1000 * (2 ** retryAttemptsByUrl[urlIndex]), MAX_RETRY_AFTER_MS);
|
||||
log?.debug?.("RETRY", `429 auto retry ${retryAttemptsByUrl[urlIndex]}/${MAX_AUTO_RETRIES} after ${backoffMs / 1000}s`);
|
||||
await new Promise(resolve => setTimeout(resolve, backoffMs));
|
||||
urlIndex--;
|
||||
continue;
|
||||
}
|
||||
|
||||
log?.debug?.("RETRY", `${response.status}, Retry-After ${retryMs ? `too long (${Math.ceil(retryMs / 1000)}s)` : 'missing'}, trying fallback`);
|
||||
lastStatus = response.status;
|
||||
|
||||
if (urlIndex + 1 < fallbackCount) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.shouldRetry(response.status, urlIndex)) {
|
||||
log?.debug?.("RETRY", `${response.status} on ${url}, trying fallback ${urlIndex + 1}`);
|
||||
lastStatus = response.status;
|
||||
continue;
|
||||
}
|
||||
|
||||
return { response, url, headers, transformedBody };
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (urlIndex + 1 < fallbackCount) {
|
||||
log?.debug?.("RETRY", `Error on ${url}, trying fallback ${urlIndex + 1}`);
|
||||
continue;
|
||||
}
|
||||
throw error;
|
||||
const errorJson = JSON.parse(await response.clone().text());
|
||||
retryMs = this.parseRetryFromErrorMessage(errorJson?.error?.message || errorJson?.message || "");
|
||||
} catch {
|
||||
// ignore parse errors → fall through to backoff
|
||||
}
|
||||
}
|
||||
|
||||
throw lastError || new Error(`All ${fallbackCount} URLs failed with status ${lastStatus}`);
|
||||
if (retryMs) return retryMs <= MAX_RETRY_AFTER_MS ? retryMs : false;
|
||||
if (response.status === HTTP_STATUS.RATE_LIMITED) {
|
||||
return Math.min(1000 * (2 ** attempt), MAX_RETRY_AFTER_MS); // exponential backoff
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ import { HTTP_STATUS, RETRY_CONFIG, DEFAULT_RETRY_CONFIG, resolveRetryEntry, FET
|
||||
import { shouldRefreshCredentials } from "../services/oauthCredentialManager.js";
|
||||
import { proxyAwareFetch } from "../utils/proxyFetch.js";
|
||||
import { dbg } from "../utils/debugLog.js";
|
||||
import { ANTHROPIC_API_VERSION, OPENAI_COMPAT_BASE, ANTHROPIC_COMPAT_BASE } from "../providers/shared.js";
|
||||
|
||||
/**
|
||||
* BaseExecutor - Base class for provider executors
|
||||
@@ -27,13 +28,13 @@ export class BaseExecutor {
|
||||
|
||||
buildUrl(model, stream, urlIndex = 0, credentials = null) {
|
||||
if (this.provider?.startsWith?.("openai-compatible-")) {
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || "https://api.openai.com/v1";
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || OPENAI_COMPAT_BASE;
|
||||
const normalized = baseUrl.replace(/\/$/, "");
|
||||
const path = this.provider.includes("responses") ? "/responses" : "/chat/completions";
|
||||
return `${normalized}${path}`;
|
||||
}
|
||||
if (this.provider?.startsWith?.("anthropic-compatible-")) {
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || "https://api.anthropic.com/v1";
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || ANTHROPIC_COMPAT_BASE;
|
||||
const normalized = baseUrl.replace(/\/$/, "");
|
||||
return `${normalized}/messages`;
|
||||
}
|
||||
@@ -55,7 +56,7 @@ export class BaseExecutor {
|
||||
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
||||
}
|
||||
if (!headers["anthropic-version"]) {
|
||||
headers["anthropic-version"] = "2023-06-01";
|
||||
headers["anthropic-version"] = ANTHROPIC_API_VERSION;
|
||||
}
|
||||
} else {
|
||||
// Standard Bearer token auth for other providers
|
||||
@@ -105,12 +106,20 @@ export class BaseExecutor {
|
||||
const retryConfig = { ...DEFAULT_RETRY_CONFIG, ...this.config.retry };
|
||||
|
||||
// Schedule retry via retryConfig[statusKey]. Returns true when caller should `urlIndex--; continue`
|
||||
const tryRetry = async (urlIndex, statusKey, reason) => {
|
||||
// response (optional) lets a subclass hook compute a dynamic delay (e.g. antigravity Retry-After).
|
||||
const tryRetry = async (urlIndex, statusKey, reason, response = null) => {
|
||||
const { attempts, delayMs } = resolveRetryEntry(retryConfig[statusKey]);
|
||||
if (attempts <= 0 || retryAttemptsByUrl[urlIndex] >= attempts) return false;
|
||||
// Hook: subclass may derive delay from the response (headers/body). null → skip retry, use fallback.
|
||||
let waitMs = delayMs;
|
||||
if (response && this.computeRetryDelay) {
|
||||
const dynamic = await this.computeRetryDelay(response, retryAttemptsByUrl[urlIndex] + 1, delayMs);
|
||||
if (dynamic === false) return false; // hook vetoes retry (e.g. Retry-After too long)
|
||||
if (dynamic != null) waitMs = dynamic;
|
||||
}
|
||||
retryAttemptsByUrl[urlIndex]++;
|
||||
log?.debug?.("RETRY", `${reason} retry ${retryAttemptsByUrl[urlIndex]}/${attempts} after ${delayMs / 1000}s`);
|
||||
await new Promise(resolve => setTimeout(resolve, delayMs));
|
||||
log?.debug?.("RETRY", `${reason} retry ${retryAttemptsByUrl[urlIndex]}/${attempts} after ${waitMs / 1000}s`);
|
||||
await new Promise(resolve => setTimeout(resolve, waitMs));
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -142,7 +151,7 @@ export class BaseExecutor {
|
||||
const cl = response.headers?.get?.("content-length") || "?";
|
||||
dbg("FETCH", `${this.provider.toUpperCase()} ← ${response.status} | ttft=${Date.now() - fetchT0}ms | ct=${ct} | cl=${cl}`);
|
||||
|
||||
if (await tryRetry(urlIndex, response.status, `status ${response.status}`)) { urlIndex--; continue; }
|
||||
if (await tryRetry(urlIndex, response.status, `status ${response.status}`, response)) { urlIndex--; continue; }
|
||||
|
||||
if (this.shouldRetry(response.status, urlIndex)) {
|
||||
log?.debug?.("RETRY", `${response.status} on ${url}, trying fallback ${urlIndex + 1}`);
|
||||
|
||||
@@ -1,11 +1,80 @@
|
||||
import { BaseExecutor } from "./base.js";
|
||||
import { PROVIDERS } from "../config/providers.js";
|
||||
import { PROVIDERS, PROVIDER_OAUTH } from "../config/providers.js";
|
||||
import { ANTHROPIC_API_VERSION, OPENAI_COMPAT_BASE, ANTHROPIC_COMPAT_BASE } from "../providers/shared.js";
|
||||
import { OAUTH_ENDPOINTS, buildKimiHeaders } from "../config/appConstants.js";
|
||||
import { buildClineHeaders } from "../shared/clineAuth.js";
|
||||
import { getCachedClaudeHeaders } from "../utils/claudeHeaderCache.js";
|
||||
import { proxyAwareFetch } from "../utils/proxyFetch.js";
|
||||
import { injectReasoningContent } from "../utils/reasoningContentInjector.js";
|
||||
|
||||
// Auth header descriptors — derived from registry transport.auth, fallback to hardcoded defaults.
|
||||
const BEARER = { combined: true, header: "Authorization", scheme: "bearer" };
|
||||
const XAPIKEY = { combined: true, header: "x-api-key", scheme: "raw" };
|
||||
const AUTH_DESCRIPTORS = Object.fromEntries(
|
||||
Object.entries(PROVIDERS)
|
||||
.filter(([, t]) => t.auth)
|
||||
.map(([id, t]) => [id, t.auth])
|
||||
);
|
||||
|
||||
// Apply a token to a header per scheme (matches legacy: combined always sets, even when undefined).
|
||||
function setAuth(headers, spec, token) {
|
||||
headers[spec.header] = spec.scheme === "bearer" ? `Bearer ${token}` : token;
|
||||
}
|
||||
|
||||
// Resolve auth onto headers from a descriptor.
|
||||
function applyAuth(headers, desc, credentials) {
|
||||
if (desc.combined) {
|
||||
// combined providers always set the header (legacy behavior, incl. noAuth → "Bearer undefined")
|
||||
setAuth(headers, desc, credentials.apiKey || credentials.accessToken);
|
||||
if (desc.anthropicVersion && !headers["anthropic-version"]) headers["anthropic-version"] = ANTHROPIC_API_VERSION;
|
||||
return;
|
||||
}
|
||||
// split apiKey/oauth: set only the matching branch (legacy: anthropic-compatible skips when both absent)
|
||||
if (credentials.apiKey) setAuth(headers, desc.apiKey, credentials.apiKey);
|
||||
else if (credentials.accessToken) setAuth(headers, desc.oauth, credentials.accessToken);
|
||||
if (desc.anthropicVersion && !headers["anthropic-version"]) headers["anthropic-version"] = ANTHROPIC_API_VERSION;
|
||||
}
|
||||
|
||||
// Provider-specific header quirks kept as small hooks (not pure auth).
|
||||
const HEADER_HOOKS = {
|
||||
kimiHeaders: (h) => Object.assign(h, buildKimiHeaders()),
|
||||
clineHeaders: (h, c) => Object.assign(h, buildClineHeaders(c.apiKey || c.accessToken)),
|
||||
kilocodeOrg: (h, c) => { if (c.providerSpecificData?.orgId) h["X-Kilocode-OrganizationID"] = c.providerSpecificData.orgId; },
|
||||
claudeOverlay: (h) => {
|
||||
const cached = getCachedClaudeHeaders();
|
||||
if (!cached) return;
|
||||
for (const lcKey of Object.keys(cached)) {
|
||||
const titleKey = lcKey.replace(/(^|-)([a-z])/g, (_, sep, ch) => sep + ch.toUpperCase());
|
||||
if (lcKey === "anthropic-beta") {
|
||||
const staticBetaStr = h[titleKey] || h[lcKey] || "";
|
||||
const flags = new Set(staticBetaStr.split(",").map(f => f.trim()).filter(Boolean));
|
||||
for (const f of cached[lcKey].split(",").map(f => f.trim()).filter(Boolean)) flags.add(f);
|
||||
cached[lcKey] = Array.from(flags).join(",");
|
||||
}
|
||||
if (titleKey !== lcKey && h[titleKey] !== undefined) delete h[titleKey];
|
||||
}
|
||||
Object.assign(h, cached);
|
||||
},
|
||||
};
|
||||
|
||||
// Config-driven OAuth refresh grants — derived from registry oauth.refresh.
|
||||
const REFRESH_GRANTS = Object.fromEntries(
|
||||
Object.entries(PROVIDER_OAUTH)
|
||||
.filter(([, o]) => o.refresh)
|
||||
.map(([id, o]) => {
|
||||
const tokenUrl = o.tokenUrl;
|
||||
const encoding = o.refresh.encoding;
|
||||
const extraParams = o.refresh.scope ? { scope: o.refresh.scope } : {};
|
||||
return [id, {
|
||||
encoding,
|
||||
url: () => tokenUrl,
|
||||
params: (ex) => id === "gemini"
|
||||
? { client_id: ex.config.clientId, client_secret: ex.config.clientSecret, ...extraParams }
|
||||
: { client_id: o.clientId, ...extraParams },
|
||||
}];
|
||||
})
|
||||
);
|
||||
|
||||
export class DefaultExecutor extends BaseExecutor {
|
||||
constructor(provider) {
|
||||
super(provider, PROVIDERS[provider] || PROVIDERS.openai);
|
||||
@@ -15,7 +84,8 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
const transformed = this.applyJsonSchemaFallback(body);
|
||||
|
||||
if (transformed && typeof transformed === "object") {
|
||||
if (this.provider === "cerebras" || this.provider === "mistral") {
|
||||
// quirk: some openai-compatible providers reject Anthropic's client_metadata field
|
||||
if (this.config.quirks?.dropClientMetadata) {
|
||||
delete transformed.client_metadata;
|
||||
}
|
||||
}
|
||||
@@ -45,13 +115,13 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
|
||||
buildUrl(model, stream, urlIndex = 0, credentials = null) {
|
||||
if (this.provider?.startsWith?.("openai-compatible-")) {
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || "https://api.openai.com/v1";
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || OPENAI_COMPAT_BASE;
|
||||
const normalized = baseUrl.replace(/\/$/, "");
|
||||
const path = this.provider.includes("responses") ? "/responses" : "/chat/completions";
|
||||
return `${normalized}${path}`;
|
||||
}
|
||||
if (this.provider?.startsWith?.("anthropic-compatible-")) {
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || "https://api.anthropic.com/v1";
|
||||
const baseUrl = credentials?.providerSpecificData?.baseUrl || ANTHROPIC_COMPAT_BASE;
|
||||
const normalized = baseUrl.replace(/\/$/, "");
|
||||
return `${normalized}/messages`;
|
||||
}
|
||||
@@ -72,86 +142,23 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
return url;
|
||||
}
|
||||
|
||||
// Fallback descriptor for providers without an explicit entry in AUTH_DESCRIPTORS.
|
||||
resolveAuthDescriptor() {
|
||||
if (this.provider?.startsWith?.("anthropic-compatible-")) {
|
||||
return { apiKey: { header: "x-api-key", scheme: "raw" }, oauth: { header: "Authorization", scheme: "bearer" }, anthropicVersion: true };
|
||||
}
|
||||
if (this.config?.format === "claude") {
|
||||
return { ...XAPIKEY, anthropicVersion: true };
|
||||
}
|
||||
return BEARER;
|
||||
}
|
||||
|
||||
buildHeaders(credentials, stream = true) {
|
||||
const headers = { "Content-Type": "application/json", ...this.config.headers };
|
||||
|
||||
switch (this.provider) {
|
||||
case "gemini":
|
||||
credentials.apiKey ? headers["x-goog-api-key"] = credentials.apiKey : headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
||||
break;
|
||||
case "claude": {
|
||||
// Overlay live cached headers from real Claude Code client over static defaults.
|
||||
// Static headers (Title-Case) remain as cold-start fallback.
|
||||
const cached = getCachedClaudeHeaders();
|
||||
if (cached) {
|
||||
// Remove Title-Case static keys that conflict with incoming lowercase cached keys
|
||||
for (const lcKey of Object.keys(cached)) {
|
||||
// Build the Title-Case equivalent: "anthropic-version" → "Anthropic-Version"
|
||||
const titleKey = lcKey.replace(/(^|-)([a-z])/g, (_, sep, c) => sep + c.toUpperCase());
|
||||
|
||||
// Special handling for Anthropic-Beta to preserve required flags like OAuth
|
||||
if (lcKey === "anthropic-beta") {
|
||||
const staticBetaStr = headers[titleKey] || headers[lcKey] || "";
|
||||
const staticFlags = new Set(staticBetaStr.split(",").map(f => f.trim()).filter(Boolean));
|
||||
const cachedFlags = new Set(cached[lcKey].split(",").map(f => f.trim()).filter(Boolean));
|
||||
|
||||
// Merge all static flags (which contain oauth, thinking, etc) into the cached ones
|
||||
for (const flag of staticFlags) {
|
||||
cachedFlags.add(flag);
|
||||
}
|
||||
|
||||
cached[lcKey] = Array.from(cachedFlags).join(",");
|
||||
}
|
||||
|
||||
if (titleKey !== lcKey && headers[titleKey] !== undefined) {
|
||||
delete headers[titleKey];
|
||||
}
|
||||
}
|
||||
Object.assign(headers, cached);
|
||||
}
|
||||
credentials.apiKey
|
||||
? (headers["x-api-key"] = credentials.apiKey)
|
||||
: (headers["Authorization"] = `Bearer ${credentials.accessToken}`);
|
||||
break;
|
||||
}
|
||||
case "glm":
|
||||
case "kimi":
|
||||
case "minimax":
|
||||
case "minimax-cn":
|
||||
case "kimi-coding":
|
||||
headers["x-api-key"] = credentials.apiKey || credentials.accessToken;
|
||||
if (this.provider === "kimi-coding") Object.assign(headers, buildKimiHeaders());
|
||||
break;
|
||||
default:
|
||||
if (this.provider?.startsWith?.("anthropic-compatible-")) {
|
||||
if (credentials.apiKey) {
|
||||
headers["x-api-key"] = credentials.apiKey;
|
||||
} else if (credentials.accessToken) {
|
||||
headers["Authorization"] = `Bearer ${credentials.accessToken}`;
|
||||
}
|
||||
if (!headers["anthropic-version"]) {
|
||||
headers["anthropic-version"] = "2023-06-01";
|
||||
}
|
||||
} else if (this.provider === "gitlab") {
|
||||
// GitLab Duo uses Bearer token (PAT with ai_features scope, or OAuth access token)
|
||||
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
|
||||
} else if (this.provider === "codebuddy") {
|
||||
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
|
||||
} else if (this.provider === "kilocode") {
|
||||
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
|
||||
if (credentials.providerSpecificData?.orgId) {
|
||||
headers["X-Kilocode-OrganizationID"] = credentials.providerSpecificData.orgId;
|
||||
}
|
||||
} else if (this.provider === "cline") {
|
||||
Object.assign(headers, buildClineHeaders(credentials.apiKey || credentials.accessToken));
|
||||
} else if (this.config?.format === "claude") {
|
||||
// Generic claude-format provider (e.g. agentrouter): x-api-key + anthropic-version
|
||||
headers["x-api-key"] = credentials.apiKey || credentials.accessToken;
|
||||
if (!headers["anthropic-version"]) headers["anthropic-version"] = "2023-06-01";
|
||||
} else {
|
||||
headers["Authorization"] = `Bearer ${credentials.apiKey || credentials.accessToken}`;
|
||||
}
|
||||
}
|
||||
const desc = AUTH_DESCRIPTORS[this.provider] || this.resolveAuthDescriptor();
|
||||
// Hooks run BEFORE auth so dynamic overlays (claude cached headers) can't clobber the token.
|
||||
for (const hook of desc.hooks || []) HEADER_HOOKS[hook]?.(headers, credentials);
|
||||
applyAuth(headers, desc, credentials);
|
||||
|
||||
// Strip first-party Claude Code identity headers for non-Anthropic anthropic-compatible upstreams
|
||||
if (this.provider?.startsWith?.("anthropic-compatible-")) {
|
||||
@@ -190,15 +197,25 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
return headers;
|
||||
}
|
||||
|
||||
// Generic OAuth refresh for the common {grant_type, refresh_token, client_id[, ...]} shape.
|
||||
// grant = REFRESH_GRANTS[provider]; client creds resolved from PROVIDERS or this.config.
|
||||
refreshFromGrant(credentials, proxyOptions) {
|
||||
const grant = REFRESH_GRANTS[this.provider];
|
||||
const params = { grant_type: "refresh_token", refresh_token: credentials.refreshToken, ...grant.params(this) };
|
||||
return grant.encoding === "json"
|
||||
? this.refreshWithJSON(grant.url(), params, proxyOptions)
|
||||
: this.refreshWithForm(grant.url(), params, proxyOptions);
|
||||
}
|
||||
|
||||
async refreshCredentials(credentials, log, proxyOptions = null) {
|
||||
if (!credentials.refreshToken) return null;
|
||||
|
||||
const refreshers = {
|
||||
claude: () => this.refreshWithJSON(OAUTH_ENDPOINTS.anthropic.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.claude.clientId }, proxyOptions),
|
||||
codex: () => this.refreshWithForm(OAUTH_ENDPOINTS.openai.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.codex.clientId, scope: "openid profile email offline_access" }, proxyOptions),
|
||||
claude: () => this.refreshFromGrant(credentials, proxyOptions),
|
||||
codex: () => this.refreshFromGrant(credentials, proxyOptions),
|
||||
qwen: () => this.refreshWithForm(OAUTH_ENDPOINTS.qwen.token, { grant_type: "refresh_token", refresh_token: credentials.refreshToken, client_id: PROVIDERS.qwen.clientId }, proxyOptions),
|
||||
iflow: () => this.refreshIflow(credentials.refreshToken, proxyOptions),
|
||||
gemini: () => this.refreshGoogle(credentials.refreshToken, proxyOptions),
|
||||
gemini: () => this.refreshFromGrant(credentials, proxyOptions),
|
||||
kiro: () => this.refreshKiro(credentials.refreshToken, proxyOptions),
|
||||
cline: () => this.refreshCline(credentials.refreshToken, proxyOptions),
|
||||
"kimi-coding": () => this.refreshKimiCoding(credentials.refreshToken, proxyOptions),
|
||||
@@ -252,17 +269,6 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
|
||||
}
|
||||
|
||||
async refreshGoogle(refreshToken, proxyOptions = null) {
|
||||
const response = await proxyAwareFetch(OAUTH_ENDPOINTS.google.token, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json" },
|
||||
body: new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, client_id: this.config.clientId, client_secret: this.config.clientSecret })
|
||||
}, proxyOptions);
|
||||
if (!response.ok) return null;
|
||||
const tokens = await response.json();
|
||||
return { accessToken: tokens.access_token, refreshToken: tokens.refresh_token || refreshToken, expiresIn: tokens.expires_in };
|
||||
}
|
||||
|
||||
async refreshKiro(refreshToken, proxyOptions = null) {
|
||||
const response = await proxyAwareFetch(PROVIDERS.kiro.tokenUrl, {
|
||||
method: "POST",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BaseExecutor } from "./base.js";
|
||||
import { PROVIDERS } from "../config/providers.js";
|
||||
import { injectReasoningContent } from "../utils/reasoningContentInjector.js";
|
||||
import { ANTHROPIC_API_VERSION } from "../providers/shared.js";
|
||||
|
||||
// Models that use /zen/go/v1/messages (Anthropic/Claude format + x-api-key auth)
|
||||
const CLAUDE_FORMAT_MODELS = new Set(["minimax-m2.5", "minimax-m2.7"]);
|
||||
@@ -26,7 +27,7 @@ export class OpenCodeGoExecutor extends BaseExecutor {
|
||||
|
||||
if (CLAUDE_FORMAT_MODELS.has(this._lastModel)) {
|
||||
headers["x-api-key"] = key;
|
||||
headers["anthropic-version"] = "2023-06-01";
|
||||
headers["anthropic-version"] = ANTHROPIC_API_VERSION;
|
||||
} else {
|
||||
headers["Authorization"] = `Bearer ${key}`;
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
* different model upstream, so a missing entry is a hard error.
|
||||
*/
|
||||
|
||||
import { qoderEncodeBody } from "@/lib/qoder/encoding.js";
|
||||
import { buildCosyHeaders } from "@/lib/qoder/cosy.js";
|
||||
import { qoderEncodeBody } from "../shared/qoder/encoding.js";
|
||||
import { buildCosyHeaders } from "../shared/qoder/cosy.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { createHash } from "crypto";
|
||||
|
||||
@@ -33,7 +33,7 @@ import { FETCH_CONNECT_TIMEOUT_MS } from "../config/runtimeConfig.js";
|
||||
import {
|
||||
QODER_CHAT_URL_ENCODED,
|
||||
QODER_MODEL_MAP,
|
||||
} from "@/lib/qoder/constants.js";
|
||||
} from "../shared/qoder/constants.js";
|
||||
import { getQoderModelConfig, resolveQoderModels } from "../services/qoderModels.js";
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,11 @@ import { convertResponsesStreamToJson } from "../../transformer/streamToJsonConv
|
||||
import { createErrorResult } from "../../utils/error.js";
|
||||
import { HTTP_STATUS } from "../../config/runtimeConfig.js";
|
||||
import { FORMATS } from "../../translator/formats.js";
|
||||
import { PROVIDERS } from "../../config/providers.js";
|
||||
import { buildRequestDetail, extractRequestConfig, saveUsageStats } from "./requestDetail.js";
|
||||
|
||||
// Responses-API providers (e.g. codex) may emit SSE without content-type + use Responses output shape
|
||||
const isResponsesProvider = (p) => PROVIDERS[p]?.format === FORMATS.OPENAI_RESPONSES;
|
||||
import { saveRequestDetail, appendRequestLog } from "@/lib/usageDb.js";
|
||||
|
||||
function textFromResponsesMessageItem(item) {
|
||||
@@ -100,7 +104,7 @@ export function parseSSEToOpenAIResponse(rawSSE, fallbackModel) {
|
||||
*/
|
||||
export async function handleForcedSSEToJson({ providerResponse, sourceFormat, provider, model, body, stream, translatedBody, finalBody, requestStartTime, connectionId, apiKey, clientRawRequest, onRequestSuccess, trackDone, appendLog }) {
|
||||
const contentType = providerResponse.headers.get("content-type") || "";
|
||||
const isSSE = contentType.includes("text/event-stream") || (contentType === "" && provider === "codex");
|
||||
const isSSE = contentType.includes("text/event-stream") || (contentType === "" && isResponsesProvider(provider));
|
||||
if (!isSSE) return null; // not handled here
|
||||
|
||||
trackDone();
|
||||
@@ -112,7 +116,7 @@ export async function handleForcedSSEToJson({ providerResponse, sourceFormat, pr
|
||||
};
|
||||
|
||||
// Codex/Responses API SSE path
|
||||
const isCodexResponsesApi = provider === "codex" || sourceFormat === FORMATS.OPENAI_RESPONSES;
|
||||
const isCodexResponsesApi = isResponsesProvider(provider) || sourceFormat === FORMATS.OPENAI_RESPONSES;
|
||||
if (isCodexResponsesApi) {
|
||||
try {
|
||||
const jsonResponse = await convertResponsesStreamToJson(providerResponse.body);
|
||||
|
||||
@@ -9,20 +9,27 @@ import { buildRequestDetail, extractRequestConfig, saveUsageStats } from "./requ
|
||||
import { saveRequestDetail } from "@/lib/usageDb.js";
|
||||
import { SSE_HEADERS_CORS as SSE_HEADERS } from "../../utils/sseConstants.js";
|
||||
|
||||
// Codex returns Responses API SSE → which client format to translate INTO, by request sourceFormat.
|
||||
// Gemini-family all map to ANTIGRAVITY decoder; unknown sources fall back to OPENAI.
|
||||
const CODEX_SOURCE_TO_TARGET = {
|
||||
[FORMATS.OPENAI_RESPONSES]: FORMATS.OPENAI_RESPONSES,
|
||||
[FORMATS.CLAUDE]: FORMATS.CLAUDE,
|
||||
[FORMATS.ANTIGRAVITY]: FORMATS.ANTIGRAVITY,
|
||||
[FORMATS.GEMINI]: FORMATS.ANTIGRAVITY,
|
||||
[FORMATS.GEMINI_CLI]: FORMATS.ANTIGRAVITY,
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine which SSE transform stream to use based on provider/format.
|
||||
*/
|
||||
function buildTransformStream({ provider, sourceFormat, targetFormat, userAgent, reqLogger, toolNameMap, model, connectionId, body, onStreamComplete, apiKey }) {
|
||||
const isDroidCLI = userAgent?.toLowerCase().includes("droid") || userAgent?.toLowerCase().includes("codex-cli");
|
||||
const needsCodexTranslation = provider === "codex" && targetFormat === FORMATS.OPENAI_RESPONSES && !isDroidCLI;
|
||||
// Responses-API providers (e.g. codex) emit Responses SSE → translate into client format
|
||||
const isResponsesProvider = PROVIDERS[provider]?.format === FORMATS.OPENAI_RESPONSES;
|
||||
const needsCodexTranslation = isResponsesProvider && targetFormat === FORMATS.OPENAI_RESPONSES && !isDroidCLI;
|
||||
|
||||
if (needsCodexTranslation) {
|
||||
// Codex returns Responses API SSE → translate to client format
|
||||
let codexTarget;
|
||||
if (sourceFormat === FORMATS.OPENAI_RESPONSES) codexTarget = FORMATS.OPENAI_RESPONSES;
|
||||
else if (sourceFormat === FORMATS.CLAUDE) codexTarget = FORMATS.CLAUDE;
|
||||
else if (sourceFormat === FORMATS.ANTIGRAVITY || sourceFormat === FORMATS.GEMINI || sourceFormat === FORMATS.GEMINI_CLI) codexTarget = FORMATS.ANTIGRAVITY;
|
||||
else codexTarget = FORMATS.OPENAI;
|
||||
const codexTarget = CODEX_SOURCE_TO_TARGET[sourceFormat] || FORMATS.OPENAI;
|
||||
return createSSETransformStreamWithLogger(FORMATS.OPENAI_RESPONSES, codexTarget, provider, reqLogger, toolNameMap, model, connectionId, body, onStreamComplete, apiKey);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,22 @@
|
||||
// OpenAI-compatible embeddings adapter (most providers)
|
||||
import { bearerAuth } from "./_base.js";
|
||||
import { PROVIDER_MEDIA } from "../../providers/index.js";
|
||||
|
||||
// media-only providers without a registry file keep URL here; rest derive from registry media.embeddingConfig.baseUrl
|
||||
const ENDPOINTS = {
|
||||
openai: "https://api.openai.com/v1/embeddings",
|
||||
openrouter: "https://openrouter.ai/api/v1/embeddings",
|
||||
mistral: "https://api.mistral.ai/v1/embeddings",
|
||||
"voyage-ai": "https://api.voyageai.com/v1/embeddings",
|
||||
fireworks: "https://api.fireworks.ai/inference/v1/embeddings",
|
||||
together: "https://api.together.xyz/v1/embeddings",
|
||||
nebius: "https://api.tokenfactory.nebius.com/v1/embeddings",
|
||||
github: "https://models.github.ai/inference/embeddings",
|
||||
nvidia: "https://integrate.api.nvidia.com/v1/embeddings",
|
||||
"jina-ai": "https://api.jina.ai/v1/embeddings",
|
||||
"vercel-ai-gateway": "https://ai-gateway.vercel.sh/v1/embeddings",
|
||||
};
|
||||
|
||||
const embedCfg = (id) => PROVIDER_MEDIA[id]?.embeddingConfig || {};
|
||||
const embedUrl = (id) => embedCfg(id).baseUrl || ENDPOINTS[id];
|
||||
|
||||
export default function createOpenAIEmbeddingAdapter(providerId) {
|
||||
const cfg = embedCfg(providerId);
|
||||
return {
|
||||
buildUrl: () => ENDPOINTS[providerId],
|
||||
buildUrl: () => embedUrl(providerId),
|
||||
buildHeaders: (creds) => {
|
||||
const headers = { "Content-Type": "application/json", ...bearerAuth(creds) };
|
||||
if (providerId === "openrouter") {
|
||||
headers["HTTP-Referer"] = "https://endpoint-proxy.local";
|
||||
headers["X-Title"] = "Endpoint Proxy";
|
||||
}
|
||||
return headers;
|
||||
return { "Content-Type": "application/json", ...bearerAuth(creds), ...(cfg.headers || {}) };
|
||||
},
|
||||
buildBody: (model, { input, encoding_format, dimensions }) => {
|
||||
const body = { model, input };
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
// Codex (ChatGPT Plus/Pro) image generation via Responses API + SSE
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { nowSec } from "./_base.js";
|
||||
import { PROVIDERS } from "../../config/providers.js";
|
||||
|
||||
const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
||||
const CODEX_RESPONSES_URL = PROVIDERS["codex"].baseUrl;
|
||||
const CODEX_USER_AGENT = "codex_cli_rs/0.136.0";
|
||||
const CODEX_VERSION = "0.136.0";
|
||||
const CODEX_ORIGINATOR = "codex_cli_rs";
|
||||
|
||||
@@ -1,40 +1,37 @@
|
||||
// OpenAI-compatible adapter (used by openai, minimax, openrouter, recraft)
|
||||
import { PROVIDER_MEDIA } from "../../providers/index.js";
|
||||
|
||||
// media-only providers without a registry file keep their URL here; rest derive from registry media.imageConfig.baseUrl
|
||||
const ENDPOINTS = {
|
||||
openai: "https://api.openai.com/v1/images/generations",
|
||||
minimax: "https://api.minimaxi.com/v1/images/generations",
|
||||
openrouter: "https://openrouter.ai/api/v1/images/generations",
|
||||
recraft: "https://external.api.recraft.ai/v1/images/generations",
|
||||
"vercel-ai-gateway": "https://ai-gateway.vercel.sh/v1/images/generations",
|
||||
xai: "https://api.x.ai/v1/images/generations",
|
||||
};
|
||||
|
||||
const imageCfg = (id) => PROVIDER_MEDIA[id]?.imageConfig || {};
|
||||
const imageUrl = (id) => imageCfg(id).baseUrl || ENDPOINTS[id];
|
||||
|
||||
export default function createOpenAIAdapter(providerId) {
|
||||
const cfg = imageCfg(providerId);
|
||||
return {
|
||||
buildUrl: () => ENDPOINTS[providerId],
|
||||
buildUrl: () => imageUrl(providerId),
|
||||
buildHeaders: (creds) => {
|
||||
const headers = { "Content-Type": "application/json" };
|
||||
const headers = { "Content-Type": "application/json", ...(cfg.headers || {}) };
|
||||
const key = creds?.apiKey || creds?.accessToken;
|
||||
if (key) headers["Authorization"] = `Bearer ${key}`;
|
||||
if (providerId === "openrouter") {
|
||||
headers["HTTP-Referer"] = "https://endpoint-proxy.local";
|
||||
headers["X-Title"] = "Endpoint Proxy";
|
||||
}
|
||||
return headers;
|
||||
},
|
||||
buildBody: (model, body) => {
|
||||
const { prompt, n = 1, size = "1024x1024", quality, style, response_format } = body;
|
||||
// xAI only accepts prompt, model, n, response_format
|
||||
if (providerId === "xai") {
|
||||
const req = { model, prompt, n };
|
||||
if (response_format) req.response_format = response_format;
|
||||
const full = { model, prompt, n, size };
|
||||
if (quality) full.quality = quality;
|
||||
if (style) full.style = style;
|
||||
if (response_format) full.response_format = response_format;
|
||||
// bodyFields whitelist (e.g. xAI accepts only model/prompt/n/response_format)
|
||||
if (Array.isArray(cfg.bodyFields)) {
|
||||
const req = {};
|
||||
for (const f of cfg.bodyFields) if (full[f] !== undefined) req[f] = full[f];
|
||||
return req;
|
||||
}
|
||||
const req = { model, prompt, n, size };
|
||||
if (quality) req.quality = quality;
|
||||
if (style) req.style = style;
|
||||
if (response_format) req.response_format = response_format;
|
||||
return req;
|
||||
return full;
|
||||
},
|
||||
normalize: (responseBody) => responseBody,
|
||||
};
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
* Wrap chat-completions endpoints (with built-in web search) into the unified
|
||||
* /v1/search response format. Supports gemini, openai, xai, kimi, minimax, perplexity.
|
||||
*/
|
||||
import { PROVIDER_MEDIA } from "../../providers/index.js";
|
||||
|
||||
// Default search model derives from registry searchViaChat (single source)
|
||||
const searchModel = (id) => PROVIDER_MEDIA[id]?.searchViaChat?.defaultModel;
|
||||
|
||||
const REQUEST_TIMEOUT_MS = 15000;
|
||||
const DEFAULT_MAX_RESULTS = 10;
|
||||
@@ -45,7 +49,6 @@ const CHAT_SEARCH_CONFIG = {
|
||||
gemini: {
|
||||
endpoint: (model) =>
|
||||
`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`,
|
||||
defaultModel: "gemini-2.5-flash",
|
||||
buildBody: (query) => ({
|
||||
contents: [{ role: "user", parts: [{ text: query }] }],
|
||||
tools: [{ google_search: {} }]
|
||||
@@ -71,7 +74,6 @@ const CHAT_SEARCH_CONFIG = {
|
||||
|
||||
openai: {
|
||||
endpoint: () => "https://api.openai.com/v1/chat/completions",
|
||||
defaultModel: "gpt-4o-mini",
|
||||
buildBody: (query, model) => {
|
||||
const body = {
|
||||
model,
|
||||
@@ -106,7 +108,6 @@ const CHAT_SEARCH_CONFIG = {
|
||||
|
||||
xai: {
|
||||
endpoint: () => "https://api.x.ai/v1/responses",
|
||||
defaultModel: "grok-4.20-reasoning",
|
||||
buildBody: (query, model) => ({
|
||||
model,
|
||||
input: [{ role: "user", content: query }],
|
||||
@@ -146,7 +147,6 @@ const CHAT_SEARCH_CONFIG = {
|
||||
|
||||
kimi: {
|
||||
endpoint: () => "https://api.moonshot.cn/v1/chat/completions",
|
||||
defaultModel: "kimi-k2.5",
|
||||
buildBody: (query, model) => ({
|
||||
model,
|
||||
messages: [{ role: "user", content: query }],
|
||||
@@ -196,7 +196,6 @@ const CHAT_SEARCH_CONFIG = {
|
||||
|
||||
minimax: {
|
||||
endpoint: () => "https://api.minimaxi.com/v1/text/chatcompletion_v2",
|
||||
defaultModel: "MiniMax-M2.7",
|
||||
buildBody: (query, model) => ({
|
||||
model,
|
||||
messages: [{ role: "user", content: query }],
|
||||
@@ -255,7 +254,6 @@ const CHAT_SEARCH_CONFIG = {
|
||||
|
||||
perplexity: {
|
||||
endpoint: () => "https://api.perplexity.ai/chat/completions",
|
||||
defaultModel: "sonar",
|
||||
buildBody: (query, model) => ({
|
||||
model,
|
||||
messages: [{ role: "user", content: query }]
|
||||
@@ -324,7 +322,7 @@ export async function handleChatSearch({
|
||||
Number.isFinite(maxResults) && maxResults > 0
|
||||
? Math.floor(maxResults)
|
||||
: DEFAULT_MAX_RESULTS;
|
||||
const useModel = model || cfg.defaultModel;
|
||||
const useModel = model || searchModel(provider);
|
||||
const url = cfg.endpoint(useModel);
|
||||
const body = cfg.buildBody(query, useModel);
|
||||
const headers = cfg.buildHeaders(token);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// Gemini TTS — generateContent with AUDIO modality returns PCM L16, wrap as WAV
|
||||
import { Buffer } from "node:buffer";
|
||||
import { PROVIDER_MEDIA } from "../../providers/index.js";
|
||||
|
||||
const DEFAULT_MODEL = "gemini-2.5-flash-preview-tts";
|
||||
const KNOWN_MODELS = (PROVIDER_MEDIA["gemini"]?.ttsConfig?.models || []).map((m) => m.id);
|
||||
const DEFAULT_MODEL = KNOWN_MODELS[0];
|
||||
const DEFAULT_VOICE = "Kore";
|
||||
const KNOWN_MODELS = ["gemini-2.5-flash-preview-tts", "gemini-2.5-pro-preview-tts"];
|
||||
|
||||
// Parse "model/voice" — if input doesn't match a known TTS model, treat it as voice with default model
|
||||
function parseGeminiModelVoice(input) {
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
// OpenAI TTS — model format: "tts-model/voice"
|
||||
import { Buffer } from "node:buffer";
|
||||
import { PROVIDER_MEDIA } from "../../providers/index.js";
|
||||
|
||||
const DEFAULT_TTS_MODEL = PROVIDER_MEDIA["openai"]?.ttsConfig?.defaultModel;
|
||||
|
||||
export default {
|
||||
async synthesize(text, model, credentials) {
|
||||
if (!credentials?.apiKey) throw new Error("No OpenAI API key configured");
|
||||
|
||||
let ttsModel = "gpt-4o-mini-tts";
|
||||
let ttsModel = DEFAULT_TTS_MODEL;
|
||||
let voice = "alloy";
|
||||
if (model && model.includes("/")) {
|
||||
const parts = model.split("/");
|
||||
|
||||
98
open-sse/providers/REGISTRY_TEMPLATE.js
Normal file
98
open-sse/providers/REGISTRY_TEMPLATE.js
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* REGISTRY ENTRY TEMPLATE — copy into registry/{id}.js when adding a new provider.
|
||||
*
|
||||
* NOT imported by registry/index.js (lives outside registry/, static-import list ignores it).
|
||||
* Delete every block your provider does not need. Only `id` + `category` are required.
|
||||
* Field contract: see schema.js `@typedef RegistryEntry`. Runtime builders: providers/index.js.
|
||||
*
|
||||
* Quick recipes:
|
||||
* - Plain API-key LLM → id, alias, category:"apikey", display, transport{baseUrl}, models.
|
||||
* - OAuth LLM (device/PKCE)→ add oauth{...}; clientId/tokenUrl auto-inject into transport.
|
||||
* - Media-only (tts/stt/…) → drop `models`+chat baseUrl, fill media{serviceKinds, *Config}.
|
||||
*/
|
||||
|
||||
// import { CLAUDE_API_HEADERS, GOOGLE_OAUTH_CLIENT, OPENAI_COMPAT_BASE } from "./shared.js";
|
||||
|
||||
export default {
|
||||
// ── identity ────────────────────────────────────────────────────────────
|
||||
id: "example", // REQUIRED. kebab-case, unique.
|
||||
alias: "ex", // short key for PROVIDER_MODELS (defaults to id if omitted).
|
||||
aliases: ["example-ai"], // optional extra lookup tokens.
|
||||
uiAlias: "ex", // optional UI badge token.
|
||||
category: "apikey", // REQUIRED. "apikey" | "oauth" | "freeTier" | ...
|
||||
|
||||
// ── auth hints (only when relevant) ──────────────────────────────────────
|
||||
authType: "apikey", // "apikey" | "oauth".
|
||||
hasOAuth: false, // true if an OAuth flow exists.
|
||||
authModes: ["apikey"], // e.g. ["oauth","apikey"] when both supported.
|
||||
// noAuth: true, // local/free providers needing no credential.
|
||||
|
||||
// ── UI display ───────────────────────────────────────────────────────────
|
||||
display: {
|
||||
name: "Example",
|
||||
icon: "bolt", // material icon name OR textIcon fallback.
|
||||
color: "#3B82F6",
|
||||
textIcon: "EX",
|
||||
website: "https://example.com",
|
||||
notice: { apiKeyUrl: "https://example.com/keys" }, // or signupUrl.
|
||||
// deprecated: true, deprecationNotice: "RISK_NOTICE",
|
||||
// kindNotice: { image: "Requires paid plan." },
|
||||
// mediaPriority: 1,
|
||||
},
|
||||
|
||||
// ── transport (HTTP runtime) → PROVIDERS[id] ─────────────────────────────
|
||||
// Defaults applied: format:"openai". Declare ONLY what differs.
|
||||
transport: {
|
||||
baseUrl: "https://api.example.com/v1/chat/completions",
|
||||
format: "openai", // "openai" | "claude" | "gemini" | "openai-responses" | ...
|
||||
// validateUrl: "https://api.example.com/v1/models",
|
||||
// headers: { "User-Agent": "..." }, // static fingerprint (anti-ban) lives here.
|
||||
// auth: { header: "x-api-key", scheme: "raw" },
|
||||
// forceStream: true, urlSuffix: "?beta=true",
|
||||
// quirks: { dropOutputConfig: true },
|
||||
// retry: { 429: { attempts: 6 }, 503: { attempts: 3 } },
|
||||
// usage: { url: "https://api.example.com/usage" }, // or { urls: [...] } for multi-call.
|
||||
// modelsFetcher: { url: "https://api.example.com/models", type: "openai" }, // dynamic model list.
|
||||
// regions: { sgp: "https://sgp...", cn: "https://cn..." }, defaultRegion: "sgp",
|
||||
// NOTE: clientId/clientSecret/tokenUrl are injected from `oauth` — do NOT duplicate here.
|
||||
},
|
||||
|
||||
// ── oauth flow → PROVIDER_OAUTH[id] (omit for pure API-key) ───────────────
|
||||
// oauth: {
|
||||
// clientId: "app_xxx",
|
||||
// authorizeUrl: "https://auth.example.com/oauth/authorize", // PKCE/code flow.
|
||||
// tokenUrl: "https://auth.example.com/oauth/token",
|
||||
// deviceCodeUrl: "https://auth.example.com/device", // device-code flow.
|
||||
// refreshUrl: "https://auth.example.com/oauth/token",
|
||||
// scope: "openid profile offline_access", // or scopes: [...].
|
||||
// codeChallengeMethod: "S256",
|
||||
// redirectUri: "http://127.0.0.1:1455/auth/callback", fixedPort: 1455, callbackPath: "/auth/callback",
|
||||
// extraParams: { foo: "bar" },
|
||||
// refresh: { encoding: "form", scope: "openid offline_access" }, // "form" | "json".
|
||||
// refreshLeadMs: 300000,
|
||||
// userInfoUrl: "https://example.com/userinfo",
|
||||
// },
|
||||
|
||||
// ── media (non-LLM services) → PROVIDER_MEDIA[id] ────────────────────────
|
||||
// media: {
|
||||
// serviceKinds: ["llm", "tts", "stt", "embedding", "image", "imageToText", "webSearch"],
|
||||
// ttsConfig: { baseUrl: "...", authType: "apikey", authHeader: "bearer", format: "openai", defaultModel: "tts-1", models: [{ id: "tts-1", name: "TTS-1" }] },
|
||||
// sttConfig: { baseUrl: "...", authType: "apikey", authHeader: "bearer", format: "openai", models: [{ id: "whisper-1", name: "Whisper" }] },
|
||||
// embeddingConfig: { baseUrl: "...", authType: "apikey", authHeader: "bearer", models: [{ id: "emb-1", name: "Emb", dimensions: 1536 }] },
|
||||
// imageConfig: { baseUrl: "https://api.example.com/v1/images/generations" },
|
||||
// searchViaChat: { defaultModel: "ex-search", pricingUrl: "https://example.com/pricing" },
|
||||
// // hiddenKinds: ["image"],
|
||||
// },
|
||||
|
||||
// ── models (omit = no key; [] = explicit empty) ──────────────────────────
|
||||
models: [
|
||||
{ id: "example-large", name: "Example Large" },
|
||||
// { id: "example-img", name: "Example Image", type: "image", capabilities: ["text2img"], params: ["size"] },
|
||||
// { id: "example-emb", name: "Example Embed", type: "embedding" },
|
||||
],
|
||||
|
||||
// ── optional flags ───────────────────────────────────────────────────────
|
||||
// features: { usage: true },
|
||||
// thinkingConfig: { options: ["auto", "none", "low", "high"], defaultMode: "auto" },
|
||||
// passthroughModels: true,
|
||||
};
|
||||
@@ -1,21 +1,37 @@
|
||||
// Single source: build PROVIDERS + PROVIDER_MODELS from registry/{id}.js (transport + models co-located).
|
||||
import REGISTRY from "./registry/index.js";
|
||||
import { PROVIDER_DEFAULTS } from "./schema.js";
|
||||
import { normalizeModel } from "./models/schema.js";
|
||||
import { buildTtsProviderModels } from "../config/ttsModels.js";
|
||||
|
||||
// transport: re-apply shared default (format:"openai") like the old defineProviders()
|
||||
function buildTransport(transport) {
|
||||
// oauth block is canonical for these fields; inject into transport so executors reading
|
||||
// this.config.{clientId,clientSecret,tokenUrl} keep working without duplicating in transport
|
||||
const OAUTH_INJECT_FIELDS = ["clientId", "clientSecret", "tokenUrl"];
|
||||
|
||||
// transport: re-apply shared default (format:"openai") + inject oauth-canonical fields
|
||||
function buildTransport(transport, oauth) {
|
||||
const t = { ...transport };
|
||||
if (!t.format) t.format = PROVIDER_DEFAULTS.format;
|
||||
if (oauth) {
|
||||
for (const f of OAUTH_INJECT_FIELDS) {
|
||||
if (t[f] === undefined && oauth[f] !== undefined) t[f] = oauth[f];
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
export const PROVIDERS = {};
|
||||
export const PROVIDER_MODELS = {};
|
||||
export const PROVIDER_OAUTH = {};
|
||||
export const PROVIDER_MEDIA = {};
|
||||
for (const entry of REGISTRY) {
|
||||
if (entry.transport) PROVIDERS[entry.id] = buildTransport(entry.transport);
|
||||
if (entry.transport) PROVIDERS[entry.id] = buildTransport(entry.transport, entry.oauth);
|
||||
// models omitted (undefined) → provider has no model list key; [] is a valid explicit empty list
|
||||
if (entry.models !== undefined) PROVIDER_MODELS[entry.alias] = entry.models;
|
||||
// normalizeModel: accept terse "id" strings + derive name via regex when omitted
|
||||
// alias defaults to id (doc 01 §2); without fallback all alias-less providers collide on key `undefined`
|
||||
if (entry.models !== undefined) PROVIDER_MODELS[entry.alias || entry.id] = entry.models.map(normalizeModel);
|
||||
if (entry.oauth) PROVIDER_OAUTH[entry.id] = entry.oauth;
|
||||
if (entry.media) PROVIDER_MEDIA[entry.id] = entry.media;
|
||||
}
|
||||
|
||||
// TTS model/voice tables keyed by special names (openai-tts-models, ...), not provider ids
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Codex auto-generates a "-review" variant for each llm model (review quota family)
|
||||
const CODEX_REVIEW_SUFFIX = "-review";
|
||||
export const CODEX_REVIEW_SUFFIX = "-review";
|
||||
|
||||
export function withCodexReviewModels(models) {
|
||||
return models.flatMap((model) => {
|
||||
|
||||
33
open-sse/providers/models/namePatterns.js
Normal file
33
open-sse/providers/models/namePatterns.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Derive a display name from a model id when the entry omits `name` (mirrors PATTERN_PRICING).
|
||||
// Provider entries that ship their own `name` always win; this is only a fallback for terse entries.
|
||||
|
||||
// Capitalize a hyphen/space separated token group: "coder-plus" → "Coder Plus".
|
||||
function titleCase(s) {
|
||||
return s
|
||||
.split(/[-_\s]+/)
|
||||
.filter(Boolean)
|
||||
.map((w) => (/^\d/.test(w) ? w : w.charAt(0).toUpperCase() + w.slice(1)))
|
||||
.join(" ");
|
||||
}
|
||||
|
||||
// Ordered: first match wins. Keep specific patterns above generic ones.
|
||||
export const NAME_PATTERNS = [
|
||||
[/^kimi-k(\d+(?:\.\d+)?)(-thinking)?$/i, (m) => `Kimi K${m[1]}${m[2] ? " Thinking" : ""}`],
|
||||
[/^glm-(\d+(?:\.\d+)?)(v)?$/i, (m) => `GLM ${m[1]}${m[2] ? "V (Vision)" : ""}`],
|
||||
[/^minimax-m(\d+(?:\.\d+)?)$/i, (m) => `MiniMax M${m[1]}`],
|
||||
[/^gpt-(.+)$/i, (m) => `GPT ${titleCase(m[1])}`],
|
||||
[/^gemini-(.+)$/i, (m) => `Gemini ${titleCase(m[1])}`],
|
||||
[/^grok-(.+)$/i, (m) => `Grok ${titleCase(m[1])}`],
|
||||
[/^deepseek-(.+)$/i, (m) => `DeepSeek ${titleCase(m[1])}`],
|
||||
[/^qwen([\d.]+.*)$/i, (m) => `Qwen ${titleCase(m[1])}`],
|
||||
];
|
||||
|
||||
// id → display name (regex fallback → id verbatim)
|
||||
export function deriveModelName(id) {
|
||||
if (typeof id !== "string") return id;
|
||||
for (const [re, fn] of NAME_PATTERNS) {
|
||||
const m = id.match(re);
|
||||
if (m) return fn(m);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { deriveModelName } from "./namePatterns.js";
|
||||
|
||||
// Model defaults centralized (was scattered as `m.type || "llm"`, `quotaFamily || "normal"`, etc.)
|
||||
export const MODEL_DEFAULTS = {
|
||||
type: "llm",
|
||||
@@ -6,6 +8,14 @@ export const MODEL_DEFAULTS = {
|
||||
targetFormat: null
|
||||
};
|
||||
|
||||
// Normalize a registry model entry: accept terse "id" string, fill name via regex when omitted.
|
||||
// Override always wins (raw spread last); name falls back to regex → id.
|
||||
export function normalizeModel(raw) {
|
||||
const model = typeof raw === "string" ? { id: raw } : raw;
|
||||
if (model.name !== undefined) return model;
|
||||
return { ...model, name: deriveModelName(model.id) };
|
||||
}
|
||||
|
||||
// Resolve a single field with its default (keeps accessor call-sites one-liners)
|
||||
export function modelType(model) {
|
||||
return model?.type || MODEL_DEFAULTS.type;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { CLAUDE_CLI_SPOOF_HEADERS } from "../shared.js";
|
||||
|
||||
export default {
|
||||
id: "agentrouter",
|
||||
alias: "agentrouter",
|
||||
transport: {
|
||||
baseUrl: "https://agentrouter.org/v1/messages",
|
||||
format: "claude",
|
||||
headers: { ...CLAUDE_CLI_SPOOF_HEADERS }
|
||||
},
|
||||
models: [
|
||||
{ id: "claude-opus-4-6", name: "Claude 4.6 Opus" },
|
||||
{ id: "claude-haiku-4-5-20251001", name: "Claude 4.5 Haiku" },
|
||||
{ id: "glm-5.1", name: "GLM 5.1" },
|
||||
{ id: "deepseek-v3.2", name: "DeepSeek V3.2" }
|
||||
]
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
export default {
|
||||
"id": "ai21",
|
||||
"alias": "ai21",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.ai21.com/studio/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "jamba-large",
|
||||
"name": "Jamba 1.5 Large"
|
||||
},
|
||||
{
|
||||
"id": "jamba-mini",
|
||||
"name": "Jamba 1.5 Mini"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
export default {
|
||||
"id": "aimlapi",
|
||||
"alias": "aimlapi",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.aimlapi.com/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4o",
|
||||
"name": "GPT-4o"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini",
|
||||
"name": "GPT-4o Mini"
|
||||
},
|
||||
{
|
||||
"id": "claude-3-5-sonnet-20241022",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
},
|
||||
{
|
||||
"id": "gemini-2.0-flash-exp",
|
||||
"name": "Gemini 2.0 Flash"
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo",
|
||||
"name": "Llama 3.1 70B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,6 +1,18 @@
|
||||
|
||||
export default {
|
||||
"id": "alicode-intl",
|
||||
"alias": "alicode-intl",
|
||||
display: {
|
||||
"name": "Alibaba Intl",
|
||||
"icon": "cloud",
|
||||
"color": "#FF6A00",
|
||||
"textIcon": "ALi",
|
||||
"website": "https://modelstudio.console.alibabacloud.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://modelstudio.console.alibabacloud.com/?apiKey=1"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://coding-intl.dashscope.aliyuncs.com/v1/chat/completions",
|
||||
"headers": {}
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
|
||||
export default {
|
||||
"id": "alicode",
|
||||
"alias": "alicode",
|
||||
display: {
|
||||
"name": "Alibaba",
|
||||
"icon": "cloud",
|
||||
"color": "#FF6A00",
|
||||
"textIcon": "ALi",
|
||||
"website": "https://bailian.console.aliyun.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://bailian.console.aliyun.com/?apiKey=1"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1/chat/completions",
|
||||
"headers": {}
|
||||
|
||||
@@ -3,11 +3,25 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
export default {
|
||||
id: "anthropic",
|
||||
alias: "anthropic",
|
||||
display: {
|
||||
"name": "Anthropic",
|
||||
"icon": "smart_toy",
|
||||
"color": "#D97757",
|
||||
"textIcon": "AN",
|
||||
"website": "https://console.anthropic.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://console.anthropic.com/settings/keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
transport: {
|
||||
baseUrl: "https://api.anthropic.com/v1/messages",
|
||||
format: "claude",
|
||||
headers: { ...CLAUDE_API_HEADERS }
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "imageToText"]
|
||||
},
|
||||
models: [
|
||||
{ id: "claude-sonnet-4-20250514", name: "Claude Sonnet 4" },
|
||||
{ id: "claude-opus-4-20250514", name: "Claude Opus 4" },
|
||||
|
||||
@@ -4,6 +4,19 @@ import { ANTIGRAVITY_OAUTH_CLIENT } from "../shared.js";
|
||||
export default {
|
||||
id: "antigravity",
|
||||
alias: "ag",
|
||||
display: {
|
||||
"name": "Antigravity",
|
||||
"icon": "rocket_launch",
|
||||
"color": "#F59E0B",
|
||||
"website": "https://antigravity.google",
|
||||
"notice": {
|
||||
"signupUrl": "https://antigravity.google"
|
||||
},
|
||||
"deprecated": true,
|
||||
"deprecationNotice": "RISK_NOTICE"
|
||||
},
|
||||
category: "oauth",
|
||||
uiAlias: "ag",
|
||||
transport: {
|
||||
baseUrls: [
|
||||
"https://daily-cloudcode-pa.googleapis.com",
|
||||
@@ -11,8 +24,35 @@ export default {
|
||||
],
|
||||
format: "antigravity",
|
||||
headers: { "User-Agent": `antigravity/1.107.0 ${platform()}/${arch()}` },
|
||||
// 429/503 use computeRetryDelay hook (Retry-After header/body → cap, else backoff)
|
||||
retry: { 429: { attempts: 6 }, 503: { attempts: 3 } },
|
||||
usage: {
|
||||
quotaApiUrl: "https://cloudcode-pa.googleapis.com/v1internal:fetchAvailableModels",
|
||||
loadProjectApiUrl: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
|
||||
tokenUrl: "https://oauth2.googleapis.com/token"
|
||||
},
|
||||
...ANTIGRAVITY_OAUTH_CLIENT
|
||||
},
|
||||
oauth: {
|
||||
authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
tokenUrl: "https://oauth2.googleapis.com/token",
|
||||
userInfoUrl: "https://www.googleapis.com/oauth2/v1/userinfo",
|
||||
scopes: [
|
||||
"https://www.googleapis.com/auth/cloud-platform",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile",
|
||||
"https://www.googleapis.com/auth/cclog",
|
||||
"https://www.googleapis.com/auth/experimentsandconfigs"
|
||||
],
|
||||
apiEndpoint: "https://cloudcode-pa.googleapis.com",
|
||||
apiVersion: "v1internal",
|
||||
loadCodeAssistEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist",
|
||||
onboardUserEndpoint: "https://cloudcode-pa.googleapis.com/v1internal:onboardUser",
|
||||
loadCodeAssistUserAgent: "google-api-nodejs-client/9.15.1",
|
||||
loadCodeAssistApiClient: "google-cloud-sdk vscode_cloudshelleditor/0.1"
|
||||
,
|
||||
refreshLeadMs: 300000
|
||||
},
|
||||
models: [
|
||||
{ id: "gemini-3-flash-agent", name: "Gemini 3.5 Flash (High)" },
|
||||
{ id: "gemini-3.5-flash-low", name: "Gemini 3.5 Flash (Medium)" },
|
||||
@@ -23,5 +63,6 @@ export default {
|
||||
{ id: "claude-opus-4-6-thinking", name: "Claude Opus 4.6 (Thinking)" },
|
||||
{ id: "gpt-oss-120b-medium", name: "GPT-OSS 120B (Medium)" },
|
||||
{ id: "gemini-3-flash", name: "Gemini 3 Flash", thinking: false }
|
||||
]
|
||||
],
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
|
||||
export default {
|
||||
"id": "assemblyai",
|
||||
"alias": "assemblyai",
|
||||
display: {
|
||||
"name": "AssemblyAI",
|
||||
"icon": "record_voice_over",
|
||||
"color": "#0062FF",
|
||||
"textIcon": "AA",
|
||||
"website": "https://assemblyai.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://www.assemblyai.com/app/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "aai",
|
||||
authType: "apikey",
|
||||
aliases: ["aai"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.assemblyai.com/v1/audio/transcriptions"
|
||||
"baseUrl": "https://api.assemblyai.com/v1/audio/transcriptions",
|
||||
"validateUrl": "https://api.assemblyai.com/v1/account"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["stt"],
|
||||
sttConfig: { baseUrl: "https://api.assemblyai.com/v2/transcript", authType: "apikey", authHeader: "authorization", format: "assemblyai", models: [{ id: "best", name: "Best (Nano + Universal)" }, { id: "nano", name: "Nano (Fast)" }] }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
export default {
|
||||
"id": "azure",
|
||||
"alias": "azure",
|
||||
display: {
|
||||
"name": "Azure OpenAI",
|
||||
"icon": "cloud",
|
||||
"color": "#0078D4",
|
||||
"textIcon": "AZ",
|
||||
"website": "https://azure.microsoft.com/en-us/products/ai-services/openai-service",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://portal.azure.com/#view/Microsoft_Azure_ProjectOxford/CognitiveServicesHub/~/OpenAI"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
hasProviderSpecificData: true,
|
||||
"transport": {
|
||||
"baseUrl": "",
|
||||
"headers": {}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
export default {
|
||||
"id": "baseten",
|
||||
"alias": "baseten",
|
||||
"transport": {
|
||||
"baseUrl": "https://inference.baseten.co/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "deepseek-ai/DeepSeek-R1",
|
||||
"name": "DeepSeek R1"
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/Llama-3.3-70B-Instruct",
|
||||
"name": "Llama 3.3 70B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
export default {
|
||||
"id": "bazaarlink",
|
||||
"alias": "bazaarlink",
|
||||
"transport": {
|
||||
"baseUrl": "https://bazaarlink.ai/api/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "auto:free",
|
||||
"name": "Auto Free (Zero Cost)"
|
||||
},
|
||||
{
|
||||
"id": "auto",
|
||||
"name": "Auto (Best Model)"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,6 +1,21 @@
|
||||
|
||||
export default {
|
||||
"id": "black-forest-labs",
|
||||
"alias": "black-forest-labs",
|
||||
display: {
|
||||
"name": "Black Forest Labs",
|
||||
"icon": "image",
|
||||
"color": "#111827",
|
||||
"textIcon": "BF",
|
||||
"website": "https://blackforestlabs.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://api.bfl.ai"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "bfl",
|
||||
authType: "apikey",
|
||||
aliases: ["bfl"],
|
||||
"transport": null,
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
|
||||
export default {
|
||||
"id": "blackbox",
|
||||
"alias": "blackbox",
|
||||
display: {
|
||||
"name": "Blackbox AI",
|
||||
"icon": "smart_toy",
|
||||
"color": "#5B5FEF",
|
||||
"textIcon": "BB",
|
||||
"website": "https://blackbox.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://www.blackbox.ai/api-management"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "bb",
|
||||
aliases: ["bb"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.blackbox.ai/chat/completions"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,28 @@
|
||||
|
||||
export default {
|
||||
"id": "byteplus",
|
||||
"alias": "byteplus",
|
||||
display: {
|
||||
"name": "BytePlus ModelArk",
|
||||
"icon": "cloud",
|
||||
"color": "#2563EB",
|
||||
"textIcon": "BP",
|
||||
"website": "https://console.byteplus.com/ark",
|
||||
"notice": {
|
||||
"text": "Free credits for new accounts. Access to Seed 2.0, Kimi K2 Thinking, GLM 4.7, GPT-OSS-120B models.",
|
||||
"apiKeyUrl": "https://console.byteplus.com/ark/region:ark+ap-southeast-1/apiKey"
|
||||
}
|
||||
},
|
||||
category: "freeTier",
|
||||
uiAlias: "bpm",
|
||||
aliases: ["bpm"],
|
||||
"transport": {
|
||||
"baseUrl": "https://ark.ap-southeast.bytepluses.com/api/coding/v3/chat/completions",
|
||||
"headers": {}
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm"]
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "seed-2-0-pro-260328",
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "bytez",
|
||||
"alias": "bytez",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.bytez.com/models/v2"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "meta-llama/Llama-3.3-70B-Instruct",
|
||||
"name": "Llama 3.3 70B"
|
||||
},
|
||||
{
|
||||
"id": "mistralai/Mistral-7B-Instruct-v0.3",
|
||||
"name": "Mistral 7B v0.3"
|
||||
},
|
||||
{
|
||||
"id": "Qwen/Qwen2.5-72B-Instruct",
|
||||
"name": "Qwen 2.5 72B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,8 +1,21 @@
|
||||
export default {
|
||||
"id": "cerebras",
|
||||
"alias": "cerebras",
|
||||
display: {
|
||||
"name": "Cerebras",
|
||||
"icon": "memory",
|
||||
"color": "#FF4F00",
|
||||
"textIcon": "CB",
|
||||
"website": "https://www.cerebras.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://cloud.cerebras.ai/platform"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.cerebras.ai/v1/chat/completions"
|
||||
"baseUrl": "https://api.cerebras.ai/v1/chat/completions",
|
||||
"validateUrl": "https://api.cerebras.ai/v1/models",
|
||||
"quirks": { "dropClientMetadata": true }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,22 @@
|
||||
|
||||
export default {
|
||||
"id": "chutes",
|
||||
"alias": "chutes",
|
||||
display: {
|
||||
"name": "Chutes AI",
|
||||
"icon": "water_drop",
|
||||
"color": "#ffffffff",
|
||||
"textIcon": "CH",
|
||||
"website": "https://chutes.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://chutes.ai/app/api"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "ch",
|
||||
aliases: ["ch"],
|
||||
"transport": {
|
||||
"baseUrl": "https://llm.chutes.ai/v1/chat/completions"
|
||||
"baseUrl": "https://llm.chutes.ai/v1/chat/completions",
|
||||
"validateUrl": "https://llm.chutes.ai/v1/models"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,13 +3,40 @@ import { CLAUDE_CLI_SPOOF_HEADERS } from "../shared.js";
|
||||
export default {
|
||||
id: "claude",
|
||||
alias: "cc",
|
||||
display: {
|
||||
"name": "Claude Code",
|
||||
"icon": "smart_toy",
|
||||
"color": "#D97757",
|
||||
"website": "https://claude.ai",
|
||||
"notice": {
|
||||
"signupUrl": "https://claude.ai"
|
||||
},
|
||||
"deprecated": true,
|
||||
"deprecationNotice": "RISK_NOTICE"
|
||||
},
|
||||
category: "oauth",
|
||||
uiAlias: "cc",
|
||||
transport: {
|
||||
baseUrl: "https://api.anthropic.com/v1/messages",
|
||||
format: "claude",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_CLI_SPOOF_HEADERS },
|
||||
quirks: { cloakToolsOnOAuth: true },
|
||||
auth: {"apiKey":{"header":"x-api-key","scheme":"raw"},"oauth":{"header":"Authorization","scheme":"bearer"},"hooks":["claudeOverlay"]},
|
||||
usage: {
|
||||
oauthUrl: "https://api.anthropic.com/api/oauth/usage",
|
||||
orgUrl: "https://api.anthropic.com/v1/organizations/{org_id}/usage",
|
||||
settingsUrl: "https://api.anthropic.com/v1/settings"
|
||||
},
|
||||
},
|
||||
oauth: {
|
||||
clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
||||
tokenUrl: "https://api.anthropic.com/v1/oauth/token"
|
||||
authorizeUrl: "https://claude.ai/oauth/authorize",
|
||||
tokenUrl: "https://api.anthropic.com/v1/oauth/token",
|
||||
scopes: ["org:create_api_key", "user:profile", "user:inference"],
|
||||
codeChallengeMethod: "S256",
|
||||
refreshLeadMs: 14400000,
|
||||
refresh: {"encoding":"json"}
|
||||
},
|
||||
models: [
|
||||
{ id: "claude-opus-4-8", name: "Claude Opus 4.8" },
|
||||
@@ -19,5 +46,6 @@ export default {
|
||||
{ id: "claude-opus-4-5-20251101", name: "Claude 4.5 Opus" },
|
||||
{ id: "claude-sonnet-4-5-20250929", name: "Claude 4.5 Sonnet" },
|
||||
{ id: "claude-haiku-4-5-20251001", name: "Claude 4.5 Haiku" }
|
||||
]
|
||||
],
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
|
||||
export default {
|
||||
"id": "cline",
|
||||
"alias": "cl",
|
||||
display: {
|
||||
"name": "Cline",
|
||||
"icon": "smart_toy",
|
||||
"color": "#5B9BD5",
|
||||
"textIcon": "CL",
|
||||
"website": "https://cline.bot",
|
||||
"notice": {
|
||||
"signupUrl": "https://cline.bot"
|
||||
}
|
||||
},
|
||||
category: "oauth",
|
||||
uiAlias: "cl",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.cline.bot/api/v1/chat/completions",
|
||||
"headers": {
|
||||
@@ -8,6 +21,14 @@ export default {
|
||||
"X-Title": "Cline"
|
||||
},
|
||||
"tokenUrl": "https://api.cline.bot/api/v1/auth/token",
|
||||
"refreshUrl": "https://api.cline.bot/api/v1/auth/refresh",
|
||||
auth: {"combined":true,"header":"Authorization","scheme":"bearer","hooks":["clineHeaders"]},
|
||||
},
|
||||
"oauth": {
|
||||
"appBaseUrl": "https://app.cline.bot",
|
||||
"apiBaseUrl": "https://api.cline.bot",
|
||||
"authorizeUrl": "https://api.cline.bot/api/v1/auth/authorize",
|
||||
"tokenExchangeUrl": "https://api.cline.bot/api/v1/auth/token",
|
||||
"refreshUrl": "https://api.cline.bot/api/v1/auth/refresh"
|
||||
},
|
||||
"models": [
|
||||
@@ -43,5 +64,5 @@ export default {
|
||||
"id": "kwaipilot/kat-coder-pro",
|
||||
"name": "KAT Coder Pro"
|
||||
}
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
|
||||
export default {
|
||||
"id": "cloudflare-ai",
|
||||
"alias": "cloudflare-ai",
|
||||
display: {
|
||||
"name": "Cloudflare",
|
||||
"icon": "cloud",
|
||||
"color": "#F38020",
|
||||
"textIcon": "CF",
|
||||
"website": "https://developers.cloudflare.com/workers-ai/",
|
||||
"notice": {
|
||||
"text": "Workers AI free tier. Requires a Cloudflare API token and Account ID.",
|
||||
"apiKeyUrl": "https://dash.cloudflare.com/profile/api-tokens"
|
||||
}
|
||||
},
|
||||
category: "freeTier",
|
||||
uiAlias: "cf",
|
||||
hasProviderSpecificData: true,
|
||||
aliases: ["cf"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.cloudflare.com/client/v4/accounts/{accountId}/ai/v1/chat/completions"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "image"],
|
||||
hasProviderSpecificData: true
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "@cf/meta/llama-3.2-1b-instruct",
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
|
||||
export default {
|
||||
"id": "codebuddy",
|
||||
"alias": "codebuddy",
|
||||
"id": "codebuddy", display: { name: "CodeBuddy", icon: "smart_toy", color: "#006EFF", website: "https://copilot.tencent.com", notice: { signupUrl: "https://copilot.tencent.com" } },
|
||||
category: "oauth",
|
||||
"transport": {
|
||||
"baseUrl": "https://copilot.tencent.com/v1/chat/completions"
|
||||
"baseUrl": "https://copilot.tencent.com/v1/chat/completions",
|
||||
auth: {"combined":true,"header":"Authorization","scheme":"bearer"},
|
||||
},
|
||||
"oauth": {
|
||||
"baseUrl": "https://copilot.tencent.com",
|
||||
"stateUrl": "https://copilot.tencent.com/v2/plugin/auth/state",
|
||||
"tokenUrl": "https://copilot.tencent.com/v2/plugin/auth/token",
|
||||
"refreshUrl": "https://copilot.tencent.com/v2/plugin/auth/token/refresh",
|
||||
"userAgent": "CLI/2.63.2 CodeBuddy/2.63.2",
|
||||
"platform": "CLI",
|
||||
"pollInterval": 5000
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,6 +3,23 @@ import { withCodexReviewModels } from "../models/helpers.js";
|
||||
export default {
|
||||
id: "codex",
|
||||
alias: "cx",
|
||||
display: {
|
||||
"name": "OpenAI Codex",
|
||||
"icon": "code",
|
||||
"color": "#3B82F6",
|
||||
"website": "https://chatgpt.com/codex",
|
||||
"notice": {
|
||||
"signupUrl": "https://chatgpt.com/codex"
|
||||
},
|
||||
"deprecated": true,
|
||||
"deprecationNotice": "RISK_NOTICE",
|
||||
"kindNotice": {
|
||||
"image": "Requires a ChatGPT Plus (or higher) account. Free accounts are not supported for image generation."
|
||||
}
|
||||
},
|
||||
category: "oauth",
|
||||
uiAlias: "cx",
|
||||
thinkingConfig: {"options":["auto","none","low","medium","high"],"defaultMode":"auto"},
|
||||
transport: {
|
||||
baseUrl: "https://chatgpt.com/backend-api/codex/responses",
|
||||
format: "openai-responses",
|
||||
@@ -11,8 +28,32 @@ export default {
|
||||
"originator": "codex_cli_rs",
|
||||
"User-Agent": "codex_cli_rs/0.136.0"
|
||||
},
|
||||
usage: { url: "https://chatgpt.com/backend-api/wham/usage" }
|
||||
},
|
||||
oauth: {
|
||||
clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
|
||||
tokenUrl: "https://auth.openai.com/oauth/token"
|
||||
authorizeUrl: "https://auth.openai.com/oauth/authorize",
|
||||
tokenUrl: "https://auth.openai.com/oauth/token",
|
||||
scope: "openid profile email offline_access",
|
||||
codeChallengeMethod: "S256",
|
||||
fixedPort: 1455,
|
||||
callbackPath: "/auth/callback",
|
||||
extraParams: {
|
||||
id_token_add_organizations: "true",
|
||||
codex_cli_simplified_flow: "true",
|
||||
originator: "codex_cli_rs"
|
||||
}
|
||||
,
|
||||
refreshLeadMs: 432000000
|
||||
,
|
||||
refresh: {"encoding":"form","scope":"openid profile email offline_access"}
|
||||
,
|
||||
maxRefreshAgeMs: 691200000
|
||||
,
|
||||
trackRefreshAt: true
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "image"]
|
||||
},
|
||||
models: withCodexReviewModels([
|
||||
{ id: "gpt-5.5", name: "GPT 5.5" },
|
||||
@@ -27,5 +68,6 @@ export default {
|
||||
{ id: "gpt-5.5-image", name: "GPT 5.5 Image", type: "image", capabilities: ["text2img", "edit"], params: ["size", "quality", "background", "image_detail", "output_format"] },
|
||||
{ id: "gpt-5.4-image", name: "GPT 5.4 Image", type: "image", capabilities: ["text2img", "edit"], params: ["size", "quality", "background", "image_detail", "output_format"] },
|
||||
{ id: "gpt-5.3-image", name: "GPT 5.3 Image", type: "image", capabilities: ["text2img", "edit"], params: ["size", "quality", "background", "image_detail", "output_format"] }
|
||||
])
|
||||
]),
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
|
||||
export default {
|
||||
"id": "cohere",
|
||||
"alias": "cohere",
|
||||
display: {
|
||||
"name": "Cohere",
|
||||
"icon": "hub",
|
||||
"color": "#39594D",
|
||||
"textIcon": "CO",
|
||||
"website": "https://cohere.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://dashboard.cohere.com/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.cohere.ai/v1/chat/completions"
|
||||
"baseUrl": "https://api.cohere.ai/v1/chat/completions",
|
||||
"validateUrl": "https://api.cohere.ai/v1/models"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
|
||||
export default {
|
||||
"id": "comfyui",
|
||||
"alias": "comfyui",
|
||||
display: {
|
||||
"name": "ComfyUI",
|
||||
"icon": "account_tree",
|
||||
"color": "#4CAF50",
|
||||
"textIcon": "CF",
|
||||
"website": "https://github.com/comfyanonymous/ComfyUI"
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": null,
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
export default {
|
||||
"id": "commandcode",
|
||||
"alias": "commandcode",
|
||||
display: {
|
||||
"name": "Command Code",
|
||||
"icon": "smart_toy",
|
||||
"color": "#000000",
|
||||
"textIcon": "CC",
|
||||
"website": "https://commandcode.ai",
|
||||
"notice": {
|
||||
"text": "Use your CommandCode CLI API key (starts with user_...) from ~/.commandcode/auth.json or commandcode.ai/studio.",
|
||||
"apiKeyUrl": "https://commandcode.ai/studio"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "cmc",
|
||||
aliases: ["cmc"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.commandcode.ai/alpha/generate",
|
||||
"format": "commandcode",
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
export default {
|
||||
"id": "completions",
|
||||
"alias": "completions",
|
||||
"transport": {
|
||||
"baseUrl": "https://completions.me/api/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "claude-opus-4",
|
||||
"name": "Claude Opus 4"
|
||||
},
|
||||
{
|
||||
"id": "claude-sonnet-4",
|
||||
"name": "Claude Sonnet 4"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o",
|
||||
"name": "GPT-4o"
|
||||
},
|
||||
{
|
||||
"id": "gemini-2.0-flash",
|
||||
"name": "Gemini 2.0 Flash"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,6 +1,18 @@
|
||||
|
||||
export default {
|
||||
"id": "cursor",
|
||||
"alias": "cu",
|
||||
display: {
|
||||
"name": "Cursor IDE",
|
||||
"icon": "edit_note",
|
||||
"color": "#00D4AA",
|
||||
"website": "https://cursor.com",
|
||||
"notice": {
|
||||
"signupUrl": "https://cursor.com"
|
||||
}
|
||||
},
|
||||
category: "oauth",
|
||||
uiAlias: "cu",
|
||||
"transport": {
|
||||
"baseUrl": "https://api2.cursor.sh",
|
||||
"chatPath": "/aiserver.v1.ChatService/StreamUnifiedChatWithTools",
|
||||
@@ -13,6 +25,20 @@ export default {
|
||||
},
|
||||
"clientVersion": "3.1.0"
|
||||
},
|
||||
"oauth": {
|
||||
"apiEndpoint": "https://api2.cursor.sh",
|
||||
"chatEndpoint": "/aiserver.v1.ChatService/StreamUnifiedChatWithTools",
|
||||
"modelsEndpoint": "/aiserver.v1.AiService/GetDefaultModelNudgeData",
|
||||
"api3Endpoint": "https://api3.cursor.sh",
|
||||
"agentEndpoint": "https://agent.api5.cursor.sh",
|
||||
"agentNonPrivacyEndpoint": "https://agentn.api5.cursor.sh",
|
||||
"clientVersion": "3.1.0",
|
||||
"clientType": "ide",
|
||||
"dbKeys": {
|
||||
"accessToken": "cursorAuth/accessToken",
|
||||
"machineId": "storage.serviceMachineId"
|
||||
}
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "default",
|
||||
|
||||
@@ -1,9 +1,29 @@
|
||||
|
||||
export default {
|
||||
"id": "deepgram",
|
||||
"alias": "deepgram",
|
||||
display: {
|
||||
"name": "Deepgram",
|
||||
"icon": "mic",
|
||||
"color": "#13EF93",
|
||||
"textIcon": "DG",
|
||||
"website": "https://deepgram.com",
|
||||
"notice": {
|
||||
"text": "$200 free credit on signup (no card required). Aura-1: $0.015/1k chars, Aura-2: $0.030/1k chars (Pay-As-You-Go).",
|
||||
"apiKeyUrl": "https://console.deepgram.com/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "dg",
|
||||
authType: "apikey",
|
||||
aliases: ["dg"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.deepgram.com/v1/listen"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["stt"],
|
||||
sttConfig: { baseUrl: "https://api.deepgram.com/v1/listen", authType: "apikey", authHeader: "token", format: "deepgram", models: [{ id: "nova-3", name: "Nova 3" }, { id: "nova-2", name: "Nova 2" }, { id: "nova", name: "Nova" }] }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "nova-3",
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "deepinfra",
|
||||
"alias": "deepinfra",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.deepinfra.com/v1/openai/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "meta-llama/Meta-Llama-3.1-70B-Instruct",
|
||||
"name": "Llama 3.1 70B"
|
||||
},
|
||||
{
|
||||
"id": "deepseek-ai/DeepSeek-V3",
|
||||
"name": "DeepSeek V3"
|
||||
},
|
||||
{
|
||||
"id": "Qwen/Qwen2.5-72B-Instruct",
|
||||
"name": "Qwen 2.5 72B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,8 +1,24 @@
|
||||
|
||||
export default {
|
||||
"id": "deepseek",
|
||||
"alias": "deepseek",
|
||||
display: {
|
||||
"name": "DeepSeek",
|
||||
"icon": "bolt",
|
||||
"color": "#4D6BFE",
|
||||
"textIcon": "DS",
|
||||
"website": "https://deepseek.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://platform.deepseek.com/api_keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "ds",
|
||||
aliases: ["ds"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.deepseek.com/chat/completions"
|
||||
"baseUrl": "https://api.deepseek.com/chat/completions",
|
||||
"validateUrl": "https://api.deepseek.com/models",
|
||||
reasoningInject: { scope: "all" }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
export default {
|
||||
"id": "enally",
|
||||
"alias": "enally",
|
||||
"transport": {
|
||||
"baseUrl": "https://ai.enally.in/v1/chat/completions",
|
||||
"authHeader": "x-api-key"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4o",
|
||||
"name": "GPT-4o"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4o-mini",
|
||||
"name": "GPT-4o Mini"
|
||||
},
|
||||
{
|
||||
"id": "claude-3-5-sonnet",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,6 +1,21 @@
|
||||
|
||||
export default {
|
||||
"id": "fal-ai",
|
||||
"alias": "fal-ai",
|
||||
display: {
|
||||
"name": "Fal.ai",
|
||||
"icon": "image",
|
||||
"color": "#2563EB",
|
||||
"textIcon": "FL",
|
||||
"website": "https://fal.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://fal.ai/dashboard/keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "fal",
|
||||
authType: "apikey",
|
||||
aliases: ["fal"],
|
||||
"transport": null,
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
|
||||
export default {
|
||||
"id": "fireworks",
|
||||
"alias": "fireworks",
|
||||
display: {
|
||||
"name": "Fireworks AI",
|
||||
"icon": "local_fire_department",
|
||||
"color": "#7B2EF2",
|
||||
"textIcon": "FW",
|
||||
"website": "https://fireworks.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://fireworks.ai/account/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
authType: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.fireworks.ai/inference/v1/chat/completions"
|
||||
"baseUrl": "https://api.fireworks.ai/inference/v1/chat/completions",
|
||||
"validateUrl": "https://api.fireworks.ai/inference/v1/models"
|
||||
},
|
||||
media: {
|
||||
embeddingConfig: { baseUrl: "https://api.fireworks.ai/inference/v1/embeddings" }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
export default {
|
||||
"id": "freetheai",
|
||||
"alias": "freetheai",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.freetheai.xyz/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4o",
|
||||
"name": "GPT-4o"
|
||||
},
|
||||
{
|
||||
"id": "claude-3-5-sonnet",
|
||||
"name": "Claude 3.5 Sonnet"
|
||||
},
|
||||
{
|
||||
"id": "gemini-1.5-pro",
|
||||
"name": "Gemini 1.5 Pro"
|
||||
},
|
||||
{
|
||||
"id": "deepseek-chat",
|
||||
"name": "DeepSeek Chat"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -3,11 +3,42 @@ import { GOOGLE_OAUTH_CLIENT } from "../shared.js";
|
||||
export default {
|
||||
"id": "gemini-cli",
|
||||
"alias": "gc",
|
||||
display: {
|
||||
"name": "Gemini CLI",
|
||||
"icon": "terminal",
|
||||
"color": "#4285F4",
|
||||
"website": "https://github.com/google-gemini/gemini-cli",
|
||||
"notice": {
|
||||
"signupUrl": "https://github.com/google-gemini/gemini-cli"
|
||||
},
|
||||
"deprecated": true,
|
||||
"deprecationNotice": "RISK_NOTICE"
|
||||
},
|
||||
category: "free",
|
||||
uiAlias: "gc",
|
||||
"transport": {
|
||||
"baseUrl": "https://cloudcode-pa.googleapis.com/v1internal",
|
||||
"format": "gemini-cli",
|
||||
cliVersion: "0.34.0",
|
||||
apiClient: "google-genai-sdk/1.41.0 gl-node/v22.19.0",
|
||||
usage: {
|
||||
quotaUrl: "https://cloudcode-pa.googleapis.com/v1internal:retrieveUserQuota",
|
||||
loadCodeAssistUrl: "https://cloudcode-pa.googleapis.com/v1internal:loadCodeAssist"
|
||||
},
|
||||
...GOOGLE_OAUTH_CLIENT
|
||||
},
|
||||
"oauth": {
|
||||
"authorizeUrl": "https://accounts.google.com/o/oauth2/v2/auth",
|
||||
"tokenUrl": "https://oauth2.googleapis.com/token",
|
||||
"userInfoUrl": "https://www.googleapis.com/oauth2/v1/userinfo",
|
||||
"scopes": [
|
||||
"https://www.googleapis.com/auth/cloud-platform",
|
||||
"https://www.googleapis.com/auth/userinfo.email",
|
||||
"https://www.googleapis.com/auth/userinfo.profile"
|
||||
]
|
||||
,
|
||||
refresh: {"encoding":"form"}
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "gemini-3-flash-preview",
|
||||
@@ -17,5 +48,6 @@ export default {
|
||||
"id": "gemini-3-pro-preview",
|
||||
"name": "Gemini 3 Pro Preview"
|
||||
}
|
||||
]
|
||||
],
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -3,10 +3,30 @@ import { GOOGLE_OAUTH_CLIENT } from "../shared.js";
|
||||
export default {
|
||||
"id": "gemini",
|
||||
"alias": "gemini",
|
||||
display: {
|
||||
"name": "Gemini",
|
||||
"icon": "diamond",
|
||||
"color": "#4285F4",
|
||||
"textIcon": "GE",
|
||||
"website": "https://ai.google.dev",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://aistudio.google.com/app/apikey"
|
||||
},
|
||||
"mediaPriority": 1
|
||||
},
|
||||
category: "freeTier",
|
||||
"transport": {
|
||||
"baseUrl": "https://generativelanguage.googleapis.com/v1beta/models",
|
||||
"format": "gemini",
|
||||
...GOOGLE_OAUTH_CLIENT
|
||||
...GOOGLE_OAUTH_CLIENT,
|
||||
auth: {"apiKey":{"header":"x-goog-api-key","scheme":"raw"},"oauth":{"header":"Authorization","scheme":"bearer"}},
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "embedding", "image", "imageToText", "webSearch", "tts", "stt"],
|
||||
ttsConfig: { baseUrl: "https://generativelanguage.googleapis.com/v1beta/models", authType: "apikey", authHeader: "key", format: "gemini-tts", models: [{ id: "gemini-2.5-flash-preview-tts", name: "Gemini 2.5 Flash TTS" }, { id: "gemini-2.5-pro-preview-tts", name: "Gemini 2.5 Pro TTS" }] },
|
||||
sttConfig: { baseUrl: "https://generativelanguage.googleapis.com/v1beta/models", authType: "apikey", authHeader: "key", format: "gemini-stt", models: [{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro (Best)" }, { id: "gemini-2.5-flash", name: "Gemini 2.5 Flash" }, { id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash Lite (Cheapest)" }, { id: "gemini-2.0-flash", name: "Gemini 2.0 Flash" }] },
|
||||
embeddingConfig: { baseUrl: "https://generativelanguage.googleapis.com/v1beta/models", authType: "apikey", authHeader: "key", models: [{ id: "text-embedding-004", name: "Text Embedding 004", dimensions: 768 }, { id: "embedding-001", name: "Embedding 001", dimensions: 768 }] },
|
||||
searchViaChat: { defaultModel: "gemini-2.5-flash", pricingUrl: "https://ai.google.dev/pricing", freeTier: "Free tier: 15 RPM, 1M tokens/day on gemini-2.5-flash via AI Studio." }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
@@ -119,5 +139,5 @@ export default {
|
||||
"prompt"
|
||||
]
|
||||
}
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
|
||||
export default {
|
||||
"id": "github",
|
||||
"alias": "gh",
|
||||
display: {
|
||||
"name": "GitHub Copilot",
|
||||
"icon": "code",
|
||||
"color": "#333333",
|
||||
"website": "https://github.com/features/copilot",
|
||||
"notice": {
|
||||
"signupUrl": "https://github.com/features/copilot"
|
||||
},
|
||||
"deprecated": true,
|
||||
"deprecationNotice": "RISK_NOTICE"
|
||||
},
|
||||
category: "oauth",
|
||||
uiAlias: "gh",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.githubcopilot.com/chat/completions",
|
||||
"responsesUrl": "https://api.githubcopilot.com/responses",
|
||||
@@ -16,7 +30,25 @@ export default {
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
"clientId": "Iv1.b507a08c87ecfe98"
|
||||
copilot: { vscodeVersion: "1.110.0", chatVersion: "0.38.0", userAgent: "GitHubCopilotChat/0.38.0", apiVersion: "2025-04-01" },
|
||||
usage: { url: "https://api.github.com/copilot_internal/user" }
|
||||
},
|
||||
"oauth": {
|
||||
"clientId": "Iv1.b507a08c87ecfe98",
|
||||
"authorizeUrl": "https://github.com/login/oauth/authorize",
|
||||
"deviceCodeUrl": "https://github.com/login/device/code",
|
||||
"tokenUrl": "https://github.com/login/oauth/access_token",
|
||||
"userInfoUrl": "https://api.github.com/user",
|
||||
"scopes": "read:user",
|
||||
"apiVersion": "2022-11-28",
|
||||
"copilotTokenUrl": "https://api.github.com/copilot_internal/v2/token",
|
||||
"userAgent": "GitHubCopilotChat/0.26.7",
|
||||
"editorVersion": "vscode/1.85.0",
|
||||
"editorPluginVersion": "copilot-chat/0.26.7"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "embedding"],
|
||||
embeddingConfig: { baseUrl: "https://models.github.ai/inference/embeddings", authType: "apikey", authHeader: "bearer", models: [{ id: "text-embedding-3-small", name: "Text Embedding 3 Small (GitHub)", dimensions: 1536 }, { id: "text-embedding-3-large", name: "Text Embedding 3 Large (GitHub)", dimensions: 3072 }] }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
@@ -125,5 +157,6 @@ export default {
|
||||
"name": "Text Embedding 3 Large (GitHub)",
|
||||
"type": "embedding"
|
||||
}
|
||||
]
|
||||
],
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
|
||||
export default {
|
||||
"id": "gitlab",
|
||||
"alias": "gitlab",
|
||||
"id": "gitlab", display: { name: "GitLab Duo", icon: "code", color: "#FC6D26", textIcon: "GL", website: "https://gitlab.com", notice: { signupUrl: "https://gitlab.com" } },
|
||||
category: "oauth",
|
||||
"transport": {
|
||||
"baseUrl": "https://gitlab.com/api/v4/chat/completions"
|
||||
"baseUrl": "https://gitlab.com/api/v4/chat/completions",
|
||||
auth: {"combined":true,"header":"Authorization","scheme":"bearer"},
|
||||
},
|
||||
"oauth": {
|
||||
"defaultBaseUrl": "https://gitlab.com",
|
||||
"authorizeUrlPath": "/oauth/authorize",
|
||||
"tokenUrlPath": "/oauth/token",
|
||||
"userInfoUrlPath": "/api/v4/user",
|
||||
"scope": "api read_user",
|
||||
"codeChallengeMethod": "S256"
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "glhf",
|
||||
"alias": "glhf",
|
||||
"transport": {
|
||||
"baseUrl": "https://glhf.chat/api/openai/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "hf:meta-llama/Meta-Llama-3.1-405B-Instruct",
|
||||
"name": "Llama 3.1 405B"
|
||||
},
|
||||
{
|
||||
"id": "hf:meta-llama/Meta-Llama-3.1-70B-Instruct",
|
||||
"name": "Llama 3.1 70B"
|
||||
},
|
||||
{
|
||||
"id": "hf:Qwen/Qwen2.5-72B-Instruct",
|
||||
"name": "Qwen 2.5 72B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,9 +1,22 @@
|
||||
|
||||
export default {
|
||||
"id": "glm-cn",
|
||||
"alias": "glm-cn",
|
||||
display: {
|
||||
"name": "GLM (China)",
|
||||
"icon": "code",
|
||||
"color": "#DC2626",
|
||||
"textIcon": "GC",
|
||||
"website": "https://open.bigmodel.cn",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://open.bigmodel.cn/usercenter/apikeys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://open.bigmodel.cn/api/coding/paas/v4/chat/completions",
|
||||
"headers": {}
|
||||
"headers": {},
|
||||
usage: { url: "https://open.bigmodel.cn/api/monitor/usage/quota/limit" }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
@@ -26,5 +39,6 @@ export default {
|
||||
"id": "glm-4.5-air",
|
||||
"name": "GLM-4.5-Air"
|
||||
}
|
||||
]
|
||||
],
|
||||
features: {"usage":true,"usageApikey":true},
|
||||
};
|
||||
|
||||
@@ -3,16 +3,30 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
export default {
|
||||
id: "glm",
|
||||
alias: "glm",
|
||||
display: {
|
||||
"name": "GLM Coding",
|
||||
"icon": "code",
|
||||
"color": "#2563EB",
|
||||
"textIcon": "GL",
|
||||
"website": "https://open.bigmodel.cn",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://open.bigmodel.cn/usercenter/apikeys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
transport: {
|
||||
baseUrl: "https://api.z.ai/api/anthropic/v1/messages",
|
||||
format: "claude",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS }
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
auth: { "combined": true, "header": "x-api-key", "scheme": "raw" },
|
||||
usage: { url: "https://api.z.ai/api/monitor/usage/quota/limit" },
|
||||
},
|
||||
models: [
|
||||
{ id: "glm-5.1", name: "GLM 5.1" },
|
||||
{ id: "glm-5", name: "GLM 5" },
|
||||
{ id: "glm-4.7", name: "GLM 4.7" },
|
||||
{ id: "glm-4.6v", name: "GLM 4.6V (Vision)" }
|
||||
]
|
||||
],
|
||||
features: { "usage": true, "usageApikey": true },
|
||||
};
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
|
||||
export default {
|
||||
"id": "grok-web",
|
||||
"alias": "grok-web",
|
||||
display: {
|
||||
"name": "Grok Web (Subscription)",
|
||||
"icon": "auto_awesome",
|
||||
"color": "#1DA1F2",
|
||||
"textIcon": "GW",
|
||||
"website": "https://grok.com"
|
||||
},
|
||||
category: "webCookie",
|
||||
uiAlias: "gw",
|
||||
authType: "cookie",
|
||||
authHint: "Paste your sso= cookie value from grok.com",
|
||||
passthroughModels: true,
|
||||
aliases: ["gw"],
|
||||
"transport": {
|
||||
"baseUrl": "https://grok.com/rest/app-chat/conversations/new",
|
||||
"format": "grok-web",
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
|
||||
export default {
|
||||
"id": "groq",
|
||||
"alias": "groq",
|
||||
display: {
|
||||
"name": "Groq",
|
||||
"icon": "speed",
|
||||
"color": "#F55036",
|
||||
"textIcon": "GQ",
|
||||
"website": "https://groq.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://console.groq.com/keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.groq.com/openai/v1/chat/completions"
|
||||
"baseUrl": "https://api.groq.com/openai/v1/chat/completions",
|
||||
"validateUrl": "https://api.groq.com/openai/v1/models"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "imageToText", "stt"],
|
||||
sttConfig: { baseUrl: "https://api.groq.com/openai/v1/audio/transcriptions", authType: "apikey", authHeader: "bearer", format: "openai", models: [{ id: "whisper-large-v3", name: "Whisper Large v3" }, { id: "whisper-large-v3-turbo", name: "Whisper Large v3 Turbo" }, { id: "distil-whisper-large-v3-en", name: "Distil Whisper Large v3 EN" }] }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
|
||||
export default {
|
||||
"id": "huggingface",
|
||||
"alias": "huggingface",
|
||||
display: {
|
||||
"name": "HuggingFace",
|
||||
"icon": "face",
|
||||
"color": "#FFD21E",
|
||||
"textIcon": "HF",
|
||||
"website": "https://huggingface.co",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://huggingface.co/settings/tokens"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "hf",
|
||||
authType: "apikey",
|
||||
hiddenKinds: ["tts"],
|
||||
aliases: ["hf"],
|
||||
"transport": null,
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,24 @@
|
||||
|
||||
export default {
|
||||
"id": "hyperbolic",
|
||||
"alias": "hyperbolic",
|
||||
display: {
|
||||
"name": "Hyperbolic",
|
||||
"icon": "bolt",
|
||||
"color": "#00D4FF",
|
||||
"textIcon": "HY",
|
||||
"website": "https://hyperbolic.xyz",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://app.hyperbolic.xyz/settings"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "hyp",
|
||||
authType: "apikey",
|
||||
aliases: ["hyp"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.hyperbolic.xyz/v1/chat/completions"
|
||||
"baseUrl": "https://api.hyperbolic.xyz/v1/chat/completions",
|
||||
"validateUrl": "https://api.hyperbolic.xyz/v1/models"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
|
||||
export default {
|
||||
"id": "iflow",
|
||||
"alias": "if",
|
||||
"id": "iflow", alias: "if", display: { name: "iFlow AI", icon: "water_drop", color: "#6366F1", website: "https://iflow.cn", notice: { signupUrl: "https://iflow.cn" } },
|
||||
category: "oauth",
|
||||
"transport": {
|
||||
"baseUrl": "https://apis.iflow.cn/v1/chat/completions",
|
||||
"headers": {
|
||||
"User-Agent": "iFlow-Cli"
|
||||
},
|
||||
}
|
||||
},
|
||||
"oauth": {
|
||||
"clientId": "10009311001",
|
||||
"clientSecret": "4Z3YjXycVsQvyGF1etiNlIBB4RsqSDtW",
|
||||
"authorizeUrl": "https://iflow.cn/oauth",
|
||||
"tokenUrl": "https://iflow.cn/oauth/token",
|
||||
"authUrl": "https://iflow.cn/oauth"
|
||||
"userInfoUrl": "https://iflow.cn/api/oauth/getUserInfo",
|
||||
"extraParams": {
|
||||
"loginMethod": "phone",
|
||||
"type": "phone"
|
||||
}
|
||||
,
|
||||
refreshLeadMs: 86400000
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,104 +1,75 @@
|
||||
// Auto-generated: static imports of all registry entries
|
||||
import p0 from './agentrouter.js';
|
||||
import p1 from './ai21.js';
|
||||
import p2 from './aimlapi.js';
|
||||
import p3 from './alicode-intl.js';
|
||||
import p4 from './alicode.js';
|
||||
import p5 from './anthropic.js';
|
||||
import p6 from './antigravity.js';
|
||||
import p7 from './assemblyai.js';
|
||||
import p8 from './azure.js';
|
||||
import p9 from './baseten.js';
|
||||
import p10 from './bazaarlink.js';
|
||||
import p11 from './black-forest-labs.js';
|
||||
import p12 from './blackbox.js';
|
||||
import p13 from './byteplus.js';
|
||||
import p14 from './bytez.js';
|
||||
import p15 from './cerebras.js';
|
||||
import p16 from './chutes.js';
|
||||
import p17 from './claude.js';
|
||||
import p18 from './cline.js';
|
||||
import p19 from './cloudflare-ai.js';
|
||||
import p20 from './codebuddy.js';
|
||||
import p21 from './codex.js';
|
||||
import p22 from './cohere.js';
|
||||
import p23 from './comfyui.js';
|
||||
import p24 from './commandcode.js';
|
||||
import p25 from './completions.js';
|
||||
import p26 from './cursor.js';
|
||||
import p27 from './deepgram.js';
|
||||
import p28 from './deepinfra.js';
|
||||
import p29 from './deepseek.js';
|
||||
import p30 from './enally.js';
|
||||
import p31 from './fal-ai.js';
|
||||
import p32 from './fireworks.js';
|
||||
import p33 from './freetheai.js';
|
||||
import p34 from './gemini-cli.js';
|
||||
import p35 from './gemini.js';
|
||||
import p36 from './github.js';
|
||||
import p37 from './gitlab.js';
|
||||
import p38 from './glhf.js';
|
||||
import p39 from './glm-cn.js';
|
||||
import p40 from './glm.js';
|
||||
import p41 from './grok-web.js';
|
||||
import p42 from './groq.js';
|
||||
import p43 from './huggingface.js';
|
||||
import p44 from './hyperbolic.js';
|
||||
import p45 from './iflow.js';
|
||||
import p46 from './inference-net.js';
|
||||
import p47 from './kilocode.js';
|
||||
import p48 from './kimi-coding.js';
|
||||
import p49 from './kimi.js';
|
||||
import p50 from './kiro.js';
|
||||
import p51 from './kluster.js';
|
||||
import p52 from './lepton.js';
|
||||
import p53 from './llm7.js';
|
||||
import p54 from './longcat.js';
|
||||
import p55 from './mimo-free.js';
|
||||
import p56 from './minimax-cn.js';
|
||||
import p57 from './minimax.js';
|
||||
import p58 from './mistral.js';
|
||||
import p59 from './mmf.js';
|
||||
import p60 from './modal.js';
|
||||
import p61 from './morph.js';
|
||||
import p62 from './nanobanana.js';
|
||||
import p63 from './nebius.js';
|
||||
import p64 from './nlpcloud.js';
|
||||
import p65 from './nous-research.js';
|
||||
import p66 from './novita.js';
|
||||
import p67 from './nscale.js';
|
||||
import p68 from './nvidia.js';
|
||||
import p69 from './ollama-local.js';
|
||||
import p70 from './ollama.js';
|
||||
import p71 from './openai.js';
|
||||
import p72 from './opencode-go.js';
|
||||
import p73 from './opencode.js';
|
||||
import p74 from './openrouter.js';
|
||||
import p75 from './perplexity-web.js';
|
||||
import p76 from './perplexity.js';
|
||||
import p77 from './predibase.js';
|
||||
import p78 from './publicai.js';
|
||||
import p79 from './puter.js';
|
||||
import p80 from './qoder.js';
|
||||
import p81 from './qwen.js';
|
||||
import p82 from './recraft.js';
|
||||
import p83 from './reka.js';
|
||||
import p84 from './runwayml.js';
|
||||
import p85 from './sambanova.js';
|
||||
import p86 from './scaleway.js';
|
||||
import p87 from './sdwebui.js';
|
||||
import p88 from './siliconflow.js';
|
||||
import p89 from './stability-ai.js';
|
||||
import p90 from './together.js';
|
||||
import p91 from './uncloseai.js';
|
||||
import p92 from './vercel-ai-gateway.js';
|
||||
import p93 from './vertex-partner.js';
|
||||
import p94 from './vertex.js';
|
||||
import p95 from './volcengine-ark.js';
|
||||
import p96 from './voyage-ai.js';
|
||||
import p97 from './xai.js';
|
||||
import p98 from './xiaomi-mimo.js';
|
||||
import p99 from './xiaomi-tokenplan.js';
|
||||
import p0 from './alicode-intl.js';
|
||||
import p1 from './alicode.js';
|
||||
import p2 from './anthropic.js';
|
||||
import p3 from './antigravity.js';
|
||||
import p4 from './assemblyai.js';
|
||||
import p5 from './azure.js';
|
||||
import p6 from './black-forest-labs.js';
|
||||
import p7 from './blackbox.js';
|
||||
import p8 from './byteplus.js';
|
||||
import p9 from './cerebras.js';
|
||||
import p10 from './chutes.js';
|
||||
import p11 from './claude.js';
|
||||
import p12 from './cline.js';
|
||||
import p13 from './cloudflare-ai.js';
|
||||
import p14 from './codebuddy.js';
|
||||
import p15 from './codex.js';
|
||||
import p16 from './cohere.js';
|
||||
import p17 from './comfyui.js';
|
||||
import p18 from './commandcode.js';
|
||||
import p19 from './cursor.js';
|
||||
import p20 from './deepgram.js';
|
||||
import p21 from './deepseek.js';
|
||||
import p22 from './fal-ai.js';
|
||||
import p23 from './fireworks.js';
|
||||
import p24 from './gemini-cli.js';
|
||||
import p25 from './gemini.js';
|
||||
import p26 from './github.js';
|
||||
import p27 from './gitlab.js';
|
||||
import p28 from './glm-cn.js';
|
||||
import p29 from './glm.js';
|
||||
import p30 from './grok-web.js';
|
||||
import p31 from './groq.js';
|
||||
import p32 from './huggingface.js';
|
||||
import p33 from './hyperbolic.js';
|
||||
import p34 from './iflow.js';
|
||||
import p35 from './kilocode.js';
|
||||
import p36 from './kimi-coding.js';
|
||||
import p37 from './kimi.js';
|
||||
import p38 from './kiro.js';
|
||||
import p39 from './mimo-free.js';
|
||||
import p40 from './minimax-cn.js';
|
||||
import p41 from './minimax.js';
|
||||
import p42 from './mistral.js';
|
||||
import p43 from './mmf.js';
|
||||
import p44 from './nanobanana.js';
|
||||
import p45 from './nebius.js';
|
||||
import p46 from './nvidia.js';
|
||||
import p47 from './ollama-local.js';
|
||||
import p48 from './ollama.js';
|
||||
import p49 from './openai.js';
|
||||
import p50 from './opencode-go.js';
|
||||
import p51 from './opencode.js';
|
||||
import p52 from './openrouter.js';
|
||||
import p53 from './perplexity-web.js';
|
||||
import p54 from './perplexity.js';
|
||||
import p55 from './qoder.js';
|
||||
import p56 from './qwen.js';
|
||||
import p57 from './recraft.js';
|
||||
import p58 from './runwayml.js';
|
||||
import p59 from './sdwebui.js';
|
||||
import p60 from './siliconflow.js';
|
||||
import p61 from './stability-ai.js';
|
||||
import p62 from './together.js';
|
||||
import p63 from './vercel-ai-gateway.js';
|
||||
import p64 from './vertex-partner.js';
|
||||
import p65 from './vertex.js';
|
||||
import p66 from './volcengine-ark.js';
|
||||
import p67 from './voyage-ai.js';
|
||||
import p68 from './xai.js';
|
||||
import p69 from './xiaomi-mimo.js';
|
||||
import p70 from './xiaomi-tokenplan.js';
|
||||
|
||||
export default [
|
||||
p0,
|
||||
@@ -171,34 +142,5 @@ export default [
|
||||
p67,
|
||||
p68,
|
||||
p69,
|
||||
p70,
|
||||
p71,
|
||||
p72,
|
||||
p73,
|
||||
p74,
|
||||
p75,
|
||||
p76,
|
||||
p77,
|
||||
p78,
|
||||
p79,
|
||||
p80,
|
||||
p81,
|
||||
p82,
|
||||
p83,
|
||||
p84,
|
||||
p85,
|
||||
p86,
|
||||
p87,
|
||||
p88,
|
||||
p89,
|
||||
p90,
|
||||
p91,
|
||||
p92,
|
||||
p93,
|
||||
p94,
|
||||
p95,
|
||||
p96,
|
||||
p97,
|
||||
p98,
|
||||
p99
|
||||
p70
|
||||
];
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "inference-net",
|
||||
"alias": "inference-net",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.inference.net/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "meta-llama/llama-3.3-70b-instruct/fp-16",
|
||||
"name": "Llama 3.3 70B"
|
||||
},
|
||||
{
|
||||
"id": "deepseek/deepseek-v3-0324",
|
||||
"name": "DeepSeek V3"
|
||||
},
|
||||
{
|
||||
"id": "mistralai/mistral-nemo-12b-instruct/fp-16",
|
||||
"name": "Mistral Nemo 12B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,9 +1,28 @@
|
||||
|
||||
export default {
|
||||
"id": "kilocode",
|
||||
"alias": "kc",
|
||||
display: {
|
||||
"name": "Kilo Code",
|
||||
"icon": "code",
|
||||
"color": "#FF6B35",
|
||||
"textIcon": "KC",
|
||||
"website": "https://kilocode.ai",
|
||||
"notice": {
|
||||
"signupUrl": "https://kilocode.ai"
|
||||
}
|
||||
},
|
||||
category: "oauth",
|
||||
uiAlias: "kc",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.kilo.ai/api/openrouter/chat/completions",
|
||||
"headers": {}
|
||||
"headers": {},
|
||||
auth: {"combined":true,"header":"Authorization","scheme":"bearer","hooks":["kilocodeOrg"]},
|
||||
},
|
||||
"oauth": {
|
||||
"apiBaseUrl": "https://api.kilo.ai",
|
||||
"initiateUrl": "https://api.kilo.ai/api/device-auth/codes",
|
||||
"pollUrlBase": "https://api.kilo.ai/api/device-auth/codes"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
@@ -38,5 +57,5 @@ export default {
|
||||
"id": "deepseek/deepseek-reasoner",
|
||||
"name": "DeepSeek Reasoner"
|
||||
}
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@ import { CLAUDE_API_HEADERS, KIMI_CODING_BASE_URL } from "../shared.js";
|
||||
export default {
|
||||
id: "kimi-coding",
|
||||
alias: "kmc",
|
||||
display: { name: "Kimi Coding", icon: "psychology", color: "#1E40AF", textIcon: "KC", website: "https://kimi.moonshot.cn", notice: { signupUrl: "https://kimi.moonshot.cn" } },
|
||||
category: "oauth",
|
||||
transport: {
|
||||
baseUrl: KIMI_CODING_BASE_URL,
|
||||
format: "claude",
|
||||
@@ -10,12 +12,20 @@ export default {
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
clientId: "17e5f671-d194-4dfb-9706-5516cb48c098",
|
||||
tokenUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
refreshUrl: "https://auth.kimi.com/api/oauth/token"
|
||||
refreshUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
auth: {"combined":true,"header":"x-api-key","scheme":"raw","hooks":["kimiHeaders"]},
|
||||
},
|
||||
oauth: {
|
||||
deviceCodeUrl: "https://auth.kimi.com/api/oauth/device_authorization",
|
||||
tokenUrl: "https://auth.kimi.com/api/oauth/token"
|
||||
,
|
||||
refreshLeadMs: 300000
|
||||
},
|
||||
models: [
|
||||
{ id: "kimi-k2.6", name: "Kimi K2.6" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "kimi-k2.5-thinking", name: "Kimi K2.5 Thinking" },
|
||||
{ id: "kimi-latest", name: "Kimi Latest" }
|
||||
]
|
||||
],
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -3,16 +3,32 @@ import { CLAUDE_API_HEADERS, KIMI_CODING_BASE_URL } from "../shared.js";
|
||||
export default {
|
||||
id: "kimi",
|
||||
alias: "kimi",
|
||||
display: {
|
||||
"name": "Kimi",
|
||||
"icon": "psychology",
|
||||
"color": "#1E3A8A",
|
||||
"textIcon": "KM",
|
||||
"website": "https://kimi.moonshot.cn",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://platform.moonshot.ai/console/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
transport: {
|
||||
baseUrl: KIMI_CODING_BASE_URL,
|
||||
format: "claude",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS }
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
auth: {"combined":true,"header":"x-api-key","scheme":"raw"},
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "webSearch"],
|
||||
searchViaChat: { defaultModel: "kimi-k2.5", pricingUrl: "https://platform.moonshot.ai/docs/pricing/chat" }
|
||||
},
|
||||
models: [
|
||||
{ id: "kimi-k2.6", name: "Kimi K2.6" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "kimi-k2.5-thinking", name: "Kimi K2.5 Thinking" },
|
||||
{ id: "kimi-latest", name: "Kimi Latest" }
|
||||
]
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
|
||||
export default {
|
||||
"id": "kiro",
|
||||
"alias": "kr",
|
||||
display: {
|
||||
"name": "Kiro AI",
|
||||
"icon": "psychology_alt",
|
||||
"color": "#FF6B35",
|
||||
"website": "https://kiro.dev",
|
||||
"notice": {
|
||||
"signupUrl": "https://kiro.dev"
|
||||
},
|
||||
"deprecated": true,
|
||||
"deprecationNotice": "RISK_NOTICE"
|
||||
},
|
||||
category: "free",
|
||||
uiAlias: "kr",
|
||||
"transport": {
|
||||
"baseUrl": "https://runtime.us-east-1.kiro.dev/generateAssistantResponse",
|
||||
"baseUrls": [
|
||||
@@ -20,7 +34,29 @@ export default {
|
||||
"X-Amz-User-Agent": "aws-sdk-js/3.0.0 kiro-ide/1.0.0"
|
||||
},
|
||||
"tokenUrl": "https://prod.us-east-1.auth.desktop.kiro.dev/refreshToken",
|
||||
"authUrl": "https://prod.us-east-1.auth.desktop.kiro.dev"
|
||||
"authUrl": "https://prod.us-east-1.auth.desktop.kiro.dev",
|
||||
usage: {
|
||||
cwHost: "https://codewhisperer.us-east-1.amazonaws.com",
|
||||
qHost: "https://q.us-east-1.amazonaws.com",
|
||||
limitsPath: "/getUsageLimits"
|
||||
}
|
||||
},
|
||||
"oauth": {
|
||||
"ssoOidcEndpoint": "https://oidc.us-east-1.amazonaws.com",
|
||||
"registerClientUrl": "https://oidc.us-east-1.amazonaws.com/client/register",
|
||||
"deviceAuthUrl": "https://oidc.us-east-1.amazonaws.com/device_authorization",
|
||||
"tokenUrl": "https://oidc.us-east-1.amazonaws.com/token",
|
||||
"startUrl": "https://view.awsapps.com/start",
|
||||
"clientName": "kiro-oauth-client",
|
||||
"clientType": "public",
|
||||
"scopes": ["codewhisperer:completions", "codewhisperer:analysis", "codewhisperer:conversations"],
|
||||
"grantTypes": ["urn:ietf:params:oauth:grant-type:device_code", "refresh_token"],
|
||||
"issuerUrl": "https://identitycenter.amazonaws.com/ssoins-722374e8c3c8e6c6",
|
||||
"socialAuthEndpoint": "https://prod.us-east-1.auth.desktop.kiro.dev",
|
||||
"socialLoginUrl": "https://prod.us-east-1.auth.desktop.kiro.dev/login",
|
||||
"socialTokenUrl": "https://prod.us-east-1.auth.desktop.kiro.dev/oauth/token",
|
||||
"socialRefreshUrl": "https://prod.us-east-1.auth.desktop.kiro.dev/refreshToken",
|
||||
"authMethods": ["builder-id", "idc", "google", "github", "import"]
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
@@ -79,5 +115,6 @@ export default {
|
||||
"id": "claude-haiku-4.5-thinking-agentic",
|
||||
"name": "Claude Haiku 4.5 (Thinking + Agentic)"
|
||||
}
|
||||
]
|
||||
],
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
export default {
|
||||
"id": "kluster",
|
||||
"alias": "kluster",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.kluster.ai/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "deepseek-ai/DeepSeek-R1",
|
||||
"name": "DeepSeek R1"
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
"name": "Llama 4 Maverick"
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/Llama-4-Scout-17B-16E-Instruct",
|
||||
"name": "Llama 4 Scout"
|
||||
},
|
||||
{
|
||||
"id": "Qwen/Qwen3-235B-A22B-Instruct",
|
||||
"name": "Qwen3 235B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
export default {
|
||||
"id": "lepton",
|
||||
"alias": "lepton",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.lepton.ai/api/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "llama3-1-405b",
|
||||
"name": "Llama 3.1 405B"
|
||||
},
|
||||
{
|
||||
"id": "llama3-1-70b",
|
||||
"name": "Llama 3.1 70B"
|
||||
},
|
||||
{
|
||||
"id": "llama3-1-8b",
|
||||
"name": "Llama 3.1 8B"
|
||||
},
|
||||
{
|
||||
"id": "mixtral-8x7b",
|
||||
"name": "Mixtral 8x7B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "llm7",
|
||||
"alias": "llm7",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.llm7.io/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-4o-mini",
|
||||
"name": "GPT-4o Mini"
|
||||
},
|
||||
{
|
||||
"id": "gpt-4.1-mini",
|
||||
"name": "GPT-4.1 Mini"
|
||||
},
|
||||
{
|
||||
"id": "gemini-1.5-flash",
|
||||
"name": "Gemini 1.5 Flash"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "longcat",
|
||||
"alias": "longcat",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.longcat.chat/openai/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "LongCat-Flash-Chat",
|
||||
"name": "LongCat Flash Chat"
|
||||
},
|
||||
{
|
||||
"id": "LongCat-Flash-Thinking",
|
||||
"name": "LongCat Flash Thinking"
|
||||
},
|
||||
{
|
||||
"id": "LongCat-Flash-Lite",
|
||||
"name": "LongCat Flash Lite"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,10 +1,26 @@
|
||||
|
||||
export default {
|
||||
"id": "mimo-free",
|
||||
"alias": "mmf",
|
||||
display: {
|
||||
"name": "MiMo Code Free",
|
||||
"icon": "smart_toy",
|
||||
"color": "#FF6900",
|
||||
"textIcon": "MF"
|
||||
},
|
||||
category: "free",
|
||||
uiAlias: "mmf",
|
||||
noAuth: true,
|
||||
passthroughModels: true,
|
||||
"transport": {
|
||||
"baseUrl": "https://api.xiaomimimo.com/api/free-ai/openai/chat",
|
||||
"noAuth": true
|
||||
},
|
||||
media: {
|
||||
noAuth: true,
|
||||
passthroughModels: true,
|
||||
modelsFetcher: { url: "https://models.dev/api.json", type: "mimo-free" }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "mimo-auto",
|
||||
|
||||
@@ -3,16 +3,36 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
export default {
|
||||
id: "minimax-cn",
|
||||
alias: "minimax-cn",
|
||||
display: {
|
||||
"name": "Minimax (China)",
|
||||
"icon": "memory",
|
||||
"color": "#DC2626",
|
||||
"textIcon": "MC",
|
||||
"website": "https://www.minimaxi.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://platform.minimaxi.com/user-center/basic-information/interface-key"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
transport: {
|
||||
baseUrl: "https://api.minimaxi.com/anthropic/v1/messages",
|
||||
format: "claude",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS }
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
quirks: { dropOutputConfig: true },
|
||||
reasoningInject: { scope: "all" },
|
||||
auth: { "combined": true, "header": "x-api-key", "scheme": "raw" },
|
||||
usage: { urls: ["https://www.minimaxi.com/v1/api/openplatform/coding_plan/remains", "https://api.minimaxi.com/v1/api/openplatform/coding_plan/remains"] },
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "tts"],
|
||||
ttsConfig: { baseUrl: "https://api.minimaxi.com/v1/t2a_v2", authType: "apikey", authHeader: "bearer", format: "minimax-tts", models: [{ id: "speech-2.8-hd", name: "Speech 2.8 HD" }, { id: "speech-2.8-turbo", name: "Speech 2.8 Turbo" }, { id: "speech-2.6-hd", name: "Speech 2.6 HD" }, { id: "speech-2.6-turbo", name: "Speech 2.6 Turbo" }, { id: "speech-02-hd", name: "Speech 02 HD" }, { id: "speech-02-turbo", name: "Speech 02 Turbo" }, { id: "speech-01-hd", name: "Speech 01 HD" }, { id: "speech-01-turbo", name: "Speech 01 Turbo" }] }
|
||||
},
|
||||
models: [
|
||||
{ id: "MiniMax-M3", name: "MiniMax M3", targetFormat: "claude" },
|
||||
{ id: "MiniMax-M2.7", name: "MiniMax M2.7" },
|
||||
{ id: "MiniMax-M2.5", name: "MiniMax M2.5" },
|
||||
{ id: "MiniMax-M2.1", name: "MiniMax M2.1" }
|
||||
]
|
||||
],
|
||||
features: { "usage": true, "usageApikey": true },
|
||||
};
|
||||
|
||||
@@ -3,11 +3,32 @@ import { CLAUDE_API_HEADERS } from "../shared.js";
|
||||
export default {
|
||||
id: "minimax",
|
||||
alias: "minimax",
|
||||
display: {
|
||||
"name": "Minimax Coding",
|
||||
"icon": "memory",
|
||||
"color": "#7C3AED",
|
||||
"textIcon": "MM",
|
||||
"website": "https://www.minimaxi.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://platform.minimaxi.com/user-center/basic-information/interface-key"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
transport: {
|
||||
baseUrl: "https://api.minimax.io/anthropic/v1/messages",
|
||||
format: "claude",
|
||||
urlSuffix: "?beta=true",
|
||||
headers: { ...CLAUDE_API_HEADERS }
|
||||
headers: { ...CLAUDE_API_HEADERS },
|
||||
quirks: { dropOutputConfig: true },
|
||||
reasoningInject: { scope: "all" },
|
||||
auth: {"combined":true,"header":"x-api-key","scheme":"raw"},
|
||||
usage: { urls: ["https://www.minimax.io/v1/token_plan/remains", "https://api.minimax.io/v1/api/openplatform/coding_plan/remains"] },
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "image", "imageToText", "webSearch", "tts"],
|
||||
ttsConfig: { baseUrl: "https://api.minimax.io/v1/t2a_v2", authType: "apikey", authHeader: "bearer", format: "minimax-tts", models: [{ id: "speech-2.8-hd", name: "Speech 2.8 HD" }, { id: "speech-2.8-turbo", name: "Speech 2.8 Turbo" }, { id: "speech-2.6-hd", name: "Speech 2.6 HD" }, { id: "speech-2.6-turbo", name: "Speech 2.6 Turbo" }, { id: "speech-02-hd", name: "Speech 02 HD" }, { id: "speech-02-turbo", name: "Speech 02 Turbo" }, { id: "speech-01-hd", name: "Speech 01 HD" }, { id: "speech-01-turbo", name: "Speech 01 Turbo" }] },
|
||||
searchViaChat: { defaultModel: "MiniMax-M2.7", pricingUrl: "https://www.minimaxi.com/document/price" },
|
||||
imageConfig: { baseUrl: "https://api.minimaxi.com/v1/images/generations" }
|
||||
},
|
||||
models: [
|
||||
{ id: "MiniMax-M3", name: "MiniMax M3", targetFormat: "claude" },
|
||||
@@ -15,5 +36,6 @@ export default {
|
||||
{ id: "MiniMax-M2.5", name: "MiniMax M2.5" },
|
||||
{ id: "MiniMax-M2.1", name: "MiniMax M2.1" },
|
||||
{ id: "minimax-image-01", name: "MiniMax Image 01", type: "image", params: ["n", "size", "response_format"] }
|
||||
]
|
||||
],
|
||||
features: {"usage":true,"usageApikey":true},
|
||||
};
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
export default {
|
||||
"id": "mistral",
|
||||
"alias": "mistral",
|
||||
display: {
|
||||
"name": "Mistral",
|
||||
"icon": "air",
|
||||
"color": "#FF7000",
|
||||
"textIcon": "MI",
|
||||
"website": "https://mistral.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://console.mistral.ai/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.mistral.ai/v1/chat/completions"
|
||||
"baseUrl": "https://api.mistral.ai/v1/chat/completions",
|
||||
"validateUrl": "https://api.mistral.ai/v1/models",
|
||||
"quirks": { "dropClientMetadata": true }
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "imageToText", "embedding"],
|
||||
embeddingConfig: { baseUrl: "https://api.mistral.ai/v1/embeddings", authType: "apikey", authHeader: "bearer", models: [{ id: "mistral-embed", name: "Mistral Embed", dimensions: 1024 }] }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
|
||||
export default {
|
||||
"id": "mmf",
|
||||
"alias": "mmf",
|
||||
"id": "mmf", display: { name: "MMF", icon: "hub", color: "#6366F1", textIcon: "MF" },
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.xiaomimimo.com/api/free-ai/openai/chat",
|
||||
"noAuth": true
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
export default {
|
||||
"id": "modal",
|
||||
"alias": "modal",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.modal.com/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "auto",
|
||||
"name": "Auto (User-hosted)"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
export default {
|
||||
"id": "morph",
|
||||
"alias": "morph",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.morphllm.com/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "morph-v3-large",
|
||||
"name": "Morph V3 Large"
|
||||
},
|
||||
{
|
||||
"id": "morph-v3-fast",
|
||||
"name": "Morph V3 Fast"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,8 +1,24 @@
|
||||
|
||||
export default {
|
||||
"id": "nanobanana",
|
||||
"alias": "nanobanana",
|
||||
display: {
|
||||
"name": "NanoBanana API",
|
||||
"icon": "extension",
|
||||
"color": "#FFD700",
|
||||
"textIcon": "🍌",
|
||||
"website": "https://nanobananaapi.ai",
|
||||
"notice": {
|
||||
"text": "3rd-party proxy for Google Nano Banana (Gemini 2.5/3 Flash Image). For official, use Gemini provider.",
|
||||
"apiKeyUrl": "https://nanobananaapi.ai/dashboard"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "nb",
|
||||
aliases: ["nb"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.nanobananaapi.ai/v1/chat/completions"
|
||||
"baseUrl": "https://api.nanobananaapi.ai/v1/chat/completions",
|
||||
"validateUrl": "https://api.nanobananaapi.ai/v1/models"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
|
||||
export default {
|
||||
"id": "nebius",
|
||||
"alias": "nebius",
|
||||
display: {
|
||||
"name": "Nebius AI",
|
||||
"icon": "cloud",
|
||||
"color": "#6C5CE7",
|
||||
"textIcon": "NB",
|
||||
"website": "https://nebius.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://studio.nebius.com/settings/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
authType: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.studio.nebius.ai/v1/chat/completions"
|
||||
"baseUrl": "https://api.studio.nebius.ai/v1/chat/completions",
|
||||
"validateUrl": "https://api.studio.nebius.ai/v1/models"
|
||||
},
|
||||
media: {
|
||||
embeddingConfig: { baseUrl: "https://api.tokenfactory.nebius.com/v1/embeddings" }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "nlpcloud",
|
||||
"alias": "nlpcloud",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.nlpcloud.io/v1/gpu/chatbot"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "chatdolphin",
|
||||
"name": "ChatDolphin"
|
||||
},
|
||||
{
|
||||
"id": "dolphin",
|
||||
"name": "Dolphin"
|
||||
},
|
||||
{
|
||||
"id": "finetuned-llama-3-70b",
|
||||
"name": "Llama 3 70B (Finetuned)"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
export default {
|
||||
"id": "nous-research",
|
||||
"alias": "nous-research",
|
||||
"transport": {
|
||||
"baseUrl": "https://inference-api.nousresearch.com/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "Hermes-4-405B",
|
||||
"name": "Hermes 4 405B"
|
||||
},
|
||||
{
|
||||
"id": "Hermes-4-70B",
|
||||
"name": "Hermes 4 70B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
export default {
|
||||
"id": "novita",
|
||||
"alias": "novita",
|
||||
"transport": {
|
||||
"baseUrl": "https://api.novita.ai/v3/openai/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "deepseek/deepseek-r1",
|
||||
"name": "DeepSeek R1"
|
||||
},
|
||||
{
|
||||
"id": "deepseek/deepseek-v3",
|
||||
"name": "DeepSeek V3"
|
||||
},
|
||||
{
|
||||
"id": "meta-llama/llama-3.3-70b-instruct",
|
||||
"name": "Llama 3.3 70B"
|
||||
},
|
||||
{
|
||||
"id": "qwen/qwen-2.5-72b-instruct",
|
||||
"name": "Qwen 2.5 72B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,17 +0,0 @@
|
||||
export default {
|
||||
"id": "nscale",
|
||||
"alias": "nscale",
|
||||
"transport": {
|
||||
"baseUrl": "https://inference.api.nscale.com/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "meta-llama/Llama-3.3-70B-Instruct",
|
||||
"name": "Llama 3.3 70B"
|
||||
},
|
||||
{
|
||||
"id": "Qwen/Qwen2.5-Coder-32B-Instruct",
|
||||
"name": "Qwen 2.5 Coder 32B"
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -1,8 +1,27 @@
|
||||
|
||||
export default {
|
||||
"id": "nvidia",
|
||||
"alias": "nvidia",
|
||||
display: {
|
||||
"name": "NVIDIA NIM",
|
||||
"icon": "developer_board",
|
||||
"color": "#76B900",
|
||||
"textIcon": "NV",
|
||||
"website": "https://developer.nvidia.com/nim",
|
||||
"notice": {
|
||||
"text": "Free access for NVIDIA Developer Program members (prototyping & testing).",
|
||||
"apiKeyUrl": "https://build.nvidia.com/settings/api-keys"
|
||||
}
|
||||
},
|
||||
category: "freeTier",
|
||||
"transport": {
|
||||
"baseUrl": "https://integrate.api.nvidia.com/v1/chat/completions"
|
||||
"baseUrl": "https://integrate.api.nvidia.com/v1/chat/completions",
|
||||
"validateUrl": "https://integrate.api.nvidia.com/v1/models"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "tts", "embedding"],
|
||||
ttsConfig: { baseUrl: "https://integrate.api.nvidia.com/v1/audio/speech", authType: "apikey", authHeader: "bearer", format: "nvidia-tts", models: [{ id: "fastpitch", name: "FastPitch" }, { id: "tacotron2", name: "Tacotron2" }] },
|
||||
embeddingConfig: { baseUrl: "https://integrate.api.nvidia.com/v1/embeddings", authType: "apikey", authHeader: "bearer", models: [{ id: "nvidia/nv-embedqa-e5-v5", name: "NV EmbedQA E5 v5", dimensions: 1024 }] }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
|
||||
export default {
|
||||
"id": "ollama-local",
|
||||
"alias": "ollama-local",
|
||||
display: {
|
||||
"name": "Ollama Local",
|
||||
"icon": "cloud",
|
||||
"color": "#ffffffff",
|
||||
"textIcon": "OL",
|
||||
"website": "https://ollama.com"
|
||||
},
|
||||
category: "apikey",
|
||||
"transport": {
|
||||
"baseUrl": "http://localhost:11434/api/chat",
|
||||
"format": "ollama"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm"]
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
|
||||
export default {
|
||||
"id": "ollama",
|
||||
"alias": "ollama",
|
||||
display: {
|
||||
"name": "Ollama Cloud",
|
||||
"icon": "cloud",
|
||||
"color": "#ffffffff",
|
||||
"textIcon": "OL",
|
||||
"website": "https://ollama.com",
|
||||
"notice": {
|
||||
"text": "Free tier: light usage, 1 cloud model at a time (limits reset every 5h & 7d). Pro $20/mo · Max $100/mo.",
|
||||
"apiKeyUrl": "https://ollama.com/settings/keys"
|
||||
}
|
||||
},
|
||||
category: "freeTier",
|
||||
"transport": {
|
||||
"baseUrl": "https://ollama.com/api/chat",
|
||||
"validateUrl": "https://ollama.com/api/tags",
|
||||
"format": "ollama"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm"]
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-oss:120b",
|
||||
@@ -30,5 +47,6 @@ export default {
|
||||
"id": "qwen3.5",
|
||||
"name": "Qwen3.5"
|
||||
}
|
||||
]
|
||||
],
|
||||
features: {"usage":true},
|
||||
};
|
||||
|
||||
@@ -1,10 +1,30 @@
|
||||
export default {
|
||||
"id": "openai",
|
||||
"alias": "openai",
|
||||
display: {
|
||||
"name": "OpenAI",
|
||||
"icon": "auto_awesome",
|
||||
"color": "#10A37F",
|
||||
"textIcon": "OA",
|
||||
"website": "https://platform.openai.com",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://platform.openai.com/api-keys"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
thinkingConfig: {"options":["auto","none","low","medium","high"],"defaultMode":"auto"},
|
||||
"transport": {
|
||||
"baseUrl": "https://api.openai.com/v1/chat/completions",
|
||||
"forceStream": true
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "embedding", "tts", "stt", "image", "imageToText", "webSearch"],
|
||||
ttsConfig: { baseUrl: "https://api.openai.com/v1/audio/speech", authType: "apikey", authHeader: "bearer", format: "openai", defaultModel: "gpt-4o-mini-tts", models: [{ id: "tts-1", name: "TTS-1" }, { id: "tts-1-hd", name: "TTS-1 HD" }, { id: "gpt-4o-mini-tts", name: "GPT-4o Mini TTS" }] },
|
||||
sttConfig: { baseUrl: "https://api.openai.com/v1/audio/transcriptions", authType: "apikey", authHeader: "bearer", format: "openai", models: [{ id: "whisper-1", name: "Whisper 1" }, { id: "gpt-4o-transcribe", name: "GPT-4o Transcribe" }, { id: "gpt-4o-mini-transcribe", name: "GPT-4o Mini Transcribe" }] },
|
||||
embeddingConfig: { baseUrl: "https://api.openai.com/v1/embeddings", authType: "apikey", authHeader: "bearer", models: [{ id: "text-embedding-3-small", name: "Text Embedding 3 Small", dimensions: 1536 }, { id: "text-embedding-3-large", name: "Text Embedding 3 Large", dimensions: 3072 }, { id: "text-embedding-ada-002", name: "Text Embedding Ada 002", dimensions: 1536 }] },
|
||||
imageConfig: { baseUrl: "https://api.openai.com/v1/images/generations" },
|
||||
searchViaChat: { defaultModel: "gpt-4o-mini", pricingUrl: "https://openai.com/api/pricing" }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "gpt-5.4",
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
|
||||
export default {
|
||||
"id": "opencode-go",
|
||||
"alias": "opencode-go",
|
||||
display: {
|
||||
"name": "OpenCode Go",
|
||||
"icon": "terminal",
|
||||
"color": "#E87040",
|
||||
"textIcon": "OC",
|
||||
"website": "https://opencode.ai/auth",
|
||||
"notice": {
|
||||
"text": "OpenCode Go subscription: $5/mo (then 0/mo). Access to Kimi, GLM, Qwen, MiMo, MiniMax models.",
|
||||
"apiKeyUrl": "https://opencode.ai/auth"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "ocg",
|
||||
aliases: ["ocg"],
|
||||
"transport": {
|
||||
"baseUrl": "https://opencode.ai/zen/go/v1/chat/completions",
|
||||
"headers": {}
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
|
||||
export default {
|
||||
"id": "opencode",
|
||||
"alias": "oc",
|
||||
display: {
|
||||
"name": "OpenCode Free",
|
||||
"icon": "terminal",
|
||||
"color": "#E87040",
|
||||
"textIcon": "OC"
|
||||
},
|
||||
category: "free",
|
||||
uiAlias: "oc",
|
||||
noAuth: true,
|
||||
passthroughModels: true,
|
||||
"transport": {
|
||||
"baseUrl": "https://opencode.ai",
|
||||
"headers": {
|
||||
@@ -8,5 +19,10 @@ export default {
|
||||
},
|
||||
"noAuth": true
|
||||
},
|
||||
media: {
|
||||
noAuth: true,
|
||||
passthroughModels: true,
|
||||
modelsFetcher: { url: "https://opencode.ai/zen/v1/models", type: "opencode-free" }
|
||||
},
|
||||
"models": []
|
||||
};
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
|
||||
export default {
|
||||
"id": "openrouter",
|
||||
"alias": "openrouter",
|
||||
display: {
|
||||
"name": "OpenRouter",
|
||||
"icon": "router",
|
||||
"color": "#F97316",
|
||||
"textIcon": "OR",
|
||||
"website": "https://openrouter.ai",
|
||||
"notice": {
|
||||
"text": "Free tier: 27+ free models, no credit card needed, 200 req/day. After 0 credit: 1,000 req/day.",
|
||||
"apiKeyUrl": "https://openrouter.ai/settings/keys"
|
||||
}
|
||||
},
|
||||
category: "freeTier",
|
||||
"transport": {
|
||||
"baseUrl": "https://openrouter.ai/api/v1/chat/completions",
|
||||
"headers": {
|
||||
@@ -8,6 +21,13 @@ export default {
|
||||
"X-Title": "Endpoint Proxy"
|
||||
}
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "embedding", "tts", "imageToText"],
|
||||
embeddingConfig: { baseUrl: "https://openrouter.ai/api/v1/embeddings", authType: "apikey", authHeader: "bearer", headers: { "HTTP-Referer": "https://endpoint-proxy.local", "X-Title": "Endpoint Proxy" }, models: [{ id: "openai/text-embedding-3-small", name: "Text Embedding 3 Small (OpenRouter)", dimensions: 1536 }, { id: "openai/text-embedding-3-large", name: "Text Embedding 3 Large (OpenRouter)", dimensions: 3072 }, { id: "openai/text-embedding-ada-002", name: "Text Embedding Ada 002 (OpenRouter)", dimensions: 1536 }] },
|
||||
modelsFetcher: { url: "https://openrouter.ai/api/v1/models", type: "openrouter-free" },
|
||||
imageConfig: { baseUrl: "https://openrouter.ai/api/v1/images/generations", headers: { "HTTP-Referer": "https://endpoint-proxy.local", "X-Title": "Endpoint Proxy" } },
|
||||
passthroughModels: true
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "openai/text-embedding-3-large",
|
||||
|
||||
@@ -1,6 +1,19 @@
|
||||
|
||||
export default {
|
||||
"id": "perplexity-web",
|
||||
"alias": "perplexity-web",
|
||||
display: {
|
||||
"name": "Perplexity Web (Pro/Max)",
|
||||
"icon": "search",
|
||||
"color": "#20808D",
|
||||
"textIcon": "PW",
|
||||
"website": "https://www.perplexity.ai"
|
||||
},
|
||||
category: "webCookie",
|
||||
uiAlias: "pw",
|
||||
authType: "cookie",
|
||||
authHint: "Paste your __Secure-next-auth.session-token cookie value from perplexity.ai",
|
||||
aliases: ["pw"],
|
||||
"transport": {
|
||||
"baseUrl": "https://www.perplexity.ai/rest/sse/perplexity_ask",
|
||||
"format": "perplexity-web",
|
||||
|
||||
@@ -1,8 +1,28 @@
|
||||
|
||||
export default {
|
||||
"id": "perplexity",
|
||||
"alias": "perplexity",
|
||||
display: {
|
||||
"name": "Perplexity",
|
||||
"icon": "search",
|
||||
"color": "#20808D",
|
||||
"textIcon": "PP",
|
||||
"website": "https://www.perplexity.ai",
|
||||
"notice": {
|
||||
"apiKeyUrl": "https://www.perplexity.ai/settings/api"
|
||||
}
|
||||
},
|
||||
category: "apikey",
|
||||
uiAlias: "pplx",
|
||||
authType: "apikey",
|
||||
aliases: ["pplx"],
|
||||
"transport": {
|
||||
"baseUrl": "https://api.perplexity.ai/chat/completions"
|
||||
"baseUrl": "https://api.perplexity.ai/chat/completions",
|
||||
"validateUrl": "https://api.perplexity.ai/models"
|
||||
},
|
||||
media: {
|
||||
serviceKinds: ["llm", "webSearch"],
|
||||
searchViaChat: { defaultModel: "sonar", pricingUrl: "https://docs.perplexity.ai/guides/pricing" }
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
export default {
|
||||
"id": "predibase",
|
||||
"alias": "predibase",
|
||||
"transport": {
|
||||
"baseUrl": "https://serving.app.predibase.com/v1/chat/completions"
|
||||
},
|
||||
"models": [
|
||||
{
|
||||
"id": "llama-3-2-3b-instruct",
|
||||
"name": "Llama 3.2 3B"
|
||||
},
|
||||
{
|
||||
"id": "llama-3-1-8b-instruct",
|
||||
"name": "Llama 3.1 8B"
|
||||
},
|
||||
{
|
||||
"id": "qwen2-5-7b-instruct",
|
||||
"name": "Qwen 2.5 7B"
|
||||
}
|
||||
]
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user