/** * Shared, provider-neutral lifecycle/boot-progress contract for the on-device + remote LLM batteries. * * @remarks * Each battery already exposes a provider-shaped `onInitProgress` covering only the **download** phase * (and shaped differently per provider). This module adds a NORMALIZED layer on top: a coarse phase * machine — `loading → compiling → ready → generating → complete` (or `error`) — observable via an * aggregate firehose callback ({@link BatteryLifecycleHooks.onLifecycle}) AND targeted per-phase hooks. * It exists because the WebGPU/wasm **boot** between download and first token (engine/graph/shader * compilation, accelerator registration) was otherwise invisible to a consumer, and because there was no * cross-battery notion of "what phase is this model in right now." That boot span is now surfaced as the * `compiling` phase — a COARSE marker (the runtimes expose the boundary, not a granular progress stream). * * The hooks are OPT-IN and additive: `onInitProgress` is unchanged; where a provider reports download * progress, the battery ALSO forwards it into a `loading` lifecycle report (with normalized `progress` * 0..1 and the raw payload on `raw`). This submodule is private to the bundled batteries — consumers * import the re-exported names from each battery's public subpath, never from here. */ import type { GpuBudget } from "./gpu_budget"; /** The coarse lifecycle phases a battery transitions through. */ export type BatteryLifecyclePhase = 'loading' | 'compiling' | 'ready' | 'generating' | 'complete' | 'error'; /** Which battery emitted a lifecycle report. */ export type BatteryLifecycleBattery = 'transformers_js' | 'litert_lm' | 'webllm' | 'transformers_js_embed' | 'transformers_js_stt' | 'transformers_js_caption' | 'transformers_js_generation' | 'transformers_js_tts' | 'local_diffusion_generation' | 'tesseract_js_ocr'; /** A single normalized lifecycle report. */ export interface BatteryLifecycleReport { /** The phase being entered. */ phase: BatteryLifecyclePhase; /** The battery that produced this report. */ battery: BatteryLifecycleBattery; /** Best-effort model identifier (the `model` option; `''` / `''` when not a string). */ model: string; /** ISO-8601 timestamp stamped when the report was emitted. */ at: string; /** Human-readable detail, e.g. `'booting WebGPU runtime'`. */ detail?: string; /** * Normalized progress in `0..1`, when the provider reports it. Emitted during the `loading` phase * (weights download/compile) and — for engines that stream per-step generation progress, e.g. the * diffusion denoise loop — during the `generating` phase as well. */ progress?: number; /** The provider's own progress payload, passed through verbatim (the `loading` or `generating` phase). */ raw?: unknown; /** The failure, populated only when `phase === 'error'`. */ error?: unknown; /** * The probed WebGPU device budget, populated on the `ready` phase for on-device batteries running on * the WebGPU EP. SURFACED, not enforced — the consumer reads this to know the per-allocation ceiling * (the wall an over-large context window hits) and choose its window accordingly. Absent on non-WebGPU * runtimes (Node/wasm) and on every other phase. See {@link GpuBudget}. */ gpuBudget?: GpuBudget; } /** A lifecycle report consumer. */ export type BatteryLifecycleCallback = (report: BatteryLifecycleReport) => void; /** * The opt-in lifecycle option block mixed into each battery's options interface. Every phase transition * fires {@link onLifecycle} (the firehose) AND the matching per-phase hook; subscribe to either or both. * All optional — omitting them leaves behavior byte-for-byte unchanged. */ export interface BatteryLifecycleHooks { /** Fires on EVERY phase transition (the firehose). */ onLifecycle?: BatteryLifecycleCallback; /** Weights/runtime loading — may fire repeatedly with `progress` as the provider reports it. */ onLoading?: BatteryLifecycleCallback; /** * Engine/graph/shader compilation after download, before the first token. A COARSE marker: the * on-device runtimes (LiteRT `Engine.create`, transformers.js `from_pretrained`) expose the boundary — * download done, opaque WebGPU/WASM graph build about to run — but NOT a progress stream, so `progress` * is usually absent. Often the slowest part of a cold start; without this it was invisible. */ onCompiling?: BatteryLifecycleCallback; /** Engine/pipeline resolved and cached, before the first generation. */ onReady?: BatteryLifecycleCallback; /** * The generate call is in progress (fires per turn). Fires once immediately before the provider call * for most engines; engines that stream per-step generation progress (e.g. a diffusion denoise loop) * fire it REPEATEDLY during generation, each carrying a `progress` in `0..1`. */ onGenerating?: BatteryLifecycleCallback; /** After the turn's output is parsed + persisted, before `ack` (fires per turn). */ onComplete?: BatteryLifecycleCallback; /** A load or generation failure (paired with `nack`). */ onError?: BatteryLifecycleCallback; } /** * Build a {@link BatteryLifecycleReport} (stamping `at`) and dispatch it to the firehose * ({@link BatteryLifecycleHooks.onLifecycle}) AND the per-phase hook for `phase`. A no-op when `hooks` * is undefined or carries no relevant callbacks. Defensive: each callback is invoked through * {@link safeInvoke}, so a throwing consumer never disrupts the battery. * * @param hooks - The merged lifecycle hooks (may be undefined). * @param battery - Which battery is emitting. * @param model - Best-effort model id string. * @param phase - The phase being entered. * @param extra - Optional `detail` / `progress` / `raw` / `error` fields. * @param now - Injectable clock for tests; defaults to luxon `DateTime.now().toISO()`. */ export declare const emitLifecycle: (hooks: BatteryLifecycleHooks | undefined, battery: BatteryLifecycleBattery, model: string, phase: BatteryLifecyclePhase, extra?: Partial>, now?: () => string) => void; /** Default {@link emitLifecycle}. */ export declare const defaultEmitLifecycle: (hooks: BatteryLifecycleHooks | undefined, battery: BatteryLifecycleBattery, model: string, phase: BatteryLifecyclePhase, extra?: Partial>, now?: () => string) => void;