@nemesis-oss/ollama-sdk 1.9.0 → 1.11.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.
- package/CHANGELOG.md +68 -0
- package/README.md +242 -2
- package/dist/{anthropic-5oYIRS6D.d.ts → anthropic-CfricAhK.d.ts} +1 -1
- package/dist/{anthropic-BIh45UXj.d.cts → anthropic-DinrABof.d.cts} +1 -1
- package/dist/{compose-DCVYMvOL.d.ts → compose-BQYXdpar.d.ts} +1 -1
- package/dist/{compose-Dj6FdF2n.d.cts → compose-lt-Qz3lI.d.cts} +1 -1
- package/dist/generated-api.d.cts +5 -5
- package/dist/generated-api.d.ts +5 -5
- package/dist/generated-runtime.cjs.map +1 -1
- package/dist/generated-runtime.d.cts +2 -2
- package/dist/generated-runtime.d.ts +2 -2
- package/dist/generated-runtime.js.map +1 -1
- package/dist/index.cjs +787 -96
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +780 -97
- package/dist/index.d.ts +780 -97
- package/dist/index.js +771 -97
- package/dist/index.js.map +1 -1
- package/dist/mcp-generated.d.cts +2 -2
- package/dist/mcp-generated.d.ts +2 -2
- package/dist/{runtime-C3rfpkZw.d.ts → runtime-CCm4CtU_.d.ts} +29 -2
- package/dist/{runtime-D2W9TfCJ.d.cts → runtime-DTtWCxDs.d.cts} +29 -2
- package/dist/skills.cjs.map +1 -1
- package/dist/skills.d.cts +3 -3
- package/dist/skills.d.ts +3 -3
- package/dist/skills.js.map +1 -1
- package/dist/{types-DVcX_zMp.d.cts → types-BtQexFpT.d.cts} +5 -2
- package/dist/{types-DVcX_zMp.d.ts → types-BtQexFpT.d.ts} +5 -2
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -339,6 +339,23 @@ var OllamaAgentMaxIterationsError = class extends OllamaClientError {
|
|
|
339
339
|
this.maxIterations = options.maxIterations;
|
|
340
340
|
}
|
|
341
341
|
};
|
|
342
|
+
var OllamaAgentToolLoopError = class extends OllamaClientError {
|
|
343
|
+
/** Name of the tool whose identical call would exceed the repeat budget. */
|
|
344
|
+
toolName;
|
|
345
|
+
/** How many times this exact call would have executed including the rejected one. */
|
|
346
|
+
repeatedExecutions;
|
|
347
|
+
/** The configured per-signature execution budget. */
|
|
348
|
+
maxRepeatedToolCalls;
|
|
349
|
+
/** Canonical `name(args)` signature of the repeated call (very large payloads truncated). */
|
|
350
|
+
signature;
|
|
351
|
+
constructor(message, options) {
|
|
352
|
+
super(message, { ...options, code: "agent_tool_loop_detected", retryable: false });
|
|
353
|
+
this.toolName = options.toolName;
|
|
354
|
+
this.repeatedExecutions = options.repeatedExecutions;
|
|
355
|
+
this.maxRepeatedToolCalls = options.maxRepeatedToolCalls;
|
|
356
|
+
this.signature = options.signature;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
342
359
|
var OllamaMcpError = class extends OllamaClientError {
|
|
343
360
|
mcpMethod;
|
|
344
361
|
toolName;
|
|
@@ -638,6 +655,15 @@ var EndpointRegistry = class {
|
|
|
638
655
|
}
|
|
639
656
|
};
|
|
640
657
|
|
|
658
|
+
// src/transport/url.ts
|
|
659
|
+
function joinUrlPath(baseUrl, path) {
|
|
660
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
661
|
+
const tail = path.replace(/^\/+/, "");
|
|
662
|
+
if (tail === "") return base;
|
|
663
|
+
if (base === "") return `/${tail}`;
|
|
664
|
+
return `${base}/${tail}`;
|
|
665
|
+
}
|
|
666
|
+
|
|
641
667
|
// src/middleware.ts
|
|
642
668
|
function composeMiddleware(middlewares, finalHandler) {
|
|
643
669
|
return function runPipeline(initialRequest) {
|
|
@@ -1190,7 +1216,7 @@ var HttpClient = class {
|
|
|
1190
1216
|
}
|
|
1191
1217
|
}
|
|
1192
1218
|
async request(options) {
|
|
1193
|
-
const url =
|
|
1219
|
+
const url = joinUrlPath(this.baseUrl, options.path);
|
|
1194
1220
|
const method = options.method ?? (options.body !== void 0 || options.rawBody !== void 0 ? "POST" : "GET");
|
|
1195
1221
|
const headers = this.buildHeaders(options.headers);
|
|
1196
1222
|
if (options.rawBody !== void 0 && !options.headers?.["Content-Type"]) {
|
|
@@ -1250,7 +1276,7 @@ var HttpClient = class {
|
|
|
1250
1276
|
* can decode their provider-specific event payloads without duplicating transport logic.
|
|
1251
1277
|
*/
|
|
1252
1278
|
async requestSseStream(options) {
|
|
1253
|
-
const url =
|
|
1279
|
+
const url = joinUrlPath(this.baseUrl, options.path);
|
|
1254
1280
|
const method = options.method ?? "POST";
|
|
1255
1281
|
const headers = {
|
|
1256
1282
|
...this.buildHeaders(options.headers),
|
|
@@ -1317,7 +1343,7 @@ var HttpClient = class {
|
|
|
1317
1343
|
);
|
|
1318
1344
|
}
|
|
1319
1345
|
async requestStream(options) {
|
|
1320
|
-
const url =
|
|
1346
|
+
const url = joinUrlPath(this.baseUrl, options.path);
|
|
1321
1347
|
const method = options.method ?? "POST";
|
|
1322
1348
|
const headers = this.buildHeaders(options.headers);
|
|
1323
1349
|
return withSpan(
|
|
@@ -2158,6 +2184,151 @@ function createTimeoutSignal(timeoutMs, userSignal) {
|
|
|
2158
2184
|
return { signal: controller.signal, cancel };
|
|
2159
2185
|
}
|
|
2160
2186
|
|
|
2187
|
+
// src/context-safety.ts
|
|
2188
|
+
var DEFAULT_CONTEXT_WARNING_THRESHOLD = 0.9;
|
|
2189
|
+
var IMAGE_TOKEN_ESTIMATE = 300;
|
|
2190
|
+
var CHARS_PER_TOKEN = 4;
|
|
2191
|
+
function isCjkCodePoint(code) {
|
|
2192
|
+
return code >= 19968 && code <= 40959 || // CJK Unified Ideographs
|
|
2193
|
+
code >= 13312 && code <= 19903 || // CJK Extension A
|
|
2194
|
+
code >= 12352 && code <= 12543 || // Hiragana + Katakana
|
|
2195
|
+
code >= 44032 && code <= 55215 || // Hangul syllables
|
|
2196
|
+
code >= 63744 && code <= 64255 || // CJK Compatibility Ideographs
|
|
2197
|
+
code >= 65280 && code <= 65519;
|
|
2198
|
+
}
|
|
2199
|
+
function estimateTokens(text) {
|
|
2200
|
+
if (text.length === 0) return 0;
|
|
2201
|
+
let cjk = 0;
|
|
2202
|
+
let other = 0;
|
|
2203
|
+
for (const char of text) {
|
|
2204
|
+
if (isCjkCodePoint(char.codePointAt(0) ?? 0)) cjk += 1;
|
|
2205
|
+
else other += 1;
|
|
2206
|
+
}
|
|
2207
|
+
return Math.ceil(cjk + other / CHARS_PER_TOKEN);
|
|
2208
|
+
}
|
|
2209
|
+
var MESSAGE_OVERHEAD_TOKENS = 4;
|
|
2210
|
+
function estimateMessageTokens(message) {
|
|
2211
|
+
let total = MESSAGE_OVERHEAD_TOKENS + estimateTokens(message.content ?? "");
|
|
2212
|
+
if (message.thinking !== void 0) total += estimateTokens(message.thinking);
|
|
2213
|
+
if (message.tool_calls !== void 0) {
|
|
2214
|
+
for (const call of message.tool_calls) {
|
|
2215
|
+
total += estimateTokens(call.function.name);
|
|
2216
|
+
total += estimateTokens(JSON.stringify(call.function.arguments ?? {}));
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
if (message.images !== void 0) total += message.images.length * IMAGE_TOKEN_ESTIMATE;
|
|
2220
|
+
return total;
|
|
2221
|
+
}
|
|
2222
|
+
function estimateChatRequestTokens(req) {
|
|
2223
|
+
let total = 0;
|
|
2224
|
+
for (const message of req.messages) total += estimateMessageTokens(message);
|
|
2225
|
+
if (req.tools !== void 0) {
|
|
2226
|
+
for (const tool of req.tools) {
|
|
2227
|
+
total += estimateTokens(tool.function.description ?? "");
|
|
2228
|
+
total += estimateTokens(JSON.stringify(tool.function.parameters ?? {}));
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
return total;
|
|
2232
|
+
}
|
|
2233
|
+
function estimateGenerateRequestTokens(req) {
|
|
2234
|
+
let total = estimateTokens(req.prompt ?? "");
|
|
2235
|
+
if (req.system !== void 0) total += estimateTokens(req.system);
|
|
2236
|
+
if (req.template !== void 0) total += estimateTokens(req.template);
|
|
2237
|
+
if (req.images !== void 0) total += req.images.length * IMAGE_TOKEN_ESTIMATE;
|
|
2238
|
+
return total;
|
|
2239
|
+
}
|
|
2240
|
+
function checkChatContext(req, options = {}) {
|
|
2241
|
+
return check(estimateChatRequestTokens(req), req.options?.num_ctx, options);
|
|
2242
|
+
}
|
|
2243
|
+
function checkGenerateContext(req, options = {}) {
|
|
2244
|
+
return check(estimateGenerateRequestTokens(req), req.options?.num_ctx, options);
|
|
2245
|
+
}
|
|
2246
|
+
function check(estimatedPromptTokens, requestNumCtx, options) {
|
|
2247
|
+
const threshold = options.threshold ?? DEFAULT_CONTEXT_WARNING_THRESHOLD;
|
|
2248
|
+
if (requestNumCtx !== void 0) {
|
|
2249
|
+
return {
|
|
2250
|
+
estimatedPromptTokens,
|
|
2251
|
+
effectiveContextLength: requestNumCtx,
|
|
2252
|
+
windowSource: "request",
|
|
2253
|
+
exceedsThreshold: estimatedPromptTokens > requestNumCtx * threshold
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
if (options.defaultContextLength !== void 0) {
|
|
2257
|
+
return {
|
|
2258
|
+
estimatedPromptTokens,
|
|
2259
|
+
effectiveContextLength: options.defaultContextLength,
|
|
2260
|
+
windowSource: "client-default",
|
|
2261
|
+
exceedsThreshold: estimatedPromptTokens > options.defaultContextLength * threshold
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
return {
|
|
2265
|
+
estimatedPromptTokens,
|
|
2266
|
+
effectiveContextLength: OLLAMA_FALLBACK_CONTEXT_LENGTH,
|
|
2267
|
+
windowSource: "server-fallback",
|
|
2268
|
+
exceedsThreshold: estimatedPromptTokens > OLLAMA_FALLBACK_CONTEXT_LENGTH
|
|
2269
|
+
};
|
|
2270
|
+
}
|
|
2271
|
+
var OLLAMA_FALLBACK_CONTEXT_LENGTH = 2048;
|
|
2272
|
+
function contextWarningMessage(check2, kind) {
|
|
2273
|
+
const where = `OllamaClient.${kind}()`;
|
|
2274
|
+
const estimate = `~${check2.estimatedPromptTokens} tokens`;
|
|
2275
|
+
if (check2.windowSource === "request") {
|
|
2276
|
+
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\`.`;
|
|
2277
|
+
}
|
|
2278
|
+
if (check2.windowSource === "client-default") {
|
|
2279
|
+
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\`.`;
|
|
2280
|
+
}
|
|
2281
|
+
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\`.`;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
// src/context-discovery.ts
|
|
2285
|
+
function extractParameterNumCtx(parameters) {
|
|
2286
|
+
if (!parameters) return void 0;
|
|
2287
|
+
const match = /^[ \t]*num_ctx[ \t]+(\d+)\s*$/m.exec(parameters);
|
|
2288
|
+
if (match === null) return void 0;
|
|
2289
|
+
const value = Number.parseInt(match[1], 10);
|
|
2290
|
+
return Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2291
|
+
}
|
|
2292
|
+
function findRunningModelContextLength(psModels, model) {
|
|
2293
|
+
for (const entry of psModels) {
|
|
2294
|
+
const exact = entry.name === model || entry.model === model;
|
|
2295
|
+
const taglessPrefix = !model.includes(":") && (entry.name.startsWith(`${model}:`) || entry.model.startsWith(`${model}:`));
|
|
2296
|
+
if (exact || taglessPrefix) {
|
|
2297
|
+
return typeof entry.context_length === "number" && Number.isFinite(entry.context_length) && entry.context_length > 0 ? entry.context_length : void 0;
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
return void 0;
|
|
2301
|
+
}
|
|
2302
|
+
function positiveInt(value) {
|
|
2303
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
|
|
2304
|
+
}
|
|
2305
|
+
function resolveContextLength(signals) {
|
|
2306
|
+
const running = positiveInt(signals.running);
|
|
2307
|
+
const parameter = positiveInt(signals.parameter);
|
|
2308
|
+
const native = positiveInt(signals.native);
|
|
2309
|
+
if (running !== void 0) {
|
|
2310
|
+
return {
|
|
2311
|
+
contextLength: running,
|
|
2312
|
+
source: "running",
|
|
2313
|
+
...parameter !== void 0 ? { parameterContextLength: parameter } : {},
|
|
2314
|
+
...native !== void 0 ? { nativeContextLength: native } : {},
|
|
2315
|
+
runningContextLength: running
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
2318
|
+
if (parameter !== void 0) {
|
|
2319
|
+
return {
|
|
2320
|
+
contextLength: parameter,
|
|
2321
|
+
source: "parameters",
|
|
2322
|
+
parameterContextLength: parameter,
|
|
2323
|
+
...native !== void 0 ? { nativeContextLength: native } : {}
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
if (native !== void 0) {
|
|
2327
|
+
return { contextLength: native, source: "model-info", nativeContextLength: native };
|
|
2328
|
+
}
|
|
2329
|
+
return { contextLength: OLLAMA_FALLBACK_CONTEXT_LENGTH, source: "fallback" };
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2161
2332
|
// src/keep-alive.ts
|
|
2162
2333
|
var KEEP_ALIVE_UNLOAD = 0;
|
|
2163
2334
|
var KEEP_ALIVE_INDEFINITE = -1;
|
|
@@ -2271,9 +2442,66 @@ var ModelsClient = class {
|
|
|
2271
2442
|
{ ...request, singleEndpoint: true }
|
|
2272
2443
|
);
|
|
2273
2444
|
}
|
|
2274
|
-
ps() {
|
|
2275
|
-
return this.runner(
|
|
2276
|
-
|
|
2445
|
+
ps(request) {
|
|
2446
|
+
return this.runner(
|
|
2447
|
+
(http, signal) => http.request({ path: "/api/ps", method: "GET", signal }),
|
|
2448
|
+
{
|
|
2449
|
+
singleEndpoint: true,
|
|
2450
|
+
...request?.signal !== void 0 ? { signal: request.signal } : {},
|
|
2451
|
+
...request?.timeoutMs !== void 0 ? { timeoutMs: request.timeoutMs } : {}
|
|
2452
|
+
}
|
|
2453
|
+
);
|
|
2454
|
+
}
|
|
2455
|
+
/**
|
|
2456
|
+
* Discovers the model's real context window from the server — no more
|
|
2457
|
+
* 2048/4096 guesswork. Consults, in precedence order:
|
|
2458
|
+
*
|
|
2459
|
+
* 1. `GET /api/ps` — the window the **running instance actually
|
|
2460
|
+
* allocated** (exact, reflects the Modelfile `num_ctx` default and
|
|
2461
|
+
* what fit in memory); skipped when the model isn't loaded, when
|
|
2462
|
+
* `skipRunningCheck` is set, or harmlessly on servers without the
|
|
2463
|
+
* field.
|
|
2464
|
+
* 2. `POST /api/show` `parameters` — a Modelfile-authored
|
|
2465
|
+
* `num_ctx <n>` default, i.e. the window a cold load will allocate.
|
|
2466
|
+
* 3. `POST /api/show` `model_info["<arch>.context_length"]` — the
|
|
2467
|
+
* model's **native GGUF maximum** (a ceiling to raise `num_ctx`
|
|
2468
|
+
* toward, not the allocated window).
|
|
2469
|
+
* 4. Fallback — Ollama's conservative unset-`num_ctx` default (2048).
|
|
2470
|
+
*
|
|
2471
|
+
* The result carries every signal it found, so `nativeContextLength:
|
|
2472
|
+
* 131072` vs `contextLength: 4096` (a 32× gap seen in the official API
|
|
2473
|
+
* examples) is visible rather than guessed. Typical wiring:
|
|
2474
|
+
*
|
|
2475
|
+
* ```ts
|
|
2476
|
+
* const { contextLength, source, nativeContextLength } =
|
|
2477
|
+
* await client.models.getContextLength({ model: 'gemma4' });
|
|
2478
|
+
* // -> { contextLength: 4096, source: 'running', nativeContextLength: 131072 }
|
|
2479
|
+
* ```
|
|
2480
|
+
*
|
|
2481
|
+
* Makes two server calls (`/api/ps` + `/api/show`) unless
|
|
2482
|
+
* `skipRunningCheck` is set. A 404 from `/api/ps` (endpoint without the
|
|
2483
|
+
* listing) is treated as "not running" and never fails the lookup;
|
|
2484
|
+
* other transport errors propagate normally.
|
|
2485
|
+
*/
|
|
2486
|
+
async getContextLength(request) {
|
|
2487
|
+
let running;
|
|
2488
|
+
if (request.skipRunningCheck !== true) {
|
|
2489
|
+
try {
|
|
2490
|
+
const ps = await this.ps({ signal: request.signal, timeoutMs: request.timeoutMs });
|
|
2491
|
+
running = findRunningModelContextLength(ps.models, request.model);
|
|
2492
|
+
} catch (err) {
|
|
2493
|
+
if (!(err instanceof OllamaNotFoundError)) throw err;
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
const showRes = await this.show({
|
|
2497
|
+
model: request.model,
|
|
2498
|
+
signal: request.signal,
|
|
2499
|
+
timeoutMs: request.timeoutMs
|
|
2500
|
+
});
|
|
2501
|
+
return resolveContextLength({
|
|
2502
|
+
running,
|
|
2503
|
+
parameter: extractParameterNumCtx(showRes.parameters),
|
|
2504
|
+
native: extractContextLength(showRes.model_info)
|
|
2277
2505
|
});
|
|
2278
2506
|
}
|
|
2279
2507
|
version() {
|
|
@@ -4271,6 +4499,52 @@ var ResponsesModule = class {
|
|
|
4271
4499
|
};
|
|
4272
4500
|
|
|
4273
4501
|
// src/conversation.ts
|
|
4502
|
+
function compactConversationHistory(messages, options) {
|
|
4503
|
+
const reserve = Math.max(0, options.reserveForReply ?? 0);
|
|
4504
|
+
const minTail = Math.max(0, options.minTailMessages ?? 2);
|
|
4505
|
+
const budget = Math.max(0, options.maxEstimatedTokens - reserve);
|
|
4506
|
+
let split = 0;
|
|
4507
|
+
while (split < messages.length && messages[split].role === "system") split += 1;
|
|
4508
|
+
const pinned = messages.slice(0, split);
|
|
4509
|
+
const tail = messages.slice(split);
|
|
4510
|
+
let used = 0;
|
|
4511
|
+
for (const message of pinned) used += estimateMessageTokens(message);
|
|
4512
|
+
const retained = [];
|
|
4513
|
+
for (let i = tail.length - 1; i >= 0; i -= 1) {
|
|
4514
|
+
const message = tail[i];
|
|
4515
|
+
const cost = estimateMessageTokens(message);
|
|
4516
|
+
const fits = used + cost <= budget;
|
|
4517
|
+
if (!fits && retained.length >= minTail) break;
|
|
4518
|
+
retained.unshift(message);
|
|
4519
|
+
used += cost;
|
|
4520
|
+
}
|
|
4521
|
+
return [...pinned, ...retained];
|
|
4522
|
+
}
|
|
4523
|
+
function sanitizeHistoryForNextTurn(messages, options = {}) {
|
|
4524
|
+
const keep = Math.max(0, options.keepImagesOnLastMessages ?? 1);
|
|
4525
|
+
const placeholder = options.imagePlaceholder;
|
|
4526
|
+
const cutoff = messages.length - keep;
|
|
4527
|
+
const sanitized = [];
|
|
4528
|
+
for (let index = 0; index < messages.length; index += 1) {
|
|
4529
|
+
const message = messages[index];
|
|
4530
|
+
if (index >= cutoff || !message.images || message.images.length === 0) {
|
|
4531
|
+
sanitized.push(message);
|
|
4532
|
+
continue;
|
|
4533
|
+
}
|
|
4534
|
+
const { images: _evicted, ...rest } = message;
|
|
4535
|
+
if (placeholder === void 0) {
|
|
4536
|
+
sanitized.push(rest);
|
|
4537
|
+
} else {
|
|
4538
|
+
sanitized.push({
|
|
4539
|
+
...rest,
|
|
4540
|
+
content: rest.content === "" ? placeholder : `${rest.content}
|
|
4541
|
+
|
|
4542
|
+
${placeholder}`
|
|
4543
|
+
});
|
|
4544
|
+
}
|
|
4545
|
+
}
|
|
4546
|
+
return sanitized;
|
|
4547
|
+
}
|
|
4274
4548
|
function extractTurnStats(res) {
|
|
4275
4549
|
const cachedTokens = res.prompt_eval_cached_count ?? 0;
|
|
4276
4550
|
const evaluatedTokens = res.prompt_eval_count ?? 0;
|
|
@@ -4357,6 +4631,48 @@ var ConversationSession = class {
|
|
|
4357
4631
|
getMessages() {
|
|
4358
4632
|
return this.history.map((message) => message);
|
|
4359
4633
|
}
|
|
4634
|
+
/**
|
|
4635
|
+
* Compacts the session's history in place: pinned system prompt plus the
|
|
4636
|
+
* most recent turns that fit the budget, oldest tailable turns dropped.
|
|
4637
|
+
*
|
|
4638
|
+
* This is the explicit escape hatch for sessions that outgrew their
|
|
4639
|
+
* window — `cacheStats.hitRate` collapsing toward zero while history grows
|
|
4640
|
+
* is the usual symptom, and the pre-flight warnings on `chat()` fire once
|
|
4641
|
+
* the prompt estimate crosses the threshold. After compaction the next
|
|
4642
|
+
* turn's prompt prefix differs from everything before it, so the KV cache
|
|
4643
|
+
* starts cold — visible as a one-turn `evaluatedTokens` spike in
|
|
4644
|
+
* `cacheStats`. Cumulative cache tallies are deliberately **not** reset:
|
|
4645
|
+
* they are observations, and the spike is part of the story.
|
|
4646
|
+
*
|
|
4647
|
+
* Returns the before/after estimates and the drop count. No-op (zero
|
|
4648
|
+
* drops, equal before/after estimates) when the history already fits.
|
|
4649
|
+
*/
|
|
4650
|
+
compact(options = {}) {
|
|
4651
|
+
const window = options.maxEstimatedTokens ?? this.config.options?.num_ctx ?? OLLAMA_FALLBACK_CONTEXT_LENGTH;
|
|
4652
|
+
const reserve = options.reserveForReply ?? Math.ceil(window * 0.1);
|
|
4653
|
+
const estimatedTokensBefore = this.history.reduce(
|
|
4654
|
+
(sum, message) => sum + estimateMessageTokens(message),
|
|
4655
|
+
0
|
|
4656
|
+
);
|
|
4657
|
+
const compacted = compactConversationHistory(this.history, {
|
|
4658
|
+
maxEstimatedTokens: window,
|
|
4659
|
+
reserveForReply: reserve,
|
|
4660
|
+
...options.minTailMessages !== void 0 ? { minTailMessages: options.minTailMessages } : {}
|
|
4661
|
+
});
|
|
4662
|
+
const estimatedTokensAfter = compacted.reduce(
|
|
4663
|
+
(sum, message) => sum + estimateMessageTokens(message),
|
|
4664
|
+
0
|
|
4665
|
+
);
|
|
4666
|
+
const droppedMessages = this.history.length - compacted.length;
|
|
4667
|
+
this.history.length = 0;
|
|
4668
|
+
this.history.push(...compacted);
|
|
4669
|
+
return {
|
|
4670
|
+
droppedMessages,
|
|
4671
|
+
estimatedTokensBefore,
|
|
4672
|
+
estimatedTokensAfter,
|
|
4673
|
+
effectiveBudget: window - reserve
|
|
4674
|
+
};
|
|
4675
|
+
}
|
|
4360
4676
|
/**
|
|
4361
4677
|
* Clears the conversation back to its initial state (system prompt, zero
|
|
4362
4678
|
* turns, zeroed cache statistics). The next turn starts with a cold cache
|
|
@@ -4375,102 +4691,129 @@ var ConversationSession = class {
|
|
|
4375
4691
|
}
|
|
4376
4692
|
};
|
|
4377
4693
|
|
|
4378
|
-
// src/
|
|
4379
|
-
var
|
|
4380
|
-
var
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
}
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4694
|
+
// src/embed-batch.ts
|
|
4695
|
+
var DEFAULT_EMBED_BATCH_SIZE = 32;
|
|
4696
|
+
var DEFAULT_EMBED_CONCURRENCY = 3;
|
|
4697
|
+
function findOversizedEmbedInputs(input, contextLength) {
|
|
4698
|
+
const oversized = [];
|
|
4699
|
+
for (let index = 0; index < input.length; index++) {
|
|
4700
|
+
const text = input[index];
|
|
4701
|
+
if (text === void 0) continue;
|
|
4702
|
+
const estimate = estimateTokens(text);
|
|
4703
|
+
if (estimate > contextLength) {
|
|
4704
|
+
oversized.push({ index, estimate });
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
return oversized;
|
|
4708
|
+
}
|
|
4709
|
+
function embedBatchOverflowMessage(oversized, totalInputs, contextLength, truncateFalse) {
|
|
4710
|
+
const shown = oversized.slice(0, 5).map((entry) => `#${entry.index} (~${entry.estimate} tokens)`);
|
|
4711
|
+
const elided = oversized.length > 5 ? `, +${oversized.length - 5} more` : "";
|
|
4712
|
+
const outcome = truncateFalse ? "the server will reject them (`truncate: false`)" : "Ollama will silently truncate them, producing degraded vectors";
|
|
4713
|
+
return `embedBatch: ${oversized.length} of ${totalInputs} input strings exceed the ${contextLength}-token context window (${shown.join(", ")}${elided}). ${outcome}. Raise \`options.num_ctx\`, pre-split long inputs, or set \`truncate: true\` explicitly to acknowledge truncation.`;
|
|
4714
|
+
}
|
|
4715
|
+
function assertPositiveInteger(value, field) {
|
|
4716
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
4717
|
+
throw new OllamaClientError(
|
|
4718
|
+
`embedBatch: \`${field}\` must be a positive integer (got ${value}).`,
|
|
4719
|
+
{ code: "invalid_request" }
|
|
4720
|
+
);
|
|
4397
4721
|
}
|
|
4398
|
-
return Math.ceil(cjk + other / CHARS_PER_TOKEN);
|
|
4399
4722
|
}
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
}
|
|
4723
|
+
async function batchEmbed(client, options) {
|
|
4724
|
+
const { model, input } = options;
|
|
4725
|
+
const batchSize = options.batchSize ?? DEFAULT_EMBED_BATCH_SIZE;
|
|
4726
|
+
const concurrency = options.concurrency ?? DEFAULT_EMBED_CONCURRENCY;
|
|
4727
|
+
assertPositiveInteger(batchSize, "batchSize");
|
|
4728
|
+
assertPositiveInteger(concurrency, "concurrency");
|
|
4729
|
+
if (input.length === 0) {
|
|
4730
|
+
return { model, embeddings: [], batchCount: 0 };
|
|
4409
4731
|
}
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4732
|
+
const batches = [];
|
|
4733
|
+
for (let start = 0; start < input.length; start += batchSize) {
|
|
4734
|
+
batches.push(input.slice(start, start + batchSize));
|
|
4735
|
+
}
|
|
4736
|
+
const embeddings = new Array(input.length);
|
|
4737
|
+
const controller = new AbortController();
|
|
4738
|
+
const propagateExternalAbort = () => {
|
|
4739
|
+
controller.abort(options.signal?.reason);
|
|
4740
|
+
};
|
|
4741
|
+
if (options.signal) {
|
|
4742
|
+
if (options.signal.aborted) {
|
|
4743
|
+
propagateExternalAbort();
|
|
4744
|
+
} else {
|
|
4745
|
+
options.signal.addEventListener("abort", propagateExternalAbort, { once: true });
|
|
4420
4746
|
}
|
|
4421
4747
|
}
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4748
|
+
let nextBatch = 0;
|
|
4749
|
+
let completedBatches = 0;
|
|
4750
|
+
let failure;
|
|
4751
|
+
const worker = async () => {
|
|
4752
|
+
for (; ; ) {
|
|
4753
|
+
if (controller.signal.aborted) {
|
|
4754
|
+
throw failure?.error ?? new OllamaAbortError("Embed batch aborted");
|
|
4755
|
+
}
|
|
4756
|
+
const batchIndex = nextBatch;
|
|
4757
|
+
nextBatch++;
|
|
4758
|
+
if (batchIndex >= batches.length) return;
|
|
4759
|
+
const batch = batches[batchIndex];
|
|
4760
|
+
if (!batch) return;
|
|
4761
|
+
try {
|
|
4762
|
+
const request = {
|
|
4763
|
+
model,
|
|
4764
|
+
input: batch,
|
|
4765
|
+
...options.truncate !== void 0 ? { truncate: options.truncate } : {},
|
|
4766
|
+
...options.dimensions !== void 0 ? { dimensions: options.dimensions } : {},
|
|
4767
|
+
...options.keep_alive !== void 0 ? { keep_alive: options.keep_alive } : {},
|
|
4768
|
+
...options.options !== void 0 ? { options: options.options } : {},
|
|
4769
|
+
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
|
|
4770
|
+
signal: controller.signal
|
|
4771
|
+
};
|
|
4772
|
+
const response = await client.embed(request);
|
|
4773
|
+
if (response.embeddings.length !== batch.length) {
|
|
4774
|
+
throw new OllamaClientError(
|
|
4775
|
+
`embedBatch: server returned ${response.embeddings.length} embeddings for a batch of ${batch.length} inputs (batch ${batchIndex}).`,
|
|
4776
|
+
{ code: "invalid_response" }
|
|
4777
|
+
);
|
|
4778
|
+
}
|
|
4779
|
+
const offset = batchIndex * batchSize;
|
|
4780
|
+
for (let j = 0; j < batch.length; j++) {
|
|
4781
|
+
embeddings[offset + j] = response.embeddings[j];
|
|
4782
|
+
}
|
|
4783
|
+
completedBatches++;
|
|
4784
|
+
options.onBatchComplete?.(completedBatches, batches.length);
|
|
4785
|
+
} catch (error) {
|
|
4786
|
+
if (!failure) {
|
|
4787
|
+
failure = { error };
|
|
4788
|
+
controller.abort(error);
|
|
4789
|
+
throw error;
|
|
4790
|
+
}
|
|
4791
|
+
throw failure.error;
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
};
|
|
4795
|
+
try {
|
|
4796
|
+
const workerCount = Math.min(concurrency, batches.length);
|
|
4797
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
4798
|
+
} finally {
|
|
4799
|
+
if (options.signal) {
|
|
4800
|
+
options.signal.removeEventListener("abort", propagateExternalAbort);
|
|
4801
|
+
}
|
|
4446
4802
|
}
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
}
|
|
4803
|
+
for (let i = 0; i < embeddings.length; i++) {
|
|
4804
|
+
if (embeddings[i] === void 0) {
|
|
4805
|
+
throw new OllamaClientError(
|
|
4806
|
+
`embedBatch: internal invariant violated \u2014 no embedding produced for input #${i}.`,
|
|
4807
|
+
{ code: "invalid_response" }
|
|
4808
|
+
);
|
|
4809
|
+
}
|
|
4454
4810
|
}
|
|
4455
4811
|
return {
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
exceedsThreshold: estimatedPromptTokens > OLLAMA_FALLBACK_CONTEXT_LENGTH
|
|
4812
|
+
model,
|
|
4813
|
+
embeddings,
|
|
4814
|
+
batchCount: batches.length
|
|
4460
4815
|
};
|
|
4461
4816
|
}
|
|
4462
|
-
var OLLAMA_FALLBACK_CONTEXT_LENGTH = 2048;
|
|
4463
|
-
function contextWarningMessage(check2, kind) {
|
|
4464
|
-
const where = `OllamaClient.${kind}()`;
|
|
4465
|
-
const estimate = `~${check2.estimatedPromptTokens} tokens`;
|
|
4466
|
-
if (check2.windowSource === "request") {
|
|
4467
|
-
return `${where}: estimated prompt size ${estimate} is close to or beyond the request's context window (num_ctx=${check2.effectiveContextLength}). The server will likely silently truncate the top of the conversation \u2014 the model loses earlier turns with no error. Raise \`options.num_ctx\` or shorten the conversation. Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
|
|
4468
|
-
}
|
|
4469
|
-
if (check2.windowSource === "client-default") {
|
|
4470
|
-
return `${where}: estimated prompt size ${estimate} is close to or beyond the client's \`defaultContextLength\` (${check2.effectiveContextLength}, injected as \`num_ctx\`). The server will likely silently truncate the top of the conversation \u2014 the model loses earlier turns with no error. Raise \`defaultContextLength\`, set \`options.num_ctx\` per request, or shorten the conversation. Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
|
|
4471
|
-
}
|
|
4472
|
-
return `${where}: estimated prompt size ${estimate} outright exceeds Ollama's conservative server-default context window (${OLLAMA_FALLBACK_CONTEXT_LENGTH}\u20134096 tokens when \`num_ctx\` is unset \u2014 model-dependent). Unless the model's default is larger, the server will silently truncate the top of the conversation \u2014 the model loses earlier turns with no error. Set \`options.num_ctx\` (or the client's \`defaultContextLength\`) to an explicit window to make truncation visible and controllable. Estimates are heuristic (\xB120\u201330%); see \`estimateTokens\`.`;
|
|
4473
|
-
}
|
|
4474
4817
|
|
|
4475
4818
|
// src/generated/api/operations.ts
|
|
4476
4819
|
var blobsOp = {
|
|
@@ -4996,6 +5339,14 @@ var OllamaClient = class {
|
|
|
4996
5339
|
contextWarningThreshold;
|
|
4997
5340
|
/** Configured overflow policy — `'warn'` (default) or `'throw'`. */
|
|
4998
5341
|
onContextOverflow;
|
|
5342
|
+
/**
|
|
5343
|
+
* Controllers for every in-flight request and active stream managed by this
|
|
5344
|
+
* client — see {@link OllamaClient.destroy} and {@link createRequestScope}.
|
|
5345
|
+
* Entries live for exactly as long as the underlying work does: until the
|
|
5346
|
+
* response settles (requests) or the stream's `finalResult` settles
|
|
5347
|
+
* (streams, via `executeWithFailover`'s `holdUntil` release path).
|
|
5348
|
+
*/
|
|
5349
|
+
activeRequests = /* @__PURE__ */ new Set();
|
|
4999
5350
|
constructor(config = {}) {
|
|
5000
5351
|
this.cloudApiKey = resolveApiKey(config.apiKey);
|
|
5001
5352
|
const defaultBaseUrl = resolveBaseUrl(config.baseUrl);
|
|
@@ -5160,6 +5511,39 @@ var OllamaClient = class {
|
|
|
5160
5511
|
}
|
|
5161
5512
|
return { ...options, num_ctx: this.defaultContextLength };
|
|
5162
5513
|
}
|
|
5514
|
+
/**
|
|
5515
|
+
* Creates the per-request abort scope registered with {@link destroy}:
|
|
5516
|
+
* a controller that fires when the caller's `signal` aborts OR when
|
|
5517
|
+
* `destroy()` runs, whichever comes first. `dispose()` removes the
|
|
5518
|
+
* registration when the work it guards settles — for plain requests when
|
|
5519
|
+
* the response settles, for streams when `finalResult` settles (the same
|
|
5520
|
+
* moment `executeWithFailover`'s `holdUntil` releases the endpoint slot),
|
|
5521
|
+
* so a destroy() can tear down a stream mid-consumption but never outlives
|
|
5522
|
+
* one that already finished.
|
|
5523
|
+
*/
|
|
5524
|
+
createRequestScope(userSignal) {
|
|
5525
|
+
const controller = new AbortController();
|
|
5526
|
+
const propagateUserAbort = () => {
|
|
5527
|
+
controller.abort(userSignal?.reason);
|
|
5528
|
+
};
|
|
5529
|
+
let detach;
|
|
5530
|
+
if (userSignal) {
|
|
5531
|
+
if (userSignal.aborted) {
|
|
5532
|
+
propagateUserAbort();
|
|
5533
|
+
} else {
|
|
5534
|
+
userSignal.addEventListener("abort", propagateUserAbort, { once: true });
|
|
5535
|
+
detach = () => userSignal.removeEventListener("abort", propagateUserAbort);
|
|
5536
|
+
}
|
|
5537
|
+
}
|
|
5538
|
+
this.activeRequests.add(controller);
|
|
5539
|
+
return {
|
|
5540
|
+
signal: controller.signal,
|
|
5541
|
+
dispose: () => {
|
|
5542
|
+
detach?.();
|
|
5543
|
+
this.activeRequests.delete(controller);
|
|
5544
|
+
}
|
|
5545
|
+
};
|
|
5546
|
+
}
|
|
5163
5547
|
/**
|
|
5164
5548
|
* Fail-fast guard for `format` (structured output) requests: throws before any network
|
|
5165
5549
|
* call if the candidate endpoint is inferred as Ollama Cloud, which does not currently
|
|
@@ -5176,7 +5560,8 @@ var OllamaClient = class {
|
|
|
5176
5560
|
}
|
|
5177
5561
|
}
|
|
5178
5562
|
async executeWithFailover(operation, options) {
|
|
5179
|
-
const
|
|
5563
|
+
const scope = this.createRequestScope(options?.signal);
|
|
5564
|
+
const timeout = createTimeoutSignal(options?.timeoutMs ?? this.timeoutMs, scope.signal);
|
|
5180
5565
|
const requestId = createLogicalRequestId();
|
|
5181
5566
|
let deferTimeoutCancel = false;
|
|
5182
5567
|
try {
|
|
@@ -5255,6 +5640,7 @@ var OllamaClient = class {
|
|
|
5255
5640
|
if (holdPromise) {
|
|
5256
5641
|
void holdPromise.catch(() => void 0).finally(() => {
|
|
5257
5642
|
timeout.cancel();
|
|
5643
|
+
scope.dispose();
|
|
5258
5644
|
this.registry.release(endpoint.name);
|
|
5259
5645
|
});
|
|
5260
5646
|
} else {
|
|
@@ -5265,7 +5651,10 @@ var OllamaClient = class {
|
|
|
5265
5651
|
throw lastError ?? new Error("No healthy Ollama endpoints available");
|
|
5266
5652
|
}
|
|
5267
5653
|
} finally {
|
|
5268
|
-
if (!deferTimeoutCancel)
|
|
5654
|
+
if (!deferTimeoutCancel) {
|
|
5655
|
+
timeout.cancel();
|
|
5656
|
+
scope.dispose();
|
|
5657
|
+
}
|
|
5269
5658
|
}
|
|
5270
5659
|
}
|
|
5271
5660
|
async chat(req) {
|
|
@@ -5425,6 +5814,58 @@ var OllamaClient = class {
|
|
|
5425
5814
|
const res = await this.embed({ model, input });
|
|
5426
5815
|
return res.embeddings;
|
|
5427
5816
|
}
|
|
5817
|
+
/**
|
|
5818
|
+
* Batch-constrained, order-preserving embedding generation for large
|
|
5819
|
+
* corpora — the safe building block for RAG/vector-index ingestion at
|
|
5820
|
+
* scale. Splits `input` into `batchSize` slices and embeds them through
|
|
5821
|
+
* {@link embed} with at most `concurrency` batches in flight (defaults:
|
|
5822
|
+
* 32 × 3), so `Promise.all`-style floods that OOM local daemons and
|
|
5823
|
+
* saturate `OLLAMA_MAX_QUEUE` can't happen by accident. Fail-fast: the
|
|
5824
|
+
* first batch error aborts every sibling in-flight batch and rejects the
|
|
5825
|
+
* whole operation with the original error.
|
|
5826
|
+
*
|
|
5827
|
+
* Before any request is sent, `defaultContextLength` is injected as
|
|
5828
|
+
* `num_ctx` (same as chat/generate) and each input string's token estimate
|
|
5829
|
+
* is checked against the resolved window: Ollama silently truncates
|
|
5830
|
+
* oversized embedding inputs by default, so the client warns (or throws,
|
|
5831
|
+
* under `onContextOverflow: 'throw'`) with the offending indexes — see
|
|
5832
|
+
* `src/embed-batch.ts` for the exact trip point (no margin: an embedding
|
|
5833
|
+
* prompt IS the whole input).
|
|
5834
|
+
*
|
|
5835
|
+
* ```ts
|
|
5836
|
+
* const { embeddings, batchCount } = await client.embedBatch({
|
|
5837
|
+
* model: 'nomic-embed-text:latest',
|
|
5838
|
+
* input: corpus, // e.g. 50k chunks
|
|
5839
|
+
* batchSize: 32, // inputs per /api/embed request
|
|
5840
|
+
* concurrency: 3, // batches in flight
|
|
5841
|
+
* onBatchComplete: (done, total) => progress.log(`${done}/${total}`),
|
|
5842
|
+
* });
|
|
5843
|
+
* // embeddings[i] always corresponds to corpus[i]
|
|
5844
|
+
* ```
|
|
5845
|
+
*/
|
|
5846
|
+
async embedBatch(options) {
|
|
5847
|
+
const resolvedOptions = this.injectDefaultContextLength(options.options);
|
|
5848
|
+
const contextLength = resolvedOptions?.num_ctx;
|
|
5849
|
+
if (contextLength !== void 0) {
|
|
5850
|
+
const oversized = findOversizedEmbedInputs(options.input, contextLength);
|
|
5851
|
+
if (oversized.length > 0) {
|
|
5852
|
+
const message = embedBatchOverflowMessage(
|
|
5853
|
+
oversized,
|
|
5854
|
+
options.input.length,
|
|
5855
|
+
contextLength,
|
|
5856
|
+
options.truncate === false
|
|
5857
|
+
);
|
|
5858
|
+
if (this.onContextOverflow === "throw") {
|
|
5859
|
+
throw new OllamaClientError(message, { code: "context_overflow" });
|
|
5860
|
+
}
|
|
5861
|
+
this.logger.warn(message);
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5864
|
+
return batchEmbed(
|
|
5865
|
+
this,
|
|
5866
|
+
resolvedOptions === options.options ? options : { ...options, options: resolvedOptions }
|
|
5867
|
+
);
|
|
5868
|
+
}
|
|
5428
5869
|
/**
|
|
5429
5870
|
* @deprecated Ollama's `/api/embeddings` endpoint has been superseded by `/api/embed`
|
|
5430
5871
|
* (exposed here as {@link OllamaClient.embed}), which additionally supports batch
|
|
@@ -5444,7 +5885,15 @@ var OllamaClient = class {
|
|
|
5444
5885
|
createModel = (r) => this.models.create(r);
|
|
5445
5886
|
deleteModel = (req) => this.models.delete(req);
|
|
5446
5887
|
copyModel = (req) => this.models.copy(req);
|
|
5447
|
-
ps = () => this.models.ps();
|
|
5888
|
+
ps = (request) => this.models.ps(request);
|
|
5889
|
+
/**
|
|
5890
|
+
* Discovers the model's real context window — the allocated window of the
|
|
5891
|
+
* running instance (`/api/ps`), the Modelfile `num_ctx` default, and the
|
|
5892
|
+
* native GGUF maximum (`/api/show` `model_info`), resolved by precedence.
|
|
5893
|
+
* Convenience alias for {@link ModelsClient.getContextLength} — see that
|
|
5894
|
+
* method for the full contract and the precedence order.
|
|
5895
|
+
*/
|
|
5896
|
+
getContextLength = (request) => this.models.getContextLength(request);
|
|
5448
5897
|
version = () => this.models.version();
|
|
5449
5898
|
createBlob = (digest, data) => this.models.createBlob(digest, data);
|
|
5450
5899
|
checkBlob = (digest) => this.models.checkBlob(digest);
|
|
@@ -5594,10 +6043,12 @@ var OllamaClient = class {
|
|
|
5594
6043
|
async executeCloudRequest(operation, options) {
|
|
5595
6044
|
var _stack = [];
|
|
5596
6045
|
try {
|
|
5597
|
-
const
|
|
6046
|
+
const scope = this.createRequestScope(options.signal);
|
|
6047
|
+
const timeout = createTimeoutSignal(options.timeoutMs ?? this.timeoutMs, scope.signal);
|
|
5598
6048
|
const _timeout = __using(_stack, {
|
|
5599
6049
|
[Symbol.dispose]() {
|
|
5600
6050
|
timeout.cancel();
|
|
6051
|
+
scope.dispose();
|
|
5601
6052
|
}
|
|
5602
6053
|
});
|
|
5603
6054
|
const requestId = createLogicalRequestId();
|
|
@@ -5658,6 +6109,34 @@ var OllamaClient = class {
|
|
|
5658
6109
|
endpointStatus() {
|
|
5659
6110
|
return this.registry.status();
|
|
5660
6111
|
}
|
|
6112
|
+
/**
|
|
6113
|
+
* Immediately aborts every in-flight request and active stream managed by
|
|
6114
|
+
* this client — the clean teardown path for short-lived processes, CLI
|
|
6115
|
+
* runners, and `node:worker_threads` tasks where dangling fetch bodies,
|
|
6116
|
+
* unconsumed stream readers, or queued capacity waiters would otherwise
|
|
6117
|
+
* keep the event loop (and thus the thread/process) alive.
|
|
6118
|
+
*
|
|
6119
|
+
* Every aborted operation rejects with an {@link OllamaAbortError}
|
|
6120
|
+
* (`code: 'aborted'`) carrying `reason` as its message — including requests
|
|
6121
|
+
* still queued behind `maxConcurrentPerEndpoint`, not just dispatched ones.
|
|
6122
|
+
* Streams reject their `finalResult` and release their endpoint slots.
|
|
6123
|
+
*
|
|
6124
|
+
* Idempotent: calling it again (or on a client with nothing in flight)
|
|
6125
|
+
* is a no-op that returns `0`. The client remains usable afterward —
|
|
6126
|
+
* `destroy()` is a drain, not a permanent disable; callers that want a
|
|
6127
|
+
* permanently-dead client should discard the instance.
|
|
6128
|
+
*
|
|
6129
|
+
* @returns the number of in-flight operations that were aborted.
|
|
6130
|
+
*/
|
|
6131
|
+
destroy(reason = "Ollama client destroyed") {
|
|
6132
|
+
const controllers = [...this.activeRequests];
|
|
6133
|
+
const cause = new OllamaAbortError(reason);
|
|
6134
|
+
for (const controller of controllers) {
|
|
6135
|
+
controller.abort(cause);
|
|
6136
|
+
}
|
|
6137
|
+
this.activeRequests.clear();
|
|
6138
|
+
return controllers.length;
|
|
6139
|
+
}
|
|
5661
6140
|
// --- Compatibility Adapters ---
|
|
5662
6141
|
get openai() {
|
|
5663
6142
|
const ep = this.registry.candidates()[0];
|
|
@@ -6662,11 +7141,29 @@ ${systemPrompt}`
|
|
|
6662
7141
|
}
|
|
6663
7142
|
|
|
6664
7143
|
// src/agent/agent.ts
|
|
7144
|
+
var SIGNATURE_REPORT_LIMIT = 200;
|
|
7145
|
+
function stableStringify(value) {
|
|
7146
|
+
if (value === null || typeof value !== "object") {
|
|
7147
|
+
return JSON.stringify(value) ?? "null";
|
|
7148
|
+
}
|
|
7149
|
+
if (Array.isArray(value)) {
|
|
7150
|
+
return `[${value.map((entry) => stableStringify(entry)).join(",")}]`;
|
|
7151
|
+
}
|
|
7152
|
+
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableStringify(entryValue)}`);
|
|
7153
|
+
return `{${entries.join(",")}}`;
|
|
7154
|
+
}
|
|
7155
|
+
function canonicalToolCallSignature(toolCall) {
|
|
7156
|
+
return `${toolCall.function.name}(${stableStringify(toolCall.function.arguments)})`;
|
|
7157
|
+
}
|
|
7158
|
+
function reportableSignature(signature) {
|
|
7159
|
+
return signature.length <= SIGNATURE_REPORT_LIMIT ? signature : `${signature.slice(0, SIGNATURE_REPORT_LIMIT)}\u2026`;
|
|
7160
|
+
}
|
|
6665
7161
|
var Agent = class {
|
|
6666
7162
|
client;
|
|
6667
7163
|
tools;
|
|
6668
7164
|
maxIterations;
|
|
6669
7165
|
maxToolCalls;
|
|
7166
|
+
maxRepeatedToolCalls;
|
|
6670
7167
|
hooks;
|
|
6671
7168
|
validateToolCapability;
|
|
6672
7169
|
toolContextSize;
|
|
@@ -6675,10 +7172,14 @@ var Agent = class {
|
|
|
6675
7172
|
this.tools = config.tools;
|
|
6676
7173
|
this.maxIterations = config.maxIterations ?? 10;
|
|
6677
7174
|
this.maxToolCalls = config.maxToolCalls;
|
|
7175
|
+
this.maxRepeatedToolCalls = config.maxRepeatedToolCalls;
|
|
6678
7176
|
this.hooks = config.hooks;
|
|
6679
7177
|
if (this.maxToolCalls !== void 0 && (!Number.isInteger(this.maxToolCalls) || this.maxToolCalls < 0)) {
|
|
6680
7178
|
throw new RangeError("Agent maxToolCalls must be a non-negative integer");
|
|
6681
7179
|
}
|
|
7180
|
+
if (this.maxRepeatedToolCalls !== void 0 && (!Number.isInteger(this.maxRepeatedToolCalls) || this.maxRepeatedToolCalls < 1)) {
|
|
7181
|
+
throw new RangeError("Agent maxRepeatedToolCalls must be a positive integer");
|
|
7182
|
+
}
|
|
6682
7183
|
this.validateToolCapability = config.validateToolCapability ?? true;
|
|
6683
7184
|
this.toolContextSize = config.toolContextSize ?? 32768;
|
|
6684
7185
|
if (!Number.isInteger(this.toolContextSize) || this.toolContextSize <= 0) {
|
|
@@ -6725,6 +7226,7 @@ var Agent = class {
|
|
|
6725
7226
|
)
|
|
6726
7227
|
} : input.options;
|
|
6727
7228
|
let toolCallsExecuted = 0;
|
|
7229
|
+
const signatureCounts = /* @__PURE__ */ new Map();
|
|
6728
7230
|
for (let iteration = 1; iteration <= this.maxIterations; iteration++) {
|
|
6729
7231
|
const outcome = await withSpan(
|
|
6730
7232
|
"ollama.agent.turn",
|
|
@@ -6756,6 +7258,24 @@ var Agent = class {
|
|
|
6756
7258
|
{ maxToolCalls: this.maxToolCalls, toolCallsExecuted }
|
|
6757
7259
|
);
|
|
6758
7260
|
}
|
|
7261
|
+
if (this.maxRepeatedToolCalls !== void 0) {
|
|
7262
|
+
for (const tc of toolCalls) {
|
|
7263
|
+
const signature = canonicalToolCallSignature(tc);
|
|
7264
|
+
const executions = (signatureCounts.get(signature) ?? 0) + 1;
|
|
7265
|
+
if (executions > this.maxRepeatedToolCalls) {
|
|
7266
|
+
throw new OllamaAgentToolLoopError(
|
|
7267
|
+
`Agent would repeat tool call "${tc.function.name}" ${executions} times (max ${this.maxRepeatedToolCalls}) without changing arguments \u2014 stuck in a tool loop`,
|
|
7268
|
+
{
|
|
7269
|
+
toolName: tc.function.name,
|
|
7270
|
+
repeatedExecutions: executions,
|
|
7271
|
+
maxRepeatedToolCalls: this.maxRepeatedToolCalls,
|
|
7272
|
+
signature: reportableSignature(signature)
|
|
7273
|
+
}
|
|
7274
|
+
);
|
|
7275
|
+
}
|
|
7276
|
+
signatureCounts.set(signature, executions);
|
|
7277
|
+
}
|
|
7278
|
+
}
|
|
6759
7279
|
for (const tc of toolCalls) {
|
|
6760
7280
|
this.hooks?.onToolCallStart?.(tc);
|
|
6761
7281
|
}
|
|
@@ -6788,6 +7308,160 @@ var Agent = class {
|
|
|
6788
7308
|
}
|
|
6789
7309
|
};
|
|
6790
7310
|
|
|
7311
|
+
// src/affinity.ts
|
|
7312
|
+
var DEFAULT_CONCURRENT_MODELS = 1;
|
|
7313
|
+
var DEFAULT_PER_MODEL_CONCURRENCY = 1;
|
|
7314
|
+
var DEFAULT_PS_CACHE_TTL_MS = 3e4;
|
|
7315
|
+
var ModelAffinityScheduler = class {
|
|
7316
|
+
constructor(client, options = {}) {
|
|
7317
|
+
this.client = client;
|
|
7318
|
+
this.concurrentModels = options.concurrentModels ?? DEFAULT_CONCURRENT_MODELS;
|
|
7319
|
+
this.perModelConcurrency = options.perModelConcurrency ?? DEFAULT_PER_MODEL_CONCURRENCY;
|
|
7320
|
+
this.psCacheTtlMs = options.psCacheTtlMs ?? DEFAULT_PS_CACHE_TTL_MS;
|
|
7321
|
+
if (!Number.isInteger(this.concurrentModels) || this.concurrentModels < 1) {
|
|
7322
|
+
throw new OllamaClientError("concurrentModels must be an integer >= 1.", {
|
|
7323
|
+
code: "invalid_request"
|
|
7324
|
+
});
|
|
7325
|
+
}
|
|
7326
|
+
if (!Number.isInteger(this.perModelConcurrency) || this.perModelConcurrency < 1) {
|
|
7327
|
+
throw new OllamaClientError("perModelConcurrency must be an integer >= 1.", {
|
|
7328
|
+
code: "invalid_request"
|
|
7329
|
+
});
|
|
7330
|
+
}
|
|
7331
|
+
if (!Number.isFinite(this.psCacheTtlMs) || this.psCacheTtlMs < 0) {
|
|
7332
|
+
throw new OllamaClientError("psCacheTtlMs must be a non-negative number.", {
|
|
7333
|
+
code: "invalid_request"
|
|
7334
|
+
});
|
|
7335
|
+
}
|
|
7336
|
+
}
|
|
7337
|
+
queues = /* @__PURE__ */ new Map();
|
|
7338
|
+
concurrentModels;
|
|
7339
|
+
perModelConcurrency;
|
|
7340
|
+
psCacheTtlMs;
|
|
7341
|
+
psCache;
|
|
7342
|
+
drainWaiters = [];
|
|
7343
|
+
/**
|
|
7344
|
+
* Runs `task` on the scheduler, serialized per model. When `model` is a
|
|
7345
|
+
* list of candidates, the already-loaded one is preferred (fresh
|
|
7346
|
+
* `/api/ps` data subject to the TTL cache); with none loaded — or when the
|
|
7347
|
+
* lookup fails — the first candidate runs. The chosen model is handed to
|
|
7348
|
+
* the task.
|
|
7349
|
+
*/
|
|
7350
|
+
async run(model, task) {
|
|
7351
|
+
const candidates = typeof model === "string" ? [model] : [...model];
|
|
7352
|
+
if (candidates.length === 0 || candidates.some((name) => typeof name !== "string" || name === "")) {
|
|
7353
|
+
throw new OllamaClientError("run() requires at least one non-empty model name.", {
|
|
7354
|
+
code: "invalid_request"
|
|
7355
|
+
});
|
|
7356
|
+
}
|
|
7357
|
+
const chosen = candidates.length === 1 ? candidates[0] : await this.chooseCandidate(candidates);
|
|
7358
|
+
return new Promise((resolve, reject) => {
|
|
7359
|
+
const queue = this.queues.get(chosen) ?? { items: [], active: 0 };
|
|
7360
|
+
this.queues.set(chosen, queue);
|
|
7361
|
+
queue.items.push({
|
|
7362
|
+
task,
|
|
7363
|
+
resolve,
|
|
7364
|
+
reject
|
|
7365
|
+
});
|
|
7366
|
+
this.pump();
|
|
7367
|
+
});
|
|
7368
|
+
}
|
|
7369
|
+
/**
|
|
7370
|
+
* Names of the models currently loaded, per `GET /api/ps`, cached for
|
|
7371
|
+
* `psCacheTtlMs`. Degrades to an empty list when the server can't answer
|
|
7372
|
+
* (never throws; candidate selection treats it as "nothing loaded").
|
|
7373
|
+
*/
|
|
7374
|
+
async loadedModels() {
|
|
7375
|
+
const names = await this.refreshPsNames();
|
|
7376
|
+
return [...names];
|
|
7377
|
+
}
|
|
7378
|
+
/** Current scheduling state — active models and queued task count. */
|
|
7379
|
+
get stats() {
|
|
7380
|
+
const activeModels = [];
|
|
7381
|
+
let queuedTasks = 0;
|
|
7382
|
+
for (const [name, queue] of this.queues) {
|
|
7383
|
+
if (queue.active > 0) activeModels.push(name);
|
|
7384
|
+
queuedTasks += queue.items.length;
|
|
7385
|
+
}
|
|
7386
|
+
return { activeModels, queuedTasks };
|
|
7387
|
+
}
|
|
7388
|
+
/**
|
|
7389
|
+
* Resolves once every queue is empty and no task is running. Does not
|
|
7390
|
+
* abort anything — tasks are caller-owned; abort them via their own
|
|
7391
|
+
* signals before disposing if needed.
|
|
7392
|
+
*/
|
|
7393
|
+
async dispose() {
|
|
7394
|
+
if (this.stats.queuedTasks === 0 && this.stats.activeModels.length === 0) return;
|
|
7395
|
+
await new Promise((resolve) => {
|
|
7396
|
+
this.drainWaiters.push(resolve);
|
|
7397
|
+
});
|
|
7398
|
+
}
|
|
7399
|
+
async chooseCandidate(candidates) {
|
|
7400
|
+
const loaded = await this.refreshPsNames();
|
|
7401
|
+
for (const candidate of candidates) {
|
|
7402
|
+
if (loaded.has(candidate)) return candidate;
|
|
7403
|
+
}
|
|
7404
|
+
return candidates[0];
|
|
7405
|
+
}
|
|
7406
|
+
async refreshPsNames() {
|
|
7407
|
+
const now = Date.now();
|
|
7408
|
+
if (this.psCache !== void 0 && now - this.psCache.at <= this.psCacheTtlMs) {
|
|
7409
|
+
return this.psCache.names;
|
|
7410
|
+
}
|
|
7411
|
+
let names;
|
|
7412
|
+
try {
|
|
7413
|
+
const ps = await this.client.models.ps();
|
|
7414
|
+
names = new Set(ps.models.map((entry) => entry.name));
|
|
7415
|
+
} catch {
|
|
7416
|
+
return /* @__PURE__ */ new Set();
|
|
7417
|
+
}
|
|
7418
|
+
this.psCache = { at: now, names };
|
|
7419
|
+
return names;
|
|
7420
|
+
}
|
|
7421
|
+
pump() {
|
|
7422
|
+
for (const [name, queue] of this.queues) {
|
|
7423
|
+
while (queue.items.length > 0 && queue.active < this.perModelConcurrency) {
|
|
7424
|
+
if (queue.active === 0 && this.activeModelCount() >= this.concurrentModels) {
|
|
7425
|
+
break;
|
|
7426
|
+
}
|
|
7427
|
+
const entry = queue.items.shift();
|
|
7428
|
+
queue.active += 1;
|
|
7429
|
+
this.execute(name, queue, entry);
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
}
|
|
7433
|
+
activeModelCount() {
|
|
7434
|
+
let count = 0;
|
|
7435
|
+
for (const queue of this.queues.values()) {
|
|
7436
|
+
if (queue.active > 0) count += 1;
|
|
7437
|
+
}
|
|
7438
|
+
return count;
|
|
7439
|
+
}
|
|
7440
|
+
execute(name, queue, entry) {
|
|
7441
|
+
void (async () => {
|
|
7442
|
+
try {
|
|
7443
|
+
entry.resolve(await entry.task(name));
|
|
7444
|
+
} catch (err) {
|
|
7445
|
+
entry.reject(err);
|
|
7446
|
+
} finally {
|
|
7447
|
+
queue.active -= 1;
|
|
7448
|
+
if (queue.items.length === 0 && queue.active === 0) {
|
|
7449
|
+
this.queues.delete(name);
|
|
7450
|
+
}
|
|
7451
|
+
if (this.stats.queuedTasks === 0 && this.stats.activeModels.length === 0) {
|
|
7452
|
+
const waiters = [...this.drainWaiters];
|
|
7453
|
+
this.drainWaiters.length = 0;
|
|
7454
|
+
for (const resolve of waiters) resolve();
|
|
7455
|
+
}
|
|
7456
|
+
this.pump();
|
|
7457
|
+
}
|
|
7458
|
+
})();
|
|
7459
|
+
}
|
|
7460
|
+
};
|
|
7461
|
+
function createModelAffinityScheduler(client, options) {
|
|
7462
|
+
return new ModelAffinityScheduler(client, options);
|
|
7463
|
+
}
|
|
7464
|
+
|
|
6791
7465
|
// src/system-one.ts
|
|
6792
7466
|
var MAX_SYSTEM_ONE_IMAGES_BYTES = 32 * 1024 * 1024;
|
|
6793
7467
|
var MAX_SYSTEM_ONE_REQUEST_BYTES = 64 * 1024;
|
|
@@ -6795,6 +7469,6 @@ function estimateSystemOneRequestBytes(request) {
|
|
|
6795
7469
|
return new TextEncoder().encode(JSON.stringify(request)).byteLength;
|
|
6796
7470
|
}
|
|
6797
7471
|
|
|
6798
|
-
export { ATTR_GEN_AI_OPERATION_NAME, ATTR_GEN_AI_REQUEST_MODEL, ATTR_GEN_AI_RESPONSE_MODEL, ATTR_GEN_AI_SYSTEM, ATTR_GEN_AI_TOOL_CALL_ID, ATTR_GEN_AI_TOOL_NAME, ATTR_GEN_AI_USAGE_INPUT_TOKENS, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_OLLAMA_AGENT_ITERATION, ATTR_OLLAMA_AGENT_MAX_ITERATIONS, ATTR_OLLAMA_ENDPOINT_ATTEMPT, ATTR_OLLAMA_ENDPOINT_NAME, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_URL_FULL, Agent, AnthropicCompatClient, AnthropicMessagesStream, ConversationSession, DEFAULT_BACKOFF, DEFAULT_BASE_URL, DEFAULT_CONTEXT_WARNING_THRESHOLD, DEFAULT_FAILOVER_CODES, DEFAULT_FETCH_BACKOFF_CONFIG, DEFAULT_RETRY_CONFIG, DEFAULT_TIMEOUT_MS, EndpointRegistry, GEN_AI_SYSTEM_OLLAMA, HttpClient, IMAGE_TOKEN_ESTIMATE, KEEP_ALIVE_INDEFINITE, KEEP_ALIVE_UNLOAD, KNOWN_DONE_REASONS, MAX_SYSTEM_ONE_IMAGES_BYTES, MAX_SYSTEM_ONE_REQUEST_BYTES, McpBridge, ModelsClient, NANOS_PER_MS, NANOS_PER_SECOND, NOOP_LOGGER, OLLAMA_CLOUD_BASE_URL, OLLAMA_FALLBACK_CONTEXT_LENGTH, OllamaAbortError, OllamaAgentMaxIterationsError, OllamaAgentMaxToolCallsError, OllamaAuthError, OllamaBadGatewayError, OllamaClient, OllamaClientError, OllamaGenericClientError, OllamaIncompatibleModelError, OllamaMcpError, OllamaModelRoutingError, OllamaNetworkError, OllamaNotFoundError, OllamaOverloadedError, OllamaQuotaExceededError, OllamaRateLimitError, OllamaRequestTooLargeError, OllamaRequestValidationError, OllamaResponseValidationError, OllamaServerError, OllamaServerVersionUnknownError, OllamaSkillInvalidError, OllamaSkillNotFoundError, OllamaStream, OllamaStreamError, OllamaTimeoutError, OllamaToolTimeoutError, OllamaToolValidationError, OllamaUnsupportedCapabilityError, OpenAIChatCompletionStream, OpenAICompatClient, OpenAICompletionStream, OpenAIResponsesStream, OpenAIResponsesStreamError, QuotaManager, RETRYABLE_STATUS_CODES, ResponsesModule, ToolCallAccumulator, ToolRegistry, addSpanEvent, applySkill, calculateBackoff, checkChatContext, checkEndpointHealth, checkGenerateContext, composeMiddleware, contextWarningMessage, createConsoleLogger, createDecision, createOllamaCloudFreeTierQuota, createTimeoutSignal, defineTool, detectModelCapabilities, encodeImage, estimateChatRequestTokens, estimateGenerateRequestTokens, estimateSystemOneRequestBytes, estimateTokens, extractUsage, fetchWithBackoff, imageStringNeedsResolution, inferRuntimeMode, isKeepAliveSugar, isKnownDoneReason, isSameToolCall, listAllMcpTools, listAvailableModels, loadMcpTools, mapError, mergeToolCall, mergeToolCallArgumentsString, mergeToolCallArrays, normalizeChatStream, normalizeGenerateStream, normalizeKeepAlive, normalizeProgressStream, parseFrontmatter, parseNdjsonStream, parseSseStream, parseStructuredOutput, registerMcpTools, resolveCredentialEndpoints, resolveImageInput, setSpanError, toDataStream, toResponse, toTextStream, withRetry, withSpan, zodToJsonSchema };
|
|
7472
|
+
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, ModelAffinityScheduler, ModelsClient, NANOS_PER_MS, NANOS_PER_SECOND, 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, OllamaQuotaExceededError, OllamaRateLimitError, OllamaRequestTooLargeError, OllamaRequestValidationError, OllamaResponseValidationError, OllamaServerError, OllamaServerVersionUnknownError, OllamaSkillInvalidError, OllamaSkillNotFoundError, OllamaStream, OllamaStreamError, OllamaTimeoutError, OllamaToolTimeoutError, OllamaToolValidationError, OllamaUnsupportedCapabilityError, OpenAIChatCompletionStream, OpenAICompatClient, OpenAICompletionStream, OpenAIResponsesStream, OpenAIResponsesStreamError, QuotaManager, RETRYABLE_STATUS_CODES, ResponsesModule, ToolCallAccumulator, ToolRegistry, addSpanEvent, applySkill, batchEmbed, calculateBackoff, canonicalToolCallSignature, checkChatContext, checkEndpointHealth, checkGenerateContext, compactConversationHistory, composeMiddleware, contextWarningMessage, createConsoleLogger, createDecision, createModelAffinityScheduler, createOllamaCloudFreeTierQuota, createTimeoutSignal, defineTool, detectModelCapabilities, embedBatchOverflowMessage, encodeImage, estimateChatRequestTokens, estimateGenerateRequestTokens, estimateMessageTokens, estimateSystemOneRequestBytes, estimateTokens, extractContextLength as extractNativeContextLength, extractParameterNumCtx, extractUsage, fetchWithBackoff, findOversizedEmbedInputs, findRunningModelContextLength, imageStringNeedsResolution, inferRuntimeMode, isKeepAliveSugar, isKnownDoneReason, isSameToolCall, listAllMcpTools, listAvailableModels, loadMcpTools, mapError, mergeToolCall, mergeToolCallArgumentsString, mergeToolCallArrays, normalizeChatStream, normalizeGenerateStream, normalizeKeepAlive, normalizeProgressStream, parseFrontmatter, parseNdjsonStream, parseSseStream, parseStructuredOutput, registerMcpTools, resolveContextLength, resolveCredentialEndpoints, resolveImageInput, sanitizeHistoryForNextTurn, setSpanError, toDataStream, toResponse, toTextStream, withRetry, withSpan, zodToJsonSchema };
|
|
6799
7473
|
//# sourceMappingURL=index.js.map
|
|
6800
7474
|
//# sourceMappingURL=index.js.map
|