/** * Ollama LLM transport — native /api/chat NDJSON-streaming protocol. * * Ollama exposes a chat endpoint at `POST /api/chat` that is *mostly* * OpenAI-compatible but with notable quirks: * * - **NDJSON streaming**: each newline-delimited JSON object carries a `message` * delta. There is no `stream_options: {include_usage: true}` equivalent — * usage is included in the final chunk whose `done` field is `true`. * - **Single tool format**: Ollama uses `{ type: "function", function: {...} }` * in the request (same as OpenAI) but the response tool-call shape has * `tool_calls[i].function.name` + `.arguments` (as object, NOT a JSON string). * - **No system-prompt caching**: Ollama has no built-in prompt-caching API; * `cachedTokens` is always 0 and `cache_control` blocks are ignored. * - **No authorization header required**: local deployments use no auth key. * Remote deployments may send an `Authorization: Bearer ` header. * - **5xx retry**: the transport retries 503/429/500/502 responses up to * {@link MAX_RETRIES} times with exponential backoff. * * Reference: https://github.com/ollama/ollama/blob/main/docs/api.md#generate-a-chat-completion * * Pattern: mirrors `chat-completions.ts` — a thin native-fetch wrapper with the * same constructor signature and the `LlmTransport` interface contract. * * @module llm/transports/ollama * @task T9355 (Task A — Ollama transport, D-ph4-05 closure) * @epic T9354 */ import type { NormalizedDelta, TransportContext } from '@cleocode/contracts/llm/interfaces.js'; import type { LlmTransport, NormalizedResponse, TransportRequest } from '@cleocode/contracts/llm/normalized-response.js'; import type { ApiMode } from '@cleocode/contracts/llm/provider-id.js'; /** * Default Ollama API base URL — the well-known local endpoint. * * Override at construction time via `OllamaTransportOptions.baseUrl`. */ export declare const OLLAMA_DEFAULT_BASE_URL = "http://localhost:11434"; /** * Options accepted by {@link OllamaTransport}. * * `baseUrl` overrides the default Ollama endpoint — useful when Ollama is * running on a non-standard port or on a remote host. Must NOT have a trailing * slash. Defaults to {@link OLLAMA_DEFAULT_BASE_URL}. * * `apiKey` is optional. Local Ollama deployments require no authentication. * Remote or proxied deployments may require an `Authorization: Bearer ` * header — supply the token via `apiKey` and the transport will inject the * header automatically. * * `defaultHeaders` are merged into every request. Useful for injecting custom * auth or proxy headers. */ export interface OllamaTransportOptions { /** Override base URL (default: `http://localhost:11434`). No trailing slash. */ baseUrl?: string; /** * Optional API key / bearer token. * * When set, the transport injects `Authorization: Bearer ` into * every request. Local Ollama deployments do not require this. */ apiKey?: string; /** Extra headers merged into every request. */ defaultHeaders?: Record; } /** * Native Ollama transport. * * Uses the global `fetch` API to send requests to Ollama's `/api/chat` * endpoint. No Ollama SDK is required — the protocol is simple enough that * direct HTTP is cleaner and avoids any dependency on third-party packages. * * Tool calls are normalized from Ollama's object-argument format to the * canonical JSON-string format expected by {@link NormalizedToolCall}. * * @example * ```ts * const transport = new OllamaTransport(); // defaults to localhost:11434 * const response = await transport.complete({ * model: 'llama3', * messages: [{ role: 'user', content: 'Hello' }], * maxTokens: 512, * }); * console.log(response.content); // "Hello! How can I help you?" * ``` */ export declare class OllamaTransport implements LlmTransport { /** * Provider identifier — always `'ollama'`. * * Matches the canonical name in the builtin provider profile and the * `BuiltinProviderId` union in `@cleocode/contracts`. */ readonly provider: "ollama"; /** * Wire protocol — always `'ollama_native'`. * * Identifies the NDJSON streaming `/api/chat` protocol so that factory code * can select this transport without inspecting the base URL. * * @see ApiMode in `@cleocode/contracts/llm/provider-id` */ readonly apiMode: ApiMode; /** Resolved base URL (no trailing slash). */ private readonly _baseUrl; /** Extra HTTP headers added to every request. */ private readonly _headers; /** * Construct an OllamaTransport. * * @param opts - Optional configuration: base URL, API key, extra headers. */ constructor(opts?: OllamaTransportOptions); /** * Execute a single (non-streaming) chat completion against Ollama. * * Sends `POST /api/chat` with `stream: false` and returns a * {@link NormalizedResponse}. Retries transient 5xx / 429 errors up to * {@link MAX_RETRIES} times with exponential backoff. * * @param request - Provider-neutral request parameters. * @param _ctx - Transport context (abort signal in `request.signal`). * @returns Normalized response envelope. * @throws {Error} After all retry attempts are exhausted or on non-retryable errors. */ complete(request: TransportRequest, _ctx?: TransportContext): Promise; /** * Stream a chat completion from Ollama, yielding incremental deltas. * * Sends `POST /api/chat` with `stream: true`. Each NDJSON line from the * response body is parsed and emitted as a {@link NormalizedDelta}. The final * chunk (`done: true`) carries `stopReason` and `usage`. * * Tool-call deltas: when the final chunk carries `message.tool_calls`, the * transport emits one `toolCallDelta` entry per tool call after the done event. * This differs from OpenAI streaming (which interleaves argument chunks) because * Ollama delivers complete tool calls only on the terminal chunk. * * @param request - Provider-neutral request parameters. * @param _ctx - Transport context (abort signal in `request.signal`). * @returns Async iterable of normalized delta chunks. */ stream(request: TransportRequest, _ctx: TransportContext): AsyncIterable; /** * Build the Ollama `/api/chat` request body from a normalized request. * * Message conversion: * - `request.system` is prepended as `{ role: 'system', content: '...' }`. * - Multimodal content arrays are collapsed to text-only (images not yet * supported by this transport — Ollama has separate image fields). * * Tool conversion: * - Provider-neutral `TransportTool` → Ollama `{ type: "function", function: {...} }`. * * @param request - Provider-neutral request. * @param stream - Whether to request NDJSON streaming. * @returns JSON-serializable request body object. */ private _buildRequestBody; /** * Perform a fetch with exponential-backoff retry for transient server errors. * * Retries on {@link RETRYABLE_STATUS_CODES} (429, 500, 502, 503, 504) up to * {@link MAX_RETRIES} times. The initial delay is {@link INITIAL_BACKOFF_MS} * and doubles on each attempt. Non-retryable status codes throw immediately. * * @param url - Full request URL. * @param body - JSON-serializable request body. * @param signal - Optional AbortSignal for cancellation. * @returns A successful `Response` object. * @throws {Error} On non-retryable HTTP errors or after all retries exhausted. */ private _fetchWithRetry; /** * Normalize a complete (non-streaming) Ollama response into a * {@link NormalizedResponse}. * * @param json - Parsed Ollama JSON response. * @param requestedModel - Model string from the originating request. * @returns Normalized response envelope. */ private _normalizeComplete; } //# sourceMappingURL=ollama.d.ts.map