/**
 * OllamaProvider — local models over Ollama's native `/api/chat`.
 *
 * Pattern: Adapter (GoF) + Ports-and-Adapters (Cockburn 2005).
 * Role:    Outer ring — translates `LLMRequest`/`LLMResponse` to/from
 *          Ollama's native chat wire. Knows nothing about agents,
 *          recorders, or compositions.
 * Emits:   N/A.
 *
 * ─── Why this exists, and why it talks the native wire ───────────────
 *
 * The adapter ladder is `mock()` → a local model → a paid API, and the
 * strongest version of "the test run and the production run are the same
 * code path" is one where the middle step costs nothing and needs no API
 * key. Through 8.0.0 the middle step needed BOTH a competitor's SDK
 * (`npm install openai`, because `ollama()` was a thin wrapper over
 * `openai({ baseURL })`) and a tolerance for failures labelled `[openai]`.
 * That is not a free rung.
 *
 * So this file owns the wire:
 *
 *   • ZERO dependencies — one `fetch` POST and NDJSON. Nothing to install
 *     beyond Ollama itself. (Same choice, same reasons, as the two
 *     `Browser*Provider` adapters.)
 *   • HONEST REFUSALS — a typed {@link OllamaUnavailableError} that names
 *     the address it tried and the command to run. Owning the fetch and
 *     the status code is what makes that possible; through an SDK you get
 *     whatever the SDK decided to throw.
 *   • REAL TOKEN COUNTS — `/api/chat` returns `prompt_eval_count` /
 *     `eval_count` on every response, streaming or not, with no opt-in
 *     flag. Through the OpenAI-compatible endpoint a streamed local call
 *     reported ZERO tokens, which silently disarmed `.compaction()` and
 *     `costBudget` (see `CompactionUnmeasurableError`, whose message names
 *     this exact case).
 *   • STRUCTURED THINKING — `message.thinking` is a first-class field on
 *     this wire; the OpenAI-compatible layer renames it to a non-standard
 *     `reasoning` key that no OpenAI adapter reads. See
 *     `OllamaThinkingHandler`.
 *
 * Want the SDK path instead? It is still there and still supported:
 * `openai({ baseURL: 'http://localhost:11434/v1', apiKey: 'ollama' })`.
 *
 * ─── Ceilings (stated, not worked around) ────────────────────────────
 *
 * • TOOL CALLING IS MODEL-DEPENDENT. Ollama forwards a `tools` array to
 *   any model; a model that was not trained for tools simply answers in
 *   prose and no tool call ever arrives. This adapter does not preflight
 *   `/api/show` capabilities to refuse first — a wrong refusal is worse
 *   than a weak answer, and the metadata is less reliable than the
 *   ceiling being written down. Pick a tool-capable model.
 * • NO FORCED TOOL CHOICE. Ollama does not support `tool_choice` on
 *   either wire, so `carriesForcedToolChoice` is `false` and an agent
 *   using `.outputSchema(parser, { strategy: 'tool-forced' })` refuses at
 *   run start, naming this provider.
 * • NO MULTI-MODAL. The wire carries `images`; `LLMMessage.content` is a
 *   string. Same ceiling as every other adapter here.
 * • NO PROMPT CACHING — resolves to the NoOp cache strategy.
 * • NO `providerRef`. The native wire returns no response id, and a
 *   fabricated one would point at nothing.
 */
import type { LLMCallHooks, LLMChunk, LLMProvider, LLMRequest, LLMResponse, WireRole } from '../../provider-contract/types.js';
/** How hard a thinking model should think. Ollama's own vocabulary. */
export type ThinkLevel = 'low' | 'medium' | 'high' | 'max';
/**
 * The two failures a local runtime actually has, told in words that
 * contain the fix.
 *
 * Both are things the person at the keyboard can resolve in one command,
 * which is exactly why they get a type instead of a wrapped
 * `ECONNREFUSED` or a bare `404`. `reason` is the discriminator; the
 * message already reads as instructions.
 */
