/**
 * Local Slow Lane Client: On-device inference via @mlc-ai/web-llm.
 *
 * Drop-in replacement for the network-based slow-lane-client. Instead of calling
 * a backend API, this client runs two models in the browser via WebGPU, in a
 * single MLCEngine, to reproduce the BE encoder's outputs on-device:
 *
 *   - Causal LM (SmolLM2-135M-Instruct): one decode step per word boundary. A
 *     registered LogitProcessor captures the raw next-token logits, which
 *     `computeBePayload` turns into a whole-word `lm_logits` payload — a faithful
 *     port of the BE `CausalLMEncoder._get_top_k_probs` (masked softmax over the
 *     vocab's first-tokens, prefix expansion, L2 reservation, log-space pooling).
 *   - Semantic embedder (Snowflake Arctic Embed S): produces the real 384-d
 *     `semantic_vector`. Inputs are wrapped as passages (see `wrapForArctic`) so
 *     the runtime vector lands in the same space as the precomputed word bin.
 *
 * ── Why main thread (no Web Worker)? ─────────────────────────────────────
 * The models are small enough (~640 MB combined VRAM) that WebGPU inference on
 * the main thread is viable:
 *
 *   - WebGPU GPU compute is inherently async (doesn't block the main thread)
 *   - CPU overhead (BE-parity post-processing) is a few ms
 *   - Per-inference latency is well within autocomplete expectations
 *     (~250 ms between word boundaries)
 *
 * This avoids all the complexity of Web Workers:
 *   - No CSP workarounds (blob URLs, inline scripts)
 *   - No bundler configuration (worker-plugin, import.meta.url)
 *   - No message passing protocol
 *   - Standard npm import — just works
 *
 * ── Interface ────────────────────────────────────────────────────────────
 * Same shape as createSlowLaneClient so text-predictor.ts needs zero changes.
 * The client exposes getContextVector() and getLmLogits() which are populated
 * asynchronously after each updateContext() call.
 */
export interface LocalSlowLaneClientConfig {
    /**
     * Optional custom model registration for models not in web-llm's
     * built-in list. When provided, the model is appended to the app
     * config before engine creation.
     */
    customModelConfig?: {
        /** Context window size override (optional) */
        contextWindowSize?: number;
        /** HuggingFace URL to the model weights (e.g. "https://huggingface.co/HuggingFaceTB/smollm-135M-instruct-add-basics-q0f16-MLC") */
        model: string;
        /** URL to the compiled WASM library for this model architecture */
        modelLib: string;
        /** VRAM required in MB (optional, for resource planning) */
        vramRequiredMB?: number;
    };
    /** Debounce interval in ms before sending context for inference. */
    debounceMs?: number;
    /**
     * MLC model identifier.
     * Defaults to the built-in SmolLM2-135M-Instruct-q0f16-MLC.
     *
     * To use a custom HuggingFace model, provide both `modelId` and
     * `customModelConfig` with the model URL and WASM library URL.
     */
    modelId?: string;
    /** Callback fired when the engine fails to load/start. */
    onLoadError?: (error: LocalSlowLaneLoadError) => void;
    /** Callback fired when the engine successfully loads and is ready. */
    onLoadSuccess?: (info: LocalSlowLaneLoadSuccess) => void;
    /** Callback fired with status messages (model loading progress, etc.). */
    onStatus?: (message: string) => void;
    /** Callback fired when inference returns new results. */
    onUpdate?: (opts: {
        hasLmLogits: boolean;
        hasVector: boolean;
        textLength: number;
    }) => void;
}
export interface LocalSlowLaneClient {
    /** Clean up resources. */
    destroy: () => void;
    getContextVector: () => Float32Array | null;
    getLmLogits: () => Record<string, number> | null;
    /** Whether the model is loaded and ready for inference. */
    isReady: () => boolean;
    isWordBoundary: (text: string) => boolean;
    setContextVector: (vector: Float32Array | null) => void;
    setLmLogits: (logits: Record<string, number> | null) => void;
    updateContext: (text: string) => void;
}
/**
 * Why the local engine failed to load/start.
 *
 * The first three are user-machine limitations (WebGPU missing, no compatible
 * GPU adapter, GPU lacks the `shader-f16` feature the model needs);
 * `insufficient_memory` is hit when weights don't fit in VRAM. The rest cover
 * delivery/runtime failures unrelated to hardware.
 */
