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

135 lines (134 loc) • 7.01 kB
/** * Native video delivery to providers that can watch. * * ## The gap this closes * * Attaching a video produced a metadata block and a handful of downsampled * JPEGs: * * ## Video File: "demo.mp4" * Duration: 0:47 | 1920x1080 | avc | 30 fps | 3 keyframes extracted * * Every provider got the same treatment, Gemini included — and Gemini has * accepted inline video, with its audio track, the whole time. A 47-second * screen recording reached it as three still frames at 768px, so anything * between them (a click, a transition, a spoken sentence) was simply not in * the request. * * Two things make that worse than the equivalent audio gap was. First, the * frames come from ffmpeg, and ffmpeg is an optional dependency: on a machine * without it `extractKeyframes` returns an empty array and the model receives * the metadata line alone — a video attachment that conveys nothing visual at * all, with no error. Second, the metadata block answers exactly the questions * a test tends to ask ("how long is it?", "what resolution?"), so the failure * reads as working support until someone asks what happens in the clip. * * Delivering the file itself needs no ffmpeg, which is what makes this the * fix rather than "extract more frames". * * ## Provider scope * * A capability table rather than "send video to everyone". A provider that * cannot accept a video part answers with an opaque HTTP 400, which is * strictly worse than the metadata-plus-frames summary it would otherwise * have received — so an unlisted provider keeps the existing behaviour and * loses nothing. * * ## Why there is no transcode path here * * `audioFormatSupport` re-encodes a container Gemini will not read, because a * voice memo is seconds of audio and the conversion is cheap. The video * equivalent is not: re-encoding a half-hour recording is minutes of CPU * inside a generation request, for a payload that will usually breach the * inline ceiling anyway. An unsupported container therefore falls back to * keyframes, which is the behaviour that already existed. * * @module adapters/videoFormatSupport */ import type { MultimodalVideoEntry, VideoDeliveryDecision, VideoProviderConfig } from "../types/index.js"; /** * Per-provider video handling. * * Keys are lowercase canonical names and the aliases each provider is * addressed by elsewhere in the codebase; `getVideoProviderConfig` normalises * before looking up, so a caller never has to know which spelling it holds. * * Only Google's Gemini front ends carry `supportsNativeVideo: true`. The rest * are listed deliberately rather than left to the unknown-provider default: * an explicit row is how `getVideoProviderConfig` distinguishes "this provider * takes frames" from "nobody has looked at this provider yet", and the frame * budgets differ enough between them to be worth stating. */ export declare const VIDEO_PROVIDER_CONFIGS: Readonly<Record<string, VideoProviderConfig>>; /** * The video-handling row for `provider`, or null when there is none. * * Null means "not described here", which is not the same as "takes frames": * an unrecognised provider still receives keyframes, because that is the * pipeline's default, but nothing in this table asserts it will understand * them. Callers wanting the safe reading should treat null as no native * video, which is what {@link supportsNativeVideo} does. * * `model` disambiguates Vertex: `GoogleVertexProvider` sends any model whose * id contains "claude" to the native Anthropic SDK, which never sees the * video part at all, so a Claude-on-Vertex request gets the frame-extraction * row (the same one plain `"anthropic"` gets) rather than the Gemini row the * provider name alone would suggest. Omitting `model` keeps the previous, * provider-only lookup, which is still correct for every non-Vertex caller — * AI Studio and the bare `"gemini"` alias never host Claude models. */ export declare function getVideoProviderConfig(provider: string, model?: string): VideoProviderConfig | null; /** Whether `provider` (optionally, running `model`) can be handed raw video * bytes. See {@link getVideoProviderConfig} for the Claude-on-Vertex case. */ export declare function supportsNativeVideo(provider: string, model?: string): boolean; /** Whether `mimeType` is a container a native provider will read as-is. */ export declare function isNativeVideoMimeType(mimeType: string): boolean; /** * Rough token cost of putting one video in front of one provider. * * Two quite different prices, because two quite different payloads: a native * provider is billed for the clip's duration, a frame-extraction provider for * the frames it is sent. Asking for one number without saying which mode * applies is how a budget ends up an order of magnitude out, so the provider * decides the formula rather than the caller. * * An estimate, not a quote — resolution settings, prompt text and provider * pricing changes all move the real figure. * * @param options.provider - Provider the video is destined for. * @param options.durationSec - Clip length. 0 when unknown. * @param options.frameCount - Frames that would be extracted. Defaults to the * provider's `recommendedFrameCount`, or 8 for an unlisted provider. * @param options.hasTranscription - Whether a speech transcript is included. * Ignored for a native provider, which already hears the audio track. */ export declare function estimateVideoTokens(options: { provider: string; durationSec: number; frameCount?: number; hasTranscription?: boolean; }): number; /** * Whether one specific clip may go to one specific provider as bytes. * * Every rejection carries a reason the caller can log verbatim, because the * user-visible symptom of all of them is identical — "it only described the * file" — and the remedies are not: shorten the clip, re-encode the * container, or switch provider. * * An unknown duration is not a rejection. Probing fails on exotic containers * and on machines without ffmpeg, and refusing a 2 MB clip because nothing * measured it would reintroduce the frames-only behaviour precisely where * frames are least likely to be available. * * @param priorNativeVideoBytes - Source bytes already committed to the * *same request's* inline budget: every inline image (keyframes * included), PDF and audio part assembled before the clips, plus each * clip accepted earlier. The caller accumulates this running total, since * this function is otherwise stateless and cannot see the rest of the * request. Defaults to 0, which makes the size check below a pure * per-clip check. * @param model - See {@link getVideoProviderConfig}: disambiguates a * Claude-on-Vertex request from a real Gemini-on-Vertex one. */ export declare function canDeliverVideoNatively(provider: string, video: MultimodalVideoEntry, priorNativeVideoBytes?: number, model?: string): VideoDeliveryDecision;