Skip to content
Ask
Reference

Engine Reference

The @inbrowser/model/local export: the engine factory, the Engine surface, the event vocabulary, and the stream transformers.

This page describes the opt-in @inbrowser/model/local export: the engine factory, the Engine surface, the event vocabulary, and the stream transformers.

For preset data and the shared static types they carry, see ./presets.md. For the worker helpers, see ./adapters-and-worker.md.

This page covers the on-device engine. The same package also owns the cloud providers and the shared ModelClient contract — see the package README and contract source for those.

Exports

Everything on this page is imported from @inbrowser/model/local.

SymbolsWhat they are
createEngine, definePreset, parseToolCalls, splitThinking, and engine typesThe on-device engine surface
The six bundled presetsdeepseek_r1_qwen_1_5b, gemma4_E2B, gemma4_E4B, qwen2_5_coder_1_5b, qwen3_1_7b, smollm2_360m. See ./presets.md.
createEngineModelClientWraps an Engine as a ModelClient. See ./adapters-and-worker.md.
hostEngineInWorker, connectWorkerEngineWorker host/connect helpers. See ./adapters-and-worker.md.

The removed @inbrowser/model/relay and @inbrowser/model/agent adapter subpaths are gone. The engine is now a ModelClient via createEngineModelClient (from @inbrowser/model/local). See ./adapters-and-worker.md.

createEngine

TS
function createEngine(opts: CreateEngineOpts): Engine;

Constructs an Engine bound to a single model. Weight loading is deferred until ensureReady() or the first generate() call. Spread a ModelPreset into the call along with optional EngineHooks.

TS
import { createEngine, gemma4_E2B } from '@inbrowser/model/local';

const engine = createEngine({ ...gemma4_E2B, onLoadProgress: console.log });

CreateEngineOpts is ModelPreset & EngineHooks.

ModelPreset

A fully-specified model configuration.

FieldTypeDescription
modelModelRefHF Hub locator.
dtypeDtypeWeight/activation precision.
backendBackendONNX Runtime Web execution backend.
capabilitiesEngineCapabilitiesStatic, pre-load capability declaration.
chatTemplate?(messages: ReadonlyArray<EngineMessage>) => stringOptional override of the tokenizer’s bundled chat template.

EngineHooks

Non-preset construction options.

FieldTypeDescription
weightsBaseUrl?stringBase URL for weight fetches. Defaults to huggingface.co. The engine appends {modelId}/{file}. With multiple engines spanning different remotes, the last one to load wins.
minGpuMemoryMb?numberMinimum reported GPU memory in MB. Below this, ensureReady() rejects rather than crashing mid-load.
onLoadProgress?(p: LoadProgress) => voidCallback for load progress. Equivalent to subscribing via on('load', ...).

ModelRef

HF Hub repo id with an optional pinned revision.

FieldTypeDescription
modelIdstringHF Hub repo id.
revision?stringOptional revision. Pin for reproducibility; main drifts.

Dtype

TS
type Dtype = 'q4f16' | 'q8' | 'fp16' | 'fp32';
ValueMeaning
q4f164-bit int weights, fp16 activations.
q88-bit int weights.
fp16Half precision throughout.
fp32Full precision.

Backend

TS
type Backend = 'auto' | 'webgpu' | 'wasm';
ValueMeaning
autoProbe navigator.gpu; fall back to wasm if absent.
webgpuWebGPU compute pipeline.
wasmSIMD CPU fallback. Always available, much slower.

EngineCapabilities

Static capability declaration carried on ModelPreset.capabilities.

FieldTypeDescription
supportsToolsbooleanWhether the model’s chat template accepts tool declarations and emits tool-call envelopes.
supportsVisionbooleanWhether the model accepts image input.
supportsAudiobooleanWhether the model accepts audio input.
contextWindownumberContext window in tokens.
supportsThinkingbooleanWhether the model emits thinking traces when prompted.
thinkingTags?{ openTag: string; closeTag: string; implicitOpen?: boolean; stripTokens?: ReadonlyArray<string> }When set, describes the reasoning-tag wrapper the model uses. Shape matches ThinkingSplitOpts so the preset can be spread into splitThinking().

