@nemesis-oss/ollama-sdk 1.7.0 → 1.9.0

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +168 -1
  2. package/README.md +181 -10
  3. package/dist/anthropic-5oYIRS6D.d.ts +1649 -0
  4. package/dist/anthropic-BIh45UXj.d.cts +1649 -0
  5. package/dist/{compose-DLStUfaF.d.ts → compose-DCVYMvOL.d.ts} +1 -1
  6. package/dist/{compose-DS-UWFx8.d.cts → compose-Dj6FdF2n.d.cts} +1 -1
  7. package/dist/generated-api.cjs +116 -9
  8. package/dist/generated-api.cjs.map +1 -1
  9. package/dist/generated-api.d.cts +321 -630
  10. package/dist/generated-api.d.ts +321 -630
  11. package/dist/generated-api.js +112 -10
  12. package/dist/generated-api.js.map +1 -1
  13. package/dist/generated-models-schemas.cjs +151 -3
  14. package/dist/generated-models-schemas.cjs.map +1 -1
  15. package/dist/generated-models-schemas.d.cts +228 -3
  16. package/dist/generated-models-schemas.d.ts +228 -3
  17. package/dist/generated-models-schemas.js +134 -4
  18. package/dist/generated-models-schemas.js.map +1 -1
  19. package/dist/generated-runtime.cjs +679 -60
  20. package/dist/generated-runtime.cjs.map +1 -1
  21. package/dist/generated-runtime.d.cts +22 -24
  22. package/dist/generated-runtime.d.ts +22 -24
  23. package/dist/generated-runtime.js +678 -61
  24. package/dist/generated-runtime.js.map +1 -1
  25. package/dist/index.cjs +2468 -132
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +1666 -864
  28. package/dist/index.d.ts +1666 -864
  29. package/dist/index.js +2432 -133
  30. package/dist/index.js.map +1 -1
  31. package/dist/mcp/tools.json +306 -73
  32. package/dist/mcp-generated.cjs +138 -7
  33. package/dist/mcp-generated.cjs.map +1 -1
  34. package/dist/mcp-generated.d.cts +2 -2
  35. package/dist/mcp-generated.d.ts +2 -2
  36. package/dist/mcp-generated.js +138 -7
  37. package/dist/mcp-generated.js.map +1 -1
  38. package/dist/{runtime-qwD72cpn.d.ts → runtime-C3rfpkZw.d.ts} +286 -5
  39. package/dist/{runtime-bQ48I-RC.d.cts → runtime-D2W9TfCJ.d.cts} +286 -5
  40. package/dist/skills.cjs.map +1 -1
  41. package/dist/skills.d.cts +3 -3
  42. package/dist/skills.d.ts +3 -3
  43. package/dist/skills.js.map +1 -1
  44. package/dist/{types-5PAjLoCp.d.cts → types-DVcX_zMp.d.cts} +110 -10
  45. package/dist/{types-5PAjLoCp.d.ts → types-DVcX_zMp.d.ts} +110 -10
  46. package/package.json +9 -5
package/dist/index.js CHANGED
@@ -46,6 +46,47 @@ var __callDispose = (stack, error, hasError) => {
46
46
  return next();
47
47
  };
48
48
 
49
+ // src/transport/host.ts
50
+ var DEFAULT_PORT = "11434";
51
+ var SCHEME_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
52
+ function normalizeBaseUrl(host) {
53
+ const trimmed = host.trim();
54
+ if (trimmed === "") return trimmed;
55
+ const implicitScheme = !SCHEME_RE.test(trimmed);
56
+ const withHost = trimmed.startsWith(":") ? `127.0.0.1${trimmed}` : trimmed;
57
+ const candidate = implicitScheme ? `http://${withHost}` : withHost;
58
+ let url;
59
+ try {
60
+ url = new URL(candidate);
61
+ } catch {
62
+ return trimmed.replace(/\/+$/, "");
63
+ }
64
+ let protocol = url.protocol;
65
+ let port = url.port;
66
+ if (implicitScheme) {
67
+ if (port === "443") {
68
+ protocol = "https:";
69
+ port = "";
70
+ } else if (port === "") {
71
+ port = DEFAULT_PORT;
72
+ }
73
+ }
74
+ const authority = `${url.hostname}${port !== "" ? `:${port}` : ""}`;
75
+ return `${protocol}//${authority}${url.pathname}`.replace(/\/+$/, "");
76
+ }
77
+ function isEnvApiKeyHost(baseUrl) {
78
+ let hostname;
79
+ try {
80
+ hostname = new URL(normalizeBaseUrl(baseUrl)).hostname.toLowerCase();
81
+ } catch {
82
+ return false;
83
+ }
84
+ if (hostname === "ollama.com" || hostname.endsWith(".ollama.com")) return true;
85
+ if (hostname === "localhost" || hostname.endsWith(".localhost")) return true;
86
+ if (hostname === "[::1]" || hostname === "::1" || hostname === "0.0.0.0") return true;
87
+ return /^127(?:\.\d{1,3}){3}$/.test(hostname);
88
+ }
89
+
49
90
  // src/config.ts
50
91
  var DEFAULT_BASE_URL = "http://localhost:11434";
51
92
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -54,11 +95,16 @@ function readEnv(name) {
54
95
  return typeof process !== "undefined" && process.env ? process.env[name] : void 0;
55
96
  }
56
97
  function resolveBaseUrl(configBaseUrl) {
57
- return configBaseUrl ?? readEnv("OLLAMA_HOST") ?? DEFAULT_BASE_URL;
98
+ return normalizeBaseUrl(configBaseUrl ?? readEnv("OLLAMA_HOST") ?? DEFAULT_BASE_URL);
58
99
  }
59
100
  function resolveApiKey(configApiKey) {
60
101
  return configApiKey ?? readEnv("OLLAMA_API_KEY");
61
102
  }
