/** * OpenAI Chat Completions streaming conversion helpers. */ import crypto from 'crypto'; function mapFinishReason(stopReason) { if (stopReason === 'max_tokens') return 'length'; if (stopReason === 'tool_use') return 'tool_calls'; return 'stop'; } export function createOpenAIStreamState(requestedModel, includeUsage = false) { return { id: `chatcmpl_${crypto.randomUUID().replace(/-/g, '')}`, created: Math.floor(Date.now() / 1000), model: requestedModel, includeUsage, started: false, finishReason: null, promptTokens: 0, completionTokens: 0, toolCallIndexes: new Map(), nextToolCallIndex: 0 }; } function makeChunk(state, delta, finishReason = null, usage = undefined) { const chunk = { id: state.id, object: 'chat.completion.chunk', created: state.created, model: state.model, choices: [{ index: 0, delta, finish_reason: finishReason, logprobs: null }] }; if (usage !== undefined) chunk.usage = usage; return chunk; } export function convertAnthropicEventToOpenAIChunks(event, state) { const chunks = []; if (!event || !state) return chunks; if (event.type === 'message_start') { state.id = event.message?.id?.replace(/^msg_/, 'chatcmpl_') || state.id; state.promptTokens = event.message?.usage?.input_tokens || 0; if (!state.started) { state.started = true; chunks.push(makeChunk(state, { role: 'assistant', content: '' })); } return chunks; } if (!state.started) { state.started = true; chunks.push(makeChunk(state, { role: 'assistant', content: '' })); } if (event.type === 'content_block_start' && event.content_block?.type === 'tool_use') { const toolIndex = state.nextToolCallIndex++; state.toolCallIndexes.set(event.index, toolIndex); chunks.push(makeChunk(state, { tool_calls: [{ index: toolIndex, id: event.content_block.id, type: 'function', function: { name: event.content_block.name, arguments: '' } }] })); return chunks; } if (event.type === 'content_block_delta') { if (event.delta?.type === 'text_delta') { chunks.push(makeChunk(state, { content: event.delta.text || '' })); } else if (event.delta?.type === 'thinking_delta') { chunks.push(makeChunk(state, { reasoning_content: event.delta.thinking || '' })); } else if (event.delta?.type === 'input_json_delta') { const toolIndex = state.toolCallIndexes.get(event.index) ?? 0; chunks.push(makeChunk(state, { tool_calls: [{ index: toolIndex, function: { arguments: event.delta.partial_json || '' } }] })); } return chunks; } if (event.type === 'message_delta') { state.finishReason = mapFinishReason(event.delta?.stop_reason); state.completionTokens = event.usage?.output_tokens || 0; // Emit search grounding (citations) just before the finish chunk. if (event.grounding?.annotations?.length > 0) { chunks.push(makeChunk(state, { annotations: event.grounding.annotations, grounding: event.grounding })); } chunks.push(makeChunk(state, {}, state.finishReason)); return chunks; } if (event.type === 'message_stop' && state.includeUsage) { chunks.push({ id: state.id, object: 'chat.completion.chunk', created: state.created, model: state.model, choices: [], usage: { prompt_tokens: state.promptTokens, completion_tokens: state.completionTokens, total_tokens: state.promptTokens + state.completionTokens } }); } return chunks; }