Update jsconfig.json and package.json to correct open-sse path references from relative to local directory.

This commit is contained in:
decolua committed 2026-01-05 10:37:09 +07:00
1 parent 3857598de4
commit e35421beb1
39 files changed
+6846 -6

No files matched your search

+239
View File
@@ -0,0 +1,239 @@
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
import { adjustMaxTokens } from "../helpers/maxTokensHelper.js";
// Convert Claude request to OpenAI format
function claudeToOpenAI(model, body, stream) {
const result = {
model: model,
messages: [],
stream: stream
};
// Max tokens
if (body.max_tokens) {
result.max_tokens = adjustMaxTokens(body);
}
// Temperature
if (body.temperature !== undefined) {
result.temperature = body.temperature;
}
// System message
if (body.system) {
const systemContent = Array.isArray(body.system)
? body.system.map(s => s.text || "").join("\n")
: body.system;
if (systemContent) {
result.messages.push({
role: "system",
content: systemContent
});
}
}
// Convert messages
if (body.messages && Array.isArray(body.messages)) {
for (let i = 0; i < body.messages.length; i++) {
const msg = body.messages[i];
const converted = convertClaudeMessage(msg);
if (converted) {
// Handle array of messages (multiple tool results)
if (Array.isArray(converted)) {
result.messages.push(...converted);
} else {
result.messages.push(converted);
}
}
}
}
// Fix missing tool responses - OpenAI requires every tool_call to have a response
fixMissingToolResponses(result.messages);
// Tools
if (body.tools && Array.isArray(body.tools)) {
result.tools = body.tools.map(tool => ({
type: "function",
function: {
name: tool.name,
description: tool.description,
parameters: tool.input_schema || { type: "object", properties: {} }
}
}));
}
// Tool choice
if (body.tool_choice) {
result.tool_choice = convertToolChoice(body.tool_choice);
}
return result;
}
// Fix missing tool responses - add empty responses for tool_calls without responses
function fixMissingToolResponses(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) {
const toolCallIds = msg.tool_calls.map(tc => tc.id);
// Collect all tool response IDs that IMMEDIATELY follow this assistant message
// Stop at any non-tool message (user or assistant)
const respondedIds = new Set();
let insertPosition = i + 1;
for (let j = i + 1; j < messages.length; j++) {
const nextMsg = messages[j];
if (nextMsg.role === "tool" && nextMsg.tool_call_id) {
respondedIds.add(nextMsg.tool_call_id);
insertPosition = j + 1;
} else {
// Stop at any non-tool message (user or assistant)
break;
}
}
// Find missing responses and insert them
const missingIds = toolCallIds.filter(id => !respondedIds.has(id));
if (missingIds.length > 0) {
const missingResponses = missingIds.map(id => ({
role: "tool",
tool_call_id: id,
content: "[No response received]"
}));
// Insert missing responses at the correct position
messages.splice(insertPosition, 0, ...missingResponses);
// Adjust index to skip inserted messages
i = insertPosition + missingResponses.length - 1;
}
}
}
}
// Convert single Claude message - returns single message or array of messages
function convertClaudeMessage(msg) {
const role = msg.role === "user" || msg.role === "tool" ? "user" : "assistant";
// Simple string content
if (typeof msg.content === "string") {
return { role, content: msg.content };
}
// Array content
if (Array.isArray(msg.content)) {
const parts = [];
const toolCalls = [];
const toolResults = [];
for (const block of msg.content) {
switch (block.type) {
case "text":
parts.push({ type: "text", text: block.text });
break;
case "image":
if (block.source?.type === "base64") {
parts.push({
type: "image_url",
image_url: {
url: `data:${block.source.media_type};base64,${block.source.data}`
}
});
}
break;
case "tool_use":
toolCalls.push({
id: block.id,
type: "function",
function: {
name: block.name,
arguments: JSON.stringify(block.input || {})
}
});
break;
case "tool_result":
// Extract actual content from tool_result
let resultContent = "";
if (typeof block.content === "string") {
resultContent = block.content;
} else if (Array.isArray(block.content)) {
// Claude tool_result content can be array of text blocks
resultContent = block.content
.filter(c => c.type === "text")
.map(c => c.text)
.join("\n") || JSON.stringify(block.content);
} else if (block.content) {
resultContent = JSON.stringify(block.content);
}
toolResults.push({
role: "tool",
tool_call_id: block.tool_use_id,
content: resultContent
});
break;
}
}
// If has tool results, return array of tool messages
if (toolResults.length > 0) {
// Also include text parts as user message if any
if (parts.length > 0) {
const textContent = parts.length === 1 && parts[0].type === "text"
? parts[0].text
: parts;
return [...toolResults, { role: "user", content: textContent }];
}
return toolResults;
}
// If has tool calls, return assistant message with tool_calls
if (toolCalls.length > 0) {
const result = { role: "assistant" };
if (parts.length > 0) {
result.content = parts.length === 1 && parts[0].type === "text"
? parts[0].text
: parts;
}
result.tool_calls = toolCalls;
return result;
}
// Return content
if (parts.length > 0) {
return {
role,
content: parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts
};
}
// Empty content array - return empty string content to keep message in conversation
if (msg.content.length === 0) {
return { role, content: "" };
}
}
return null;
}
// Convert tool choice
function convertToolChoice(choice) {
if (!choice) return "auto";
if (typeof choice === "string") return choice;
switch (choice.type) {
case "auto": return "auto";
case "any": return "required";
case "tool": return { type: "function", function: { name: choice.name } };
default: return "auto";
}
}
// Register
register(FORMATS.CLAUDE, FORMATS.OPENAI, claudeToOpenAI, null);
+154
View File
@@ -0,0 +1,154 @@
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
import { adjustMaxTokens } from "../helpers/maxTokensHelper.js";
// Convert Gemini request to OpenAI format
function geminiToOpenAI(model, body, stream) {
const result = {
model: model,
messages: [],
stream: stream
};
// Generation config
if (body.generationConfig) {
const config = body.generationConfig;
if (config.maxOutputTokens) {
// Create temporary body object for adjustMaxTokens
const tempBody = { max_tokens: config.maxOutputTokens, tools: body.tools };
result.max_tokens = adjustMaxTokens(tempBody);
}
if (config.temperature !== undefined) {
result.temperature = config.temperature;
}
if (config.topP !== undefined) {
result.top_p = config.topP;
}
}
// System instruction
if (body.systemInstruction) {
const systemText = extractGeminiText(body.systemInstruction);
if (systemText) {
result.messages.push({
role: "system",
content: systemText
});
}
}
// Convert contents to messages
if (body.contents && Array.isArray(body.contents)) {
for (const content of body.contents) {
const converted = convertGeminiContent(content);
if (converted) {
result.messages.push(converted);
}
}
}
// Tools
if (body.tools && Array.isArray(body.tools)) {
result.tools = [];
for (const tool of body.tools) {
if (tool.functionDeclarations) {
for (const func of tool.functionDeclarations) {
result.tools.push({
type: "function",
function: {
name: func.name,
description: func.description || "",
parameters: func.parameters || { type: "object", properties: {} }
}
});
}
}
}
}
return result;
}
// Convert Gemini content to OpenAI message
function convertGeminiContent(content) {
const role = content.role === "user" ? "user" : "assistant";
if (!content.parts || !Array.isArray(content.parts)) {
return null;
}
const parts = [];
const toolCalls = [];
for (const part of content.parts) {
// Text
if (part.text !== undefined) {
parts.push({ type: "text", text: part.text });
}
// Image
if (part.inlineData) {
parts.push({
type: "image_url",
image_url: {
url: `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`
}
});
}
// Function call
if (part.functionCall) {
toolCalls.push({
id: `call_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
type: "function",
function: {
name: part.functionCall.name,
arguments: JSON.stringify(part.functionCall.args || {})
}
});
}
// Function response - use id if available, fallback to name
if (part.functionResponse) {
return {
role: "tool",
tool_call_id: part.functionResponse.id || part.functionResponse.name,
content: JSON.stringify(part.functionResponse.response?.result || part.functionResponse.response || {})
};
}
}
// Has tool calls
if (toolCalls.length > 0) {
const result = { role: "assistant" };
if (parts.length > 0) {
result.content = parts.length === 1 ? parts[0].text : parts;
}
result.tool_calls = toolCalls;
return result;
}
// Regular message
if (parts.length > 0) {
return {
role,
content: parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts
};
}
return null;
}
// Extract text from Gemini content
function extractGeminiText(content) {
if (typeof content === "string") return content;
if (content.parts && Array.isArray(content.parts)) {
return content.parts.map(p => p.text || "").join("");
}
return "";
}
// Register
register(FORMATS.GEMINI, FORMATS.OPENAI, geminiToOpenAI, null);
register(FORMATS.GEMINI_CLI, FORMATS.OPENAI, geminiToOpenAI, null);
@@ -0,0 +1,140 @@
/**
* Translator: OpenAI Responses API → OpenAI Chat Completions
*
* Responses API uses: { input: [...], instructions: "..." }
* Chat API uses: { messages: [...] }
*/
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
/**
* Convert OpenAI Responses API request to OpenAI Chat Completions format
*/
function translateRequest(model, body, stream, credentials) {
if (!body.input) return body;
const result = { ...body };
result.messages = [];
// Convert instructions to system message
if (body.instructions) {
result.messages.push({ role: "system", content: body.instructions });
}
// Group items by conversation turn
let currentAssistantMsg = null;
let pendingToolResults = [];
for (const item of body.input) {
if (item.type === "message") {
// Flush any pending assistant message with tool calls
if (currentAssistantMsg) {
result.messages.push(currentAssistantMsg);
currentAssistantMsg = null;
}
// Flush pending tool results
if (pendingToolResults.length > 0) {
for (const tr of pendingToolResults) {
result.messages.push(tr);
}
pendingToolResults = [];
}
// Convert content: input_text → text, output_text → text
const content = Array.isArray(item.content)
? item.content.map(c => {
if (c.type === "input_text") return { type: "text", text: c.text };
if (c.type === "output_text") return { type: "text", text: c.text };
return c;
})
: item.content;
result.messages.push({ role: item.role, content });
}
else if (item.type === "function_call") {
// Start or append to assistant message with tool_calls
if (!currentAssistantMsg) {
currentAssistantMsg = {
role: "assistant",
content: null,
tool_calls: []
};
}
currentAssistantMsg.tool_calls.push({
id: item.call_id,
type: "function",
function: {
name: item.name,
arguments: item.arguments
}
});
}
else if (item.type === "function_call_output") {
// Flush assistant message first if exists
if (currentAssistantMsg) {
result.messages.push(currentAssistantMsg);
currentAssistantMsg = null;
}
// Flush any pending tool results first
if (pendingToolResults.length > 0) {
for (const tr of pendingToolResults) {
result.messages.push(tr);
}
pendingToolResults = [];
}
// Add tool result immediately (not pending)
result.messages.push({
role: "tool",
tool_call_id: item.call_id,
content: typeof item.output === "string" ? item.output : JSON.stringify(item.output)
});
}
else if (item.type === "reasoning") {
// Skip reasoning items - they are for display only
continue;
}
}
// Flush remaining
if (currentAssistantMsg) {
result.messages.push(currentAssistantMsg);
}
if (pendingToolResults.length > 0) {
for (const tr of pendingToolResults) {
result.messages.push(tr);
}
}
// Tools are already in OpenAI format, just keep them
// Responses API tools: { type: "function", name, description, parameters }
// OpenAI tools: { type: "function", function: { name, description, parameters } }
if (body.tools && Array.isArray(body.tools)) {
result.tools = body.tools.map(tool => {
// Already has function wrapper
if (tool.function) return tool;
// Responses API format: flatten to OpenAI format
return {
type: "function",
function: {
name: tool.name,
description: tool.description,
parameters: tool.parameters,
strict: tool.strict
}
};
});
}
// Cleanup Responses API specific fields
delete result.input;
delete result.instructions;
delete result.include;
delete result.prompt_cache_key;
delete result.store;
delete result.reasoning;
return result;
}
// Register translator
register(FORMATS.OPENAI_RESPONSES, FORMATS.OPENAI, translateRequest, null);
+372
View File
@@ -0,0 +1,372 @@
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
import { CLAUDE_SYSTEM_PROMPT } from "../../config/constants.js";
import { adjustMaxTokens } from "../helpers/maxTokensHelper.js";
// Convert OpenAI request to Claude format
function openaiToClaude(model, body, stream) {
const result = {
model: model,
max_tokens: adjustMaxTokens(body),
stream: stream
};
// Temperature
if (body.temperature !== undefined) {
result.temperature = body.temperature;
}
// Messages
result.messages = [];
const systemParts = [];
if (body.messages && Array.isArray(body.messages)) {
// Extract system messages
for (const msg of body.messages) {
if (msg.role === "system") {
systemParts.push(typeof msg.content === "string" ? msg.content : extractTextContent(msg.content));
}
}
// Filter out system messages for separate processing
const nonSystemMessages = body.messages.filter(m => m.role !== "system");
// Process messages with merging logic
// CRITICAL: tool_result must be in separate message immediately after tool_use
let currentRole = undefined;
let currentParts = [];
const flushCurrentMessage = () => {
if (currentRole && currentParts.length > 0) {
result.messages.push({ role: currentRole, content: currentParts });
currentParts = [];
}
};
for (const msg of nonSystemMessages) {
const newRole = (msg.role === "user" || msg.role === "tool") ? "user" : "assistant";
const blocks = getContentBlocksFromMessage(msg);
const hasToolUse = blocks.some(b => b.type === "tool_use");
const hasToolResult = blocks.some(b => b.type === "tool_result");
// Separate tool_result from other content
if (hasToolResult) {
const toolResultBlocks = blocks.filter(b => b.type === "tool_result");
const otherBlocks = blocks.filter(b => b.type !== "tool_result");
// Flush current message first
flushCurrentMessage();
// Add tool_result as separate user message
if (toolResultBlocks.length > 0) {
result.messages.push({ role: "user", content: toolResultBlocks });
}
// Add other blocks to current parts for next message
if (otherBlocks.length > 0) {
currentRole = newRole;
currentParts.push(...otherBlocks);
}
continue;
}
if (currentRole !== newRole) {
flushCurrentMessage();
currentRole = newRole;
}
currentParts.push(...blocks);
if (hasToolUse) {
flushCurrentMessage();
}
}
flushCurrentMessage();
// Add cache_control to last assistant message (like worker.old)
for (let i = result.messages.length - 1; i >= 0; i--) {
const message = result.messages[i];
if (message.role === "assistant" && Array.isArray(message.content) && message.content.length > 0) {
const lastBlock = message.content[message.content.length - 1];
if (lastBlock) {
lastBlock.cache_control = { type: "ephemeral" };
break;
}
}
}
}
// System with Claude Code prompt and cache_control
const claudeCodePrompt = { type: "text", text: CLAUDE_SYSTEM_PROMPT };
if (systemParts.length > 0) {
const systemText = systemParts.join("\n");
result.system = [
claudeCodePrompt,
{ type: "text", text: systemText, cache_control: { type: "ephemeral", ttl: "1h" } }
];
} else {
result.system = [claudeCodePrompt];
}
// Tools - convert from OpenAI format to Claude format
if (body.tools && Array.isArray(body.tools)) {
result.tools = body.tools.map(tool => {
// Handle both OpenAI format {type: "function", function: {...}} and direct format
const toolData = tool.type === "function" && tool.function ? tool.function : tool;
return {
name: toolData.name,
description: toolData.description || "",
input_schema: toolData.parameters || toolData.input_schema || { type: "object", properties: {}, required: [] }
};
});
// Add cache control to last tool (like worker.old)
if (result.tools.length > 0) {
result.tools[result.tools.length - 1].cache_control = { type: "ephemeral", ttl: "1h" };
}
// console.log("[CLAUDE TOOLS DEBUG] Converted tools:", result.tools.map(t => t.name));
}
// Tool choice
if (body.tool_choice) {
result.tool_choice = convertOpenAIToolChoice(body.tool_choice);
}
return result;
}
// Convert OpenAI request to Gemini format
function openaiToGemini(model, body, stream) {
const result = {
contents: [],
generationConfig: {}
};
// Generation config
if (body.max_tokens) {
result.generationConfig.maxOutputTokens = body.max_tokens;
}
if (body.temperature !== undefined) {
result.generationConfig.temperature = body.temperature;
}
if (body.top_p !== undefined) {
result.generationConfig.topP = body.top_p;
}
// Messages
if (body.messages && Array.isArray(body.messages)) {
for (const msg of body.messages) {
if (msg.role === "system") {
result.systemInstruction = {
parts: [{ text: typeof msg.content === "string" ? msg.content : extractTextContent(msg.content) }]
};
} else if (msg.role === "tool") {
result.contents.push({
role: "function",
parts: [{
functionResponse: {
name: msg.tool_call_id,
response: tryParseJSON(msg.content)
}
}]
});
} else {
const converted = convertOpenAIToGeminiContent(msg);
if (converted) {
result.contents.push(converted);
}
}
}
}
// Tools
if (body.tools && Array.isArray(body.tools)) {
const validTools = body.tools.filter(tool => tool && tool.function && tool.function.name);
if (validTools.length > 0) {
result.tools = [{
functionDeclarations: validTools.map(tool => ({
name: tool.function.name,
description: tool.function.description || "",
parameters: tool.function.parameters || { type: "object", properties: {} }
}))
}];
}
}
return result;
}
// Get content blocks from single message (like src.cc getContentBlocksFromMessage)
function getContentBlocksFromMessage(msg) {
const blocks = [];
if (msg.role === "tool") {
blocks.push({
type: "tool_result",
tool_use_id: msg.tool_call_id,
content: msg.content
});
} else if (msg.role === "user") {
if (typeof msg.content === "string") {
if (msg.content) {
blocks.push({ type: "text", text: msg.content });
}
} else if (Array.isArray(msg.content)) {
for (const part of msg.content) {
if (part.type === "text" && part.text) {
blocks.push({ type: "text", text: part.text });
} else if (part.type === "tool_result") {
blocks.push({
type: "tool_result",
tool_use_id: part.tool_use_id,
content: part.content,
...(part.is_error && { is_error: part.is_error })
});
} else if (part.type === "image_url") {
const url = part.image_url.url;
const match = url.match(/^data:([^;]+);base64,(.+)$/);
if (match) {
blocks.push({
type: "image",
source: { type: "base64", media_type: match[1], data: match[2] }
});
}
} else if (part.type === "image" && part.source) {
blocks.push({ type: "image", source: part.source });
}
}
}
} else if (msg.role === "assistant") {
// Handle Anthropic format: content is array with tool_use blocks
if (Array.isArray(msg.content)) {
for (const part of msg.content) {
if (part.type === "text" && part.text) {
blocks.push({ type: "text", text: part.text });
} else if (part.type === "tool_use") {
blocks.push({ type: "tool_use", id: part.id, name: part.name, input: part.input });
}
}
} else if (msg.content) {
const text = typeof msg.content === "string" ? msg.content : extractTextContent(msg.content);
if (text) {
blocks.push({ type: "text", text });
}
}
// Handle OpenAI format: tool_calls array
if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
for (const tc of msg.tool_calls) {
if (tc.type === "function") {
blocks.push({
type: "tool_use",
id: tc.id,
name: tc.function.name,
input: tryParseJSON(tc.function.arguments)
});
}
}
}
}
return blocks;
}
// Convert single OpenAI message to Claude format (for backward compatibility)
function convertOpenAIMessage(msg) {
const role = msg.role === "assistant" ? "assistant" : "user";
const content = convertOpenAIMessageContent(msg);
if (content.length === 0) return null;
return { role, content };
}
// Convert OpenAI message to Gemini content
function convertOpenAIToGeminiContent(msg) {
const role = msg.role === "assistant" ? "model" : "user";
const parts = [];
// Text content
if (typeof msg.content === "string") {
if (msg.content) {
parts.push({ text: msg.content });
}
} else if (Array.isArray(msg.content)) {
for (const part of msg.content) {
if (part.type === "text") {
parts.push({ text: part.text });
} else if (part.type === "image_url") {
const url = part.image_url.url;
if (url.startsWith("data:")) {
const match = url.match(/^data:([^;]+);base64,(.+)$/);
if (match) {
parts.push({
inlineData: {
mimeType: match[1],
data: match[2]
}
});
}
}
}
}
}
// Tool calls
if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
for (const tc of msg.tool_calls) {
parts.push({
functionCall: {
name: tc.function.name,
args: tryParseJSON(tc.function.arguments)
}
});
}
}
if (parts.length === 0) return null;
return { role, parts };
}
// Convert tool choice
function convertOpenAIToolChoice(choice) {
if (!choice) return { type: "auto" };
// Passthrough if already Claude format
if (typeof choice === "object" && choice.type) return choice;
if (choice === "auto" || choice === "none") return { type: "auto" };
if (choice === "required") return { type: "any" };
if (typeof choice === "object" && choice.function) {
return { type: "tool", name: choice.function.name };
}
return { type: "auto" };
}
// Extract text from content
function extractTextContent(content) {
if (typeof content === "string") return content;
if (Array.isArray(content)) {
return content.filter(c => c.type === "text").map(c => c.text).join("\n");
}
return "";
}
// Try parse JSON
function tryParseJSON(str) {
if (typeof str !== "string") return str;
try {
return JSON.parse(str);
} catch {
return str;
}
}
// Register
register(FORMATS.OPENAI, FORMATS.CLAUDE, openaiToClaude, null);
register(FORMATS.OPENAI, FORMATS.GEMINI, openaiToGemini, null);
register(FORMATS.OPENAI, FORMATS.GEMINI_CLI, openaiToGemini, null);