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

86 lines (85 loc) • 4.39 kB
/** * Lifecycle callback firing + dedupe. * * The same thrown error can travel through multiple layers that each * want to surface it to the consumer's `onError`: * - LifecycleMiddleware's `wrapGenerate` / `wrapStream` catch * - `BaseProvider.wrapStreamWithLifecycleCallbacks` (raw-fetch * streaming providers that bypass AI SDK middleware) * - `BaseProvider.fireLifecycleErrorCallback` (top-level provider catch) * - `NeuroLink.generate()` / `NeuroLink.stream()` (early-resolution * failures, before the language model is wrapped) * * Without a shared dedupe these layers would fire `onError` multiple * times for one logical failure. This module stamps a non-enumerable * `Symbol.for("neurolink.onErrorFired")` on the error the first time * a firing site is reached; subsequent sites observe the stamp and * skip their own fire. * * `Symbol.for` (rather than a local Symbol) so the same key works * across modules — anyone who can read the symbol can read the stamp. * * Frozen / sealed / non-extensible errors: `Object.defineProperty` * throws, we catch and proceed. Worst case is a single duplicate fire * (the pre-shared-marker behaviour). `WeakSet`-based bookkeeping * would handle this case cleanly but the symbol stamp is preferred * for cross-realm consistency: a Symbol.for-keyed property survives * structuredClone / cross-realm postMessage where a closed-over * WeakSet does not. */ import type { LifecycleErrorPayload, LifecycleMiddlewareConfig, OnErrorCallback } from "../types/index.js"; /** * Read the consumer-facing lifecycle callbacks buried inside a request's * middleware blob. The parameter is `unknown` on purpose: request options * arrive as several structurally-unrelated shapes (StreamOptions, * TextGenerationOptions), and the lifecycle branch is an optional add-on * none of them declare — a single structural view keeps the read cast-free * at every call site. * * Shared (moved out of `BaseProvider`) because two independent firing sites * now need the same read: `BaseProvider.wrapStreamWithLifecycleCallbacks` * (every provider's stream) and `GoogleVertexProvider.wrapStreamResultWithLifecycle` * (Vertex's own native-stream firer, which must check the SAME * `isLifecycleStreamCallbacksOwnedByBaseProvider` marker on the SAME nested * config object that BaseProvider's wrapper reads from `options`, so that * both sites agree on who owns firing without a second marker). */ export declare function getLifecycleMiddlewareConfig(options: unknown): LifecycleMiddlewareConfig | undefined; /** * Stamp a stream lifecycle config object with * `STREAM_CALLBACKS_OWNED_BY_BASE_PROVIDER`. See that symbol's doc comment * for who may call this (only `applyStreamLifecycleMiddleware`) and why. */ export declare function markLifecycleStreamCallbacksOwnedByBaseProvider(config: object): void; /** * True when `markLifecycleStreamCallbacksOwnedByBaseProvider` already * stamped this config. The model-level lifecycle middleware reads this to * decide whether to fire its own onChunk/onFinish for a stream config. */ export declare function isLifecycleStreamCallbacksOwnedByBaseProvider(config: unknown): boolean; /** * Returns true when `markLifecycleErrorFired` or a previous * `fireOnErrorOnce` call has already stamped this error. */ export declare function hasLifecycleErrorFired(error: unknown): boolean; /** * Stamps the error as already-fired without invoking any callback. * Use this from sites that already invoked `onError` via their own * path (e.g. a provider-specific raw-fetch stream wrapper) so the * shared dedupe still works. */ export declare function markLifecycleErrorFired(error: unknown): void; /** * Fire the consumer's `onError` once per logical failure. * * - No-op when `onError` is missing. * - No-op when the error is already stamped (any prior layer fired). * - Otherwise: stamps the error, then invokes the callback. * * The callback is fire-and-forget; rejections are swallowed so a * faulty handler can't mask the original throw. Callers that need * to AWAIT the callback (e.g. to enforce a timeout) should use * `hasLifecycleErrorFired` + `markLifecycleErrorFired` directly and * run the callback themselves. */ export declare function fireOnErrorOnce(onError: OnErrorCallback | undefined, error: unknown, payload: LifecycleErrorPayload): void;