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

+348
View File
@@ -0,0 +1,348 @@
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
// Create OpenAI chunk helper
function createChunk(state, delta, finishReason = null) {
return {
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta,
finish_reason: finishReason
}]
};
}
// Convert Claude stream chunk to OpenAI format
function claudeToOpenAIResponse(chunk, state) {
if (!chunk) return null;
const results = [];
const event = chunk.type;
switch (event) {
case "message_start": {
state.messageId = chunk.message?.id || `msg_${Date.now()}`;
state.model = chunk.message?.model;
state.toolCallIndex = 0; // Reset tool call counter for OpenAI format
console.log("🔍 ----------- toolCallIndex", state.toolCallIndex);
results.push(createChunk(state, { role: "assistant" }));
break;
}
case "content_block_start": {
const block = chunk.content_block;
if (block?.type === "text") {
state.textBlockStarted = true;
} else if (block?.type === "thinking") {
// console.log("🧠 Thinking block started");
state.inThinkingBlock = true;
state.currentBlockIndex = chunk.index;
results.push(createChunk(state, { content: "<think>" }));
} else if (block?.type === "tool_use") {
// OpenAI format: tool_calls index must be independent and start from 0
const toolCallIndex = state.toolCallIndex++;
const toolCall = {
index: toolCallIndex,
id: block.id,
type: "function",
function: {
name: block.name,
arguments: ""
}
};
// Map Claude content_block index to OpenAI tool_call index
state.toolCalls.set(chunk.index, toolCall);
results.push(createChunk(state, { tool_calls: [toolCall] }));
}
break;
}
case "content_block_delta": {
const delta = chunk.delta;
if (delta?.type === "text_delta" && delta.text) {
results.push(createChunk(state, { content: delta.text }));
} else if (delta?.type === "thinking_delta" && delta.thinking) {
// Stream thinking content
results.push(createChunk(state, { content: delta.thinking }));
} else if (delta?.type === "input_json_delta" && delta.partial_json) {
const toolCall = state.toolCalls.get(chunk.index);
if (toolCall) {
toolCall.function.arguments += delta.partial_json;
// Include both index and id for better client compatibility
results.push(createChunk(state, {
tool_calls: [{
index: toolCall.index,
id: toolCall.id,
function: { arguments: delta.partial_json }
}]
}));
}
}
break;
}
case "content_block_stop": {
if (state.inThinkingBlock && chunk.index === state.currentBlockIndex) {
// console.log("✅ Thinking block ended");
results.push(createChunk(state, { content: "</think>" }));
state.inThinkingBlock = false;
}
state.textBlockStarted = false;
state.thinkingBlockStarted = false;
break;
}
case "message_delta": {
if (chunk.delta?.stop_reason) {
state.finishReason = convertStopReason(chunk.delta.stop_reason);
// Send the final chunk with finish_reason immediately
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: {},
finish_reason: state.finishReason
}]
});
state.finishReasonSent = true;
}
// Usage is now extracted in stream.js extractUsage()
break;
}
case "message_stop": {
// CLIProxyAPI and OpenAI standard: message_stop should send the final chunk with finish_reason
// This ensures proper signaling to the client that the response is complete
// Only send a chunk if we haven't already sent the finish_reason in message_delta
// In some cases, finish_reason might not have been sent yet
if (!state.finishReasonSent) {
const finishReason = state.finishReason || (state.toolCalls?.size > 0 ? "tool_calls" : "stop");
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: {},
finish_reason: finishReason
}],
...(state.usage && {
usage: {
prompt_tokens: state.usage.input_tokens || 0,
completion_tokens: state.usage.output_tokens || 0,
total_tokens: (state.usage.input_tokens || 0) + (state.usage.output_tokens || 0)
}
})
});
state.finishReasonSent = true;
}
break;
}
}
return results.length > 0 ? results : null;
}
// Helper: stop thinking block if started
function stopThinkingBlock(state, results) {
if (!state.thinkingBlockStarted) return;
results.push({
type: "content_block_stop",
index: state.thinkingBlockIndex
});
state.thinkingBlockStarted = false;
}
// Helper: stop text block if started
function stopTextBlock(state, results) {
if (!state.textBlockStarted || state.textBlockClosed) return;
state.textBlockClosed = true;
results.push({
type: "content_block_stop",
index: state.textBlockIndex
});
state.textBlockStarted = false;
}
// Convert OpenAI stream chunk to Claude format
function openaiToClaudeResponse(chunk, state) {
if (!chunk || !chunk.choices?.[0]) return null;
const results = [];
const choice = chunk.choices[0];
const delta = choice.delta;
// First chunk - ALWAYS send message_start first
if (!state.messageStartSent) {
state.messageStartSent = true;
state.messageId = chunk.id?.replace("chatcmpl-", "") || `msg_${Date.now()}`;
if (!state.messageId || state.messageId === "chat" || state.messageId.length < 8) {
state.messageId = chunk.extend_fields?.requestId ||
chunk.extend_fields?.traceId ||
`msg_${Date.now()}`;
}
state.model = chunk.model || "unknown";
state.nextBlockIndex = 0;
results.push({
type: "message_start",
message: {
id: state.messageId,
type: "message",
role: "assistant",
model: state.model,
content: [],
stop_reason: null,
stop_sequence: null,
usage: { input_tokens: 0, output_tokens: 0 }
}
});
}
// Handle reasoning_content (thinking) - GLM, DeepSeek, etc.
const reasoningContent = delta?.reasoning_content || delta?.reasoning;
if (reasoningContent) {
// Stop text block before thinking
stopTextBlock(state, results);
// Start thinking block if needed
if (!state.thinkingBlockStarted) {
state.thinkingBlockIndex = state.nextBlockIndex++;
state.thinkingBlockStarted = true;
results.push({
type: "content_block_start",
index: state.thinkingBlockIndex,
content_block: { type: "thinking", thinking: "" }
});
}
// Send thinking delta
results.push({
type: "content_block_delta",
index: state.thinkingBlockIndex,
delta: { type: "thinking_delta", thinking: reasoningContent }
});
}
// Handle regular content
if (delta?.content) {
// Stop thinking block before text
stopThinkingBlock(state, results);
// Start text block if needed
if (!state.textBlockStarted) {
state.textBlockIndex = state.nextBlockIndex++;
state.textBlockStarted = true;
state.textBlockClosed = false;
results.push({
type: "content_block_start",
index: state.textBlockIndex,
content_block: { type: "text", text: "" }
});
}
// Send text delta
results.push({
type: "content_block_delta",
index: state.textBlockIndex,
delta: { type: "text_delta", text: delta.content }
});
}
// Tool calls
if (delta?.tool_calls) {
for (const tc of delta.tool_calls) {
const idx = tc.index ?? 0;
if (tc.id) {
// Stop thinking and text blocks before tool use
stopThinkingBlock(state, results);
stopTextBlock(state, results);
// New tool call
const toolBlockIndex = state.nextBlockIndex++;
state.toolCalls.set(idx, { id: tc.id, name: tc.function?.name || "", blockIndex: toolBlockIndex });
results.push({
type: "content_block_start",
index: toolBlockIndex,
content_block: {
type: "tool_use",
id: tc.id,
name: tc.function?.name || "",
input: {}
}
});
}
if (tc.function?.arguments) {
const toolInfo = state.toolCalls.get(idx);
if (toolInfo) {
results.push({
type: "content_block_delta",
index: toolInfo.blockIndex,
delta: { type: "input_json_delta", partial_json: tc.function.arguments }
});
}
}
}
}
// Finish
if (choice.finish_reason) {
// Stop all open blocks
stopThinkingBlock(state, results);
stopTextBlock(state, results);
// Close tool call blocks
for (const [, toolInfo] of state.toolCalls) {
results.push({
type: "content_block_stop",
index: toolInfo.blockIndex
});
}
results.push({
type: "message_delta",
delta: { stop_reason: convertFinishReason(choice.finish_reason) },
usage: { output_tokens: 0 }
});
results.push({ type: "message_stop" });
}
return results.length > 0 ? results : null;
}
// Convert Claude stop_reason to OpenAI finish_reason
function convertStopReason(reason) {
switch (reason) {
case "end_turn": return "stop";
case "max_tokens": return "length";
case "tool_use": return "tool_calls";
case "stop_sequence": return "stop";
default: return "stop";
}
}
// Convert OpenAI finish_reason to Claude stop_reason
function convertFinishReason(reason) {
switch (reason) {
case "stop": return "end_turn";
case "length": return "max_tokens";
case "tool_calls": return "tool_use";
default: return "end_turn";
}
}
// Register
register(FORMATS.CLAUDE, FORMATS.OPENAI, null, claudeToOpenAIResponse);
register(FORMATS.OPENAI, FORMATS.CLAUDE, null, openaiToClaudeResponse);
+469
View File
@@ -0,0 +1,469 @@
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
import { DEFAULT_THINKING_GEMINI_SIGNATURE } from "../../config/defaultThinkingSignature.js";
import {
UNSUPPORTED_SCHEMA_CONSTRAINTS,
DEFAULT_SAFETY_SETTINGS,
convertOpenAIContentToParts,
extractTextContent,
tryParseJSON,
generateRequestId,
generateSessionId,
generateProjectId,
cleanJSONSchemaForAntigravity
} from "../helpers/geminiHelper.js";
// ============================================
// REQUEST TRANSLATORS: OpenAI -> Gemini/GeminiCLI/Antigravity
// ============================================
// Core: Convert OpenAI request to Gemini format (base for all variants)
function openaiToGeminiBase(model, body, stream) {
const result = {
model: model,
contents: [],
generationConfig: {},
safetySettings: DEFAULT_SAFETY_SETTINGS
};
// Generation config
if (body.temperature !== undefined) {
result.generationConfig.temperature = body.temperature;
}
if (body.top_p !== undefined) {
result.generationConfig.topP = body.top_p;
}
if (body.top_k !== undefined) {
result.generationConfig.topK = body.top_k;
}
if (body.max_tokens !== undefined) {
result.generationConfig.maxOutputTokens = body.max_tokens;
}
// Build tool_call_id -> name map
const tcID2Name = {};
if (body.messages && Array.isArray(body.messages)) {
for (const msg of body.messages) {
if (msg.role === "assistant" && msg.tool_calls) {
for (const tc of msg.tool_calls) {
if (tc.type === "function" && tc.id && tc.function?.name) {
tcID2Name[tc.id] = tc.function.name;
}
}
}
}
}
// Build tool responses cache
const toolResponses = {};
if (body.messages && Array.isArray(body.messages)) {
for (const msg of body.messages) {
if (msg.role === "tool" && msg.tool_call_id) {
toolResponses[msg.tool_call_id] = msg.content;
}
}
}
// Convert messages
if (body.messages && Array.isArray(body.messages)) {
for (let i = 0; i < body.messages.length; i++) {
const msg = body.messages[i];
const role = msg.role;
const content = msg.content;
if (role === "system" && body.messages.length > 1) {
result.systemInstruction = {
role: "user",
parts: [{ text: typeof content === "string" ? content : extractTextContent(content) }]
};
} else if (role === "user" || (role === "system" && body.messages.length === 1)) {
const parts = convertOpenAIContentToParts(content);
if (parts.length > 0) {
result.contents.push({ role: "user", parts });
}
} else if (role === "assistant") {
const parts = [];
if (content) {
const text = typeof content === "string" ? content : extractTextContent(content);
if (text) {
parts.push({ text });
}
}
if (msg.tool_calls && Array.isArray(msg.tool_calls)) {
const toolCallIds = [];
for (const tc of msg.tool_calls) {
if (tc.type !== "function") continue;
const args = tryParseJSON(tc.function?.arguments || "{}");
parts.push({
thoughtSignature: DEFAULT_THINKING_GEMINI_SIGNATURE,
functionCall: {
id: tc.id,
name: tc.function.name,
args: args
}
});
toolCallIds.push(tc.id);
}
if (parts.length > 0) {
result.contents.push({ role: "model", parts });
}
// Append function responses - extract name from tool_call_id format "ToolName-timestamp-index"
const toolParts = [];
for (const fid of toolCallIds) {
// Try to get name from tcID2Name map first, then extract from id format
let name = tcID2Name[fid];
if (!name) {
// Extract name from id format: "ToolName-timestamp-index"
const idParts = fid.split("-");
if (idParts.length > 2) {
name = idParts.slice(0, -2).join("-");
} else {
name = fid;
}
}
let resp = toolResponses[fid] || "{}";
let parsedResp = tryParseJSON(resp);
if (parsedResp === null) {
parsedResp = { result: resp };
} else if (typeof parsedResp !== "object") {
parsedResp = { result: parsedResp };
}
toolParts.push({
functionResponse: {
id: fid,
name: name,
response: { result: parsedResp }
}
});
}
if (toolParts.length > 0) {
result.contents.push({ role: "user", parts: toolParts });
}
} else if (parts.length > 0) {
result.contents.push({ role: "model", parts });
}
}
}
}
// Convert tools
if (body.tools && Array.isArray(body.tools) && body.tools.length > 0) {
const functionDeclarations = [];
for (const t of body.tools) {
if (t.type === "function" && t.function) {
const fn = t.function;
functionDeclarations.push({
name: fn.name,
description: fn.description || "",
parameters: fn.parameters || { type: "object", properties: {} }
});
}
}
if (functionDeclarations.length > 0) {
result.tools = [{ functionDeclarations }];
}
}
return result;
}
// OpenAI -> Gemini (standard API)
function openaiToGemini(model, body, stream) {
return openaiToGeminiBase(model, body, stream);
}
// OpenAI -> Gemini CLI (Cloud Code Assist)
function openaiToGeminiCLI(model, body, stream) {
const gemini = openaiToGeminiBase(model, body, stream);
const isClaude = model.toLowerCase().includes("claude");
// Add thinking config for CLI
if (body.reasoning_effort) {
const budgetMap = { low: 1024, medium: 8192, high: 32768 };
const budget = budgetMap[body.reasoning_effort] || 8192;
gemini.generationConfig.thinkingConfig = {
thinkingBudget: budget,
include_thoughts: true
};
}
// Thinking config from Claude format
if (body.thinking?.type === "enabled" && body.thinking.budget_tokens) {
gemini.generationConfig.thinkingConfig = {
thinkingBudget: body.thinking.budget_tokens,
include_thoughts: true
};
}
// Clean schema for tools
// Claude models: use "parameters" (backend converts parametersJsonSchema -> parameters)
// Gemini native: use "parametersJsonSchema" (backend expects this field)
if (gemini.tools?.[0]?.functionDeclarations) {
for (const fn of gemini.tools[0].functionDeclarations) {
if (fn.parameters) {
const cleanedSchema = cleanJSONSchemaForAntigravity(fn.parameters);
if (isClaude) {
fn.parameters = cleanedSchema;
} else {
fn.parametersJsonSchema = cleanedSchema;
delete fn.parameters;
}
}
}
}
return gemini;
}
// Wrap Gemini CLI format in Cloud Code wrapper
function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null) {
// Use real project ID if available, otherwise generate random
const projectId = credentials?.projectId || generateProjectId();
return {
project: projectId,
model: model,
userAgent: "gemini-cli",
requestId: generateRequestId(),
request: {
sessionId: generateSessionId(),
contents: geminiCLI.contents,
systemInstruction: geminiCLI.systemInstruction,
generationConfig: geminiCLI.generationConfig,
safetySettings: geminiCLI.safetySettings,
tools: geminiCLI.tools,
}
};
}
// OpenAI -> Antigravity (Sandbox Cloud Code with wrapper)
function openaiToAntigravity(model, body, stream, credentials = null) {
const geminiCLI = openaiToGeminiCLI(model, body, stream);
return wrapInCloudCodeEnvelope(model, geminiCLI, credentials);
}
// ============================================
// RESPONSE TRANSLATORS: Gemini/GeminiCLI/Antigravity -> OpenAI
// ============================================
// Core: Convert Gemini response chunk to OpenAI format
function geminiToOpenAIResponse(chunk, state) {
if (!chunk) return null;
// Handle Antigravity wrapper
const response = chunk.response || chunk;
if (!response || !response.candidates?.[0]) return null;
const results = [];
const candidate = response.candidates[0];
const content = candidate.content;
// Initialize state
if (!state.messageId) {
state.messageId = response.responseId || `msg_${Date.now()}`;
state.model = response.modelVersion || "gemini";
state.functionIndex = 0;
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: { role: "assistant" },
finish_reason: null
}]
});
}
// Process parts
if (content?.parts) {
for (const part of content.parts) {
const hasThoughtSig = part.thoughtSignature || part.thought_signature;
const isThought = part.thought === true;
// Handle thought signature (thinking mode)
if (hasThoughtSig) {
const hasTextContent = part.text !== undefined && part.text !== "";
const hasFunctionCall = !!part.functionCall;
// If there's text with thoughtSignature
if (hasTextContent) {
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: isThought
? { reasoning_content: part.text }
: { content: part.text },
finish_reason: null
}]
});
}
// Process functionCall if exists, then skip to next part
if (hasFunctionCall) {
const fcName = part.functionCall.name;
const fcArgs = part.functionCall.args || {};
const toolCallIndex = state.functionIndex++;
const toolCall = {
id: `${fcName}-${Date.now()}-${toolCallIndex}`,
index: toolCallIndex,
type: "function",
function: {
name: fcName,
arguments: JSON.stringify(fcArgs)
}
};
state.toolCalls.set(toolCallIndex, toolCall);
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: { tool_calls: [toolCall] },
finish_reason: null
}]
});
}
continue;
}
// Text content (non-thinking) - skip empty text
if (part.text !== undefined && part.text !== "") {
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: { content: part.text },
finish_reason: null
}]
});
}
// Function call
if (part.functionCall) {
const fcName = part.functionCall.name;
const fcArgs = part.functionCall.args || {};
const toolCallIndex = state.functionIndex++;
const toolCall = {
id: `${fcName}-${Date.now()}-${toolCallIndex}`,
index: toolCallIndex,
type: "function",
function: {
name: fcName,
arguments: JSON.stringify(fcArgs)
}
};
state.toolCalls.set(toolCallIndex, toolCall);
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: { tool_calls: [toolCall] },
finish_reason: null
}]
});
}
// Inline data (images)
const inlineData = part.inlineData || part.inline_data;
if (inlineData?.data) {
const mimeType = inlineData.mimeType || inlineData.mime_type || "image/png";
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: {
images: [{
type: "image_url",
image_url: { url: `data:${mimeType};base64,${inlineData.data}` }
}]
},
finish_reason: null
}]
});
}
}
}
// Finish reason
if (candidate.finishReason) {
let finishReason = candidate.finishReason.toLowerCase();
if (finishReason === "stop" && state.toolCalls.size > 0) {
finishReason = "tool_calls";
}
results.push({
id: `chatcmpl-${state.messageId}`,
object: "chat.completion.chunk",
created: Math.floor(Date.now() / 1000),
model: state.model,
choices: [{
index: 0,
delta: {},
finish_reason: finishReason
}]
});
state.finishReason = finishReason;
}
// Usage metadata
const usage = response.usageMetadata || chunk.usageMetadata;
if (usage) {
const promptTokens = (usage.promptTokenCount || 0) + (usage.thoughtsTokenCount || 0);
state.usage = {
prompt_tokens: promptTokens,
completion_tokens: usage.candidatesTokenCount || 0,
total_tokens: usage.totalTokenCount || 0
};
if (usage.thoughtsTokenCount > 0) {
state.usage.completion_tokens_details = {
reasoning_tokens: usage.thoughtsTokenCount
};
}
}
return results.length > 0 ? results : null;
}
// ============================================
// REGISTER ALL TRANSLATORS
// ============================================
// Request: OpenAI -> Gemini variants
register(FORMATS.OPENAI, FORMATS.GEMINI, openaiToGemini, null);
register(FORMATS.OPENAI, FORMATS.GEMINI_CLI, (model, body, stream, credentials) => wrapInCloudCodeEnvelope(model, openaiToGeminiCLI(model, body, stream), credentials), null);
register(FORMATS.OPENAI, FORMATS.ANTIGRAVITY, openaiToAntigravity, null);
// Response: Gemini variants -> OpenAI (all use same handler)
register(FORMATS.GEMINI, FORMATS.OPENAI, null, geminiToOpenAIResponse);
register(FORMATS.GEMINI_CLI, FORMATS.OPENAI, null, geminiToOpenAIResponse);
register(FORMATS.ANTIGRAVITY, FORMATS.OPENAI, null, geminiToOpenAIResponse);
@@ -0,0 +1,361 @@
/**
* Translator: OpenAI Chat Completions → OpenAI Responses API (response)
* Converts streaming chunks from Chat Completions to Responses API events
*/
import { register } from "../index.js";
import { FORMATS } from "../formats.js";
/**
* Translate OpenAI chunk to Responses API events
* @returns {Array} Array of events with { event, data } structure
*/
function translateResponse(chunk, state) {
if (!chunk) {
// Flush remaining events
return flushEvents(state);
}
if (!chunk.choices?.length) return [];
const events = [];
const nextSeq = () => ++state.seq;
const emit = (eventType, data) => {
data.sequence_number = nextSeq();
events.push({ event: eventType, data });
};
const choice = chunk.choices[0];
const idx = choice.index || 0;
const delta = choice.delta || {};
// Emit initial events
if (!state.started) {
state.started = true;
state.responseId = chunk.id ? `resp_${chunk.id}` : state.responseId;
emit("response.created", {
type: "response.created",
response: {
id: state.responseId,
object: "response",
created_at: state.created,
status: "in_progress",
background: false,
error: null,
output: []
}
});
emit("response.in_progress", {
type: "response.in_progress",
response: {
id: state.responseId,
object: "response",
created_at: state.created,
status: "in_progress"
}
});
}
// Handle reasoning_content
if (delta.reasoning_content) {
startReasoning(state, emit, idx);
emitReasoningDelta(state, emit, delta.reasoning_content);
}
// Handle text content
if (delta.content) {
let content = delta.content;
if (content.includes("<think>")) {
state.inThinking = true;
content = content.replace("<think>", "");
startReasoning(state, emit, idx);
}
if (content.includes("</think>")) {
const parts = content.split("</think>");
const thinkPart = parts[0];
const textPart = parts.slice(1).join("</think>");
if (thinkPart) emitReasoningDelta(state, emit, thinkPart);
closeReasoning(state, emit);
state.inThinking = false;
content = textPart;
}
if (state.inThinking && content) {
emitReasoningDelta(state, emit, content);
return events;
}
if (content) {
emitTextContent(state, emit, idx, content);
}
}
// Handle tool_calls
if (delta.tool_calls) {
closeMessage(state, emit, idx);
for (const tc of delta.tool_calls) {
emitToolCall(state, emit, tc);
}
}
// Handle finish_reason
if (choice.finish_reason) {
for (const i in state.msgItemAdded) closeMessage(state, emit, i);
closeReasoning(state, emit);
for (const i in state.funcCallIds) closeToolCall(state, emit, i);
sendCompleted(state, emit);
}
return events;
}
// Helper functions
function startReasoning(state, emit, idx) {
if (!state.reasoningId) {
state.reasoningId = `rs_${state.responseId}_${idx}`;
state.reasoningIndex = idx;
emit("response.output_item.added", {
type: "response.output_item.added",
output_index: idx,
item: { id: state.reasoningId, type: "reasoning", summary: [] }
});
emit("response.reasoning_summary_part.added", {
type: "response.reasoning_summary_part.added",
item_id: state.reasoningId,
output_index: idx,
summary_index: 0,
part: { type: "summary_text", text: "" }
});
state.reasoningPartAdded = true;
}
}
function emitReasoningDelta(state, emit, text) {
if (!text) return;
state.reasoningBuf += text;
emit("response.reasoning_summary_text.delta", {
type: "response.reasoning_summary_text.delta",
item_id: state.reasoningId,
output_index: state.reasoningIndex,
summary_index: 0,
delta: text
});
}
function closeReasoning(state, emit) {
if (state.reasoningId && !state.reasoningDone) {
state.reasoningDone = true;
emit("response.reasoning_summary_text.done", {
type: "response.reasoning_summary_text.done",
item_id: state.reasoningId,
output_index: state.reasoningIndex,
summary_index: 0,
text: state.reasoningBuf
});
emit("response.reasoning_summary_part.done", {
type: "response.reasoning_summary_part.done",
item_id: state.reasoningId,
output_index: state.reasoningIndex,
summary_index: 0,
part: { type: "summary_text", text: state.reasoningBuf }
});
emit("response.output_item.done", {
type: "response.output_item.done",
output_index: state.reasoningIndex,
item: {
id: state.reasoningId,
type: "reasoning",
summary: [{ type: "summary_text", text: state.reasoningBuf }]
}
});
}
}
function emitTextContent(state, emit, idx, content) {
if (!state.msgItemAdded[idx]) {
state.msgItemAdded[idx] = true;
const msgId = `msg_${state.responseId}_${idx}`;
emit("response.output_item.added", {
type: "response.output_item.added",
output_index: idx,
item: { id: msgId, type: "message", content: [], role: "assistant" }
});
}
if (!state.msgContentAdded[idx]) {
state.msgContentAdded[idx] = true;
emit("response.content_part.added", {
type: "response.content_part.added",
item_id: `msg_${state.responseId}_${idx}`,
output_index: idx,
content_index: 0,
part: { type: "output_text", annotations: [], logprobs: [], text: "" }
});
}
emit("response.output_text.delta", {
type: "response.output_text.delta",
item_id: `msg_${state.responseId}_${idx}`,
output_index: idx,
content_index: 0,
delta: content,
logprobs: []
});
if (!state.msgTextBuf[idx]) state.msgTextBuf[idx] = "";
state.msgTextBuf[idx] += content;
}
function closeMessage(state, emit, idx) {
if (state.msgItemAdded[idx] && !state.msgItemDone[idx]) {
state.msgItemDone[idx] = true;
const fullText = state.msgTextBuf[idx] || "";
const msgId = `msg_${state.responseId}_${idx}`;
emit("response.output_text.done", {
type: "response.output_text.done",
item_id: msgId,
output_index: parseInt(idx),
content_index: 0,
text: fullText,
logprobs: []
});
emit("response.content_part.done", {
type: "response.content_part.done",
item_id: msgId,
output_index: parseInt(idx),
content_index: 0,
part: { type: "output_text", annotations: [], logprobs: [], text: fullText }
});
emit("response.output_item.done", {
type: "response.output_item.done",
output_index: parseInt(idx),
item: {
id: msgId,
type: "message",
content: [{ type: "output_text", annotations: [], logprobs: [], text: fullText }],
role: "assistant"
}
});
}
}
function emitToolCall(state, emit, tc) {
const tcIdx = tc.index ?? 0;
const newCallId = tc.id;
const funcName = tc.function?.name;
if (funcName) state.funcNames[tcIdx] = funcName;
if (!state.funcCallIds[tcIdx] && newCallId) {
state.funcCallIds[tcIdx] = newCallId;
emit("response.output_item.added", {
type: "response.output_item.added",
output_index: tcIdx,
item: {
id: `fc_${newCallId}`,
type: "function_call",
arguments: "",
call_id: newCallId,
name: state.funcNames[tcIdx] || ""
}
});
}
if (!state.funcArgsBuf[tcIdx]) state.funcArgsBuf[tcIdx] = "";
if (tc.function?.arguments) {
const refCallId = state.funcCallIds[tcIdx] || newCallId;
if (refCallId) {
emit("response.function_call_arguments.delta", {
type: "response.function_call_arguments.delta",
item_id: `fc_${refCallId}`,
output_index: tcIdx,
delta: tc.function.arguments
});
}
state.funcArgsBuf[tcIdx] += tc.function.arguments;
}
}
function closeToolCall(state, emit, idx) {
const callId = state.funcCallIds[idx];
if (callId && !state.funcItemDone[idx]) {
const args = state.funcArgsBuf[idx] || "{}";
emit("response.function_call_arguments.done", {
type: "response.function_call_arguments.done",
item_id: `fc_${callId}`,
output_index: parseInt(idx),
arguments: args
});
emit("response.output_item.done", {
type: "response.output_item.done",
output_index: parseInt(idx),
item: {
id: `fc_${callId}`,
type: "function_call",
arguments: args,
call_id: callId,
name: state.funcNames[idx] || ""
}
});
state.funcItemDone[idx] = true;
state.funcArgsDone[idx] = true;
}
}
function sendCompleted(state, emit) {
if (!state.completedSent) {
state.completedSent = true;
emit("response.completed", {
type: "response.completed",
response: {
id: state.responseId,
object: "response",
created_at: state.created,
status: "completed",
background: false,
error: null
}
});
}
}
function flushEvents(state) {
if (state.completedSent) return [];
const events = [];
const nextSeq = () => ++state.seq;
const emit = (eventType, data) => {
data.sequence_number = nextSeq();
events.push({ event: eventType, data });
};
for (const i in state.msgItemAdded) closeMessage(state, emit, i);
closeReasoning(state, emit);
for (const i in state.funcCallIds) closeToolCall(state, emit, i);
sendCompleted(state, emit);
return events;
}
// Register translator
register(FORMATS.OPENAI, FORMATS.OPENAI_RESPONSES, null, translateResponse);