import crypto from 'node:crypto'; function secureEqual(left, right) { const a = Buffer.from(String(left ?? '')); const b = Buffer.from(String(right ?? '')); return a.length === b.length && crypto.timingSafeEqual(a, b); } function normalizeMessages(value) { if (!Array.isArray(value) || !value.length || value.length > 20) return null; const messages = value.map((message) => ({ role: ['system', 'user', 'assistant'].includes(message?.role) ? message.role : '', content: String(message?.content ?? '').trim(), })); if (messages.some((message) => !message.role || !message.content || message.content.length > 120_000)) { return null; } if (messages.reduce((total, message) => total + message.content.length, 0) > 150_000) return null; return messages; } export async function executeDeepSearchLlmGateway({ llmProviderService, expectedSecret, providedSecret, input = {}, } = {}) { if (!expectedSecret || !secureEqual(expectedSecret, providedSecret)) { return { status: 401, body: { ok: false, message: 'Deep Search LLM gateway credential is invalid' } }; } if (!llmProviderService?.createChatCompletion) { return { status: 503, body: { ok: false, message: 'LLM provider service is unavailable' } }; } const providerKeyId = String(input.providerKeyId ?? '').trim(); const model = String(input.model ?? '').trim(); const messages = normalizeMessages(input.messages); if (!/^[a-zA-Z0-9._:-]{1,128}$/.test(providerKeyId) || !model || model.length > 200 || !messages) { return { status: 400, body: { ok: false, message: 'Invalid Deep Search LLM request' } }; } const result = await llmProviderService.createChatCompletion({ providerKeyId, model, messages, temperature: Math.max(0, Math.min(1, Number(input.temperature ?? 0.2) || 0.2)), }); if (!result?.ok) { return { status: Number(result?.status) >= 400 ? Number(result.status) : 502, body: { ok: false, message: result?.message || 'Deep Search LLM request failed' }, }; } return { status: 200, body: { ok: true, reply: result.reply, providerKeyId: result.providerKeyId, providerId: result.providerId, model: result.model, usage: result.usage ?? null, }, }; }