@nemesis-oss/ollama-sdk 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/streaming/ndjson.ts","../src/generated/runtime/runtime.ts"],"names":[],"mappings":"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","file":"generated-runtime.js","sourcesContent":["/**\n * Structured, typed error hierarchy for OllamaClient.\n */\n\nexport interface OllamaErrorRequestContext {\n readonly method?: string | undefined;\n readonly url?: string | undefined;\n readonly model?: string | undefined;\n}\n\nexport interface OllamaErrorResponseContext {\n readonly status?: number | undefined;\n readonly headers?: Record<string, string> | undefined;\n readonly body?: unknown;\n}\n\nexport interface OllamaClientErrorOptions extends ErrorOptions {\n readonly code: string;\n readonly status?: number | undefined;\n readonly retryable?: boolean | undefined;\n readonly request?: OllamaErrorRequestContext | undefined;\n readonly response?: OllamaErrorResponseContext | undefined;\n}\n\nexport class OllamaClientError extends Error {\n readonly code: string;\n readonly status?: number | undefined;\n readonly retryable: boolean;\n readonly request?: OllamaErrorRequestContext | undefined;\n readonly response?: OllamaErrorResponseContext | undefined;\n\n constructor(message: string, options: OllamaClientErrorOptions) {\n super(message, { cause: options.cause });\n this.name = this.constructor.name;\n this.code = options.code;\n this.status = options.status;\n this.retryable = options.retryable ?? false;\n this.request = options.request;\n this.response = options.response;\n }\n}\n\nexport class OllamaNetworkError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'network_error', retryable: true });\n }\n}\n\nexport class OllamaTimeoutError extends OllamaClientError {\n readonly timeoutMs?: number | undefined;\n constructor(\n message: string,\n options?:\n | (Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & { timeoutMs?: number | undefined })\n | undefined,\n ) {\n super(message, { ...options, code: 'timeout', retryable: true });\n this.timeoutMs = options?.timeoutMs;\n }\n}\n\nexport class OllamaAuthError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'auth_error', retryable: false });\n }\n}\n\nexport class OllamaNotFoundError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'not_found', retryable: false });\n }\n}\n\nexport class OllamaRateLimitError extends OllamaClientError {\n readonly retryAfterMs?: number | undefined;\n constructor(\n message: string,\n options?:\n | (Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n retryAfterMs?: number | undefined;\n })\n | undefined,\n ) {\n super(message, { ...options, code: 'rate_limited', retryable: true });\n this.retryAfterMs = options?.retryAfterMs;\n }\n}\n\n/**\n * Thrown client-side, before a network request is made, by `QuotaManager.assertCanProceed`\n * (see `src/quota.ts`) when issuing the request would exceed a configured usage budget for\n * one of its rolling windows. This is distinct from `OllamaRateLimitError`, which reflects\n * the server's own 429 response — Ollama Cloud does not expose account-level quota via the\n * API, so `QuotaManager` tracks usage against budgets the caller configures locally and\n * fails fast rather than waiting for the server to reject the request.\n */\nexport class OllamaQuotaExceededError extends OllamaClientError {\n readonly windowId: string;\n readonly resetAt: number;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n windowId: string;\n resetAt: number;\n },\n ) {\n super(message, { ...options, code: 'quota_exceeded', retryable: false });\n this.windowId = options.windowId;\n this.resetAt = options.resetAt;\n }\n}\n\nexport class OllamaServerError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'server_error', retryable: true });\n }\n}\n\nexport class OllamaAbortError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'aborted', retryable: false });\n }\n}\n\nexport class OllamaToolValidationError extends OllamaClientError {\n readonly toolName: string;\n readonly issues?: unknown;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & { toolName: string; issues?: unknown },\n ) {\n super(message, { ...options, code: 'tool_validation_error', retryable: false });\n this.toolName = options.toolName;\n this.issues = options.issues;\n }\n}\n\nexport class OllamaToolTimeoutError extends OllamaClientError {\n readonly toolName: string;\n readonly timeoutMs: number;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n toolName: string;\n timeoutMs: number;\n },\n ) {\n super(message, { ...options, code: 'tool_timeout', retryable: false });\n this.toolName = options.toolName;\n this.timeoutMs = options.timeoutMs;\n }\n}\n\n/**\n * Thrown client-side, before a network request is made, when a request asks for a\n * capability an endpoint is known not to support (e.g. structured output `format` against\n * an Ollama Cloud endpoint — see `ModelCapabilities.supportsStructuredOutputRequest` in\n * `src/capabilities/capabilities.ts`). Its `code` is included in `DEFAULT_FAILOVER_CODES`\n * by default, so in a multi-endpoint setup it causes failover to the next candidate rather\n * than failing outright — it only surfaces to the caller if every candidate is rejected.\n */\nexport class OllamaUnsupportedCapabilityError extends OllamaClientError {\n readonly capability: string;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & { capability: string },\n ) {\n super(message, { ...options, code: 'unsupported_capability', retryable: false });\n this.capability = options.capability;\n }\n}\n\n/**\n * Thrown client-side, before any network call, when `config.endpoints` uses\n * `OllamaEndpoint.models` to scope endpoints/credentials to specific models and the\n * requested `model` isn't in any configured endpoint's allow-list. This is a routing\n * misconfiguration, not a connectivity problem — the SDK deliberately does not \"probe\"\n * unauthorized endpoints to find one that happens to work (that would burn quota on keys\n * that were never entitled to the model and produce non-deterministic startup behavior).\n */\nexport class OllamaModelRoutingError extends OllamaClientError {\n readonly model: string;\n readonly availableModels: readonly string[];\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n model: string;\n availableModels: readonly string[];\n },\n ) {\n super(message, { ...options, code: 'model_routing_error', retryable: false });\n this.model = options.model;\n this.availableModels = options.availableModels;\n }\n}\n\n/**\n * Thrown before an agent tool loop starts when the selected model's /api/show metadata\n * does not advertise a capability required by the loop.\n */\nexport class OllamaIncompatibleModelError extends OllamaClientError {\n readonly model: string;\n readonly capability: string;\n readonly reportedCapabilities: readonly string[];\n\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n model: string;\n capability: string;\n reportedCapabilities: readonly string[];\n },\n ) {\n super(message, { ...options, code: 'incompatible_model', retryable: false });\n this.model = options.model;\n this.capability = options.capability;\n this.reportedCapabilities = options.reportedCapabilities;\n }\n}\nexport class OllamaAgentMaxToolCallsError extends OllamaClientError {\n readonly maxToolCalls: number;\n readonly toolCallsExecuted: number;\n\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & {\n maxToolCalls: number;\n toolCallsExecuted: number;\n },\n ) {\n super(message, { ...options, code: 'agent_max_tool_calls_exceeded', retryable: false });\n this.maxToolCalls = options.maxToolCalls;\n this.toolCallsExecuted = options.toolCallsExecuted;\n }\n}\n\nexport class OllamaAgentMaxIterationsError extends OllamaClientError {\n readonly maxIterations: number;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & { maxIterations: number },\n ) {\n super(message, { ...options, code: 'agent_max_iterations_exceeded', retryable: false });\n this.maxIterations = options.maxIterations;\n }\n}\n\nexport class OllamaMcpError extends OllamaClientError {\n readonly mcpMethod: 'listTools' | 'callTool';\n readonly toolName?: string | undefined;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & {\n mcpMethod: 'listTools' | 'callTool';\n toolName?: string | undefined;\n },\n ) {\n super(message, { ...options, code: 'mcp_error', retryable: options.retryable ?? false });\n this.mcpMethod = options.mcpMethod;\n this.toolName = options.toolName;\n }\n}\n\nexport class OllamaSkillNotFoundError extends OllamaClientError {\n readonly skillName: string;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & { skillName: string },\n ) {\n super(message, { ...options, code: 'skill_not_found', retryable: false });\n this.skillName = options.skillName;\n }\n}\n\nexport class OllamaSkillInvalidError extends OllamaClientError {\n readonly skillName: string;\n readonly path?: string | undefined;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & {\n skillName: string;\n path?: string | undefined;\n },\n ) {\n super(message, { ...options, code: 'skill_invalid', retryable: false });\n this.skillName = options.skillName;\n this.path = options.path;\n }\n}\n\nexport class OllamaGenericClientError extends OllamaClientError {\n constructor(message: string, options?: Omit<OllamaClientErrorOptions, 'code'> | undefined) {\n super(message, { ...options, code: 'client_error' });\n }\n}\n\nfunction isDomAbortError(error: unknown): error is DOMException {\n return (\n typeof DOMException !== 'undefined' &&\n error instanceof DOMException &&\n error.name === 'AbortError'\n );\n}\n\nfunction statusToError(\n status: number,\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'status'>,\n): OllamaClientError {\n if (status === 401 || status === 403) {\n return new OllamaAuthError(message, { ...options, status });\n }\n if (status === 404) {\n return new OllamaNotFoundError(message, { ...options, status });\n }\n if (status === 429) {\n return new OllamaRateLimitError(message, { ...options, status });\n }\n if (status >= 500) {\n return new OllamaServerError(message, { ...options, status });\n }\n return new OllamaGenericClientError(message, { ...options, status });\n}\n\n/**\n * Maps arbitrary thrown exceptions into structured OllamaClientError instances.\n */\nexport function mapError(\n error: unknown,\n context: {\n request?: OllamaErrorRequestContext | undefined;\n response?: OllamaErrorResponseContext | undefined;\n } = {},\n): OllamaClientError {\n if (error instanceof OllamaClientError) {\n return error;\n }\n if (isDomAbortError(error)) {\n return new OllamaAbortError(error.message || 'Request was aborted', {\n cause: error,\n request: context.request,\n });\n }\n if (error instanceof TypeError) {\n return new OllamaNetworkError(error.message || 'Network request failed', {\n cause: error,\n request: context.request,\n });\n }\n if (context.response?.status !== undefined) {\n const message = error instanceof Error ? error.message : String(error);\n return statusToError(context.response.status, message, {\n cause: error,\n request: context.request,\n response: context.response,\n });\n }\n const message = error instanceof Error ? error.message : String(error);\n return new OllamaGenericClientError(message, {\n cause: error,\n request: context.request,\n response: context.response,\n });\n}\n","/**\n * NDJSON (newline-delimited JSON) stream reader for native fetch Response bodies.\n */\n\nimport { OllamaGenericClientError } from '../errors.js';\n\n/**\n * Parses a byte stream from fetch body into an async generator of typed JSON chunks.\n */\nexport async function* parseNdjsonStream<T>(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<T, void, undefined> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n // Keep the last partial line in buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n yield JSON.parse(trimmed) as T;\n } catch (err) {\n throw new OllamaGenericClientError(`Failed to parse stream JSON chunk: ${trimmed}`, {\n cause: err,\n });\n }\n }\n }\n\n if (buffer.trim()) {\n yield JSON.parse(buffer.trim()) as T;\n }\n } finally {\n try {\n await reader.cancel();\n } catch {\n // The stream may already be closed or errored.\n }\n reader.releaseLock();\n }\n}\n","/**\n * OllamaRuntime — the runtime seam generated API classes delegate to.\n *\n * This is the only hand-written file in `src/generated/runtime/`. Every\n * generated method in `src/generated/api/*-api.ts` ends with:\n *\n * ```ts\n * return this.runtime.invoke({ operation: chatOp, body: request });\n * ```\n *\n * The runtime is responsible for:\n * - Method/path resolution (from {@link OperationDefinition})\n * - Streaming default application (when the request doesn't set `stream`)\n * - Environment guard (rejecting cloud calls for local-only operations)\n * - Version constraint guard (failing fast when the server is too old)\n * - Delegating to the existing {@link HttpClient} for transport, retry,\n * telemetry, NDJSON/SSE parsing\n *\n * It is intentionally a thin adapter — the existing transport, streaming,\n * middleware, and error-mapping layers are preserved verbatim. This file\n * only adds the contract-aware glue between generated API classes and the\n * existing HttpClient.\n */\nimport { HttpClient, type HttpRequestOptions } from '../../transport/http.js';\nimport { parseNdjsonStream } from '../../streaming/ndjson.js';\nimport { OllamaGenericClientError } from '../../errors.js';\nimport type { OperationDefinition, InvokeRequest } from './operation-definition.js';\n\n/** Constructor options for {@link OllamaRuntime}. */\nexport interface OllamaRuntimeOptions {\n readonly http: HttpClient;\n /**\n * Whether this runtime is targeting a local Ollama instance. Defaults to\n * `true`. When `false`, operations marked `environment.cloud === false`\n * (i.e. local-only like `/v1/systemone`) are rejected at request time.\n */\n readonly localMode?: boolean;\n /**\n * Server version, if known (typically from a prior `/api/version` call).\n * When set, operations with `constraints.minOllamaVersion` are checked\n * against this value at request time.\n */\n readonly serverVersion?: string;\n}\n\nfunction compareVersions(a: string, b: string): number {\n const pa = a.split('.').map((s) => Number.parseInt(s, 10) ?? 0);\n const pb = b.split('.').map((s) => Number.parseInt(s, 10) ?? 0);\n for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va !== vb) return va - vb;\n }\n return 0;\n}\n\n/**\n * Apply environment + version guards before delegating to HttpClient.\n *\n * Throws {@link OllamaGenericClientError} when:\n * - The operation is local-only and the runtime is in cloud mode.\n * - The operation has `constraints.minOllamaVersion` and the server is older.\n */\nfunction assertOperationAllowed(\n operation: OperationDefinition,\n options: OllamaRuntimeOptions,\n): void {\n const localMode = options.localMode ?? true;\n if (!localMode && !operation.environment.cloud) {\n throw new OllamaGenericClientError(\n `Operation ${operation.operationId} (${operation.method} ${operation.path}) ` +\n `is local-only and not supported in cloud mode.`,\n );\n }\n if (\n operation.constraints?.minOllamaVersion &&\n options.serverVersion &&\n compareVersions(options.serverVersion, operation.constraints.minOllamaVersion) < 0\n ) {\n throw new OllamaGenericClientError(\n `Operation ${operation.operationId} (${operation.method} ${operation.path}) ` +\n `requires Ollama >= ${operation.constraints.minOllamaVersion} ` +\n `(server reports ${options.serverVersion}).`,\n );\n }\n}\n\n/**\n * Decide whether the request should stream. If the request body explicitly\n * sets `stream`, that wins. Otherwise the operation's `streamingDefault`\n * from the contract is used.\n */\nfunction shouldStream(req: InvokeRequest): boolean {\n if (req.body && typeof req.body === 'object' && 'stream' in req.body) {\n const explicit = (req.body as { stream?: unknown }).stream;\n if (typeof explicit === 'boolean') return explicit;\n }\n return Boolean(req.streamingDefault ?? req.operation.transport.streamingDefault);\n}\n\n/**\n * Apply the streaming default to the request body if needed.\n *\n * If the operation streams by default and the body doesn't set `stream`,\n * add `stream: true`. The user can always override by setting `stream`\n * explicitly on the request.\n */\nfunction buildBody(req: InvokeRequest): unknown {\n if (req.body === undefined) return undefined;\n if (typeof req.body !== 'object' || req.body === null) return req.body;\n const body = req.body as Record<string, unknown>;\n if (\n req.operation.transport.streaming &&\n req.operation.transport.streamingDefault &&\n body.stream === undefined\n ) {\n return { ...body, stream: true };\n }\n return body;\n}\n\n/** The runtime seam. */\nexport class OllamaRuntime {\n constructor(private readonly options: OllamaRuntimeOptions) {}\n\n /**\n * Invoke an operation. Called by every generated API method.\n *\n * Returns a `Promise<T>` for non-streaming operations, or an\n * `AsyncGenerator<T>` for streaming operations. The generated API\n * class's overloads pick the right return type at compile time.\n */\n async invoke<T = unknown>(req: InvokeRequest): Promise<T> {\n assertOperationAllowed(req.operation, this.options);\n const body = buildBody(req);\n const streaming = shouldStream(req);\n\n // The HttpClient expects the narrow method union 'GET' | 'POST' |\n // 'DELETE' | 'HEAD' | undefined — cast through `as` because our\n // OperationDefinition.method is the wider HttpMethod type.\n const httpReq: HttpRequestOptions = {\n path: req.operation.path,\n method: req.operation.method as 'GET' | 'POST' | 'DELETE' | 'HEAD',\n ...(body !== undefined ? { body } : {}),\n ...(req.signal !== undefined ? { signal: req.signal } : {}),\n // For streaming calls, request the raw Response so we can pipe it\n // through parseNdjsonStream directly. This inherits middleware,\n // retry, telemetry, and error-mapping from HttpClient — the\n // previous Wave 3 implementation bypassed them with a direct\n // `fetch()` call.\n ...(streaming ? { raw: true } : {}),\n };\n\n if (!streaming) {\n return (await this.options.http.request<T>(httpReq)) as T;\n }\n\n // Streaming: HttpClient returns the raw Response (via the `raw: true`\n // option) so we can read its body as an NDJSON stream.\n const response = await this.options.http.request<Response>(httpReq);\n if (!response.body) {\n throw new OllamaGenericClientError(\n `Operation ${req.operation.operationId}: streaming response had no body.`,\n );\n }\n return parseNdjsonStream<T>(response.body) as unknown as T;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/utils.ts","../src/streaming/ndjson.ts","../src/generated/runtime/runtime.ts"],"names":[],"mappings":"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","file":"generated-runtime.js","sourcesContent":["/**\n * Structured, typed error hierarchy for OllamaClient.\n */\n\nexport interface OllamaErrorRequestContext {\n readonly method?: string | undefined;\n readonly url?: string | undefined;\n readonly model?: string | undefined;\n}\n\nexport interface OllamaErrorResponseContext {\n readonly status?: number | undefined;\n readonly headers?: Record<string, string> | undefined;\n readonly body?: unknown;\n}\n\nexport interface OllamaClientErrorOptions extends ErrorOptions {\n readonly code: string;\n readonly status?: number | undefined;\n readonly retryable?: boolean | undefined;\n readonly request?: OllamaErrorRequestContext | undefined;\n readonly response?: OllamaErrorResponseContext | undefined;\n}\n\nexport class OllamaClientError extends Error {\n readonly code: string;\n readonly status?: number | undefined;\n readonly retryable: boolean;\n readonly request?: OllamaErrorRequestContext | undefined;\n readonly response?: OllamaErrorResponseContext | undefined;\n\n constructor(message: string, options: OllamaClientErrorOptions) {\n super(message, { cause: options.cause });\n this.name = this.constructor.name;\n this.code = options.code;\n this.status = options.status;\n this.retryable = options.retryable ?? false;\n this.request = options.request;\n this.response = options.response;\n }\n}\n\nexport class OllamaNetworkError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'network_error', retryable: true });\n }\n}\n\nexport class OllamaTimeoutError extends OllamaClientError {\n readonly timeoutMs?: number | undefined;\n constructor(\n message: string,\n options?:\n | (Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & { timeoutMs?: number | undefined })\n | undefined,\n ) {\n super(message, { ...options, code: 'timeout', retryable: true });\n this.timeoutMs = options?.timeoutMs;\n }\n}\n\nexport class OllamaAuthError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'auth_error', retryable: false });\n }\n}\n\nexport class OllamaNotFoundError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'not_found', retryable: false });\n }\n}\n\nexport class OllamaRateLimitError extends OllamaClientError {\n readonly retryAfterMs?: number | undefined;\n constructor(\n message: string,\n options?:\n | (Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n retryAfterMs?: number | undefined;\n })\n | undefined,\n ) {\n super(message, { ...options, code: 'rate_limited', retryable: true });\n this.retryAfterMs = options?.retryAfterMs;\n }\n}\n\n/**\n * Thrown client-side, before a network request is made, by `QuotaManager.assertCanProceed`\n * (see `src/quota.ts`) when issuing the request would exceed a configured usage budget for\n * one of its rolling windows. This is distinct from `OllamaRateLimitError`, which reflects\n * the server's own 429 response — Ollama Cloud does not expose account-level quota via the\n * API, so `QuotaManager` tracks usage against budgets the caller configures locally and\n * fails fast rather than waiting for the server to reject the request.\n */\nexport class OllamaQuotaExceededError extends OllamaClientError {\n readonly windowId: string;\n readonly resetAt: number;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n windowId: string;\n resetAt: number;\n },\n ) {\n super(message, { ...options, code: 'quota_exceeded', retryable: false });\n this.windowId = options.windowId;\n this.resetAt = options.resetAt;\n }\n}\n\nexport class OllamaServerError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'server_error', retryable: true });\n }\n}\n\nexport class OllamaAbortError extends OllamaClientError {\n constructor(\n message: string,\n options?: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> | undefined,\n ) {\n super(message, { ...options, code: 'aborted', retryable: false });\n }\n}\n\nexport class OllamaToolValidationError extends OllamaClientError {\n readonly toolName: string;\n readonly issues?: unknown;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & { toolName: string; issues?: unknown },\n ) {\n super(message, { ...options, code: 'tool_validation_error', retryable: false });\n this.toolName = options.toolName;\n this.issues = options.issues;\n }\n}\n\nexport class OllamaToolTimeoutError extends OllamaClientError {\n readonly toolName: string;\n readonly timeoutMs: number;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n toolName: string;\n timeoutMs: number;\n },\n ) {\n super(message, { ...options, code: 'tool_timeout', retryable: false });\n this.toolName = options.toolName;\n this.timeoutMs = options.timeoutMs;\n }\n}\n\n/**\n * Thrown client-side, before a network request is made, when a request asks for a\n * capability an endpoint is known not to support (e.g. structured output `format` against\n * an Ollama Cloud endpoint — see `ModelCapabilities.supportsStructuredOutputRequest` in\n * `src/capabilities/capabilities.ts`). Its `code` is included in `DEFAULT_FAILOVER_CODES`\n * by default, so in a multi-endpoint setup it causes failover to the next candidate rather\n * than failing outright — it only surfaces to the caller if every candidate is rejected.\n */\nexport class OllamaUnsupportedCapabilityError extends OllamaClientError {\n readonly capability: string;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & { capability: string },\n ) {\n super(message, { ...options, code: 'unsupported_capability', retryable: false });\n this.capability = options.capability;\n }\n}\n\n/**\n * Thrown client-side, before any network call, when `config.endpoints` uses\n * `OllamaEndpoint.models` to scope endpoints/credentials to specific models and the\n * requested `model` isn't in any configured endpoint's allow-list. This is a routing\n * misconfiguration, not a connectivity problem — the SDK deliberately does not \"probe\"\n * unauthorized endpoints to find one that happens to work (that would burn quota on keys\n * that were never entitled to the model and produce non-deterministic startup behavior).\n */\nexport class OllamaModelRoutingError extends OllamaClientError {\n readonly model: string;\n readonly availableModels: readonly string[];\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n model: string;\n availableModels: readonly string[];\n },\n ) {\n super(message, { ...options, code: 'model_routing_error', retryable: false });\n this.model = options.model;\n this.availableModels = options.availableModels;\n }\n}\n\n/**\n * Thrown before an agent tool loop starts when the selected model's /api/show metadata\n * does not advertise a capability required by the loop.\n */\nexport class OllamaIncompatibleModelError extends OllamaClientError {\n readonly model: string;\n readonly capability: string;\n readonly reportedCapabilities: readonly string[];\n\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'retryable'> & {\n model: string;\n capability: string;\n reportedCapabilities: readonly string[];\n },\n ) {\n super(message, { ...options, code: 'incompatible_model', retryable: false });\n this.model = options.model;\n this.capability = options.capability;\n this.reportedCapabilities = options.reportedCapabilities;\n }\n}\nexport class OllamaAgentMaxToolCallsError extends OllamaClientError {\n readonly maxToolCalls: number;\n readonly toolCallsExecuted: number;\n\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & {\n maxToolCalls: number;\n toolCallsExecuted: number;\n },\n ) {\n super(message, { ...options, code: 'agent_max_tool_calls_exceeded', retryable: false });\n this.maxToolCalls = options.maxToolCalls;\n this.toolCallsExecuted = options.toolCallsExecuted;\n }\n}\n\nexport class OllamaAgentMaxIterationsError extends OllamaClientError {\n readonly maxIterations: number;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & { maxIterations: number },\n ) {\n super(message, { ...options, code: 'agent_max_iterations_exceeded', retryable: false });\n this.maxIterations = options.maxIterations;\n }\n}\n\nexport class OllamaMcpError extends OllamaClientError {\n readonly mcpMethod: 'listTools' | 'callTool';\n readonly toolName?: string | undefined;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & {\n mcpMethod: 'listTools' | 'callTool';\n toolName?: string | undefined;\n },\n ) {\n super(message, { ...options, code: 'mcp_error', retryable: options.retryable ?? false });\n this.mcpMethod = options.mcpMethod;\n this.toolName = options.toolName;\n }\n}\n\nexport class OllamaSkillNotFoundError extends OllamaClientError {\n readonly skillName: string;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & { skillName: string },\n ) {\n super(message, { ...options, code: 'skill_not_found', retryable: false });\n this.skillName = options.skillName;\n }\n}\n\nexport class OllamaSkillInvalidError extends OllamaClientError {\n readonly skillName: string;\n readonly path?: string | undefined;\n constructor(\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code'> & {\n skillName: string;\n path?: string | undefined;\n },\n ) {\n super(message, { ...options, code: 'skill_invalid', retryable: false });\n this.skillName = options.skillName;\n this.path = options.path;\n }\n}\n\nexport class OllamaGenericClientError extends OllamaClientError {\n constructor(message: string, options?: Omit<OllamaClientErrorOptions, 'code'> | undefined) {\n super(message, { ...options, code: 'client_error' });\n }\n}\n\nfunction isDomAbortError(error: unknown): error is DOMException {\n return (\n typeof DOMException !== 'undefined' &&\n error instanceof DOMException &&\n error.name === 'AbortError'\n );\n}\n\nfunction statusToError(\n status: number,\n message: string,\n options: Omit<OllamaClientErrorOptions, 'code' | 'status'>,\n): OllamaClientError {\n if (status === 401 || status === 403) {\n return new OllamaAuthError(message, { ...options, status });\n }\n if (status === 404) {\n return new OllamaNotFoundError(message, { ...options, status });\n }\n if (status === 429) {\n return new OllamaRateLimitError(message, { ...options, status });\n }\n if (status >= 500) {\n return new OllamaServerError(message, { ...options, status });\n }\n return new OllamaGenericClientError(message, { ...options, status });\n}\n\n/**\n * Maps arbitrary thrown exceptions into structured OllamaClientError instances.\n */\nexport function mapError(\n error: unknown,\n context: {\n request?: OllamaErrorRequestContext | undefined;\n response?: OllamaErrorResponseContext | undefined;\n } = {},\n): OllamaClientError {\n if (error instanceof OllamaClientError) {\n return error;\n }\n if (isDomAbortError(error)) {\n return new OllamaAbortError(error.message || 'Request was aborted', {\n cause: error,\n request: context.request,\n });\n }\n if (error instanceof TypeError) {\n return new OllamaNetworkError(error.message || 'Network request failed', {\n cause: error,\n request: context.request,\n });\n }\n if (context.response?.status !== undefined) {\n const message = error instanceof Error ? error.message : String(error);\n return statusToError(context.response.status, message, {\n cause: error,\n request: context.request,\n response: context.response,\n });\n }\n const message = error instanceof Error ? error.message : String(error);\n return new OllamaGenericClientError(message, {\n cause: error,\n request: context.request,\n response: context.response,\n });\n}\n","/**\n * Cross-runtime helpers with no Node.js API dependency, so they stay safe to import from\n * the core client bundle (see ADR 0006 — Edge Runtime CI).\n */\n\n/**\n * Encodes an image to a base64 string, matching the wire format Ollama's `/api/chat` and\n * `/api/generate` `images` arrays expect.\n *\n * - A `string` is assumed to already be base64-encoded and is returned unchanged.\n * - A `Uint8Array` is base64-encoded using whichever universal primitive the runtime\n * exposes: `Buffer` on Node.js, `btoa` on browsers/Edge runtimes. Chunked so it doesn't\n * blow the engine's max call-stack/argument-count limits on large images.\n */\nexport async function encodeImage(image: Uint8Array | string): Promise<string> {\n if (typeof image === 'string') return image;\n\n const bufferCtor = (\n globalThis as { Buffer?: { from(data: Uint8Array): { toString(encoding: string): string } } }\n ).Buffer;\n if (bufferCtor !== undefined) return bufferCtor.from(image).toString('base64');\n\n const CHUNK_SIZE = 0x8000;\n let binary = '';\n for (let i = 0; i < image.length; i += CHUNK_SIZE) {\n binary += String.fromCharCode(...image.subarray(i, i + CHUNK_SIZE));\n }\n return btoa(binary);\n}\n\nasync function encodeImages(\n images: readonly (string | Uint8Array)[] | undefined,\n): Promise<readonly string[] | undefined> {\n if (images === undefined) return undefined;\n return Promise.all(images.map((image) => encodeImage(image)));\n}\n\n/**\n * Returns `req` unchanged if `req.images` has no `Uint8Array` entries (the common case),\n * otherwise a shallow copy with `images` base64-encoded. Keeps `chat`/`generate` request\n * bodies wire-compatible without requiring callers to pre-encode raw image bytes.\n */\nexport async function withEncodedImages<\n T extends { images?: readonly (string | Uint8Array)[] | undefined },\n>(req: T): Promise<T> {\n if (!req.images?.some((image) => image instanceof Uint8Array)) return req;\n return { ...req, images: await encodeImages(req.images) };\n}\n\n/**\n * Applies {@link withEncodedImages} to each message's `images` array. Returns `messages`\n * unchanged (same reference) if no message carries a `Uint8Array` image.\n */\nexport async function withEncodedMessageImages<\n T extends { images?: readonly (string | Uint8Array)[] | undefined },\n>(messages: readonly T[]): Promise<readonly T[]> {\n if (!messages.some((message) => message.images?.some((image) => image instanceof Uint8Array)))\n return messages;\n return Promise.all(messages.map((message) => withEncodedImages(message)));\n}\n\n// ─── Disposable helpers (TS 5.2+ `using` declarations) ──────────────────\n//\n// These wrappers turn cleanup operations into `Disposable` / `AsyncDisposable`\n// values so callers can use `using` / `await using` declarations instead of\n// try/finally blocks. This eliminates the \"forgot to clean up in the finally\n// block\" bug class for timer, reader, and span lifecycles.\n\n/** Wraps a `setTimeout` handle so `clearTimeout` runs at scope exit. */\nexport function disposableTimer(timer: ReturnType<typeof setTimeout>): Disposable {\n return {\n [Symbol.dispose]() {\n clearTimeout(timer);\n },\n };\n}\n\n/** Wraps a `ReadableStreamDefaultReader` so `cancel()` + `releaseLock()` run at scope exit. */\nexport function disposableReader<T>(reader: ReadableStreamDefaultReader<T>): AsyncDisposable {\n return {\n async [Symbol.asyncDispose]() {\n try {\n await reader.cancel();\n } catch {\n /* stream may already be closed */\n }\n reader.releaseLock();\n },\n };\n}\n\n/** Wraps an OpenTelemetry span so `span.end()` runs at scope exit. */\nexport function disposableSpan(span: { end(): void }): Disposable {\n return {\n [Symbol.dispose]() {\n span.end();\n },\n };\n}\n","/**\n * NDJSON (newline-delimited JSON) stream reader for native fetch Response bodies.\n */\n\nimport { OllamaGenericClientError } from '../errors.js';\nimport { disposableReader } from '../utils.js';\n\n/**\n * Parses a byte stream from fetch body into an async generator of typed JSON chunks.\n */\nexport async function* parseNdjsonStream<T>(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<T, void, undefined> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n await using _reader = disposableReader(reader);\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n // Keep the last partial line in buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n yield JSON.parse(trimmed) as T;\n } catch (err) {\n throw new OllamaGenericClientError(`Failed to parse stream JSON chunk: ${trimmed}`, {\n cause: err,\n });\n }\n }\n }\n\n if (buffer.trim()) {\n yield JSON.parse(buffer.trim()) as T;\n }\n // reader.cancel() + reader.releaseLock() called automatically via `await using _reader`\n}\n","/**\n * OllamaRuntime — the runtime seam generated API classes delegate to.\n *\n * This is the only hand-written file in `src/generated/runtime/`. Every\n * generated method in `src/generated/api/*-api.ts` ends with:\n *\n * ```ts\n * return this.runtime.invoke({ operation: chatOp, body: request });\n * ```\n *\n * The runtime is responsible for:\n * - Method/path resolution (from {@link OperationDefinition})\n * - Streaming default application (when the request doesn't set `stream`)\n * - Environment guard (rejecting cloud calls for local-only operations)\n * - Version constraint guard (failing fast when the server is too old)\n * - Delegating to the existing {@link HttpClient} for transport, retry,\n * telemetry, NDJSON/SSE parsing\n *\n * It is intentionally a thin adapter — the existing transport, streaming,\n * middleware, and error-mapping layers are preserved verbatim. This file\n * only adds the contract-aware glue between generated API classes and the\n * existing HttpClient.\n */\nimport { HttpClient, type HttpRequestOptions } from '../../transport/http.js';\nimport { parseNdjsonStream } from '../../streaming/ndjson.js';\nimport { OllamaGenericClientError } from '../../errors.js';\nimport type { OperationDefinition, InvokeRequest } from './operation-definition.js';\n\n/** Constructor options for {@link OllamaRuntime}. */\nexport interface OllamaRuntimeOptions {\n readonly http: HttpClient;\n /**\n * Whether this runtime is targeting a local Ollama instance. Defaults to\n * `true`. When `false`, operations marked `environment.cloud === false`\n * (i.e. local-only like `/v1/systemone`) are rejected at request time.\n */\n readonly localMode?: boolean;\n /**\n * Server version, if known (typically from a prior `/api/version` call).\n * When set, operations with `constraints.minOllamaVersion` are checked\n * against this value at request time.\n */\n readonly serverVersion?: string;\n}\n\nfunction compareVersions(a: string, b: string): number {\n const pa = a.split('.').map((s) => Number.parseInt(s, 10) ?? 0);\n const pb = b.split('.').map((s) => Number.parseInt(s, 10) ?? 0);\n for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {\n const va = pa[i] ?? 0;\n const vb = pb[i] ?? 0;\n if (va !== vb) return va - vb;\n }\n return 0;\n}\n\n/**\n * Apply environment + version guards before delegating to HttpClient.\n *\n * Throws {@link OllamaGenericClientError} when:\n * - The operation is local-only and the runtime is in cloud mode.\n * - The operation has `constraints.minOllamaVersion` and the server is older.\n */\nfunction assertOperationAllowed(\n operation: OperationDefinition,\n options: OllamaRuntimeOptions,\n): void {\n const localMode = options.localMode ?? true;\n if (!localMode && !operation.environment.cloud) {\n throw new OllamaGenericClientError(\n `Operation ${operation.operationId} (${operation.method} ${operation.path}) ` +\n `is local-only and not supported in cloud mode.`,\n );\n }\n if (\n operation.constraints?.minOllamaVersion &&\n options.serverVersion &&\n compareVersions(options.serverVersion, operation.constraints.minOllamaVersion) < 0\n ) {\n throw new OllamaGenericClientError(\n `Operation ${operation.operationId} (${operation.method} ${operation.path}) ` +\n `requires Ollama >= ${operation.constraints.minOllamaVersion} ` +\n `(server reports ${options.serverVersion}).`,\n );\n }\n}\n\n/**\n * Decide whether the request should stream. If the request body explicitly\n * sets `stream`, that wins. Otherwise the operation's `streamingDefault`\n * from the contract is used.\n */\nfunction shouldStream(req: InvokeRequest): boolean {\n if (req.body && typeof req.body === 'object' && 'stream' in req.body) {\n const explicit = (req.body as { stream?: unknown }).stream;\n if (typeof explicit === 'boolean') return explicit;\n }\n return Boolean(req.streamingDefault ?? req.operation.transport.streamingDefault);\n}\n\n/**\n * Apply the streaming default to the request body if needed.\n *\n * If the operation streams by default and the body doesn't set `stream`,\n * add `stream: true`. The user can always override by setting `stream`\n * explicitly on the request.\n */\nfunction buildBody(req: InvokeRequest): unknown {\n if (req.body === undefined) return undefined;\n if (typeof req.body !== 'object' || req.body === null) return req.body;\n const body = req.body as Record<string, unknown>;\n if (\n req.operation.transport.streaming &&\n req.operation.transport.streamingDefault &&\n body.stream === undefined\n ) {\n return { ...body, stream: true };\n }\n return body;\n}\n\n/** The runtime seam. */\nexport class OllamaRuntime {\n constructor(private readonly options: OllamaRuntimeOptions) {}\n\n /**\n * Invoke an operation. Called by every generated API method.\n *\n * Returns a `Promise<T>` for non-streaming operations, or an\n * `AsyncGenerator<T>` for streaming operations. The generated API\n * class's overloads pick the right return type at compile time.\n */\n async invoke<T = unknown>(req: InvokeRequest): Promise<T> {\n assertOperationAllowed(req.operation, this.options);\n const body = buildBody(req);\n const streaming = shouldStream(req);\n\n // The HttpClient expects the narrow method union 'GET' | 'POST' |\n // 'DELETE' | 'HEAD' | undefined — cast through `as` because our\n // OperationDefinition.method is the wider HttpMethod type.\n const httpReq: HttpRequestOptions = {\n path: req.operation.path,\n method: req.operation.method as 'GET' | 'POST' | 'DELETE' | 'HEAD',\n ...(body !== undefined ? { body } : {}),\n ...(req.signal !== undefined ? { signal: req.signal } : {}),\n // For streaming calls, request the raw Response so we can pipe it\n // through parseNdjsonStream directly. This inherits middleware,\n // retry, telemetry, and error-mapping from HttpClient — the\n // previous Wave 3 implementation bypassed them with a direct\n // `fetch()` call.\n ...(streaming ? { raw: true } : {}),\n };\n\n if (!streaming) {\n return (await this.options.http.request<T>(httpReq)) as T;\n }\n\n // Streaming: HttpClient returns the raw Response (via the `raw: true`\n // option) so we can read its body as an NDJSON stream.\n const response = await this.options.http.request<Response>(httpReq);\n if (!response.body) {\n throw new OllamaGenericClientError(\n `Operation ${req.operation.operationId}: streaming response had no body.`,\n );\n }\n return parseNdjsonStream<T>(response.body) as unknown as T;\n }\n}\n"]}
package/dist/index.cjs CHANGED
@@ -2,6 +2,52 @@
2
2
 
3
3
  var zod = require('zod');
4
4
 
5
+ var __knownSymbol = (name, symbol) => (symbol = Symbol[name]) ? symbol : /* @__PURE__ */ Symbol.for("Symbol." + name);
6
+ var __typeError = (msg) => {
7
+ throw TypeError(msg);
8
+ };
9
+ var __using = (stack, value, async) => {
10
+ if (value != null) {
11
+ if (typeof value !== "object" && typeof value !== "function") __typeError("Object expected");
12
+ var dispose, inner;
13
+ if (async) dispose = value[__knownSymbol("asyncDispose")];
14
+ if (dispose === void 0) {
15
+ dispose = value[__knownSymbol("dispose")];
16
+ if (async) inner = dispose;
17
+ }
18
+ if (typeof dispose !== "function") __typeError("Object not disposable");
19
+ if (inner) dispose = function() {
20
+ try {
21
+ inner.call(this);
22
+ } catch (e) {
23
+ return Promise.reject(e);
24
+ }
25
+ };
26
+ stack.push([async, dispose, value]);
27
+ } else if (async) {
28
+ stack.push([async]);
29
+ }
30
+ return value;
31
+ };
32
+ var __callDispose = (stack, error, hasError) => {
33
+ var E = typeof SuppressedError === "function" ? SuppressedError : function(e, s, m, _) {
34
+ return _ = Error(m), _.name = "SuppressedError", _.error = e, _.suppressed = s, _;
35
+ };
36
+ var fail = (e) => error = hasError ? new E(e, error, "An error was suppressed during disposal") : (hasError = true, e);
37
+ var next = (it) => {
38
+ while (it = stack.pop()) {
39
+ try {
40
+ var result = it[1] && it[1].call(it[2]);
41
+ if (it[0]) return Promise.resolve(result).then(next, (e) => (fail(e), next()));
42
+ } catch (e) {
43
+ fail(e);
44
+ }
45
+ }
46
+ if (hasError) throw error;
47
+ };
48
+ return next();
49
+ };
50
+
5
51
  // src/config.ts
6
52
  var DEFAULT_BASE_URL = "http://localhost:11434";
7
53
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -494,12 +540,65 @@ function composeMiddleware(middlewares, finalHandler) {
494
540
  };
495
541
  }
