UNPKG

manifest

Version:

Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard

272 lines 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toResponsesRequest = toResponsesRequest; exports.fromResponsesResponse = fromResponsesResponse; exports.transformResponsesStreamChunk = transformResponsesStreamChunk; exports.collectChatGptSseResponse = collectChatGptSseResponse; const crypto_1 = require("crypto"); const chatgpt_helpers_1 = require("./chatgpt-helpers"); function toResponsesRequest(body, model) { const messages = (body.messages ?? []); const input = []; for (const message of messages) { if (message.role === 'system' || message.role === 'developer') continue; if (message.role === 'assistant' && Array.isArray(message.tool_calls) && message.tool_calls.length > 0) { const assistantText = (0, chatgpt_helpers_1.extractTextContent)(message.content); if (assistantText) { input.push({ role: 'assistant', content: (0, chatgpt_helpers_1.convertContent)(assistantText, 'assistant') }); } input.push(...(0, chatgpt_helpers_1.convertAssistantToolCalls)(message.tool_calls)); continue; } if (message.role === 'tool' || message.role === 'function') { input.push({ type: 'function_call_output', call_id: typeof message.tool_call_id === 'string' ? message.tool_call_id : (0, crypto_1.randomUUID)(), output: (0, chatgpt_helpers_1.extractTextContent)(message.content) ?? JSON.stringify(message.content ?? ''), }); continue; } input.push({ role: message.role, content: (0, chatgpt_helpers_1.convertContent)(message.content, message.role) }); } const request = { model, input, stream: body.stream !== false, store: false, instructions: (0, chatgpt_helpers_1.extractInstructions)(messages), }; if (Array.isArray(body.tools)) { request.tools = (0, chatgpt_helpers_1.convertTools)(body.tools); } return request; } function fromResponsesResponse(data, model) { const output = (data.output ?? []); let text = ''; const toolCalls = []; for (const item of output) { if (item.type === 'message') { const content = item.content; if (!content) continue; for (const part of content) { if (part.type === 'output_text' && part.text) text += part.text; } continue; } if (item.type === 'function_call') { toolCalls.push({ id: item.call_id ?? (0, crypto_1.randomUUID)(), type: 'function', function: { name: item.name ?? '', arguments: item.arguments ?? '{}', }, }); } } const usage = data.usage ?? {}; const inputDetails = usage.input_tokens_details; const message = { role: 'assistant', content: text || null, }; if (toolCalls.length > 0) { message.tool_calls = toolCalls; } return { id: `chatcmpl-${(0, crypto_1.randomUUID)().replace(/-/g, '').slice(0, 29)}`, object: 'chat.completion', created: Math.floor(Date.now() / 1000), model, choices: [ { index: 0, message, finish_reason: toolCalls.length > 0 ? 'tool_calls' : 'stop', }, ], usage: { prompt_tokens: usage.input_tokens ?? 0, completion_tokens: usage.output_tokens ?? 0, total_tokens: usage.total_tokens ?? 0, cache_read_tokens: inputDetails?.cached_tokens ?? 0, cache_creation_tokens: 0, }, }; } function transformResponsesStreamChunk(chunk, model) { const lines = chunk.split('\n'); let eventType = ''; let dataStr = ''; for (const line of lines) { if (line.startsWith('event: ')) { eventType = line.slice(7).trim(); } else if (line.startsWith('data: ')) { dataStr = line.slice(6); } else if (line.trim()) { dataStr = line.trim(); } } if (!eventType && !dataStr) return null; if (eventType === 'response.output_text.delta') { const data = (0, chatgpt_helpers_1.safeParse)(dataStr); if (!data) return null; const delta = typeof data.delta === 'string' ? data.delta : ''; return (0, chatgpt_helpers_1.formatSSE)({ delta: { content: delta }, finish_reason: null }, model); } if (eventType === 'response.function_call_arguments.delta') { const data = (0, chatgpt_helpers_1.safeParse)(dataStr); if (!data) return null; const delta = typeof data.delta === 'string' ? data.delta : ''; return (0, chatgpt_helpers_1.formatSSE)({ delta: { tool_calls: [ { index: typeof data.output_index === 'number' ? data.output_index : 0, function: { arguments: delta }, }, ], }, finish_reason: null, }, model); } if (eventType === 'response.output_item.added') { const data = (0, chatgpt_helpers_1.safeParse)(dataStr); if (!data) return null; const item = (0, chatgpt_helpers_1.isObjectRecord)(data.item) ? data.item : undefined; if (item?.type !== 'function_call') return null; return (0, chatgpt_helpers_1.formatSSE)({ delta: { tool_calls: [ { index: typeof data.output_index === 'number' ? data.output_index : 0, id: item.call_id ?? '', type: 'function', function: { name: item.name ?? '', arguments: '' }, }, ], }, finish_reason: null, }, model); } if (eventType === 'response.completed') { return handleCompletedEvent(dataStr, model); } return null; } function handleCompletedEvent(dataStr, model) { const data = (0, chatgpt_helpers_1.safeParse)(dataStr); const response = (0, chatgpt_helpers_1.isObjectRecord)(data?.response) ? data.response : undefined; const responseUsage = response?.usage; const inputDetails = responseUsage?.input_tokens_details; const cachedTokens = inputDetails?.cached_tokens ?? 0; const usage = responseUsage ? { prompt_tokens: responseUsage.input_tokens ?? 0, completion_tokens: responseUsage.output_tokens ?? 0, total_tokens: responseUsage.total_tokens ?? 0, cache_read_tokens: cachedTokens, cache_creation_tokens: 0, } : undefined; const responseOutput = Array.isArray(response?.output) ? response.output : []; const hasFunctionCalls = responseOutput.some((item) => item.type === 'function_call'); const finish = (0, chatgpt_helpers_1.formatSSE)({ delta: {}, finish_reason: hasFunctionCalls ? 'tool_calls' : 'stop' }, model, usage); return `${finish}\ndata: [DONE]\n\n`; } function collectChatGptSseResponse(sseText, model) { let text = ''; const toolCallMap = new Map(); let usage; let hasFunctionCalls = false; const events = sseText.split('\n\n'); for (const event of events) { const lines = event.split('\n'); let eventType = ''; let dataStr = ''; for (const line of lines) { if (line.startsWith('event: ')) eventType = line.slice(7).trim(); else if (line.startsWith('data: ')) dataStr = line.slice(6); } if (!eventType || !dataStr) continue; const data = (0, chatgpt_helpers_1.safeParse)(dataStr); if (!data) continue; if (eventType === 'response.output_text.delta') { text += typeof data.delta === 'string' ? data.delta : ''; } else if (eventType === 'response.output_item.added') { const item = (0, chatgpt_helpers_1.isObjectRecord)(data.item) ? data.item : undefined; if (item?.type === 'function_call') { const idx = typeof data.output_index === 'number' ? data.output_index : toolCallMap.size; toolCallMap.set(idx, { id: item.call_id ?? '', type: 'function', function: { name: item.name ?? '', arguments: '' }, }); } } else if (eventType === 'response.function_call_arguments.delta') { const idx = typeof data.output_index === 'number' ? data.output_index : 0; const tc = toolCallMap.get(idx); if (tc) { tc.function.arguments += typeof data.delta === 'string' ? data.delta : ''; } } else if (eventType === 'response.completed') { const response = (0, chatgpt_helpers_1.isObjectRecord)(data.response) ? data.response : undefined; const respUsage = response?.usage; if (respUsage) { const inputDetails = respUsage.input_tokens_details; usage = { prompt_tokens: respUsage.input_tokens ?? 0, completion_tokens: respUsage.output_tokens ?? 0, total_tokens: respUsage.total_tokens ?? 0, cache_read_tokens: inputDetails?.cached_tokens ?? 0, cache_creation_tokens: 0, }; } const output = Array.isArray(response?.output) ? response.output : []; hasFunctionCalls = output.some((item) => item.type === 'function_call'); } } const toolCalls = [...toolCallMap.values()]; const message = { role: 'assistant', content: text || null }; if (toolCalls.length > 0) message.tool_calls = toolCalls; return { id: `chatcmpl-${(0, crypto_1.randomUUID)().replace(/-/g, '').slice(0, 29)}`, object: 'chat.completion', created: Math.floor(Date.now() / 1000), model, choices: [ { index: 0, message, finish_reason: hasFunctionCalls || toolCalls.length > 0 ? 'tool_calls' : 'stop', }, ], usage: usage ?? { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }, }; } //# sourceMappingURL=chatgpt-adapter.js.map