@cubicecho/agent-core 2.25.0 → 3.0.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 +3 -3
- package/dist/agent-loop.js +1 -1
- package/dist/calibration.d.ts +2 -2
- package/dist/calibration.js +2 -2
- package/dist/client.d.ts +1 -1
- package/dist/client.js +3 -3
- package/dist/compaction.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/guards.d.ts +17 -3
- package/dist/guards.js +25 -3
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/platform.d.ts +2 -0
- package/dist/platform.js +2 -0
- package/dist/preselect.d.ts +8 -30
- package/dist/preselect.js +10 -9
- package/dist/retry.d.ts +15 -0
- package/dist/retry.js +16 -1
- package/dist/run-turn.d.ts +2 -2
- package/dist/schema-compat.js +9 -4
- package/dist/side-task.d.ts +6 -5
- package/dist/side-task.js +12 -10
- package/dist/tokens.d.ts +4 -4
- package/dist/tokens.js +4 -4
- package/dist/tool-calls.d.ts +8 -0
- package/dist/tool-calls.js +9 -1
- package/dist/tool-loading.js +1 -21
- package/llms.txt +1 -9
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -316,7 +316,7 @@ and wrong for what a tool-using run is made of — JSON schemas and tool results
|
|
|
316
316
|
three characters a token — so a request the guard let through was refused anyway. The divisor is
|
|
317
317
|
learned instead. Every turn `runTurn` answers comes back with the endpoint's exact prompt count for a
|
|
318
318
|
body whose characters were already counted, and `calibrate` keeps that ratio per endpoint and model;
|
|
319
|
-
the next request to the same model is sized by `charsPerTokenFor`, which is
|
|
319
|
+
the next request to the same model is sized by `charsPerTokenFor`, which is four
|
|
320
320
|
until a turn has reported one. It is the highest of the model's last four readings, which is the
|
|
321
321
|
lowest token count: a count that comes out high refuses a run that would have fit, and one that
|
|
322
322
|
comes out low only costs the round trip the guard was saving. A reading from a request carrying an
|
|
@@ -349,7 +349,7 @@ large for gpt-4o ... on tokens per min"); that is ruled out and waited through a
|
|
|
349
349
|
A server still loading the model is waited for on its own clock. llama.cpp answers 503 `Loading
|
|
350
350
|
model` (type `unavailable_error`) until the weights are mapped, thirty to ninety seconds for a large
|
|
351
351
|
model from a cold cache, and a router build says the same while it swaps models; `backoffMs` would
|
|
352
|
-
give up inside fifteen. `isModelLoading` recognises it, and `runTurn` polls every
|
|
352
|
+
give up inside fifteen. `isModelLoading` recognises it, and `runTurn` polls every three seconds
|
|
353
353
|
for up to `loadingTimeoutMs` (two minutes by default, zero to turn it off) without spending
|
|
354
354
|
`maxRetries`, with one notice at the start. `runAgentLoop` reads it as `loadingTimeoutSeconds` off
|
|
355
355
|
the config. A 503 that says nothing about loading stays on the ordinary backoff.
|
|
@@ -985,7 +985,7 @@ not turn the backlog off. `resetEvents` (and `resetAll`) puts the defaults back,
|
|
|
985
985
|
is not the next one's.
|
|
986
986
|
|
|
987
987
|
`expandNames`, `carryOver`, `preselection`, `preselectInput` and `preselectSystem` take their caps
|
|
988
|
-
the same way — as a last argument
|
|
988
|
+
the same way — as a last argument, which is 12 tools a load and 16 carried unless given. A resolution
|
|
989
989
|
carries the cap it was held to, so `loadResult` tells the model the number it was actually
|
|
990
990
|
measured against rather than the module's own.
|
|
991
991
|
|
package/dist/agent-loop.js
CHANGED
|
@@ -204,7 +204,7 @@ function ownSummariser(options, notice) {
|
|
|
204
204
|
const call = taskCall(compactor, { signal, onNotice: notice }, config);
|
|
205
205
|
const write = summariser(call.endpoint, call.model, call.options);
|
|
206
206
|
// A summary that fails folds nothing, and the step goes out as it was.
|
|
207
|
-
return async (text) => (await tryAsk('compaction', () => write(text), { onNotice: notice })) ?? '';
|
|
207
|
+
return async (text) => (await tryAsk('compaction', () => write(text), { onNotice: notice, signal })) ?? '';
|
|
208
208
|
}
|
|
209
209
|
/**
|
|
210
210
|
* A step's transcript after the loop's own compaction: folded where a fold was due and its summary
|
package/dist/calibration.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type OpenAI from 'openai';
|
|
2
2
|
import type { Capabilities } from './capabilities.ts';
|
|
3
3
|
/**
|
|
4
|
-
* The characters per token to size a request to this model with,
|
|
4
|
+
* The characters per token to size a request to this model with, four until a turn
|
|
5
5
|
* has reported one.
|
|
6
6
|
*
|
|
7
7
|
* @param supports - The endpoint, as `capabilitiesFor` hands it over.
|
|
8
8
|
* @param model - The name the endpoint knows the model as, as it goes in the body.
|
|
9
9
|
* @returns The highest of the readings held for the model in the current runtime, or
|
|
10
|
-
*
|
|
10
|
+
* four where it holds none.
|
|
11
11
|
*
|
|
12
12
|
* @remarks
|
|
13
13
|
* The highest of the model's last few readings rather than their mean. The estimate guards a
|
package/dist/calibration.js
CHANGED
|
@@ -32,13 +32,13 @@ const PLAUSIBLE = { least: 1, most: 8 };
|
|
|
32
32
|
*/
|
|
33
33
|
const readings = scoped(() => new WeakMap());
|
|
34
34
|
/**
|
|
35
|
-
* The characters per token to size a request to this model with,
|
|
35
|
+
* The characters per token to size a request to this model with, four until a turn
|
|
36
36
|
* has reported one.
|
|
37
37
|
*
|
|
38
38
|
* @param supports - The endpoint, as `capabilitiesFor` hands it over.
|
|
39
39
|
* @param model - The name the endpoint knows the model as, as it goes in the body.
|
|
40
40
|
* @returns The highest of the readings held for the model in the current runtime, or
|
|
41
|
-
*
|
|
41
|
+
* four where it holds none.
|
|
42
42
|
*
|
|
43
43
|
* @remarks
|
|
44
44
|
* The highest of the model's last few readings rather than their mean. The estimate guards a
|
package/dist/client.d.ts
CHANGED
|
@@ -66,7 +66,7 @@ export declare const FIRST_TOKEN_FACTOR = 5;
|
|
|
66
66
|
*
|
|
67
67
|
* @param config - Read for `firstTokenSeconds`, and `requestTimeoutSeconds` where that is absent.
|
|
68
68
|
* @returns Milliseconds. `firstTokenSeconds` where it is given, zero or less being no limit;
|
|
69
|
-
* otherwise
|
|
69
|
+
* otherwise five request timeouts, and no limit where the request has none.
|
|
70
70
|
*/
|
|
71
71
|
export declare const firstTokenMs: (config: Pick<Endpoint, 'requestTimeoutSeconds' | 'firstTokenSeconds'>) => number | undefined;
|
|
72
72
|
/**
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
2
|
import { digestOf } from './digest.js';
|
|
3
|
-
import { isPositive } from './guards.js';
|
|
3
|
+
import { byCodeUnit, isPositive } from './guards.js';
|
|
4
4
|
import { MS_PER_SECOND } from './platform.js';
|
|
5
5
|
import { assignSettings, scoped } from './scope.js';
|
|
6
6
|
import { HttpStatus } from './wire.js';
|
|
@@ -127,7 +127,7 @@ export const FIRST_TOKEN_FACTOR = 5;
|
|
|
127
127
|
*
|
|
128
128
|
* @param config - Read for `firstTokenSeconds`, and `requestTimeoutSeconds` where that is absent.
|
|
129
129
|
* @returns Milliseconds. `firstTokenSeconds` where it is given, zero or less being no limit;
|
|
130
|
-
* otherwise
|
|
130
|
+
* otherwise five request timeouts, and no limit where the request has none.
|
|
131
131
|
*/
|
|
132
132
|
export const firstTokenMs = (config) => {
|
|
133
133
|
if (config.firstTokenSeconds === undefined) {
|
|
@@ -466,7 +466,7 @@ export async function listModels(config) {
|
|
|
466
466
|
}
|
|
467
467
|
models.push({ id: model.id, contextLength: window.contextLength });
|
|
468
468
|
}
|
|
469
|
-
models.sort((a, b) => a.id
|
|
469
|
+
models.sort((a, b) => byCodeUnit(a.id, b.id));
|
|
470
470
|
const key = endpointKey(config);
|
|
471
471
|
listings().set(key, models);
|
|
472
472
|
trainedOnly().set(key, trained);
|
package/dist/compaction.d.ts
CHANGED
|
@@ -107,7 +107,7 @@ export interface CompactionOptions {
|
|
|
107
107
|
* The divisor the default `estimate` uses — `charsPerTokenFor` the model, for a transcript
|
|
108
108
|
* weighed the way `runTurn` sizes its requests. Ignored beside an `estimate` of the caller's own.
|
|
109
109
|
*
|
|
110
|
-
* @defaultValue `
|
|
110
|
+
* @defaultValue `4`
|
|
111
111
|
*/
|
|
112
112
|
charsPerToken?: number;
|
|
113
113
|
/**
|
package/dist/config.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export interface Endpoint {
|
|
|
36
36
|
/**
|
|
37
37
|
* How long a streamed turn may wait for its first chunk. Zero or less is no limit.
|
|
38
38
|
*
|
|
39
|
-
* @defaultValue
|
|
39
|
+
* @defaultValue Five times `requestTimeoutSeconds`
|
|
40
40
|
*
|
|
41
41
|
* @remarks
|
|
42
42
|
* Its own number because the first wait is prefill, and on a local server prefill of a long
|
package/dist/guards.d.ts
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A leaf with no imports, because the modules that need these — a browser-safe `spec`, the schema
|
|
5
5
|
* walk, the tool-call reader, the snapshot importer — otherwise share nothing, and each had grown
|
|
6
|
-
* its own copy. `getOrCreate` and `
|
|
7
|
-
* are not checks: every cache in the package fills itself on a miss, and each had
|
|
8
|
-
* out
|
|
6
|
+
* its own copy. `getOrCreate`, `counted` and `byCodeUnit` are here for the same reason and are the
|
|
7
|
+
* three things that are not checks: every cache in the package fills itself on a miss, and each had
|
|
8
|
+
* written that out; a notice that counts something is written in more than one module; and so is a
|
|
9
|
+
* sort whose order must not depend on the host.
|
|
9
10
|
*/
|
|
10
11
|
/**
|
|
11
12
|
* Whether a value is a plain keyed object: not null, and not an array.
|
|
@@ -50,4 +51,17 @@ export declare function getOrCreate<K, V>(map: Lookup<K, V>, key: K, create: ()
|
|
|
50
51
|
* @returns The phrase. Only a count of exactly one is singular: zero reads `0 tools`.
|
|
51
52
|
*/
|
|
52
53
|
export declare const counted: (count: number, noun: string) => string;
|
|
54
|
+
/**
|
|
55
|
+
* Orders two strings by code unit, which is the same on every host.
|
|
56
|
+
*
|
|
57
|
+
* @param a - The string on the left.
|
|
58
|
+
* @param b - The string on the right.
|
|
59
|
+
* @returns Negative where `a` sorts first, positive where `b` does, zero where they are the same.
|
|
60
|
+
*
|
|
61
|
+
* @remarks
|
|
62
|
+
* Rather than `localeCompare`, whose answer depends on the host's locale. An order the package
|
|
63
|
+
* relies on — the tool array a prompt cache matches, a tie between two ranked tools, a listing a
|
|
64
|
+
* host may key on — has to come out the same wherever it runs.
|
|
65
|
+
*/
|
|
66
|
+
export declare function byCodeUnit(a: string, b: string): number;
|
|
53
67
|
export {};
|
package/dist/guards.js
CHANGED
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A leaf with no imports, because the modules that need these — a browser-safe `spec`, the schema
|
|
5
5
|
* walk, the tool-call reader, the snapshot importer — otherwise share nothing, and each had grown
|
|
6
|
-
* its own copy. `getOrCreate` and `
|
|
7
|
-
* are not checks: every cache in the package fills itself on a miss, and each had
|
|
8
|
-
* out
|
|
6
|
+
* its own copy. `getOrCreate`, `counted` and `byCodeUnit` are here for the same reason and are the
|
|
7
|
+
* three things that are not checks: every cache in the package fills itself on a miss, and each had
|
|
8
|
+
* written that out; a notice that counts something is written in more than one module; and so is a
|
|
9
|
+
* sort whose order must not depend on the host.
|
|
9
10
|
*/
|
|
10
11
|
/**
|
|
11
12
|
* Whether a value is a plain keyed object: not null, and not an array.
|
|
@@ -53,3 +54,24 @@ export function getOrCreate(map, key, create) {
|
|
|
53
54
|
* @returns The phrase. Only a count of exactly one is singular: zero reads `0 tools`.
|
|
54
55
|
*/
|
|
55
56
|
export const counted = (count, noun) => `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
57
|
+
/**
|
|
58
|
+
* Orders two strings by code unit, which is the same on every host.
|
|
59
|
+
*
|
|
60
|
+
* @param a - The string on the left.
|
|
61
|
+
* @param b - The string on the right.
|
|
62
|
+
* @returns Negative where `a` sorts first, positive where `b` does, zero where they are the same.
|
|
63
|
+
*
|
|
64
|
+
* @remarks
|
|
65
|
+
* Rather than `localeCompare`, whose answer depends on the host's locale. An order the package
|
|
66
|
+
* relies on — the tool array a prompt cache matches, a tie between two ranked tools, a listing a
|
|
67
|
+
* host may key on — has to come out the same wherever it runs.
|
|
68
|
+
*/
|
|
69
|
+
export function byCodeUnit(a, b) {
|
|
70
|
+
if (a < b) {
|
|
71
|
+
return -1;
|
|
72
|
+
}
|
|
73
|
+
if (a > b) {
|
|
74
|
+
return 1;
|
|
75
|
+
}
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { type AgentLoopHooks, type AgentLoopOptions, type AgentLoopRequest, type
|
|
|
13
13
|
export { calibrate, charsPerTokenFor, resetCalibration } from './calibration.ts';
|
|
14
14
|
export { type Capabilities, capabilitiesFor, EFFORT_LADDER, EFFORT_NONE, EFFORT_OFF, effortFor, expireCapabilities, type ModelCapabilities, modelCapabilitiesFor, type NegotiateOptions, negotiate, resetCapabilities, } from './capabilities.ts';
|
|
15
15
|
export type { CatalogServer } from './catalog.ts';
|
|
16
|
-
export { type ClientPoolOptions, configureClients, contextLimitFor, endpointId, endpointKey,
|
|
16
|
+
export { type ClientPoolOptions, configureClients, contextLimitFor, endpointId, endpointKey, firstTokenMs, getClient, listModels, type ModelInfo, NO_KEY, resetClients, resolveApiKey, servedWindow, timeoutMs, } from './client.ts';
|
|
17
17
|
export { applyCompaction, COMPACT_AT, type CompactionOptions, type CompactionPlan, type CompactionRecord, type CompactionRunOptions, compactTranscript, KEEP_RATIO, type PruneOptions, planCompaction, pruneToolResults, requestIndex, runCompaction, SUMMARY_LEAD, SUMMARY_PROMPT, summariser, summaryInput, } from './compaction.ts';
|
|
18
18
|
export { type AgentConfig, type Endpoint, type ModelParams, type RetryPolicy, ToolDiscovery, type ToolPolicy, } from './config.ts';
|
|
19
19
|
export { type ContinueTurnOptions, continueTurn, isContinuable, } from './continuation.ts';
|
|
@@ -22,10 +22,10 @@ export { configureEvents, type EventBusOptions, emit, endRun, fold, history, typ
|
|
|
22
22
|
export { HOOK_EVENTS, HookEvent, INJECT_EVENTS } from './hook-events.ts';
|
|
23
23
|
export { assembleContext, configureHooks, consult, type Gathered, gather, HOOK_CONTEXT_TOKENS, HOOK_PREFACE, type HookContext, type HookMessage, type HookNote, type HookOptions, type HookOutcome, type HookRunner, notify, resetHooks, turnIndex, turnMessages, UNTRUSTED_PREFACE, untrusted, withContext, } from './hooks.ts';
|
|
24
24
|
export { estimateFrom, type LedgerEntry, type LedgerEstimateOptions, type LedgerRequest, rebaseLedger, recordRequest, type TokenLedger, tokensBetween, } from './ledger.ts';
|
|
25
|
-
export {
|
|
25
|
+
export { type KeywordPreselection, type KeywordPreselectOptions, PRESELECT_SCHEMA, PRESELECT_SYSTEM, preselect, preselectByKeywords, preselectInput, preselection, preselectSystem, type ToolMatch, } from './preselect.ts';
|
|
26
26
|
export { buildBody } from './request-body.ts';
|
|
27
27
|
export { resetAll } from './reset.ts';
|
|
28
|
-
export { backoffMs, ContextOverflow, EndpointSilent, isModelLoading, isOverflow, isTransient,
|
|
28
|
+
export { backoffMs, ContextOverflow, EndpointSilent, isModelLoading, isOverflow, isTransient, SMALLEST_LIKELY_WINDOW, sleep, } from './retry.ts';
|
|
29
29
|
export { preview } from './run-calls.ts';
|
|
30
30
|
export { type RunTurnOptions, runTurn } from './run-turn.ts';
|
|
31
31
|
export { createRuntime, defaultRuntime, type Runtime, type RuntimeOptions } from './runtime.ts';
|
|
@@ -35,7 +35,7 @@ export { CAPABILITY_SNAPSHOT_VERSION, type CapabilitySnapshot, type EndpointSnap
|
|
|
35
35
|
export { AGENT_SPEC, AGENT_SPEC_VERSION, AGENT_TASKS, type AgentHook, type AgentSpec, type EndpointSpec, type ExportSpecOptions, exportSpec, type ModelSpec, type ParsedSpec, type ParseSpecOptions, type PromptPart, parseSpec, REF_FILE, REF_URL, RESOLVED_DEFAULTS, type ResolvedAgent, type ResolvedTask, type RetrySpec, resolveAgentSpec, SPEC_EVENTS, type SpecBundle, type SpecServer, type TaskSpec, type ToolsSpec, } from './spec.ts';
|
|
36
36
|
export { CacheBreakReason, type Produced, type StreamTurnOptions, streamTurn, type Turn, type TurnUsage, } from './stream.ts';
|
|
37
37
|
export { ALL_FENCES, DEFAULT_FENCES, type Fence, FenceSplitter, type FenceSplitterOptions, SPLIT_OUTPUT, SPLIT_REASONING, type Split, stripThinking, THINK_FENCE, } from './thinking.ts';
|
|
38
|
-
export {
|
|
38
|
+
export { type ContextBreakdown, type ContextBreakdownOptions, compact, contextChars, contextTokens, estimateTokens, messageTokens, requestChars, requestTokens, type TokenEstimateOptions, toolsChars, } from './tokens.ts';
|
|
39
39
|
export { ARGUMENTS_MALFORMED, ARGUMENTS_TRUNCATED, parseToolArguments, recoverToolCalls, ToolArgumentsError, type ToolCall, type ToolCallOutcome, type ToolCallRequest, type ToolCallResult, } from './tool-calls.ts';
|
|
40
|
-
export { CALL_TOOL, carryOver, catalogList, catalogPrompt, expandNames, holdsDefinitions, inCatalog, LOAD_TOOLS, LOAD_TOOLS_DEFINITION, loadedTools, loadResult,
|
|
40
|
+
export { CALL_TOOL, carryOver, catalogList, catalogPrompt, expandNames, holdsDefinitions, inCatalog, LOAD_TOOLS, LOAD_TOOLS_DEFINITION, loadedTools, loadResult, orderTools, PROXY_TOOLS, proxiedCall, proxyCatalogPrompt, proxyLoadResult, requestedNames, shownCall, type ToolOrder, } from './tool-loading.ts';
|
|
41
41
|
export { FinishReason, FUNCTION_TOOL, JSON_SCHEMA_FORMAT, PartType, Role, SchemaType } from './wire.ts';
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
export { PRESELECT_APPEND, PRESELECT_EXCLUSIVE, runAgentLoop, } from './agent-loop.js';
|
|
13
13
|
export { calibrate, charsPerTokenFor, resetCalibration } from './calibration.js';
|
|
14
14
|
export { capabilitiesFor, EFFORT_LADDER, EFFORT_NONE, EFFORT_OFF, effortFor, expireCapabilities, modelCapabilitiesFor, negotiate, resetCapabilities, } from './capabilities.js';
|
|
15
|
-
export { configureClients, contextLimitFor, endpointId, endpointKey,
|
|
15
|
+
export { configureClients, contextLimitFor, endpointId, endpointKey, firstTokenMs, getClient, listModels, NO_KEY, resetClients, resolveApiKey, servedWindow, timeoutMs, } from './client.js';
|
|
16
16
|
export { applyCompaction, COMPACT_AT, compactTranscript, KEEP_RATIO, planCompaction, pruneToolResults, requestIndex, runCompaction, SUMMARY_LEAD, SUMMARY_PROMPT, summariser, summaryInput, } from './compaction.js';
|
|
17
17
|
export { ToolDiscovery, } from './config.js';
|
|
18
18
|
export { continueTurn, isContinuable, } from './continuation.js';
|
|
@@ -21,10 +21,10 @@ export { configureEvents, emit, endRun, fold, history, RunEventKind, RunOutcome,
|
|
|
21
21
|
export { HOOK_EVENTS, HookEvent, INJECT_EVENTS } from './hook-events.js';
|
|
22
22
|
export { assembleContext, configureHooks, consult, gather, HOOK_CONTEXT_TOKENS, HOOK_PREFACE, notify, resetHooks, turnIndex, turnMessages, UNTRUSTED_PREFACE, untrusted, withContext, } from './hooks.js';
|
|
23
23
|
export { estimateFrom, rebaseLedger, recordRequest, tokensBetween, } from './ledger.js';
|
|
24
|
-
export {
|
|
24
|
+
export { PRESELECT_SCHEMA, PRESELECT_SYSTEM, preselect, preselectByKeywords, preselectInput, preselection, preselectSystem, } from './preselect.js';
|
|
25
25
|
export { buildBody } from './request-body.js';
|
|
26
26
|
export { resetAll } from './reset.js';
|
|
27
|
-
export { backoffMs, ContextOverflow, EndpointSilent, isModelLoading, isOverflow, isTransient,
|
|
27
|
+
export { backoffMs, ContextOverflow, EndpointSilent, isModelLoading, isOverflow, isTransient, SMALLEST_LIKELY_WINDOW, sleep, } from './retry.js';
|
|
28
28
|
export { preview } from './run-calls.js';
|
|
29
29
|
export { runTurn } from './run-turn.js';
|
|
30
30
|
export { createRuntime, defaultRuntime } from './runtime.js';
|
|
@@ -34,7 +34,7 @@ export { CAPABILITY_SNAPSHOT_VERSION, exportCapabilities, importCapabilities, }
|
|
|
34
34
|
export { AGENT_SPEC, AGENT_SPEC_VERSION, AGENT_TASKS, exportSpec, parseSpec, REF_FILE, REF_URL, RESOLVED_DEFAULTS, resolveAgentSpec, SPEC_EVENTS, } from './spec.js';
|
|
35
35
|
export { CacheBreakReason, streamTurn, } from './stream.js';
|
|
36
36
|
export { ALL_FENCES, DEFAULT_FENCES, FenceSplitter, SPLIT_OUTPUT, SPLIT_REASONING, stripThinking, THINK_FENCE, } from './thinking.js';
|
|
37
|
-
export {
|
|
37
|
+
export { compact, contextChars, contextTokens, estimateTokens, messageTokens, requestChars, requestTokens, toolsChars, } from './tokens.js';
|
|
38
38
|
export { ARGUMENTS_MALFORMED, ARGUMENTS_TRUNCATED, parseToolArguments, recoverToolCalls, ToolArgumentsError, } from './tool-calls.js';
|
|
39
|
-
export { CALL_TOOL, carryOver, catalogList, catalogPrompt, expandNames, holdsDefinitions, inCatalog, LOAD_TOOLS, LOAD_TOOLS_DEFINITION, loadedTools, loadResult,
|
|
39
|
+
export { CALL_TOOL, carryOver, catalogList, catalogPrompt, expandNames, holdsDefinitions, inCatalog, LOAD_TOOLS, LOAD_TOOLS_DEFINITION, loadedTools, loadResult, orderTools, PROXY_TOOLS, proxiedCall, proxyCatalogPrompt, proxyLoadResult, requestedNames, shownCall, } from './tool-loading.js';
|
|
40
40
|
export { FinishReason, FUNCTION_TOOL, JSON_SCHEMA_FORMAT, PartType, Role, SchemaType } from './wire.js';
|
package/dist/platform.d.ts
CHANGED
|
@@ -11,3 +11,5 @@ export declare const MS_PER_SECOND = 1000;
|
|
|
11
11
|
export declare const MS_PER_MINUTE: number;
|
|
12
12
|
/** The event an `AbortSignal` fires when it aborts. */
|
|
13
13
|
export declare const ABORT_EVENT = "abort";
|
|
14
|
+
/** The name an error carries when it is what an aborted `AbortSignal` gave as its reason. */
|
|
15
|
+
export declare const ABORT_ERROR = "AbortError";
|
package/dist/platform.js
CHANGED
|
@@ -11,3 +11,5 @@ export const MS_PER_SECOND = 1000;
|
|
|
11
11
|
export const MS_PER_MINUTE = 60 * MS_PER_SECOND;
|
|
12
12
|
/** The event an `AbortSignal` fires when it aborts. */
|
|
13
13
|
export const ABORT_EVENT = 'abort';
|
|
14
|
+
/** The name an error carries when it is what an aborted `AbortSignal` gave as its reason. */
|
|
15
|
+
export const ABORT_ERROR = 'AbortError';
|
package/dist/preselect.d.ts
CHANGED
|
@@ -66,29 +66,6 @@ export declare const preselectInput: (catalog: CatalogServer[], prompt: string,
|
|
|
66
66
|
* more than `maxPerLoad`. Empty where nothing resolved.
|
|
67
67
|
*/
|
|
68
68
|
export declare function preselection(names: unknown, catalog: CatalogServer[], maxPerLoad?: number): string[];
|
|
69
|
-
/**
|
|
70
|
-
* The least a best match may score and still be acted on without a model.
|
|
71
|
-
*
|
|
72
|
-
* @remarks
|
|
73
|
-
* A BM25 score, so it is read against the shape of the corpus rather than as a percentage: a
|
|
74
|
-
* query term carried by half the catalogue is worth about 0.7, and one carried by a single tool
|
|
75
|
-
* about 3. One at this floor is therefore "something more distinctive than a word every other
|
|
76
|
-
* tool uses", which is the weakest evidence worth skipping a round trip on.
|
|
77
|
-
*
|
|
78
|
-
* A term is distinctive only against other terms, so a catalogue of three or four tools rarely
|
|
79
|
-
* clears it. That is the right answer rather than a gap: a catalogue that small is not costing
|
|
80
|
-
* enough tokens to be worth choosing from in the first place.
|
|
81
|
-
*/
|
|
82
|
-
export declare const KEYWORD_MIN_SCORE = 1;
|
|
83
|
-
/**
|
|
84
|
-
* How far the best unpicked tool must fall below the last picked one for the cut to count clean.
|
|
85
|
-
*
|
|
86
|
-
* @remarks
|
|
87
|
-
* Half. The cap is the only reason a hit is dropped, so a hit just underneath it scoring nearly
|
|
88
|
-
* as much as one just above means the ranking chose arbitrarily, which is exactly the case a
|
|
89
|
-
* model should be spent on.
|
|
90
|
-
*/
|
|
91
|
-
export declare const KEYWORD_DROPOFF = 0.5;
|
|
92
69
|
/** One tool's score against a request. */
|
|
93
70
|
export interface ToolMatch {
|
|
94
71
|
name: string;
|
|
@@ -109,19 +86,19 @@ export interface KeywordPreselectOptions {
|
|
|
109
86
|
/**
|
|
110
87
|
* The most to pick. The same cap the model is held to.
|
|
111
88
|
*
|
|
112
|
-
* @defaultValue `
|
|
89
|
+
* @defaultValue `12`
|
|
113
90
|
*/
|
|
114
91
|
maxPerLoad?: number;
|
|
115
92
|
/**
|
|
116
93
|
* The floor under a confident best match.
|
|
117
94
|
*
|
|
118
|
-
* @defaultValue `
|
|
95
|
+
* @defaultValue `1`
|
|
119
96
|
*/
|
|
120
97
|
minScore?: number;
|
|
121
98
|
/**
|
|
122
99
|
* The gap a confident cut needs.
|
|
123
100
|
*
|
|
124
|
-
* @defaultValue `
|
|
101
|
+
* @defaultValue `0.5`
|
|
125
102
|
*/
|
|
126
103
|
dropoff?: number;
|
|
127
104
|
/**
|
|
@@ -184,12 +161,13 @@ export interface PreselectOptions {
|
|
|
184
161
|
/**
|
|
185
162
|
* The most tools the choice may name.
|
|
186
163
|
*
|
|
187
|
-
* @defaultValue `
|
|
164
|
+
* @defaultValue `12`
|
|
188
165
|
*/
|
|
189
166
|
maxPerLoad?: number;
|
|
190
167
|
/**
|
|
191
168
|
* Try `preselectByKeywords` first and spend the model only on what it cannot settle. `true`
|
|
192
|
-
* takes its defaults; an object tunes the thresholds.
|
|
169
|
+
* takes its defaults; an object tunes the thresholds. Its own `maxPerLoad` can ask the words
|
|
170
|
+
* for fewer than `maxPerLoad` above, never for more. An empty `model` still means no
|
|
193
171
|
* preselection at all, words included — that is what `toolSelectModel: ""` asks for.
|
|
194
172
|
*/
|
|
195
173
|
keywords?: boolean | KeywordPreselectOptions;
|
|
@@ -204,8 +182,8 @@ export interface PreselectOptions {
|
|
|
204
182
|
* @param prompt - The request being planned for. Only its head is read; see `preselectInput`.
|
|
205
183
|
* @param [options] - Cancellation, notices, the reply ceiling, the temperature and reasoning effort
|
|
206
184
|
* as `ask` reads them, the cap the choice is held to, and whether to try the words first.
|
|
207
|
-
* @returns Names as the catalogue spells them
|
|
208
|
-
* tools, the call failed, or its reply named nothing in the catalogue.
|
|
185
|
+
* @returns Names as the catalogue spells them, never more than `maxPerLoad`. Empty where `model` is
|
|
186
|
+
* empty, the catalogue has no tools, the call failed, or its reply named nothing in the catalogue.
|
|
209
187
|
*
|
|
210
188
|
* @remarks
|
|
211
189
|
* On-demand loading otherwise spends a round trip on reading the catalogue and calling
|
package/dist/preselect.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { counted, isRecord } from './guards.js';
|
|
1
|
+
import { byCodeUnit, counted, isRecord } from './guards.js';
|
|
2
2
|
import { askJson, tryAsk } from './side-task.js';
|
|
3
3
|
import { catalogList, expandNames, MAX_PER_LOAD } from './tool-loading.js';
|
|
4
4
|
import { SchemaType } from './wire.js';
|
|
@@ -112,7 +112,7 @@ const IDF_SMOOTHING = 0.5;
|
|
|
112
112
|
* clears it. That is the right answer rather than a gap: a catalogue that small is not costing
|
|
113
113
|
* enough tokens to be worth choosing from in the first place.
|
|
114
114
|
*/
|
|
115
|
-
|
|
115
|
+
const KEYWORD_MIN_SCORE = 1;
|
|
116
116
|
/**
|
|
117
117
|
* How far the best unpicked tool must fall below the last picked one for the cut to count clean.
|
|
118
118
|
*
|
|
@@ -121,7 +121,7 @@ export const KEYWORD_MIN_SCORE = 1;
|
|
|
121
121
|
* as much as one just above means the ranking chose arbitrarily, which is exactly the case a
|
|
122
122
|
* model should be spent on.
|
|
123
123
|
*/
|
|
124
|
-
|
|
124
|
+
const KEYWORD_DROPOFF = 0.5;
|
|
125
125
|
/** The longest word `terms` leaves its trailing `s` on: `bus` and `was` are not plurals. */
|
|
126
126
|
const SHORTEST_PLURAL = 3;
|
|
127
127
|
/** What the preselector's reply schema is called on the wire. */
|
|
@@ -230,7 +230,7 @@ export function preselectByKeywords(catalog, prompt, { maxPerLoad = MAX_PER_LOAD
|
|
|
230
230
|
}
|
|
231
231
|
// Ties break on the name, not on where the tool sat in the catalogue, so reconnecting a
|
|
232
232
|
// server in a different order does not change what a run opens with.
|
|
233
|
-
ranked.sort((a, b) => b.score - a.score || a.name
|
|
233
|
+
ranked.sort((a, b) => b.score - a.score || byCodeUnit(a.name, b.name));
|
|
234
234
|
if (!ranked.length) {
|
|
235
235
|
return empty;
|
|
236
236
|
}
|
|
@@ -249,8 +249,8 @@ export function preselectByKeywords(catalog, prompt, { maxPerLoad = MAX_PER_LOAD
|
|
|
249
249
|
* @param prompt - The request being planned for. Only its head is read; see `preselectInput`.
|
|
250
250
|
* @param [options] - Cancellation, notices, the reply ceiling, the temperature and reasoning effort
|
|
251
251
|
* as `ask` reads them, the cap the choice is held to, and whether to try the words first.
|
|
252
|
-
* @returns Names as the catalogue spells them
|
|
253
|
-
* tools, the call failed, or its reply named nothing in the catalogue.
|
|
252
|
+
* @returns Names as the catalogue spells them, never more than `maxPerLoad`. Empty where `model` is
|
|
253
|
+
* empty, the catalogue has no tools, the call failed, or its reply named nothing in the catalogue.
|
|
254
254
|
*
|
|
255
255
|
* @remarks
|
|
256
256
|
* On-demand loading otherwise spends a round trip on reading the catalogue and calling
|
|
@@ -269,9 +269,10 @@ export async function preselect(config, model, catalog, prompt, { signal, onNoti
|
|
|
269
269
|
return [];
|
|
270
270
|
}
|
|
271
271
|
if (keywords) {
|
|
272
|
+
const tuned = keywords === true ? {} : keywords;
|
|
272
273
|
const guess = preselectByKeywords(catalog, prompt, {
|
|
273
|
-
|
|
274
|
-
|
|
274
|
+
...tuned,
|
|
275
|
+
maxPerLoad: Math.min(maxPerLoad, tuned.maxPerLoad ?? maxPerLoad),
|
|
275
276
|
});
|
|
276
277
|
if (guess.confident) {
|
|
277
278
|
onNotice?.(`chose ${counted(guess.names.length, 'tool')} by name`);
|
|
@@ -285,6 +286,6 @@ export async function preselect(config, model, catalog, prompt, { signal, onNoti
|
|
|
285
286
|
reasoningEffort,
|
|
286
287
|
signal,
|
|
287
288
|
onNotice,
|
|
288
|
-
}), { onNotice });
|
|
289
|
+
}), { onNotice, signal });
|
|
289
290
|
return preselection(reply, catalog, maxPerLoad);
|
|
290
291
|
}
|
package/dist/retry.d.ts
CHANGED
|
@@ -82,6 +82,21 @@ export declare function isTransient(error: unknown): boolean;
|
|
|
82
82
|
* latches off whatever the re-send left out.
|
|
83
83
|
*/
|
|
84
84
|
export declare const refusesRequest: (error: unknown) => boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Whether a failure is the caller cancelling, rather than anything going wrong.
|
|
87
|
+
*
|
|
88
|
+
* @param error - The rejection, as caught.
|
|
89
|
+
* @returns `true` for the SDK's `APIUserAbortError` and for any error named `AbortError`, which is
|
|
90
|
+
* what an aborted signal gives as its reason; `false` for everything else, a timeout included.
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* An abort reaches a catch in two shapes. The SDK raises its own class when a request in flight is
|
|
94
|
+
* cancelled, and `sleep` rejects with the signal's reason when the cancel lands between attempts.
|
|
95
|
+
* A catch that knew only the first reported the second as a failure. A signal aborted with a
|
|
96
|
+
* reason of the caller's own making is not recognised, since only the error is looked at: a
|
|
97
|
+
* catch that holds the signal asks it as well, as `tryAsk` does.
|
|
98
|
+
*/
|
|
99
|
+
export declare const isAbort: (error: unknown) => boolean;
|
|
85
100
|
/**
|
|
86
101
|
* Whether a failure is a local server still loading the model, rather than one failing to serve.
|
|
87
102
|
*
|
package/dist/retry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import { ABORT_EVENT } from './platform.js';
|
|
2
|
+
import { ABORT_ERROR, ABORT_EVENT } from './platform.js';
|
|
3
3
|
import { HttpStatus } from './wire.js';
|
|
4
4
|
/**
|
|
5
5
|
* Everything about a request failing that is not about what the request said.
|
|
@@ -126,6 +126,21 @@ export function isTransient(error) {
|
|
|
126
126
|
*/
|
|
127
127
|
export const refusesRequest = (error) => error instanceof OpenAI.APIError &&
|
|
128
128
|
(error.status === HttpStatus.BadRequest || error.status === HttpStatus.UnprocessableEntity);
|
|
129
|
+
/**
|
|
130
|
+
* Whether a failure is the caller cancelling, rather than anything going wrong.
|
|
131
|
+
*
|
|
132
|
+
* @param error - The rejection, as caught.
|
|
133
|
+
* @returns `true` for the SDK's `APIUserAbortError` and for any error named `AbortError`, which is
|
|
134
|
+
* what an aborted signal gives as its reason; `false` for everything else, a timeout included.
|
|
135
|
+
*
|
|
136
|
+
* @remarks
|
|
137
|
+
* An abort reaches a catch in two shapes. The SDK raises its own class when a request in flight is
|
|
138
|
+
* cancelled, and `sleep` rejects with the signal's reason when the cancel lands between attempts.
|
|
139
|
+
* A catch that knew only the first reported the second as a failure. A signal aborted with a
|
|
140
|
+
* reason of the caller's own making is not recognised, since only the error is looked at: a
|
|
141
|
+
* catch that holds the signal asks it as well, as `tryAsk` does.
|
|
142
|
+
*/
|
|
143
|
+
export const isAbort = (error) => error instanceof OpenAI.APIUserAbortError || (error instanceof Error && error.name === ABORT_ERROR);
|
|
129
144
|
/** The error type llama.cpp gives a request it cannot serve yet. */
|
|
130
145
|
const UNAVAILABLE_ERROR = 'unavailable_error';
|
|
131
146
|
/**
|
package/dist/run-turn.d.ts
CHANGED
|
@@ -75,10 +75,10 @@ export interface RunTurnOptions extends Omit<StreamTurnOptions, 'produced'> {
|
|
|
75
75
|
* How long to wait on a server answering that the model is still loading; zero gives up on the
|
|
76
76
|
* first such answer like any other 503.
|
|
77
77
|
*
|
|
78
|
-
* @defaultValue `
|
|
78
|
+
* @defaultValue `120000`
|
|
79
79
|
*
|
|
80
80
|
* @remarks
|
|
81
|
-
* Polled every
|
|
81
|
+
* Polled every three seconds without spending `maxRetries`, and announced once rather than
|
|
82
82
|
* per poll. A consumer that starts alongside its llama.cpp, or asks a router for a model it
|
|
83
83
|
* has to swap in, meets this on its first request every time.
|
|
84
84
|
*/
|
package/dist/schema-compat.js
CHANGED
|
@@ -381,7 +381,13 @@ function collectRefs(node, into) {
|
|
|
381
381
|
* outside it still refers in.
|
|
382
382
|
*/
|
|
383
383
|
function pruneDefs(schema) {
|
|
384
|
-
const pools =
|
|
384
|
+
const pools = [];
|
|
385
|
+
for (const key of POOL_KEYS) {
|
|
386
|
+
const pool = schema[key];
|
|
387
|
+
if (isRecord(pool)) {
|
|
388
|
+
pools.push([key, pool]);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
385
391
|
if (!pools.length) {
|
|
386
392
|
return schema;
|
|
387
393
|
}
|
|
@@ -411,19 +417,18 @@ function pruneDefs(schema) {
|
|
|
411
417
|
// The pools themselves are not roots: a definition is reached from the schema body, or by
|
|
412
418
|
// another definition that was, or not at all.
|
|
413
419
|
const body = { ...schema };
|
|
414
|
-
for (const key of pools) {
|
|
420
|
+
for (const [key] of pools) {
|
|
415
421
|
delete body[key];
|
|
416
422
|
}
|
|
417
423
|
visit(body);
|
|
418
424
|
let pruned = schema;
|
|
419
|
-
for (const key of pools) {
|
|
425
|
+
for (const [key, pool] of pools) {
|
|
420
426
|
const names = live[key];
|
|
421
427
|
if (!names?.size) {
|
|
422
428
|
const { [key]: _unreached, ...rest } = pruned;
|
|
423
429
|
pruned = rest;
|
|
424
430
|
continue;
|
|
425
431
|
}
|
|
426
|
-
const pool = schema[key];
|
|
427
432
|
if (names.size === Object.keys(pool).length) {
|
|
428
433
|
continue;
|
|
429
434
|
}
|
package/dist/side-task.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
1
|
+
import type OpenAI from 'openai';
|
|
2
2
|
import { type OnNotice } from './capabilities.ts';
|
|
3
3
|
import type { Endpoint, EndpointIdentity } from './config.ts';
|
|
4
4
|
/**
|
|
@@ -154,15 +154,16 @@ export declare function askJson<T>(config: Endpoint, model: string, system: stri
|
|
|
154
154
|
*
|
|
155
155
|
* @param label - Names the task in the notice when it fails.
|
|
156
156
|
* @param run - The call to attempt. Anything it throws becomes `undefined`, an abort excepted.
|
|
157
|
-
* @param [options] - `onNotice`, told what was given up on
|
|
158
|
-
*
|
|
159
|
-
*
|
|
157
|
+
* @param [options] - `onNotice`, told what was given up on, and the `signal` that `run` is
|
|
158
|
+
* cancelled by.
|
|
159
|
+
* @returns What `run` resolved with, or `undefined` when it threw. Whatever it threw once the
|
|
160
|
+
* signal had aborted is thrown on and raises no notice, as is an error that is itself an abort.
|
|
160
161
|
*
|
|
161
162
|
* @remarks
|
|
162
163
|
* A side task is never worth failing the work it supports. Callers that can carry on without
|
|
163
164
|
* an answer use this and get `undefined` instead of an exception.
|
|
164
165
|
*/
|
|
165
|
-
export declare function tryAsk<T>(label: string, run: () => Promise<T>, { onNotice }?: Pick<SideTaskOptions, 'onNotice'>): Promise<T | undefined>;
|
|
166
|
+
export declare function tryAsk<T>(label: string, run: () => Promise<T>, { onNotice, signal }?: Pick<SideTaskOptions, 'onNotice' | 'signal'>): Promise<T | undefined>;
|
|
166
167
|
/**
|
|
167
168
|
* The first array or object in a model's reply, parsed, whatever prose or fence surrounds it.
|
|
168
169
|
*
|
package/dist/side-task.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import OpenAI from 'openai';
|
|
2
1
|
import { capabilitiesFor, ceilingAndTemperature, EFFORT_NONE, effortFor, modelCapabilitiesFor, negotiate, } from './capabilities.js';
|
|
3
2
|
import { endpointId, getClient, modelKey, resolveApiKey, sameUrl } from './client.js';
|
|
4
3
|
import { errorMessage } from './errors.js';
|
|
5
|
-
import { refusesRequest } from './retry.js';
|
|
4
|
+
import { isAbort, refusesRequest } from './retry.js';
|
|
6
5
|
import { relaxSchema, sanitizeSchema } from './schema-compat.js';
|
|
7
6
|
import { scoped } from './scope.js';
|
|
8
7
|
import { stripThinking } from './thinking.js';
|
|
9
|
-
import { looseJson } from './tool-calls.js';
|
|
8
|
+
import { CODE_FENCE, looseJson } from './tool-calls.js';
|
|
10
9
|
import { JSON_SCHEMA_FORMAT, Role } from './wire.js';
|
|
11
10
|
/**
|
|
12
11
|
* One-shot calls that support a run without being one: picking tools, naming a session,
|
|
@@ -234,22 +233,25 @@ export async function askJson(config, model, system, user, schema, { name = 'ans
|
|
|
234
233
|
*
|
|
235
234
|
* @param label - Names the task in the notice when it fails.
|
|
236
235
|
* @param run - The call to attempt. Anything it throws becomes `undefined`, an abort excepted.
|
|
237
|
-
* @param [options] - `onNotice`, told what was given up on
|
|
238
|
-
*
|
|
239
|
-
*
|
|
236
|
+
* @param [options] - `onNotice`, told what was given up on, and the `signal` that `run` is
|
|
237
|
+
* cancelled by.
|
|
238
|
+
* @returns What `run` resolved with, or `undefined` when it threw. Whatever it threw once the
|
|
239
|
+
* signal had aborted is thrown on and raises no notice, as is an error that is itself an abort.
|
|
240
240
|
*
|
|
241
241
|
* @remarks
|
|
242
242
|
* A side task is never worth failing the work it supports. Callers that can carry on without
|
|
243
243
|
* an answer use this and get `undefined` instead of an exception.
|
|
244
244
|
*/
|
|
245
|
-
export async function tryAsk(label, run, { onNotice } = {}) {
|
|
245
|
+
export async function tryAsk(label, run, { onNotice, signal } = {}) {
|
|
246
246
|
try {
|
|
247
247
|
return await run();
|
|
248
248
|
}
|
|
249
249
|
catch (error) {
|
|
250
250
|
// A cancelled run is not a failed side task. Swallowing the abort made the two
|
|
251
|
-
// indistinguishable and left the cancellation with nowhere to go.
|
|
252
|
-
|
|
251
|
+
// indistinguishable and left the cancellation with nowhere to go. The signal is asked first:
|
|
252
|
+
// an abort with a reason of the caller's own rejects with that reason, which no class names.
|
|
253
|
+
const wasCancelled = signal?.aborted === true || isAbort(error);
|
|
254
|
+
if (wasCancelled) {
|
|
253
255
|
throw error;
|
|
254
256
|
}
|
|
255
257
|
onNotice?.(`${label}: ${errorMessage(error)}`);
|
|
@@ -274,7 +276,7 @@ export async function tryAsk(label, run, { onNotice } = {}) {
|
|
|
274
276
|
* a reply as in a call, and a side task that fails over one costs the run a second request.
|
|
275
277
|
*/
|
|
276
278
|
export function parseJson(text) {
|
|
277
|
-
const fenced = text.
|
|
279
|
+
const [fenced] = text.matchAll(CODE_FENCE);
|
|
278
280
|
const body = (fenced?.[1] ?? text).trim();
|
|
279
281
|
const start = body.search(/[[{]/);
|
|
280
282
|
if (start < 0) {
|
package/dist/tokens.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export declare const CHARS_PER_TOKEN = 4;
|
|
|
13
13
|
* vocabulary either.
|
|
14
14
|
*
|
|
15
15
|
* @param text - Prose or serialised JSON — both counted the same way, which is why JSON reads low.
|
|
16
|
-
* @returns The length over
|
|
16
|
+
* @returns The length over four, rounded up: zero for an empty string, and one for
|
|
17
17
|
* anything shorter than a token.
|
|
18
18
|
*
|
|
19
19
|
* @remarks
|
|
@@ -46,7 +46,7 @@ export interface TokenEstimateOptions {
|
|
|
46
46
|
* The divisor — `charsPerTokenFor` for a model whose reported usage has calibrated it. A value
|
|
47
47
|
* that is not a number above zero is ignored.
|
|
48
48
|
*
|
|
49
|
-
* @defaultValue `
|
|
49
|
+
* @defaultValue `4`
|
|
50
50
|
*/
|
|
51
51
|
charsPerToken?: number;
|
|
52
52
|
}
|
|
@@ -83,7 +83,7 @@ export declare function requestChars(body: OpenAI.ChatCompletionCreateParamsStre
|
|
|
83
83
|
* What this request will cost the window, in tokens, near enough.
|
|
84
84
|
*
|
|
85
85
|
* @param body - The request as it will be sent, tools included.
|
|
86
|
-
* @param [options] - The divisor,
|
|
86
|
+
* @param [options] - The divisor, four when none is given.
|
|
87
87
|
* @returns Whole tokens: the messages' and the tools', each rounded up on its own and added.
|
|
88
88
|
*
|
|
89
89
|
* @remarks
|
|
@@ -172,7 +172,7 @@ export declare function contextTokens(body: OpenAI.ChatCompletionCreateParamsStr
|
|
|
172
172
|
* One message's estimated tokens, by the same count `requestTokens` sums for a whole request.
|
|
173
173
|
*
|
|
174
174
|
* @param message - The message as it will be sent.
|
|
175
|
-
* @param [options] - The divisor,
|
|
175
|
+
* @param [options] - The divisor, four when none is given.
|
|
176
176
|
* @returns `messageChars` over the divisor, rounded up per message, where `requestTokens` rounds
|
|
177
177
|
* once over them all.
|
|
178
178
|
*
|
package/dist/tokens.js
CHANGED
|
@@ -13,7 +13,7 @@ export const CHARS_PER_TOKEN = 4;
|
|
|
13
13
|
* vocabulary either.
|
|
14
14
|
*
|
|
15
15
|
* @param text - Prose or serialised JSON — both counted the same way, which is why JSON reads low.
|
|
16
|
-
* @returns The length over
|
|
16
|
+
* @returns The length over four, rounded up: zero for an empty string, and one for
|
|
17
17
|
* anything shorter than a token.
|
|
18
18
|
*
|
|
19
19
|
* @remarks
|
|
@@ -80,7 +80,7 @@ const REASONING_ALT_KEY = 15;
|
|
|
80
80
|
* The divisor an option asked for, or the fallback when it asked for nothing usable.
|
|
81
81
|
*
|
|
82
82
|
* @param charsPerToken - What the caller gave. Absent, zero, negative or `NaN` is nothing usable.
|
|
83
|
-
* @returns `charsPerToken` when it is above zero, otherwise
|
|
83
|
+
* @returns `charsPerToken` when it is above zero, otherwise four.
|
|
84
84
|
*/
|
|
85
85
|
const divisor = (charsPerToken) => charsPerToken !== undefined && charsPerToken > 0 ? charsPerToken : CHARS_PER_TOKEN;
|
|
86
86
|
/**
|
|
@@ -197,7 +197,7 @@ export function requestChars(body) {
|
|
|
197
197
|
* What this request will cost the window, in tokens, near enough.
|
|
198
198
|
*
|
|
199
199
|
* @param body - The request as it will be sent, tools included.
|
|
200
|
-
* @param [options] - The divisor,
|
|
200
|
+
* @param [options] - The divisor, four when none is given.
|
|
201
201
|
* @returns Whole tokens: the messages' and the tools', each rounded up on its own and added.
|
|
202
202
|
*
|
|
203
203
|
* @remarks
|
|
@@ -327,7 +327,7 @@ export function contextTokens(body, { charsPerToken, promptTokens } = {}) {
|
|
|
327
327
|
* One message's estimated tokens, by the same count `requestTokens` sums for a whole request.
|
|
328
328
|
*
|
|
329
329
|
* @param message - The message as it will be sent.
|
|
330
|
-
* @param [options] - The divisor,
|
|
330
|
+
* @param [options] - The divisor, four when none is given.
|
|
331
331
|
* @returns `messageChars` over the divisor, rounded up per message, where `requestTokens` rounds
|
|
332
332
|
* once over them all.
|
|
333
333
|
*
|
package/dist/tool-calls.d.ts
CHANGED
|
@@ -14,6 +14,14 @@ export declare const ARGUMENT_PREVIEW_CHARS = 200;
|
|
|
14
14
|
export declare const ARGUMENTS_TRUNCATED: 'truncated';
|
|
15
15
|
/** The `ToolArgumentsError` kind for a call the model wrote wrongly. */
|
|
16
16
|
export declare const ARGUMENTS_MALFORMED: 'malformed';
|
|
17
|
+
/**
|
|
18
|
+
* A fenced block in a model's reply, tagged `json` or not tagged; the first group is what it holds.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* Global, for `matchAll`, which reads from a copy: nothing here keeps a place in the text between
|
|
22
|
+
* two uses. What it captures still wants trimming.
|
|
23
|
+
*/
|
|
24
|
+
export declare const CODE_FENCE: RegExp;
|
|
17
25
|
/** A tool call as the loop handles it, recovered or streamed. */
|
|
18
26
|
export type ToolCall = OpenAI.ChatCompletionMessageFunctionToolCall;
|
|
19
27
|
/**
|
package/dist/tool-calls.js
CHANGED
|
@@ -16,6 +16,14 @@ export const ARGUMENT_PREVIEW_CHARS = 200;
|
|
|
16
16
|
export const ARGUMENTS_TRUNCATED = 'truncated';
|
|
17
17
|
/** The `ToolArgumentsError` kind for a call the model wrote wrongly. */
|
|
18
18
|
export const ARGUMENTS_MALFORMED = 'malformed';
|
|
19
|
+
/**
|
|
20
|
+
* A fenced block in a model's reply, tagged `json` or not tagged; the first group is what it holds.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* Global, for `matchAll`, which reads from a copy: nothing here keeps a place in the text between
|
|
24
|
+
* two uses. What it captures still wants trimming.
|
|
25
|
+
*/
|
|
26
|
+
export const CODE_FENCE = /```(?:json)?\s*([\s\S]*?)```/gi;
|
|
19
27
|
/**
|
|
20
28
|
* Tool arguments that could not be read as an object, with why.
|
|
21
29
|
*
|
|
@@ -405,7 +413,7 @@ function bareCalls(text, names) {
|
|
|
405
413
|
return [{ start, end: read.end, calls }];
|
|
406
414
|
}
|
|
407
415
|
}
|
|
408
|
-
const fences = [...text.matchAll(
|
|
416
|
+
const fences = [...text.matchAll(CODE_FENCE)];
|
|
409
417
|
if (fences.length !== 1) {
|
|
410
418
|
return [];
|
|
411
419
|
}
|
package/dist/tool-loading.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRecord } from './guards.js';
|
|
1
|
+
import { byCodeUnit, isRecord } from './guards.js';
|
|
2
2
|
import { ARGUMENT_PREVIEW_CHARS } from './tool-calls.js';
|
|
3
3
|
import { FUNCTION_TOOL, SchemaType } from './wire.js';
|
|
4
4
|
/**
|
|
@@ -170,26 +170,6 @@ export function loadedTools(previous, matched) {
|
|
|
170
170
|
}
|
|
171
171
|
return tools;
|
|
172
172
|
}
|
|
173
|
-
/**
|
|
174
|
-
* Orders two names by code unit.
|
|
175
|
-
*
|
|
176
|
-
* @param a - The name on the left.
|
|
177
|
-
* @param b - The name on the right.
|
|
178
|
-
* @returns Negative where `a` sorts first, positive where `b` does, zero where they are the same.
|
|
179
|
-
*
|
|
180
|
-
* @remarks
|
|
181
|
-
* Rather than `localeCompare`, whose answer depends on the host's locale — which is the kind of
|
|
182
|
-
* instability `orderTools` exists to remove.
|
|
183
|
-
*/
|
|
184
|
-
function byCodeUnit(a, b) {
|
|
185
|
-
if (a < b) {
|
|
186
|
-
return -1;
|
|
187
|
-
}
|
|
188
|
-
if (a > b) {
|
|
189
|
-
return 1;
|
|
190
|
-
}
|
|
191
|
-
return 0;
|
|
192
|
-
}
|
|
193
173
|
/**
|
|
194
174
|
* The tool array in a stable order, so the same set of tools renders the same way twice.
|
|
195
175
|
*
|
package/llms.txt
CHANGED
|
@@ -27,7 +27,7 @@ The loop above a turn: send, run the tools the model asked for, send again, unti
|
|
|
27
27
|
How many characters a token is worth on one model, learned from what its endpoint reports.
|
|
28
28
|
|
|
29
29
|
- `calibrate` — Takes one reading from a request that was answered, so the next one to this model is sized by it.
|
|
30
|
-
- `charsPerTokenFor` — The characters per token to size a request to this model with,
|
|
30
|
+
- `charsPerTokenFor` — The characters per token to size a request to this model with, four until a turn has reported one.
|
|
31
31
|
- `resetCalibration` — Forgets every reading, so the next request is sized at `CHARS_PER_TOKEN` again.
|
|
32
32
|
|
|
33
33
|
### capabilities
|
|
@@ -60,7 +60,6 @@ The contract between whatever holds the tools and the loop that offers them to a
|
|
|
60
60
|
- `contextLimitFor` — How much a model will read, in tokens.
|
|
61
61
|
- `endpointId` — `endpointKey` hashed, for the remembered facts that can leave the process.
|
|
62
62
|
- `endpointKey` — What counts as one endpoint, everywhere in this package that has to remember something about one — the model listings, `capabilities`, and the no-thinking hints in `side-task`.
|
|
63
|
-
- `FIRST_TOKEN_FACTOR` — How many idle windows the first chunk gets when `firstTokenSeconds` is not given.
|
|
64
63
|
- `firstTokenMs` — The wait for a streamed turn's first chunk, in the SDK's spelling: `undefined` is no limit.
|
|
65
64
|
- `getClient` — The client for an endpoint, built once and kept.
|
|
66
65
|
- `listModels` — Asks an endpoint what it serves, and remembers the answer.
|
|
@@ -193,8 +192,6 @@ What stretches of a transcript cost, read off the prompt counts the server alrea
|
|
|
193
192
|
|
|
194
193
|
Choosing a run's tools before it starts, from the request and the catalogue's names.
|
|
195
194
|
|
|
196
|
-
- `KEYWORD_DROPOFF` — How far the best unpicked tool must fall below the last picked one for the cut to count clean.
|
|
197
|
-
- `KEYWORD_MIN_SCORE` — The least a best match may score and still be acted on without a model.
|
|
198
195
|
- `KeywordPreselection` (type) — What `preselectByKeywords` found.
|
|
199
196
|
- `KeywordPreselectOptions` (type) — What `preselectByKeywords` takes besides the catalogue and the request.
|
|
200
197
|
- `PRESELECT_SCHEMA` — The shape a preselector's answer is held to where the server takes a schema: `{ tools: [...] }`.
|
|
@@ -224,8 +221,6 @@ Everything about a request failing that is not about what the request said.
|
|
|
224
221
|
- `isModelLoading` — Whether a failure is a local server still loading the model, rather than one failing to serve.
|
|
225
222
|
- `isOverflow` — Whether a refusal means the request was too big, rather than merely refused.
|
|
226
223
|
- `isTransient` — Whether a failed request is worth trying again.
|
|
227
|
-
- `LOADING_POLL_MS` — How long to wait between asking a loading server again.
|
|
228
|
-
- `LOADING_TIMEOUT_MS` — How long `runTurn` waits for a model to load unless told otherwise.
|
|
229
224
|
- `SMALLEST_LIKELY_WINDOW` — The smallest window worth believing in, and the floor under `runTurn`'s guard.
|
|
230
225
|
- `sleep` — A delay an abort cuts short, rejecting rather than resolving early.
|
|
231
226
|
|
|
@@ -345,7 +340,6 @@ Telling a model's scratchpad from its answer when both arrive in `content`.
|
|
|
345
340
|
|
|
346
341
|
### tokens
|
|
347
342
|
|
|
348
|
-
- `CHARS_PER_TOKEN` — The characters one token is taken to be, where nothing has measured it.
|
|
349
343
|
- `ContextBreakdown` (type) — What a request is made of, by the part of it a consumer can actually do something about.
|
|
350
344
|
- `ContextBreakdownOptions` (type) — What `contextTokens` takes besides the request.
|
|
351
345
|
- `compact` — A token count as a message shows it: 1234 → "1.2k".
|
|
@@ -385,8 +379,6 @@ Reading what a model meant by a tool call when it did not write one cleanly.
|
|
|
385
379
|
- `LOAD_TOOLS_DEFINITION` — One object for the life of the process — the agent loop asks for it on every iteration.
|
|
386
380
|
- `loadedTools` — A tool array with newly loaded definitions appended, in the order they were loaded.
|
|
387
381
|
- `loadResult` — What `load_tools` reports back: the descriptions, now that they are worth their tokens.
|
|
388
|
-
- `MAX_CARRIED` — The most a conversation carries between turns.
|
|
389
|
-
- `MAX_PER_LOAD` — The most a single `load_tools` call may pull in.
|
|
390
382
|
- `orderTools` — The tool array in a stable order, so the same set of tools renders the same way twice.
|
|
391
383
|
- `PROXY_TOOLS` — The whole tool array of a proxied run, `load_tools` then `call_tool`, frozen because it is shared.
|
|
392
384
|
- `proxiedCall` — The tool a `call_tool` names and the arguments to run it with, or a throw the model can read.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cubicecho/agent-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "The endpoint-agnostic half of an OpenAI-compatible agent loop: tool-schema compatibility, on-demand tool loading, one-shot side tasks, run events, and a pooled client.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openai",
|
|
@@ -46,10 +46,10 @@
|
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsc -p tsconfig.build.json && node scripts/llms-txt.mjs",
|
|
49
|
-
"
|
|
49
|
+
"prepack": "npm run build",
|
|
50
50
|
"check": "npm run check:biome && npm run check:types",
|
|
51
51
|
"check:biome": "biome check --write .",
|
|
52
|
-
"check:types": "tsc
|
|
52
|
+
"check:types": "tsc --noEmit",
|
|
53
53
|
"test": "vitest run",
|
|
54
54
|
"test:watch": "vitest",
|
|
55
55
|
"llms": "node scripts/llms-txt.mjs",
|