496
542
 
543
+ // src/utils.ts
544
+ async function encodeImage(image) {
545
+ if (typeof image === "string") return image;
546
+ const bufferCtor = globalThis.Buffer;
547
+ if (bufferCtor !== void 0) return bufferCtor.from(image).toString("base64");
548
+ const CHUNK_SIZE = 32768;
549
+ let binary = "";
550
+ for (let i = 0; i < image.length; i += CHUNK_SIZE) {
551
+ binary += String.fromCharCode(...image.subarray(i, i + CHUNK_SIZE));
552
+ }
553
+ return btoa(binary);
554
+ }
555
+ async function encodeImages(images) {
556
+ if (images === void 0) return void 0;
557
+ return Promise.all(images.map((image) => encodeImage(image)));
558
+ }
559
+ async function withEncodedImages(req) {
560
+ if (!req.images?.some((image) => image instanceof Uint8Array)) return req;
561
+ return { ...req, images: await encodeImages(req.images) };
562
+ }
563
+ async function withEncodedMessageImages(messages) {
564
+ if (!messages.some((message) => message.images?.some((image) => image instanceof Uint8Array)))
565
+ return messages;
566
+ return Promise.all(messages.map((message) => withEncodedImages(message)));
567
+ }
568
+ function disposableTimer(timer) {
569
+ return {
570
+ [Symbol.dispose]() {
571
+ clearTimeout(timer);
572
+ }
573
+ };
574
+ }
575
+ function disposableReader(reader) {
576
+ return {
577
+ async [Symbol.asyncDispose]() {
578
+ try {
579
+ await reader.cancel();
580
+ } catch {
581
+ }
582
+ reader.releaseLock();
583
+ }
584
+ };
585
+ }
586
+ function disposableSpan(span) {
587
+ return {
588
+ [Symbol.dispose]() {
589
+ span.end();
590
+ }
591
+ };
592
+ }
593
+
497
594
  // src/streaming/ndjson.ts
