@nemesis-oss/ollama-sdk 1.8.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 +158 -0
  2. package/README.md +181 -10
  3. package/dist/{anthropic-B_UXplNF.d.ts → anthropic-5oYIRS6D.d.ts} +4 -2
  4. package/dist/{anthropic-CVDFy_a0.d.cts → anthropic-BIh45UXj.d.cts} +4 -2
  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 +42 -3
  8. package/dist/generated-api.cjs.map +1 -1
  9. package/dist/generated-api.d.cts +96 -6
  10. package/dist/generated-api.d.ts +96 -6
  11. package/dist/generated-api.js +41 -4
  12. package/dist/generated-api.js.map +1 -1
  13. package/dist/generated-models-schemas.cjs +62 -2
  14. package/dist/generated-models-schemas.cjs.map +1 -1
  15. package/dist/generated-models-schemas.d.cts +108 -3
  16. package/dist/generated-models-schemas.d.ts +108 -3
  17. package/dist/generated-models-schemas.js +56 -3
  18. package/dist/generated-models-schemas.js.map +1 -1
  19. package/dist/generated-runtime.cjs +105 -5
  20. package/dist/generated-runtime.cjs.map +1 -1
  21. package/dist/generated-runtime.d.cts +2 -2
  22. package/dist/generated-runtime.d.ts +2 -2
  23. package/dist/generated-runtime.js +105 -5
  24. package/dist/generated-runtime.js.map +1 -1
  25. package/dist/index.cjs +1384 -51
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +1312 -14
  28. package/dist/index.d.ts +1312 -14
  29. package/dist/index.js +1352 -52
  30. package/dist/index.js.map +1 -1
  31. package/dist/mcp/tools.json +43 -24
  32. package/dist/mcp-generated.cjs +43 -1
  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 +43 -1
  37. package/dist/mcp-generated.js.map +1 -1
  38. package/dist/{runtime-C6UYtBRB.d.ts → runtime-C3rfpkZw.d.ts} +69 -2
  39. package/dist/{runtime-DTmUeU6K.d.cts → runtime-D2W9TfCJ.d.cts} +69 -2
  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 +8 -5
package/dist/index.cjs CHANGED
@@ -48,6 +48,47 @@ var __callDispose = (stack, error, hasError) => {
48
48
  return next();
49
49
  };
50
50
 
51
+ // src/transport/host.ts
52
+ var DEFAULT_PORT = "11434";
53
+ var SCHEME_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
54
+ function normalizeBaseUrl(host) {
55
+ const trimmed = host.trim();
56
+ if (trimmed === "") return trimmed;
57
+ const implicitScheme = !SCHEME_RE.test(trimmed);
58
+ const withHost = trimmed.startsWith(":") ? `127.0.0.1${trimmed}` : trimmed;
59
+ const candidate = implicitScheme ? `http://${withHost}` : withHost;
60
+ let url;
61
+ try {
62
+ url = new URL(candidate);
63
+ } catch {
64
+ return trimmed.replace(/\/+$/, "");
65
+ }
66
+ let protocol = url.protocol;
67
+ let port = url.port;
68
+ if (implicitScheme) {
69
+ if (port === "443") {
70
+ protocol = "https:";
71
+ port = "";
72
+ } else if (port === "") {
73
+ port = DEFAULT_PORT;
74
+ }
75
+ }
76
+ const authority = `${url.hostname}${port !== "" ? `:${port}` : ""}`;
77
+ return `${protocol}//${authority}${url.pathname}`.replace(/\/+$/, "");
78
+ }
79
+ function isEnvApiKeyHost(baseUrl) {
80
+ let hostname;
81
+ try {
82
+ hostname = new URL(normalizeBaseUrl(baseUrl)).hostname.toLowerCase();
83
+ } catch {
84
+ return false;
85
+ }
86
+ if (hostname === "ollama.com" || hostname.endsWith(".ollama.com")) return true;
87
+ if (hostname === "localhost" || hostname.endsWith(".localhost")) return true;
88
+ if (hostname === "[::1]" || hostname === "::1" || hostname === "0.0.0.0") return true;
89
+ return /^127(?:\.\d{1,3}){3}$/.test(hostname);
90
+ }
91
+
51
92
  // src/config.ts
52
93
  var DEFAULT_BASE_URL = "http://localhost:11434";
53
94
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -56,11 +97,16 @@ function readEnv(name) {
56
97
  return typeof process !== "undefined" && process.env ? process.env[name] : void 0;
57
98
  }
58
99
  function resolveBaseUrl(configBaseUrl) {
59
- return configBaseUrl ?? readEnv("OLLAMA_HOST") ?? DEFAULT_BASE_URL;
100
+ return normalizeBaseUrl(configBaseUrl ?? readEnv("OLLAMA_HOST") ?? DEFAULT_BASE_URL);
60
101
  }
61
102
  function resolveApiKey(configApiKey) {
62
103
  return configApiKey ?? readEnv("OLLAMA_API_KEY");
63
104
  }
105
+ function resolveEndpointApiKey(configApiKey, baseUrl) {
106
+ if (configApiKey !== void 0) return configApiKey;
107
+ const envKey = readEnv("OLLAMA_API_KEY");
108
+ return envKey !== void 0 && isEnvApiKeyHost(baseUrl) ? envKey : void 0;
109
+ }
64
110
  function resolveCredentialEndpoints(config) {
65
111
  if (config.credentials === void 0) return [];
66
112
  const modelsByCredential = /* @__PURE__ */ new Map();
@@ -93,6 +139,18 @@ var DEFAULT_FAILOVER_CODES = [
93
139
  "network_error",
94
140
  "timeout",
95
141
  "server_error",
142
+ // HTTP 503 — server queue saturated (OLLAMA_MAX_QUEUE exceeded).
143
+ // The new OllamaOverloadedError specialization is retryable: the
144
+ // queue will eventually drain, and a different candidate endpoint
145
+ // may have capacity right now. Listed separately from
146
+ // 'server_error' because OllamaOverloadedError extends
147
+ // OllamaClientError (NOT OllamaServerError) — see
148
+ // src/errors.ts for the rationale.
149
+ "overloaded",
150
+ // HTTP 502 — cloud model could not be reached by the Ollama Cloud
151
+ // proxy. Fail over to a different model/endpoint rather than
152
+ // retrying the same one.
153
+ "bad_gateway",
96
154
  "rate_limited",
97
155
  "auth_error",
98
156
  // A candidate endpoint rejected the request pre-flight for a known-unsupported
@@ -161,6 +219,24 @@ var OllamaServerError = class extends OllamaClientError {
161
219
  super(message, { ...options, code: "server_error", retryable: true });
162
220
  }
163
221
  };
222
+ var OllamaOverloadedError = class extends OllamaClientError {
223
+ constructor(message, options) {
224
+ super(message, { ...options, code: "overloaded", status: 503, retryable: true });
225
+ }
226
+ };
227
+ var OllamaBadGatewayError = class extends OllamaClientError {
228
+ constructor(message, options) {
229
+ super(message, { ...options, code: "bad_gateway", status: 502, retryable: true });
230
+ }
231
+ };
232
+ var OllamaStreamError = class extends OllamaClientError {
233
+ /** Content accumulated before the in-band error frame, or '' if none. */
234
+ partialContent;
235
+ constructor(message, options) {
236
+ super(message, { ...options, code: "stream_error", retryable: false });
237
+ this.partialContent = options?.partialContent ?? "";
238
+ }
239
+ };
164
240
  var OllamaAbortError = class extends OllamaClientError {
165
241
  constructor(message, options) {
166
242
  super(message, { ...options, code: "aborted", retryable: false });
@@ -310,6 +386,12 @@ function statusToError(status, message, options) {
310
386
  if (status === 429) {
311
387
  return new OllamaRateLimitError(message, { ...options, status });
312
388
  }
389
+ if (status === 502) {
390
+ return new OllamaBadGatewayError(message, { ...options });
391
+ }
392
+ if (status === 503) {
393
+ return new OllamaOverloadedError(message, { ...options });
394
+ }
313
395
  if (status >= 500) {
314
396
  return new OllamaServerError(message, { ...options, status });
315
397
  }
@@ -580,6 +662,75 @@ function composeMiddleware(middlewares, finalHandler) {
580
662
  };
581
663
  }
582
664
 
665
+ // src/vision.ts
666
+ var DATA_URI_PATTERN = /^data:image\/[a-z0-9.+-]+;base64,/i;
667
+ var IMAGE_FILE_EXTENSION_PATTERN = /\.(png|jpe?g|webp|gif|bmp|tiff?|avif)$/i;
668
+ function imageStringNeedsResolution(input) {
669
+ return DATA_URI_PATTERN.test(input) || input.startsWith("http://") || input.startsWith("https://") || IMAGE_FILE_EXTENSION_PATTERN.test(input);
670
+ }
671
+ function hasNodeFs() {
672
+ return typeof process !== "undefined" && process !== null && typeof process.versions?.node === "string";
673
+ }
674
+ var NODE_FS_MODULE = "node:fs";
675
+ async function readFileAsBase64(path) {
676
+ const { promises: fs } = await import(NODE_FS_MODULE);
677
+ const data = await fs.readFile(path);
678
+ return data.toString("base64");
679
+ }
680
+ async function fetchUrlAsBase64(url, signal) {
681
+ const res = await fetch(url, signal !== void 0 ? { signal } : void 0);
682
+ if (!res.ok) {
683
+ throw new OllamaClientError(
684
+ `Failed to fetch image from URL "${url}": HTTP ${res.status} ${res.statusText}.`,
685
+ { code: "image_fetch_failed" }
686
+ );
687
+ }
688
+ const arrayBuffer = await res.arrayBuffer();
689
+ return encodeBytesBase64(new Uint8Array(arrayBuffer));
690
+ }
691
+ function encodeBytesBase64(bytes) {
692
+ const bufferCtor = globalThis.Buffer;
693
+ if (bufferCtor !== void 0) return bufferCtor.from(bytes).toString("base64");
694
+ const CHUNK_SIZE = 32768;
695
+ let binary = "";
696
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
697
+ binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK_SIZE));
698
+ }
699
+ return btoa(binary);
700
+ }
701
+ async function resolveImageInput(input, signal) {
702
+ if (typeof input !== "string") {
703
+ return encodeBytesBase64(input instanceof Uint8Array ? input : new Uint8Array(input));
704
+ }
705
+ if (DATA_URI_PATTERN.test(input)) {
706
+ return input.replace(DATA_URI_PATTERN, "").trim();
707
+ }
708
+ if (input.startsWith("http://") || input.startsWith("https://")) {
709
+ return fetchUrlAsBase64(input, signal);
710
+ }
711
+ if (IMAGE_FILE_EXTENSION_PATTERN.test(input)) {
712
+ if (!hasNodeFs()) {
713
+ throw new OllamaClientError(
714
+ `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.`,
715
+ { code: "vision_path_unsupported_runtime" }
716
+ );
717
+ }
718
+ try {
719
+ return await readFileAsBase64(input);
720
+ } catch {
721
+ return input;
722
+ }
723
+ }
724
+ return input.trim();
725
+ }
726
+ async function resolveImages(images, signal) {
727
+ if (images === void 0) return void 0;
728
+ if (images.every((image) => typeof image === "string" && !imageStringNeedsResolution(image))) {
729
+ return images;
730
+ }
731
+ return Promise.all(images.map((image) => resolveImageInput(image, signal)));
732
+ }
733
+
583
734
  // src/utils.ts
584
735
  async function encodeImage(image) {
585
736
  if (typeof image === "string") return image;
@@ -592,18 +743,22 @@ async function encodeImage(image) {
592
743
  }
593
744
  return btoa(binary);
594
745
  }
