import type { Embedder, EmbedderMetadata } from './types.js'; /** * v1.7.22 (O3/N2): Discriminated union of lifecycle states for an * OllamaEmbedder instance. Exposed synchronously via `embedder.phase` * so tools that bypass `ensureEmbedderReady()` (the search/reindex * preparing-state paths) can render an informative status to clients * instead of either throwing or blocking on a multi-minute pull. * * State diagram: * not-started → probing → (ready | pulling | failed) * pulling → (probing → ready | failed) * failed → terminal until next init() retry * * Pull progress fields update in-place on the same `pulling` instance * each throttle tick (≤1 update/second) — callers read via the getter * for whatever the latest snapshot is. */ export type OllamaPhase = { status: 'not-started'; } | { status: 'probing'; } | { status: 'pulling'; /** Progress in MB (rounded down). 0 before first byte-progress event. */ completedMb: number; /** Total in MB. 0 until Ollama emits a `total` field. */ totalMb: number; /** Percentage 0-100, rounded down. */ pct: number; /** Most recent Ollama status string (e.g. "pulling manifest", * "downloading", "verifying sha256 digest"). */ phase: string; } | { status: 'ready'; } | { status: 'failed'; error: Error; }; /** * Ollama-backed Embedder. Talks to a local (or remote) Ollama server's * `/api/embeddings` endpoint. Task-type prefixes are applied automatically * for known asymmetric models (nomic-embed-text, qwen embeddings, * mxbai-embed-large / mixedbread) — other models get the raw text through. * * Dimensions are either supplied up-front via the `expectedDim` constructor * arg (typically from `OLLAMA_EMBEDDING_DIM`) or discovered by the first * `embed()` call. Callers that need `dimensions()` synchronously before any * embed — e.g. the bootstrap compatibility check — should call `init()`, * which probes once when no dim was declared. */ export declare class OllamaEmbedder implements Embedder { readonly baseUrl: string; private readonly model; private cachedDim; private cachedDigest; private cachedContextLength; /** True when the most recent `/api/show` returned 404 — distinct from * "cachedDim happens to be undefined." Drives auto-pull (v1.7.21): * pulling a non-existent model is correct; running `/api/pull` against * an existing model with schema variation is incorrect. */ private modelNotPulled; /** Last error from init() / embed(). Sticky until cleared by the next * successful call. Surfaced via `dimensions()` so callers that bypass * `ensureEmbedderReady()` (e.g. `index_status` reading the dim directly) * get the actionable cause — "Ollama embed failed: HTTP 404 ... try * ollama pull " — instead of the generic "dim not known yet". */ private lastError; /** v1.7.22 (O3/N2): current lifecycle phase. Synchronously readable via * the public `phase` getter. Updated at probe entry, pull start, pull * progress (in-place mutation of the same instance for ≤1Hz updates), * ready, and failure. */ private _phase; /** Resolved metadata pushed in by `metadata-resolver.ts` after `bootstrap()` * runs. When set, embed() uses these prefixes instead of the hardcoded * family heuristics in `getPrefix()` — that's how `models override` / * `models add` flow through to Ollama. Null at construction; populated * in production by `context.ts` orchestration. Falls back to the * hardcoded `getPrefix()` when null (init-time, tests). */ private _metadata; /** The user's explicit `OLLAMA_NUM_CTX` if they set one, else undefined. * Resolved against the model's real `context_length` (from `/api/show`) * during init() — see `effectiveNumCtx`. */ private readonly explicitNumCtx; /** Fallback when `/api/show` is unreachable and the user didn't set * OLLAMA_NUM_CTX. Most embedding models cap well below this. */ private static readonly NUM_CTX_FALLBACK; /** v1.7.23: name of the preset that picked this model, or null when the * user supplied `EMBEDDING_MODEL` directly (BYOM). Drives the new BYOM * auto-pull gate — preset-known models always auto-pull (existing v1.7.21 * behaviour); BYOM models require an opt-in env var unless the model id * falls in Ollama's official `library/` namespace allowlist. */ private readonly presetName; constructor(baseUrl?: string, model?: string, expectedDim?: number, numCtx?: number, presetName?: string | null); /** Resolved `num_ctx` for `/api/embeddings` calls. Precedence: * 1. User's `OLLAMA_NUM_CTX` if set (they know what they want). * 2. The model's real `context_length` from `/api/show` (verified * live: nomic=2048, mxbai-embed-large=512, all-minilm=512 — * sending the legacy 8192 default to nomic exceeded the model's * hard cap). * 3. Fallback `NUM_CTX_FALLBACK` (8192) when neither is available. */ private get effectiveNumCtx(); init(): Promise; /** v1.7.22 (O3/N2): synchronous read of current lifecycle phase. Tools * that want to render a preparing-state response without blocking on * `ensureEmbedderReady()` read this to differentiate "pulling 612 MB * (38%)" from "still probing /api/show" from "fully ready." */ get phase(): OllamaPhase; /** v1.7.23: synchronous read of the preset name (null when BYOM). Exposed * for tests + future `index_status` field so clients can see whether the * current model was chosen via a known preset or supplied directly via * `EMBEDDING_MODEL`. */ get presetNameForTest(): string | null; /** v1.7.23 BYOM-aware auto-pull gate. Evaluation order: * 1. Master kill: `OBSIDIAN_BRAIN_OLLAMA_AUTO_PULL=0` → never pull. * 2. Preset-known model (`presetName != null`) → always pull. * 3. BYOM model in Ollama's official `library/` namespace → pull * (allowlist: ids with no `/` or `library/` prefix are official). * 4. BYOM explicit opt-in: `OBSIDIAN_BRAIN_OLLAMA_BYOM_AUTO_PULL=1` * → pull anything the user named. * 5. Otherwise → refuse; init() throws the BYOM-blocked actionable * error so the user sees the three escape hatches in their MCP * response / CLI stderr. */ private shouldAutoPull; /** v1.7.23: true when the gate refused BYOM auto-pull specifically (model * is custom AND not in the official-namespace allowlist AND opt-in is off). * Used by `init()` to choose between the existing v1.7.20-style HTTP-404 * message (master kill) and the new BYOM-blocked actionable message. */ private isBlockedByByomGate; /** v1.7.23: format the actionable error thrown when the BYOM gate refuses * to auto-pull a custom user-supplied model. The message names exactly * which gate refused and the three escape hatches the user can take. */ private formatByomBlockedMessage; /** Issue `/api/pull` and stream NDJSON progress to stderr. Returns when * the pull completes successfully; throws on failure. The caller is * expected to re-probe `/api/show` afterward to populate cachedDim * / cachedContextLength / capability check. */ private pullModel; /** Pull dim, max-tokens, and embedding-capability from `/api/show`. */ private fetchModelInfo; /** Pull manifest digest from `/api/tags` for the active model. */ private fetchDigest; /** Expose the model's authoritative max-token context (from `/api/show`) * for the capacity layer to read. Null when unavailable. */ getContextLength(): number | null; embed(text: string, taskType?: 'document' | 'query'): Promise; private embedInner; dimensions(): number; modelIdentifier(): string; providerName(): string; /** Manifest digest of the active Ollama model (sha256 from `/api/tags`), * or null when Ollama was unreachable / didn't return one at init time. */ identityHash(): string | null; /** Accept the resolver's authoritative prefix strings. After this call, * embed() prefers these over the hardcoded family heuristics. Called * by context.ts orchestration after `resolveModelMetadata()` finishes. */ setMetadata(meta: EmbedderMetadata): void; /** Diagnostic accessor; mirrors TransformersEmbedder.getMetadata(). */ getMetadata(): EmbedderMetadata | null; dispose(): Promise; private getPrefix; } /** * v1.7.23 BYOM auto-pull allowlist heuristic. Returns true when `modelId` * names a model in Ollama's official `library/` namespace, where auto-pull * is considered safe-by-default (the same set of models surfaced by * https://ollama.com/library). * * Allowlist rules: * - No `/` in the id → bare model name → official library * (e.g. `qwen3-embedding:0.6b`, `nomic-embed-text`, `bge-m3`) * - Single `/` AND starts with `library/` → explicit official prefix * (e.g. `library/llama3:8b`) * - Anything else → third-party namespace, returns false * (e.g. `user/custom-fork`, `myregistry.com/team/model:tag`) * * This is intentionally conservative: when in doubt, refuse. The user can * always opt in via `OBSIDIAN_BRAIN_OLLAMA_BYOM_AUTO_PULL=1`. */ export declare function isOllamaOfficialNamespace(modelId: string): boolean; /** * v1.7.23: extract the namespace prefix of an Ollama model id for diagnostic * messages. Returns `null` for bare ids with no `/`. * - `qwen3-embedding:0.6b` → null * - `library/llama3:8b` → `'library'` * - `user/custom-fork` → `'user'` * - `registry.example.com/team/model:tag` → `'registry.example.com/team'` */ export declare function ollamaNamespaceOf(modelId: string): string | null;