103
+ function resolveEndpointApiKey(configApiKey, baseUrl) {
104
+ if (configApiKey !== void 0) return configApiKey;
105
+ const envKey = readEnv("OLLAMA_API_KEY");
106
+ return envKey !== void 0 && isEnvApiKeyHost(baseUrl) ? envKey : void 0;
107
+ }
62
108
  function resolveCredentialEndpoints(config) {
63
109
  if (config.credentials === void 0) return [];
64
110
  const modelsByCredential = /* @__PURE__ */ new Map();
@@ -91,6 +137,18 @@ var DEFAULT_FAILOVER_CODES = [
91
137
  "network_error",
92
138
  "timeout",
93
139
  "server_error",
140
+ // HTTP 503 — server queue saturated (OLLAMA_MAX_QUEUE exceeded).
141
+ // The new OllamaOverloadedError specialization is retryable: the
142
+ // queue will eventually drain, and a different candidate endpoint
143
+ // may have capacity right now. Listed separately from
144
+ // 'server_error' because OllamaOverloadedError extends
145
+ // OllamaClientError (NOT OllamaServerError) — see
146
+ // src/errors.ts for the rationale.
147
+ "overloaded",
148
+ // HTTP 502 — cloud model could not be reached by the Ollama Cloud
149
+ // proxy. Fail over to a different model/endpoint rather than
150
+ // retrying the same one.
151
+ "bad_gateway",
94
152
  "rate_limited",
95
153
  "auth_error",
96
154
  // A candidate endpoint rejected the request pre-flight for a known-unsupported
@@ -159,6 +217,24 @@ var OllamaServerError = class extends OllamaClientError {
159
217
  super(message, { ...options, code: "server_error", retryable: true });
160
218
  }
161
219
  };
220
+ var OllamaOverloadedError = class extends OllamaClientError {
221
+ constructor(message, options) {
222
+ super(message, { ...options, code: "overloaded", status: 503, retryable: true });
223
+ }
224
+ };
225
+ var OllamaBadGatewayError = class extends OllamaClientError {
226
+ constructor(message, options) {
227
+ super(message, { ...options, code: "bad_gateway", status: 502, retryable: true });
228
+ }
229
+ };
230
+ var OllamaStreamError = class extends OllamaClientError {
231
+ /** Content accumulated before the in-band error frame, or '' if none. */
232
+ partialContent;
233
+ constructor(message, options) {
234
+ super(message, { ...options, code: "stream_error", retryable: false });
235
+ this.partialContent = options?.partialContent ?? "";
236
+ }
237
+ };
162
238
  var OllamaAbortError = class extends OllamaClientError {
163
239
  constructor(message, options) {
164
240
  super(message, { ...options, code: "aborted", retryable: false });
@@ -182,6 +258,35 @@ var OllamaRequestValidationError = class extends OllamaClientError {
182
258
  this.issues = options.issues;
183
259
  }
184
260
  };
261
+ var OllamaRequestTooLargeError = class extends OllamaClientError {
262
+ operationId;
263
+ actualBytes;
264
+ maxBytes;
265
+ constructor(message, options) {
266
+ super(message, { ...options, code: "request_too_large", retryable: false, status: 413 });
267
+ this.operationId = options.operationId;
268
+ this.actualBytes = options.actualBytes;
269
+ this.maxBytes = options.maxBytes;
270
+ }
271
+ };
272
+ var OllamaResponseValidationError = class extends OllamaClientError {
273
+ operationId;
274
+ issues;
275
+ constructor(message, options) {
276
+ super(message, { ...options, code: "response_validation_error", retryable: false });
277
+ this.operationId = options.operationId;
278
+ this.issues = options.issues;
279
+ }
280
+ };
281
+ var OllamaServerVersionUnknownError = class extends OllamaClientError {
282
+ operationId;
283
+ minRequiredVersion;
284
+ constructor(message, options) {
285
+ super(message, { ...options, code: "server_version_unknown", retryable: false });
286
+ this.operationId = options.operationId;
287
+ this.minRequiredVersion = options.minRequiredVersion;
288
+ }
289
+ };
185
290
  var OllamaToolTimeoutError = class extends OllamaClientError {
186
291
  toolName;
187
292
  timeoutMs;
@@ -279,6 +384,12 @@ function statusToError(status, message, options) {
279
384
  if (status === 429) {
280
385
  return new OllamaRateLimitError(message, { ...options, status });
281
386
  }
387
+ if (status === 502) {
388
+ return new OllamaBadGatewayError(message, { ...options });
389
+ }
390
+ if (status === 503) {
391
+ return new OllamaOverloadedError(message, { ...options });
392
+ }
282
393
  if (status >= 500) {
283
394
  return new OllamaServerError(message, { ...options, status });
284
395
  }
@@ -549,6 +660,75 @@ function composeMiddleware(middlewares, finalHandler) {
549
660
  };
550
661
  }
551
662
 
663
+ // src/vision.ts
664
+ var DATA_URI_PATTERN = /^data:image\/[a-z0-9.+-]+;base64,/i;
665
+ var IMAGE_FILE_EXTENSION_PATTERN = /\.(png|jpe?g|webp|gif|bmp|tiff?|avif)$/i;
666
+ function imageStringNeedsResolution(input) {
667
+ return DATA_URI_PATTERN.test(input) || input.startsWith("http://") || input.startsWith("https://") || IMAGE_FILE_EXTENSION_PATTERN.test(input);
668
+ }
669
+ function hasNodeFs() {
670
+ return typeof process !== "undefined" && process !== null && typeof process.versions?.node === "string";
671
+ }
672
+ var NODE_FS_MODULE = "node:fs";
673
+ async function readFileAsBase64(path) {
674
+ const { promises: fs } = await import(NODE_FS_MODULE);
675
+ const data = await fs.readFile(path);
676
+ return data.toString("base64");
677
+ }
678
+ async function fetchUrlAsBase64(url, signal) {
679
+ const res = await fetch(url, signal !== void 0 ? { signal } : void 0);
680
+ if (!res.ok) {
681
+ throw new OllamaClientError(
682
+ `Failed to fetch image from URL "${url}": HTTP ${res.status} ${res.statusText}.`,
683
+ { code: "image_fetch_failed" }
684
+ );
685
+ }
686
+ const arrayBuffer = await res.arrayBuffer();
687
+ return encodeBytesBase64(new Uint8Array(arrayBuffer));
688
+ }
689
+ function encodeBytesBase64(bytes) {
690
+ const bufferCtor = globalThis.Buffer;
691
+ if (bufferCtor !== void 0) return bufferCtor.from(bytes).toString("base64");
692
+ const CHUNK_SIZE = 32768;
693
+ let binary = "";
694
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
695
+ binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
696
+ }
697
+ return btoa(binary);
698
+ }
699
+ async function resolveImageInput(input, signal) {
700
+ if (typeof input !== "string") {
701
+ return encodeBytesBase64(input instanceof Uint8Array ? input : new Uint8Array(input));
702
+ }
703
+ if (DATA_URI_PATTERN.test(input)) {
704
+ return input.replace(DATA_URI_PATTERN, "").trim();
705
+ }
706
+ if (input.startsWith("http://") || input.startsWith("https://")) {
707
+ return fetchUrlAsBase64(input, signal);
708
+ }
709
+ if (IMAGE_FILE_EXTENSION_PATTERN.test(input)) {
710
+ if (!hasNodeFs()) {
711
+ throw new OllamaClientError(
712
+ `Vision input "${input}" looks like a local file path, but this runtime has no Node.js \`fs\` module (browser/edge). Read the file into bytes yourself and pass the \`Uint8Array\`, or pass a base64 string / data URI / http(s) URL instead.`,
713
+ { code: "vision_path_unsupported_runtime" }
714
+ );
715
+ }
716
+ try {
717
+ return await readFileAsBase64(input);
718
+ } catch {
719
+ return input;
720
+ }
721
+ }
722
+ return input.trim();
723
+ }
724
+ async function resolveImages(images, signal) {
725
+ if (images === void 0) return void 0;
726
+ if (images.every((image) => typeof image === "string" && !imageStringNeedsResolution(image))) {
727
+ return images;
728
+ }
729
+ return Promise.all(images.map((image) => resolveImageInput(image, signal)));
730
+ }
731
+
552
732
  // src/utils.ts
553
733
  async function encodeImage(image) {
554
734
  if (typeof image === "string") return image;
@@ -561,18 +741,22 @@ async function encodeImage(image) {
561
741
  }
562
742
  return btoa(binary);
563
743
  }
564
- async function encodeImages(images) {
565
- if (images === void 0) return void 0;
566
- return Promise.all(images.map((image) => encodeImage(image)));
744
+ async function encodeImages(images, signal) {
745
+ return resolveImages(images, signal);
567
746
  }
568
- async function withEncodedImages(req) {
569
- if (!req.images?.some((image) => image instanceof Uint8Array)) return req;
570
- return { ...req, images: await encodeImages(req.images) };
747
+ async function withEncodedImages(req, signal) {
748
+ const needsResolution = req.images?.some(
749
+ (image) => typeof image !== "string" || imageStringNeedsResolution(image)
750
+ );
751
+ if (!needsResolution) return req;
752
+ return { ...req, images: await encodeImages(req.images ?? [], signal) };
571
753
  }
572
- async function withEncodedMessageImages(messages) {
573
- if (!messages.some((message) => message.images?.some((image) => image instanceof Uint8Array)))
574
- return messages;
575
- return Promise.all(messages.map((message) => withEncodedImages(message)));
754
+ async function withEncodedMessageImages(messages, signal) {
755
+ const needsResolution = messages.some(
756
+ (message) => message.images?.some((image) => typeof image !== "string" || imageStringNeedsResolution(image))
757
+ );
758
+ if (!needsResolution) return messages;
759
+ return Promise.all(messages.map((message) => withEncodedImages(message, signal)));
576
760
  }
577
761
  function disposableTimer(timer) {
578
762
  return {
@@ -601,6 +785,12 @@ function disposableSpan(span) {
601
785
  }
602
786
 
603
787
  // src/streaming/ndjson.ts
788
+ function isInBandErrorFrame(value) {
789
+ if (typeof value !== "object" || value === null) return false;
790
+ const record = value;
791
+ if (typeof record.error !== "string" || record.error.length === 0) return false;
792
+ return !("model" in record) && !("response" in record) && !("message" in record) && !("status" in record) && !("done" in record);
793
+ }
604
794
  async function* parseNdjsonStream(stream) {
605
795
  var _stack = [];
606
796
  try {
@@ -617,17 +807,34 @@ async function* parseNdjsonStream(stream) {
617
807
  for (const line of lines) {
618
808
  const trimmed = line.trim();
619
809
  if (!trimmed) continue;
810
+ let json;
620
811
  try {
621
- yield JSON.parse(trimmed);
812
+ json = JSON.parse(trimmed);
622
813
  } catch (err) {
623
814
  throw new OllamaGenericClientError(`Failed to parse stream JSON chunk: ${trimmed}`, {
624
815
  cause: err
625
816
  });
626
817
  }
818
+ if (isInBandErrorFrame(json)) {
819
+ throw new OllamaStreamError(json.error);
820
+ }
821
+ yield json;
627
822
  }
628
823
  }
629
824
  if (buffer.trim()) {
630
- yield JSON.parse(buffer.trim());
825
+ const trimmed = buffer.trim();
826
+ let json;
827
+ try {
828
+ json = JSON.parse(trimmed);
829
+ } catch (err) {
830
+ throw new OllamaGenericClientError(`Failed to parse stream JSON chunk: ${trimmed}`, {
831
+ cause: err
832
+ });
833
+ }
834
+ if (isInBandErrorFrame(json)) {
835
+ throw new OllamaStreamError(json.error);
836
+ }
837
+ yield json;
631
838
  }
632
839
  } catch (_) {
633
840
  var _error = _, _hasError = true;
@@ -838,7 +1045,7 @@ var HttpClient = class {
838
1045
  onLifecycleEvent;
839
1046
  requestId;
840
1047
  constructor(options) {
841
- this.baseUrl = options.baseUrl.replace(/\/+$/, "");
1048
+ this.baseUrl = normalizeBaseUrl(options.baseUrl);
842
1049
  this.apiKey = options.apiKey;
843
1050
  this.defaultHeaders = options.headers ?? {};
844
1051
  this.fetchImpl = options.fetch ?? globalThis.fetch;
@@ -1227,7 +1434,7 @@ async function checkEndpointHealth(endpoint, fetchImpl, timeoutMs = 5e3, middlew
1227
1434
  function inferRuntimeMode(baseUrl) {
1228
1435
  try {
1229
1436
  const url = new URL(baseUrl);
1230
- const host = url.hostname.toLowerCase();
1437
+ const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
1231
1438
  if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.startsWith("192.168.") || host.startsWith("10.")) {
1232
1439
  return "local";
1233
1440
  }
@@ -1245,6 +1452,63 @@ function extractContextLength(modelInfo) {
1245
1452
  }
1246
1453
  return void 0;
1247
1454
  }
1455
+ function extractModelFamily(showRes) {
1456
+ if (showRes.model_info) {
1457
+ for (const [key, value] of Object.entries(showRes.model_info)) {
1458
+ if (key.endsWith(".architecture") && typeof value === "string" && value.length > 0) {
1459
+ return value.toLowerCase();
1460
+ }
1461
+ }
1462
+ }
1463
+ if (showRes.details?.family && showRes.details.family.length > 0) {
1464
+ return showRes.details.family.toLowerCase();
1465
+ }
1466
+ const families = showRes.details?.families;
1467
+ if (families && families.length > 0) {
1468
+ const first = families[0];
1469
+ if (typeof first === "string" && first.length > 0) return first.toLowerCase();
1470
+ }
1471
+ return void 0;
1472
+ }
1473
+ var PARALLEL_TOOL_CALL_LOCAL_FAMILIES = /* @__PURE__ */ new Set([
1474
+ "qwen2",
1475
+ "qwen2.5",
1476
+ "qwen3",
1477
+ "llama3.1",
1478
+ "llama3.2",
1479
+ "llama3.3",
1480
+ "llama4",
1481
+ "mistral",
1482
+ "mixtral",
1483
+ "hermes",
1484
+ "command-r",
1485
+ "command-r-plus"
1486
+ ]);
1487
+ var PARALLEL_TOOL_CALL_NAME_HINTS = /* @__PURE__ */ new Set([
1488
+ "tool-use",
1489
+ "tooluse",
1490
+ "function-call",
1491
+ "functioncall",
1492
+ "instruct",
1493
+ // Most "instruct" variants of tool-capable families ship with parallel tool calls
1494
+ "hermes",
1495
+ "command-r"
1496
+ ]);
1497
+ function inferLocalParallelToolCalls(model, family) {
1498
+ const modelLower = model.toLowerCase();
1499
+ if (family !== void 0 && PARALLEL_TOOL_CALL_LOCAL_FAMILIES.has(family)) {
1500
+ return "yes";
1501
+ }
1502
+ for (const hint of PARALLEL_TOOL_CALL_NAME_HINTS) {
1503
+ if (modelLower.includes(hint)) {
1504
+ return "yes";
1505
+ }
1506
+ }
1507
+ return "unknown";
1508
+ }
1509
+ function inferCloudParallelToolCalls(supportsTools) {
1510
+ return supportsTools ? "yes" : "unknown";
1511
+ }
1248
1512
  async function detectModelCapabilities(http, model, signal) {
1249
1513
  const showRes = await http.request({
1250
1514
  path: "/api/show",
@@ -1254,10 +1518,16 @@ async function detectModelCapabilities(http, model, signal) {
1254
1518
  const reported = showRes.capabilities ?? [];
1255
1519
  const reportedSet = new Set(reported.map((c) => c.toLowerCase()));
1256
1520
  const contextLength = extractContextLength(showRes.model_info);
1521
+ const supportsTools = reportedSet.has("tools");
1522
+ const runtimeMode = inferRuntimeMode(http.baseUrl);
1523
+ const parallelToolCalls = runtimeMode === "cloud" ? inferCloudParallelToolCalls(supportsTools) : runtimeMode === "local" ? inferLocalParallelToolCalls(model, extractModelFamily(showRes)) : (
1524
+ // unknown runtime mode — can't even tell if we're cloud or local
1525
+ "unknown"
1526
+ );
1257
1527
  return {
1258
1528
  model,
1259
1529
  reported,
1260
- supportsTools: reportedSet.has("tools"),
1530
+ supportsTools,
1261
1531
  supportsVision: reportedSet.has("vision"),
1262
1532
  supportsEmbedding: reportedSet.has("embedding"),
1263
1533
  supportsCompletion: reportedSet.has("completion") || !reportedSet.has("embedding"),
@@ -1265,7 +1535,8 @@ async function detectModelCapabilities(http, model, signal) {
1265
1535
  ...showRes.thinking !== void 0 ? { thinking: showRes.thinking } : {},
1266
1536
  ...contextLength !== void 0 ? { contextLength } : {},
1267
1537
  supportsStreaming: true,
1268
- supportsStructuredOutputRequest: inferRuntimeMode(http.baseUrl) !== "cloud"
1538
+ supportsStructuredOutputRequest: runtimeMode !== "cloud",
1539
+ parallelToolCalls
1269
1540
  };
1270
1541
  }
1271
1542
  async function listAvailableModels(http) {
@@ -1410,7 +1681,131 @@ function ensureToolCallIds(toolCalls) {
1410
1681
  return toolCalls.map((tc) => tc.id !== void 0 ? tc : { ...tc, id: generateToolCallId() });
1411
1682
  }
1412
1683
 
1684
+ // src/tools/tool-call-accumulator.ts
1685
+ function isSameToolCall(a, b, aIndex, bIndex) {
1686
+ if (a.id !== void 0 && b.id !== void 0) {
1687
+ return a.id === b.id;
1688
+ }
1689
+ if (aIndex !== void 0 && bIndex !== void 0) {
1690
+ return aIndex === bIndex;
1691
+ }
1692
+ return false;
1693
+ }
1694
+ function mergeToolCall(earlier, later) {
1695
+ const earlierFn = earlier.function;
1696
+ const laterFn = later.function;
1697
+ const earlierArgs = earlierFn.arguments;
1698
+ const laterArgs = laterFn.arguments;
1699
+ const mergedFn = {
1700
+ name: laterFn.name || earlierFn.name,
1701
+ arguments: earlierArgs !== void 0 && laterArgs !== void 0 ? { ...earlierArgs, ...laterArgs } : laterArgs ?? earlierArgs ?? {}
1702
+ };
1703
+ return {
1704
+ // id: prefer the later one if present, else the earlier one
1705
+ ...later.id !== void 0 ? { id: later.id } : earlier.id !== void 0 ? { id: earlier.id } : {},
1706
+ function: mergedFn
1707
+ };
1708
+ }
1709
+ function mergeToolCallArgumentsString(earlier, later) {
1710
+ const earlierArgs = earlier.function.arguments;
1711
+ const laterArgs = later.function.arguments;
1712
+ const concatenated = (typeof earlierArgs === "string" ? earlierArgs : "") + (typeof laterArgs === "string" ? laterArgs : "");
1713
+ return {
1714
+ ...later.id !== void 0 ? { id: later.id } : earlier.id !== void 0 ? { id: earlier.id } : {},
1715
+ function: {
1716
+ name: later.function.name || earlier.function.name,
1717
+ arguments: concatenated
1718
+ }
1719
+ };
1720
+ }
1721
+ var ToolCallAccumulator = class {
1722
+ entries = [];
1723
+ /**
1724
+ * Process a chunk's `tool_calls` array. Entries that match an
1725
+ * existing accumulated entry (by id or array position) are merged;
1726
+ * new entries are appended.
1727
+ *
1728
+ * Returns the current accumulated state (a shallow copy — entries
1729
+ * themselves are NOT deep-cloned, so callers that mutate
1730
+ * `function.arguments` on a returned entry will mutate the
1731
+ * accumulator's internal state. Use {@link snapshot} for an
1732
+ * explicitly read-only view, or deep-clone the result if you need
1733
+ * full isolation).
1734
+ */
1735
+ process(toolCalls) {
1736
+ if (!toolCalls || toolCalls.length === 0) {
1737
+ return [...this.entries];
1738
+ }
1739
+ for (let i = 0; i < toolCalls.length; i++) {
1740
+ const incoming = toolCalls[i];
1741
+ if (!incoming) continue;
1742
+ let matchedIndex = -1;
1743
+ for (let j = 0; j < this.entries.length; j++) {
1744
+ const existing = this.entries[j];
1745
+ if (existing && isSameToolCall(existing, incoming, j, i)) {
1746
+ matchedIndex = j;
1747
+ break;
1748
+ }
1749
+ }
1750
+ if (matchedIndex >= 0) {
1751
+ this.entries[matchedIndex] = mergeToolCall(this.entries[matchedIndex], incoming);
1752
+ } else {
1753
+ this.entries.push(incoming);
1754
+ }
1755
+ }
1756
+ return [...this.entries];
1757
+ }
1758
+ /**
1759
+ * Returns the final accumulated tool calls. Identical to calling
1760
+ * `process(undefined)` — included for ergonomics so callers can
1761
+ * express "I'm done, give me the result" without a no-op call.
1762
+ *
1763
+ * The returned array is a shallow copy; entries themselves are
1764
+ * the same object references the accumulator holds internally.
1765
+ */
1766
+ finalize() {
1767
+ return [...this.entries];
1768
+ }
1769
+ /**
1770
+ * Returns the current accumulated state without modifying the
1771
+ * accumulator. Useful for mid-stream inspection.
1772
+ *
1773
+ * The returned array is a shallow copy; mutating it (e.g.
1774
+ * `result.push(...)`) does not affect the accumulator, but
1775
+ * mutating an entry's `function.arguments` object DOES affect
1776
+ * the accumulator's internal state because the entry reference
1777
+ * is shared. Deep-clone the result if you need full isolation.
1778
+ */
1779
+ snapshot() {
1780
+ return [...this.entries];
1781
+ }
1782
+ /**
1783
+ * Returns the number of accumulated tool calls so far.
1784
+ */
1785
+ get length() {
1786
+ return this.entries.length;
1787
+ }
1788
+ };
1789
+ function mergeToolCallArrays(earlier, later) {
1790
+ const acc = new ToolCallAccumulator();
1791
+ acc.process(earlier);
1792
+ acc.process(later);
1793
+ return acc.finalize();
1794
+ }
1795
+
1413
1796
  // src/streaming/stream.ts
1797
+ function extractPartialContent(accumulated) {
1798
+ if (typeof accumulated !== "object" || accumulated === null) return "";
1799
+ const record = accumulated;
1800
+ const message = record.message;
1801
+ if (typeof message === "object" && message !== null) {
1802
+ const content = message.content;
1803
+ if (typeof content === "string") return content;
1804
+ }
1805
+ const response = record.response;
1806
+ if (typeof response === "string") return response;
1807
+ return "";
1808
+ }
1414
1809
  var OllamaStream = class {
1415
1810
  constructor(source, mapChunk, aggregate, initial, signal) {
1416
1811
  this.source = source;
@@ -1431,10 +1826,6 @@ var OllamaStream = class {
1431
1826
  }
1432
1827
  }
1433
1828
  }
1434
- source;
1435
- mapChunk;
1436
- aggregate;
1437
- initial;
1438
1829
  mode = "unconsumed";
1439
1830
  listeners = /* @__PURE__ */ new Map();
1440
1831
  finalResultPromise;
@@ -1493,7 +1884,10 @@ var OllamaStream = class {
1493
1884
  } catch (error) {
1494
1885
  this.removeAbortListener?.();
1495
1886
  this.removeAbortListener = void 0;
1496
- const mapped = mapError(error);
1887
+ const mapped = error instanceof OllamaStreamError && error.partialContent === "" ? new OllamaStreamError(error.message, {
1888
+ partialContent: extractPartialContent(accumulated),
1889
+ cause: error
1890
+ }) : mapError(error);
1497
1891
  this.emit({ type: "error", data: { error: mapped } });
1498
1892
  this.rejectFinal(mapped);
1499
1893
  }
@@ -1520,7 +1914,10 @@ var OllamaStream = class {
1520
1914
  }
1521
1915
  }
1522
1916
  } catch (error) {
1523
- const mapped = mapError(error);
1917
+ const mapped = error instanceof OllamaStreamError && error.partialContent === "" ? new OllamaStreamError(error.message, {
1918
+ partialContent: extractPartialContent(accumulated),
1919
+ cause: error
1920
+ }) : mapError(error);
1524
1921
  this.rejectFinal(mapped);
1525
1922
  yield { type: "error", data: { error: mapped } };
1526
1923
  } finally {
@@ -1542,7 +1939,17 @@ function aggregateChat(accumulated, chunk) {
1542
1939
  role: message.role || accumulated.message.role,
1543
1940
  content: accumulated.message.content + (message.content ?? ""),
1544
1941
  thinking: message.thinking !== void 0 ? (accumulated.message.thinking ?? "") + message.thinking : accumulated.message.thinking,
1545
- tool_calls: message.tool_calls?.length ? [...accumulated.message.tool_calls ?? [], ...message.tool_calls] : accumulated.message.tool_calls
1942
+ // Defensive tool-call merging: instead of blindly appending
1943
+ // every chunk's tool_calls array (which would produce
1944
+ // duplicates if Ollama ever starts streaming them
1945
+ // incrementally), merge entries that refer to the same logical
1946
+ // call (matched by id or array position). For the documented
1947
+ // native behavior (tool_calls arrive complete in one chunk)
1948
+ // this is a no-op — the accumulator appends exactly once.
1949
+ //
1950
+ // See src/tools/tool-call-accumulator.ts for the full merge
1951
+ // semantics and the rationale.
1952
+ tool_calls: message.tool_calls?.length || accumulated.message.tool_calls?.length ? mergeToolCallArrays(accumulated.message.tool_calls, message.tool_calls) : void 0
1546
1953
  },
1547
1954
  model: chunk.model,
1548
1955
  done: chunk.done,
@@ -1751,12 +2158,24 @@ function createTimeoutSignal(timeoutMs, userSignal) {
1751
2158
  return { signal: controller.signal, cancel };
1752
2159
  }
1753
2160
 
2161
+ // src/keep-alive.ts
2162
+ var KEEP_ALIVE_UNLOAD = 0;
2163
+ var KEEP_ALIVE_INDEFINITE = -1;
2164
+ function normalizeKeepAlive(keepAlive) {
2165
+ if (keepAlive === void 0) return void 0;
2166
+ if (keepAlive === "unload") return KEEP_ALIVE_UNLOAD;
2167
+ if (keepAlive === "indefinite") return KEEP_ALIVE_INDEFINITE;
2168
+ return keepAlive;
2169
+ }
2170
+ function isKeepAliveSugar(value) {
2171
+ return value === "unload" || value === "indefinite";
2172
+ }
2173
+
1754
2174
  // src/models-client.ts
1755
2175
  var ModelsClient = class {
1756
2176
  constructor(runner) {
1757
2177
  this.runner = runner;
1758
2178
  }
1759
- runner;
1760
2179
  list() {
1761
2180
  return this.runner((http) => listAvailableModels(http), { singleEndpoint: true });
1762
2181
  }
@@ -1863,6 +2282,76 @@ var ModelsClient = class {
1863
2282
  { singleEndpoint: true }
1864
2283
  );
1865
2284
  }
2285
+ /**
2286
+ * Immediately unloads `model` from VRAM/RAM by issuing an empty
2287
+ * `/api/generate` request with `keep_alive: 0`. The server
2288
+ * finalizes any in-flight generation, releases the model's
2289
+ * weights from GPU memory, and frees the slot for subsequent
2290
+ * pipelines (e.g. swap a vision encoder out before loading an
2291
+ * LLM).
2292
+ *
2293
+ * This is the ergonomic equivalent of:
2294
+ *
2295
+ * ```ts
2296
+ * client.generate({ model, prompt: '', keep_alive: 0 });
2297
+ * ```
2298
+ *
2299
+ * Use {@link pin} for the opposite lifecycle — pinning a model
2300
+ * indefinitely in VRAM for hot-loop inference.
2301
+ *
2302
+ * Resolves once the server acknowledges the unload. The promise
2303
+ * rejects on transport errors, model-not-found (404), or auth
2304
+ * failures — callers handling transient issues can retry via the
2305
+ * standard {@link withRetry} policy.
2306
+ *
2307
+ * See: https://github.com/ollama/ollama/blob/main/docs/faq.md
2308
+ * #how-do-i-keep-a-model-loaded-in-memory-or-make-it-unload-immediately
2309
+ */
2310
+ async unload(model) {
2311
+ await this.runner(
2312
+ (http, signal) => http.request({
2313
+ path: "/api/generate",
2314
+ body: { model, prompt: "", keep_alive: KEEP_ALIVE_UNLOAD, stream: false },
2315
+ signal
2316
+ }),
2317
+ { model, singleEndpoint: true }
2318
+ );
2319
+ }
2320
+ /**
2321
+ * Pre-loads and pins `model` into VRAM indefinitely by issuing an
2322
+ * empty `/api/generate` request with `keep_alive: -1`. The server
2323
+ * loads the model into GPU memory (paying the cold-load latency
2324
+ * once) and keeps it resident until either:
2325
+ *
2326
+ * - an explicit {@link unload} call is made,
2327
+ * - the server process is restarted, or
2328
+ * - the host runs out of VRAM and the server's own eviction
2329
+ * policy kicks in.
2330
+ *
2331
+ * This is the ergonomic equivalent of:
2332
+ *
2333
+ * ```ts
2334
+ * client.generate({ model, prompt: '', keep_alive: -1 });
2335
+ * ```
2336
+ *
2337
+ * Use {@link unload} to release the pinned slot when the hot loop
2338
+ * is done — leaving models pinned indefinitely exhausts VRAM and
2339
+ * starves subsequent pipelines.
2340
+ *
2341
+ * Resolves once the server acknowledges the pin (i.e. the model
2342
+ * is fully loaded). The promise rejects on transport errors,
2343
+ * model-not-found (404), or VRAM-exhaustion failures.
2344
+ */
2345
+ async pin(model) {
2346
+ await this.runner(
2347
+ (http, signal) => http.request({
2348
+ path: "/api/generate",
2349
+ body: { model, prompt: "", keep_alive: KEEP_ALIVE_INDEFINITE, stream: false },
2350
+ signal
2351
+ }),
2352
+ { model, singleEndpoint: true }
2353
+ );
2354
+ }
1866
2355
  async createBlob(digest, data) {
1867
2356
  await this.runner(
1868
2357
  (http, signal) => http.request({
@@ -1885,9 +2374,104 @@ var ModelsClient = class {
1885
2374
  { singleEndpoint: true }
1886
2375
  );
1887
2376
  return true;
1888
- } catch {
1889
- return false;
2377
+ } catch (err) {
2378
+ if (err instanceof OllamaNotFoundError) return false;
2379
+ throw err;
2380
+ }
2381
+ }
2382
+ /**
2383
+ * Computes a blob digest the way Ollama's content-addressed store expects:
2384
+ * SHA-256 over the raw bytes, formatted as `sha256:<64 lowercase hex>`.
2385
+ *
2386
+ * Uses the Web Crypto API (`crypto.subtle.digest`), which exists in every
2387
+ * supported runtime — Node.js ≥ 19 exposes it as a global (this SDK
2388
+ * requires Node ≥ 20), and browsers/edge runtimes have always had it.
2389
+ */
2390
+ async computeBlobDigest(data) {
2391
+ const subtle = globalThis.crypto?.subtle;
2392
+ if (subtle === void 0) {
2393
+ throw new OllamaClientError(
2394
+ "computeBlobDigest requires the Web Crypto API (crypto.subtle), unavailable in this runtime.",
2395
+ { code: "crypto_unavailable" }
2396
+ );
2397
+ }
2398
+ const digestBytes = await subtle.digest("SHA-256", data);
2399
+ const hex = Array.from(
2400
+ new Uint8Array(digestBytes),
2401
+ (byte) => byte.toString(16).padStart(2, "0")
2402
+ ).join("");
2403
+ return `sha256:${hex}`;
2404
+ }
2405
+ /**
2406
+ * Computes the SHA-256 digest of `data` and pushes it as a blob in one
2407
+ * step — the content-addressed upload Ollama's own tooling performs
2408
+ * (`curl -T file http://…/api/blobs/sha256:…`). Returns the digest, which
2409
+ * `/api/create`'s `files` field references.
2410
+ *
2411
+ * Pass `digest` to skip recomputation when the caller already knows it —
2412
+ * the server verifies the content against the digest and rejects mismatches.
2413
+ */
2414
+ async createBlobFromData(data, opts) {
2415
+ const digest = opts?.digest ?? await this.computeBlobDigest(data);
2416
+ const alreadyExisted = await this.checkBlob(digest);
2417
+ if (!alreadyExisted) {
2418
+ await this.createBlob(digest, data);
2419
+ }
2420
+ return { digest, alreadyExisted };
2421
+ }
2422
+ /**
2423
+ * Reads a local file and uploads it as a blob (Node.js only — dynamic
2424
+ * `node:fs` import, so browser bundles never pull it in). The file name
2425
+ * (basename) is what `/api/create`'s `files` map expects as the key, so
2426
+ * this returns it alongside the digest.
2427
+ */
2428
+ async createBlobFromFile(path, opts) {
2429
+ const nodeFsModule = "node:fs";
2430
+ const { promises: fs } = await import(nodeFsModule);
2431
+ const data = await fs.readFile(path);
2432
+ const result = await this.createBlobFromData(new Uint8Array(data), {
2433
+ ...opts
2434
+ });
2435
+ const fileName = path.split(/[\\/]/).pop() ?? path;
2436
+ return { ...result, fileName };
2437
+ }
2438
+ /**
2439
+ * One-shot custom-model publishing from raw GGUF weights, following the
2440
+ * protocol Ollama's API reference documents: push a blob for the GGUF
2441
+ * bytes, then `POST /api/create` with `files: { <fileName>: <digest> }`.
2442
+ * Split GGUFs are supported via `files` — pass every shard's path.
2443
+ *
2444
+ * ```ts
2445
+ * await client.models.createModelFromGguf('my-model', '/path/to/model.gguf', {
2446
+ * template: '{{ .Prompt }}',
2447
+ * });
2448
+ * ```
2449
+ *
2450
+ * @param model Name for the new model (e.g. `'my-model:latest'`).
2451
+ * @param gguf Path(s) to GGUF file(s) on the local filesystem.
2452
+ * @param opts Everything else `/api/create` accepts (`template`, `system`,
2453
+ * `parameters`, `license`, `quantize`, …) except `files` and `from`.
2454
+ */
2455
+ async createModelFromGguf(model, gguf, opts) {
2456
+ const paths = typeof gguf === "string" ? [gguf] : gguf;
2457
+ if (paths.length === 0) {
2458
+ throw new OllamaClientError("createModelFromGguf: at least one GGUF path is required.", {
2459
+ code: "invalid_request"
2460
+ });
1890
2461
  }
2462
+ const files = {};
2463
+ for (const path of paths) {
2464
+ const uploaded = await this.createBlobFromFile(path, { signal: opts?.signal });
2465
+ files[uploaded.fileName] = uploaded.digest;
2466
+ }
2467
+ const {
2468
+ model: _model,
2469
+ files: _files,
2470
+ from: _from,
2471
+ stream: _stream,
2472
+ ...rest
2473
+ } = opts ?? {};
2474
+ return this.create({ ...rest, model, files, stream: false });
1891
2475
  }
1892
2476
  };
1893
2477
 
@@ -1915,7 +2499,6 @@ var OpenAIChatCompletionStream = class {
1915
2499
  }
1916
2500
  }
1917
2501
  }
1918
- source;
1919
2502
  finalResultPromise;
1920
2503
  resolveFinal;
1921
2504
  rejectFinal;
@@ -2059,7 +2642,6 @@ var OpenAICompletionStream = class {
2059
2642
  }
2060
2643
  }
2061
2644
  }
2062
- source;
2063
2645
  finalResultPromise;
2064
2646
  resolveFinal;
2065
2647
  rejectFinal;
@@ -2140,7 +2722,6 @@ var OpenAIResponsesStream = class {
2140
2722
  }
2141
2723
  }
2142
2724
  }
2143
- source;
2144
2725
  finalResultPromise;
2145
2726
  resolveFinal;
2146
2727
  rejectFinal;
@@ -2365,8 +2946,6 @@ var OpenAICompatClient = class {
2365
2946
  this.http = http;
2366
2947
  this.runner = runner;
2367
2948
  }
2368
- http;
2369
- runner;
2370
2949
  request(operation, model, signal, holdUntil) {
2371
2950
  if (this.runner) {
2372
2951
  return this.runner(
@@ -2586,7 +3165,6 @@ var AnthropicMessagesStream = class {
2586
3165
  }
2587
3166
  }
2588
3167
  }
2589
- source;
2590
3168
  finalResultPromise;
2591
3169
  resolveFinal;
2592
3170
  rejectFinal;
@@ -2701,8 +3279,6 @@ var AnthropicCompatClient = class {
2701
3279
  this.http = http;
2702
3280
  this.runner = runner;
2703
3281
  }
2704
- http;
2705
- runner;
2706
3282
  request(operation, model, signal, holdUntil) {
2707
3283
  if (this.runner) {
2708
3284
  return this.runner(
@@ -2748,6 +3324,32 @@ var AnthropicCompatClient = class {
2748
3324
  return this.createMessage(request, signal);
2749
3325
  }
2750
3326
  };
3327
+ var IncludedBalanceSchema = z.object({
3328
+ balance_usd: z.number(),
3329
+ allowance_usd: z.number(),
3330
+ period: z.object({
3331
+ from: z.string(),
3332
+ until: z.string()
3333
+ })
3334
+ });
3335
+ var LegacyBalanceLimitSchema = z.object({
3336
+ remaining_percent: z.number().min(0).max(100),
3337
+ resets_at: z.string()
3338
+ });
3339
+
3340
+ // src/generated/models/LegacyIncludedBalance.schema.ts
3341
+ var LegacyIncludedBalanceSchema = z.object({
3342
+ session: LegacyBalanceLimitSchema,
3343
+ weekly: LegacyBalanceLimitSchema
3344
+ });
3345
+
3346
+ // src/generated/models/BalanceResponse.schema.ts
3347
+ var BalanceResponseSchema = z.object({
3348
+ included: z.union([IncludedBalanceSchema, LegacyIncludedBalanceSchema]),
3349
+ purchased: z.object({
3350
+ balance_usd: z.number()
3351
+ })
3352
+ });
2751
3353
  var ToolCallSchema = z.object({
2752
3354
  function: z.object({
2753
3355
  name: z.string(),
@@ -2796,28 +3398,45 @@ var ChatRequestSchema = z.object({
2796
3398
  logprobs: z.boolean().optional(),
2797
3399
  top_logprobs: z.number().optional()
2798
3400
  });
2799
- var GenerateRequestSchema = z.object({
2800
- model: z.string(),
2801
- prompt: z.string().optional(),
2802
- suffix: z.string().optional(),
2803
- images: z.array(z.string()).optional(),
2804
- format: z.union([z.string(), z.object({})]).optional(),
2805
- system: z.string().optional(),
2806
- stream: z.boolean().optional(),
2807
- think: ThinkValueSchema.optional(),
2808
- raw: z.boolean().optional(),
2809
- keep_alive: z.union([z.string(), z.number()]).optional(),
2810
- options: ModelOptionsSchema.optional(),
2811
- logprobs: z.boolean().optional(),
2812
- top_logprobs: z.number().optional()
3401
+ var TokenLogprobSchema = z.object({
3402
+ token: z.string().optional(),
3403
+ logprob: z.number().optional(),
3404
+ bytes: z.array(z.number()).optional()
2813
3405
  });
2814
- var EmbedRequestSchema = z.object({
2815
- model: z.string(),
2816
- input: z.union([z.string(), z.array(z.string())]),
2817
- truncate: z.boolean().optional(),
2818
- dimensions: z.number().optional(),
2819
- keep_alive: z.string().optional(),
2820
- options: ModelOptionsSchema.optional()
3406
+
3407
+ // src/generated/models/Logprob.schema.ts
3408
+ var LogprobSchema = z.object({
3409
+ token: z.string().optional(),
3410
+ logprob: z.number().optional(),
3411
+ bytes: z.array(z.number()).optional(),
3412
+ top_logprobs: z.array(TokenLogprobSchema).optional()
3413
+ });
3414
+
3415
+ // src/generated/models/ChatResponse.schema.ts
3416
+ var ChatResponseSchema = z.object({
3417
+ model: z.string().optional(),
3418
+ created_at: z.string().optional(),
3419
+ message: z.object({
3420
+ role: z.enum(["assistant"]).optional(),
3421
+ content: z.string().optional(),
3422
+ thinking: z.string().optional(),
3423
+ tool_calls: z.array(ToolCallSchema).optional(),
3424
+ images: z.array(z.string()).optional()
3425
+ }).optional(),
3426
+ done: z.boolean().optional(),
3427
+ done_reason: z.string().optional(),
3428
+ total_duration: z.number().optional(),
3429
+ load_duration: z.number().optional(),
3430
+ prompt_eval_count: z.number().optional(),
3431
+ prompt_eval_cached_count: z.number().optional(),
3432
+ prompt_eval_duration: z.number().optional(),
3433
+ eval_count: z.number().optional(),
3434
+ eval_duration: z.number().optional(),
3435
+ logprobs: z.array(LogprobSchema).optional()
3436
+ });
3437
+ var CopyRequestSchema = z.object({
3438
+ source: z.string(),
3439
+ destination: z.string()
2821
3440
  });
2822
3441
  var CreateRequestSchema = z.object({
2823
3442
  model: z.string(),
@@ -2834,50 +3453,296 @@ var CreateRequestSchema = z.object({
2834
3453
  quantize: z.string().optional(),
2835
3454
  draft_quantize: z.string().optional(),
2836
3455
  requires: z.string().optional(),
3456
+ capabilities: z.array(z.string()).optional(),
2837
3457
  stream: z.boolean().optional()
2838
3458
  });
2839
- var CopyRequestSchema = z.object({
2840
- source: z.string(),
2841
- destination: z.string()
2842
- });
2843
3459
  var DeleteRequestSchema = z.object({
2844
3460
  model: z.string()
2845
3461
  });
2846
- var PullRequestSchema = z.object({
3462
+ var EmbedRequestSchema = z.object({
2847
3463
  model: z.string(),
2848
- insecure: z.boolean().optional(),
2849
- stream: z.boolean().optional()
3464
+ input: z.union([z.string(), z.array(z.string())]),
3465
+ truncate: z.boolean().optional(),
3466
+ dimensions: z.number().optional(),
3467
+ keep_alive: z.string().optional(),
3468
+ options: ModelOptionsSchema.optional()
2850
3469
  });
2851
- var PushRequestSchema = z.object({
2852
- model: z.string(),
2853
- insecure: z.boolean().optional(),
2854
- stream: z.boolean().optional()
3470
+ var EmbedResponseSchema = z.object({
3471
+ model: z.string().optional(),
3472
+ embeddings: z.array(z.array(z.number())).optional(),
3473
+ total_duration: z.number().optional(),
3474
+ load_duration: z.number().optional(),
3475
+ prompt_eval_count: z.number().optional()
2855
3476
  });
2856
- var ShowRequestSchema = z.object({
3477
+ var GenerateRequestSchema = z.object({
2857
3478
  model: z.string(),
2858
- verbose: z.boolean().optional()
3479
+ prompt: z.string().optional(),
3480
+ suffix: z.string().optional(),
3481
+ images: z.array(z.string()).optional(),
3482
+ format: z.union([z.string(), z.object({})]).optional(),
3483
+ system: z.string().optional(),
3484
+ stream: z.boolean().optional(),
3485
+ think: ThinkValueSchema.optional(),
3486
+ raw: z.boolean().optional(),
3487
+ keep_alive: z.union([z.string(), z.number()]).optional(),
3488
+ options: ModelOptionsSchema.optional(),
3489
+ logprobs: z.boolean().optional(),
3490
+ top_logprobs: z.number().optional()
2859
3491
  });
2860
-
3492
+ var GenerateResponseSchema = z.object({
3493
+ model: z.string().optional(),
3494
+ created_at: z.string().optional(),
3495
+ response: z.string().optional(),
3496
+ thinking: z.string().optional(),
3497
+ done: z.boolean().optional(),
3498
+ done_reason: z.string().optional(),
3499
+ total_duration: z.number().optional(),
3500
+ load_duration: z.number().optional(),
3501
+ prompt_eval_count: z.number().optional(),
3502
+ prompt_eval_cached_count: z.number().optional(),
3503
+ prompt_eval_duration: z.number().optional(),
3504
+ eval_count: z.number().optional(),
3505
+ eval_duration: z.number().optional(),
3506
+ logprobs: z.array(LogprobSchema).optional()
3507
+ });
3508
+ var ModelSummarySchema = z.object({
3509
+ name: z.string().optional(),
3510
+ model: z.string().optional(),
3511
+ remote_model: z.string().optional(),
3512
+ remote_host: z.string().optional(),
3513
+ modified_at: z.string().optional(),
3514
+ size: z.number().optional(),
3515
+ digest: z.string().optional(),
3516
+ details: z.object({
3517
+ format: z.string().optional(),
3518
+ family: z.string().optional(),
3519
+ families: z.union([z.array(z.string()), z.null()]).optional(),
3520
+ parameter_size: z.string().optional(),
3521
+ quantization_level: z.string().optional()
3522
+ }).optional()
3523
+ });
3524
+
3525
+ // src/generated/models/ListResponse.schema.ts
3526
+ var ListResponseSchema = z.object({
3527
+ models: z.array(ModelSummarySchema).optional()
3528
+ });
3529
+ var PsSchema = z.object({
3530
+ name: z.string().optional(),
3531
+ model: z.string().optional(),
3532
+ size: z.number().optional(),
3533
+ digest: z.string().optional(),
3534
+ details: z.object({}).optional(),
3535
+ expires_at: z.string().optional(),
3536
+ size_vram: z.number().optional(),
3537
+ context_length: z.number().optional()
3538
+ });
3539
+
3540
+ // src/generated/models/PsResponse.schema.ts
3541
+ var PsResponseSchema = z.object({
3542
+ models: z.array(PsSchema).optional()
3543
+ });
3544
+ var PullRequestSchema = z.object({
3545
+ model: z.string(),
3546
+ insecure: z.boolean().optional(),
3547
+ stream: z.boolean().optional()
3548
+ });
3549
+ var PushRequestSchema = z.object({
3550
+ model: z.string(),
3551
+ insecure: z.boolean().optional(),
3552
+ stream: z.boolean().optional()
3553
+ });
3554
+ var ShowRequestSchema = z.object({
3555
+ model: z.string(),
3556
+ verbose: z.boolean().optional()
3557
+ });
3558
+ var ThinkingSchema = z.object({
3559
+ values: z.array(z.union([z.boolean(), z.string()])),
3560
+ default: z.union([z.boolean(), z.string()])
3561
+ });
3562
+
3563
+ // src/generated/models/ShowResponse.schema.ts
3564
+ var ShowResponseSchema = z.object({
3565
+ thinking: ThinkingSchema.optional(),
3566
+ parameters: z.string().optional(),
3567
+ license: z.string().optional(),
3568
+ modified_at: z.string().optional(),
3569
+ details: z.object({}).optional(),
3570
+ template: z.string().optional(),
3571
+ capabilities: z.array(z.string()).optional(),
3572
+ model_info: z.object({}).optional()
3573
+ });
3574
+ var StatusResponseSchema = z.object({
3575
+ status: z.string().optional()
3576
+ });
3577
+ var SystemOneContentSchema = z.union([
3578
+ z.string().regex(new RegExp("\\S")),
3579
+ z.object({}).catchall(z.unknown()),
3580
+ z.array(z.unknown())
3581
+ ]);
3582
+
3583
+ // src/generated/models/SystemOneChoiceQuestion.schema.ts
3584
+ var SystemOneChoiceQuestionSchema = z.object({
3585
+ type: z.enum(["choice"]),
3586
+ instructions: SystemOneContentSchema,
3587
+ criteria: z.object({}).catchall(z.union([z.string(), z.null()])).refine((v) => Object.keys(v).length >= 2, { message: "must have at least 2 properties" }).refine((v) => Object.keys(v).length <= 26, { message: "must have at most 26 properties" }).refine((v) => Object.keys(v).every((k) => new RegExp("\\S").test(k)), {
3588
+ message: 'property names must match pattern "\\S"'
3589
+ })
3590
+ });
3591
+ var SystemOneNoulQuestionSchema = z.object({
3592
+ type: z.enum(["noul"]),
3593
+ instructions: SystemOneContentSchema,
3594
+ criteria: z.object({
3595
+ false: z.string().optional(),
3596
+ true: z.string().optional()
3597
+ }).optional()
3598
+ });
3599
+ var SystemOneScoreQuestionSchema = z.object({
3600
+ type: z.enum(["score"]),
3601
+ instructions: SystemOneContentSchema,
3602
+ criteria: z.array(z.string()).min(2).max(26)
3603
+ });
3604
+
3605
+ // src/generated/models/SystemOneRequest.schema.ts
3606
+ var SystemOneRequestSchema = z.object({
3607
+ model: z.string().regex(new RegExp("\\S")),
3608
+ state: SystemOneContentSchema,
3609
+ images: z.array(z.string()).optional(),
3610
+ questions: z.object({}).catchall(
3611
+ z.union([
3612
+ SystemOneChoiceQuestionSchema,
3613
+ SystemOneNoulQuestionSchema,
3614
+ SystemOneScoreQuestionSchema
3615
+ ])
3616
+ ).refine((v) => Object.keys(v).length >= 1, { message: "must have at least 1 properties" }).refine((v) => Object.keys(v).length <= 64, { message: "must have at most 64 properties" }).refine((v) => Object.keys(v).every((k) => new RegExp("\\S").test(k)), {
3617
+ message: 'property names must match pattern "\\S"'
3618
+ }),
3619
+ keep_alive: z.union([z.string(), z.number()]).optional()
3620
+ });
3621
+ var SystemOneConfidenceSchema = z.number().min(0).max(1);
3622
+ var SystemOneProbabilitiesSchema = z.object({}).catchall(z.number().min(0).max(1));
3623
+
3624
+ // src/generated/models/SystemOneChoiceAnswer.schema.ts
3625
+ var SystemOneChoiceAnswerSchema = z.object({
3626
+ type: z.enum(["choice"]),
3627
+ choice: z.string(),
3628
+ probabilities: SystemOneProbabilitiesSchema,
3629
+ confidence: SystemOneConfidenceSchema
3630
+ });
3631
+ var SystemOneNoulAnswerSchema = z.object({
3632
+ type: z.enum(["noul"]),
3633
+ noul: z.number().min(0).max(1)
3634
+ });
3635
+ var SystemOneScoreAnswerSchema = z.object({
3636
+ type: z.enum(["score"]),
3637
+ score: z.number().min(0).max(25),
3638
+ legend: z.object({}).catchall(z.string()),
3639
+ probabilities: SystemOneProbabilitiesSchema,
3640
+ confidence: SystemOneConfidenceSchema
3641
+ });
3642
+
3643
+ // src/generated/models/SystemOneResponse.schema.ts
3644
+ var SystemOneResponseSchema = z.object({
3645
+ model: z.string(),
3646
+ answers: z.object({}).catchall(
3647
+ z.union([SystemOneChoiceAnswerSchema, SystemOneNoulAnswerSchema, SystemOneScoreAnswerSchema])
3648
+ ),
3649
+ usage: z.object({
3650
+ input_tokens: z.number().min(0),
3651
+ output_tokens: z.number().min(0)
3652
+ })
3653
+ });
3654
+ var UsageMetricsSchema = z.object({
3655
+ request_count: z.number().min(0),
3656
+ usage_usd: z.number().optional(),
3657
+ input_tokens: z.number().optional(),
3658
+ cached_input_tokens: z.number().optional(),
3659
+ output_tokens: z.number().optional()
3660
+ });
3661
+
3662
+ // src/generated/models/UsageBucket.schema.ts
3663
+ var UsageBucketSchema = z.intersection(
3664
+ UsageMetricsSchema,
3665
+ z.object({
3666
+ from: z.string(),
3667
+ until: z.string(),
3668
+ partial: z.boolean().optional()
3669
+ })
3670
+ );
3671
+
3672
+ // src/generated/models/UsageResponse.schema.ts
3673
+ var UsageResponseSchema = z.object({
3674
+ range: z.enum(["24h", "7d", "30d"]),
3675
+ scope: z.enum(["self", "team"]),
3676
+ granularity: z.enum(["hour", "day"]),
3677
+ from: z.string(),
3678
+ until: z.string(),
3679
+ totals: UsageMetricsSchema,
3680
+ buckets: z.array(UsageBucketSchema)
3681
+ });
3682
+ var VersionResponseSchema = z.object({
3683
+ version: z.string().optional()
3684
+ });
3685
+ var WebFetchRequestSchema = z.object({
3686
+ url: z.string()
3687
+ });
3688
+ var WebFetchResponseSchema = z.object({
3689
+ title: z.string().optional(),
3690
+ content: z.string().optional(),
3691
+ links: z.array(z.string()).optional()
3692
+ });
3693
+ var WebSearchRequestSchema = z.object({
3694
+ query: z.string(),
3695
+ max_results: z.number().min(1).max(10).optional()
3696
+ });
3697
+ var WebSearchResultSchema = z.object({
3698
+ title: z.string().optional(),
3699
+ url: z.string().optional(),
3700
+ content: z.string().optional()
3701
+ });
3702
+
3703
+ // src/generated/models/WebSearchResponse.schema.ts
3704
+ var WebSearchResponseSchema = z.object({
3705
+ results: z.array(WebSearchResultSchema).optional()
3706
+ });
3707
+
2861
3708
  // src/generated/runtime/schema-registry.ts
2862
3709
  var requestSchemas = {
2863
3710
  chat: ChatRequestSchema,
2864
- generate: GenerateRequestSchema,
2865
- embed: EmbedRequestSchema,
2866
- create: CreateRequestSchema,
2867
3711
  copy: CopyRequestSchema,
3712
+ create: CreateRequestSchema,
2868
3713
  delete: DeleteRequestSchema,
3714
+ embed: EmbedRequestSchema,
3715
+ generate: GenerateRequestSchema,
2869
3716
  pull: PullRequestSchema,
2870
3717
  push: PushRequestSchema,
2871
- show: ShowRequestSchema
2872
- // Operations without a registered request schema (blobs, ps, tags,
2873
- // version, systemOne, openai*, anthropic*) skip validation. This is
2874
- // deliberate — the OpenAPI spec doesn't define request bodies for
2875
- // GET/HEAD operations, and the compat-surface request schemas (OpenAI,
2876
- // Anthropic) are richer than what the IR currently models.
3718
+ show: ShowRequestSchema,
3719
+ systemOne: SystemOneRequestSchema,
3720
+ webFetch: WebFetchRequestSchema,
3721
+ webSearch: WebSearchRequestSchema
3722
+ };
3723
+ var responseSchemas = {
3724
+ balance: BalanceResponseSchema,
3725
+ chat: ChatResponseSchema,
3726
+ create: StatusResponseSchema,
3727
+ embed: EmbedResponseSchema,
3728
+ generate: GenerateResponseSchema,
3729
+ ps: PsResponseSchema,
3730
+ pull: StatusResponseSchema,
3731
+ push: StatusResponseSchema,
3732
+ show: ShowResponseSchema,
3733
+ systemOne: SystemOneResponseSchema,
3734
+ tags: ListResponseSchema,
3735
+ usage: UsageResponseSchema,
3736
+ version: VersionResponseSchema,
3737
+ webFetch: WebFetchResponseSchema,
3738
+ webSearch: WebSearchResponseSchema
2877
3739
  };
2878
3740
  function getRequestSchema(operationId) {
2879
3741
  return requestSchemas[operationId];
2880
3742
  }
3743
+ function getResponseSchema(operationId) {
3744
+ return responseSchemas[operationId];
3745
+ }
2881
3746
 
2882
3747
  // src/generated/runtime/runtime.ts
2883
3748
  function compareVersions(a, b) {
@@ -2890,18 +3755,13 @@ function compareVersions(a, b) {
2890
3755
  }
2891
3756
  return 0;
2892
3757
  }
2893
- function assertOperationAllowed(operation, options) {
2894
- const localMode = options.localMode ?? true;
2895
- if (!localMode && !operation.environment.cloud) {
2896
- throw new OllamaGenericClientError(
2897
- `Operation ${operation.operationId} (${operation.method} ${operation.path}) is local-only and not supported in cloud mode.`
2898
- );
2899
- }
2900
- if (operation.constraints?.minOllamaVersion && options.serverVersion && compareVersions(options.serverVersion, operation.constraints.minOllamaVersion) < 0) {
2901
- throw new OllamaGenericClientError(
2902
- `Operation ${operation.operationId} (${operation.method} ${operation.path}) requires Ollama >= ${operation.constraints.minOllamaVersion} (server reports ${options.serverVersion}).`
2903
- );
2904
- }
3758
+ async function fetchServerVersion(http) {
3759
+ const result = await http.request({
3760
+ path: "/api/version",
3761
+ method: "GET"
3762
+ });
3763
+ if (result && typeof result.version === "string") return result.version;
3764
+ return void 0;
2905
3765
  }
2906
3766
  function shouldStream(req) {
2907
3767
  if (req.body && typeof req.body === "object" && "stream" in req.body) {
@@ -2923,7 +3783,35 @@ var OllamaRuntime = class {
2923
3783
  constructor(options) {
2924
3784
  this.options = options;
2925
3785
  }
2926
- options;
3786
+ /**
3787
+ * Cached promise of the Ollama server version, populated lazily the first
3788
+ * time a version-gated operation is invoked. `undefined` until then.
3789
+ *
3790
+ * Wave 12 (P0 #5): see {@link OllamaRuntimeOptions.enforceVersion}.
3791
+ */
3792
+ serverVersionCache;
3793
+ /**
3794
+ * Wave 15 (P0): resolve the HTTP backend for a given operation.
3795
+ *
3796
+ * Operations with a declared `host` (e.g. web search/fetch at
3797
+ * https://ollama.com) must route to a HttpClient pointed at that host,
3798
+ * not the default one. The runtime accepts an optional `cloudHttp`
3799
+ * backend for this purpose — if the operation declares a host and no
3800
+ * cloud backend is configured, the runtime throws rather than silently
3801
+ * sending the request to the wrong server.
3802
+ */
3803
+ resolveHttpBackend(operation) {
3804
+ if (operation.host) {
3805
+ const cloudHttp = this.options.cloudHttp;
3806
+ if (!cloudHttp) {
3807
+ throw new OllamaGenericClientError(
3808
+ `Operation ${operation.operationId} (${operation.method} ${operation.path}) targets host "${operation.host}" but the runtime has no cloudHttp backend configured. Use OllamaClient.webSearch() / OllamaClient.webFetch() for cloud-hosted operations, or construct OllamaRuntime with a cloudHttp option.`
3809
+ );
3810
+ }
3811
+ return cloudHttp;
3812
+ }
3813
+ return this.options.http;
3814
+ }
2927
3815
  /**
2928
3816
  * Invoke an operation. Called by every generated API method.
2929
3817
  *
@@ -2932,7 +3820,7 @@ var OllamaRuntime = class {
2932
3820
  * class's overloads pick the right return type at compile time.
2933
3821
  */
2934
3822
  async invoke(req) {
2935
- assertOperationAllowed(req.operation, this.options);
3823
+ await this.assertOperationAllowed(req.operation);
2936
3824
  let body = buildBody(req);
2937
3825
  if (this.options.validateRequests === true && body !== void 0) {
2938
3826
  const schema = getRequestSchema(req.operation.operationId);
@@ -2951,32 +3839,1133 @@ var OllamaRuntime = class {
2951
3839
  body = result.data;
2952
3840
  }
2953
3841
  }
3842
+ if (body !== void 0) {
3843
+ const serialized = serializeForByteCount(body);
3844
+ const hasImages = bodyHasImages(body);
3845
+ const maxBytesWithImages = req.operation.constraints?.maxRequestBytesWithImages;
3846
+ const maxBytesBase = req.operation.constraints?.maxRequestBytes;
3847
+ const applicableLimit = hasImages && maxBytesWithImages !== void 0 ? maxBytesWithImages : maxBytesBase;
3848
+ if (applicableLimit !== void 0 && serialized.byteLength > applicableLimit) {
3849
+ throw new OllamaRequestTooLargeError(
3850
+ `Operation ${req.operation.operationId} (${req.operation.method} ${req.operation.path}) request body is ${serialized.byteLength} bytes, exceeding the contract limit of ${applicableLimit} bytes` + (hasImages ? " (images limit)." : "."),
3851
+ {
3852
+ operationId: req.operation.operationId,
3853
+ actualBytes: serialized.byteLength,
3854
+ maxBytes: applicableLimit,
3855
+ request: { method: req.operation.method, url: req.operation.path }
3856
+ }
3857
+ );
3858
+ }
3859
+ }
2954
3860
  const streaming = shouldStream(req);
3861
+ const resolvedPath = resolvePathParams(req.operation.path, req.pathParams);
3862
+ const pathWithQuery = applyQueryParams(resolvedPath, req.queryParams);
3863
+ const model = req.model ?? extractModelFromBody(body);
3864
+ const headers = {};
3865
+ if (req.headerParams) {
3866
+ for (const [key, value] of Object.entries(req.headerParams)) {
3867
+ headers[key] = value;
3868
+ }
3869
+ }
2955
3870
  const httpReq = {
2956
- path: req.operation.path,
3871
+ path: pathWithQuery,
2957
3872
  method: req.operation.method,
2958
3873
  ...body !== void 0 ? { body } : {},
3874
+ ...Object.keys(headers).length > 0 ? { headers } : {},
2959
3875
  ...req.signal !== void 0 ? { signal: req.signal } : {},
2960
- // For streaming calls, request the raw Response so we can pipe it
2961
- // through parseNdjsonStream directly. This inherits middleware,
2962
- // retry, telemetry, and error-mapping from HttpClient — the
2963
- // previous Wave 3 implementation bypassed them with a direct
2964
- // `fetch()` call.
2965
- ...streaming ? { raw: true } : {}
3876
+ ...streaming ? { raw: true } : {},
3877
+ ...model !== void 0 ? { model } : {}
2966
3878
  };
2967
3879
  if (!streaming) {
2968
- return await this.options.http.request(httpReq);
3880
+ const result = await this.resolveHttpBackend(req.operation).request(httpReq);
3881
+ if (this.options.validateResponses === true) {
3882
+ const responseSchema = getResponseSchema(req.operation.operationId);
3883
+ if (responseSchema) {
3884
+ const result2 = responseSchema.safeParse(result);
3885
+ if (!result2.success) {
3886
+ throw new OllamaResponseValidationError(
3887
+ `Response validation failed for operation "${req.operation.operationId}" (${req.operation.method} ${req.operation.path}): ` + result2.error.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`).join("; "),
3888
+ {
3889
+ operationId: req.operation.operationId,
3890
+ issues: result2.error.issues,
3891
+ request: { method: req.operation.method, url: req.operation.path }
3892
+ }
3893
+ );
3894
+ }
3895
+ }
3896
+ }
3897
+ return result;
2969
3898
  }
2970
- const response = await this.options.http.request(httpReq);
3899
+ const response = await this.resolveHttpBackend(req.operation).request(httpReq);
2971
3900
  if (!response.body) {
2972
3901
  throw new OllamaGenericClientError(
2973
3902
  `Operation ${req.operation.operationId}: streaming response had no body.`
2974
3903
  );
2975
3904
  }
2976
- return parseNdjsonStream(response.body);
3905
+ return parseStreamByMode(req.operation.transport.mode, response.body);
3906
+ }
3907
+ /**
3908
+ * Apply environment + version guards before delegating to HttpClient.
3909
+ *
3910
+ * Throws {@link OllamaGenericClientError} when:
3911
+ * - The operation is local-only and the runtime is in cloud mode.
3912
+ * - The operation has `constraints.minOllamaVersion` and the server is older.
3913
+ *
3914
+ * Wave 12 (P0 #5): when `minOllamaVersion` is declared but the runtime's
3915
+ * `serverVersion` is unknown AND `enforceVersion` is `'auto'` or
3916
+ * `'strict'`, the runtime lazily fetches `/api/version` (cached on the
3917
+ * runtime instance) so the constraint is actually enforced even when the
3918
+ * caller didn't supply it. Previously the constraint was only enforced
3919
+ * when `serverVersion` was manually supplied — meaning in practice it was
3920
+ * almost never enforced.
3921
+ */
3922
+ async assertOperationAllowed(operation) {
3923
+ const localMode = this.options.localMode ?? true;
3924
+ if (!localMode && !operation.environment.cloud) {
3925
+ throw new OllamaGenericClientError(
3926
+ `Operation ${operation.operationId} (${operation.method} ${operation.path}) is local-only and not supported in cloud mode.`
3927
+ );
3928
+ }
3929
+ const minVersion = operation.constraints?.minOllamaVersion;
3930
+ if (!minVersion) return;
3931
+ let serverVersion = this.options.serverVersion;
3932
+ const policy = this.options.enforceVersion ?? "auto";
3933
+ if (!serverVersion && policy !== "off") {
3934
+ if (!this.serverVersionCache) {
3935
+ const p = fetchServerVersion(this.options.http).catch((err) => {
3936
+ if (policy === "strict") {
3937
+ throw new OllamaServerVersionUnknownError(
3938
+ `Operation ${operation.operationId} requires Ollama >= ${minVersion} but the runtime could not determine the server version (enforceVersion: 'strict').`,
3939
+ {
3940
+ operationId: operation.operationId,
3941
+ minRequiredVersion: minVersion,
3942
+ cause: err
3943
+ }
3944
+ );
3945
+ }
3946
+ return void 0;
3947
+ });
3948
+ this.serverVersionCache = p;
3949
+ }
3950
+ serverVersion = await this.serverVersionCache ?? void 0;
3951
+ }
3952
+ if (serverVersion && compareVersions(serverVersion, minVersion) < 0) {
3953
+ throw new OllamaGenericClientError(
3954
+ `Operation ${operation.operationId} (${operation.method} ${operation.path}) requires Ollama >= ${minVersion} (server reports ${serverVersion}).`
3955
+ );
3956
+ }
3957
+ }
3958
+ };
3959
+ function parseStreamByMode(mode, body) {
3960
+ if (mode === "ndjson") {
3961
+ return parseNdjsonStream(body);
3962
+ }
3963
+ if (mode === "sse") {
3964
+ return parseSseStreamAsJson(body);
3965
+ }
3966
+ throw new OllamaGenericClientError(
3967
+ `Transport mode "${mode}" is not a streaming mode; cannot parse a stream for it.`
3968
+ );
3969
+ }
3970
+ async function* parseSseStreamAsJson(body) {
3971
+ for await (const event of parseSseStream(body)) {
3972
+ const data = event.data;
3973
+ if (!data) continue;
3974
+ if (data === "[DONE]") return;
3975
+ try {
3976
+ yield JSON.parse(data);
3977
+ } catch (err) {
3978
+ throw new OllamaGenericClientError(`Failed to parse SSE event data as JSON: ${data}`, {
3979
+ cause: err
3980
+ });
3981
+ }
3982
+ }
3983
+ }
3984
+ function serializeForByteCount(body) {
3985
+ if (body instanceof Uint8Array) return body;
3986
+ if (typeof body === "string") return new TextEncoder().encode(body);
3987
+ const json = JSON.stringify(body);
3988
+ return new TextEncoder().encode(json);
3989
+ }
3990
+ function bodyHasImages(body) {
3991
+ if (typeof body !== "object" || body === null) return false;
3992
+ const obj = body;
3993
+ const images = obj.images;
3994
+ return Array.isArray(images) && images.length > 0;
3995
+ }
3996
+ function resolvePathParams(pathTemplate, pathParams) {
3997
+ if (!pathTemplate.includes("{")) return pathTemplate;
3998
+ const params = pathParams ?? {};
3999
+ return pathTemplate.replace(/\{([^}]+)\}/g, (_match, name) => {
4000
+ const value = params[name];
4001
+ if (value === void 0) {
4002
+ throw new OllamaGenericClientError(
4003
+ `Missing path parameter "${name}" for path template "${pathTemplate}". Provide it via the pathParams field on the invoke() request.`
4004
+ );
4005
+ }
4006
+ return encodeURIComponent(value);
4007
+ });
4008
+ }
4009
+ function extractModelFromBody(body) {
4010
+ if (typeof body !== "object" || body === null) return void 0;
4011
+ const obj = body;
4012
+ const model = obj.model;
4013
+ return typeof model === "string" ? model : void 0;
4014
+ }
4015
+ function applyQueryParams(path, queryParams) {
4016
+ if (!queryParams || Object.keys(queryParams).length === 0) return path;
4017
+ const pairs = Object.entries(queryParams).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
4018
+ return `${path}?${pairs}`;
4019
+ }
4020
+
4021
+ // src/responses.ts
4022
+ var adapterResponseSequence = 0;
4023
+ function toNativeTools(tools) {
4024
+ if (tools === void 0 || tools.length === 0) return void 0;
4025
+ return tools.map((tool) => ({
4026
+ type: "function",
4027
+ function: {
4028
+ name: tool.name,
4029
+ description: tool.description ?? "",
4030
+ parameters: tool.parameters ?? { type: "object", properties: {} }
4031
+ }
4032
+ }));
4033
+ }
4034
+ function mapNativeUsage(usage) {
4035
+ if (usage === void 0) return void 0;
4036
+ const input = usage.input_tokens ?? 0;
4037
+ const output = usage.output_tokens ?? 0;
4038
+ return { input_tokens: input, output_tokens: output, total_tokens: input + output };
4039
+ }
4040
+ function parseToolCallArguments(raw) {
4041
+ if (raw === void 0 || raw === null || raw === "") return {};
4042
+ try {
4043
+ const parsed = JSON.parse(raw);
4044
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
4045
+ } catch {
4046
+ return { _raw: raw };
4047
+ }
4048
+ }
4049
+ function mapNativeResponse(native) {
4050
+ const textParts = [];
4051
+ const thinkingParts = [];
4052
+ const toolCalls = [];
4053
+ for (const item of native.output ?? []) {
4054
+ if (item.type === "message") {
4055
+ for (const part of item.content ?? []) {
4056
+ if (part.type === "output_text") textParts.push(part.text ?? "");
4057
+ else if (part.type === "refusal") textParts.push(part.refusal ?? "");
4058
+ }
4059
+ } else if (item.type === "reasoning") {
4060
+ if (item.text !== void 0 && item.text !== "") thinkingParts.push(item.text);
4061
+ } else if (item.type === "function_call") {
4062
+ toolCalls.push({
4063
+ ...item.id !== void 0 ? { id: item.id } : {},
4064
+ function: { name: item.name, arguments: parseToolCallArguments(item.arguments) }
4065
+ });
4066
+ }
4067
+ }
4068
+ return {
4069
+ id: native.id ?? `resp_${Date.now().toString(36)}`,
4070
+ model: native.model,
4071
+ created_at: new Date((native.created_at ?? 0) * 1e3).toISOString(),
4072
+ output_text: textParts.join(""),
4073
+ ...thinkingParts.length > 0 ? { thinking: thinkingParts.join("\n") } : {},
4074
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
4075
+ ...mapNativeUsage(native.usage) !== void 0 ? { usage: mapNativeUsage(native.usage) } : {}
4076
+ };
4077
+ }
4078
+ function mapChatUsage(res) {
4079
+ const asChat = res;
4080
+ const asStream = res;
4081
+ const input = asChat.prompt_eval_count ?? asStream.usage?.promptTokens ?? asStream.raw?.prompt_eval_count ?? 0;
4082
+ const output = asChat.eval_count ?? asStream.usage?.completionTokens ?? asStream.raw?.eval_count ?? 0;
4083
+ if (input === 0 && output === 0) return void 0;
4084
+ return { input_tokens: input, output_tokens: output, total_tokens: input + output };
4085
+ }
4086
+ function mapChatResponse(res) {
4087
+ adapterResponseSequence += 1;
4088
+ return {
4089
+ id: `resp_${Date.now().toString(36)}${adapterResponseSequence.toString(36)}`,
4090
+ model: res.model,
4091
+ created_at: res.created_at,
4092
+ output_text: res.message.content,
4093
+ ...res.message.thinking !== void 0 ? { thinking: res.message.thinking } : {},
4094
+ ...res.message.tool_calls !== void 0 && res.message.tool_calls.length > 0 ? { tool_calls: res.message.tool_calls } : {},
4095
+ ...mapChatUsage(res) !== void 0 ? { usage: mapChatUsage(res) } : {},
4096
+ transport: "chat-adapter"
4097
+ };
4098
+ }
4099
+ var ResponsesModule = class {
4100
+ constructor(runner) {
4101
+ this.runner = runner;
4102
+ }
4103
+ /** The request body sent to the native endpoint (also reused for streaming). */
4104
+ nativeBody(req) {
4105
+ return {
4106
+ model: req.model,
4107
+ input: req.input,
4108
+ ...req.instructions !== void 0 ? { instructions: req.instructions } : {},
4109
+ ...req.tools !== void 0 && req.tools.length > 0 ? { tools: toNativeTools(req.tools) } : {},
4110
+ ...req.temperature !== void 0 ? { temperature: req.temperature } : {},
4111
+ ...req.top_p !== void 0 ? { top_p: req.top_p } : {},
4112
+ ...req.max_output_tokens !== void 0 ? { max_output_tokens: req.max_output_tokens } : {},
4113
+ ...req.reasoning_effort !== void 0 ? { reasoning: { effort: req.reasoning_effort } } : {},
4114
+ ...req.think !== void 0 ? { think: req.think } : {}
4115
+ };
4116
+ }
4117
+ /**
4118
+ * Runs one Responses request, preferring the native `POST /v1/responses`
4119
+ * endpoint and transparently re-issuing via `/api/chat` when the server
4120
+ * answers `404` (pre-v0.13.3 Ollama without the compatibility endpoint).
4121
+ */
4122
+ async create(req) {
4123
+ const body = this.nativeBody(req);
4124
+ try {
4125
+ const native = await this.runner(
4126
+ (http, signal) => http.request({
4127
+ path: "/v1/responses",
4128
+ body: { ...body, stream: false },
4129
+ signal
4130
+ }),
4131
+ { model: req.model, signal: req.signal, timeoutMs: req.timeoutMs }
4132
+ );
4133
+ return { ...mapNativeResponse(native), transport: "native" };
4134
+ } catch (err) {
4135
+ if (!(err instanceof OllamaNotFoundError)) throw err;
4136
+ }
4137
+ return this.createViaChatAdapter(req);
4138
+ }
4139
+ /** Convenience: `create()` returning only the `output_text` string. */
4140
+ async createText(req) {
4141
+ const res = await this.create(req);
4142
+ return res.output_text;
4143
+ }
4144
+ async createViaChatAdapter(req) {
4145
+ const messages = [
4146
+ ...req.instructions !== void 0 ? [{ role: "system", content: req.instructions }] : [],
4147
+ { role: "user", content: req.input }
4148
+ ];
4149
+ const chatRes = await this.runner(
4150
+ (http, signal) => http.request({
4151
+ path: "/api/chat",
4152
+ body: {
4153
+ model: req.model,
4154
+ messages,
4155
+ ...req.tools !== void 0 && req.tools.length > 0 ? { tools: toNativeTools(req.tools) } : {},
4156
+ options: {
4157
+ ...req.temperature !== void 0 ? { temperature: req.temperature } : {},
4158
+ ...req.top_p !== void 0 ? { top_p: req.top_p } : {},
4159
+ ...req.max_output_tokens !== void 0 ? { num_predict: req.max_output_tokens } : {}
4160
+ },
4161
+ ...req.think !== void 0 ? { think: req.think } : {},
4162
+ stream: false
4163
+ },
4164
+ signal
4165
+ }),
4166
+ { model: req.model, signal: req.signal, timeoutMs: req.timeoutMs }
4167
+ );
4168
+ return mapChatResponse(chatRes);
4169
+ }
4170
+ /**
4171
+ * Streams a Responses request as simplified
4172
+ * `{type: 'text_delta' | 'thinking_delta' | 'done'}` events. Dual-mode like
4173
+ * {@link create}: native SSE from `/v1/responses` when available, otherwise
4174
+ * the normalized `/api/chat` token stream. Tool calls are not delta'd —
4175
+ * collect them from the final `done` event's `response.tool_calls`.
4176
+ *
4177
+ * This is a lazy async generator: nothing is sent until the first
4178
+ * iteration, matching `for await (... of client.responses.stream(req))`
4179
+ * intuition. The generator is also cancellable through `req.signal`.
4180
+ */
4181
+ async *stream(req) {
4182
+ const body = this.nativeBody(req);
4183
+ let nativeStream;
4184
+ try {
4185
+ nativeStream = await this.runner(
4186
+ async (http, signal) => {
4187
+ const source = await http.requestSseStream({
4188
+ path: "/v1/responses",
4189
+ body: { ...body, stream: true },
4190
+ signal
4191
+ });
4192
+ return new OpenAIResponsesStream(source, signal);
4193
+ },
4194
+ {
4195
+ model: req.model,
4196
+ signal: req.signal,
4197
+ timeoutMs: req.timeoutMs,
4198
+ holdUntil: (s) => s.finalResult
4199
+ }
4200
+ );
4201
+ } catch (err) {
4202
+ if (!(err instanceof OllamaNotFoundError)) throw err;
4203
+ }
4204
+ if (nativeStream !== void 0) {
4205
+ for await (const event of nativeStream) {
4206
+ const type = event.type;
4207
+ if (type === "response.output_text.delta") {
4208
+ yield { type: "text_delta", delta: event.delta };
4209
+ } else if (type === "response.reasoning_text.delta") {
4210
+ yield { type: "thinking_delta", delta: event.delta };
4211
+ }
4212
+ }
4213
+ yield {
4214
+ type: "done",
4215
+ response: { ...mapNativeResponse(await nativeStream.finalResult), transport: "native" }
4216
+ };
4217
+ return;
4218
+ }
4219
+ const chatStream = await this.runner(
4220
+ async (http, signal) => {
4221
+ const source = await http.requestStream({
4222
+ path: "/api/chat",
4223
+ body: {
4224
+ model: req.model,
4225
+ messages: [
4226
+ ...req.instructions !== void 0 ? [{ role: "system", content: req.instructions }] : [],
4227
+ { role: "user", content: req.input }
4228
+ ],
4229
+ ...req.tools !== void 0 && req.tools.length > 0 ? { tools: toNativeTools(req.tools) } : {},
4230
+ options: {
4231
+ ...req.temperature !== void 0 ? { temperature: req.temperature } : {},
4232
+ ...req.top_p !== void 0 ? { top_p: req.top_p } : {},
4233
+ ...req.max_output_tokens !== void 0 ? { num_predict: req.max_output_tokens } : {}
4234
+ },
4235
+ ...req.think !== void 0 ? { think: req.think } : {},
4236
+ stream: true
4237
+ },
4238
+ signal
4239
+ });
4240
+ return normalizeChatStream(source, signal);
4241
+ },
4242
+ {
4243
+ model: req.model,
4244
+ signal: req.signal,
4245
+ timeoutMs: req.timeoutMs,
4246
+ holdUntil: (s) => s.finalResult
4247
+ }
4248
+ );
4249
+ for await (const event of chatStream) {
4250
+ if (event.type === "token") {
4251
+ yield { type: "text_delta", delta: event.data.delta };
4252
+ } else if (event.type === "thinking") {
4253
+ yield { type: "thinking_delta", delta: event.data.delta };
4254
+ }
4255
+ }
4256
+ const result = await chatStream.finalResult;
4257
+ yield {
4258
+ type: "done",
4259
+ response: {
4260
+ id: `resp_${Date.now().toString(36)}${(++adapterResponseSequence).toString(36)}`,
4261
+ model: result.model,
4262
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
4263
+ output_text: result.message.content,
4264
+ ...result.message.thinking !== void 0 ? { thinking: result.message.thinking } : {},
4265
+ ...result.message.tool_calls !== void 0 && result.message.tool_calls.length > 0 ? { tool_calls: result.message.tool_calls } : {},
4266
+ ...mapChatUsage(result) !== void 0 ? { usage: mapChatUsage(result) } : {},
4267
+ transport: "chat-adapter"
4268
+ }
4269
+ };
4270
+ }
4271
+ };
4272
+
4273
+ // src/conversation.ts
4274
+ function extractTurnStats(res) {
4275
+ const cachedTokens = res.prompt_eval_cached_count ?? 0;
4276
+ const evaluatedTokens = res.prompt_eval_count ?? 0;
4277
+ const total = cachedTokens + evaluatedTokens;
4278
+ return {
4279
+ cachedTokens,
4280
+ evaluatedTokens,
4281
+ hitRate: total > 0 ? cachedTokens / total : 0
4282
+ };
4283
+ }
4284
+ var ConversationSession = class {
4285
+ constructor(client, config) {
4286
+ this.client = client;
4287
+ this.config = config;
4288
+ if (config.systemPrompt !== void 0 && config.systemPrompt !== "") {
4289
+ this.history.push(Object.freeze({ role: "system", content: config.systemPrompt }));
4290
+ }
4291
+ }
4292
+ /** Ordered, append-only history. Message objects are frozen on push. */
4293
+ history = [];
4294
+ /** Mutable tallies behind the read-only {@link cacheStats} view. */
4295
+ tallies = {
4296
+ turns: 0,
4297
+ cachedTokens: 0,
4298
+ evaluatedTokens: 0
4299
+ };
4300
+ /** The model name this session talks to. */
4301
+ get model() {
4302
+ return this.config.model;
4303
+ }
4304
+ /** Number of completed turns. */
4305
+ get turnCount() {
4306
+ return this.tallies.turns;
4307
+ }
4308
+ /** Cumulative KV-cache statistics across completed turns. */
4309
+ get cacheStats() {
4310
+ const total = this.tallies.cachedTokens + this.tallies.evaluatedTokens;
4311
+ return {
4312
+ turns: this.tallies.turns,
4313
+ cachedTokens: this.tallies.cachedTokens,
4314
+ evaluatedTokens: this.tallies.evaluatedTokens,
4315
+ totalPromptTokens: total,
4316
+ hitRate: total > 0 ? this.tallies.cachedTokens / total : 0
4317
+ };
4318
+ }
4319
+ /**
4320
+ * Sends the user's message and returns the assistant's reply text —
4321
+ * the ergonomic path. Use {@link sendTurn} when you also need the
4322
+ * per-turn cache statistics or the raw response.
4323
+ */
4324
+ async send(content, overrides) {
4325
+ const turn = await this.sendTurn(content, overrides);
4326
+ return turn.content;
4327
+ }
4328
+ /**
4329
+ * Sends the user's message and returns the full turn record: reply text,
4330
+ * per-turn KV-cache statistics, and the raw `/api/chat` response.
4331
+ */
4332
+ async sendTurn(content, overrides) {
4333
+ this.history.push(Object.freeze({ role: "user", content }));
4334
+ const response = await this.client.chat({
4335
+ model: this.config.model,
4336
+ messages: [...this.history],
4337
+ ...this.config.tools !== void 0 || overrides?.tools !== void 0 ? { tools: overrides?.tools ?? this.config.tools } : {},
4338
+ ...this.config.format !== void 0 || overrides?.format !== void 0 ? { format: overrides?.format ?? this.config.format } : {},
4339
+ options: { ...this.config.options, ...overrides?.options },
4340
+ ...this.config.think !== void 0 || overrides?.think !== void 0 ? { think: overrides?.think ?? this.config.think } : {},
4341
+ ...this.config.keep_alive !== void 0 || overrides?.keep_alive !== void 0 ? { keep_alive: overrides?.keep_alive ?? this.config.keep_alive } : {},
4342
+ ...overrides?.signal !== void 0 ? { signal: overrides.signal } : {},
4343
+ ...overrides?.timeoutMs !== void 0 ? { timeoutMs: overrides.timeoutMs } : {},
4344
+ stream: false
4345
+ });
4346
+ this.history.push(Object.freeze({ ...response.message }));
4347
+ const cache = extractTurnStats(response);
4348
+ this.tallies.turns += 1;
4349
+ this.tallies.cachedTokens += cache.cachedTokens;
4350
+ this.tallies.evaluatedTokens += cache.evaluatedTokens;
4351
+ return { content: response.message.content, cache, response };
4352
+ }
4353
+ /**
4354
+ * A defensive copy of the session history — mutating the returned array or
4355
+ * its frozen messages has no effect on the session.
4356
+ */
4357
+ getMessages() {
4358
+ return this.history.map((message) => message);
4359
+ }
4360
+ /**
4361
+ * Clears the conversation back to its initial state (system prompt, zero
4362
+ * turns, zeroed cache statistics). The next turn starts with a cold cache
4363
+ * by definition — there is nothing upstream of it to reuse.
4364
+ */
4365
+ reset() {
4366
+ this.history.length = 0;
4367
+ if (this.config.systemPrompt !== void 0 && this.config.systemPrompt !== "") {
4368
+ this.history.push(
4369
+ Object.freeze({ role: "system", content: this.config.systemPrompt })
4370
+ );
4371
+ }
4372
+ this.tallies.turns = 0;
4373
+ this.tallies.cachedTokens = 0;
4374
+ this.tallies.evaluatedTokens = 0;
2977
4375
  }
2978
4376
  };
2979
4377
 
4378
+ // src/context-safety.ts
4379
+ var DEFAULT_CONTEXT_WARNING_THRESHOLD = 0.9;
4380
+ var IMAGE_TOKEN_ESTIMATE = 300;
4381
+ var CHARS_PER_TOKEN = 4;
4382
+ function isCjkCodePoint(code) {
4383
+ return code >= 19968 && code <= 40959 || // CJK Unified Ideographs
4384
+ code >= 13312 && code <= 19903 || // CJK Extension A
4385
+ code >= 12352 && code <= 12543 || // Hiragana + Katakana
4386
+ code >= 44032 && code <= 55215 || // Hangul syllables
4387
+ code >= 63744 && code <= 64255 || // CJK Compatibility Ideographs
4388
+ code >= 65280 && code <= 65519;
4389
+ }
4390
+ function estimateTokens(text) {
4391
+ if (text.length === 0) return 0;
4392
+ let cjk = 0;
4393
+ let other = 0;
4394
+ for (const char of text) {
4395
+ if (isCjkCodePoint(char.codePointAt(0) ?? 0)) cjk += 1;
4396
+ else other += 1;
4397
+ }
4398
+ return Math.ceil(cjk + other / CHARS_PER_TOKEN);
4399
+ }
4400
+ var MESSAGE_OVERHEAD_TOKENS = 4;
4401
+ function estimateMessageTokens(message) {
4402
+ let total = MESSAGE_OVERHEAD_TOKENS + estimateTokens(message.content ?? "");
4403
+ if (message.thinking !== void 0) total += estimateTokens(message.thinking);
4404
+ if (message.tool_calls !== void 0) {
4405
+ for (const call of message.tool_calls) {
4406
+ total += estimateTokens(call.function.name);
4407
+ total += estimateTokens(JSON.stringify(call.function.arguments ?? {}));
4408
+ }
4409
+ }
4410
+ if (message.images !== void 0) total += message.images.length * IMAGE_TOKEN_ESTIMATE;
4411
+ return total;
4412
+ }
4413
+ function estimateChatRequestTokens(req) {
4414
+ let total = 0;
4415
+ for (const message of req.messages) total += estimateMessageTokens(message);
4416
+ if (req.tools !== void 0) {
4417
+ for (const tool of req.tools) {
4418
+ total += estimateTokens(tool.function.description ?? "");
4419
+ total += estimateTokens(JSON.stringify(tool.function.parameters ?? {}));
4420
+ }
4421
+ }
4422
+ return total;
4423
+ }
4424
+ function estimateGenerateRequestTokens(req) {
4425
+ let total = estimateTokens(req.prompt ?? "");
4426
+ if (req.system !== void 0) total += estimateTokens(req.system);
4427
+ if (req.template !== void 0) total += estimateTokens(req.template);
4428
+ if (req.images !== void 0) total += req.images.length * IMAGE_TOKEN_ESTIMATE;
4429
+ return total;
4430
+ }
4431
+ function checkChatContext(req, options = {}) {
4432
+ return check(estimateChatRequestTokens(req), req.options?.num_ctx, options);
4433
+ }
4434
+ function checkGenerateContext(req, options = {}) {
4435
+ return check(estimateGenerateRequestTokens(req), req.options?.num_ctx, options);
4436
+ }
4437
+ function check(estimatedPromptTokens, requestNumCtx, options) {
4438
+ const threshold = options.threshold ?? DEFAULT_CONTEXT_WARNING_THRESHOLD;
4439
+ if (requestNumCtx !== void 0) {
4440
+ return {
4441
+ estimatedPromptTokens,
4442
+ effectiveContextLength: requestNumCtx,
4443
+ windowSource: "request",
4444
+ exceedsThreshold: estimatedPromptTokens > requestNumCtx * threshold
4445
+ };
4446
+ }
4447
+ if (options.defaultContextLength !== void 0) {
4448
+ return {
4449
+ estimatedPromptTokens,
4450
+ effectiveContextLength: options.defaultContextLength,
4451
+ windowSource: "client-default",
4452
+ exceedsThreshold: estimatedPromptTokens > options.defaultContextLength * threshold
4453
+ };
4454
+ }
4455
+ return {
4456
+ estimatedPromptTokens,
4457
+ effectiveContextLength: OLLAMA_FALLBACK_CONTEXT_LENGTH,
4458
+ windowSource: "server-fallback",
4459
+ exceedsThreshold: estimatedPromptTokens > OLLAMA_FALLBACK_CONTEXT_LENGTH
4460
+ };
4461
+ }
4462
+ var OLLAMA_FALLBACK_CONTEXT_LENGTH = 2048;
4463
+ function contextWarningMessage(check2, kind) {
4464
+ const where = `OllamaClient.${kind}()`;
4465
+ const estimate = `~${check2.estimatedPromptTokens} tokens`;
4466
+ if (check2.windowSource === "request") {
4467
+ return `${where}: estimated prompt size ${estimate} is close to or beyond the request's context window (num_ctx=${check2.effectiveContextLength}). The server will likely silently truncate the top of the conversation \u2014 the model loses earlier turns with no error. Raise \`options.num_ctx\` or shorten the conversation. Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
4468
+ }
4469
+ if (check2.windowSource === "client-default") {
4470
+ return `${where}: estimated prompt size ${estimate} is close to or beyond the client's \`defaultContextLength\` (${check2.effectiveContextLength}, injected as \`num_ctx\`). The server will likely silently truncate the top of the conversation \u2014 the model loses earlier turns with no error. Raise \`defaultContextLength\`, set \`options.num_ctx\` per request, or shorten the conversation. Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
4471
+ }
4472
+ return `${where}: estimated prompt size ${estimate} outright exceeds Ollama's conservative server-default context window (${OLLAMA_FALLBACK_CONTEXT_LENGTH}\u20134096 tokens when \`num_ctx\` is unset \u2014 model-dependent). Unless the model's default is larger, the server will silently truncate the top of the conversation \u2014 the model loses earlier turns with no error. Set \`options.num_ctx\` (or the client's \`defaultContextLength\`) to an explicit window to make truncation visible and controllable. Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
4473
+ }
4474
+
4475
+ // src/generated/api/operations.ts
4476
+ var blobsOp = {
4477
+ operationId: "blobs",
4478
+ method: "HEAD",
4479
+ path: "/api/blobs/{digest}",
4480
+ environment: { local: true, cloud: false },
4481
+ parameters: [{ name: "digest", in: "path", required: true, schema: { type: "string" } }],
4482
+ transport: { mode: "json", streaming: false },
4483
+ capabilities: {},
4484
+ status: { documented: true },
4485
+ domain: "native",
4486
+ notes: [
4487
+ "HEAD-only endpoint for blob existence checks; declared in OpenAPI but not in the legacy parity manifest."
4488
+ ]
4489
+ };
4490
+ var chatOp = {
4491
+ operationId: "chat",
4492
+ method: "POST",
4493
+ path: "/api/chat",
4494
+ request: { $ref: "#/schemas/ChatRequest" },
4495
+ response: { $ref: "#/schemas/ChatResponse" },
4496
+ environment: { local: true, cloud: true },
4497
+ transport: { mode: "ndjson", streaming: true, streamingDefault: true },
4498
+ capabilities: {
4499
+ thinking: "model-dependent",
4500
+ tools: "model-dependent",
4501
+ vision: "model-dependent",
4502
+ structuredOutput: "supported",
4503
+ logprobs: "supported"
4504
+ },
4505
+ status: { documented: true },
4506
+ domain: "native",
4507
+ notes: [
4508
+ "Use /api/show to discover model-specific thinking values and defaults.",
4509
+ "Structured output (format field) is rejected by Ollama Cloud."
4510
+ ]
4511
+ };
4512
+ var copyOp = {
4513
+ operationId: "copy",
4514
+ method: "POST",
4515
+ path: "/api/copy",
4516
+ request: { $ref: "#/schemas/CopyRequest" },
4517
+ environment: { local: true, cloud: false },
4518
+ transport: { mode: "json", streaming: false },
4519
+ capabilities: {},
4520
+ status: { documented: true },
4521
+ domain: "native"
4522
+ };
4523
+ var createOp = {
4524
+ operationId: "create",
4525
+ method: "POST",
4526
+ path: "/api/create",
4527
+ request: { $ref: "#/schemas/CreateRequest" },
4528
+ response: { $ref: "#/schemas/StatusResponse" },
4529
+ environment: { local: true, cloud: false },
4530
+ transport: { mode: "ndjson", streaming: true, streamingDefault: true },
4531
+ capabilities: { thinking: "unsupported", tools: "unsupported" },
4532
+ status: { documented: true },
4533
+ domain: "native",
4534
+ notes: ["Model creation is local-only; Ollama Cloud does not support it."]
4535
+ };
4536
+ var deleteOp = {
4537
+ operationId: "delete",
4538
+ method: "DELETE",
4539
+ path: "/api/delete",
4540
+ request: { $ref: "#/schemas/DeleteRequest" },
4541
+ environment: { local: true, cloud: false },
4542
+ transport: { mode: "json", streaming: false },
4543
+ capabilities: {},
4544
+ status: { documented: true },
4545
+ domain: "native"
4546
+ };
4547
+ var embedOp = {
4548
+ operationId: "embed",
4549
+ method: "POST",
4550
+ path: "/api/embed",
4551
+ request: { $ref: "#/schemas/EmbedRequest" },
4552
+ response: { $ref: "#/schemas/EmbedResponse" },
4553
+ environment: { local: true, cloud: true },
4554
+ transport: { mode: "json", streaming: false },
4555
+ capabilities: { embeddings: "supported" },
4556
+ status: { documented: true },
4557
+ domain: "native"
4558
+ };
4559
+ var generateOp = {
4560
+ operationId: "generate",
4561
+ method: "POST",
4562
+ path: "/api/generate",
4563
+ request: { $ref: "#/schemas/GenerateRequest" },
4564
+ response: { $ref: "#/schemas/GenerateResponse" },
4565
+ environment: { local: true, cloud: true },
4566
+ transport: { mode: "ndjson", streaming: true, streamingDefault: true },
4567
+ capabilities: {
4568
+ thinking: "model-dependent",
4569
+ vision: "supported",
4570
+ structuredOutput: "supported",
4571
+ logprobs: "supported"
4572
+ },
4573
+ status: { documented: true },
4574
+ domain: "native"
4575
+ };
4576
+ var psOp = {
4577
+ operationId: "ps",
4578
+ method: "GET",
4579
+ path: "/api/ps",
4580
+ response: { $ref: "#/schemas/PsResponse" },
4581
+ environment: { local: true, cloud: true },
4582
+ transport: { mode: "json", streaming: false },
4583
+ capabilities: {},
4584
+ status: { documented: true },
4585
+ domain: "native"
4586
+ };
4587
+ var pullOp = {
4588
+ operationId: "pull",
4589
+ method: "POST",
4590
+ path: "/api/pull",
4591
+ request: { $ref: "#/schemas/PullRequest" },
4592
+ response: { $ref: "#/schemas/StatusResponse" },
4593
+ environment: { local: true, cloud: false },
4594
+ transport: { mode: "ndjson", streaming: true, streamingDefault: true },
4595
+ capabilities: {},
4596
+ status: { documented: true },
4597
+ domain: "native"
4598
+ };
4599
+ var pushOp = {
4600
+ operationId: "push",
4601
+ method: "POST",
4602
+ path: "/api/push",
4603
+ request: { $ref: "#/schemas/PushRequest" },
4604
+ response: { $ref: "#/schemas/StatusResponse" },
4605
+ environment: { local: true, cloud: false },
4606
+ transport: { mode: "ndjson", streaming: true, streamingDefault: true },
4607
+ capabilities: {},
4608
+ status: { documented: true },
4609
+ domain: "native"
4610
+ };
4611
+ var showOp = {
4612
+ operationId: "show",
4613
+ method: "POST",
4614
+ path: "/api/show",
4615
+ request: { $ref: "#/schemas/ShowRequest" },
4616
+ response: { $ref: "#/schemas/ShowResponse" },
4617
+ environment: { local: true, cloud: true },
4618
+ transport: { mode: "json", streaming: false },
4619
+ capabilities: {},
4620
+ status: { documented: true },
4621
+ domain: "native",
4622
+ notes: [
4623
+ "Capabilities source: /api/show is the runtime source of truth for model-dependent capability values (tools, vision, thinking, embeddings)."
4624
+ ]
4625
+ };
4626
+ var systemOneOp = {
4627
+ operationId: "systemOne",
4628
+ method: "POST",
4629
+ path: "/v1/systemone",
4630
+ request: { $ref: "#/schemas/SystemOneRequest" },
4631
+ response: { $ref: "#/schemas/SystemOneResponse" },
4632
+ environment: { local: true, cloud: false },
4633
+ transport: { mode: "json", streaming: false },
4634
+ capabilities: {
4635
+ thinking: "unsupported",
4636
+ tools: "unsupported",
4637
+ vision: "model-dependent",
4638
+ structuredOutput: "unsupported",
4639
+ logprobs: "unsupported",
4640
+ embeddings: "unsupported"
4641
+ },
4642
+ constraints: {
4643
+ minOllamaVersion: "0.35.0",
4644
+ maxRequestBytes: 65536,
4645
+ maxRequestBytesWithImages: 33554432
4646
+ },
4647
+ status: { documented: true, experimental: true },
4648
+ domain: "native",
4649
+ notes: [
4650
+ "Local-only; Ollama Cloud rejects /v1/systemone.",
4651
+ "64 KiB request limit without images, 32 MiB with images (includes JSON/base64 overhead).",
4652
+ "No tools, no generation controls (temperature, top_p, etc.).",
4653
+ "Vision is model-dependent: images are accepted by vision-weighted models (Clef/Clef Flash) but not by text-only models.",
4654
+ "Experimental: behavioral surface may change between Ollama releases."
4655
+ ]
4656
+ };
4657
+ var tagsOp = {
4658
+ operationId: "tags",
4659
+ method: "GET",
4660
+ path: "/api/tags",
4661
+ response: { $ref: "#/schemas/ListResponse" },
4662
+ environment: { local: true, cloud: true },
4663
+ transport: { mode: "json", streaming: false },
4664
+ capabilities: {},
4665
+ status: { documented: true },
4666
+ domain: "native"
4667
+ };
4668
+ var versionOp = {
4669
+ operationId: "version",
4670
+ method: "GET",
4671
+ path: "/api/version",
4672
+ response: { $ref: "#/schemas/VersionResponse" },
4673
+ environment: { local: true, cloud: true },
4674
+ transport: { mode: "json", streaming: false },
4675
+ capabilities: {},
4676
+ status: { documented: true },
4677
+ domain: "native"
4678
+ };
4679
+
4680
+ // src/generated/api/native-api.ts
4681
+ var NativeApi = class {
4682
+ constructor(runtime) {
4683
+ this.runtime = runtime;
4684
+ }
4685
+ blobs(digest, options) {
4686
+ return this.runtime.invoke({
4687
+ operation: blobsOp,
4688
+ body: void 0,
4689
+ pathParams: { digest },
4690
+ ...options?.signal !== void 0 ? { signal: options.signal } : {}
4691
+ });
4692
+ }
4693
+ chat(request) {
4694
+ return this.runtime.invoke({
4695
+ operation: chatOp,
4696
+ body: request,
4697
+ streamingDefault: true
4698
+ });
4699
+ }
4700
+ copy(request) {
4701
+ return this.runtime.invoke({
4702
+ operation: copyOp,
4703
+ body: request
4704
+ });
4705
+ }
4706
+ create(request) {
4707
+ return this.runtime.invoke({
4708
+ operation: createOp,
4709
+ body: request,
4710
+ streamingDefault: true
4711
+ });
4712
+ }
4713
+ delete(request) {
4714
+ return this.runtime.invoke({
4715
+ operation: deleteOp,
4716
+ body: request
4717
+ });
4718
+ }
4719
+ embed(request) {
4720
+ return this.runtime.invoke({
4721
+ operation: embedOp,
4722
+ body: request
4723
+ });
4724
+ }
4725
+ generate(request) {
4726
+ return this.runtime.invoke({
4727
+ operation: generateOp,
4728
+ body: request,
4729
+ streamingDefault: true
4730
+ });
4731
+ }
4732
+ ps(options) {
4733
+ return this.runtime.invoke({
4734
+ operation: psOp,
4735
+ body: void 0,
4736
+ ...options?.signal !== void 0 ? { signal: options.signal } : {}
4737
+ });
4738
+ }
4739
+ pull(request) {
4740
+ return this.runtime.invoke({
4741
+ operation: pullOp,
4742
+ body: request,
4743
+ streamingDefault: true
4744
+ });
4745
+ }
4746
+ push(request) {
4747
+ return this.runtime.invoke({
4748
+ operation: pushOp,
4749
+ body: request,
4750
+ streamingDefault: true
4751
+ });
4752
+ }
4753
+ show(request) {
4754
+ return this.runtime.invoke({
4755
+ operation: showOp,
4756
+ body: request
4757
+ });
4758
+ }
4759
+ systemOne(request) {
4760
+ return this.runtime.invoke({
4761
+ operation: systemOneOp,
4762
+ body: request
4763
+ });
4764
+ }
4765
+ tags(options) {
4766
+ return this.runtime.invoke({
4767
+ operation: tagsOp,
4768
+ body: void 0,
4769
+ ...options?.signal !== void 0 ? { signal: options.signal } : {}
4770
+ });
4771
+ }
4772
+ version(options) {
4773
+ return this.runtime.invoke({
4774
+ operation: versionOp,
4775
+ body: void 0,
4776
+ ...options?.signal !== void 0 ? { signal: options.signal } : {}
4777
+ });
4778
+ }
4779
+ };
4780
+
4781
+ // src/failover-http-client.ts
4782
+ var FailoverHttpClient = class {
4783
+ constructor(client, baseUrl) {
4784
+ this.client = client;
4785
+ this.baseUrl = baseUrl;
4786
+ }
4787
+ /**
4788
+ * The first candidate endpoint's base URL. Used for inferRuntimeMode()
4789
+ * checks (local vs cloud). The actual request routing picks the best
4790
+ * healthy endpoint per call.
4791
+ */
4792
+ baseUrl;
4793
+ /**
4794
+ * Route a request through OllamaClient's failover machinery.
4795
+ *
4796
+ * Each call to request() goes through executeWithFailover, which:
4797
+ * - Picks the best healthy endpoint from the registry
4798
+ * - Applies retry/backoff within that endpoint
4799
+ * - Fails over to the next candidate on retryable errors
4800
+ * - Reports success/failure to the endpoint health tracker
4801
+ *
4802
+ * Wave 15 (P1): the model is extracted from the request body by the
4803
+ * runtime and passed via the `model` field on the options. This lets
4804
+ * the failover layer filter endpoints by `OllamaEndpoint.models`
4805
+ * (credential-scoped routing). If no model is provided, all endpoints
4806
+ * are considered.
4807
+ *
4808
+ * The `raw` option (for streaming responses) is passed through
4809
+ * unchanged — executeWithFailover returns whatever the operation
4810
+ * callback returns, including raw Response objects.
4811
+ */
4812
+ async request(options) {
4813
+ const { model, ...httpOptions } = options;
4814
+ return this.client.executeWithFailover(
4815
+ async (http, signal) => {
4816
+ const mergedOptions = {
4817
+ ...httpOptions,
4818
+ ...httpOptions.signal !== void 0 ? { signal } : { signal }
4819
+ };
4820
+ return http.request(mergedOptions);
4821
+ },
4822
+ {
4823
+ singleEndpoint: false,
4824
+ ...model !== void 0 ? { model } : {}
4825
+ }
4826
+ );
4827
+ }
4828
+ };
4829
+
4830
+ // src/decision.ts
4831
+ function createDecision(client) {
4832
+ return {
4833
+ async choice(params) {
4834
+ const response = await client.systemOne({
4835
+ model: params.model,
4836
+ state: params.state,
4837
+ ...params.images ? { images: params.images } : {},
4838
+ questions: {
4839
+ decision: {
4840
+ type: "choice",
4841
+ instructions: params.instructions,
4842
+ criteria: params.criteria
4843
+ }
4844
+ },
4845
+ ...params.keepAlive !== void 0 ? { keep_alive: params.keepAlive } : {}
4846
+ });
4847
+ const answer = response.answers.decision;
4848
+ return {
4849
+ choice: answer.choice,
4850
+ probabilities: answer.probabilities,
4851
+ confidence: answer.confidence,
4852
+ usage: response.usage
4853
+ };
4854
+ },
4855
+ async noul(params) {
4856
+ const response = await client.systemOne({
4857
+ model: params.model,
4858
+ state: params.state,
4859
+ ...params.images ? { images: params.images } : {},
4860
+ questions: {
4861
+ decision: {
4862
+ type: "noul",
4863
+ instructions: params.instructions
4864
+ }
4865
+ },
4866
+ ...params.keepAlive !== void 0 ? { keep_alive: params.keepAlive } : {}
4867
+ });
4868
+ const answer = response.answers.decision;
4869
+ return {
4870
+ noul: answer.noul,
4871
+ bool: answer.noul >= 0.5,
4872
+ usage: response.usage
4873
+ };
4874
+ },
4875
+ async score(params) {
4876
+ const response = await client.systemOne({
4877
+ model: params.model,
4878
+ state: params.state,
4879
+ ...params.images ? { images: params.images } : {},
4880
+ questions: {
4881
+ decision: {
4882
+ type: "score",
4883
+ instructions: params.instructions,
4884
+ criteria: params.criteria
4885
+ }
4886
+ },
4887
+ ...params.keepAlive !== void 0 ? { keep_alive: params.keepAlive } : {}
4888
+ });
4889
+ const answer = response.answers.decision;
4890
+ return {
4891
+ score: answer.score,
4892
+ legend: answer.legend,
4893
+ probabilities: answer.probabilities,
4894
+ confidence: answer.confidence,
4895
+ usage: response.usage
4896
+ };
4897
+ },
4898
+ async route(params) {
4899
+ const response = await client.systemOne({
4900
+ model: params.model,
4901
+ state: params.state,
4902
+ ...params.images ? { images: params.images } : {},
4903
+ questions: {
4904
+ decision: {
4905
+ type: "choice",
4906
+ instructions: params.instructions,
4907
+ criteria: params.criteria
4908
+ }
4909
+ },
4910
+ ...params.keepAlive !== void 0 ? { keep_alive: params.keepAlive } : {}
4911
+ });
4912
+ const answer = response.answers.decision;
4913
+ return {
4914
+ route: answer.choice,
4915
+ confidence: answer.confidence
4916
+ };
4917
+ },
4918
+ async verify(params) {
4919
+ const state = {
4920
+ claim: params.claim,
4921
+ evidence: params.evidence
4922
+ };
4923
+ const response = await client.systemOne({
4924
+ model: params.model,
4925
+ state,
4926
+ ...params.images ? { images: params.images } : {},
4927
+ questions: {
4928
+ decision: {
4929
+ type: "noul",
4930
+ instructions: params.instructions ?? "Is the claim supported by the evidence?"
4931
+ }
4932
+ },
4933
+ ...params.keepAlive !== void 0 ? { keep_alive: params.keepAlive } : {}
4934
+ });
4935
+ const answer = response.answers.decision;
4936
+ return {
4937
+ verified: answer.noul >= 0.5,
4938
+ probability: answer.noul
4939
+ };
4940
+ },
4941
+ async rank(params) {
4942
+ const promises = params.candidates.map(async (candidate) => {
4943
+ const response = await client.systemOne({
4944
+ model: params.model,
4945
+ state: candidate.state,
4946
+ ...params.images ? { images: params.images } : {},
4947
+ questions: {
4948
+ decision: {
4949
+ type: "score",
4950
+ instructions: params.instructions,
4951
+ criteria: params.criteria
4952
+ }
4953
+ },
4954
+ ...params.keepAlive !== void 0 ? { keep_alive: params.keepAlive } : {}
4955
+ });
4956
+ const answer = response.answers.decision;
4957
+ return {
4958
+ candidate: candidate.id,
4959
+ score: answer.score,
4960
+ confidence: answer.confidence
4961
+ };
4962
+ });
4963
+ const results = await Promise.all(promises);
4964
+ return results.sort((a, b) => b.score - a.score);
4965
+ }
4966
+ };
4967
+ }
4968
+
2980
4969
  // src/client.ts
2981
4970
  var logicalRequestSequence = 0;
2982
4971
  function createLogicalRequestId() {
@@ -3001,14 +4990,21 @@ var OllamaClient = class {
3001
4990
  * `OLLAMA_API_KEY`), independent of whether `config.endpoints` was used instead.
3002
4991
  */
3003
4992
  cloudApiKey;
4993
+ /** Configured `defaultContextLength` — injected as `num_ctx` when a request omits it. */
4994
+ defaultContextLength;
4995
+ /** Configured `contextWarningThreshold` (default 0.9). */
4996
+ contextWarningThreshold;
4997
+ /** Configured overflow policy — `'warn'` (default) or `'throw'`. */
4998
+ onContextOverflow;
3004
4999
  constructor(config = {}) {
3005
- const resolvedApiKey = resolveApiKey(config.apiKey);
3006
- this.cloudApiKey = resolvedApiKey;
5000
+ this.cloudApiKey = resolveApiKey(config.apiKey);
5001
+ const defaultBaseUrl = resolveBaseUrl(config.baseUrl);
5002
+ const resolvedApiKey = resolveEndpointApiKey(config.apiKey, defaultBaseUrl);
3007
5003
  const credentialEndpoints = resolveCredentialEndpoints(config);
3008
5004
  const endpoints = config.endpoints ?? (credentialEndpoints.length > 0 ? [] : [
3009
5005
  {
3010
5006
  name: "default",
3011
- baseUrl: resolveBaseUrl(config.baseUrl),
5007
+ baseUrl: defaultBaseUrl,
3012
5008
  ...resolvedApiKey !== void 0 ? { apiKey: resolvedApiKey } : {},
3013
5009
  ...config.headers !== void 0 ? { headers: config.headers } : {}
3014
5010
  }
@@ -3024,6 +5020,9 @@ var OllamaClient = class {
3024
5020
  this.middleware = config.middleware;
3025
5021
  this.onLifecycleEvent = config.onLifecycleEvent;
3026
5022
  this.retryConfig = typeof config.retries === "number" ? { ...DEFAULT_RETRY_CONFIG, maxRetries: config.retries } : { ...DEFAULT_RETRY_CONFIG, ...config.retries };
5023
+ this.defaultContextLength = config.defaultContextLength;
5024
+ this.contextWarningThreshold = config.contextWarningThreshold;
5025
+ this.onContextOverflow = config.onContextOverflow ?? "warn";
3027
5026
  this.models = new ModelsClient((op, opts) => this.executeWithFailover(op, opts));
3028
5027
  }
3029
5028
  get modelsClient() {
@@ -3037,42 +5036,130 @@ var OllamaClient = class {
3037
5036
  * API with the generated `NativeApi` / `OpenAIApi` / `AnthropicApi`
3038
5037
  * surface without configuring two separate HttpClient instances.
3039
5038
  *
3040
- * The returned runtime is bound to the registry's first candidate
3041
- * endpoint (or the resolved single-endpoint config if no `endpoints`
3042
- * array was provided). For multi-endpoint configs that need per-call
3043
- * routing, prefer constructing `OllamaRuntime` directly with a
3044
- * specific HttpClient:
3045
- *
3046
- * ```ts
3047
- * const http = new HttpClient({ baseUrl: 'http://host-a:11434' });
3048
- * const runtime = new OllamaRuntime({ http });
3049
- * const api = new NativeApi(runtime);
3050
- * ```
5039
+ * Wave 14: the runtime now participates in multi-endpoint failover.
5040
+ * Each request is routed through this client's `executeWithFailover`
5041
+ * machinery via a {@link FailoverHttpClient} wrapper. If the first
5042
+ * candidate endpoint is down, requests automatically fail over to
5043
+ * the next healthy candidate — matching the behavior of the
5044
+ * hand-written OllamaClient methods (chat, generate, etc.).
3051
5045
  *
3052
5046
  * The runtime is cached on first call — subsequent calls return the
3053
- * same instance.
5047
+ * same instance. The `FailoverHttpClient` holds a reference to this
5048
+ * client, so it always sees the current endpoint registry state.
3054
5049
  */
3055
5050
  get runtime() {
3056
5051
  if (this._runtime === void 0) {
3057
5052
  const candidates = this.registry.candidates();
3058
5053
  const endpoint = candidates[0];
3059
5054
  const baseUrl = endpoint?.baseUrl ?? "http://localhost:11434";
3060
- const http = new HttpClient({
3061
- baseUrl,
3062
- ...endpoint?.apiKey !== void 0 ? { apiKey: endpoint.apiKey } : {},
3063
- ...endpoint?.headers !== void 0 ? { headers: endpoint.headers } : {},
5055
+ const http = new FailoverHttpClient(this, baseUrl);
5056
+ const cloudHttp = new HttpClient({
5057
+ baseUrl: OLLAMA_CLOUD_BASE_URL,
5058
+ ...this.cloudApiKey !== void 0 ? { apiKey: this.cloudApiKey } : {},
3064
5059
  fetch: this.fetchImpl,
3065
5060
  ...this.middleware !== void 0 ? { middleware: this.middleware } : {},
3066
5061
  ...this.onLifecycleEvent !== void 0 ? { onLifecycleEvent: this.onLifecycleEvent } : {}
3067
5062
  });
3068
5063
  this._runtime = new OllamaRuntime({
3069
5064
  http,
5065
+ cloudHttp,
3070
5066
  localMode: inferRuntimeMode(baseUrl) === "local"
3071
5067
  });
3072
5068
  }
3073
5069
  return this._runtime;
3074
5070
  }
3075
5071
  _runtime;
5072
+ /**
5073
+ * Lazily-constructed `NativeApi` bound to this client's runtime. Exposes
5074
+ * the full generated native API surface (chat, generate, embed, systemOne,
5075
+ * etc.) with contract-driven types and runtime enforcement.
5076
+ *
5077
+ * Wave 13: `systemOne()` delegates to this accessor.
5078
+ */
5079
+ get native() {
5080
+ if (this._nativeApi === void 0) {
5081
+ this._nativeApi = new NativeApi(this.runtime);
5082
+ }
5083
+ return this._nativeApi;
5084
+ }
5085
+ _nativeApi;
5086
+ /**
5087
+ * Higher-level System One decision helpers (Wave 14B).
5088
+ *
5089
+ * Ergonomic wrappers around `systemOne()` for the six most common
5090
+ * decision patterns: `choice()`, `noul()`, `score()`, `route()`,
5091
+ * `verify()`, `rank()`. Each constructs a single-question request
5092
+ * and extracts the typed answer.
5093
+ *
5094
+ * ```ts
5095
+ * const result = await client.decision.choice({
5096
+ * model: 'tev1:4b',
5097
+ * state: 'Customer was charged twice',
5098
+ * instructions: 'What is the primary intent?',
5099
+ * criteria: {
5100
+ * refund: 'Customer wants a refund',
5101
+ * duplicate_charge: 'Customer reports multiple charges',
5102
+ * },
5103
+ * });
5104
+ * console.log(result.choice); // 'duplicate_charge'
5105
+ * console.log(result.confidence); // 0.89
5106
+ * ```
5107
+ */
5108
+ get decision() {
5109
+ if (this._decision === void 0) {
5110
+ this._decision = createDecision(this);
5111
+ }
5112
+ return this._decision;
5113
+ }
5114
+ _decision;
5115
+ /**
5116
+ * Ergonomic OpenAI Responses API bridge (`client.responses.create()` /
5117
+ * `.stream()`). Dual-mode transport: prefers the native `POST
5118
+ * /v1/responses` endpoint (Ollama ≥ v0.13.3) and transparently re-issues via
5119
+ * `/api/chat` when the server answers 404, so migrating OpenAI code works
5120
+ * unchanged against older servers. See `src/responses.ts`.
5121
+ */
5122
+ get responses() {
5123
+ if (this._responses === void 0) {
5124
+ this._responses = new ResponsesModule((op, opts) => this.executeWithFailover(op, opts));
5125
+ }
5126
+ return this._responses;
5127
+ }
5128
+ _responses;
5129
+ session(modelOrOptions, systemPrompt) {
5130
+ const options = typeof modelOrOptions === "string" ? {
5131
+ model: modelOrOptions,
5132
+ ...systemPrompt !== void 0 ? { systemPrompt } : {}
5133
+ } : modelOrOptions;
5134
+ return new ConversationSession(this, options);
5135
+ }
5136
+ /**
5137
+ * Context-window pre-flight enforcement — see `src/context-safety.ts` and
5138
+ * the `defaultContextLength` / `onContextOverflow` config docs. Logs a
5139
+ * warning (default) or throws client-side when the estimated prompt size
5140
+ * is close to / beyond the effective context window.
5141
+ */
5142
+ enforceContextSafety(kind, check2) {
5143
+ if (!check2.exceedsThreshold) return;
5144
+ const message = contextWarningMessage(check2, kind);
5145
+ if (this.onContextOverflow === "throw") {
5146
+ throw new OllamaClientError(message, { code: "context_overflow" });
5147
+ }
5148
+ this.logger.warn(message);
5149
+ }
5150
+ /**
5151
+ * Returns `options` with `num_ctx` injected from the client's
5152
+ * `defaultContextLength` when the request didn't set one — making the
5153
+ * effective context window explicit on the wire instead of relying on
5154
+ * Ollama's silently-truncating server default. Returns the original
5155
+ * reference when there's nothing to inject (the common case).
5156
+ */
5157
+ injectDefaultContextLength(options) {
5158
+ if (options?.num_ctx !== void 0 || this.defaultContextLength === void 0) {
5159
+ return options;
5160
+ }
5161
+ return { ...options, num_ctx: this.defaultContextLength };
5162
+ }
3076
5163
  /**
3077
5164
  * Fail-fast guard for `format` (structured output) requests: throws before any network
3078
5165
  * call if the candidate endpoint is inferred as Ollama Cloud, which does not currently
@@ -3182,7 +5269,7 @@ var OllamaClient = class {
3182
5269
  }
3183
5270
  }
3184
5271
  async chat(req) {
3185
- const encodedMessages = await withEncodedMessageImages(req.messages);
5272
+ const encodedMessages = await withEncodedMessageImages(req.messages, req.signal);
3186
5273
  const messages = encodedMessages.map((message) => {
3187
5274
  if (message.role === "tool" && message.tool_call_id !== void 0) {
3188
5275
  const { tool_call_id: _toolCallId, ...nativeMessage } = message;
@@ -3190,6 +5277,14 @@ var OllamaClient = class {
3190
5277
  }
3191
5278
  return message;
3192
5279
  });
5280
+ this.enforceContextSafety(
5281
+ "chat",
5282
+ checkChatContext(req, {
5283
+ defaultContextLength: this.defaultContextLength,
5284
+ threshold: this.contextWarningThreshold
5285
+ })
5286
+ );
5287
+ const options = this.injectDefaultContextLength(req.options);
3193
5288
  if (req.stream) {
3194
5289
  return this.executeWithFailover(
3195
5290
  async (http, signal) => {
@@ -3197,7 +5292,7 @@ var OllamaClient = class {
3197
5292
  this.assertStructuredOutputSupported(http.baseUrl, req.model);
3198
5293
  const stream = await http.requestStream({
3199
5294
  path: "/api/chat",
3200
- body: { ...req, messages, stream: true },
5295
+ body: { ...req, options, messages, stream: true },
3201
5296
  signal
3202
5297
  });
3203
5298
  return normalizeChatStream(stream, signal);
@@ -3226,7 +5321,7 @@ var OllamaClient = class {
3226
5321
  this.assertStructuredOutputSupported(http.baseUrl, req.model);
3227
5322
  return http.request({
3228
5323
  path: "/api/chat",
3229
- body: { ...req, messages, stream: false },
5324
+ body: { ...req, options, messages, stream: false },
3230
5325
  signal
3231
5326
  });
3232
5327
  }, req);
@@ -3253,7 +5348,15 @@ var OllamaClient = class {
3253
5348
  return parseStructuredOutput(res.message.content, schema);
3254
5349
  }
3255
5350
  async generate(req) {
3256
- const encodedReq = await withEncodedImages(req);
5351
+ const encodedReq = await withEncodedImages(req, req.signal);
5352
+ this.enforceContextSafety(
5353
+ "generate",
5354
+ checkGenerateContext(req, {
5355
+ defaultContextLength: this.defaultContextLength,
5356
+ threshold: this.contextWarningThreshold
5357
+ })
5358
+ );
5359
+ const options = this.injectDefaultContextLength(req.options);
3257
5360
  if (encodedReq.stream) {
3258
5361
  return this.executeWithFailover(
3259
5362
  async (http, signal) => {
@@ -3261,7 +5364,7 @@ var OllamaClient = class {
3261
5364
  this.assertStructuredOutputSupported(http.baseUrl, encodedReq.model);
3262
5365
  const stream = await http.requestStream({
3263
5366
  path: "/api/generate",
3264
- body: { ...encodedReq, stream: true },
5367
+ body: { ...encodedReq, options, stream: true },
3265
5368
  signal
3266
5369
  });
3267
5370
  return normalizeGenerateStream(stream, signal);
@@ -3287,7 +5390,7 @@ var OllamaClient = class {
3287
5390
  this.assertStructuredOutputSupported(http.baseUrl, encodedReq.model);
3288
5391
  return http.request({
3289
5392
  path: "/api/generate",
3290
- body: { ...encodedReq, stream: false },
5393
+ body: { ...encodedReq, options, stream: false },
3291
5394
  signal
3292
5395
  });
3293
5396
  }, encodedReq);
@@ -3345,6 +5448,55 @@ var OllamaClient = class {
3345
5448
  version = () => this.models.version();
3346
5449
  createBlob = (digest, data) => this.models.createBlob(digest, data);
3347
5450
  checkBlob = (digest) => this.models.checkBlob(digest);
5451
+ /**
5452
+ * Immediately unloads `model` from VRAM. Convenience alias for
5453
+ * {@link ModelsClient.unload} — see that method for the full
5454
+ * lifecycle contract.
5455
+ */
5456
+ unloadModel = (model) => this.models.unload(model);
5457
+ /**
5458
+ * Pre-loads and pins `model` into VRAM indefinitely. Convenience
5459
+ * alias for {@link ModelsClient.pin} — see that method for the
5460
+ * full lifecycle contract.
5461
+ */
5462
+ pinModel = (model) => this.models.pin(model);
5463
+ // --- System One (Wave 13) ---
5464
+ /**
5465
+ * Ollama's System One decision layer (`POST /v1/systemone`) — evaluates
5466
+ * 1–64 typed questions (choice, noul, or score) against a provided state
5467
+ * and returns typed answers with probabilities and confidence.
5468
+ *
5469
+ * Local-only; requires Ollama >= 0.35.0. The runtime enforces the
5470
+ * version constraint automatically (lazily fetching `/api/version` when
5471
+ * `enforceVersion` is `'auto'` or `'strict'`) and rejects cloud-mode
5472
+ * calls. Request size is checked client-side: 64 KiB without images,
5473
+ * 32 MiB with images.
5474
+ *
5475
+ * The generic `Q` parameter captures the caller's question map at
5476
+ * compile time, giving key-safe answer access:
5477
+ *
5478
+ * ```ts
5479
+ * const result = await ollama.systemOne({
5480
+ * model: 'tev1:4b',
5481
+ * state: { ticket: 'Customer was charged twice' },
5482
+ * questions: {
5483
+ * intent: { type: 'choice', instructions: '...', criteria: { ... } },
5484
+ * urgent: { type: 'noul', instructions: '...' },
5485
+ * },
5486
+ * });
5487
+ * result.answers.intent // ✓ SystemOneAnswer (choice)
5488
+ * result.answers.urgent // ✓ SystemOneAnswer (noul)
5489
+ * result.answers.typo // ✗ TypeScript error
5490
+ * ```
5491
+ *
5492
+ * For the low-level generated API (without the generic wrapper), use
5493
+ * `client.native.systemOne(request)` directly.
5494
+ */
5495
+ async systemOne(request) {
5496
+ return this.native.systemOne(
5497
+ request
5498
+ );
5499
+ }
3348
5500
  // --- Web Endpoints ---
3349
5501
  /**
3350
5502
  * Ollama's hosted web search tool (`POST https://ollama.com/api/web_search`) — a fixed
@@ -3377,6 +5529,63 @@ var OllamaClient = class {
3377
5529
  req
3378
5530
  );
3379
5531
  }
5532
+ /**
5533
+ * Ollama Cloud usage statistics (`GET https://ollama.com/api/usage`) — request counts,
5534
+ * USD spend, and token totals (including cached input tokens) for cloud inference,
5535
+ * web search, and web fetch, bucketed by hour (`range: '24h'`) or day (`'7d'`/`'30d'`).
5536
+ *
5537
+ * See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
5538
+ * behavior, which applies identically here. Query parameters mirror the official
5539
+ * endpoint: `range` (`'24h' | '7d' | '30d'`, server default `'7d'`) and `scope`
5540
+ * (`'self' | 'team'`, server default `'self'`; team scope requires a team admin).
5541
+ * Omitted options are left unset so the server applies its own defaults.
5542
+ *
5543
+ * ```ts
5544
+ * const usage = await client.usage({ range: '24h' });
5545
+ * console.log(usage.totals.request_count, usage.totals.usage_usd);
5546
+ * for (const bucket of usage.buckets) {
5547
+ * if (bucket.partial) continue; // current hour, still in progress
5548
+ * // ...
5549
+ * }
5550
+ * ```
5551
+ */
5552
+ usage(req = {}) {
5553
+ const params = new URLSearchParams();
5554
+ if (req.range !== void 0) params.set("range", req.range);
5555
+ if (req.scope !== void 0) params.set("scope", req.scope);
5556
+ const query = params.toString();
5557
+ return this.executeCloudRequest(
5558
+ (http, signal) => http.request({
5559
+ path: query ? `/api/usage?${query}` : "/api/usage",
5560
+ method: "GET",
5561
+ signal
5562
+ }),
5563
+ req
5564
+ );
5565
+ }
5566
+ /**
5567
+ * Ollama Cloud balance (`GET https://ollama.com/api/balance`) — remaining included
5568
+ * and purchased usage credits. The included balance is either a plan-period credit
5569
+ * object (`balance_usd`/`allowance_usd`/`period`) or, on legacy plans, session/weekly
5570
+ * percentage limits; the shape is discriminated at runtime by the server response.
5571
+ * See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
5572
+ * behavior, which applies identically here.
5573
+ *
5574
+ * ```ts
5575
+ * const balance = await client.balance();
5576
+ * if ('balance_usd' in balance.included) {
5577
+ * console.log(balance.included.balance_usd, balance.purchased.balance_usd);
5578
+ * } else {
5579
+ * console.log(balance.included.session.remaining_percent, '% of session limit left');
5580
+ * }
5581
+ * ```
5582
+ */
5583
+ balance(req = {}) {
5584
+ return this.executeCloudRequest(
5585
+ (http, signal) => http.request({ path: "/api/balance", method: "GET", signal }),
5586
+ req
5587
+ );
5588
+ }
3380
5589
  /**
3381
5590
  * Runs `operation` against the fixed Ollama Cloud host with this client's default
3382
5591
  * timeout and retry policy — the same primitives `executeWithFailover` uses, minus the
@@ -3480,6 +5689,91 @@ var OllamaClient = class {
3480
5689
  }
3481
5690
  };
3482
5691
 
5692
+ // src/transport/fetch-with-backoff.ts
5693
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 503]);
5694
+ var DEFAULT_FETCH_BACKOFF_CONFIG = {
5695
+ maxRetries: 3
5696
+ };
5697
+ function sleep2(ms, signal) {
5698
+ return new Promise((resolve, reject) => {
5699
+ if (signal?.aborted) {
5700
+ reject(
5701
+ Object.assign(new Error("fetchWithBackoff aborted during backoff sleep"), {
5702
+ name: "AbortError"
5703
+ })
5704
+ );
5705
+ return;
5706
+ }
5707
+ const timer = setTimeout(() => {
5708
+ signal?.removeEventListener("abort", onAbort);
5709
+ resolve();
5710
+ }, ms);
5711
+ const onAbort = () => {
5712
+ clearTimeout(timer);
5713
+ signal?.removeEventListener("abort", onAbort);
5714
+ reject(
5715
+ Object.assign(new Error("fetchWithBackoff aborted during backoff sleep"), {
5716
+ name: "AbortError"
5717
+ })
5718
+ );
5719
+ };
5720
+ signal?.addEventListener("abort", onAbort, { once: true });
5721
+ });
5722
+ }
5723
+ async function fetchWithBackoff(input, init, config = {}) {
5724
+ const fetchImpl = config.fetch ?? globalThis.fetch;
5725
+ if (typeof fetchImpl !== "function") {
5726
+ throw new Error(
5727
+ "fetchWithBackoff: no global fetch is available. Pass a fetch implementation via config.fetch, or run in Node.js >= 18 / a browser-like environment."
5728
+ );
5729
+ }
5730
+ const maxRetries = config.maxRetries ?? DEFAULT_FETCH_BACKOFF_CONFIG.maxRetries;
5731
+ const backoff = config.backoff ?? DEFAULT_BACKOFF;
5732
+ const signal = config.signal;
5733
+ let lastError;
5734
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
5735
+ if (signal?.aborted) {
5736
+ throw Object.assign(new Error("fetchWithBackoff aborted"), { name: "AbortError" });
5737
+ }
5738
+ try {
5739
+ const response = await fetchImpl(input, {
5740
+ ...init,
5741
+ ...signal !== void 0 ? { signal } : {}
5742
+ });
5743
+ if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries) {
5744
+ const delayMs = calculateBackoff(attempt, backoff);
5745
+ config.onRetry?.(attempt, response.status, delayMs);
5746
+ await sleep2(delayMs, signal);
5747
+ continue;
5748
+ }
5749
+ return response;
5750
+ } catch (err) {
5751
+ lastError = err;
5752
+ if (signal?.aborted || err instanceof Error && err.name === "AbortError") {
5753
+ throw err;
5754
+ }
5755
+ if (attempt < maxRetries) {
5756
+ const delayMs = calculateBackoff(attempt, backoff);
5757
+ config.onRetry?.(attempt, 0, delayMs);
5758
+ await sleep2(delayMs, signal);
5759
+ continue;
5760
+ }
5761
+ }
5762
+ }
5763
+ throw lastError ?? new Error("fetchWithBackoff: exhausted retry attempts");
5764
+ }
5765
+
5766
+ // src/done-reason.ts
5767
+ var KNOWN_DONE_REASONS = /* @__PURE__ */ new Set([
5768
+ "stop",
5769
+ "length",
5770
+ "load",
5771
+ "unload"
5772
+ ]);
5773
+ function isKnownDoneReason(value) {
5774
+ return value !== void 0 && value !== null && KNOWN_DONE_REASONS.has(value);
5775
+ }
5776
+
3483
5777
  // src/streaming/adapters.ts
3484
5778
  function toTextStream(stream) {
3485
5779
  const iterator = stream[Symbol.asyncIterator]();
@@ -3935,9 +6229,9 @@ function validate(value, schema, path) {
3935
6229
  ["exclusiveMinimum", (n, b) => n > b, "must be greater than"],
3936
6230
  ["exclusiveMaximum", (n, b) => n < b, "must be less than"]
3937
6231
  ];
3938
- for (const [keyword, check, phrase] of numericRules) {
6232
+ for (const [keyword, check2, phrase] of numericRules) {
3939
6233
  const bound = s[keyword];
3940
- if (typeof bound === "number" && !check(value, bound)) issues.push({ path, message: label(path) + " " + phrase + " " + bound });
6234
+ if (typeof bound === "number" && !check2(value, bound)) issues.push({ path, message: label(path) + " " + phrase + " " + bound });
3941
6235
  }
3942
6236
  const multipleOf = s["multipleOf"];
3943
6237
  if (typeof multipleOf === "number" && multipleOf > 0) {
@@ -4198,8 +6492,6 @@ var McpBridge = class _McpBridge {
4198
6492
  this.client = client;
4199
6493
  this.options = options;
4200
6494
  }
4201
- client;
4202
- options;
4203
6495
  static toOllamaTools(tools, options = {}) {
4204
6496
  const prefix = options.namePrefix ?? "";
4205
6497
  return tools.map((tool) => ({
@@ -4496,6 +6788,13 @@ var Agent = class {
4496
6788
  }
4497
6789
  };
4498
6790
 
4499
- export { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_REQUEST_MODEL, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_USAGE_INPUT_TOKENS, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_OLLAMA_AGENT_ITERATION, ATTR_OLLAMA_AGENT_MAX_ITERATIONS, ATTR_OLLAMA_ENDPOINT_ATTEMPT, ATTR_OLLAMA_ENDPOINT_NAME, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_URL_FULL, Agent, AnthropicCompatClient, AnthropicMessagesStream, DEFAULT_BACKOFF, DEFAULT_BASE_URL, DEFAULT_FAILOVER_CODES, DEFAULT_RETRY_CONFIG, DEFAULT_TIMEOUT_MS, EndpointRegistry, GEN_AI_SYSTEM_OLLAMA, HttpClient, McpBridge, ModelsClient, NANOS_PER_MS, NANOS_PER_SECOND, NOOP_LOGGER, OLLAMA_CLOUD_BASE_URL, OllamaAbortError, OllamaAgentMaxIterationsError, OllamaAgentMaxToolCallsError, OllamaAuthError, OllamaClient, OllamaClientError, OllamaGenericClientError, OllamaIncompatibleModelError, OllamaMcpError, OllamaModelRoutingError, OllamaNetworkError, OllamaNotFoundError, OllamaQuotaExceededError, OllamaRateLimitError, OllamaRequestValidationError, OllamaServerError, OllamaSkillInvalidError, OllamaSkillNotFoundError, OllamaStream, OllamaTimeoutError, OllamaToolTimeoutError, OllamaToolValidationError, OllamaUnsupportedCapabilityError, OpenAIChatCompletionStream, OpenAICompatClient, OpenAICompletionStream, OpenAIResponsesStream, OpenAIResponsesStreamError, QuotaManager, ToolRegistry, addSpanEvent, applySkill, calculateBackoff, checkEndpointHealth, composeMiddleware, createConsoleLogger, createOllamaCloudFreeTierQuota, createTimeoutSignal, defineTool, detectModelCapabilities, encodeImage, extractUsage, inferRuntimeMode, listAllMcpTools, listAvailableModels, loadMcpTools, mapError, normalizeChatStream, normalizeGenerateStream, normalizeProgressStream, parseFrontmatter, parseNdjsonStream, parseSseStream, parseStructuredOutput, registerMcpTools, resolveCredentialEndpoints, setSpanError, toDataStream, toResponse, toTextStream, withRetry, withSpan, zodToJsonSchema };
6791
+ // src/system-one.ts
6792
+ var MAX_SYSTEM_ONE_IMAGES_BYTES = 32 * 1024 * 1024;
6793
+ var MAX_SYSTEM_ONE_REQUEST_BYTES = 64 * 1024;
6794
+ function estimateSystemOneRequestBytes(request) {
6795
+ return new TextEncoder().encode(JSON.stringify(request)).byteLength;
6796
+ }
6797
+
6798
+ export { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_REQUEST_MODEL, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_USAGE_INPUT_TOKENS, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_OLLAMA_AGENT_ITERATION, ATTR_OLLAMA_AGENT_MAX_ITERATIONS, ATTR_OLLAMA_ENDPOINT_ATTEMPT, ATTR_OLLAMA_ENDPOINT_NAME, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_URL_FULL, Agent, AnthropicCompatClient, AnthropicMessagesStream, ConversationSession, DEFAULT_BACKOFF, DEFAULT_BASE_URL, DEFAULT_CONTEXT_WARNING_THRESHOLD, DEFAULT_FAILOVER_CODES, DEFAULT_FETCH_BACKOFF_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_TIMEOUT_MS, EndpointRegistry, GEN_AI_SYSTEM_OLLAMA, HttpClient, IMAGE_TOKEN_ESTIMATE, KEEP_ALIVE_INDEFINITE, KEEP_ALIVE_UNLOAD, KNOWN_DONE_REASONS, MAX_SYSTEM_ONE_IMAGES_BYTES, MAX_SYSTEM_ONE_REQUEST_BYTES, McpBridge, ModelsClient, NANOS_PER_MS, NANOS_PER_SECOND, NOOP_LOGGER, OLLAMA_CLOUD_BASE_URL, OLLAMA_FALLBACK_CONTEXT_LENGTH, OllamaAbortError, OllamaAgentMaxIterationsError, OllamaAgentMaxToolCallsError, OllamaAuthError, OllamaBadGatewayError, OllamaClient, OllamaClientError, OllamaGenericClientError, OllamaIncompatibleModelError, OllamaMcpError, OllamaModelRoutingError, OllamaNetworkError, OllamaNotFoundError, OllamaOverloadedError, OllamaQuotaExceededError, OllamaRateLimitError, OllamaRequestTooLargeError, OllamaRequestValidationError, OllamaResponseValidationError, OllamaServerError, OllamaServerVersionUnknownError, OllamaSkillInvalidError, OllamaSkillNotFoundError, OllamaStream, OllamaStreamError, OllamaTimeoutError, OllamaToolTimeoutError, OllamaToolValidationError, OllamaUnsupportedCapabilityError, OpenAIChatCompletionStream, OpenAICompatClient, OpenAICompletionStream, OpenAIResponsesStream, OpenAIResponsesStreamError, QuotaManager, RETRYABLE_STATUS_CODES, ResponsesModule, ToolCallAccumulator, ToolRegistry, addSpanEvent, applySkill, calculateBackoff, checkChatContext, checkEndpointHealth, checkGenerateContext, composeMiddleware, contextWarningMessage, createConsoleLogger, createDecision, createOllamaCloudFreeTierQuota, createTimeoutSignal, defineTool, detectModelCapabilities, encodeImage, estimateChatRequestTokens, estimateGenerateRequestTokens, estimateSystemOneRequestBytes, estimateTokens, extractUsage, fetchWithBackoff, imageStringNeedsResolution, inferRuntimeMode, isKeepAliveSugar, isKnownDoneReason, isSameToolCall, listAllMcpTools, listAvailableModels, loadMcpTools, mapError, mergeToolCall, mergeToolCallArgumentsString, mergeToolCallArrays, normalizeChatStream, normalizeGenerateStream, normalizeKeepAlive, normalizeProgressStream, parseFrontmatter, parseNdjsonStream, parseSseStream, parseStructuredOutput, registerMcpTools, resolveCredentialEndpoints, resolveImageInput, setSpanError, toDataStream, toResponse, toTextStream, withRetry, withSpan, zodToJsonSchema };
4500
6799
  //# sourceMappingURL=index.js.map
4501
6800
  //# sourceMappingURL=index.js.map