/** * Ollama HTTP client — generate, embed, residency probe (/api/ps). * * Every call is guarded by the global semaphore. Timeouts via AbortController. * Residency probe runs on every call so the envelope can surface eviction * without the caller asking. This is what prevents silent 5–10× slowdowns * from hiding behind polished output. */ import type { Residency } from "./envelope.js"; import type { Logger } from "./observability.js"; import type { Tier } from "./tiers.js"; export declare function setTestBackoff(ms: number | null): void; /** Test seam — pass 0 for deterministic tests. Jitter is ±RETRY_JITTER_PCT. */ declare function backoffDelayMs(attemptIdx: number, jitterPct?: number): number; /** * True if the error should cause a retry. Transient buckets: * - 5xx response from Ollama * - 429 Too Many Requests * - Low-level connection errors (ECONNRESET, ECONNREFUSED, ETIMEDOUT, EPIPE, * fetch "TypeError: fetch failed" chain, etc.) * 4xx (other than 429) and AbortError are not retried — the caller already * has a definitive signal. */ declare function isTransient(err: unknown): boolean; /** Internal signal from post() to the retry wrapper — HTTP 5xx/429 responses. */ declare class TransientHttpError extends Error { readonly status: number; readonly bodyPreview: string; constructor(status: number, bodyPreview: string); } export declare function setClientLogger(logger: Logger | null): void; export declare function setClientProfileName(name: string | null): void; export interface GenerateRequest { model: string; prompt: string; system?: string; format?: "json"; stream?: boolean; options?: { temperature?: number; num_predict?: number; top_p?: number; /** * Ollama context-window size in tokens (added v2.4.0). When omitted, * Ollama uses the model-loaded default (typically 2048-32768 depending * on the model). The MCP server resolves this from the active * profile's per-tier `num_ctx` map and only sets it when the profile * explicitly specifies a value for the calling tier — preserving * v2.3.0 behavior when unset. Smaller `num_ctx` keeps mid-sized * models in constrained VRAM (RTX 5080 16GB workhorse case). */ num_ctx?: number; }; /** * Thinking-mode toggle (Ollama 2026 API, added for Qwen 3 / DeepSeek R1 / etc.). * `false` suppresses CoT and keeps the `response` field tight; `true` lets * the model reason before answering (CoT goes into the `thinking` response * field, not `response`). Non-thinking models (e.g. hermes3:8b) ignore it. * * Load-bearing on Qwen 3: leaving thinking on for short-output tasks * (classify/extract/triage/summarize) causes `response` to come back empty * when num_predict gets consumed by thinking tokens. The prompt-level * `/no_think` soft-switch is IGNORED by Ollama — only this field works. */ think?: boolean; /** Keep model resident for this long. "-1" = forever. */ keep_alive?: string | number; } export interface GenerateResponse { model: string; response: string; /** * CoT content, populated when the request had think=true and the model * supports thinking. Separate from `response` — never concatenate. */ thinking?: string; done: boolean; prompt_eval_count?: number; eval_count?: number; total_duration?: number; load_duration?: number; eval_duration?: number; } export interface ChatMessage { role: "system" | "user" | "assistant"; content: string; } export interface ChatRequest { model: string; messages: ChatMessage[]; format?: "json"; stream?: boolean; options?: GenerateRequest["options"]; /** * Thinking-mode toggle — same semantics as GenerateRequest.think. Without * this field the chat path could never suppress CoT, so a thinking model * (e.g. minimax-m3:cloud) burned the entire num_predict budget on thinking * tokens and returned an empty `message.content` (observed live 2026-06-09 * against Ollama Cloud). Non-thinking models ignore the field. */ think?: boolean; keep_alive?: string | number; } export interface ChatResponse { model: string; message: ChatMessage; done: boolean; prompt_eval_count?: number; eval_count?: number; } export interface EmbedRequest { model: string; input: string | string[]; keep_alive?: string | number; } export interface EmbedResponse { model: string; embeddings: number[][]; } export interface PsModel { name: string; model: string; size: number; size_vram: number; digest: string; expires_at: string; } export interface PsResponse { models: PsModel[]; } export interface OllamaClient { /** * `tier` (added Stage C / F-004) is OPTIONAL context that lets the HTTP * layer label `semaphore:wait` events with the originating tier * (instant/workhorse/deep/embed). Without it, operators reading the * log_tail see `tier: 'unknown'` on every queued call — defeating the * "this MCP is slow because the deep tier is queueing" diagnosis. * Backward-compat: omitting `tier` preserves the prior call shape. */ generate(req: GenerateRequest, signal?: AbortSignal, tier?: Tier): Promise; chat(req: ChatRequest, signal?: AbortSignal, tier?: Tier): Promise; embed(req: EmbedRequest, signal?: AbortSignal, tier?: Tier): Promise; residency(model: string): Promise; /** Reachability probe — no retry, short timeout. Used at startup. */ probe(timeoutMs?: number): Promise<{ ok: boolean; reason?: string; }>; } /** * Normalize an OLLAMA_HOST value. * * Ollama's CLI accepts `127.0.0.1:11434` (no scheme) and routes Windows users * often set it that way. fetch() requires a full URL, so we coerce anything * that looks like host[:port] into a proper http:// URL, and strip trailing * slashes so path concatenation stays clean. */ export declare function normalizeOllamaHost(raw: string | undefined): string; /** * True when a normalized base URL points at the local loopback interface. * Used to refuse attaching a cloud Bearer key to a LOCAL Ollama — sending an * `Authorization` header to 127.0.0.1:11434 returns 403, a footgun for * operators who `export OLLAMA_API_KEY` globally. Best-effort: a parse * failure returns false (treat as remote) so valid remote hosts never * silently lose their auth header. */ export declare function isLoopbackHost(baseUrl: string): boolean; /** * Construction options for HttpOllamaClient. The legacy bare-`string` form * (a baseUrl) is still accepted for backward-compat with existing callers and * tests; the options object adds cloud auth + cloud-aware probe behavior. */ export interface OllamaClientOptions { /** Base URL (host[:port] or full http(s)://). Falls back to OLLAMA_HOST then loopback. */ baseUrl?: string; /** * Bearer API key for Ollama Cloud (https://ollama.com). When set AND the * resolved host is NOT loopback, an `Authorization: Bearer ` header is * attached to every request. Never sent to a localhost host (see * isLoopbackHost). */ apiKey?: string; /** * Backend kind. 'cloud' changes the two behaviors that are local-only on * the Ollama API: residency() returns null (no /api/ps on the stateless * cloud host) and probe() hits /api/tags instead of /api/ps. Default 'local'. */ kind?: "local" | "cloud"; } export declare class HttpOllamaClient implements OllamaClient { private baseUrl; private apiKey?; private kind; constructor(opts?: string | OllamaClientOptions); /** * Build request headers, adding the cloud Bearer auth header when an apiKey * is configured (already loopback-filtered in the constructor). `extra` * lets POST merge in Content-Type without duplicating the auth logic. */ private headers; generate(req: GenerateRequest, signal?: AbortSignal, tier?: Tier): Promise; chat(req: ChatRequest, signal?: AbortSignal, tier?: Tier): Promise; embed(req: EmbedRequest, signal?: AbortSignal, tier?: Tier): Promise; residency(model: string): Promise; private post; /** * Retry wrapper around one HTTP attempt. Transient errors (5xx, 429, * connection resets) retry up to RETRY_MAX_ATTEMPTS with exponential * backoff + jitter. Definitive failures (4xx model missing, AbortError from * a tier timeout) throw immediately. The final thrown InternError includes * the attempt count in its hint so operators know retries happened. */ private postWithRetry; /** One HTTP attempt — raises InternError for definitive 4xx, TransientHttpError for 5xx/429, raw for network. */ private postOnce; /** * Lightweight reachability probe used at server startup. Hits /api/ps with * a short timeout — if Ollama isn't up yet, we want a clear stderr message * without blocking server startup. Returns true if reachable, false on any * error (including 4xx/5xx/connection/timeout). */ probe(timeoutMs?: number): Promise<{ ok: boolean; reason?: string; }>; private get; } export declare function countTokens(resp: { prompt_eval_count?: number; eval_count?: number; }): { in: number; out: number; }; export declare const __retryInternals: { RETRY_MAX_ATTEMPTS: number; RETRY_BASE_DELAYS_MS: number[]; backoffDelayMs: typeof backoffDelayMs; isTransient: typeof isTransient; TransientHttpError: typeof TransientHttpError; }; export {}; //# sourceMappingURL=ollama.d.ts.map