@nemesis-oss/ollama-sdk 1.19.0 → 1.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +8 -8
- package/dist/{anthropic-CAvwqIjx.d.ts → anthropic-Bf16hGLL.d.cts} +1 -1
- package/dist/{anthropic-DIPnBwlf.d.cts → anthropic-BmFk4Pjq.d.ts} +1 -1
- package/dist/generated-api.d.cts +4 -4
- package/dist/generated-api.d.ts +4 -4
- package/dist/generated-runtime.cjs.map +1 -1
- package/dist/generated-runtime.d.cts +1 -1
- package/dist/generated-runtime.d.ts +1 -1
- package/dist/generated-runtime.js.map +1 -1
- package/dist/index.cjs +115 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -34
- package/dist/index.d.ts +73 -34
- package/dist/index.js +115 -15
- package/dist/index.js.map +1 -1
- package/dist/mcp-generated.d.cts +1 -1
- package/dist/mcp-generated.d.ts +1 -1
- package/dist/{runtime-C28YEAHC.d.cts → runtime-CjCriL5A.d.cts} +29 -1
- package/dist/{runtime-DYCxYGG-.d.ts → runtime-EwyUaNnZ.d.ts} +29 -1
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -428,6 +428,43 @@ function isNetworkLevelError(error) {
|
|
|
428
428
|
}
|
|
429
429
|
return false;
|
|
430
430
|
}
|
|
431
|
+
function lookupHeader(headers, name) {
|
|
432
|
+
if (headers === void 0) {
|
|
433
|
+
return void 0;
|
|
434
|
+
}
|
|
435
|
+
const needle = name.toLowerCase();
|
|
436
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
437
|
+
if (key.toLowerCase() === needle) {
|
|
438
|
+
return value;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return void 0;
|
|
442
|
+
}
|
|
443
|
+
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$/;
|
|
444
|
+
function parseRetryAfterMs(headerValue, now = Date.now()) {
|
|
445
|
+
if (headerValue === void 0) {
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
const trimmed = headerValue.trim();
|
|
449
|
+
if (trimmed === "") {
|
|
450
|
+
return void 0;
|
|
451
|
+
}
|
|
452
|
+
if (/^\d+$/.test(trimmed)) {
|
|
453
|
+
const seconds = Number(trimmed);
|
|
454
|
+
if (!Number.isSafeInteger(seconds)) {
|
|
455
|
+
return void 0;
|
|
456
|
+
}
|
|
457
|
+
return seconds * 1e3;
|
|
458
|
+
}
|
|
459
|
+
if (!HTTP_DATE_PATTERN.test(trimmed)) {
|
|
460
|
+
return void 0;
|
|
461
|
+
}
|
|
462
|
+
const instant = Date.parse(trimmed);
|
|
463
|
+
if (Number.isNaN(instant)) {
|
|
464
|
+
return void 0;
|
|
465
|
+
}
|
|
466
|
+
return Math.max(0, instant - now);
|
|
467
|
+
}
|
|
431
468
|
function statusToError(status, message, options) {
|
|
432
469
|
if (status === 401 || status === 403) {
|
|
433
470
|
return new OllamaAuthError(message, { ...options, status });
|
|
@@ -439,7 +476,8 @@ function statusToError(status, message, options) {
|
|
|
439
476
|
return new OllamaNotFoundError(message, { ...options, status });
|
|
440
477
|
}
|
|
441
478
|
if (status === 429) {
|
|
442
|
-
|
|
479
|
+
const retryAfterMs = parseRetryAfterMs(lookupHeader(options.response?.headers, "retry-after"));
|
|
480
|
+
return new OllamaRateLimitError(message, { ...options, status, retryAfterMs });
|
|
443
481
|
}
|
|
444
482
|
if (status === 502) {
|
|
445
483
|
return new OllamaBadGatewayError(message, { ...options });
|
|
@@ -1142,6 +1180,21 @@ function sameHeaders(actual, expected) {
|
|
|
1142
1180
|
const expectedKeys = Object.keys(expectedEntries);
|
|
1143
1181
|
return actualKeys.length === expectedKeys.length && expectedKeys.every((key) => actualEntries[key] === expectedEntries[key]);
|
|
1144
1182
|
}
|
|
1183
|
+
function retryAfterHeaderFrom(response) {
|
|
1184
|
+
try {
|
|
1185
|
+
const headers = response?.headers;
|
|
1186
|
+
if (headers === null || headers === void 0 || typeof headers.get !== "function") {
|
|
1187
|
+
return void 0;
|
|
1188
|
+
}
|
|
1189
|
+
const value = headers.get("retry-after");
|
|
1190
|
+
if (value === null || value === "") {
|
|
1191
|
+
return void 0;
|
|
1192
|
+
}
|
|
1193
|
+
return { "retry-after": value };
|
|
1194
|
+
} catch {
|
|
1195
|
+
return void 0;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1145
1198
|
var HttpClient = class {
|
|
1146
1199
|
baseUrl;
|
|
1147
1200
|
apiKey;
|
|
@@ -1209,7 +1262,12 @@ var HttpClient = class {
|
|
|
1209
1262
|
durationMs: Date.now() - startedAt,
|
|
1210
1263
|
error: mapError(new Error(`HTTP ${response2.status}`), {
|
|
1211
1264
|
request: { method: request.method, url: request.url },
|
|
1212
|
-
|
|
1265
|
+
// Mirrors the middleware path, which reports context.headers:
|
|
1266
|
+
// a 429 lifecycle event carries the server's Retry-After.
|
|
1267
|
+
response: {
|
|
1268
|
+
status: response2.status,
|
|
1269
|
+
headers: retryAfterHeaderFrom(response2)
|
|
1270
|
+
}
|
|
1213
1271
|
}),
|
|
1214
1272
|
timestamp: Date.now()
|
|
1215
1273
|
});
|
|
@@ -1315,7 +1373,11 @@ var HttpClient = class {
|
|
|
1315
1373
|
const message = typeof errorBody === "object" && errorBody !== null && "error" in errorBody ? String(errorBody.error) : `HTTP ${response.status} ${response.statusText}`;
|
|
1316
1374
|
throw mapError(new Error(message), {
|
|
1317
1375
|
request: { method, url },
|
|
1318
|
-
response: {
|
|
1376
|
+
response: {
|
|
1377
|
+
status: response.status,
|
|
1378
|
+
headers: retryAfterHeaderFrom(response),
|
|
1379
|
+
body: errorBody
|
|
1380
|
+
}
|
|
1319
1381
|
});
|
|
1320
1382
|
}
|
|
1321
1383
|
if (options.method === "HEAD" || response.status === 204) {
|
|
@@ -1380,7 +1442,11 @@ var HttpClient = class {
|
|
|
1380
1442
|
const errorText = await response.text();
|
|
1381
1443
|
throw mapError(new Error(errorText || `HTTP ${response.status}`), {
|
|
1382
1444
|
request: { method, url },
|
|
1383
|
-
response: {
|
|
1445
|
+
response: {
|
|
1446
|
+
status: response.status,
|
|
1447
|
+
headers: retryAfterHeaderFrom(response),
|
|
1448
|
+
body: errorText
|
|
1449
|
+
}
|
|
1384
1450
|
});
|
|
1385
1451
|
}
|
|
1386
1452
|
if (!response.body) {
|
|
@@ -1444,7 +1510,11 @@ var HttpClient = class {
|
|
|
1444
1510
|
const errorText = await response.text();
|
|
1445
1511
|
throw mapError(new Error(errorText || `HTTP ${response.status}`), {
|
|
1446
1512
|
request: { method, url },
|
|
1447
|
-
response: {
|
|
1513
|
+
response: {
|
|
1514
|
+
status: response.status,
|
|
1515
|
+
headers: retryAfterHeaderFrom(response),
|
|
1516
|
+
body: errorText
|
|
1517
|
+
}
|
|
1448
1518
|
});
|
|
1449
1519
|
}
|
|
1450
1520
|
if (!response.body) {
|
|
@@ -1750,7 +1820,7 @@ function contextWarningMessage(check2, kind) {
|
|
|
1750
1820
|
if (check2.windowSource === "client-default") {
|
|
1751
1821
|
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\`.`;
|
|
1752
1822
|
}
|
|
1753
|
-
return `${where}: estimated prompt size ${estimate} outright exceeds
|
|
1823
|
+
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\`.`;
|
|
1754
1824
|
}
|
|
1755
1825
|
|
|
1756
1826
|
// src/context-discovery.ts
|
|
@@ -2478,6 +2548,12 @@ function sleep(ms, signal) {
|
|
|
2478
2548
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
2479
2549
|
});
|
|
2480
2550
|
}
|
|
2551
|
+
function retryDelayMs(error, attempt, backoff) {
|
|
2552
|
+
if (error instanceof OllamaRateLimitError && error.retryAfterMs !== void 0) {
|
|
2553
|
+
return error.retryAfterMs;
|
|
2554
|
+
}
|
|
2555
|
+
return calculateBackoff(attempt, backoff);
|
|
2556
|
+
}
|
|
2481
2557
|
async function withRetry(operation, config = DEFAULT_RETRY_CONFIG, signal) {
|
|
2482
2558
|
let attempt = 0;
|
|
2483
2559
|
while (true) {
|
|
@@ -2489,7 +2565,7 @@ async function withRetry(operation, config = DEFAULT_RETRY_CONFIG, signal) {
|
|
|
2489
2565
|
if (attempt >= config.maxRetries || !shouldRetry(error, attempt)) {
|
|
2490
2566
|
throw error;
|
|
2491
2567
|
}
|
|
2492
|
-
const delayMs =
|
|
2568
|
+
const delayMs = retryDelayMs(error, attempt, config.backoff);
|
|
2493
2569
|
config.onRetry?.(error, attempt, delayMs);
|
|
2494
2570
|
await sleep(delayMs, signal);
|
|
2495
2571
|
attempt += 1;
|
|
@@ -2658,7 +2734,8 @@ var ModelsClient = class {
|
|
|
2658
2734
|
}
|
|
2659
2735
|
/**
|
|
2660
2736
|
* Discovers the model's real context window from the server — no more
|
|
2661
|
-
*
|
|
2737
|
+
* default-window guesswork (current daemons tier the unset-`num_ctx`
|
|
2738
|
+
* default by VRAM: 4k / 32k / 256k). Consults, in precedence order:
|
|
2662
2739
|
*
|
|
2663
2740
|
* 1. `GET /api/ps` — the window the **running instance actually
|
|
2664
2741
|
* allocated** (exact, reflects the Modelfile `num_ctx` default and
|
|
@@ -2670,7 +2747,7 @@ var ModelsClient = class {
|
|
|
2670
2747
|
* 3. `POST /api/show` `model_info["<arch>.context_length"]` — the
|
|
2671
2748
|
* model's **native GGUF maximum** (a ceiling to raise `num_ctx`
|
|
2672
2749
|
* toward, not the allocated window).
|
|
2673
|
-
* 4. Fallback —
|
|
2750
|
+
* 4. Fallback — the 2048 worst-case floor for the unset-`num_ctx` window.
|
|
2674
2751
|
*
|
|
2675
2752
|
* The result carries every signal it found, so `nativeContextLength:
|
|
2676
2753
|
* 131072` vs `contextLength: 4096` (a 32× gap seen in the official API
|
|
@@ -6371,7 +6448,9 @@ var OllamaClient = class _OllamaClient {
|
|
|
6371
6448
|
* through a local server. Requires an Ollama account API key: pass `apiKey` to the
|
|
6372
6449
|
* client constructor or set `OLLAMA_API_KEY`. Applies the same default `timeoutMs` and
|
|
6373
6450
|
* retry policy as inference calls, but never cross-endpoint-fails-over — there is only
|
|
6374
|
-
* ever the one cloud endpoint to call.
|
|
6451
|
+
* ever the one cloud endpoint to call. If the service answers 429 with a `Retry-After`
|
|
6452
|
+
* header, the transport parses it into `OllamaRateLimitError.retryAfterMs` and the
|
|
6453
|
+
* retry loop sleeps exactly that long instead of jittered backoff (see ADR 0035).
|
|
6375
6454
|
*/
|
|
6376
6455
|
async webSearch(req) {
|
|
6377
6456
|
const { count, max_results, ...rest } = req;
|
|
@@ -6402,8 +6481,11 @@ var OllamaClient = class _OllamaClient {
|
|
|
6402
6481
|
* web search, and web fetch, bucketed by hour (`range: '24h'`) or day (`'7d'`/`'30d'`).
|
|
6403
6482
|
*
|
|
6404
6483
|
* See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
|
|
6405
|
-
* behavior, which applies identically here.
|
|
6406
|
-
* endpoint
|
|
6484
|
+
* behavior, which applies identically here. The pinned contract documents this
|
|
6485
|
+
* endpoint's 429 with a `Retry-After` header ("Seconds to wait before retrying"),
|
|
6486
|
+
* which the SDK parses into `OllamaRateLimitError.retryAfterMs` and honors verbatim
|
|
6487
|
+
* when retrying. Query parameters mirror the official endpoint: `range`
|
|
6488
|
+
* (`'24h' | '7d' | '30d'`, server default `'7d'`) and `scope`
|
|
6407
6489
|
* (`'self' | 'team'`, server default `'self'`; team scope requires a team admin).
|
|
6408
6490
|
* Omitted options are left unset so the server applies its own defaults.
|
|
6409
6491
|
*
|
|
@@ -6436,7 +6518,9 @@ var OllamaClient = class _OllamaClient {
|
|
|
6436
6518
|
* object (`balance_usd`/`allowance_usd`/`period`) or, on legacy plans, session/weekly
|
|
6437
6519
|
* percentage limits; the shape is discriminated at runtime by the server response.
|
|
6438
6520
|
* See {@link OllamaClient.webSearch} for the cloud-endpoint/auth/timeout/retry
|
|
6439
|
-
* behavior, which applies identically here.
|
|
6521
|
+
* behavior, which applies identically here. Like `/api/usage`, the pinned contract
|
|
6522
|
+
* documents this endpoint's 429 with a `Retry-After` header, parsed into
|
|
6523
|
+
* `OllamaRateLimitError.retryAfterMs` and honored verbatim when retrying (ADR 0035).
|
|
6440
6524
|
*
|
|
6441
6525
|
* ```ts
|
|
6442
6526
|
* const balance = await client.balance();
|
|
@@ -6722,6 +6806,21 @@ function sleep2(ms, signal) {
|
|
|
6722
6806
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
6723
6807
|
});
|
|
6724
6808
|
}
|
|
6809
|
+
function retryAfterMsFromResponse(response) {
|
|
6810
|
+
try {
|
|
6811
|
+
const headers = response?.headers;
|
|
6812
|
+
if (headers === null || headers === void 0 || typeof headers.get !== "function") {
|
|
6813
|
+
return void 0;
|
|
6814
|
+
}
|
|
6815
|
+
const value = headers.get("retry-after");
|
|
6816
|
+
if (value === null || value === "") {
|
|
6817
|
+
return void 0;
|
|
6818
|
+
}
|
|
6819
|
+
return parseRetryAfterMs(value);
|
|
6820
|
+
} catch {
|
|
6821
|
+
return void 0;
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6725
6824
|
async function fetchWithBackoff(input, init, config = {}) {
|
|
6726
6825
|
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
6727
6826
|
if (typeof fetchImpl !== "function") {
|
|
@@ -6743,7 +6842,8 @@ async function fetchWithBackoff(input, init, config = {}) {
|
|
|
6743
6842
|
...signal !== void 0 ? { signal } : {}
|
|
6744
6843
|
});
|
|
6745
6844
|
if (RETRYABLE_STATUS_CODES.has(response.status) && attempt < maxRetries) {
|
|
6746
|
-
const
|
|
6845
|
+
const serverDelayMs = response.status === 429 ? retryAfterMsFromResponse(response) : void 0;
|
|
6846
|
+
const delayMs = serverDelayMs ?? calculateBackoff(attempt, backoff);
|
|
6747
6847
|
config.onRetry?.(attempt, response.status, delayMs);
|
|
6748
6848
|
await sleep2(delayMs, signal);
|
|
6749
6849
|
continue;
|
|
@@ -8448,6 +8548,7 @@ exports.observeStream = observeStream;
|
|
|
8448
8548
|
exports.onProgress = onProgress;
|
|
8449
8549
|
exports.parseFrontmatter = parseFrontmatter;
|
|
8450
8550
|
exports.parseNdjsonStream = parseNdjsonStream;
|
|
8551
|
+
exports.parseRetryAfterMs = parseRetryAfterMs;
|
|
8451
8552
|
exports.parseSseStream = parseSseStream;
|
|
8452
8553
|
exports.parseStructuredOutput = parseStructuredOutput;
|
|
8453
8554
|
exports.recommendContextWindow = recommendContextWindow;
|