/** * Local Model Engine * * Singleton wrapper around `@huggingface/transformers` for server-side * local LLM inference. Provides lazy model loading and streaming text * generation via async generators. * * Uses ONNX Runtime for inference with q4 quantization, not q4f16, * due to a known ONNX bug with f16 LayerNorm on CPU. * * @module provider/local */ import { type ModelInfo } from "./model-catalog.js"; import { type LocalAIDevice } from "./env.js"; /** Chat message format expected by Transformers.js */ export interface ChatMessage { role: "system" | "user" | "assistant"; content: string; } /** Options for text generation */ export interface GenerateOptions { maxNewTokens?: number; temperature?: number; topP?: number; topK?: number; stopSequences?: string[]; } interface TransformersEnv { cacheDir: string; useBrowserCache: boolean; } interface TransformersOnnxBackend { webgpu?: { powerPreference?: string; }; } interface TransformersBackendConfig { onnx?: TransformersOnnxBackend; } /** Minimal Transformers.js stopping-criteria contract (see generation/stopping_criteria.js). */ interface StoppingCriteriaInstance { _call(inputIds: number[][], scores: unknown): boolean[]; } interface StoppingCriteriaListInstance extends StoppingCriteriaInstance { push(item: StoppingCriteriaInstance): void; extend(items: StoppingCriteriaInstance[]): void; } interface TransformersModule { env: TransformersEnv; backends?: TransformersBackendConfig; pipeline: (task: string, model: string, options: { dtype: ModelInfo["dtype"]; device: LocalAIDevice; }) => Promise; AutoProcessor: { from_pretrained(model: string): Promise; }; Gemma4ForConditionalGeneration: ConditionalModelConstructor; Qwen3_5ForConditionalGeneration: ConditionalModelConstructor; TextStreamer: new (tokenizer: unknown, options: { skip_prompt: boolean; skip_special_tokens: boolean; callback_function: (text: string) => void; }) => unknown; StoppingCriteria: new () => StoppingCriteriaInstance; StoppingCriteriaList: new () => StoppingCriteriaListInstance; } interface ConditionalProcessor { tokenizer: unknown; apply_chat_template(messages: unknown[], options: Record): string; batch_decode(outputs: unknown, options: Record): string[]; (...args: unknown[]): Promise>; } interface ConditionalModel { generate(options: Record): Promise; } interface ConditionalModelConstructor { from_pretrained(model: string, options: { dtype: ModelInfo["dtype"]; device: LocalAIDevice; }): Promise; } /** Tokenizer surface used to decode generated token ids back to text. */ interface DecodingTokenizer { decode(tokens: number[]): string; } /** Options object forwarded to the Transformers.js text-generation pipeline. */ export interface PipeOptions { max_new_tokens: number; temperature: number; top_p?: number; top_k?: number; do_sample: boolean; streamer: unknown; stopping_criteria?: StoppingCriteriaListInstance; } /** * Translate engine-level {@link GenerateOptions} into the options object passed * to the Transformers.js text-generation pipeline. * * Exported for unit testing the option-forwarding seam (notably that * `stopSequences` is not silently dropped) without downloading a model. */ export declare function buildPipeOptions(options: GenerateOptions, transformers: Pick, tokenizer: DecodingTokenizer, streamer: unknown): PipeOptions; /** * Lazily import @huggingface/transformers. * Only loads when actually needed, keeping startup fast when API keys are present. */ export declare function getTransformers(): Promise; export declare function buildConditionalGenerateOptions(options: GenerateOptions, transformers: Pick, tokenizer: DecodingTokenizer, streamer: unknown): PipeOptions; export declare function buildConditionalChatTemplateOptions(modelInfo: Pick): Record; /** * Eagerly verify that the local AI runtime (@huggingface/transformers + ONNX) * is available by loading the default model pipeline. * * Call this *before* creating the HTTP response stream so that failures surface * as a thrown error (503) rather than being swallowed inside a ReadableStream * (200 with in-band SSE error). * * In compiled binaries, `import("@huggingface/transformers")` itself fails * because `onnxruntime-node` eagerly `require()`s a native `.node` addon at * import time and the addon isn't embedded in the binary. In dev mode (Deno) * the native addon exists on disk so the import succeeds, but `pipeline()` can * still fail if the ONNX model files are missing. Either way this function * surfaces the error before the response stream is created. The pipeline is * cached after the first successful call, so subsequent checks are instant. */ export declare function verifyLocalRuntime(modelId?: string): Promise; /** * Generate text in a streaming fashion using an async generator. * * Yields individual tokens as they are generated by the model. */ export declare function generateStream(modelId: string, messages: ChatMessage[], options?: GenerateOptions): AsyncGenerator; /** * Generate text without streaming (full completion). */ export declare function generate(modelId: string, messages: ChatMessage[], options?: GenerateOptions): Promise; /** * Preload a model into memory. Useful for warming up on server start. */ export declare function preloadModel(modelId: string): Promise; /** * Check if a model is currently loaded in memory. */ export declare function isModelLoaded(modelId: string): boolean; export {}; //# sourceMappingURL=local-engine.d.ts.map