export declare class OllamaUnavailableError extends Error {
    readonly name = "OllamaUnavailableError";
    /** Which of the two situations this is. */
    readonly reason: 'daemon-unreachable' | 'model-not-pulled';
    /** The address that was tried — the thing to check or change. */
    readonly baseUrl: string;
    /** The model asked for. Absent when the daemon never answered at all. */
    readonly model?: string;
    /** Models this machine DOES have, when the daemon could tell us. */
    readonly availableModels?: readonly string[];
    constructor(init: {
        reason: 'daemon-unreachable' | 'model-not-pulled';
        baseUrl: string;
        model?: string;
        availableModels?: readonly string[];
        cause?: unknown;
    });
}
export interface OllamaProviderOptions {
    /**
     * Where Ollama is listening. Defaults to `OLLAMA_HOST` when set,
     * otherwise `http://localhost:11434`. A bare `host:port` gets `http://`.
     *
     * Point it at the ROOT, not at a path — this adapter talks to
     * `/api/chat` and `/api/tags` itself. A URL ending in `/v1` (the
     * OpenAI-compatible path) is accepted and trimmed, so a config written
     * for the 8.0.0 factory keeps working.
     */
    readonly baseUrl?: string;
    /** Shipped-in-8.0.0 spelling of {@link baseUrl}. Still honored. */
    readonly host?: string;
    /** Shipped-in-8.0.0 spelling of {@link baseUrl}. Still honored. */
    readonly baseURL?: string;
    /**
     * Model used when `LLMRequest.model` is the `'ollama'` shorthand.
     * Prefer the positional form: `ollama('qwen3')`.
     */
    readonly defaultModel?: string;
    /** Default token cap when the request doesn't set one. Maps to `num_predict`. */
    readonly defaultMaxTokens?: number;
    /**
     * Ask a thinking model to reason before answering. `true` turns it on;
     * `'low' | 'medium' | 'high' | 'max'` sets how hard.
     *
     * Worth setting on reasoning models (deepseek-r1, qwen3, gpt-oss, …):
     * with `think` on, Ollama lifts the reasoning OUT of the answer into
     * `message.thinking`, where `ollamaThinkingHandler` normalizes it.
     * With it off, the same model leaves `<think>…</think>` sitting in the
     * answer text.
     *
     * A per-request `LLMRequest.thinking` (what `AgentBuilder.thinking()`
     * sets) also turns it on; this option is the always-on default and the
     * only way to name a level.
     */
    readonly think?: boolean | ThinkLevel;
    /** How long Ollama keeps the model in memory after a call, e.g. `'5m'`. */
    readonly keepAlive?: string | number;
    /**
     * How long to wait for the daemon to ANSWER, in ms. Default 10000.
     *
     * This bounds the wait for response headers, NOT generation: a laptop
     * model may take minutes to finish a long answer and that is fine. What
     * it prevents is the failure this whole file exists to avoid — a run
     * that hangs because nothing is listening.
     */
    readonly timeoutMs?: number;
    /** Accepted and ignored — Ollama needs no key. Kept so 8.0.0 configs still typecheck. */
    readonly apiKey?: string;
    /** @internal Custom fetch implementation for tests. */
    readonly _fetch?: typeof fetch;
}
/**
 * Build an `LLMProvider` backed by a local Ollama runtime.
 *
 * Free, offline, no API key. The rung between `mock()` and a paid API —
 * and the agent code above it does not change between the three.
 *
 * @example
 *   import { Agent } from 'agentfootprint';
 *   import { ollama } from 'agentfootprint/providers';
 *
 *   const agent = Agent.create({
 *     provider: ollama('qwen3'),
 *     model: 'qwen3',
 *   }).build();
 *
 * @example  // reasoning model, thinking lifted out of the answer
 *   ollama('deepseek-r1', { think: 'high' });
 *
 * @example  // a runtime on another machine
 *   ollama('llama3.2', { baseUrl: 'http://192.168.1.20:11434' });
 */
export declare function ollama(model: string, options?: OllamaProviderOptions): LLMProvider;
/**
 * Object form, as shipped in 8.0.0. Still supported — `host` / `baseURL` /
 * `defaultModel` / `apiKey` all keep their meaning.
 */
export declare function ollama(options?: OllamaProviderOptions): LLMProvider;
/**
 * Class form for consumers who prefer `new OllamaProvider(...)`.
 */
export declare class OllamaProvider implements LLMProvider {
    readonly name = "ollama";
    readonly carriesInMessages: readonly WireRole[];
    readonly carriesForcedToolChoice = false;
    readonly thinkingHandler: import("../../thinking/types.js").ThinkingHandler;
    private readonly inner;
    constructor(model?: string | OllamaProviderOptions, options?: OllamaProviderOptions);
    complete(req: LLMRequest, hooks?: LLMCallHooks): Promise<LLMResponse>;
    stream(req: LLMRequest, hooks?: LLMCallHooks): AsyncIterable<LLMChunk>;
}
//# sourceMappingURL=OllamaProvider.d.ts.map