UNPKG

manifest

Version:

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

314 lines 10.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.toGoogleRequest = toGoogleRequest; exports.fromGoogleResponse = fromGoogleResponse; exports.transformGoogleStreamChunk = transformGoogleStreamChunk; const crypto_1 = require("crypto"); const UNSUPPORTED_SCHEMA_FIELDS = new Set([ 'patternProperties', 'additionalProperties', '$schema', '$id', '$ref', '$defs', 'definitions', 'allOf', 'anyOf', 'oneOf', 'not', 'if', 'then', 'else', 'dependentSchemas', 'dependentRequired', 'unevaluatedProperties', 'unevaluatedItems', 'contentMediaType', 'contentEncoding', 'examples', 'default', 'const', 'title', ]); function sanitizeSchema(schema, isPropertiesMap = false) { if (schema === null || schema === undefined || typeof schema !== 'object') { return schema; } if (Array.isArray(schema)) { return schema.map((item) => sanitizeSchema(item)); } const result = {}; for (const [key, value] of Object.entries(schema)) { if (!isPropertiesMap && UNSUPPORTED_SCHEMA_FIELDS.has(key)) continue; result[key] = sanitizeSchema(value, key === 'properties'); } return result; } function safeParseArgs(args) { try { return JSON.parse(args || '{}'); } catch { return {}; } } function mapRole(role) { if (role === 'assistant') return 'model'; if (role === 'system') return 'user'; return 'user'; } function messageToContent(msg, signatureLookup) { const parts = []; if (typeof msg.content === 'string') { parts.push({ text: msg.content }); } else if (Array.isArray(msg.content)) { for (const block of msg.content) { if (block.type === 'text' && typeof block.text === 'string') { parts.push({ text: block.text }); } } } if (Array.isArray(msg.tool_calls)) { for (const tc of msg.tool_calls) { const functionCall = { name: tc.function.name, args: safeParseArgs(tc.function.arguments), }; const sig = tc.thought_signature; if (sig) { functionCall.thought_signature = sig; } else if (signatureLookup) { const cached = signatureLookup(tc.id); if (cached) functionCall.thought_signature = cached; } parts.push({ functionCall }); } } if (msg.role === 'tool' && typeof msg.content === 'string') { return { role: 'user', parts: [ { functionResponse: { name: msg.tool_call_id || 'unknown', response: { result: msg.content }, }, }, ], }; } if (parts.length === 0) return null; return { role: mapRole(msg.role), parts }; } function convertTools(tools) { if (!tools || tools.length === 0) return undefined; const declarations = tools .map((t) => { const fn = t.function; if (!fn) return null; return { name: fn.name, description: fn.description, parameters: fn.parameters ? sanitizeSchema(fn.parameters) : undefined, }; }) .filter(Boolean); if (declarations.length === 0) return undefined; return [{ functionDeclarations: declarations }]; } function toGoogleRequest(body, _model, signatureLookup) { const messages = body.messages || []; const contents = []; const systemMsgs = messages.filter((m) => m.role === 'system'); const systemText = systemMsgs .map((m) => (typeof m.content === 'string' ? m.content : '')) .filter(Boolean) .join('\n'); for (const msg of messages) { if (msg.role === 'system') continue; const content = messageToContent(msg, signatureLookup); if (content) contents.push(content); } const result = { contents }; if (systemText) { result.systemInstruction = { parts: [{ text: systemText }], }; } const tools = convertTools(body.tools); if (tools) result.tools = tools; const genConfig = {}; if (body.max_tokens !== undefined) genConfig.maxOutputTokens = body.max_tokens; if (body.temperature !== undefined) genConfig.temperature = body.temperature; if (body.top_p !== undefined) genConfig.topP = body.top_p; if (Object.keys(genConfig).length > 0) result.generationConfig = genConfig; return result; } function fromGoogleResponse(googleResp, model) { const candidates = googleResp.candidates || []; const candidate = candidates[0]; if (!candidate) { return { id: `chatcmpl-${(0, crypto_1.randomUUID)()}`, object: 'chat.completion', created: Math.floor(Date.now() / 1000), model, choices: [], }; } const content = candidate.content; const parts = content?.parts || []; let textContent = ''; const toolCalls = []; for (const part of parts) { if (part.text) textContent += part.text; if (part.functionCall) { const fc = part.functionCall; const toolCall = { id: `call_${(0, crypto_1.randomUUID)()}`, type: 'function', function: { name: fc.name, arguments: JSON.stringify(fc.args) }, }; if (fc.thought_signature) toolCall.thought_signature = fc.thought_signature; toolCalls.push(toolCall); } } const message = { role: 'assistant', content: textContent || null }; if (toolCalls.length > 0) message.tool_calls = toolCalls; const extractedSignatures = []; for (const tc of toolCalls) { if (tc.thought_signature && typeof tc.id === 'string') { extractedSignatures.push({ toolCallId: tc.id, signature: tc.thought_signature, }); } } const usage = googleResp.usageMetadata; return { id: `chatcmpl-${(0, crypto_1.randomUUID)()}`, object: 'chat.completion', created: Math.floor(Date.now() / 1000), model, choices: [ { index: 0, message, finish_reason: mapFinishReason(candidate, toolCalls.length > 0) }, ], usage: usage ? { prompt_tokens: usage.promptTokenCount ?? 0, completion_tokens: usage.candidatesTokenCount ?? 0, total_tokens: usage.totalTokenCount ?? 0, prompt_tokens_details: { cached_tokens: usage.cachedContentTokenCount ?? 0 }, cache_read_tokens: usage.cachedContentTokenCount ?? 0, cache_creation_tokens: 0, } : undefined, ...(extractedSignatures.length > 0 ? { _extractedSignatures: extractedSignatures } : {}), }; } function mapFinishReason(candidate, hasToolCalls = false) { const reason = candidate.finishReason; if (!reason || reason === 'STOP') { return hasToolCalls ? 'tool_calls' : 'stop'; } const map = { MAX_TOKENS: 'length', SAFETY: 'content_filter', RECITATION: 'content_filter', }; return map[reason] ?? 'stop'; } function transformGoogleStreamChunk(chunk, model) { if (!chunk.trim()) return null; let data; try { data = JSON.parse(chunk); } catch { return null; } const candidates = data.candidates || []; const candidate = candidates[0]; const content = candidate?.content; const parts = content?.parts || []; const text = parts.map((p) => p.text || '').join(''); const toolCalls = []; for (const part of parts) { if (part.functionCall) { const fc = part.functionCall; const toolCall = { index: toolCalls.length, id: `call_${(0, crypto_1.randomUUID)()}`, type: 'function', function: { name: fc.name, arguments: JSON.stringify(fc.args ?? {}) }, }; if (fc.thought_signature) toolCall.thought_signature = fc.thought_signature; toolCalls.push(toolCall); } } let result = ''; if (text || toolCalls.length > 0) { const delta = {}; if (text) delta.content = text; if (toolCalls.length > 0) delta.tool_calls = toolCalls; result += `data: ${JSON.stringify({ id: `chatcmpl-${(0, crypto_1.randomUUID)()}`, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model, choices: [{ index: 0, delta, finish_reason: null }], })}\n\n`; } const usage = data.usageMetadata; if (usage) { const finishReason = mapFinishReason(candidate ?? {}, toolCalls.length > 0); result += `data: ${JSON.stringify({ id: `chatcmpl-${(0, crypto_1.randomUUID)()}`, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model, choices: [{ index: 0, delta: {}, finish_reason: finishReason }], })}\n\n`; result += `data: ${JSON.stringify({ id: `chatcmpl-${(0, crypto_1.randomUUID)()}`, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000), model, choices: [], usage: { prompt_tokens: usage.promptTokenCount ?? 0, completion_tokens: usage.candidatesTokenCount ?? 0, total_tokens: usage.totalTokenCount ?? 0, prompt_tokens_details: { cached_tokens: usage.cachedContentTokenCount ?? 0 }, cache_read_tokens: usage.cachedContentTokenCount ?? 0, cache_creation_tokens: 0, }, })}\n\n`; } return result || null; } //# sourceMappingURL=google-adapter.js.map