manifest
Version:
Self-hosted Manifest LLM router with embedded server, SQLite database, and dashboard
314 lines • 10.3 kB
JavaScript
;
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