UNPKG

@juspay/neurolink

Version:

The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002

161 lines (160 loc) • 5.98 kB
import { promises as fs } from "node:fs"; import { sniffImageMimeType } from "./imageDetection.js"; import { detectIsoBmffImageMimeType, hasFtypBoxSignature } from "./isoBmff.js"; const REMOTE_URL = /^https?:\/\//i; const URL_SCHEME = /^[a-z][a-z0-9+.-]*:\/\//i; const MAX_ECHOED_PATH_CHARS = 200; const DATA_URL = { image: /^data:image\/[a-z0-9.+-]+;base64,/i, video: /^data:video\/[a-z0-9.+-]+;base64,/i, }; const WEBM_HEADER = [0x1a, 0x45, 0xdf, 0xa3]; const NOT_RECOGNISED = { image: "is not a recognised image format", video: "is not a recognised video format (MP4, MOV or WebM)", }; /** * MP4 and MOV share the ISO-BMFF box. An AVIF or HEIC image shares it too, and * is not a video. */ function sniffVideoMimeType(buffer) { if (hasFtypBoxSignature(buffer) && detectIsoBmffImageMimeType(buffer) === null) { return "video/mp4"; } return WEBM_HEADER.every((byte, i) => buffer[i] === byte) ? "video/webm" : null; } function encode(buffer, kind, label) { const mime = kind === "image" ? sniffImageMimeType(buffer) : sniffVideoMimeType(buffer); return mime === null ? { status: "error", message: `${label} ${NOT_RECOGNISED[kind]}.` } : { status: "ok", dataUrl: `data:${mime};base64,${buffer.toString("base64")}`, }; } /** * A path can be echoed in an error. A stray base64 blob passed as a path cannot, * so past a short length only its size is shown. */ function describePath(path) { return path.length <= MAX_ECHOED_PATH_CHARS ? `(${path})` : `(a ${path.length}-character string)`; } /** A file over the limit is refused from `stat`, before it is read into memory. */ async function readMediaFile(path, label, maxBytes) { try { const stats = await fs.stat(path); if (!stats.isFile()) { return { status: "error", message: `${label} ${describePath(path)} is not a file.`, }; } if (stats.size > maxBytes) { return { status: "error", message: `${label} ${describePath(path)} is ${stats.size} bytes; a request may carry at most ${maxBytes}.`, }; } return { status: "ok", buffer: await fs.readFile(path) }; } catch (error) { const reason = error instanceof Error && "code" in error ? String(error.code) : "unreadable"; return { status: "error", message: `Could not read ${label} ${describePath(path)}: ${reason}.`, }; } } /** * Nothing here echoes a `data:` URL or a remote URL: either can carry a * credential or megabytes of base64, and neither belongs in an error message. */ async function toDataUrl(source, kind, label, maxBytes) { if (typeof source !== "string") { return source.length === 0 ? { status: "error", message: `${label} is an empty Buffer.` } : encode(source, kind, label); } const value = source.trim(); if (value === "") { return { status: "error", message: `${label} is an empty string.` }; } if (REMOTE_URL.test(value)) { return { status: "error", message: `${label} is a remote URL. Media is not fetched for you; pass a Buffer, a file path or a data: URL.`, }; } if (/^data:/i.test(value)) { return DATA_URL[kind].test(value) ? { status: "ok", dataUrl: value } : { status: "error", message: `${label} is a data: URL that is not base64 ${kind} content.`, }; } if (URL_SCHEME.test(value)) { return { status: "error", message: `${label} uses an unsupported URL scheme. Pass a Buffer, a file path or a data: URL.`, }; } const file = await readMediaFile(value, label, maxBytes); return file.status === "ok" ? encode(file.buffer, kind, label) : file; } /** * Turn the caller's images and video into `data:` URLs, or say why not. * Runs before any request, so every refusal costs nothing and none is retried. */ export async function prepareDecisionMedia(request, limits, vendor, alternatives) { const images = request.images ?? []; if (images.length === 0 && request.video === undefined) { return { status: "prepared", media: undefined }; } if (!limits) { const use = alternatives.length > 0 ? ` Use a decision provider that does: ${alternatives.join(", ")}.` : ""; return { status: "refused", message: `${vendor} does not accept images or video.${use}`, }; } if (images.length > limits.maxImages) { return { status: "refused", message: `${vendor} accepts at most ${limits.maxImages} images per request; this one has ${images.length}.`, }; } if (request.video !== undefined && !limits.video) { return { status: "refused", message: `${vendor} does not accept video.` }; } const prepared = []; for (const [index, image] of images.entries()) { const converted = await toDataUrl(image, "image", `Image ${index + 1}`, limits.maxRequestBytes); if (converted.status === "error") { return { status: "refused", message: converted.message }; } prepared.push(converted.dataUrl); } let video; if (request.video !== undefined) { const converted = await toDataUrl(request.video, "video", "The video", limits.maxRequestBytes); if (converted.status === "error") { return { status: "refused", message: converted.message }; } video = converted.dataUrl; } const bytes = prepared.reduce((sum, url) => sum + url.length, video?.length ?? 0); return { status: "prepared", media: { images: prepared, ...(video ? { video } : {}), bytes }, }; }