@nemesis-oss/ollama-sdk 1.19.0 → 1.20.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 +46 -0
  2. package/README.md +8 -8
  3. package/dist/{anthropic-CAvwqIjx.d.ts → anthropic-BFOC6eI7.d.ts} +7 -1
  4. package/dist/{anthropic-DIPnBwlf.d.cts → anthropic-C2-qaxjI.d.cts} +7 -1
  5. package/dist/{compose-C4QxEZOS.d.cts → compose-BXXgHmrS.d.cts} +1 -1
  6. package/dist/{compose-75UFDzOO.d.ts → compose-DaG83yId.d.ts} +1 -1
  7. package/dist/generated-api.cjs +1 -1
  8. package/dist/generated-api.cjs.map +1 -1
  9. package/dist/generated-api.d.cts +7 -7
  10. package/dist/generated-api.d.ts +7 -7
  11. package/dist/generated-api.js +1 -1
  12. package/dist/generated-api.js.map +1 -1
  13. package/dist/generated-models-schemas.cjs +25 -2
  14. package/dist/generated-models-schemas.cjs.map +1 -1
  15. package/dist/generated-models-schemas.d.cts +97 -5
  16. package/dist/generated-models-schemas.d.ts +97 -5
  17. package/dist/generated-models-schemas.js +24 -3
  18. package/dist/generated-models-schemas.js.map +1 -1
  19. package/dist/generated-runtime.cjs +23 -2
  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 +23 -2
  24. package/dist/generated-runtime.js.map +1 -1
  25. package/dist/index.cjs +227 -28
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +169 -46
  28. package/dist/index.d.ts +169 -46
  29. package/dist/index.js +222 -29
  30. package/dist/index.js.map +1 -1
  31. package/dist/mcp/tools.json +1 -1
  32. package/dist/mcp-generated.cjs +1 -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 +1 -1
  37. package/dist/mcp-generated.js.map +1 -1
  38. package/dist/{runtime-C28YEAHC.d.cts → runtime-CpO3H6yy.d.cts} +55 -2
  39. package/dist/{runtime-DYCxYGG-.d.ts → runtime-DPSrPwAa.d.ts} +55 -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-Y3u0FtdN.d.cts → types-CnsZ24tm.d.cts} +17 -1
  45. package/dist/{types-Y3u0FtdN.d.ts → types-CnsZ24tm.d.ts} +17 -1
  46. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -301,6 +301,17 @@ var OllamaToolTimeoutError = class extends OllamaClientError {
301
301
  this.timeoutMs = options.timeoutMs;
302
302
  }
303
303
  };