Engine

The runtime engine. One engine per model instance.

MemberSignatureDescription
modelreadonly ModelRefThe bound model locator.
statereadonly EngineStateCurrent lifecycle state.
capabilitiesreadonly EngineCapabilitiesStatic capabilities, equal to preset.capabilities.
ensureReady() => Promise<void>Idempotent. Loads weights and resolves once state is 'ready'.
on<K extends keyof EngineEventMap>(event: K, handler: (value: EngineEventMap[K]) => void) => () => voidSubscribe to a lifecycle event. Returns an unsubscribe function.
generate(messages: ReadonlyArray<EngineMessage>, opts?: GenerateOpts) => AsyncIterable<EngineEvent>Run inference, yielding EngineEvents.
dispose() => Promise<void>Release GPU buffers and tokenizer state. The engine is unusable afterward.

EngineState

TS
type EngineState = 'idle' | 'loading' | 'ready' | 'error' | 'disposed';

EngineEventMap

The events engine.on() subscribes to.

EventValue typeDescription
stateEngineStateEmitted on each state transition.
loadLoadProgressEmitted during cold start.

LoadProgress

Progress for the observable phases of cold start.

TS
type LoadProgress =
  | { phase: 'fetch'; file: string; loadedBytes: number; totalBytes: number }
  | { phase: 'init'; backend: Backend }
  | { phase: 'warmup'; tokensGenerated: number }
  | { phase: 'ready' };
PhaseMeaning
fetchWeights flowing from HF Hub (or weightsBaseUrl) into the browser Cache API. Cached after first run.
initONNX Runtime compiling the graph for backend.
warmupFirst forward pass primes WebGPU pipelines and kernel caches.
readyTerminal phase; safe to generate.

generate

TS
generate(
  messages: ReadonlyArray<EngineMessage>,
  opts?: GenerateOpts,
): AsyncIterable<EngineEvent>;

Applies the model’s chat template to messages, drives the decode loop, and yields EngineEvents. The terminal event is usage on success, or error on failure.

TS
for await (const evt of engine.generate([{ role: 'user', text: 'Hello' }])) {
  if (evt.kind === 'token') process.stdout.write(evt.text);
}

EngineMessage

Engine-side chat message. The engine is toolless: there is no tool role.

FieldTypeDescription
role'system' | 'user' | 'assistant'Message role.
textstringMessage text.
media?ReadonlyArray<MediaPart>Inline media for multimodal models. Dropped on the text-only path.

MediaPart

TS
type MediaPart =
  | { kind: 'image'; data: Blob | ArrayBuffer; mimeType: string }
  | { kind: 'audio'; data: Blob | ArrayBuffer; mimeType: string };

GenerateOpts

FieldTypeDescription
maxNewTokens?numberMaximum tokens to decode. Defaults to 512.
temperature?numberSampling temperature. When set, enables sampling (do_sample).
topP?numberNucleus sampling cutoff.
topK?numberTop-k sampling cutoff.
stop?ReadonlyArray<string>Stop sequences. Accepted but not yet enforced.
signal?AbortSignalCaller-side cancellation. Aborting stops the decode loop.
tools?ReadonlyArray<ToolSpec>Tool declarations. Honored only when the preset declares capabilities.supportsTools: true; the output stream is then wrapped so tool_call events are emitted.
enableThinking?booleanOpt into the model’s thinking mode. Honored only when the preset declares capabilities.supportsThinking: true.

ToolSpec

Tool declaration matching the OpenAI function-calling format.

TS
interface ToolSpec {
  type: 'function';
  function: {
    name: string;
    description: string;
    parameters: unknown;
  };
}