export type LocalSlowLaneLoadErrorReason = 'webgpu_unavailable' | 'webgpu_no_adapter' | 'missing_shader_f16' | 'insufficient_memory' | 'model_download_failed' | 'module_load_failed' | 'init_failed';
/** Snapshot of the machine's WebGPU support, used to explain hardware limits. */
export interface WebGpuCapabilities {
    /** Whether `navigator.gpu.requestAdapter()` returned a usable adapter. */
    adapterAvailable?: boolean;
    /** GPU architecture reported by the adapter (e.g. "metal-3", "rdna2"). */
    architecture?: string;
    /** Whether `navigator.gpu` exists at all. */
    available: boolean;
    /** Largest single GPU buffer the adapter allows, in MB. */
    maxBufferSizeMB?: number;
    /** Largest storage-buffer binding the adapter allows, in MB. */
    maxStorageBufferBindingSizeMB?: number;
    /** Whether the adapter exposes the `shader-f16` feature the model requires. */
    shaderF16Supported?: boolean;
    /** GPU vendor reported by the adapter (e.g. "apple", "intel"). */
    vendor?: string;
}
export interface LocalSlowLaneLoadError {
    /** WebGPU support snapshot — explains hardware limitations behind the failure. */
    capabilities: WebGpuCapabilities;
    /** Semantic embedder loaded alongside the causal LM (loads atomically). */
    embeddingModelId: string;
    /** Canonical, controlled failure description (never raw error text). */
    message: string;
    /** Causal LM identifier that failed to load. */
    modelId: string;
    /** Coarse, privacy-safe failure category. */
    reason: LocalSlowLaneLoadErrorReason;
}
export interface LocalSlowLaneLoadSuccess {
    /** WebGPU support snapshot for the machine that loaded the model. */
    capabilities: WebGpuCapabilities;
    /** Semantic embedder loaded alongside the causal LM (loads atomically). */
    embeddingModelId: string;
    /** Model engine load time in ms (excludes the WebGPU capability probe). */
    loadDurationMs: number;
    /** Causal LM identifier that loaded. */
    modelId: string;
}
export declare const LOCAL_MLC_CAUSAL_MODEL_ID = "SmolLM2-135M-Instruct-q0f16-MLC";
/**
 * MLC ID for the semantic embedder (Snowflake Arctic Embed S, batch=4 variant).
 *
 * The `-b4` suffix selects the prebuilt variant compiled for a max batch size of
 * 4 (≈239 MB VRAM) rather than `-b32` (≈1023 MB VRAM). Autocomplete embeds one
 * context at a time, so `-b4` is the right fit. This model IS in
 * `prebuiltAppConfig.model_list` of web-llm 0.2.82 — no `customModelConfig` needed.
 */
export declare const LOCAL_MLC_EMBEDDING_MODEL_ID = "snowflake-arctic-embed-s-q0f32-MLC-b4";
/**
 * Wrap raw context text with BERT special tokens before embedding.
 *
 * web-llm's `EmbeddingPipeline` does NOT auto-prepend `[CLS]` / append `[SEP]`
 * (the official MLC embeddings example wraps manually). The Python
 * `sentence_transformers` side that generated the word-vector bin adds these
 * inside `model.encode()`, so we must mirror it here for the runtime context
 * vector to land in the same region of Arctic's embedding space as the bin.
 *
 * No query prefix is applied: the semantic step is sentence-to-sentence (`s2s`)
 * similarity ("which words are conceptually similar to this context?"), not
 * sentence-to-passage (`s2p`) retrieval. Arctic's query prefix would misframe
 * the relationship. Encode both sides as passages. See implementation.md §4.3.
 */