304
+ var OllamaCloudModelVersionError = class extends OllamaClientError {
305
+ model;
306
+ serverVersion;
307
+ minRequiredVersion;
308
+ constructor(message, options) {
309
+ super(message, { ...options, code: "cloud_model_version", retryable: false });
310
+ this.model = options.model;
311
+ this.serverVersion = options.serverVersion;
312
+ this.minRequiredVersion = options.minRequiredVersion;
313
+ }
314
+ };
304
315
  var OllamaUnsupportedCapabilityError = class extends OllamaClientError {
305
316
  capability;
306
317
  constructor(message, options) {
@@ -426,6 +437,43 @@ function isNetworkLevelError(error) {
426
437
  }
427
438
  return false;
428
439
  }
440
+ function lookupHeader(headers, name) {
441
+ if (headers === void 0) {
442
+ return void 0;
443
+ }
444
+ const needle = name.toLowerCase();
445
+ for (const [key, value] of Object.entries(headers)) {
446
+ if (key.toLowerCase() === needle) {
447
+ return value;
448
+ }
449
+ }
450
+ return void 0;
451
+ }
452
+ var HTTP_DATE_PATTERN = /^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), \d{2} (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) \d{4} \d{2}:\d{2}:\d{2} GMT$/;
453
+ function parseRetryAfterMs(headerValue, now = Date.now()) {
454
+ if (headerValue === void 0) {
455
+ return void 0;
456
+ }
457
+ const trimmed = headerValue.trim();
458
+ if (trimmed === "") {
459
+ return void 0;
460
+ }
461
+ if (/^\d+$/.test(trimmed)) {
462
+ const seconds = Number(trimmed);
463
+ if (!Number.isSafeInteger(seconds)) {
464
+ return void 0;
465
+ }
466
+ return seconds * 1e3;
467
+ }
468
+ if (!HTTP_DATE_PATTERN.test(trimmed)) {
469
+ return void 0;
470
+ }
471
+ const instant = Date.parse(trimmed);
472
+ if (Number.isNaN(instant)) {
473
+ return void 0;
474
+ }
475
+ return Math.max(0, instant - now);
476
+ }
429
477
  function statusToError(status, message, options) {
430
478
  if (status === 401 || status === 403) {
431
479
  return new OllamaAuthError(message, { ...options, status });
@@ -437,7 +485,8 @@ function statusToError(status, message, options) {
437
485
  return new OllamaNotFoundError(message, { ...options, status });
438
486
  }
439
487
  if (status === 429) {
440
- return new OllamaRateLimitError(message, { ...options, status });
488
+ const retryAfterMs = parseRetryAfterMs(lookupHeader(options.response?.headers, "retry-after"));
489
+ return new OllamaRateLimitError(message, { ...options, status, retryAfterMs });
441
490
  }
442
491
  if (status === 502) {
443
492
  return new OllamaBadGatewayError(message, { ...options });
@@ -1140,6 +1189,21 @@ function sameHeaders(actual, expected) {
1140
1189
  const expectedKeys = Object.keys(expectedEntries);
1141
1190
  return actualKeys.length === expectedKeys.length && expectedKeys.every((key) => actualEntries[key] === expectedEntries[key]);
1142
1191
  }
1192
+ function retryAfterHeaderFrom(response) {
1193
+ try {
1194
+ const headers = response?.headers;
1195
+ if (headers === null || headers === void 0 || typeof headers.get !== "function") {
1196
+ return void 0;
1197
+ }
1198
+ const value = headers.get("retry-after");
1199
+ if (value === null || value === "") {
1200
+ return void 0;
1201
+ }
1202
+ return { "retry-after": value };
1203
+ } catch {
1204
+ return void 0;
1205
+ }
1206
+ }
1143
1207
  var HttpClient = class {
1144
1208
  baseUrl;
1145
1209
  apiKey;
@@ -1207,7 +1271,12 @@ var HttpClient = class {
1207
1271
  durationMs: Date.now() - startedAt,
1208
1272
  error: mapError(new Error(`HTTP ${response2.status}`), {
1209
1273
  request: { method: request.method, url: request.url },
1210
- response: { status: response2.status }
1274
+ // Mirrors the middleware path, which reports context.headers:
1275
+ // a 429 lifecycle event carries the server's Retry-After.
1276
+ response: {
1277
+ status: response2.status,
1278
+ headers: retryAfterHeaderFrom(response2)
1279
+ }
1211
1280
  }),
1212
1281
  timestamp: Date.now()
1213
1282
  });
@@ -1313,7 +1382,11 @@ var HttpClient = class {
1313
1382
  const message = typeof errorBody === "object" && errorBody !== null && "error" in errorBody ? String(errorBody.error) : `HTTP ${response.status} ${response.statusText}`;
1314
1383
  throw mapError(new Error(message), {
1315
1384
  request: { method, url },
1316
- response: { status: response.status, body: errorBody }
1385
+ response: {
1386
+ status: response.status,
1387
+ headers: retryAfterHeaderFrom(response),
1388
+ body: errorBody
1389
+ }
1317
1390
  });
1318
1391
  }
1319
1392
  if (options.method === "HEAD" || response.status === 204) {
@@ -1378,7 +1451,11 @@ var HttpClient = class {
1378
1451
  const errorText = await response.text();
1379
1452
  throw mapError(new Error(errorText || `HTTP ${response.status}`), {
1380
1453
  request: { method, url },
1381
- response: { status: response.status, body: errorText }
1454
+ response: {
1455
+ status: response.status,
1456
+ headers: retryAfterHeaderFrom(response),
1457
+ body: errorText
1458
+ }
1382
1459
  });
1383
1460
  }
1384
1461
  if (!response.body) {
@@ -1442,7 +1519,11 @@ var HttpClient = class {
1442
1519
  const errorText = await response.text();
1443
1520
  throw mapError(new Error(errorText || `HTTP ${response.status}`), {
1444
1521
  request: { method, url },
1445
- response: { status: response.status, body: errorText }
1522
+ response: {
1523
+ status: response.status,
1524
+ headers: retryAfterHeaderFrom(response),
1525
+ body: errorText
1526
+ }
1446
1527
  });
1447
1528
  }
1448
1529
  if (!response.body) {
@@ -1748,7 +1829,7 @@ function contextWarningMessage(check2, kind) {
1748
1829
  if (check2.windowSource === "client-default") {
1749
1830
  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.${suggestion} Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
1750
1831
  }
1751
- 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.${suggestion} Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
1832
+ return `${where}: estimated prompt size ${estimate} outright exceeds the worst-case floor for the unset-\`num_ctx\` window (${OLLAMA_FALLBACK_CONTEXT_LENGTH} tokens \u2014 current daemons default higher, tiered by VRAM: 4096 / 32768 / 262144; \`OLLAMA_CONTEXT_LENGTH\` can override either way). If your daemon's effective window is that small, 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 \u2014 or discover the real one via \`client.models.getContextLength()\` \u2014 to make truncation visible and controllable.${suggestion} Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
1752
1833
  }
1753
1834
 
1754
1835
  // src/context-discovery.ts
@@ -2476,6 +2557,12 @@ function sleep(ms, signal) {
2476
2557
  signal?.addEventListener("abort", onAbort, { once: true });
2477
2558
  });
2478
2559
  }
2560
+ function retryDelayMs(error, attempt, backoff) {
2561
+ if (error instanceof OllamaRateLimitError && error.retryAfterMs !== void 0) {
2562
+ return error.retryAfterMs;
2563
+ }
2564
+ return calculateBackoff(attempt, backoff);
2565
+ }
2479
2566
  async function withRetry(operation, config = DEFAULT_RETRY_CONFIG, signal) {
2480
2567
  let attempt = 0;
2481
2568
  while (true) {
@@ -2487,7 +2574,7 @@ async function withRetry(operation, config = DEFAULT_RETRY_CONFIG, signal) {
2487
2574
  if (attempt >= config.maxRetries || !shouldRetry(error, attempt)) {
2488
2575
  throw error;
2489
2576
  }
2490
- const delayMs = calculateBackoff(attempt, config.backoff);
2577
+ const delayMs = retryDelayMs(error, attempt, config.backoff);
2491
2578
  config.onRetry?.(error, attempt, delayMs);
2492
2579
  await sleep(delayMs, signal);
2493
2580
  attempt += 1;
@@ -2656,7 +2743,8 @@ var ModelsClient = class {
2656
2743
  }
2657
2744
  /**
2658
2745
  * Discovers the model's real context window from the server — no more
2659
- * 2048/4096 guesswork. Consults, in precedence order:
2746
+ * default-window guesswork (current daemons tier the unset-`num_ctx`
2747
+ * default by VRAM: 4k / 32k / 256k). Consults, in precedence order:
2660
2748
  *
2661
2749
  * 1. `GET /api/ps` — the window the **running instance actually
2662
2750
  * allocated** (exact, reflects the Modelfile `num_ctx` default and
@@ -2668,7 +2756,7 @@ var ModelsClient = class {
2668
2756
  * 3. `POST /api/show` `model_info["<arch>.context_length"]` — the
2669
2757
  * model's **native GGUF maximum** (a ceiling to raise `num_ctx`
2670
2758
  * toward, not the allocated window).
2671
- * 4. Fallback — Ollama's conservative unset-`num_ctx` default (2048).
2759
+ * 4. Fallback — the 2048 worst-case floor for the unset-`num_ctx` window.
2672
2760
  *
2673
2761
  * The result carries every signal it found, so `nativeContextLength:
2674
2762
  * 131072` vs `contextLength: 4096` (a 32× gap seen in the official API
@@ -4154,6 +4242,9 @@ var ShowResponseSchema = z.object({
4154
4242
  modified_at: z.string().optional(),
4155
4243
  details: z.object({}).optional(),
4156
4244
  template: z.string().optional(),
4245
+ renderer: z.string().optional(),
4246
+ parser: z.string().optional(),
4247
+ template_source: z.string().optional(),
4157
4248
  capabilities: z.array(z.string()).optional(),
4158
4249
  model_info: z.object({}).optional()
4159
4250
  });
@@ -4245,9 +4336,26 @@ var UsageMetricsSchema = z.object({
4245
4336
  output_tokens: z.number().optional()
4246
4337
  });
4247
4338
 
4339
+ // src/generated/models/UsageGroup.schema.ts
4340
+ var UsageGroupSchema = z.intersection(
4341
+ UsageMetricsSchema,
4342
+ z.object({
4343
+ model: z.string()
4344
+ })
4345
+ );
4346
+
4347
+ // src/generated/models/UsageSummary.schema.ts
4348
+ var UsageSummarySchema = z.intersection(
4349
+ UsageMetricsSchema,
4350
+ z.object({
4351
+ groups: z.array(UsageGroupSchema).optional(),
4352
+ remainder: UsageMetricsSchema.optional()
4353
+ })
4354
+ );
4355
+
4248
4356
  // src/generated/models/UsageBucket.schema.ts
4249
4357
  var UsageBucketSchema = z.intersection(
4250
- UsageMetricsSchema,
4358
+ UsageSummarySchema,
4251
4359
  z.object({
4252
4360
  from: z.string(),
4253
4361
  until: z.string(),
@@ -4260,9 +4368,10 @@ var UsageResponseSchema = z.object({
4260
4368
  range: z.enum(["24h", "7d", "30d"]),
4261
4369
  scope: z.enum(["self", "team"]),
4262
4370
  granularity: z.enum(["hour", "day"]),
4371
+ group_by: z.enum(["model"]).optional(),
4263
4372
  from: z.string(),
4264
4373
  until: z.string(),
4265
- totals: UsageMetricsSchema,
4374
+ totals: UsageSummarySchema,
4266
4375
  buckets: z.array(UsageBucketSchema)
4267
4376
  });
4268
4377
  var VersionResponseSchema = z.object({
@@ -5705,6 +5814,29 @@ function createDecision(client) {
5705
5814
  };
5706
5815
  }
5707
5816
 
5817
+ // src/system-one.ts
5818
+ var MAX_SYSTEM_ONE_IMAGES_BYTES = 32 * 1024 * 1024;
5819
+ var MAX_SYSTEM_ONE_REQUEST_BYTES = 64 * 1024;
5820
+ var CLOUD_MODEL_SUFFIX = ":cloud";
5821
+ var CLOUD_MODEL_MIN_OLLAMA_VERSION = "0.40.5";
5822
+ function isCloudModel(model) {
5823
+ return model.endsWith(CLOUD_MODEL_SUFFIX);
5824
+ }
5825
+ function compareOllamaVersions(a, b) {
5826
+ const pa = a.split(".").map((s) => Number.parseInt(s, 10) || 0);
5827
+ const pb = b.split(".").map((s) => Number.parseInt(s, 10) || 0);
5828
+ for (let i = 0; i < Math.max(pa.length, pb.length); i += 1) {
5829
+ const va = pa[i] ?? 0;
5830
+ const vb = pb[i] ?? 0;
5831
+ if (va !== vb) return va - vb;
5832
+ }
5833
+ return 0;
5834
+ }
5835
+ function estimateSystemOneRequestBytes(request, options) {
5836
+ const body = options?.excludeImages && request.images !== void 0 ? { ...request, images: void 0 } : request;
5837
+ return new TextEncoder().encode(JSON.stringify(body)).byteLength;
5838
+ }
5839
+
5708
5840
  // src/client.ts
5709
5841
  var logicalRequestSequence = 0;
5710
5842
  function createLogicalRequestId() {
@@ -6331,9 +6463,13 @@ var OllamaClient = class _OllamaClient {
6331
6463
  * launch): `model` also accepts cloud models such as `clef:cloud` —
6332
6464
  * cloud models require Ollama >= 0.40.5 or the hosted
6333
6465
  * `https://ollama.com` endpoint. Requires Ollama >= 0.35.0 locally.
6334
- * The runtime enforces the version constraint automatically (lazily
6466
+ * Both version constraints are enforced client-side before the request
6467
+ * is sent: the blanket 0.35.0 floor by the generated runtime (lazily
6335
6468
  * fetching `/api/version` when `enforceVersion` is `'auto'` or
6336
- * `'strict'`). Request size is checked client-side as a dual cap:
6469
+ * `'strict'`), and the model-dependent 0.40.5 cloud gate by this
6470
+ * wrapper — a cloud model through a local server older than 0.40.5
6471
+ * throws {@link OllamaCloudModelVersionError} without a round-trip
6472
+ * (ADR 0036). Request size is checked client-side as a dual cap:
6337
6473
  * text and schema (images excluded) within 64 KiB AND the whole
6338
6474
  * request within 32 MiB.
6339
6475
  *
@@ -6358,10 +6494,44 @@ var OllamaClient = class _OllamaClient {
6358
6494
  * `client.native.systemOne(request)` directly.
6359
6495
  */
6360
6496
  async systemOne(request) {
6497
+ await this.assertCloudModelSupported(request.model);
6361
6498
  return this.native.systemOne(
6362
6499
  request
6363
6500
  );
6364
6501
  }
6502
+ /**
6503
+ * Cloud-model version gate for {@link systemOne} — throws
6504
+ * {@link OllamaCloudModelVersionError} when a cloud model is requested
6505
+ * through a local server older than {@link CLOUD_MODEL_MIN_OLLAMA_VERSION}
6506
+ * (0.40.5). No-op for non-cloud models and for cloud-mode clients
6507
+ * (hosted `ollama.com`). The `/api/version` probe goes through the normal
6508
+ * failover machinery and is cached per client instance (reset on failure,
6509
+ * so a later gated call retries), so at most one extra round-trip happens
6510
+ * across all gated calls.
6511
+ */
6512
+ async assertCloudModelSupported(model) {
6513
+ if (!isCloudModel(model)) return;
6514
+ const candidates = this.registry.candidates();
6515
+ const baseUrl = candidates[0]?.baseUrl ?? DEFAULT_BASE_URL;
6516
+ if (inferRuntimeMode(baseUrl) !== "local") return;
6517
+ let version;
6518
+ try {
6519
+ if (this.serverVersionCache === void 0) {
6520
+ this.serverVersionCache = this.native.version().then((v) => v.version);
6521
+ }
6522
+ version = await this.serverVersionCache;
6523
+ } catch {
6524
+ this.serverVersionCache = void 0;
6525
+ return;
6526
+ }
6527
+ if (version && compareOllamaVersions(version, CLOUD_MODEL_MIN_OLLAMA_VERSION) < 0) {
6528
+ throw new OllamaCloudModelVersionError(
6529
+ `System One cloud model "${model}" requires Ollama >= ${CLOUD_MODEL_MIN_OLLAMA_VERSION} when routed through a local server (this server reports ${version}). Upgrade Ollama, or point an endpoint at https://ollama.com.`,
6530
+ { model, serverVersion: version, minRequiredVersion: CLOUD_MODEL_MIN_OLLAMA_VERSION }
6531
+ );
6532
+ }
6533
+ }
6534
+ serverVersionCache;
6365
6535
  // --- Web Endpoints ---
6366
6536
  /**
6367
6537
  * Ollama's hosted web search tool (`POST https://ollama.com/api/web_search`) — a fixed
@@ -6369,7 +6539,9 @@ var OllamaClient = class _OllamaClient {
6369
6539
  * through a local server. Requires an Ollama account API key: pass `apiKey` to the
6370
6540
  * client constructor or set `OLLAMA_API_KEY`. Applies the same default `timeoutMs` and
6371
6541
  * retry policy as inference calls, but never cross-endpoint-fails-over — there is only
6372
- * ever the one cloud endpoint to call.
6542
+ * ever the one cloud endpoint to call. If the service answers 429 with a `Retry-After`
6543
+ * header, the transport parses it into `OllamaRateLimitError.retryAfterMs` and the
6544
+ * retry loop sleeps exactly that long instead of jittered backoff (see ADR 0035).
6373
6545
  */
6374
6546
  async webSearch(req) {
6375
6547
  const { count, max_results, ...rest } = req;
@@ -6400,14 +6572,24 @@ var OllamaClient = class _OllamaClient {
6400
6572
  * web search, and web fetch, bucketed by hour (`range: '24h'`) or day (`'7d'`/`'30d'`).
6401
6573
  *
6402
6574
  * See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
6403
- * behavior, which applies identically here. Query parameters mirror the official
6404
- * endpoint: `range` (`'24h' | '7d' | '30d'`, server default `'7d'`) and `scope`
6405
- * (`'self' | 'team'`, server default `'self'`; team scope requires a team admin).
6575
+ * behavior, which applies identically here. The pinned contract documents this
6576
+ * endpoint's 429 with a `Retry-After` header ("Seconds to wait before retrying"),
6577
+ * which the SDK parses into `OllamaRateLimitError.retryAfterMs` and honors verbatim
6578
+ * when retrying. Query parameters mirror the official endpoint: `range`
6579
+ * (`'24h' | '7d' | '30d'`, server default `'7d'`), `scope`
6580
+ * (`'self' | 'team'`, server default `'self'`; team scope requires a team
6581
+ * admin), and — since the 2026-10-22 pin (upstream 2476d5e) — `groupBy`
6582
+ * (`group_by=model`), which makes `totals` and every bucket carry up to 10
6583
+ * per-model `UsageGroup` entries plus a `remainder` roll-up, with the
6584
+ * selection echoed back as `group_by` on the response.
6406
6585
  * Omitted options are left unset so the server applies its own defaults.
6407
6586
  *
6408
6587
  * ```ts
6409
- * const usage = await client.usage({ range: '24h' });
6588
+ * const usage = await client.usage({ range: '24h', groupBy: 'model' });
6410
6589
  * console.log(usage.totals.request_count, usage.totals.usage_usd);
6590
+ * for (const group of usage.totals.groups ?? []) {
6591
+ * console.log(group.model, group.usage_usd);
6592
+ * }
6411
6593
  * for (const bucket of usage.buckets) {
6412
6594
  * if (bucket.partial) continue; // current hour, still in progress
6413
6595
  * // ...
@@ -6418,6 +6600,7 @@ var OllamaClient = class _OllamaClient {
6418
6600
  const params = new URLSearchParams();
6419
6601
  if (req.range !== void 0) params.set("range", req.range);
6420
6602
  if (req.scope !== void 0) params.set("scope", req.scope);
6603
+ if (req.groupBy !== void 0) params.set("group_by", req.groupBy);
6421
6604
  const query = params.toString();
6422
6605
  return this.executeCloudRequest(
6423
6606
  (http, signal) => http.request({
@@ -6434,7 +6617,9 @@ var OllamaClient = class _OllamaClient {
6434
6617
  * object (`balance_usd`/`allowance_usd`/`period`) or, on legacy plans, session/weekly
6435
6618
  * percentage limits; the shape is discriminated at runtime by the server response.
6436
6619
  * See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
6437
- * behavior, which applies identically here.
6620
+ * behavior, which applies identically here. Like `/api/usage`, the pinned contract
6621
+ * documents this endpoint's 429 with a `Retry-After` header, parsed into
6622
+ * `OllamaRateLimitError.retryAfterMs` and honored verbatim when retrying (ADR 0035).
6438
6623
  *
6439
6624
  * ```ts
6440
6625
  * const balance = await client.balance();
@@ -6720,6 +6905,21 @@ function sleep2(ms, signal) {
6720
6905
  signal?.addEventListener("abort", onAbort, { once: true });
6721
6906
  });
6722
6907
  }
6908
+ function retryAfterMsFromResponse(response) {
6909
+ try {
6910
+ const headers = response?.headers;
6911
+ if (headers === null || headers === void 0 || typeof headers.get !== "function") {
6912
+ return void 0;
6913
+ }
6914
+ const value = headers.get("retry-after");
6915
+ if (value === null || value === "") {
6916
+ return void 0;
6917
+ }
6918
+ return parseRetryAfterMs(value);
6919
+ } catch {
6920
+ return void 0;
6921
+ }
6922
+ }
6723
6923
  async function fetchWithBackoff(input, init, config = {}) {
6724
6924
  const fetchImpl = config.fetch ?? globalThis.fetch;
6725
6925
  if (typeof fetchImpl !== "function") {
@@ -6741,7 +6941,8 @@ async function fetchWithBackoff(input, init, config = {}) {
6741
6941
  ...signal !== void 0 ? { signal } : {}
6742
6942
  });
6743
6943
  if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries) {
6744
- const delayMs = calculateBackoff(attempt, backoff);
6944
+ const serverDelayMs = response.status === 429 ? retryAfterMsFromResponse(response) : void 0;
6945
+ const delayMs = serverDelayMs ?? calculateBackoff(attempt, backoff);
6745
6946
  config.onRetry?.(attempt, response.status, delayMs);
6746
6947
  await sleep2(delayMs, signal);
6747
6948
  continue;
@@ -8283,14 +8484,6 @@ function createModelAffinityScheduler(client, options) {
8283
8484
  return new ModelAffinityScheduler(client, options);
8284
8485
  }
8285
8486
 
8286
- // src/system-one.ts
8287
- var MAX_SYSTEM_ONE_IMAGES_BYTES = 32 * 1024 * 1024;
8288
- var MAX_SYSTEM_ONE_REQUEST_BYTES = 64 * 1024;
8289
- function estimateSystemOneRequestBytes(request, options) {
8290
- const body = options?.excludeImages && request.images !== void 0 ? { ...request, images: void 0 } : request;
8291
- return new TextEncoder().encode(JSON.stringify(body)).byteLength;
8292
- }
8293
-
8294
- 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_EMBED_BATCH_SIZE, DEFAULT_EMBED_CONCURRENCY, 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, MESSAGE_OVERHEAD_TOKENS, McpBridge, ModelAffinityRouter, ModelAffinityScheduler, ModelsClient, NANOS_PER_MS, NANOS_PER_SECOND, NETWORK_ERROR_CODES, NOOP_LOGGER, OLLAMA_CLOUD_BASE_URL, OLLAMA_FALLBACK_CONTEXT_LENGTH, OllamaAbortError, OllamaAgentMaxIterationsError, OllamaAgentMaxToolCallsError, OllamaAgentToolLoopError, OllamaAuthError, OllamaBadGatewayError, OllamaClient, OllamaClientError, OllamaGenericClientError, OllamaIncompatibleModelError, OllamaMcpError, OllamaModelRoutingError, OllamaNetworkError, OllamaNotFoundError, OllamaOverloadedError, OllamaPaymentRequiredError, 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, WEB_FETCH_TOOL_NAME, WEB_SEARCH_TOOL_NAME, addSpanEvent, applySkill, batchEmbed, calculateBackoff, canonicalToolCallSignature, checkChatContext, checkEndpointHealth, checkGenerateContext, compactConversationHistory, compareDraftPerformance, composeMiddleware, computeProgressPercent, contextWarningMessage, createConsoleLogger, createDecision, createModelAffinityScheduler, createOllamaCloudFreeTierQuota, createProgressSpeedTracker, createTimeoutSignal, createWebFetchTool, createWebSearchTool, createWebTools, defineTool, detectModelCapabilities, embedBatchOverflowMessage, encodeImage, estimateChatRequestTokens, estimateGenerateRequestTokens, estimateMessageTokens, estimateSystemOneRequestBytes, estimateTokens, extractContextLength as extractNativeContextLength, extractParameterNumCtx, extractUsage, fetchWithBackoff, findOversizedEmbedInputs, findRunningModelContextLength, formatTelemetry, formatWebFetchResult, formatWebSearchResult, imageStringNeedsResolution, inferRuntimeMode, isKeepAliveSugar, isKnownDoneReason, isModelResident, isNetworkLevelError, isSameToolCall, listAllMcpTools, listAvailableModels, loadMcpTools, mapError, mergeToolCall, mergeToolCallArgumentsString, mergeToolCallArrays, normalizeChatStream, normalizeGenerateStream, normalizeKeepAlive, normalizeProgressStream, observeStream, onProgress, parseFrontmatter, parseNdjsonStream, parseSseStream, parseStructuredOutput, recommendContextWindow, registerMcpTools, resolveContextLength, resolveCredentialEndpoints, resolveImageInput, sanitizeHistoryForNextTurn, setSpanError, toDataStream, toPullProgressEvent, toResponse, toTextStream, withRetry, withSpan, zodToJsonSchema };
8487
+ 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, CLOUD_MODEL_MIN_OLLAMA_VERSION, CLOUD_MODEL_SUFFIX, ConversationSession, DEFAULT_BACKOFF, DEFAULT_BASE_URL, DEFAULT_CONTEXT_WARNING_THRESHOLD, DEFAULT_EMBED_BATCH_SIZE, DEFAULT_EMBED_CONCURRENCY, 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, MESSAGE_OVERHEAD_TOKENS, McpBridge, ModelAffinityRouter, ModelAffinityScheduler, ModelsClient, NANOS_PER_MS, NANOS_PER_SECOND, NETWORK_ERROR_CODES, NOOP_LOGGER, OLLAMA_CLOUD_BASE_URL, OLLAMA_FALLBACK_CONTEXT_LENGTH, OllamaAbortError, OllamaAgentMaxIterationsError, OllamaAgentMaxToolCallsError, OllamaAgentToolLoopError, OllamaAuthError, OllamaBadGatewayError, OllamaClient, OllamaClientError, OllamaCloudModelVersionError, OllamaGenericClientError, OllamaIncompatibleModelError, OllamaMcpError, OllamaModelRoutingError, OllamaNetworkError, OllamaNotFoundError, OllamaOverloadedError, OllamaPaymentRequiredError, 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, WEB_FETCH_TOOL_NAME, WEB_SEARCH_TOOL_NAME, addSpanEvent, applySkill, batchEmbed, calculateBackoff, canonicalToolCallSignature, checkChatContext, checkEndpointHealth, checkGenerateContext, compactConversationHistory, compareDraftPerformance, compareOllamaVersions, composeMiddleware, computeProgressPercent, contextWarningMessage, createConsoleLogger, createDecision, createModelAffinityScheduler, createOllamaCloudFreeTierQuota, createProgressSpeedTracker, createTimeoutSignal, createWebFetchTool, createWebSearchTool, createWebTools, defineTool, detectModelCapabilities, embedBatchOverflowMessage, encodeImage, estimateChatRequestTokens, estimateGenerateRequestTokens, estimateMessageTokens, estimateSystemOneRequestBytes, estimateTokens, extractContextLength as extractNativeContextLength, extractParameterNumCtx, extractUsage, fetchWithBackoff, findOversizedEmbedInputs, findRunningModelContextLength, formatTelemetry, formatWebFetchResult, formatWebSearchResult, imageStringNeedsResolution, inferRuntimeMode, isCloudModel, isKeepAliveSugar, isKnownDoneReason, isModelResident, isNetworkLevelError, isSameToolCall, listAllMcpTools, listAvailableModels, loadMcpTools, mapError, mergeToolCall, mergeToolCallArgumentsString, mergeToolCallArrays, normalizeChatStream, normalizeGenerateStream, normalizeKeepAlive, normalizeProgressStream, observeStream, onProgress, parseFrontmatter, parseNdjsonStream, parseRetryAfterMs, parseSseStream, parseStructuredOutput, recommendContextWindow, registerMcpTools, resolveContextLength, resolveCredentialEndpoints, resolveImageInput, sanitizeHistoryForNextTurn, setSpanError, toDataStream, toPullProgressEvent, toResponse, toTextStream, withRetry, withSpan, zodToJsonSchema };
8295
8488
  //# sourceMappingURL=index.js.map
8296
8489
  //# sourceMappingURL=index.js.map