595
- async function encodeImages(images) {
596
- if (images === void 0) return void 0;
597
- return Promise.all(images.map((image) => encodeImage(image)));
746
+ async function encodeImages(images, signal) {
747
+ return resolveImages(images, signal);
598
748
  }
599
- async function withEncodedImages(req) {
600
- if (!req.images?.some((image) => image instanceof Uint8Array)) return req;
601
- return { ...req, images: await encodeImages(req.images) };
749
+ async function withEncodedImages(req, signal) {
750
+ const needsResolution = req.images?.some(
751
+ (image) => typeof image !== "string" || imageStringNeedsResolution(image)
752
+ );
753
+ if (!needsResolution) return req;
754
+ return { ...req, images: await encodeImages(req.images ?? [], signal) };
602
755
  }
603
- async function withEncodedMessageImages(messages) {
604
- if (!messages.some((message) => message.images?.some((image) => image instanceof Uint8Array)))
605
- return messages;
606
- return Promise.all(messages.map((message) => withEncodedImages(message)));
756
+ async function withEncodedMessageImages(messages, signal) {
757
+ const needsResolution = messages.some(
758
+ (message) => message.images?.some((image) => typeof image !== "string" || imageStringNeedsResolution(image))
759
+ );
760
+ if (!needsResolution) return messages;
761
+ return Promise.all(messages.map((message) => withEncodedImages(message, signal)));
607
762
  }
608
763
  function disposableTimer(timer) {
609
764
  return {
@@ -632,6 +787,12 @@ function disposableSpan(span) {
632
787
  }
633
788
 
634
789
  // src/streaming/ndjson.ts
790
+ function isInBandErrorFrame(value) {
791
+ if (typeof value !== "object" || value === null) return false;
792
+ const record = value;
793
+ if (typeof record.error !== "string" || record.error.length === 0) return false;
794
+ return !("model" in record) && !("response" in record) && !("message" in record) && !("status" in record) && !("done" in record);
795
+ }
635
796
  async function* parseNdjsonStream(stream) {
636
797
  var _stack = [];
637
798
  try {
@@ -648,17 +809,34 @@ async function* parseNdjsonStream(stream) {
648
809
  for (const line of lines) {
649
810
  const trimmed = line.trim();
650
811
  if (!trimmed) continue;
812
+ let json;
651
813
  try {
652
- yield JSON.parse(trimmed);
814
+ json = JSON.parse(trimmed);
653
815
  } catch (err) {
654
816
  throw new OllamaGenericClientError(`Failed to parse stream JSON chunk: ${trimmed}`, {
655
817
  cause: err
656
818
  });
657
819
  }
820
+ if (isInBandErrorFrame(json)) {
821
+ throw new OllamaStreamError(json.error);
822
+ }
823
+ yield json;
658
824
  }
659
825
  }
660
826
  if (buffer.trim()) {
661
- yield JSON.parse(buffer.trim());
827
+ const trimmed = buffer.trim();
828
+ let json;
829
+ try {
830
+ json = JSON.parse(trimmed);
831
+ } catch (err) {
832
+ throw new OllamaGenericClientError(`Failed to parse stream JSON chunk: ${trimmed}`, {
833
+ cause: err
834
+ });
835
+ }
836
+ if (isInBandErrorFrame(json)) {
837
+ throw new OllamaStreamError(json.error);
838
+ }
839
+ yield json;
662
840
  }
663
841
  } catch (_) {
664
842
  var _error = _, _hasError = true;
@@ -869,7 +1047,7 @@ var HttpClient = class {
869
1047
  onLifecycleEvent;
870
1048
  requestId;
871
1049
  constructor(options) {
872
- this.baseUrl = options.baseUrl.replace(/\/+$/, "");
1050
+ this.baseUrl = normalizeBaseUrl(options.baseUrl);
873
1051
  this.apiKey = options.apiKey;
874
1052
  this.defaultHeaders = options.headers ?? {};
875
1053
  this.fetchImpl = options.fetch ?? globalThis.fetch;
@@ -1258,7 +1436,7 @@ async function checkEndpointHealth(endpoint, fetchImpl, timeoutMs = 5e3, middlew
1258
1436
  function inferRuntimeMode(baseUrl) {
1259
1437
  try {
1260
1438
  const url = new URL(baseUrl);
1261
- const host = url.hostname.toLowerCase();
1439
+ const host = url.hostname.toLowerCase().replace(/^\[|\]$/g, "");
1262
1440
  if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.startsWith("192.168.") || host.startsWith("10.")) {
1263
1441
  return "local";
1264
1442
  }
@@ -1276,6 +1454,63 @@ function extractContextLength(modelInfo) {
1276
1454
  }
1277
1455
  return void 0;
1278
1456
  }
1457
+ function extractModelFamily(showRes) {
1458
+ if (showRes.model_info) {
1459
+ for (const [key, value] of Object.entries(showRes.model_info)) {
1460
+ if (key.endsWith(".architecture") && typeof value === "string" && value.length > 0) {
1461
+ return value.toLowerCase();
1462
+ }
1463
+ }
1464
+ }
1465
+ if (showRes.details?.family && showRes.details.family.length > 0) {
1466
+ return showRes.details.family.toLowerCase();
1467
+ }
1468
+ const families = showRes.details?.families;
1469
+ if (families && families.length > 0) {
1470
+ const first = families[0];
1471
+ if (typeof first === "string" && first.length > 0) return first.toLowerCase();
1472
+ }
1473
+ return void 0;
1474
+ }
1475
+ var PARALLEL_TOOL_CALL_LOCAL_FAMILIES = /* @__PURE__ */ new Set([
1476
+ "qwen2",
1477
+ "qwen2.5",
1478
+ "qwen3",
1479
+ "llama3.1",
1480
+ "llama3.2",
1481
+ "llama3.3",
1482
+ "llama4",
1483
+ "mistral",
1484
+ "mixtral",
1485
+ "hermes",
1486
+ "command-r",
1487
+ "command-r-plus"
1488
+ ]);
1489
+ var PARALLEL_TOOL_CALL_NAME_HINTS = /* @__PURE__ */ new Set([
1490
+ "tool-use",
1491
+ "tooluse",
1492
+ "function-call",
1493
+ "functioncall",
1494
+ "instruct",
1495
+ // Most "instruct" variants of tool-capable families ship with parallel tool calls
1496
+ "hermes",
1497
+ "command-r"
1498
+ ]);
1499
+ function inferLocalParallelToolCalls(model, family) {
1500
+ const modelLower = model.toLowerCase();
1501
+ if (family !== void 0 && PARALLEL_TOOL_CALL_LOCAL_FAMILIES.has(family)) {
1502
+ return "yes";
1503
+ }
1504
+ for (const hint of PARALLEL_TOOL_CALL_NAME_HINTS) {
1505
+ if (modelLower.includes(hint)) {
1506
+ return "yes";
1507
+ }
1508
+ }
1509
+ return "unknown";
1510
+ }
1511
+ function inferCloudParallelToolCalls(supportsTools) {
1512
+ return supportsTools ? "yes" : "unknown";
1513
+ }
1279
1514
  async function detectModelCapabilities(http, model, signal) {
1280
1515
  const showRes = await http.request({
1281
1516
  path: "/api/show",
@@ -1285,10 +1520,16 @@ async function detectModelCapabilities(http, model, signal) {
1285
1520
  const reported = showRes.capabilities ?? [];
1286
1521
  const reportedSet = new Set(reported.map((c) => c.toLowerCase()));
1287
1522
  const contextLength = extractContextLength(showRes.model_info);
1523
+ const supportsTools = reportedSet.has("tools");
1524
+ const runtimeMode = inferRuntimeMode(http.baseUrl);
1525
+ const parallelToolCalls = runtimeMode === "cloud" ? inferCloudParallelToolCalls(supportsTools) : runtimeMode === "local" ? inferLocalParallelToolCalls(model, extractModelFamily(showRes)) : (
1526
+ // unknown runtime mode — can't even tell if we're cloud or local
1527
+ "unknown"
1528
+ );
1288
1529
  return {
1289
1530
  model,
1290
1531
  reported,
1291
- supportsTools: reportedSet.has("tools"),
1532
+ supportsTools,
1292
1533
  supportsVision: reportedSet.has("vision"),
1293
1534
  supportsEmbedding: reportedSet.has("embedding"),
1294
1535
  supportsCompletion: reportedSet.has("completion") || !reportedSet.has("embedding"),
@@ -1296,7 +1537,8 @@ async function detectModelCapabilities(http, model, signal) {
1296
1537
  ...showRes.thinking !== void 0 ? { thinking: showRes.thinking } : {},
1297
1538
  ...contextLength !== void 0 ? { contextLength } : {},
1298
1539
  supportsStreaming: true,
1299
- supportsStructuredOutputRequest: inferRuntimeMode(http.baseUrl) !== "cloud"
1540
+ supportsStructuredOutputRequest: runtimeMode !== "cloud",
1541
+ parallelToolCalls
1300
1542
  };
1301
1543
  }
1302
1544
  async function listAvailableModels(http) {
@@ -1441,7 +1683,131 @@ function ensureToolCallIds(toolCalls) {
1441
1683
  return toolCalls.map((tc) => tc.id !== void 0 ? tc : { ...tc, id: generateToolCallId() });
1442
1684
  }
1443
1685
 
1686
+ // src/tools/tool-call-accumulator.ts
1687
+ function isSameToolCall(a, b, aIndex, bIndex) {
1688
+ if (a.id !== void 0 && b.id !== void 0) {
1689
+ return a.id === b.id;
1690
+ }
1691
+ if (aIndex !== void 0 && bIndex !== void 0) {
1692
+ return aIndex === bIndex;
1693
+ }
1694
+ return false;
1695
+ }
1696
+ function mergeToolCall(earlier, later) {
1697
+ const earlierFn = earlier.function;
1698
+ const laterFn = later.function;
1699
+ const earlierArgs = earlierFn.arguments;
1700
+ const laterArgs = laterFn.arguments;
1701
+ const mergedFn = {
1702
+ name: laterFn.name || earlierFn.name,
1703
+ arguments: earlierArgs !== void 0 && laterArgs !== void 0 ? { ...earlierArgs, ...laterArgs } : laterArgs ?? earlierArgs ?? {}
1704
+ };
1705
+ return {
1706
+ // id: prefer the later one if present, else the earlier one
1707
+ ...later.id !== void 0 ? { id: later.id } : earlier.id !== void 0 ? { id: earlier.id } : {},
1708
+ function: mergedFn
1709
+ };
1710
+ }
1711
+ function mergeToolCallArgumentsString(earlier, later) {
1712
+ const earlierArgs = earlier.function.arguments;
1713
+ const laterArgs = later.function.arguments;
1714
+ const concatenated = (typeof earlierArgs === "string" ? earlierArgs : "") + (typeof laterArgs === "string" ? laterArgs : "");
1715
+ return {
1716
+ ...later.id !== void 0 ? { id: later.id } : earlier.id !== void 0 ? { id: earlier.id } : {},
1717
+ function: {
1718
+ name: later.function.name || earlier.function.name,
1719
+ arguments: concatenated
1720
+ }
1721
+ };
1722
+ }
1723
+ var ToolCallAccumulator = class {
1724
+ entries = [];
1725
+ /**
1726
+ * Process a chunk's `tool_calls` array. Entries that match an
1727
+ * existing accumulated entry (by id or array position) are merged;
1728
+ * new entries are appended.
1729
+ *
1730
+ * Returns the current accumulated state (a shallow copy — entries
1731
+ * themselves are NOT deep-cloned, so callers that mutate
1732
+ * `function.arguments` on a returned entry will mutate the
1733
+ * accumulator's internal state. Use {@link snapshot} for an
1734
+ * explicitly read-only view, or deep-clone the result if you need
1735
+ * full isolation).
1736
+ */
1737
+ process(toolCalls) {
1738
+ if (!toolCalls || toolCalls.length === 0) {
1739
+ return [...this.entries];
1740
+ }
1741
+ for (let i = 0; i < toolCalls.length; i++) {
1742
+ const incoming = toolCalls[i];
1743
+ if (!incoming) continue;
1744
+ let matchedIndex = -1;
1745
+ for (let j = 0; j < this.entries.length; j++) {
1746
+ const existing = this.entries[j];
1747
+ if (existing && isSameToolCall(existing, incoming, j, i)) {
1748
+ matchedIndex = j;
1749
+ break;
1750
+ }
1751
+ }
1752
+ if (matchedIndex >= 0) {
1753
+ this.entries[matchedIndex] = mergeToolCall(this.entries[matchedIndex], incoming);
1754
+ } else {
1755
+ this.entries.push(incoming);
1756
+ }
1757
+ }
1758
+ return [...this.entries];
1759
+ }
1760
+ /**
1761
+ * Returns the final accumulated tool calls. Identical to calling
1762
+ * `process(undefined)` — included for ergonomics so callers can
1763
+ * express "I'm done, give me the result" without a no-op call.
1764
+ *
1765
+ * The returned array is a shallow copy; entries themselves are
1766
+ * the same object references the accumulator holds internally.
1767
+ */
1768
+ finalize() {
1769
+ return [...this.entries];
1770
+ }
1771
+ /**
1772
+ * Returns the current accumulated state without modifying the
1773
+ * accumulator. Useful for mid-stream inspection.
1774
+ *
1775
+ * The returned array is a shallow copy; mutating it (e.g.
1776
+ * `result.push(...)`) does not affect the accumulator, but
1777
+ * mutating an entry's `function.arguments` object DOES affect
1778
+ * the accumulator's internal state because the entry reference
1779
+ * is shared. Deep-clone the result if you need full isolation.
1780
+ */
1781
+ snapshot() {
1782
+ return [...this.entries];
1783
+ }
1784
+ /**
1785
+ * Returns the number of accumulated tool calls so far.
1786
+ */
1787
+ get length() {
1788
+ return this.entries.length;
1789
+ }
1790
+ };
1791
+ function mergeToolCallArrays(earlier, later) {
1792
+ const acc = new ToolCallAccumulator();
1793
+ acc.process(earlier);
1794
+ acc.process(later);
1795
+ return acc.finalize();
1796
+ }
1797
+
1444
1798
  // src/streaming/stream.ts
1799
+ function extractPartialContent(accumulated) {
1800
+ if (typeof accumulated !== "object" || accumulated === null) return "";
1801
+ const record = accumulated;
1802
+ const message = record.message;
1803
+ if (typeof message === "object" && message !== null) {
1804
+ const content = message.content;
1805
+ if (typeof content === "string") return content;
1806
+ }
1807
+ const response = record.response;
1808
+ if (typeof response === "string") return response;
1809
+ return "";
1810
+ }
1445
1811
  var OllamaStream = class {
1446
1812
  constructor(source, mapChunk, aggregate, initial, signal) {
1447
1813
  this.source = source;
@@ -1462,10 +1828,6 @@ var OllamaStream = class {
1462
1828
  }
1463
1829
  }
1464
1830
  }
1465
- source;
1466
- mapChunk;
1467
- aggregate;
1468
- initial;
1469
1831
  mode = "unconsumed";
1470
1832
  listeners = /* @__PURE__ */ new Map();
1471
1833
  finalResultPromise;
@@ -1524,7 +1886,10 @@ var OllamaStream = class {
1524
1886
  } catch (error) {
1525
1887
  this.removeAbortListener?.();
1526
1888
  this.removeAbortListener = void 0;
1527
- const mapped = mapError(error);
1889
+ const mapped = error instanceof OllamaStreamError && error.partialContent === "" ? new OllamaStreamError(error.message, {
1890
+ partialContent: extractPartialContent(accumulated),
1891
+ cause: error
1892
+ }) : mapError(error);
1528
1893
  this.emit({ type: "error", data: { error: mapped } });
1529
1894
  this.rejectFinal(mapped);
1530
1895
  }
@@ -1551,7 +1916,10 @@ var OllamaStream = class {
1551
1916
  }
1552
1917
  }
1553
1918
  } catch (error) {
1554
- const mapped = mapError(error);
1919
+ const mapped = error instanceof OllamaStreamError && error.partialContent === "" ? new OllamaStreamError(error.message, {
1920
+ partialContent: extractPartialContent(accumulated),
1921
+ cause: error
1922
+ }) : mapError(error);
1555
1923
  this.rejectFinal(mapped);
1556
1924
  yield { type: "error", data: { error: mapped } };
1557
1925
  } finally {
@@ -1573,7 +1941,17 @@ function aggregateChat(accumulated, chunk) {
1573
1941
  role: message.role || accumulated.message.role,
1574
1942
  content: accumulated.message.content + (message.content ?? ""),
1575
1943
  thinking: message.thinking !== void 0 ? (accumulated.message.thinking ?? "") + message.thinking : accumulated.message.thinking,
1576
- tool_calls: message.tool_calls?.length ? [...accumulated.message.tool_calls ?? [], ...message.tool_calls] : accumulated.message.tool_calls
1944
+ // Defensive tool-call merging: instead of blindly appending
1945
+ // every chunk's tool_calls array (which would produce
1946
+ // duplicates if Ollama ever starts streaming them
1947
+ // incrementally), merge entries that refer to the same logical
1948
+ // call (matched by id or array position). For the documented
1949
+ // native behavior (tool_calls arrive complete in one chunk)
1950
+ // this is a no-op — the accumulator appends exactly once.
1951
+ //
1952
+ // See src/tools/tool-call-accumulator.ts for the full merge
1953
+ // semantics and the rationale.
1954
+ tool_calls: message.tool_calls?.length || accumulated.message.tool_calls?.length ? mergeToolCallArrays(accumulated.message.tool_calls, message.tool_calls) : void 0
1577
1955
  },
1578
1956
  model: chunk.model,
1579
1957
  done: chunk.done,
@@ -1782,12 +2160,24 @@ function createTimeoutSignal(timeoutMs, userSignal) {
1782
2160
  return { signal: controller.signal, cancel };
1783
2161
  }
1784
2162
 
2163
+ // src/keep-alive.ts
2164
+ var KEEP_ALIVE_UNLOAD = 0;
2165
+ var KEEP_ALIVE_INDEFINITE = -1;
2166
+ function normalizeKeepAlive(keepAlive) {
2167
+ if (keepAlive === void 0) return void 0;
2168
+ if (keepAlive === "unload") return KEEP_ALIVE_UNLOAD;
2169
+ if (keepAlive === "indefinite") return KEEP_ALIVE_INDEFINITE;
2170
+ return keepAlive;
2171
+ }
2172
+ function isKeepAliveSugar(value) {
2173
+ return value === "unload" || value === "indefinite";
2174
+ }
2175
+
1785
2176
  // src/models-client.ts
1786
2177
  var ModelsClient = class {
1787
2178
  constructor(runner) {
1788
2179
  this.runner = runner;
1789
2180
  }
1790
- runner;
1791
2181
  list() {
1792
2182
  return this.runner((http) => listAvailableModels(http), { singleEndpoint: true });
1793
2183
  }
@@ -1894,6 +2284,76 @@ var ModelsClient = class {
1894
2284
  { singleEndpoint: true }
1895
2285
  );
1896
2286
  }
2287
+ /**
2288
+ * Immediately unloads `model` from VRAM/RAM by issuing an empty
2289
+ * `/api/generate` request with `keep_alive: 0`. The server
2290
+ * finalizes any in-flight generation, releases the model's
2291
+ * weights from GPU memory, and frees the slot for subsequent
2292
+ * pipelines (e.g. swap a vision encoder out before loading an
2293
+ * LLM).
2294
+ *
2295
+ * This is the ergonomic equivalent of:
2296
+ *
2297
+ * ```ts
2298
+ * client.generate({ model, prompt: '', keep_alive: 0 });
2299
+ * ```
2300
+ *
2301
+ * Use {@link pin} for the opposite lifecycle — pinning a model
2302
+ * indefinitely in VRAM for hot-loop inference.
2303
+ *
2304
+ * Resolves once the server acknowledges the unload. The promise
2305
+ * rejects on transport errors, model-not-found (404), or auth
2306
+ * failures — callers handling transient issues can retry via the
2307
+ * standard {@link withRetry} policy.
2308
+ *
2309
+ * See: https://github.com/ollama/ollama/blob/main/docs/faq.md
2310
+ * #how-do-i-keep-a-model-loaded-in-memory-or-make-it-unload-immediately
2311
+ */
2312
+ async unload(model) {
2313
+ await this.runner(
2314
+ (http, signal) => http.request({
2315
+ path: "/api/generate",
2316
+ body: { model, prompt: "", keep_alive: KEEP_ALIVE_UNLOAD, stream: false },
2317
+ signal
2318
+ }),
2319
+ { model, singleEndpoint: true }
2320
+ );
2321
+ }
2322
+ /**
2323
+ * Pre-loads and pins `model` into VRAM indefinitely by issuing an
2324
+ * empty `/api/generate` request with `keep_alive: -1`. The server
2325
+ * loads the model into GPU memory (paying the cold-load latency
2326
+ * once) and keeps it resident until either:
2327
+ *
2328
+ * - an explicit {@link unload} call is made,
2329
+ * - the server process is restarted, or
2330
+ * - the host runs out of VRAM and the server's own eviction
2331
+ * policy kicks in.
2332
+ *
2333
+ * This is the ergonomic equivalent of:
2334
+ *
2335
+ * ```ts
2336
+ * client.generate({ model, prompt: '', keep_alive: -1 });
2337
+ * ```
2338
+ *
2339
+ * Use {@link unload} to release the pinned slot when the hot loop
2340
+ * is done — leaving models pinned indefinitely exhausts VRAM and
2341
+ * starves subsequent pipelines.
2342
+ *
2343
+ * Resolves once the server acknowledges the pin (i.e. the model
2344
+ * is fully loaded). The promise rejects on transport errors,
2345
+ * model-not-found (404), or VRAM-exhaustion failures.
2346
+ */
2347
+ async pin(model) {
2348
+ await this.runner(
2349
+ (http, signal) => http.request({
2350
+ path: "/api/generate",
2351
+ body: { model, prompt: "", keep_alive: KEEP_ALIVE_INDEFINITE, stream: false },
2352
+ signal
2353
+ }),
2354
+ { model, singleEndpoint: true }
2355
+ );
2356
+ }
1897
2357
  async createBlob(digest, data) {
1898
2358
  await this.runner(
1899
2359
  (http, signal) => http.request({
@@ -1921,6 +2381,100 @@ var ModelsClient = class {
1921
2381
  throw err;
1922
2382
  }
1923
2383
  }
2384
+ /**
2385
+ * Computes a blob digest the way Ollama's content-addressed store expects:
2386
+ * SHA-256 over the raw bytes, formatted as `sha256:<64 lowercase hex>`.
2387
+ *
2388
+ * Uses the Web Crypto API (`crypto.subtle.digest`), which exists in every
2389
+ * supported runtime — Node.js ≥ 19 exposes it as a global (this SDK
2390
+ * requires Node ≥ 20), and browsers/edge runtimes have always had it.
2391
+ */
2392
+ async computeBlobDigest(data) {
2393
+ const subtle = globalThis.crypto?.subtle;
2394
+ if (subtle === void 0) {
2395
+ throw new OllamaClientError(
2396
+ "computeBlobDigest requires the Web Crypto API (crypto.subtle), unavailable in this runtime.",
2397
+ { code: "crypto_unavailable" }
2398
+ );
2399
+ }
2400
+ const digestBytes = await subtle.digest("SHA-256", data);
2401
+ const hex = Array.from(
2402
+ new Uint8Array(digestBytes),
2403
+ (byte) => byte.toString(16).padStart(2, "0")
2404
+ ).join("");
2405
+ return `sha256:${hex}`;
2406
+ }
2407
+ /**
2408
+ * Computes the SHA-256 digest of `data` and pushes it as a blob in one
2409
+ * step — the content-addressed upload Ollama's own tooling performs
2410
+ * (`curl -T file http://…/api/blobs/sha256:…`). Returns the digest, which
2411
+ * `/api/create`'s `files` field references.
2412
+ *
2413
+ * Pass `digest` to skip recomputation when the caller already knows it —
2414
+ * the server verifies the content against the digest and rejects mismatches.
2415
+ */
2416
+ async createBlobFromData(data, opts) {
2417
+ const digest = opts?.digest ?? await this.computeBlobDigest(data);
2418
+ const alreadyExisted = await this.checkBlob(digest);
2419
+ if (!alreadyExisted) {
2420
+ await this.createBlob(digest, data);
2421
+ }
2422
+ return { digest, alreadyExisted };
2423
+ }
2424
+ /**
2425
+ * Reads a local file and uploads it as a blob (Node.js only — dynamic
2426
+ * `node:fs` import, so browser bundles never pull it in). The file name
2427
+ * (basename) is what `/api/create`'s `files` map expects as the key, so
2428
+ * this returns it alongside the digest.
2429
+ */
2430
+ async createBlobFromFile(path, opts) {
2431
+ const nodeFsModule = "node:fs";
2432
+ const { promises: fs } = await import(nodeFsModule);
2433
+ const data = await fs.readFile(path);
2434
+ const result = await this.createBlobFromData(new Uint8Array(data), {
2435
+ ...opts
2436
+ });
2437
+ const fileName = path.split(/[\\/]/).pop() ?? path;
2438
+ return { ...result, fileName };
2439
+ }
2440
+ /**
2441
+ * One-shot custom-model publishing from raw GGUF weights, following the
2442
+ * protocol Ollama's API reference documents: push a blob for the GGUF
2443
+ * bytes, then `POST /api/create` with `files: { <fileName>: <digest> }`.
2444
+ * Split GGUFs are supported via `files` — pass every shard's path.
2445
+ *
2446
+ * ```ts
2447
+ * await client.models.createModelFromGguf('my-model', '/path/to/model.gguf', {
2448
+ * template: '{{ .Prompt }}',
2449
+ * });
2450
+ * ```
2451
+ *
2452
+ * @param model Name for the new model (e.g. `'my-model:latest'`).
2453
+ * @param gguf Path(s) to GGUF file(s) on the local filesystem.
2454
+ * @param opts Everything else `/api/create` accepts (`template`, `system`,
2455
+ * `parameters`, `license`, `quantize`, …) except `files` and `from`.
2456
+ */
2457
+ async createModelFromGguf(model, gguf, opts) {
2458
+ const paths = typeof gguf === "string" ? [gguf] : gguf;
2459
+ if (paths.length === 0) {
2460
+ throw new OllamaClientError("createModelFromGguf: at least one GGUF path is required.", {
2461
+ code: "invalid_request"
2462
+ });
2463
+ }
2464
+ const files = {};
2465
+ for (const path of paths) {
2466
+ const uploaded = await this.createBlobFromFile(path, { signal: opts?.signal });
2467
+ files[uploaded.fileName] = uploaded.digest;
2468
+ }
2469
+ const {
2470
+ model: _model,
2471
+ files: _files,
2472
+ from: _from,
2473
+ stream: _stream,
2474
+ ...rest
2475
+ } = opts ?? {};
2476
+ return this.create({ ...rest, model, files, stream: false });
2477
+ }
1924
2478
  };
1925
2479
 
1926
2480
  // src/integrations/openai.ts
@@ -1947,7 +2501,6 @@ var OpenAIChatCompletionStream = class {
1947
2501
  }
1948
2502
  }
1949
2503
  }
1950
- source;
1951
2504
  finalResultPromise;
1952
2505
  resolveFinal;
1953
2506
  rejectFinal;
@@ -2091,7 +2644,6 @@ var OpenAICompletionStream = class {
2091
2644
  }
2092
2645
  }
2093
2646
  }
2094
- source;
2095
2647
  finalResultPromise;
2096
2648
  resolveFinal;
2097
2649
  rejectFinal;
@@ -2172,7 +2724,6 @@ var OpenAIResponsesStream = class {
2172
2724
  }
2173
2725
  }
2174
2726
  }
2175
- source;
2176
2727
  finalResultPromise;
2177
2728
  resolveFinal;
2178
2729
  rejectFinal;
@@ -2397,8 +2948,6 @@ var OpenAICompatClient = class {
2397
2948
  this.http = http;
2398
2949
  this.runner = runner;
2399
2950
  }
2400
- http;
2401
- runner;
2402
2951
  request(operation, model, signal, holdUntil) {
2403
2952
  if (this.runner) {
2404
2953
  return this.runner(
@@ -2618,7 +3167,6 @@ var AnthropicMessagesStream = class {
2618
3167
  }
2619
3168
  }
2620
3169
  }
2621
- source;
2622
3170
  finalResultPromise;
2623
3171
  resolveFinal;
2624
3172
  rejectFinal;
@@ -2733,8 +3281,6 @@ var AnthropicCompatClient = class {
2733
3281
  this.http = http;
2734
3282
  this.runner = runner;
2735
3283
  }
2736
- http;
2737
- runner;
2738
3284
  request(operation, model, signal, holdUntil) {
2739
3285
  if (this.runner) {
2740
3286
  return this.runner(
@@ -2780,6 +3326,32 @@ var AnthropicCompatClient = class {
2780
3326
  return this.createMessage(request, signal);
2781
3327
  }
2782
3328
  };
3329
+ var IncludedBalanceSchema = zod.z.object({
3330
+ balance_usd: zod.z.number(),
3331
+ allowance_usd: zod.z.number(),
3332
+ period: zod.z.object({
3333
+ from: zod.z.string(),
3334
+ until: zod.z.string()
3335
+ })
3336
+ });
3337
+ var LegacyBalanceLimitSchema = zod.z.object({
3338
+ remaining_percent: zod.z.number().min(0).max(100),
3339
+ resets_at: zod.z.string()
3340
+ });
3341
+
3342
+ // src/generated/models/LegacyIncludedBalance.schema.ts
3343
+ var LegacyIncludedBalanceSchema = zod.z.object({
3344
+ session: LegacyBalanceLimitSchema,
3345
+ weekly: LegacyBalanceLimitSchema
3346
+ });
3347
+
3348
+ // src/generated/models/BalanceResponse.schema.ts
3349
+ var BalanceResponseSchema = zod.z.object({
3350
+ included: zod.z.union([IncludedBalanceSchema, LegacyIncludedBalanceSchema]),
3351
+ purchased: zod.z.object({
3352
+ balance_usd: zod.z.number()
3353
+ })
3354
+ });
2783
3355
  var ToolCallSchema = zod.z.object({
2784
3356
  function: zod.z.object({
2785
3357
  name: zod.z.string(),
@@ -2883,6 +3455,7 @@ var CreateRequestSchema = zod.z.object({
2883
3455
  quantize: zod.z.string().optional(),
2884
3456
  draft_quantize: zod.z.string().optional(),
2885
3457
  requires: zod.z.string().optional(),
3458
+ capabilities: zod.z.array(zod.z.string()).optional(),
2886
3459
  stream: zod.z.boolean().optional()
2887
3460
  });
2888
3461
  var DeleteRequestSchema = zod.z.object({
@@ -2945,7 +3518,7 @@ var ModelSummarySchema = zod.z.object({
2945
3518
  details: zod.z.object({
2946
3519
  format: zod.z.string().optional(),
2947
3520
  family: zod.z.string().optional(),
2948
- families: zod.z.array(zod.z.string()).optional(),
3521
+ families: zod.z.union([zod.z.array(zod.z.string()), zod.z.null()]).optional(),
2949
3522
  parameter_size: zod.z.string().optional(),
2950
3523
  quantization_level: zod.z.string().optional()
2951
3524
  }).optional()
@@ -3080,6 +3653,34 @@ var SystemOneResponseSchema = zod.z.object({
3080
3653
  output_tokens: zod.z.number().min(0)
3081
3654
  })
3082
3655
  });
3656
+ var UsageMetricsSchema = zod.z.object({
3657
+ request_count: zod.z.number().min(0),
3658
+ usage_usd: zod.z.number().optional(),
3659
+ input_tokens: zod.z.number().optional(),
3660
+ cached_input_tokens: zod.z.number().optional(),
3661
+ output_tokens: zod.z.number().optional()
3662
+ });
3663
+
3664
+ // src/generated/models/UsageBucket.schema.ts
3665
+ var UsageBucketSchema = zod.z.intersection(
3666
+ UsageMetricsSchema,
3667
+ zod.z.object({
3668
+ from: zod.z.string(),
3669
+ until: zod.z.string(),
3670
+ partial: zod.z.boolean().optional()
3671
+ })
3672
+ );
3673
+
3674
+ // src/generated/models/UsageResponse.schema.ts
3675
+ var UsageResponseSchema = zod.z.object({
3676
+ range: zod.z.enum(["24h", "7d", "30d"]),
3677
+ scope: zod.z.enum(["self", "team"]),
3678
+ granularity: zod.z.enum(["hour", "day"]),
3679
+ from: zod.z.string(),
3680
+ until: zod.z.string(),
3681
+ totals: UsageMetricsSchema,
3682
+ buckets: zod.z.array(UsageBucketSchema)
3683
+ });
3083
3684
  var VersionResponseSchema = zod.z.object({
3084
3685
  version: zod.z.string().optional()
3085
3686
  });
@@ -3122,6 +3723,7 @@ var requestSchemas = {
3122
3723
  webSearch: WebSearchRequestSchema
3123
3724
  };
3124
3725
  var responseSchemas = {
3726
+ balance: BalanceResponseSchema,
3125
3727
  chat: ChatResponseSchema,
3126
3728
  create: StatusResponseSchema,
3127
3729
  embed: EmbedResponseSchema,
@@ -3132,6 +3734,7 @@ var responseSchemas = {
3132
3734
  show: ShowResponseSchema,
3133
3735
  systemOne: SystemOneResponseSchema,
3134
3736
  tags: ListResponseSchema,
3737
+ usage: UsageResponseSchema,
3135
3738
  version: VersionResponseSchema,
3136
3739
  webFetch: WebFetchResponseSchema,
3137
3740
  webSearch: WebSearchResponseSchema
@@ -3182,7 +3785,6 @@ var OllamaRuntime = class {
3182
3785
  constructor(options) {
3183
3786
  this.options = options;
3184
3787
  }
3185
- options;
3186
3788
  /**
3187
3789
  * Cached promise of the Ollama server version, populated lazily the first
3188
3790
  * time a version-gated operation is invoked. `undefined` until then.
@@ -3259,11 +3861,19 @@ var OllamaRuntime = class {
3259
3861
  }
3260
3862
  const streaming = shouldStream(req);
3261
3863
  const resolvedPath = resolvePathParams(req.operation.path, req.pathParams);
3864
+ const pathWithQuery = applyQueryParams(resolvedPath, req.queryParams);
3262
3865
  const model = req.model ?? extractModelFromBody(body);
3866
+ const headers = {};
3867
+ if (req.headerParams) {
3868
+ for (const [key, value] of Object.entries(req.headerParams)) {
3869
+ headers[key] = value;
3870
+ }
3871
+ }
3263
3872
  const httpReq = {
3264
- path: resolvedPath,
3873
+ path: pathWithQuery,
3265
3874
  method: req.operation.method,
3266
3875
  ...body !== void 0 ? { body } : {},
3876
+ ...Object.keys(headers).length > 0 ? { headers } : {},
3267
3877
  ...req.signal !== void 0 ? { signal: req.signal } : {},
3268
3878
  ...streaming ? { raw: true } : {},
3269
3879
  ...model !== void 0 ? { model } : {}
@@ -3404,6 +4014,465 @@ function extractModelFromBody(body) {
3404
4014
  const model = obj.model;
3405
4015
  return typeof model === "string" ? model : void 0;
3406
4016
  }
4017
+ function applyQueryParams(path, queryParams) {
4018
+ if (!queryParams || Object.keys(queryParams).length === 0) return path;
4019
+ const pairs = Object.entries(queryParams).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`).join("&");
4020
+ return `${path}?${pairs}`;
4021
+ }
4022
+
4023
+ // src/responses.ts
4024
+ var adapterResponseSequence = 0;
4025
+ function toNativeTools(tools) {
4026
+ if (tools === void 0 || tools.length === 0) return void 0;
4027
+ return tools.map((tool) => ({
4028
+ type: "function",
4029
+ function: {
4030
+ name: tool.name,
4031
+ description: tool.description ?? "",
4032
+ parameters: tool.parameters ?? { type: "object", properties: {} }
4033
+ }
4034
+ }));
4035
+ }
4036
+ function mapNativeUsage(usage) {
4037
+ if (usage === void 0) return void 0;
4038
+ const input = usage.input_tokens ?? 0;
4039
+ const output = usage.output_tokens ?? 0;
4040
+ return { input_tokens: input, output_tokens: output, total_tokens: input + output };
4041
+ }
4042
+ function parseToolCallArguments(raw) {
4043
+ if (raw === void 0 || raw === null || raw === "") return {};
4044
+ try {
4045
+ const parsed = JSON.parse(raw);
4046
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
4047
+ } catch {
4048
+ return { _raw: raw };
4049
+ }
4050
+ }
4051
+ function mapNativeResponse(native) {
4052
+ const textParts = [];
4053
+ const thinkingParts = [];
4054
+ const toolCalls = [];
4055
+ for (const item of native.output ?? []) {
4056
+ if (item.type === "message") {
4057
+ for (const part of item.content ?? []) {
4058
+ if (part.type === "output_text") textParts.push(part.text ?? "");
4059
+ else if (part.type === "refusal") textParts.push(part.refusal ?? "");
4060
+ }
4061
+ } else if (item.type === "reasoning") {
4062
+ if (item.text !== void 0 && item.text !== "") thinkingParts.push(item.text);
4063
+ } else if (item.type === "function_call") {
4064
+ toolCalls.push({
4065
+ ...item.id !== void 0 ? { id: item.id } : {},
4066
+ function: { name: item.name, arguments: parseToolCallArguments(item.arguments) }
4067
+ });
4068
+ }
4069
+ }
4070
+ return {
4071
+ id: native.id ?? `resp_${Date.now().toString(36)}`,
4072
+ model: native.model,
4073
+ created_at: new Date((native.created_at ?? 0) * 1e3).toISOString(),
4074
+ output_text: textParts.join(""),
4075
+ ...thinkingParts.length > 0 ? { thinking: thinkingParts.join("\n") } : {},
4076
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
4077
+ ...mapNativeUsage(native.usage) !== void 0 ? { usage: mapNativeUsage(native.usage) } : {}
4078
+ };
4079
+ }
4080
+ function mapChatUsage(res) {
4081
+ const asChat = res;
4082
+ const asStream = res;
4083
+ const input = asChat.prompt_eval_count ?? asStream.usage?.promptTokens ?? asStream.raw?.prompt_eval_count ?? 0;
4084
+ const output = asChat.eval_count ?? asStream.usage?.completionTokens ?? asStream.raw?.eval_count ?? 0;
4085
+ if (input === 0 && output === 0) return void 0;
4086
+ return { input_tokens: input, output_tokens: output, total_tokens: input + output };
4087
+ }
4088
+ function mapChatResponse(res) {
4089
+ adapterResponseSequence += 1;
4090
+ return {
4091
+ id: `resp_${Date.now().toString(36)}${adapterResponseSequence.toString(36)}`,
4092
+ model: res.model,
4093
+ created_at: res.created_at,
4094
+ output_text: res.message.content,
4095
+ ...res.message.thinking !== void 0 ? { thinking: res.message.thinking } : {},
4096
+ ...res.message.tool_calls !== void 0 && res.message.tool_calls.length > 0 ? { tool_calls: res.message.tool_calls } : {},
4097
+ ...mapChatUsage(res) !== void 0 ? { usage: mapChatUsage(res) } : {},
4098
+ transport: "chat-adapter"
4099
+ };
4100
+ }
4101
+ var ResponsesModule = class {
4102
+ constructor(runner) {
4103
+ this.runner = runner;
4104
+ }
4105
+ /** The request body sent to the native endpoint (also reused for streaming). */
4106
+ nativeBody(req) {
4107
+ return {
4108
+ model: req.model,
4109
+ input: req.input,
4110
+ ...req.instructions !== void 0 ? { instructions: req.instructions } : {},
4111
+ ...req.tools !== void 0 && req.tools.length > 0 ? { tools: toNativeTools(req.tools) } : {},
4112
+ ...req.temperature !== void 0 ? { temperature: req.temperature } : {},
4113
+ ...req.top_p !== void 0 ? { top_p: req.top_p } : {},
4114
+ ...req.max_output_tokens !== void 0 ? { max_output_tokens: req.max_output_tokens } : {},
4115
+ ...req.reasoning_effort !== void 0 ? { reasoning: { effort: req.reasoning_effort } } : {},
4116
+ ...req.think !== void 0 ? { think: req.think } : {}
4117
+ };
4118
+ }
4119
+ /**
4120
+ * Runs one Responses request, preferring the native `POST /v1/responses`
4121
+ * endpoint and transparently re-issuing via `/api/chat` when the server
4122
+ * answers `404` (pre-v0.13.3 Ollama without the compatibility endpoint).
4123
+ */
4124
+ async create(req) {
4125
+ const body = this.nativeBody(req);
4126
+ try {
4127
+ const native = await this.runner(
4128
+ (http, signal) => http.request({
4129
+ path: "/v1/responses",
4130
+ body: { ...body, stream: false },
4131
+ signal
4132
+ }),
4133
+ { model: req.model, signal: req.signal, timeoutMs: req.timeoutMs }
4134
+ );
4135
+ return { ...mapNativeResponse(native), transport: "native" };
4136
+ } catch (err) {
4137
+ if (!(err instanceof OllamaNotFoundError)) throw err;
4138
+ }
4139
+ return this.createViaChatAdapter(req);
4140
+ }
4141
+ /** Convenience: `create()` returning only the `output_text` string. */
4142
+ async createText(req) {
4143
+ const res = await this.create(req);
4144
+ return res.output_text;
4145
+ }
4146
+ async createViaChatAdapter(req) {
4147
+ const messages = [
4148
+ ...req.instructions !== void 0 ? [{ role: "system", content: req.instructions }] : [],
4149
+ { role: "user", content: req.input }
4150
+ ];
4151
+ const chatRes = await this.runner(
4152
+ (http, signal) => http.request({
4153
+ path: "/api/chat",
4154
+ body: {
4155
+ model: req.model,
4156
+ messages,
4157
+ ...req.tools !== void 0 && req.tools.length > 0 ? { tools: toNativeTools(req.tools) } : {},
4158
+ options: {
4159
+ ...req.temperature !== void 0 ? { temperature: req.temperature } : {},
4160
+ ...req.top_p !== void 0 ? { top_p: req.top_p } : {},
4161
+ ...req.max_output_tokens !== void 0 ? { num_predict: req.max_output_tokens } : {}
4162
+ },
4163
+ ...req.think !== void 0 ? { think: req.think } : {},
4164
+ stream: false
4165
+ },
4166
+ signal
4167
+ }),
4168
+ { model: req.model, signal: req.signal, timeoutMs: req.timeoutMs }
4169
+ );
4170
+ return mapChatResponse(chatRes);
4171
+ }
4172
+ /**
4173
+ * Streams a Responses request as simplified
4174
+ * `{type: 'text_delta' | 'thinking_delta' | 'done'}` events. Dual-mode like
4175
+ * {@link create}: native SSE from `/v1/responses` when available, otherwise
4176
+ * the normalized `/api/chat` token stream. Tool calls are not delta'd —
4177
+ * collect them from the final `done` event's `response.tool_calls`.
4178
+ *
4179
+ * This is a lazy async generator: nothing is sent until the first
4180
+ * iteration, matching `for await (... of client.responses.stream(req))`
4181
+ * intuition. The generator is also cancellable through `req.signal`.
4182
+ */
4183
+ async *stream(req) {
4184
+ const body = this.nativeBody(req);
4185
+ let nativeStream;
4186
+ try {
4187
+ nativeStream = await this.runner(
4188
+ async (http, signal) => {
4189
+ const source = await http.requestSseStream({
4190
+ path: "/v1/responses",
4191
+ body: { ...body, stream: true },
4192
+ signal
4193
+ });
4194
+ return new OpenAIResponsesStream(source, signal);
4195
+ },
4196
+ {
4197
+ model: req.model,
4198
+ signal: req.signal,
4199
+ timeoutMs: req.timeoutMs,
4200
+ holdUntil: (s) => s.finalResult
4201
+ }
4202
+ );
4203
+ } catch (err) {
4204
+ if (!(err instanceof OllamaNotFoundError)) throw err;
4205
+ }
4206
+ if (nativeStream !== void 0) {
4207
+ for await (const event of nativeStream) {
4208
+ const type = event.type;
4209
+ if (type === "response.output_text.delta") {
4210
+ yield { type: "text_delta", delta: event.delta };
4211
+ } else if (type === "response.reasoning_text.delta") {
4212
+ yield { type: "thinking_delta", delta: event.delta };
4213
+ }
4214
+ }
4215
+ yield {
4216
+ type: "done",
4217
+ response: { ...mapNativeResponse(await nativeStream.finalResult), transport: "native" }
4218
+ };
4219
+ return;
4220
+ }
4221
+ const chatStream = await this.runner(
4222
+ async (http, signal) => {
4223
+ const source = await http.requestStream({
4224
+ path: "/api/chat",
4225
+ body: {
4226
+ model: req.model,
4227
+ messages: [
4228
+ ...req.instructions !== void 0 ? [{ role: "system", content: req.instructions }] : [],
4229
+ { role: "user", content: req.input }
4230
+ ],
4231
+ ...req.tools !== void 0 && req.tools.length > 0 ? { tools: toNativeTools(req.tools) } : {},
4232
+ options: {
4233
+ ...req.temperature !== void 0 ? { temperature: req.temperature } : {},
4234
+ ...req.top_p !== void 0 ? { top_p: req.top_p } : {},
4235
+ ...req.max_output_tokens !== void 0 ? { num_predict: req.max_output_tokens } : {}
4236
+ },
4237
+ ...req.think !== void 0 ? { think: req.think } : {},
4238
+ stream: true
4239
+ },
4240
+ signal
4241
+ });
4242
+ return normalizeChatStream(source, signal);
4243
+ },
4244
+ {
4245
+ model: req.model,
4246
+ signal: req.signal,
4247
+ timeoutMs: req.timeoutMs,
4248
+ holdUntil: (s) => s.finalResult
4249
+ }
4250
+ );
4251
+ for await (const event of chatStream) {
4252
+ if (event.type === "token") {
4253
+ yield { type: "text_delta", delta: event.data.delta };
4254
+ } else if (event.type === "thinking") {
4255
+ yield { type: "thinking_delta", delta: event.data.delta };
4256
+ }
4257
+ }
4258
+ const result = await chatStream.finalResult;
4259
+ yield {
4260
+ type: "done",
4261
+ response: {
4262
+ id: `resp_${Date.now().toString(36)}${(++adapterResponseSequence).toString(36)}`,
4263
+ model: result.model,
4264
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
4265
+ output_text: result.message.content,
4266
+ ...result.message.thinking !== void 0 ? { thinking: result.message.thinking } : {},
4267
+ ...result.message.tool_calls !== void 0 && result.message.tool_calls.length > 0 ? { tool_calls: result.message.tool_calls } : {},
4268
+ ...mapChatUsage(result) !== void 0 ? { usage: mapChatUsage(result) } : {},
4269
+ transport: "chat-adapter"
4270
+ }
4271
+ };
4272
+ }
4273
+ };
4274
+
4275
+ // src/conversation.ts
4276
+ function extractTurnStats(res) {
4277
+ const cachedTokens = res.prompt_eval_cached_count ?? 0;
4278
+ const evaluatedTokens = res.prompt_eval_count ?? 0;
4279
+ const total = cachedTokens + evaluatedTokens;
4280
+ return {
4281
+ cachedTokens,
4282
+ evaluatedTokens,
4283
+ hitRate: total > 0 ? cachedTokens / total : 0
4284
+ };
4285
+ }
4286
+ var ConversationSession = class {
4287
+ constructor(client, config) {
4288
+ this.client = client;
4289
+ this.config = config;
4290
+ if (config.systemPrompt !== void 0 && config.systemPrompt !== "") {
4291
+ this.history.push(Object.freeze({ role: "system", content: config.systemPrompt }));
4292
+ }
4293
+ }
4294
+ /** Ordered, append-only history. Message objects are frozen on push. */
4295
+ history = [];
4296
+ /** Mutable tallies behind the read-only {@link cacheStats} view. */
4297
+ tallies = {
4298
+ turns: 0,
4299
+ cachedTokens: 0,
4300
+ evaluatedTokens: 0
4301
+ };
4302
+ /** The model name this session talks to. */
4303
+ get model() {
4304
+ return this.config.model;
4305
+ }
4306
+ /** Number of completed turns. */
4307
+ get turnCount() {
4308
+ return this.tallies.turns;
4309
+ }
4310
+ /** Cumulative KV-cache statistics across completed turns. */
4311
+ get cacheStats() {
4312
+ const total = this.tallies.cachedTokens + this.tallies.evaluatedTokens;
4313
+ return {
4314
+ turns: this.tallies.turns,
4315
+ cachedTokens: this.tallies.cachedTokens,
4316
+ evaluatedTokens: this.tallies.evaluatedTokens,
4317
+ totalPromptTokens: total,
4318
+ hitRate: total > 0 ? this.tallies.cachedTokens / total : 0
4319
+ };
4320
+ }
4321
+ /**
4322
+ * Sends the user's message and returns the assistant's reply text —
4323
+ * the ergonomic path. Use {@link sendTurn} when you also need the
4324
+ * per-turn cache statistics or the raw response.
4325
+ */
4326
+ async send(content, overrides) {
4327
+ const turn = await this.sendTurn(content, overrides);
4328
+ return turn.content;
4329
+ }
4330
+ /**
4331
+ * Sends the user's message and returns the full turn record: reply text,
4332
+ * per-turn KV-cache statistics, and the raw `/api/chat` response.
4333
+ */
4334
+ async sendTurn(content, overrides) {
4335
+ this.history.push(Object.freeze({ role: "user", content }));
4336
+ const response = await this.client.chat({
4337
+ model: this.config.model,
4338
+ messages: [...this.history],
4339
+ ...this.config.tools !== void 0 || overrides?.tools !== void 0 ? { tools: overrides?.tools ?? this.config.tools } : {},
4340
+ ...this.config.format !== void 0 || overrides?.format !== void 0 ? { format: overrides?.format ?? this.config.format } : {},
4341
+ options: { ...this.config.options, ...overrides?.options },
4342
+ ...this.config.think !== void 0 || overrides?.think !== void 0 ? { think: overrides?.think ?? this.config.think } : {},
4343
+ ...this.config.keep_alive !== void 0 || overrides?.keep_alive !== void 0 ? { keep_alive: overrides?.keep_alive ?? this.config.keep_alive } : {},
4344
+ ...overrides?.signal !== void 0 ? { signal: overrides.signal } : {},
4345
+ ...overrides?.timeoutMs !== void 0 ? { timeoutMs: overrides.timeoutMs } : {},
4346
+ stream: false
4347
+ });
4348
+ this.history.push(Object.freeze({ ...response.message }));
4349
+ const cache = extractTurnStats(response);
4350
+ this.tallies.turns += 1;
4351
+ this.tallies.cachedTokens += cache.cachedTokens;
4352
+ this.tallies.evaluatedTokens += cache.evaluatedTokens;
4353
+ return { content: response.message.content, cache, response };
4354
+ }
4355
+ /**
4356
+ * A defensive copy of the session history — mutating the returned array or
4357
+ * its frozen messages has no effect on the session.
4358
+ */
4359
+ getMessages() {
4360
+ return this.history.map((message) => message);
4361
+ }
4362
+ /**
4363
+ * Clears the conversation back to its initial state (system prompt, zero
4364
+ * turns, zeroed cache statistics). The next turn starts with a cold cache
4365
+ * by definition — there is nothing upstream of it to reuse.
4366
+ */
4367
+ reset() {
4368
+ this.history.length = 0;
4369
+ if (this.config.systemPrompt !== void 0 && this.config.systemPrompt !== "") {
4370
+ this.history.push(
4371
+ Object.freeze({ role: "system", content: this.config.systemPrompt })
4372
+ );
4373
+ }
4374
+ this.tallies.turns = 0;
4375
+ this.tallies.cachedTokens = 0;
4376
+ this.tallies.evaluatedTokens = 0;
4377
+ }
4378
+ };
4379
+
4380
+ // src/context-safety.ts
4381
+ var DEFAULT_CONTEXT_WARNING_THRESHOLD = 0.9;
4382
+ var IMAGE_TOKEN_ESTIMATE = 300;
4383
+ var CHARS_PER_TOKEN = 4;
4384
+ function isCjkCodePoint(code) {
4385
+ return code >= 19968 && code <= 40959 || // CJK Unified Ideographs
4386
+ code >= 13312 && code <= 19903 || // CJK Extension A
4387
+ code >= 12352 && code <= 12543 || // Hiragana + Katakana
4388
+ code >= 44032 && code <= 55215 || // Hangul syllables
4389
+ code >= 63744 && code <= 64255 || // CJK Compatibility Ideographs
4390
+ code >= 65280 && code <= 65519;
4391
+ }
4392
+ function estimateTokens(text) {
4393
+ if (text.length === 0) return 0;
4394
+ let cjk = 0;
4395
+ let other = 0;
4396
+ for (const char of text) {
4397
+ if (isCjkCodePoint(char.codePointAt(0) ?? 0)) cjk += 1;
4398
+ else other += 1;
4399
+ }
4400
+ return Math.ceil(cjk + other / CHARS_PER_TOKEN);
4401
+ }
4402
+ var MESSAGE_OVERHEAD_TOKENS = 4;
4403
+ function estimateMessageTokens(message) {
4404
+ let total = MESSAGE_OVERHEAD_TOKENS + estimateTokens(message.content ?? "");
4405
+ if (message.thinking !== void 0) total += estimateTokens(message.thinking);
4406
+ if (message.tool_calls !== void 0) {
4407
+ for (const call of message.tool_calls) {
4408
+ total += estimateTokens(call.function.name);
4409
+ total += estimateTokens(JSON.stringify(call.function.arguments ?? {}));
4410
+ }
4411
+ }
4412
+ if (message.images !== void 0) total += message.images.length * IMAGE_TOKEN_ESTIMATE;
4413
+ return total;
4414
+ }
4415
+ function estimateChatRequestTokens(req) {
4416
+ let total = 0;
4417
+ for (const message of req.messages) total += estimateMessageTokens(message);
4418
+ if (req.tools !== void 0) {
4419
+ for (const tool of req.tools) {
4420
+ total += estimateTokens(tool.function.description ?? "");
4421
+ total += estimateTokens(JSON.stringify(tool.function.parameters ?? {}));
4422
+ }
4423
+ }
4424
+ return total;
4425
+ }
4426
+ function estimateGenerateRequestTokens(req) {
4427
+ let total = estimateTokens(req.prompt ?? "");
4428
+ if (req.system !== void 0) total += estimateTokens(req.system);
4429
+ if (req.template !== void 0) total += estimateTokens(req.template);
4430
+ if (req.images !== void 0) total += req.images.length * IMAGE_TOKEN_ESTIMATE;
4431
+ return total;
4432
+ }
4433
+ function checkChatContext(req, options = {}) {
4434
+ return check(estimateChatRequestTokens(req), req.options?.num_ctx, options);
4435
+ }
4436
+ function checkGenerateContext(req, options = {}) {
4437
+ return check(estimateGenerateRequestTokens(req), req.options?.num_ctx, options);
4438
+ }
4439
+ function check(estimatedPromptTokens, requestNumCtx, options) {
4440
+ const threshold = options.threshold ?? DEFAULT_CONTEXT_WARNING_THRESHOLD;
4441
+ if (requestNumCtx !== void 0) {
4442
+ return {
4443
+ estimatedPromptTokens,
4444
+ effectiveContextLength: requestNumCtx,
4445
+ windowSource: "request",
4446
+ exceedsThreshold: estimatedPromptTokens > requestNumCtx * threshold
4447
+ };
4448
+ }
4449
+ if (options.defaultContextLength !== void 0) {
4450
+ return {
4451
+ estimatedPromptTokens,
4452
+ effectiveContextLength: options.defaultContextLength,
4453
+ windowSource: "client-default",
4454
+ exceedsThreshold: estimatedPromptTokens > options.defaultContextLength * threshold
4455
+ };
4456
+ }
4457
+ return {
4458
+ estimatedPromptTokens,
4459
+ effectiveContextLength: OLLAMA_FALLBACK_CONTEXT_LENGTH,
4460
+ windowSource: "server-fallback",
4461
+ exceedsThreshold: estimatedPromptTokens > OLLAMA_FALLBACK_CONTEXT_LENGTH
4462
+ };
4463
+ }
4464
+ var OLLAMA_FALLBACK_CONTEXT_LENGTH = 2048;
4465
+ function contextWarningMessage(check2, kind) {
4466
+ const where = `OllamaClient.${kind}()`;
4467
+ const estimate = `~${check2.estimatedPromptTokens} tokens`;
4468
+ if (check2.windowSource === "request") {
4469
+ 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\`.`;
4470
+ }
4471
+ if (check2.windowSource === "client-default") {
4472
+ 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\`.`;
4473
+ }
4474
+ 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\`.`;
4475
+ }
3407
4476
 
3408
4477
  // src/generated/api/operations.ts
3409
4478
  var blobsOp = {
@@ -3615,7 +4684,6 @@ var NativeApi = class {
3615
4684
  constructor(runtime) {
3616
4685
  this.runtime = runtime;
3617
4686
  }
3618
- runtime;
3619
4687
  blobs(digest, options) {
3620
4688
  return this.runtime.invoke({
3621
4689
  operation: blobsOp,
@@ -3718,7 +4786,6 @@ var FailoverHttpClient = class {
3718
4786
  this.client = client;
3719
4787
  this.baseUrl = baseUrl;
3720
4788
  }
3721
- client;
3722
4789
  /**
3723
4790
  * The first candidate endpoint's base URL. Used for inferRuntimeMode()
3724
4791
  * checks (local vs cloud). The actual request routing picks the best
@@ -3925,14 +4992,21 @@ var OllamaClient = class {
3925
4992
  * `OLLAMA_API_KEY`), independent of whether `config.endpoints` was used instead.
3926
4993
  */
3927
4994
  cloudApiKey;
4995
+ /** Configured `defaultContextLength` — injected as `num_ctx` when a request omits it. */
4996
+ defaultContextLength;
4997
+ /** Configured `contextWarningThreshold` (default 0.9). */
4998
+ contextWarningThreshold;
4999
+ /** Configured overflow policy — `'warn'` (default) or `'throw'`. */
5000
+ onContextOverflow;
3928
5001
  constructor(config = {}) {
3929
- const resolvedApiKey = resolveApiKey(config.apiKey);
3930
- this.cloudApiKey = resolvedApiKey;
5002
+ this.cloudApiKey = resolveApiKey(config.apiKey);
5003
+ const defaultBaseUrl = resolveBaseUrl(config.baseUrl);
5004
+ const resolvedApiKey = resolveEndpointApiKey(config.apiKey, defaultBaseUrl);
3931
5005
  const credentialEndpoints = resolveCredentialEndpoints(config);
3932
5006
  const endpoints = config.endpoints ?? (credentialEndpoints.length > 0 ? [] : [
3933
5007
  {
3934
5008
  name: "default",
3935
- baseUrl: resolveBaseUrl(config.baseUrl),
5009
+ baseUrl: defaultBaseUrl,
3936
5010
  ...resolvedApiKey !== void 0 ? { apiKey: resolvedApiKey } : {},
3937
5011
  ...config.headers !== void 0 ? { headers: config.headers } : {}
3938
5012
  }
@@ -3948,6 +5022,9 @@ var OllamaClient = class {
3948
5022
  this.middleware = config.middleware;
3949
5023
  this.onLifecycleEvent = config.onLifecycleEvent;
3950
5024
  this.retryConfig = typeof config.retries === "number" ? { ...DEFAULT_RETRY_CONFIG, maxRetries: config.retries } : { ...DEFAULT_RETRY_CONFIG, ...config.retries };
5025
+ this.defaultContextLength = config.defaultContextLength;
5026
+ this.contextWarningThreshold = config.contextWarningThreshold;
5027
+ this.onContextOverflow = config.onContextOverflow ?? "warn";
3951
5028
  this.models = new ModelsClient((op, opts) => this.executeWithFailover(op, opts));
3952
5029
  }
3953
5030
  get modelsClient() {
@@ -4037,6 +5114,54 @@ var OllamaClient = class {
4037
5114
  return this._decision;
4038
5115
  }
4039
5116
  _decision;
5117
+ /**
5118
+ * Ergonomic OpenAI Responses API bridge (`client.responses.create()` /
5119
+ * `.stream()`). Dual-mode transport: prefers the native `POST
5120
+ * /v1/responses` endpoint (Ollama ≥ v0.13.3) and transparently re-issues via
5121
+ * `/api/chat` when the server answers 404, so migrating OpenAI code works
5122
+ * unchanged against older servers. See `src/responses.ts`.
5123
+ */
5124
+ get responses() {
5125
+ if (this._responses === void 0) {
5126
+ this._responses = new ResponsesModule((op, opts) => this.executeWithFailover(op, opts));
5127
+ }
5128
+ return this._responses;
5129
+ }
5130
+ _responses;
5131
+ session(modelOrOptions, systemPrompt) {
5132
+ const options = typeof modelOrOptions === "string" ? {
5133
+ model: modelOrOptions,
5134
+ ...systemPrompt !== void 0 ? { systemPrompt } : {}
5135
+ } : modelOrOptions;
5136
+ return new ConversationSession(this, options);
5137
+ }
5138
+ /**
5139
+ * Context-window pre-flight enforcement — see `src/context-safety.ts` and
5140
+ * the `defaultContextLength` / `onContextOverflow` config docs. Logs a
5141
+ * warning (default) or throws client-side when the estimated prompt size
5142
+ * is close to / beyond the effective context window.
5143
+ */
5144
+ enforceContextSafety(kind, check2) {
5145
+ if (!check2.exceedsThreshold) return;
5146
+ const message = contextWarningMessage(check2, kind);
5147
+ if (this.onContextOverflow === "throw") {
5148
+ throw new OllamaClientError(message, { code: "context_overflow" });
5149
+ }
5150
+ this.logger.warn(message);
5151
+ }
5152
+ /**
5153
+ * Returns `options` with `num_ctx` injected from the client's
5154
+ * `defaultContextLength` when the request didn't set one — making the
5155
+ * effective context window explicit on the wire instead of relying on
5156
+ * Ollama's silently-truncating server default. Returns the original
5157
+ * reference when there's nothing to inject (the common case).
5158
+ */
5159
+ injectDefaultContextLength(options) {
5160
+ if (options?.num_ctx !== void 0 || this.defaultContextLength === void 0) {
5161
+ return options;
5162
+ }
5163
+ return { ...options, num_ctx: this.defaultContextLength };
5164
+ }
4040
5165
  /**
4041
5166
  * Fail-fast guard for `format` (structured output) requests: throws before any network
4042
5167
  * call if the candidate endpoint is inferred as Ollama Cloud, which does not currently
@@ -4146,7 +5271,7 @@ var OllamaClient = class {
4146
5271
  }
4147
5272
  }
4148
5273
  async chat(req) {
4149
- const encodedMessages = await withEncodedMessageImages(req.messages);
5274
+ const encodedMessages = await withEncodedMessageImages(req.messages, req.signal);
4150
5275
  const messages = encodedMessages.map((message) => {
4151
5276
  if (message.role === "tool" && message.tool_call_id !== void 0) {
4152
5277
  const { tool_call_id: _toolCallId, ...nativeMessage } = message;
@@ -4154,6 +5279,14 @@ var OllamaClient = class {
4154
5279
  }
4155
5280
  return message;
4156
5281
  });
5282
+ this.enforceContextSafety(
5283
+ "chat",
5284
+ checkChatContext(req, {
5285
+ defaultContextLength: this.defaultContextLength,
5286
+ threshold: this.contextWarningThreshold
5287
+ })
5288
+ );
5289
+ const options = this.injectDefaultContextLength(req.options);
4157
5290
  if (req.stream) {
4158
5291
  return this.executeWithFailover(
4159
5292
  async (http, signal) => {
@@ -4161,7 +5294,7 @@ var OllamaClient = class {
4161
5294
  this.assertStructuredOutputSupported(http.baseUrl, req.model);
4162
5295
  const stream = await http.requestStream({
4163
5296
  path: "/api/chat",
4164
- body: { ...req, messages, stream: true },
5297
+ body: { ...req, options, messages, stream: true },
4165
5298
  signal
4166
5299
  });
4167
5300
  return normalizeChatStream(stream, signal);
@@ -4190,7 +5323,7 @@ var OllamaClient = class {
4190
5323
  this.assertStructuredOutputSupported(http.baseUrl, req.model);
4191
5324
  return http.request({
4192
5325
  path: "/api/chat",
4193
- body: { ...req, messages, stream: false },
5326
+ body: { ...req, options, messages, stream: false },
4194
5327
  signal
4195
5328
  });
4196
5329
  }, req);
@@ -4217,7 +5350,15 @@ var OllamaClient = class {
4217
5350
  return parseStructuredOutput(res.message.content, schema);
4218
5351
  }
4219
5352
  async generate(req) {
4220
- const encodedReq = await withEncodedImages(req);
5353
+ const encodedReq = await withEncodedImages(req, req.signal);
5354
+ this.enforceContextSafety(
5355
+ "generate",
5356
+ checkGenerateContext(req, {
5357
+ defaultContextLength: this.defaultContextLength,
5358
+ threshold: this.contextWarningThreshold
5359
+ })
5360
+ );
5361
+ const options = this.injectDefaultContextLength(req.options);
4221
5362
  if (encodedReq.stream) {
4222
5363
  return this.executeWithFailover(
4223
5364
  async (http, signal) => {
@@ -4225,7 +5366,7 @@ var OllamaClient = class {
4225
5366
  this.assertStructuredOutputSupported(http.baseUrl, encodedReq.model);
4226
5367
  const stream = await http.requestStream({
4227
5368
  path: "/api/generate",
4228
- body: { ...encodedReq, stream: true },
5369
+ body: { ...encodedReq, options, stream: true },
4229
5370
  signal
4230
5371
  });
4231
5372
  return normalizeGenerateStream(stream, signal);
@@ -4251,7 +5392,7 @@ var OllamaClient = class {
4251
5392
  this.assertStructuredOutputSupported(http.baseUrl, encodedReq.model);
4252
5393
  return http.request({
4253
5394
  path: "/api/generate",
4254
- body: { ...encodedReq, stream: false },
5395
+ body: { ...encodedReq, options, stream: false },
4255
5396
  signal
4256
5397
  });
4257
5398
  }, encodedReq);
@@ -4309,6 +5450,18 @@ var OllamaClient = class {
4309
5450
  version = () => this.models.version();
4310
5451
  createBlob = (digest, data) => this.models.createBlob(digest, data);
4311
5452
  checkBlob = (digest) => this.models.checkBlob(digest);
5453
+ /**
5454
+ * Immediately unloads `model` from VRAM. Convenience alias for
5455
+ * {@link ModelsClient.unload} — see that method for the full
5456
+ * lifecycle contract.
5457
+ */
5458
+ unloadModel = (model) => this.models.unload(model);
5459
+ /**
5460
+ * Pre-loads and pins `model` into VRAM indefinitely. Convenience
5461
+ * alias for {@link ModelsClient.pin} — see that method for the
5462
+ * full lifecycle contract.
5463
+ */
5464
+ pinModel = (model) => this.models.pin(model);
4312
5465
  // --- System One (Wave 13) ---
4313
5466
  /**
4314
5467
  * Ollama's System One decision layer (`POST /v1/systemone`) — evaluates
@@ -4378,6 +5531,63 @@ var OllamaClient = class {
4378
5531
  req
4379
5532
  );
4380
5533
  }
5534
+ /**
5535
+ * Ollama Cloud usage statistics (`GET https://ollama.com/api/usage`) — request counts,
5536
+ * USD spend, and token totals (including cached input tokens) for cloud inference,
5537
+ * web search, and web fetch, bucketed by hour (`range: '24h'`) or day (`'7d'`/`'30d'`).
5538
+ *
5539
+ * See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
5540
+ * behavior, which applies identically here. Query parameters mirror the official
5541
+ * endpoint: `range` (`'24h' | '7d' | '30d'`, server default `'7d'`) and `scope`
5542
+ * (`'self' | 'team'`, server default `'self'`; team scope requires a team admin).
5543
+ * Omitted options are left unset so the server applies its own defaults.
5544
+ *
5545
+ * ```ts
5546
+ * const usage = await client.usage({ range: '24h' });
5547
+ * console.log(usage.totals.request_count, usage.totals.usage_usd);
5548
+ * for (const bucket of usage.buckets) {
5549
+ * if (bucket.partial) continue; // current hour, still in progress
5550
+ * // ...
5551
+ * }
5552
+ * ```
5553
+ */
5554
+ usage(req = {}) {
5555
+ const params = new URLSearchParams();
5556
+ if (req.range !== void 0) params.set("range", req.range);
5557
+ if (req.scope !== void 0) params.set("scope", req.scope);
5558
+ const query = params.toString();
5559
+ return this.executeCloudRequest(
5560
+ (http, signal) => http.request({
5561
+ path: query ? `/api/usage?${query}` : "/api/usage",
5562
+ method: "GET",
5563
+ signal
5564
+ }),
5565
+ req
5566
+ );
5567
+ }
5568
+ /**
5569
+ * Ollama Cloud balance (`GET https://ollama.com/api/balance`) — remaining included
5570
+ * and purchased usage credits. The included balance is either a plan-period credit
5571
+ * object (`balance_usd`/`allowance_usd`/`period`) or, on legacy plans, session/weekly
5572
+ * percentage limits; the shape is discriminated at runtime by the server response.
5573
+ * See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
5574
+ * behavior, which applies identically here.
5575
+ *
5576
+ * ```ts
5577
+ * const balance = await client.balance();
5578
+ * if ('balance_usd' in balance.included) {
5579
+ * console.log(balance.included.balance_usd, balance.purchased.balance_usd);
5580
+ * } else {
5581
+ * console.log(balance.included.session.remaining_percent, '% of session limit left');
5582
+ * }
5583
+ * ```
5584
+ */
5585
+ balance(req = {}) {
5586
+ return this.executeCloudRequest(
5587
+ (http, signal) => http.request({ path: "/api/balance", method: "GET", signal }),
5588
+ req
5589
+ );
5590
+ }
4381
5591
  /**
4382
5592
  * Runs `operation` against the fixed Ollama Cloud host with this client's default
4383
5593
  * timeout and retry policy — the same primitives `executeWithFailover` uses, minus the
@@ -4481,6 +5691,91 @@ var OllamaClient = class {
4481
5691
  }
4482
5692
  };
4483
5693
 
5694
+ // src/transport/fetch-with-backoff.ts
5695
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([429, 503]);
5696
+ var DEFAULT_FETCH_BACKOFF_CONFIG = {
5697
+ maxRetries: 3
5698
+ };
5699
+ function sleep2(ms, signal) {
5700
+ return new Promise((resolve, reject) => {
5701
+ if (signal?.aborted) {
5702
+ reject(
5703
+ Object.assign(new Error("fetchWithBackoff aborted during backoff sleep"), {
5704
+ name: "AbortError"
5705
+ })
5706
+ );
5707
+ return;
5708
+ }
5709
+ const timer = setTimeout(() => {
5710
+ signal?.removeEventListener("abort", onAbort);
5711
+ resolve();
5712
+ }, ms);
5713
+ const onAbort = () => {
5714
+ clearTimeout(timer);
5715
+ signal?.removeEventListener("abort", onAbort);
5716
+ reject(
5717
+ Object.assign(new Error("fetchWithBackoff aborted during backoff sleep"), {
5718
+ name: "AbortError"
5719
+ })
5720
+ );
5721
+ };
5722
+ signal?.addEventListener("abort", onAbort, { once: true });
5723
+ });
5724
+ }
5725
+ async function fetchWithBackoff(input, init, config = {}) {
5726
+ const fetchImpl = config.fetch ?? globalThis.fetch;
5727
+ if (typeof fetchImpl !== "function") {
5728
+ throw new Error(
5729
+ "fetchWithBackoff: no global fetch is available. Pass a fetch implementation via config.fetch, or run in Node.js >= 18 / a browser-like environment."
5730
+ );
5731
+ }
5732
+ const maxRetries = config.maxRetries ?? DEFAULT_FETCH_BACKOFF_CONFIG.maxRetries;
5733
+ const backoff = config.backoff ?? DEFAULT_BACKOFF;
5734
+ const signal = config.signal;
5735
+ let lastError;
5736
+ for (let attempt = 0; attempt <= maxRetries; attempt += 1) {
5737
+ if (signal?.aborted) {
5738
+ throw Object.assign(new Error("fetchWithBackoff aborted"), { name: "AbortError" });
5739
+ }
5740
+ try {
5741
+ const response = await fetchImpl(input, {
5742
+ ...init,
5743
+ ...signal !== void 0 ? { signal } : {}
5744
+ });
5745
+ if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries) {
5746
+ const delayMs = calculateBackoff(attempt, backoff);
5747
+ config.onRetry?.(attempt, response.status, delayMs);
5748
+ await sleep2(delayMs, signal);
5749
+ continue;
5750
+ }
5751
+ return response;
5752
+ } catch (err) {
5753
+ lastError = err;
5754
+ if (signal?.aborted || err instanceof Error && err.name === "AbortError") {
5755
+ throw err;
5756
+ }
5757
+ if (attempt < maxRetries) {
5758
+ const delayMs = calculateBackoff(attempt, backoff);
5759
+ config.onRetry?.(attempt, 0, delayMs);
5760
+ await sleep2(delayMs, signal);
5761
+ continue;
5762
+ }
5763
+ }
5764
+ }
5765
+ throw lastError ?? new Error("fetchWithBackoff: exhausted retry attempts");
5766
+ }
5767
+
5768
+ // src/done-reason.ts
5769
+ var KNOWN_DONE_REASONS = /* @__PURE__ */ new Set([
5770
+ "stop",
5771
+ "length",
5772
+ "load",
5773
+ "unload"
5774
+ ]);
5775
+ function isKnownDoneReason(value) {
5776
+ return value !== void 0 && value !== null && KNOWN_DONE_REASONS.has(value);
5777
+ }
5778
+
4484
5779
  // src/streaming/adapters.ts
4485
5780
  function toTextStream(stream) {
4486
5781
  const iterator = stream[Symbol.asyncIterator]();
@@ -4936,9 +6231,9 @@ function validate(value, schema, path) {
4936
6231
  ["exclusiveMinimum", (n, b) => n > b, "must be greater than"],
4937
6232
  ["exclusiveMaximum", (n, b) => n < b, "must be less than"]
4938
6233
  ];
4939
- for (const [keyword, check, phrase] of numericRules) {
6234
+ for (const [keyword, check2, phrase] of numericRules) {
4940
6235
  const bound = s[keyword];
4941
- if (typeof bound === "number" && !check(value, bound)) issues.push({ path, message: label(path) + " " + phrase + " " + bound });
6236
+ if (typeof bound === "number" && !check2(value, bound)) issues.push({ path, message: label(path) + " " + phrase + " " + bound });
4942
6237
  }
4943
6238
  const multipleOf = s["multipleOf"];
4944
6239
  if (typeof multipleOf === "number" && multipleOf > 0) {
@@ -5199,8 +6494,6 @@ var McpBridge = class _McpBridge {
5199
6494
  this.client = client;
5200
6495
  this.options = options;
5201
6496
  }
5202
- client;
5203
- options;
5204
6497
  static toOllamaTools(tools, options = {}) {
5205
6498
  const prefix = options.namePrefix ?? "";
5206
6499
  return tools.map((tool) => ({
@@ -5497,6 +6790,13 @@ var Agent = class {
5497
6790
  }
5498
6791
  };
5499
6792
 
6793
+ // src/system-one.ts
6794
+ var MAX_SYSTEM_ONE_IMAGES_BYTES = 32 * 1024 * 1024;
6795
+ var MAX_SYSTEM_ONE_REQUEST_BYTES = 64 * 1024;
6796
+ function estimateSystemOneRequestBytes(request) {
6797
+ return new TextEncoder().encode(JSON.stringify(request)).byteLength;
6798
+ }
6799
+
5500
6800
  exports.ATTR_GEN_AI_OPERATION_NAME = ATTR_GEN_AI_OPERATION_NAME;
5501
6801
  exports.ATTR_GEN_AI_REQUEST_MODEL = ATTR_GEN_AI_REQUEST_MODEL;
5502
6802
  exports.ATTR_GEN_AI_RESPONSE_MODEL = ATTR_GEN_AI_RESPONSE_MODEL;
@@ -5517,24 +6817,35 @@ exports.ATTR_URL_FULL = ATTR_URL_FULL;
5517
6817
  exports.Agent = Agent;
5518
6818
  exports.AnthropicCompatClient = AnthropicCompatClient;
5519
6819
  exports.AnthropicMessagesStream = AnthropicMessagesStream;
6820
+ exports.ConversationSession = ConversationSession;
5520
6821
  exports.DEFAULT_BACKOFF = DEFAULT_BACKOFF;
5521
6822
  exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
6823
+ exports.DEFAULT_CONTEXT_WARNING_THRESHOLD = DEFAULT_CONTEXT_WARNING_THRESHOLD;
5522
6824
  exports.DEFAULT_FAILOVER_CODES = DEFAULT_FAILOVER_CODES;
6825
+ exports.DEFAULT_FETCH_BACKOFF_CONFIG = DEFAULT_FETCH_BACKOFF_CONFIG;
5523
6826
  exports.DEFAULT_RETRY_CONFIG = DEFAULT_RETRY_CONFIG;
5524
6827
  exports.DEFAULT_TIMEOUT_MS = DEFAULT_TIMEOUT_MS;
5525
6828
  exports.EndpointRegistry = EndpointRegistry;
5526
6829
  exports.GEN_AI_SYSTEM_OLLAMA = GEN_AI_SYSTEM_OLLAMA;
5527
6830
  exports.HttpClient = HttpClient;
6831
+ exports.IMAGE_TOKEN_ESTIMATE = IMAGE_TOKEN_ESTIMATE;
6832
+ exports.KEEP_ALIVE_INDEFINITE = KEEP_ALIVE_INDEFINITE;
6833
+ exports.KEEP_ALIVE_UNLOAD = KEEP_ALIVE_UNLOAD;
6834
+ exports.KNOWN_DONE_REASONS = KNOWN_DONE_REASONS;
6835
+ exports.MAX_SYSTEM_ONE_IMAGES_BYTES = MAX_SYSTEM_ONE_IMAGES_BYTES;
6836
+ exports.MAX_SYSTEM_ONE_REQUEST_BYTES = MAX_SYSTEM_ONE_REQUEST_BYTES;
5528
6837
  exports.McpBridge = McpBridge;
5529
6838
  exports.ModelsClient = ModelsClient;
5530
6839
  exports.NANOS_PER_MS = NANOS_PER_MS;
5531
6840
  exports.NANOS_PER_SECOND = NANOS_PER_SECOND;
5532
6841
  exports.NOOP_LOGGER = NOOP_LOGGER;
5533
6842
  exports.OLLAMA_CLOUD_BASE_URL = OLLAMA_CLOUD_BASE_URL;
6843
+ exports.OLLAMA_FALLBACK_CONTEXT_LENGTH = OLLAMA_FALLBACK_CONTEXT_LENGTH;
5534
6844
  exports.OllamaAbortError = OllamaAbortError;
5535
6845
  exports.OllamaAgentMaxIterationsError = OllamaAgentMaxIterationsError;
5536
6846
  exports.OllamaAgentMaxToolCallsError = OllamaAgentMaxToolCallsError;
5537
6847
  exports.OllamaAuthError = OllamaAuthError;
6848
+ exports.OllamaBadGatewayError = OllamaBadGatewayError;
5538
6849
  exports.OllamaClient = OllamaClient;
5539
6850
  exports.OllamaClientError = OllamaClientError;
5540
6851
  exports.OllamaGenericClientError = OllamaGenericClientError;
@@ -5543,6 +6854,7 @@ exports.OllamaMcpError = OllamaMcpError;
5543
6854
  exports.OllamaModelRoutingError = OllamaModelRoutingError;
5544
6855
  exports.OllamaNetworkError = OllamaNetworkError;
5545
6856
  exports.OllamaNotFoundError = OllamaNotFoundError;
6857
+ exports.OllamaOverloadedError = OllamaOverloadedError;
5546
6858
  exports.OllamaQuotaExceededError = OllamaQuotaExceededError;
5547
6859
  exports.OllamaRateLimitError = OllamaRateLimitError;
5548
6860
  exports.OllamaRequestTooLargeError = OllamaRequestTooLargeError;
@@ -5553,6 +6865,7 @@ exports.OllamaServerVersionUnknownError = OllamaServerVersionUnknownError;
5553
6865
  exports.OllamaSkillInvalidError = OllamaSkillInvalidError;
5554
6866
  exports.OllamaSkillNotFoundError = OllamaSkillNotFoundError;
5555
6867
  exports.OllamaStream = OllamaStream;
6868
+ exports.OllamaStreamError = OllamaStreamError;
5556
6869
  exports.OllamaTimeoutError = OllamaTimeoutError;
5557
6870
  exports.OllamaToolTimeoutError = OllamaToolTimeoutError;
5558
6871
  exports.OllamaToolValidationError = OllamaToolValidationError;
@@ -5563,12 +6876,18 @@ exports.OpenAICompletionStream = OpenAICompletionStream;
5563
6876
  exports.OpenAIResponsesStream = OpenAIResponsesStream;
5564
6877
  exports.OpenAIResponsesStreamError = OpenAIResponsesStreamError;
5565
6878
  exports.QuotaManager = QuotaManager;
6879
+ exports.RETRYABLE_STATUS_CODES = RETRYABLE_STATUS_CODES;
6880
+ exports.ResponsesModule = ResponsesModule;
6881
+ exports.ToolCallAccumulator = ToolCallAccumulator;
5566
6882
  exports.ToolRegistry = ToolRegistry;
5567
6883
  exports.addSpanEvent = addSpanEvent;
5568
6884
  exports.applySkill = applySkill;
5569
6885
  exports.calculateBackoff = calculateBackoff;
6886
+ exports.checkChatContext = checkChatContext;
5570
6887
  exports.checkEndpointHealth = checkEndpointHealth;
6888
+ exports.checkGenerateContext = checkGenerateContext;
5571
6889
  exports.composeMiddleware = composeMiddleware;
6890
+ exports.contextWarningMessage = contextWarningMessage;
5572
6891
  exports.createConsoleLogger = createConsoleLogger;
5573
6892
  exports.createDecision = createDecision;
5574
6893
  exports.createOllamaCloudFreeTierQuota = createOllamaCloudFreeTierQuota;
@@ -5576,14 +6895,27 @@ exports.createTimeoutSignal = createTimeoutSignal;
5576
6895
  exports.defineTool = defineTool;
5577
6896
  exports.detectModelCapabilities = detectModelCapabilities;
5578
6897
  exports.encodeImage = encodeImage;
6898
+ exports.estimateChatRequestTokens = estimateChatRequestTokens;
6899
+ exports.estimateGenerateRequestTokens = estimateGenerateRequestTokens;
6900
+ exports.estimateSystemOneRequestBytes = estimateSystemOneRequestBytes;
6901
+ exports.estimateTokens = estimateTokens;
5579
6902
  exports.extractUsage = extractUsage;
6903
+ exports.fetchWithBackoff = fetchWithBackoff;
6904
+ exports.imageStringNeedsResolution = imageStringNeedsResolution;
5580
6905
  exports.inferRuntimeMode = inferRuntimeMode;
6906
+ exports.isKeepAliveSugar = isKeepAliveSugar;
6907
+ exports.isKnownDoneReason = isKnownDoneReason;
6908
+ exports.isSameToolCall = isSameToolCall;
5581
6909
  exports.listAllMcpTools = listAllMcpTools;
5582
6910
  exports.listAvailableModels = listAvailableModels;
5583
6911
  exports.loadMcpTools = loadMcpTools;
5584
6912
  exports.mapError = mapError;
6913
+ exports.mergeToolCall = mergeToolCall;
6914
+ exports.mergeToolCallArgumentsString = mergeToolCallArgumentsString;
6915
+ exports.mergeToolCallArrays = mergeToolCallArrays;
5585
6916
  exports.normalizeChatStream = normalizeChatStream;
5586
6917
  exports.normalizeGenerateStream = normalizeGenerateStream;
6918
+ exports.normalizeKeepAlive = normalizeKeepAlive;
5587
6919
  exports.normalizeProgressStream = normalizeProgressStream;
5588
6920
  exports.parseFrontmatter = parseFrontmatter;
5589
6921
  exports.parseNdjsonStream = parseNdjsonStream;
@@ -5591,6 +6923,7 @@ exports.parseSseStream = parseSseStream;
5591
6923
  exports.parseStructuredOutput = parseStructuredOutput;
5592
6924
  exports.registerMcpTools = registerMcpTools;
5593
6925
  exports.resolveCredentialEndpoints = resolveCredentialEndpoints;
6926
+ exports.resolveImageInput = resolveImageInput;
5594
6927
  exports.setSpanError = setSpanError;
5595
6928
  exports.toDataStream = toDataStream;
5596
6929
  exports.toResponse = toResponse;