export declare const wrapForArctic: (text: string) => string;
/**
 * BE-parity constants — must match `CausalLMEncoder` defaults in the Python
 * sidecar (`cc-smarts/python-sidecar/src/causal_lm_encoder.py`) and
 * `SlowLaneEngine` (`typeahead_context_encoding.py`) so local payloads behave
 * identically to the server-client setup.
 */
export declare const BE_PARITY: {
    /** Final payload size cap (BE: `top_k_words`). */
    readonly TOP_K_WORDS: 2000;
    /** L2 (domain) words admitted unconditionally before pooling (BE: `reserved_l2_slots`). */
    readonly RESERVED_L2_SLOTS: 500;
    /** Log-space additive bias favouring L2 over L3 in the pool (BE: `l2_bias`). */
    readonly L2_BIAS: 1;
    /** Drop words below this probability from the final payload (BE: `> 0.00001`). */
    readonly MIN_PROB: 0.00001;
    /**
     * Word-level approximation of the BE causal LM token limit.
     *
     * BE: `CausalLMEncoder.max_context_tokens = 100` (BPE tokens, left-truncated).
     * FE: no tokenizer available, so we approximate with word count. English text
     * averages ~1.3–1.5 BPE tokens/word, meaning 100 words ≈ 130–150 tokens.
     * Using 100 words keeps the approximation simple and errs on the side of
     * sending slightly more context than the BE sees — acceptable for a PoC.
     */
    readonly MAX_CONTEXT_TOKENS: 100;
    /**
     * Word-level rolling window for the semantic embedder.
     *
     * BE: `SlowLaneEngine.max_context_words = 100` (applied in
     * `typeahead_context_encoding.py` before calling `SemanticEncoder.encode`).
     * Truncated identically here so the runtime Arctic vector lands in the same
     * region of the embedding space as the precomputed word-vector bin.
     */
    readonly MAX_CONTEXT_WORDS: 100;
};
/**
 * Convert a raw next-token logit vector into a whole-word probability payload,
 * faithfully porting the BE `CausalLMEncoder._get_top_k_probs`
 * (`cc-smarts/python-sidecar/src/causal_lm_encoder.py`).
 *
 * Steps: (1) numerically-stable masked softmax over only the token ids present
 * in the prefix-expansion map; (2) spread each token's probability to every
 * whole word sharing that first token, taking the max; (3) reserve the top L2
 * words unconditionally; (4) rank the remainder in a log-space pool with an
 * additive L2 bias; (5) emit raw probabilities for the survivors, lowercased
 * and trimmed at `MIN_PROB`.
 *
 * :params:
 *   rawLogits: Full-vocabulary logits from the LM's single decode step
 *   prefixMap: Map of first-token id to the words starting with that token
 *   domainWords: Set of L2 (domain) words, for tier-aware ranking
 * :returns:
 *   A record of lowercase word to probability — the BE `lm_logits` payload
 */
export declare const computeBePayload: (rawLogits: Float32Array, prefixMap: Map<number, string[]>, domainWords: Set<string>, 
/**
 * Pre-derived token-ID array for the softmax mask. Defaults to the
 * module-level `prefixMapTokenIds` (zero allocation in production). Pass
 * `Array.from(prefixMap.keys())` in tests that supply a custom prefixMap so
 * the softmax mask stays consistent with the iteration in Step 2.
 */
validTokenIds?: number[]) => Record<string, number>;
/**
 * Create a local slow-lane client powered by MLC WebLLM.
 *
 * The engine is initialised lazily — model weights are downloaded (and cached
 * in IndexedDB) on first use. Subsequent page loads skip the download.
 *
 * Usage:
 * ```ts
 * const client = createLocalSlowLaneClient({ debounceMs: 300 });
 * // On word boundaries:
 * client.updateContext(docText);
 * // In scoring pipeline:
 * const vec = client.getContextVector();
 * const logits = client.getLmLogits();
 * // On plugin teardown:
 * client.destroy();
 * ```
 */
export declare const createLocalSlowLaneClient: (config?: LocalSlowLaneClientConfig) => LocalSlowLaneClient;
