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:
@@ -3,6 +3,7 @@ import { DEFAULT_THINKING_CLAUDE_SIGNATURE } from "../../config/defaultThinkingS
|
||||
import { adjustMaxTokens } from "./maxTokensHelper.js";
|
||||
import { applyCloaking } from "../../utils/claudeCloaking.js";
|
||||
import { deriveSessionId } from "../../utils/sessionManager.js";
|
||||
import { PROVIDERS } from "../../providers/index.js";
|
||||
|
||||
// Check if message has valid non-empty content
|
||||
export function hasValidContent(msg) {
|
||||
@@ -122,8 +123,6 @@ export function normalizeClaudePassthrough(body, model = "") {
|
||||
return body;
|
||||
}
|
||||
|
||||
const CLAUDE_FORMAT_PROVIDERS_WITHOUT_OUTPUT_CONFIG = new Set(["minimax", "minimax-cn"]);
|
||||
|
||||
// Prepare request for Claude format endpoints
|
||||
// - Cleanup cache_control
|
||||
// - Filter empty messages
|
||||
@@ -131,9 +130,8 @@ const CLAUDE_FORMAT_PROVIDERS_WITHOUT_OUTPUT_CONFIG = new Set(["minimax", "minim
|
||||
// - Fix tool_use/tool_result ordering
|
||||
// - Apply cloaking (billing header + fake user ID) for OAuth tokens
|
||||
export function prepareClaudeRequest(body, provider = null, apiKey = null, connectionId = null) {
|
||||
// MiniMax exposes a Claude-compatible endpoint but rejects Anthropic's extended
|
||||
// structured output parameter with a generic 400 "invalid params" response.
|
||||
if (CLAUDE_FORMAT_PROVIDERS_WITHOUT_OUTPUT_CONFIG.has(provider)) {
|
||||
// quirk: MiniMax's Claude-compatible endpoint rejects Anthropic's output_config (400 invalid params)
|
||||
if (PROVIDERS[provider]?.quirks?.dropOutputConfig) {
|
||||
delete body.output_config;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,15 @@ export function encodeDataUri(mimeType, base64) {
|
||||
return `data:${mimeType};base64,${base64}`;
|
||||
}
|
||||
|
||||
// Parse a base64 data URI → { mimeType, base64 }, or null if not a data URI.
|
||||
// [\s\S] tolerates newlines inside the base64 payload.
|
||||
const DATA_URI_RE = /^data:([^;]+);base64,([\s\S]+)$/;
|
||||
export function parseDataUri(url) {
|
||||
if (typeof url !== "string") return null;
|
||||
const m = url.match(DATA_URI_RE);
|
||||
return m ? { mimeType: m[1], base64: m[2] } : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a remote image URL and return it as a base64 data URI.
|
||||
* Used when upstream providers (Codex, etc.) require inline base64 images
|
||||
|
||||
@@ -5,6 +5,7 @@ import { cloakClaudeTools } from "../utils/claudeCloaking.js";
|
||||
import { filterToOpenAIFormat } from "./helpers/openaiHelper.js";
|
||||
import { normalizeThinkingConfig } from "../services/provider.js";
|
||||
import { AntigravityExecutor } from "../executors/antigravity.js";
|
||||
import { PROVIDERS } from "../providers/index.js";
|
||||
|
||||
// Registry for translators
|
||||
const requestRegistry = new Map();
|
||||
@@ -122,8 +123,8 @@ export function translateRequest(sourceFormat, targetFormat, model, body, stream
|
||||
}
|
||||
|
||||
// Claude cloaking: rename client tools with _cc suffix (anti-ban)
|
||||
// Only for claude provider (not anthropic-compatible-*) with OAuth token
|
||||
if (provider === "claude") {
|
||||
// quirk: only providers flagged cloakToolsOnOAuth, and only with an OAuth token
|
||||
if (PROVIDERS[provider]?.quirks?.cloakToolsOnOAuth) {
|
||||
const apiKey = credentials?.accessToken || credentials?.apiKey || null;
|
||||
if (apiKey?.includes("sk-ant-oat")) {
|
||||
const { body: cloakedBody, toolNameMap } = cloakClaudeTools(result);
|
||||
|
||||
@@ -56,8 +56,10 @@ export function claudeToOpenAIRequest(model, body, stream) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fix missing tool responses - OpenAI requires every tool_call to have a response
|
||||
fixMissingToolResponses(result.messages);
|
||||
// Fix missing tool responses - OpenAI requires every tool_call to have a response.
|
||||
// Local variant: scans contiguous tool replies + inserts "[No response received]"
|
||||
// (distinct from the global immediate-next check in toolCallHelper, runs on the openai leg).
|
||||
fixMissingToolResponsesOpenAI(result.messages);
|
||||
|
||||
// Tools
|
||||
if (body.tools && Array.isArray(body.tools)) {
|
||||
@@ -80,7 +82,7 @@ export function claudeToOpenAIRequest(model, body, stream) {
|
||||
}
|
||||
|
||||
// Fix missing tool responses - add empty responses for tool_calls without responses
|
||||
function fixMissingToolResponses(messages) {
|
||||
function fixMissingToolResponsesOpenAI(messages) {
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
const msg = messages[i];
|
||||
if (msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { FORMATS } from "../formats.js";
|
||||
import { CLAUDE_SYSTEM_PROMPT } from "../../config/appConstants.js";
|
||||
import { adjustMaxTokens } from "../helpers/maxTokensHelper.js";
|
||||
import { safeParseJSON } from "../helpers/jsonUtil.js";
|
||||
import { parseDataUri } from "../helpers/imageHelper.js";
|
||||
|
||||
// Empty prefix matches real Claude Code behavior (no tool name prefix).
|
||||
// Previously "proxy_" was used but this is a detectable fingerprint difference.
|
||||
@@ -235,11 +236,11 @@ function getContentBlocksFromMessage(msg, toolNameMap = new Map()) {
|
||||
});
|
||||
} else if (part.type === "image_url") {
|
||||
const url = part.image_url.url;
|
||||
const match = url.match(/^data:([^;]+);base64,(.+)$/);
|
||||
if (match) {
|
||||
const parsed = parseDataUri(url);
|
||||
if (parsed) {
|
||||
blocks.push({
|
||||
type: "image",
|
||||
source: { type: "base64", media_type: match[1], data: match[2] }
|
||||
source: { type: "base64", media_type: parsed.mimeType, data: parsed.base64 }
|
||||
});
|
||||
} else if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
blocks.push({
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
KIRO_AGENTIC_SYSTEM_PROMPT,
|
||||
resolveDefaultProfileArn
|
||||
} from "../../config/kiroConstants.js";
|
||||
import { parseDataUri } from "../helpers/imageHelper.js";
|
||||
|
||||
/** Render a single tool call as a readable text line. */
|
||||
function toolCallToText(name, input) {
|
||||
@@ -289,11 +290,10 @@ function convertMessages(messages, tools, model) {
|
||||
} else if (c.type === "image_url") {
|
||||
// OpenAI format: image_url.url with data URI
|
||||
const url = c.image_url?.url || "";
|
||||
const base64Match = url.match(/^data:([^;]+);base64,(.+)$/);
|
||||
if (base64Match) {
|
||||
const mediaType = base64Match[1];
|
||||
const format = mediaType.split("/")[1] || mediaType;
|
||||
pendingImages.push({ format, source: { bytes: base64Match[2] } });
|
||||
const parsed = parseDataUri(url);
|
||||
if (parsed) {
|
||||
const format = parsed.mimeType.split("/")[1] || parsed.mimeType;
|
||||
pendingImages.push({ format, source: { bytes: parsed.base64 } });
|
||||
} else if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
// Kiro only supports base64 — fallback to URL text
|
||||
textParts.push(`[Image: ${url}]`);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { register } from "../index.js";
|
||||
import { FORMATS } from "../formats.js";
|
||||
import { parseDataUri } from "../helpers/imageHelper.js";
|
||||
|
||||
/**
|
||||
* Convert OpenAI request to Ollama format
|
||||
@@ -179,10 +180,10 @@ function extractImagesFromContent(content) {
|
||||
const url = typeof block.image_url === "string" ? block.image_url : block.image_url?.url;
|
||||
if (typeof url !== "string" || !url) continue;
|
||||
|
||||
const m = url.match(/^data:[^;]+;base64,([\s\S]+)$/);
|
||||
if (!m) continue;
|
||||
const parsed = parseDataUri(url);
|
||||
if (!parsed) continue;
|
||||
|
||||
images.push(m[1]);
|
||||
images.push(parsed.base64);
|
||||
}
|
||||
|
||||
return images;
|
||||
|
||||
Reference in New Issue
Block a user