498
595
  async function* parseNdjsonStream(stream) {
499
- const reader = stream.getReader();
500
- const decoder = new TextDecoder();
501
- let buffer = "";
596
+ var _stack = [];
502
597
  try {
598
+ const reader = stream.getReader();
599
+ const decoder = new TextDecoder();
600
+ let buffer = "";
601
+ const _reader = __using(_stack, disposableReader(reader), true);
503
602
  while (true) {
504
603
  const { done, value } = await reader.read();
505
604
  if (done) break;
@@ -521,12 +620,11 @@ async function* parseNdjsonStream(stream) {
521
620
  if (buffer.trim()) {
522
621
  yield JSON.parse(buffer.trim());
523
622
  }
623
+ } catch (_) {
624
+ var _error = _, _hasError = true;
524
625
  } finally {
525
- try {
526
- await reader.cancel();
527
- } catch {
528
- }
529
- reader.releaseLock();
626
+ var _promise = __callDispose(_stack, _error, _hasError);
627
+ _promise && await _promise;
530
628
  }
531
629
  }
532
630
 
@@ -542,47 +640,51 @@ function dispatch(event, data, id, retry) {
542
640
  };
543
641
  }
544
642
  async function* parseSseStream(stream) {
545
- const reader = stream.getReader();
546
- const decoder = new TextDecoder();
547
- let buffer = "";
548
- let eventType;
549
- let data = [];
550
- let lastEventId;
551
- let retry;
552
- const processLine = (line) => {
553
- if (line.length === 0) {
554
- const event = dispatch(eventType, data, lastEventId, retry);
555
- eventType = void 0;
556
- data = [];
557
- retry = void 0;
558
- return event;
559
- }
560
- if (line.startsWith(":")) return void 0;
561
- const separator = line.indexOf(":");
562
- const field = separator === -1 ? line : line.slice(0, separator);
563
- let value = separator === -1 ? "" : line.slice(separator + 1);
564
- if (value.startsWith(" ")) value = value.slice(1);
565
- switch (field) {
566
- case "event":
567
- eventType = value;
568
- break;
569
- case "data":
570
- data.push(value);
571
- break;
572
- case "id":
573
- if (!value.includes("\0")) lastEventId = value;
574
- break;
575
- case "retry": {
576
- if (/^\d+$/.test(value)) {
577
- const parsed = Number(value);
578
- if (Number.isSafeInteger(parsed)) retry = parsed;
643
+ var _stack = [];
644
+ try {
645
+ const reader = stream.getReader();
646
+ const decoder = new TextDecoder();
647
+ let buffer = "";
648
+ let eventType;
649
+ let data = [];
650
+ let lastEventId;
651
+ let retry;
652
+ const processLine = (line) => {
653
+ if (line.length === 0) {
654
+ const event = dispatch(eventType, data, lastEventId, retry);
655
+ eventType = void 0;
656
+ data = [];
657
+ retry = void 0;
658
+ return event;
659
+ }
660
+ if (line.startsWith(":")) return void 0;
661
+ const separator = line.indexOf(":");
662
+ const field = separator === -1 ? line : line.slice(0, separator);
663
+ let value = separator === -1 ? "" : line.slice(separator + 1);
664
+ if (value.startsWith(" ")) value = value.slice(1);
665
+ switch (field) {
666
+ case "event":
667
+ eventType = value;
668
+ break;
669
+ case "data":
670
+ data.push(value);
671
+ break;
672
+ case "id":
673
+ if (!value.includes("\0")) lastEventId = value;
674
+ break;
675
+ case "retry": {
676
+ if (/^\d+$/.test(value)) {
677
+ const parsed = Number(value);
678
+ if (Number.isSafeInteger(parsed)) retry = parsed;
679
+ }
680
+ break;
579
681
  }
580
- break;
682
+ default:
683
+ break;
581
684
  }
582
- }
583
- return void 0;
584
- };
585
- try {
685
+ return void 0;
686
+ };
687
+ const _reader = __using(_stack, disposableReader(reader), true);
586
688
  while (true) {
587
689
  const { done, value } = await reader.read();
588
690
  if (done) break;
@@ -610,12 +712,11 @@ async function* parseSseStream(stream) {
610
712
  }
611
713
  const terminal = dispatch(eventType, data, lastEventId, retry);
612
714
  if (terminal) yield terminal;
715
+ } catch (_) {
716
+ var _error = _, _hasError = true;
613
717
  } finally {
614
- try {
615
- await reader.cancel();
616
- } catch {
617
- }
618
- reader.releaseLock();
718
+ var _promise = __callDispose(_stack, _error, _hasError);
719
+ _promise && await _promise;
619
720
  }
620
721
  }
621
722
 
@@ -643,16 +744,22 @@ async function withSpan(name, attributes, fn) {
643
744
  }
644
745
  const tracer = api.trace.getTracer(INSTRUMENTATION_NAME, INSTRUMENTATION_VERSION);
645
746
  return tracer.startActiveSpan(name, async (span) => {
646
- span.setAttributes(cleanAttributes(attributes));
747
+ var _stack = [];
647
748
  try {
648
- return await fn(span);
649
- } catch (err) {
650
- const error = err instanceof Error ? err : new Error(String(err));
651
- span.recordException(error);
652
- span.setStatus({ code: api.SpanStatusCode.ERROR, message: error.message });
653
- throw error;
749
+ span.setAttributes(cleanAttributes(attributes));
750
+ const _span = __using(_stack, disposableSpan(span));
751
+ try {
752
+ return await fn(span);
753
+ } catch (err) {
754
+ const error = err instanceof Error ? err : new Error(String(err));
755
+ span.recordException(error);
756
+ span.setStatus({ code: api.SpanStatusCode.ERROR, message: error.message });
757
+ throw error;
758
+ }
759
+ } catch (_) {
760
+ var _error = _, _hasError = true;
654
761
  } finally {
655
- span.end();
762
+ __callDispose(_stack, _error, _hasError);
656
763
  }
657
764
  });
658
765
  }
@@ -1062,42 +1169,48 @@ var HttpClient = class {
1062
1169
 
1063
1170
  // src/providers/health-check.ts
1064
1171
  async function checkEndpointHealth(endpoint, fetchImpl, timeoutMs = 5e3, middleware, onLifecycleEvent) {
1065
- const client = new HttpClient({
1066
- baseUrl: endpoint.baseUrl,
1067
- apiKey: endpoint.apiKey,
1068
- headers: endpoint.headers,
1069
- fetch: fetchImpl,
1070
- middleware,
1071
- onLifecycleEvent
1072
- });
1073
- const startTime = Date.now();
1074
- const controller = new AbortController();
1075
- const timer = setTimeout(() => controller.abort(), timeoutMs);
1172
+ var _stack = [];
1076
1173
  try {
1077
- const res = await client.request({
1078
- path: "/api/version",
1079
- method: "GET",
1080
- signal: controller.signal
1081
- });
1082
- const latencyMs = Date.now() - startTime;
1083
- return {
1084
- name: endpoint.name,
1174
+ const client = new HttpClient({
1085
1175
  baseUrl: endpoint.baseUrl,
1086
- reachable: true,
1087
- latencyMs,
1088
- version: res.version
1089
- };
1090
- } catch (err) {
1091
- const latencyMs = Date.now() - startTime;
1092
- return {
1093
- name: endpoint.name,
1094
- baseUrl: endpoint.baseUrl,
1095
- reachable: false,
1096
- latencyMs,
1097
- error: err instanceof Error ? err.message : String(err)
1098
- };
1176
+ apiKey: endpoint.apiKey,
1177
+ headers: endpoint.headers,
1178
+ fetch: fetchImpl,
1179
+ middleware,
1180
+ onLifecycleEvent
1181
+ });
1182
+ const startTime = Date.now();
1183
+ const controller = new AbortController();
1184
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
1185
+ const _timer = __using(_stack, disposableTimer(timer));
1186
+ try {
1187
+ const res = await client.request({
1188
+ path: "/api/version",
1189
+ method: "GET",
1190
+ signal: controller.signal
1191
+ });
1192
+ const latencyMs = Date.now() - startTime;
1193
+ return {
1194
+ name: endpoint.name,
1195
+ baseUrl: endpoint.baseUrl,
1196
+ reachable: true,
1197
+ latencyMs,
1198
+ version: res.version
1199
+ };
1200
+ } catch (err) {
1201
+ const latencyMs = Date.now() - startTime;
1202
+ return {
1203
+ name: endpoint.name,
1204
+ baseUrl: endpoint.baseUrl,
1205
+ reachable: false,
1206
+ latencyMs,
1207
+ error: err instanceof Error ? err.message : String(err)
1208
+ };
1209
+ }
1210
+ } catch (_) {
1211
+ var _error = _, _hasError = true;
1099
1212
  } finally {
1100
- clearTimeout(timer);
1213
+ __callDispose(_stack, _error, _hasError);
1101
1214
  }
1102
1215
  }
1103
1216
 
@@ -2708,32 +2821,6 @@ var OllamaRuntime = class {
2708
2821
  }
2709
2822
  };
2710
2823
 
2711
- // src/utils.ts
2712
- async function encodeImage(image) {
2713
- if (typeof image === "string") return image;
2714
- const bufferCtor = globalThis.Buffer;
2715
- if (bufferCtor !== void 0) return bufferCtor.from(image).toString("base64");
2716
- const CHUNK_SIZE = 32768;
2717
- let binary = "";
2718
- for (let i = 0; i < image.length; i += CHUNK_SIZE) {
2719
- binary += String.fromCharCode(...image.subarray(i, i + CHUNK_SIZE));
2720
- }
2721
- return btoa(binary);
2722
- }
2723
- async function encodeImages(images) {
2724
- if (images === void 0) return void 0;
2725
- return Promise.all(images.map((image) => encodeImage(image)));
2726
- }
2727
- async function withEncodedImages(req) {
2728
- if (!req.images?.some((image) => image instanceof Uint8Array)) return req;
2729
- return { ...req, images: await encodeImages(req.images) };
2730
- }
2731
- async function withEncodedMessageImages(messages) {
2732
- if (!messages.some((message) => message.images?.some((image) => image instanceof Uint8Array)))
2733
- return messages;
2734
- return Promise.all(messages.map((message) => withEncodedImages(message)));
2735
- }
2736
-
2737
2824
  // src/client.ts
2738
2825
  var logicalRequestSequence = 0;
2739
2826
  function createLogicalRequestId() {
@@ -3140,9 +3227,15 @@ var OllamaClient = class {
3140
3227
  * multi-candidate loop, since `webSearch`/`webFetch` only ever have the one endpoint.
3141
3228
  */
3142
3229
  async executeCloudRequest(operation, options) {
3143
- const timeout = createTimeoutSignal(options.timeoutMs ?? this.timeoutMs, options.signal);
3144
- const requestId = createLogicalRequestId();
3230
+ var _stack = [];
3145
3231
  try {
3232
+ const timeout = createTimeoutSignal(options.timeoutMs ?? this.timeoutMs, options.signal);
3233
+ const _timeout = __using(_stack, {
3234
+ [Symbol.dispose]() {
3235
+ timeout.cancel();
3236
+ }
3237
+ });
3238
+ const requestId = createLogicalRequestId();
3146
3239
  const http = new HttpClient({
3147
3240
  baseUrl: OLLAMA_CLOUD_BASE_URL,
3148
3241
  apiKey: this.cloudApiKey,
@@ -3169,8 +3262,10 @@ var OllamaClient = class {
3169
3262
  },
3170
3263
  timeout.signal
3171
3264
  );
3265
+ } catch (_) {
3266
+ var _error = _, _hasError = true;
3172
3267
  } finally {
3173
- timeout.cancel();
3268
+ __callDispose(_stack, _error, _hasError);
3174
3269
  }
3175
3270
  }
3176
3271
  // --- Capabilities & Health ---
@@ -3531,25 +3626,32 @@ var ToolRegistry = class {
3531
3626
  return results;
3532
3627
  }
3533
3628
  async runWithTimeout(tool, params, ctx, toolName) {
3534
- const timeoutMs = tool.timeoutMs ?? this.defaultTimeoutMs;
3535
- if (!timeoutMs || timeoutMs <= 0) {
3536
- return tool.execute(params, ctx);
3537
- }
3538
- const controller = new AbortController();
3539
- const onParentAbort = () => controller.abort(ctx.signal?.reason);
3540
- if (ctx.signal) {
3541
- if (ctx.signal.aborted) {
3542
- controller.abort(ctx.signal.reason);
3543
- } else {
3544
- ctx.signal.addEventListener("abort", onParentAbort, { once: true });
3545
- }
3546
- }
3547
- let timedOut = false;
3548
- const timer = setTimeout(() => {
3549
- timedOut = true;
3550
- controller.abort();
3551
- }, timeoutMs);
3629
+ var _stack = [];
3552
3630
  try {
3631
+ const timeoutMs = tool.timeoutMs ?? this.defaultTimeoutMs;
3632
+ if (!timeoutMs || timeoutMs <= 0) {
3633
+ return tool.execute(params, ctx);
3634
+ }
3635
+ const controller = new AbortController();
3636
+ const onParentAbort = () => controller.abort(ctx.signal?.reason);
3637
+ if (ctx.signal) {
3638
+ if (ctx.signal.aborted) {
3639
+ controller.abort(ctx.signal.reason);
3640
+ } else {
3641
+ ctx.signal.addEventListener("abort", onParentAbort, { once: true });
3642
+ }
3643
+ }
3644
+ let timedOut = false;
3645
+ const timer = setTimeout(() => {
3646
+ timedOut = true;
3647
+ controller.abort();
3648
+ }, timeoutMs);
3649
+ const _timer = __using(_stack, disposableTimer(timer));
3650
+ const _listener = __using(_stack, {
3651
+ [Symbol.dispose]() {
3652
+ ctx.signal?.removeEventListener("abort", onParentAbort);
3653
+ }
3654
+ });
3553
3655
  const executePromise = Promise.resolve(
3554
3656
  tool.execute(params, { ...ctx, signal: controller.signal })
3555
3657
  );
@@ -3566,11 +3668,10 @@ var ToolRegistry = class {
3566
3668
  });
3567
3669
  })
3568
3670
  ]);
3671
+ } catch (_) {
3672
+ var _error = _, _hasError = true;
3569
3673
  } finally {
3570
- clearTimeout(timer);
3571
- if (ctx.signal) {
3572
- ctx.signal.removeEventListener("abort", onParentAbort);
3573
- }
3674
+ __callDispose(_stack, _error, _hasError);
3574
3675
  }
3575
3676
  }
3576
3677
  truncateOutput(output) {