@cubicecho/agent-core 2.22.0 → 2.24.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/README.md +61 -6
- package/dist/agent-loop.d.ts +3 -2
- package/dist/agent-loop.js +385 -303
- package/dist/calibration.js +8 -6
- package/dist/capabilities.d.ts +36 -0
- package/dist/capabilities.js +116 -65
- package/dist/client.d.ts +5 -4
- package/dist/client.js +62 -57
- package/dist/compaction.js +5 -9
- package/dist/continuation.d.ts +4 -4
- package/dist/continuation.js +1 -4
- package/dist/events.d.ts +16 -1
- package/dist/events.js +64 -44
- package/dist/guards.d.ts +22 -0
- package/dist/guards.js +22 -0
- package/dist/hook-events.d.ts +22 -0
- package/dist/hook-events.js +22 -0
- package/dist/hooks.d.ts +15 -16
- package/dist/hooks.js +44 -44
- package/dist/index.d.ts +6 -4
- package/dist/index.js +6 -4
- package/dist/reset.d.ts +6 -2
- package/dist/reset.js +6 -2
- package/dist/retry.d.ts +13 -7
- package/dist/retry.js +14 -7
- package/dist/run-turn.d.ts +8 -1
- package/dist/runtime.d.ts +120 -0
- package/dist/runtime.js +113 -0
- package/dist/schema-compat.d.ts +23 -0
- package/dist/schema-compat.js +76 -59
- package/dist/scope.d.ts +35 -0
- package/dist/scope.js +42 -0
- package/dist/side-task.d.ts +5 -0
- package/dist/side-task.js +24 -52
- package/dist/snapshot.d.ts +2 -2
- package/dist/snapshot.js +27 -48
- package/dist/spec.d.ts +11 -12
- package/dist/spec.js +36 -66
- package/dist/stream.d.ts +7 -0
- package/dist/stream.js +8 -1
- package/dist/thinking.js +6 -4
- package/dist/tokens.d.ts +9 -2
- package/dist/tokens.js +10 -3
- package/dist/tool-calls.d.ts +9 -0
- package/dist/tool-calls.js +11 -6
- package/dist/tool-loading.d.ts +10 -0
- package/dist/tool-loading.js +15 -7
- package/llms.txt +19 -5
- package/package.json +1 -1
package/dist/calibration.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { requestChars, toolsChars } from "./retry.js";
|
|
2
|
+
import { scoped } from "./scope.js";
|
|
3
|
+
import { CHARS_PER_TOKEN } from "./tokens.js";
|
|
2
4
|
/**
|
|
3
5
|
* How many characters a token is worth on one model, learned from what its endpoint reports.
|
|
4
6
|
*
|
|
@@ -27,7 +29,7 @@ const PLAUSIBLE = { least: 1, most: 8 };
|
|
|
27
29
|
* `resetCapabilities` takes its readings with it. Replaced rather than cleared by `resetCalibration`,
|
|
28
30
|
* since a `WeakMap` has no `clear`.
|
|
29
31
|
*/
|
|
30
|
-
|
|
32
|
+
const readings = scoped(() => new WeakMap());
|
|
31
33
|
/**
|
|
32
34
|
* The characters per token to size a request to this model with, `CHARS_PER_TOKEN` until a turn
|
|
33
35
|
* has reported one.
|
|
@@ -43,7 +45,7 @@ let readings = new WeakMap();
|
|
|
43
45
|
* @param model The name the endpoint knows the model as, as it goes in the body.
|
|
44
46
|
*/
|
|
45
47
|
export function charsPerTokenFor(supports, model) {
|
|
46
|
-
const known = readings.get(supports)?.get(model);
|
|
48
|
+
const known = readings().get(supports)?.get(model);
|
|
47
49
|
return known?.length ? Math.max(...known) : CHARS_PER_TOKEN;
|
|
48
50
|
}
|
|
49
51
|
/** Whether any message carries a part whose tokens its characters do not count. */
|
|
@@ -69,10 +71,10 @@ export function calibrate(supports, body, promptTokens) {
|
|
|
69
71
|
if (ratio < PLAUSIBLE.least || ratio > PLAUSIBLE.most) {
|
|
70
72
|
return charsPerTokenFor(supports, body.model);
|
|
71
73
|
}
|
|
72
|
-
let models = readings.get(supports);
|
|
74
|
+
let models = readings().get(supports);
|
|
73
75
|
if (!models) {
|
|
74
76
|
models = new Map();
|
|
75
|
-
readings.set(supports, models);
|
|
77
|
+
readings().set(supports, models);
|
|
76
78
|
}
|
|
77
79
|
const known = models.get(body.model) ?? [];
|
|
78
80
|
known.push(ratio);
|
|
@@ -83,5 +85,5 @@ export function calibrate(supports, body, promptTokens) {
|
|
|
83
85
|
}
|
|
84
86
|
/** Forgets every reading, so the next request is sized at `CHARS_PER_TOKEN` again. */
|
|
85
87
|
export function resetCalibration() {
|
|
86
|
-
readings
|
|
88
|
+
readings.reset();
|
|
87
89
|
}
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -118,6 +118,20 @@ export interface ModelCapabilities {
|
|
|
118
118
|
*/
|
|
119
119
|
assistantPrefill: boolean;
|
|
120
120
|
}
|
|
121
|
+
/** The name of one of a model's on/off flags: every `boolean` on `ModelCapabilities`. */
|
|
122
|
+
export type ModelFlag = {
|
|
123
|
+
[Key in keyof ModelCapabilities]-?: ModelCapabilities[Key] extends boolean ? Key : never;
|
|
124
|
+
}[keyof ModelCapabilities];
|
|
125
|
+
/**
|
|
126
|
+
* A model's on/off flags as it starts out, every one optimistic.
|
|
127
|
+
*
|
|
128
|
+
* A record over every boolean on `ModelCapabilities`, so a flag added there does not compile until
|
|
129
|
+
* it is listed here — and this is the list a snapshot is exported, imported and judged empty by,
|
|
130
|
+
* where each of those used to name the flags again and one could be missed.
|
|
131
|
+
*/
|
|
132
|
+
export declare const OPTIMISTIC_MODEL: Readonly<Record<ModelFlag, true>>;
|
|
133
|
+
/** The names of those flags, for a caller that reads or latches each in turn. */
|
|
134
|
+
export declare const MODEL_FLAGS: ModelFlag[];
|
|
121
135
|
/**
|
|
122
136
|
* What this endpoint is known not to support. The same object every time, so what `negotiate`
|
|
123
137
|
* latches off stays off.
|
|
@@ -210,6 +224,28 @@ export declare const EFFORT_LADDER: readonly ["none", "minimal", "low", "medium"
|
|
|
210
224
|
* @param asked What the config asks for. `"off"` and absent mean no effort.
|
|
211
225
|
*/
|
|
212
226
|
export declare function effortFor(refused: ModelCapabilities | undefined, asked: string | undefined): string;
|
|
227
|
+
/**
|
|
228
|
+
* The reply ceiling and the temperature as this model takes them, to spread into a request body.
|
|
229
|
+
*
|
|
230
|
+
* The reasoning models want the ceiling spelled the other way, and one that will only run at the
|
|
231
|
+
* temperature it was built with is sent none. Both are tested `=== false`, so a caller that named
|
|
232
|
+
* no model sends what one that has refused nothing is sent; see `ModelCapabilities.legacyTokenLimit`.
|
|
233
|
+
*
|
|
234
|
+
* @param refused What the model has refused, as `negotiate` hands it over. Absent is a model that
|
|
235
|
+
* has refused nothing.
|
|
236
|
+
* @param maxTokens The ceiling. Zero or less sends none and leaves it to the server: a zero sent
|
|
237
|
+
* as `max_tokens: 0` asks for an empty reply.
|
|
238
|
+
* @param temperature What to sample at, where the model takes one that was picked for it.
|
|
239
|
+
*/
|
|
240
|
+
export declare const ceilingAndTemperature: (refused: ModelCapabilities | undefined, maxTokens: number, temperature: number) => {
|
|
241
|
+
max_completion_tokens: number;
|
|
242
|
+
temperature?: number | undefined;
|
|
243
|
+
} | {
|
|
244
|
+
max_tokens: number;
|
|
245
|
+
temperature?: number | undefined;
|
|
246
|
+
} | {
|
|
247
|
+
temperature?: number | undefined;
|
|
248
|
+
};
|
|
213
249
|
/** What `negotiate` takes besides the request. All optional. */
|
|
214
250
|
export interface NegotiateOptions {
|
|
215
251
|
/**
|
package/dist/capabilities.js
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import { endpointId } from "./client.js";
|
|
2
2
|
import { errorMessage } from "./errors.js";
|
|
3
3
|
import { isGrammarError } from "./schema-compat.js";
|
|
4
|
+
import { scoped } from "./scope.js";
|
|
5
|
+
/**
|
|
6
|
+
* A model's on/off flags as it starts out, every one optimistic.
|
|
7
|
+
*
|
|
8
|
+
* A record over every boolean on `ModelCapabilities`, so a flag added there does not compile until
|
|
9
|
+
* it is listed here — and this is the list a snapshot is exported, imported and judged empty by,
|
|
10
|
+
* where each of those used to name the flags again and one could be missed.
|
|
11
|
+
*/
|
|
12
|
+
export const OPTIMISTIC_MODEL = {
|
|
13
|
+
reasoningEffort: true,
|
|
14
|
+
legacyTokenLimit: true,
|
|
15
|
+
chosenTemperature: true,
|
|
16
|
+
structuredOutput: true,
|
|
17
|
+
assistantPrefill: true,
|
|
18
|
+
};
|
|
19
|
+
/** The names of those flags, for a caller that reads or latches each in turn. */
|
|
20
|
+
export const MODEL_FLAGS = Object.keys(OPTIMISTIC_MODEL);
|
|
4
21
|
/**
|
|
5
22
|
* What each endpoint cannot do, remembered for the life of the process.
|
|
6
23
|
*
|
|
@@ -19,7 +36,7 @@ import { isGrammarError } from "./schema-compat.js";
|
|
|
19
36
|
* behind it, including the `models` map underneath, which is the level where two keys through
|
|
20
37
|
* one host are most likely to differ at all.
|
|
21
38
|
*/
|
|
22
|
-
const capabilities = new Map();
|
|
39
|
+
const capabilities = scoped(() => new Map());
|
|
23
40
|
/**
|
|
24
41
|
* What this endpoint is known not to support. The same object every time, so what `negotiate`
|
|
25
42
|
* latches off stays off.
|
|
@@ -50,30 +67,22 @@ export function capabilitiesFor(baseUrl, apiKey) {
|
|
|
50
67
|
export function modelCapabilitiesFor(supports, model) {
|
|
51
68
|
let known = supports.models.get(model);
|
|
52
69
|
if (!known) {
|
|
53
|
-
known = {
|
|
54
|
-
reasoningEffort: true,
|
|
55
|
-
legacyTokenLimit: true,
|
|
56
|
-
chosenTemperature: true,
|
|
57
|
-
refusedFields: new Set(),
|
|
58
|
-
structuredOutput: true,
|
|
59
|
-
refusedEfforts: new Set(),
|
|
60
|
-
assistantPrefill: true,
|
|
61
|
-
};
|
|
70
|
+
known = { ...OPTIMISTIC_MODEL, refusedFields: new Set(), refusedEfforts: new Set() };
|
|
62
71
|
supports.models.set(model, known);
|
|
63
72
|
}
|
|
64
73
|
return known;
|
|
65
74
|
}
|
|
66
75
|
/** Every endpoint's capabilities by `endpointId`, the live objects, for `exportCapabilities`. */
|
|
67
|
-
export const knownCapabilities = () => capabilities;
|
|
76
|
+
export const knownCapabilities = () => capabilities();
|
|
68
77
|
/**
|
|
69
78
|
* The capabilities of the endpoint with this `endpointId`, created optimistic if unseen — the way
|
|
70
79
|
* `importCapabilities` reaches an endpoint it has only a digest for.
|
|
71
80
|
*/
|
|
72
81
|
export function capabilitiesById(id) {
|
|
73
|
-
let known = capabilities.get(id);
|
|
82
|
+
let known = capabilities().get(id);
|
|
74
83
|
if (!known) {
|
|
75
84
|
known = { strictSchemas: true, usageInStream: true, models: new Map(), since: Date.now() };
|
|
76
|
-
capabilities.set(id, known);
|
|
85
|
+
capabilities().set(id, known);
|
|
77
86
|
}
|
|
78
87
|
return known;
|
|
79
88
|
}
|
|
@@ -92,11 +101,11 @@ export function capabilitiesById(id) {
|
|
|
92
101
|
*/
|
|
93
102
|
export function resetCapabilities(endpoint) {
|
|
94
103
|
if (!endpoint) {
|
|
95
|
-
const held = capabilities.size > 0;
|
|
96
|
-
capabilities.clear();
|
|
104
|
+
const held = capabilities().size > 0;
|
|
105
|
+
capabilities().clear();
|
|
97
106
|
return held;
|
|
98
107
|
}
|
|
99
|
-
return capabilities.delete(endpointId(endpoint));
|
|
108
|
+
return capabilities().delete(endpointId(endpoint));
|
|
100
109
|
}
|
|
101
110
|
/**
|
|
102
111
|
* Forgets every endpoint whose entry is older than this, so the next request finds out again.
|
|
@@ -117,10 +126,10 @@ export function resetCapabilities(endpoint) {
|
|
|
117
126
|
*/
|
|
118
127
|
export function expireCapabilities(maxAgeMs, now = Date.now()) {
|
|
119
128
|
let dropped = 0;
|
|
120
|
-
for (const [id, known] of capabilities) {
|
|
129
|
+
for (const [id, known] of capabilities()) {
|
|
121
130
|
if (now - known.since < maxAgeMs)
|
|
122
131
|
continue;
|
|
123
|
-
capabilities.delete(id);
|
|
132
|
+
capabilities().delete(id);
|
|
124
133
|
dropped++;
|
|
125
134
|
}
|
|
126
135
|
return dropped;
|
|
@@ -260,6 +269,27 @@ export function effortFor(refused, asked) {
|
|
|
260
269
|
return asked;
|
|
261
270
|
return nextEffort(refused, asked) ?? asked;
|
|
262
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* The reply ceiling and the temperature as this model takes them, to spread into a request body.
|
|
274
|
+
*
|
|
275
|
+
* The reasoning models want the ceiling spelled the other way, and one that will only run at the
|
|
276
|
+
* temperature it was built with is sent none. Both are tested `=== false`, so a caller that named
|
|
277
|
+
* no model sends what one that has refused nothing is sent; see `ModelCapabilities.legacyTokenLimit`.
|
|
278
|
+
*
|
|
279
|
+
* @param refused What the model has refused, as `negotiate` hands it over. Absent is a model that
|
|
280
|
+
* has refused nothing.
|
|
281
|
+
* @param maxTokens The ceiling. Zero or less sends none and leaves it to the server: a zero sent
|
|
282
|
+
* as `max_tokens: 0` asks for an empty reply.
|
|
283
|
+
* @param temperature What to sample at, where the model takes one that was picked for it.
|
|
284
|
+
*/
|
|
285
|
+
export const ceilingAndTemperature = (refused, maxTokens, temperature) => ({
|
|
286
|
+
...(maxTokens > 0
|
|
287
|
+
? refused?.legacyTokenLimit === false
|
|
288
|
+
? { max_completion_tokens: maxTokens }
|
|
289
|
+
: { max_tokens: maxTokens }
|
|
290
|
+
: {}),
|
|
291
|
+
...(refused?.chosenTemperature === false ? {} : { temperature }),
|
|
292
|
+
});
|
|
263
293
|
/**
|
|
264
294
|
* How to answer a refused effort *value*, or `undefined` when there is nothing new to learn or
|
|
265
295
|
* nowhere left to go — in which case the refusal is the caller's, as it was before this existed.
|
|
@@ -346,9 +376,58 @@ function unknownFields(detail) {
|
|
|
346
376
|
.split(/[.[]/)[0])
|
|
347
377
|
.filter(Boolean);
|
|
348
378
|
}
|
|
349
|
-
/**
|
|
350
|
-
const
|
|
351
|
-
|
|
379
|
+
/** The answer to a refusal that is the endpoint's: a flag still set, and a wording that clears it. */
|
|
380
|
+
const endpointAnswer = (flag, matches, notice) => ({ detail, supports }) => {
|
|
381
|
+
if (!supports[flag] || !matches(detail))
|
|
382
|
+
return undefined;
|
|
383
|
+
supports[flag] = false;
|
|
384
|
+
return notice;
|
|
385
|
+
};
|
|
386
|
+
/** The same for a refusal that is the model's, which only a request that named one can meet. */
|
|
387
|
+
const modelAnswer = (flag, matches, notice) => ({ detail, named }) => {
|
|
388
|
+
if (!named?.refused[flag] || !matches(detail))
|
|
389
|
+
return undefined;
|
|
390
|
+
named.refused[flag] = false;
|
|
391
|
+
return notice(named.name);
|
|
392
|
+
};
|
|
393
|
+
/** An effort refused by value: latched, so `effortFor` sends the next rung down. */
|
|
394
|
+
const effortStepAnswer = ({ detail, named }) => {
|
|
395
|
+
const stepped = named && planEffortStep(detail, named.refused);
|
|
396
|
+
if (!named || !stepped)
|
|
397
|
+
return undefined;
|
|
398
|
+
named.refused.refusedEfforts.add(stepped.value);
|
|
399
|
+
if (stepped.supported)
|
|
400
|
+
named.refused.supportedEfforts = stepped.supported;
|
|
401
|
+
return `${named.name} does not reason at ${stepped.value}; retrying at ${stepped.next}`;
|
|
402
|
+
};
|
|
403
|
+
/** Fields the endpoint has never heard of, where they are droppable and not refused already. */
|
|
404
|
+
const droppedFieldsAnswer = ({ detail, named, droppable }) => {
|
|
405
|
+
if (!named || droppable.size === 0)
|
|
406
|
+
return undefined;
|
|
407
|
+
const fields = unknownFields(detail).filter((field) => droppable.has(field) && !named.refused.refusedFields.has(field));
|
|
408
|
+
if (!fields.length)
|
|
409
|
+
return undefined;
|
|
410
|
+
for (const field of fields)
|
|
411
|
+
named.refused.refusedFields.add(field);
|
|
412
|
+
return `${named.name} does not take ${fields.join(", ")}; retrying without ${fields.length > 1 ? "them" : "it"}`;
|
|
413
|
+
};
|
|
414
|
+
/**
|
|
415
|
+
* Every refusal `negotiate` answers, in the order it asks. The first to answer is the only one.
|
|
416
|
+
*
|
|
417
|
+
* The order is behaviour rather than layout: one error text can satisfy two of these — a message
|
|
418
|
+
* about `reasoning_effort` is a refused field to one and a refused value to the next — and the
|
|
419
|
+
* earlier row is the reading that wins.
|
|
420
|
+
*/
|
|
421
|
+
const ANSWERS = [
|
|
422
|
+
endpointAnswer("strictSchemas", isGrammarError, "server could not build a grammar; retrying without pattern/format"),
|
|
423
|
+
endpointAnswer("usageInStream", (detail) => REJECTS_USAGE.test(detail), "server rejected stream_options; token counts unavailable"),
|
|
424
|
+
modelAnswer("reasoningEffort", rejectsEffort, (name) => `${name} does not take a reasoning effort; retrying without one`),
|
|
425
|
+
effortStepAnswer,
|
|
426
|
+
modelAnswer("legacyTokenLimit", wantsCompletionLimit, (name) => `${name} wants max_completion_tokens; retrying with the limit spelled that way`),
|
|
427
|
+
modelAnswer("chosenTemperature", refusesChosenTemperature, (name) => `${name} takes only its own temperature; retrying without ours`),
|
|
428
|
+
modelAnswer("structuredOutput", rejectsResponseFormat, (name) => `${name} does not take response_format; asking for JSON in words instead`),
|
|
429
|
+
droppedFieldsAnswer,
|
|
430
|
+
];
|
|
352
431
|
/**
|
|
353
432
|
* Sends a request, re-sending it each time the answer is this endpoint refusing something the
|
|
354
433
|
* request can do without. Returns once the endpoint has answered, or throws if the refusal is
|
|
@@ -385,16 +464,13 @@ const refusesDroppable = (detail, droppable, refused) => droppable.size > 0 &&
|
|
|
385
464
|
*/
|
|
386
465
|
export async function negotiate(supports, send, { produced = { any: false }, onNotice, model: name, droppable } = {}) {
|
|
387
466
|
const optional = new Set(droppable);
|
|
388
|
-
// The name and what it has refused, bound together because the notices below need both. They
|
|
389
|
-
// exist or are absent as one — the second is resolved from the first — but that is a fact
|
|
390
|
-
// about two locals, and narrowing one of those tells TypeScript nothing about the other.
|
|
391
467
|
const named = name === undefined ? undefined : { name, refused: modelCapabilitiesFor(supports, name) };
|
|
392
468
|
const model = named?.refused;
|
|
393
469
|
for (;;) {
|
|
394
470
|
// What this attempt was built with. `capabilitiesFor` and `modelCapabilitiesFor` hand one
|
|
395
471
|
// object per endpoint and per model to everyone on them, so a run starting alongside this
|
|
396
|
-
// one may latch a flag off while this call is in flight — and
|
|
397
|
-
// on
|
|
472
|
+
// one may latch a flag off while this call is in flight — and every answer in `ANSWERS` is
|
|
473
|
+
// guarded on its flag still being set.
|
|
398
474
|
const sent = flagsOf(supports, model);
|
|
399
475
|
try {
|
|
400
476
|
return await send(supports, produced, model);
|
|
@@ -402,47 +478,22 @@ export async function negotiate(supports, send, { produced = { any: false }, onN
|
|
|
402
478
|
catch (error) {
|
|
403
479
|
if (produced.any)
|
|
404
480
|
throw error;
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
named.refused.reasoningEffort = false;
|
|
417
|
-
onNotice?.(`${named.name} does not take a reasoning effort; retrying without one`);
|
|
481
|
+
const refusal = {
|
|
482
|
+
detail: errorMessage(error),
|
|
483
|
+
supports,
|
|
484
|
+
named,
|
|
485
|
+
droppable: optional,
|
|
486
|
+
};
|
|
487
|
+
let notice;
|
|
488
|
+
for (const answer of ANSWERS) {
|
|
489
|
+
notice = answer(refusal);
|
|
490
|
+
if (notice !== undefined)
|
|
491
|
+
break;
|
|
418
492
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
named.refused.supportedEfforts = stepped.supported;
|
|
423
|
-
onNotice?.(`${named.name} does not reason at ${stepped.value}; retrying at ${stepped.next}`);
|
|
424
|
-
}
|
|
425
|
-
else if (named?.refused.legacyTokenLimit && wantsCompletionLimit(detail)) {
|
|
426
|
-
named.refused.legacyTokenLimit = false;
|
|
427
|
-
onNotice?.(`${named.name} wants max_completion_tokens; retrying with the limit spelled that way`);
|
|
428
|
-
}
|
|
429
|
-
else if (named?.refused.chosenTemperature && refusesChosenTemperature(detail)) {
|
|
430
|
-
named.refused.chosenTemperature = false;
|
|
431
|
-
onNotice?.(`${named.name} takes only its own temperature; retrying without ours`);
|
|
432
|
-
}
|
|
433
|
-
else if (named?.refused.structuredOutput && rejectsResponseFormat(detail)) {
|
|
434
|
-
named.refused.structuredOutput = false;
|
|
435
|
-
onNotice?.(`${named.name} does not take response_format; asking for JSON in words instead`);
|
|
436
|
-
}
|
|
437
|
-
else if (named && refusesDroppable(detail, optional, named.refused)) {
|
|
438
|
-
const fields = unknownFields(detail).filter((field) => optional.has(field) && !named.refused.refusedFields.has(field));
|
|
439
|
-
for (const field of fields)
|
|
440
|
-
named.refused.refusedFields.add(field);
|
|
441
|
-
onNotice?.(`${named.name} does not take ${fields.join(", ")}; retrying without ${fields.length > 1 ? "them" : "it"}`);
|
|
442
|
-
}
|
|
443
|
-
else if (flagsOf(supports, model).every((flag, index) => flag === sent[index])) {
|
|
493
|
+
if (notice !== undefined)
|
|
494
|
+
onNotice?.(notice);
|
|
495
|
+
else if (flagsOf(supports, model).every((flag, index) => flag === sent[index]))
|
|
444
496
|
throw error;
|
|
445
|
-
}
|
|
446
497
|
// Otherwise the refusal was answered by whoever got there first, and this attempt was
|
|
447
498
|
// built before the answer existed. Two runs opening on a fresh llama.cpp box both get the
|
|
448
499
|
// grammar error; the first latches it off and re-sends, and the second used to find the
|
package/dist/client.d.ts
CHANGED
|
@@ -27,10 +27,11 @@ export interface ClientPoolOptions {
|
|
|
27
27
|
* Changes what the client pool is held to, for a process whose endpoints are not shaped like the
|
|
28
28
|
* deployments these defaults were chosen for.
|
|
29
29
|
*
|
|
30
|
-
*
|
|
31
|
-
* its size is a deployment's setting, said once at startup. A multi-tenant host
|
|
32
|
-
* per user raises `maxClients` so its tenants stop evicting each other's
|
|
33
|
-
*
|
|
30
|
+
* One setting per runtime for the same reason `configureEvents` is: the pool is one thing per
|
|
31
|
+
* runtime, and its size is a deployment's setting, said once at startup. A multi-tenant host
|
|
32
|
+
* keying on a key per user raises `maxClients` so its tenants stop evicting each other's
|
|
33
|
+
* connection pools, or gives each tenant a `createRuntime` of its own; a dev box shortens
|
|
34
|
+
* `listingMissMs` so a model it has just pulled is seen sooner.
|
|
34
35
|
*
|
|
35
36
|
* A `maxClients` below the pool's current size evicts down to it at once, least recently asked
|
|
36
37
|
* for first, as the next `getClient` would have. A shorter `listingMissMs` applies to misses
|