EngineEvent

The engine’s narrow event vocabulary.

TS
type EngineEvent =
  | { kind: 'token'; text: string }
  | { kind: 'thinking'; text: string }
  | { kind: 'tool_call'; id: string; name: string; args: unknown }
  | { kind: 'usage'; promptTokens: number; outputTokens: number; decodeMs: number }
  | { kind: 'error'; message: string; recoverable: boolean };
KindFieldsDescription
tokentext: stringDecoded text per decode step.
thinkingtext: stringReasoning content. The engine never produces this directly; it is emitted by splitThinking(). The variant lives on EngineEvent so one switch (kind) handles wrapped and raw streams.
tool_callid: string, name: string, args: unknownA tool invocation. id is locally generated. name is the tool name as the model wrote it. args is the parsed object, or { _raw: string } when JSON parsing fails.
usagepromptTokens: number, outputTokens: number, decodeMs: numberTerminal accounting, once per stream.
errormessage: string, recoverable: booleanA failure. recoverable distinguishes retryable transients from terminal failures.

definePreset

TS
function definePreset<P extends ModelPreset>(p: P): P;

Compile-time identity helper. At runtime it returns its argument unchanged; its value is the completeness check it enforces on caller-defined presets. Used to author both the bundled presets and community presets.

TS
import { definePreset } from '@inbrowser/model/local';

export const myPreset = definePreset({
  model: { modelId: 'org/model-ONNX' },
  dtype: 'q4f16',
  backend: 'auto',
  capabilities: {
    supportsTools: false,
    supportsVision: false,
    supportsAudio: false,
    contextWindow: 8_192,
    supportsThinking: false,
  },
});

Stream transformers

The engine emits only token, usage, and error events. Two transformers wrap an AsyncIterable<EngineEvent> and re-emit the same shape with additional tool_call or thinking events surfaced. generate() applies parseToolCalls internally when tools are passed to a tools-capable preset; splitThinking is applied by the consumer.

parseToolCalls

TS
function parseToolCalls(
  source: AsyncIterable<EngineEvent>,
  opts?: ToolCallParseOpts,
): AsyncIterable<EngineEvent>;

Detects native tool-call envelopes in the token stream and re-emits them as tool_call events. thinking, usage, and error events forward unchanged. token events outside an envelope forward as token; inside an envelope they are buffered and converted to a single tool_call on close.

ToolCallParseOpts:

FieldTypeDescription
format?'qwen'Envelope format. Default 'qwen': <tool_call>...</tool_call> with a JSON body carrying name and arguments (parameters is also accepted). Malformed JSON falls through as { _raw: string }.
generateId?() => stringOverride id generator. Default uses a short random suffix.

splitThinking

TS
function splitThinking(
  source: AsyncIterable<EngineEvent>,
  opts?: ThinkingSplitOpts,
): AsyncIterable<EngineEvent>;

Splits reasoning-tagged content out of the token stream, re-emitting text inside the tags as thinking events. usage and error events forward unchanged.

TS
for await (const evt of splitThinking(engine.generate(msgs))) {
  if (evt.kind === 'thinking') showReasoning(evt.text);
  else if (evt.kind === 'token') showOutput(evt.text);
}

ThinkingSplitOpts:

FieldTypeDescription
openTag?stringTag that opens a reasoning block. Default <think>.
closeTag?stringTag that closes a reasoning block. Default </think>. Must be non-empty.
implicitOpen?booleanWhen true, the stream is treated as starting inside the thinking channel; the opening tag is implicit and the first closeTag ends the block. Default false.
stripTokens?ReadonlyArray<string>Literal substrings to strip from token events after mode classification. Content inside thinking blocks is unaffected. Default [].

A preset’s capabilities.thinkingTags is shape-compatible with ThinkingSplitOpts, so it can be spread directly: splitThinking(stream, preset.capabilities.thinkingTags).