import { FORMATS } from "./formats.js"; import { ensureToolCallIds, fixMissingToolResponses } from "./helpers/toolCallHelper.js"; import { prepareClaudeRequest } from "./helpers/claudeHelper.js"; import { filterToOpenAIFormat } from "./helpers/openaiHelper.js"; import { normalizeThinkingConfig } from "../services/provider.js"; // Registry for translators const requestRegistry = new Map(); const responseRegistry = new Map(); // Register translator export function register(from, to, requestFn, responseFn) { const key = `${from}:${to}`; if (requestFn) { requestRegistry.set(key, requestFn); } if (responseFn) { responseRegistry.set(key, responseFn); } } // Translate request: source -> openai -> target export function translateRequest(sourceFormat, targetFormat, model, body, stream = true, credentials = null, provider = null) { let result = body; // Normalize thinking config: remove if lastMessage is not user normalizeThinkingConfig(result); // Always ensure tool_calls have id (some providers require it) ensureToolCallIds(result); // Fix missing tool responses (insert empty tool_result if needed) fixMissingToolResponses(result); // If same format, skip translation steps if (sourceFormat !== targetFormat) { // Step 1: source -> openai (if source is not openai) if (sourceFormat !== FORMATS.OPENAI) { const toOpenAI = requestRegistry.get(`${sourceFormat}:${FORMATS.OPENAI}`); if (toOpenAI) { result = toOpenAI(model, result, stream, credentials); } } // Step 1.5: Filter to clean OpenAI format (only when target is OpenAI) if (targetFormat === FORMATS.OPENAI) { result = filterToOpenAIFormat(result); } // Step 2: openai -> target (if target is not openai) if (targetFormat !== FORMATS.OPENAI) { const fromOpenAI = requestRegistry.get(`${FORMATS.OPENAI}:${targetFormat}`); if (fromOpenAI) { result = fromOpenAI(model, result, stream, credentials); } } } // Final step: prepare request for Claude format endpoints if (targetFormat === FORMATS.CLAUDE) { result = prepareClaudeRequest(result, provider); } return result; } // Translate response chunk: target -> openai -> source export function translateResponse(targetFormat, sourceFormat, chunk, state) { // If same format, return as-is if (sourceFormat === targetFormat) { return [chunk]; } let results = [chunk]; // Step 1: target -> openai (if target is not openai) if (targetFormat !== FORMATS.OPENAI) { const toOpenAI = responseRegistry.get(`${targetFormat}:${FORMATS.OPENAI}`); if (toOpenAI) { results = []; const converted = toOpenAI(chunk, state); if (converted) { results = Array.isArray(converted) ? converted : [converted]; } } } // Step 2: openai -> source (if source is not openai) if (sourceFormat !== FORMATS.OPENAI) { const fromOpenAI = responseRegistry.get(`${FORMATS.OPENAI}:${sourceFormat}`); if (fromOpenAI) { const finalResults = []; for (const r of results) { const converted = fromOpenAI(r, state); if (converted) { finalResults.push(...(Array.isArray(converted) ? converted : [converted])); } } results = finalResults; } } return results; } // Check if translation needed export function needsTranslation(sourceFormat, targetFormat) { return sourceFormat !== targetFormat; } // Initialize state for streaming response based on format export function initState(sourceFormat) { // Base state for all formats const base = { messageId: null, model: null, textBlockStarted: false, thinkingBlockStarted: false, inThinkingBlock: false, currentBlockIndex: null, toolCalls: new Map(), finishReason: null, finishReasonSent: false, usage: null, contentBlockIndex: -1 }; // Add openai-responses specific fields if (sourceFormat === FORMATS.OPENAI_RESPONSES) { return { ...base, seq: 0, responseId: `resp_${Date.now()}`, created: Math.floor(Date.now() / 1000), started: false, msgTextBuf: {}, msgItemAdded: {}, msgContentAdded: {}, msgItemDone: {}, reasoningId: "", reasoningIndex: -1, reasoningBuf: "", reasoningPartAdded: false, reasoningDone: false, inThinking: false, funcArgsBuf: {}, funcNames: {}, funcCallIds: {}, funcArgsDone: {}, funcItemDone: {}, completedSent: false }; } return base; } // Initialize all translators export async function initTranslators() { // Request translators await import("./request/claude-to-openai.js"); await import("./request/openai-to-claude.js"); await import("./request/gemini-to-openai.js"); await import("./request/openai-to-gemini.js"); await import("./request/openai-responses.js"); await import("./request/openai-to-kiro.js"); // Response translators await import("./response/claude-to-openai.js"); await import("./response/openai-to-claude.js"); await import("./response/gemini-to-openai.js"); await import("./response/openai-responses.js"); await import("./response/kiro-to-openai.js"); }