theorum 0.1.11 → 0.1.13
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 +25 -2
- package/docs/COMPACTION.md +227 -0
- package/docs/SECRETS.md +6 -1
- package/docs/STOP.md +85 -0
- package/esm/mod.d.ts +6 -2
- package/esm/mod.js +3 -1
- package/esm/src/cli/commands/bench.js +2 -4
- package/esm/src/cli/commands/fuzz-guardrails.js +195 -48
- package/esm/src/guardrails/injection.js +13 -8
- package/esm/src/guardrails/normalize.js +65 -38
- package/esm/src/guardrails/sensitive.js +1 -1
- package/esm/src/kernel/engine/compaction.d.ts +69 -0
- package/esm/src/kernel/engine/compaction.js +141 -0
- package/esm/src/kernel/engine/delta.js +30 -7
- package/esm/src/kernel/engine/history-tokens.d.ts +43 -0
- package/esm/src/kernel/engine/history-tokens.js +100 -0
- package/esm/src/kernel/engine/runner/mod.js +164 -62
- package/esm/src/kernel/engine/runner/state.d.ts +3 -1
- package/esm/src/kernel/engine/runner/steps.js +3 -0
- package/esm/src/kernel/mod.d.ts +4 -0
- package/esm/src/kernel/mod.js +2 -0
- package/esm/src/kernel/registry/profiles.js +37 -0
- package/esm/src/kernel/stop.d.ts +75 -0
- package/esm/src/kernel/stop.js +120 -0
- package/esm/src/kernel/types.d.ts +117 -1
- package/esm/src/providers/create-provider.d.ts +7 -0
- package/esm/src/providers/create-provider.js +24 -4
- package/esm/src/providers/expose-for-tests.js +5 -1
- package/esm/src/providers/local.d.ts +29 -0
- package/esm/src/providers/local.js +259 -0
- package/esm/src/providers/mod.d.ts +2 -0
- package/esm/src/providers/mod.js +1 -0
- package/esm/src/providers/openrouter.js +32 -13
- package/esm/src/providers/provider.js +1 -1
- package/esm/src/providers/speech.js +1 -1
- package/esm/src/streaming/mod.d.ts +3 -1
- package/esm/src/streaming/mod.js +2 -1
- package/package.json +6 -1
- package/docs/AGENT_PROFILE_CONTRACT.md +0 -189
- package/docs/CLI_SPEC.md +0 -183
package/README.md
CHANGED
|
@@ -288,6 +288,8 @@ import { createProvider, runTurn } from "jsr:@theorum/core";
|
|
|
288
288
|
const provider = createProvider(profile, {
|
|
289
289
|
gemini: { vault: hostGeminiKeyVault, fetch },
|
|
290
290
|
openRouter: { apiKey: hostSecrets.openRouterApiKey },
|
|
291
|
+
// openAi + local — optional; default baseUrl http://127.0.0.1:11434
|
|
292
|
+
local: { baseUrl: hostResolvedLocalBaseUrl },
|
|
291
293
|
});
|
|
292
294
|
|
|
293
295
|
for await (const event of runTurn({ profile: profile.id, input: { text: "…" } }, provider)) {
|
|
@@ -302,10 +304,21 @@ for await (const event of runTurn({ profile: profile.id, input: { text: "…" }
|
|
|
302
304
|
| `geminiInteractions` + `google` | Google Interactions (chat, image, speech) |
|
|
303
305
|
| `openAi` + `openrouter` (chat) | OpenRouter chat completions |
|
|
304
306
|
| `openAi` + `openrouter` (speech role) | OpenRouter `/audio/speech` |
|
|
307
|
+
| `openAi` + `local` | Local OpenAI-compatible `/v1/chat/completions` (Ollama, llama.cpp, vLLM, LM Studio, …) |
|
|
305
308
|
|
|
306
|
-
|
|
309
|
+
Local adapters take an optional `baseUrl` (default `http://127.0.0.1:11434`). THEORUM does not read `OLLAMA_HOST`; hosts that honor that env should resolve it and pass `local.baseUrl`. History `parts` (including images) are mapped on the wire; `done` events include a normalized `stop` from the OpenAI `finish_reason`.
|
|
307
310
|
|
|
308
|
-
|
|
311
|
+
OpenRouter uses Vercel AI SDK Core inside THEORUM's provider adapter. That stack
|
|
312
|
+
loads **lazily on the first `complete` call** for `openAi` + `openrouter` chat —
|
|
313
|
+
not when importing THEORUM, and not for Google or local providers. The adapter
|
|
314
|
+
still emits THEORUM `TurnEvent` values and preserves raw provider evidence for
|
|
315
|
+
citations/provenance where the normalized SDK stream does not expose enough detail.
|
|
316
|
+
|
|
317
|
+
Advanced OpenRouter exports live under `theorum/openrouter` (`createOpenRouterProvider`,
|
|
318
|
+
`toOpenRouterPayload`, …). Prefer `createProvider` for turns unless the host needs
|
|
319
|
+
to wire the OpenRouter adapter directly. Direct local construction is also available
|
|
320
|
+
as `createLocalProvider` from the main / providers entrypoints. Importing
|
|
321
|
+
`theorum/openrouter` loads the Vercel SDK immediately.
|
|
309
322
|
|
|
310
323
|
---
|
|
311
324
|
|
|
@@ -328,6 +341,16 @@ Internal files remain present in source for maintainability, but package consume
|
|
|
328
341
|
|
|
329
342
|
---
|
|
330
343
|
|
|
344
|
+
## Documentation
|
|
345
|
+
|
|
346
|
+
| Doc | Topic |
|
|
347
|
+
| :--- | :--- |
|
|
348
|
+
| [`docs/SECRETS.md`](docs/SECRETS.md) | Host-owned credentials; `createProvider` args; no env reads |
|
|
349
|
+
| [`docs/COMPACTION.md`](docs/COMPACTION.md) | History / input meters, lazy BPE, `trigger`, signals |
|
|
350
|
+
| [`docs/STOP.md`](docs/STOP.md) | Normalized `done.stop`, resume policy, `continueFrom` |
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
331
354
|
## Development
|
|
332
355
|
|
|
333
356
|
```bash
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# Compaction
|
|
2
|
+
|
|
3
|
+
Optional, profile-driven compaction for managing conversation history when it
|
|
4
|
+
approaches a model's context window limit.
|
|
5
|
+
|
|
6
|
+
The kernel owns the trigger, the split logic, and the timing. The host (or the
|
|
7
|
+
kernel itself, for the easy path) owns the execution and reassembly.
|
|
8
|
+
|
|
9
|
+
## Why
|
|
10
|
+
|
|
11
|
+
Long-running conversations accumulate history that eventually exceeds the
|
|
12
|
+
model's context window. Compaction types the trigger and split into a config
|
|
13
|
+
block; the host still owns how summaries are shaped and persisted.
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
Compaction is configured per model in a profile's `model.config`:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
const speakerModel: ModelSpec = {
|
|
21
|
+
apiId: "gemini-3.5-flash",
|
|
22
|
+
// ...existing model config...
|
|
23
|
+
compaction: {
|
|
24
|
+
maxTokens: 2000, // budget for the chosen meter
|
|
25
|
+
compactAt: 0.75, // fire when 75% full
|
|
26
|
+
previousExchanges: 8, // keep last 8 exchanges verbatim
|
|
27
|
+
profile: "my.compactor", // profile that does the compacting
|
|
28
|
+
timing: "after", // 'before' or 'after' the turn
|
|
29
|
+
meter: "history", // default; or "input"
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### `meter`
|
|
35
|
+
|
|
36
|
+
What the threshold compares:
|
|
37
|
+
|
|
38
|
+
| Value | Meaning |
|
|
39
|
+
| --- | --- |
|
|
40
|
+
| `'history'` (default) | Conversational history only. Host `input.historyTokens`, else local estimate of `input.history`. Excludes system, tool schemas, and this-turn attachments. |
|
|
41
|
+
| `'input'` | Full-prompt provider input tokens. For `timing: 'before'`, pass previous turn's usage as `input.inputTokens`. For `timing: 'after'`, the kernel uses this turn's `tokens.input`. |
|
|
42
|
+
|
|
43
|
+
Provider `tokens` events always stay on the stream for billing / observability.
|
|
44
|
+
With `meter: 'history'` they do **not** gate compaction. With `meter: 'input'`
|
|
45
|
+
they (or host-threaded `inputTokens`) **do**.
|
|
46
|
+
|
|
47
|
+
### `maxTokens`
|
|
48
|
+
|
|
49
|
+
Budget compared as `tokens > compactAt * maxTokens`.
|
|
50
|
+
|
|
51
|
+
- With `meter: 'history'`: set from the model's context window minus known fixed
|
|
52
|
+
costs (system, tools, output headroom).
|
|
53
|
+
- With `meter: 'input'`: set as a full-prompt ceiling (or baseline + headroom).
|
|
54
|
+
Small history-style budgets (e.g. 2000) will fire often if system + tools are
|
|
55
|
+
large — that is expected for this meter.
|
|
56
|
+
|
|
57
|
+
### How `meter: 'history'` counts
|
|
58
|
+
|
|
59
|
+
1. If the host sets `input.historyTokens`, that value is used.
|
|
60
|
+
2. Otherwise the kernel estimates from `input.history` (or `[]`):
|
|
61
|
+
- **Text** — tiktoken `o200k_base` (via `gpt-tokenizer`) over `content`,
|
|
62
|
+
text `parts`, and `tool_calls` arguments. Declared local BPE; Gemini has
|
|
63
|
+
no open JS tokenizer — pass `historyTokens` for Gemini `countTokens`.
|
|
64
|
+
The BPE ranks load **lazily on first text estimate** — not when the package
|
|
65
|
+
is imported. `meter: 'input'`, host `historyTokens`, empty history, and
|
|
66
|
+
media-only estimates never load the tokenizer.
|
|
67
|
+
- **Media parts** — minimum stubs when size/duration are unknown (not
|
|
68
|
+
payload bytes). Image/document: 258 (one still-image / page unit).
|
|
69
|
+
Audio: 32 (1s @ 32/s). Video: 263 (1s @ 263/s). Larger Gemini 2.x images
|
|
70
|
+
are 258×tiles; Gemini 3 uses `media_resolution` budgets (often 560–1120).
|
|
71
|
+
Prefer `historyTokens` when the host knows better.
|
|
72
|
+
- Current-turn `attachments` / `voice` are **not** history.
|
|
73
|
+
|
|
74
|
+
The same history estimator is used by `splitForCompaction` for fractional
|
|
75
|
+
`previousExchanges` (integer / `0` splits do not need the tokenizer). Empty or
|
|
76
|
+
missing history is **0** (does not fire).
|
|
77
|
+
|
|
78
|
+
### `compactAt`
|
|
79
|
+
|
|
80
|
+
Fraction of `maxTokens` at which compaction fires. Must be in (0, 1).
|
|
81
|
+
|
|
82
|
+
### `previousExchanges`
|
|
83
|
+
|
|
84
|
+
How many recent exchanges to preserve verbatim after compaction. An exchange
|
|
85
|
+
starts at each user message and includes all subsequent messages (assistant
|
|
86
|
+
replies, tool calls, tool results) until the next user message.
|
|
87
|
+
|
|
88
|
+
- **`>= 1` (integer)** — keep that many recent exchanges.
|
|
89
|
+
- **`(0, 1)` (fraction)** — retain exchanges that fit within this fraction of
|
|
90
|
+
`maxTokens` (history estimate), walking backwards. Must be less than
|
|
91
|
+
`compactAt`.
|
|
92
|
+
- **`0`** — compact everything; no tail is retained.
|
|
93
|
+
|
|
94
|
+
### `profile`
|
|
95
|
+
|
|
96
|
+
Profile id of the compaction agent. Must be registered before the owning
|
|
97
|
+
profile.
|
|
98
|
+
|
|
99
|
+
### `timing`
|
|
100
|
+
|
|
101
|
+
- **`'before'`** — compact synchronously before the turn.
|
|
102
|
+
- **`'after'`** — signal on the `done` event; host runs compaction async.
|
|
103
|
+
|
|
104
|
+
### `trigger`
|
|
105
|
+
|
|
106
|
+
Optional custom gate. When set, it **replaces** the default
|
|
107
|
+
`tokens > compactAt * maxTokens` check. The kernel still resolves `{ meter,
|
|
108
|
+
tokens }` first and passes them as `CompactionTriggerContext` so the host can
|
|
109
|
+
combine token pressure with other signals (e.g. free RAM):
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
compaction: {
|
|
113
|
+
maxTokens: 2000,
|
|
114
|
+
compactAt: 0.75,
|
|
115
|
+
previousExchanges: 8,
|
|
116
|
+
profile: "my.compactor",
|
|
117
|
+
timing: "after",
|
|
118
|
+
trigger: (ctx) =>
|
|
119
|
+
ctx.tokens > ctx.compactAt * ctx.maxTokens || hostRamPressure(),
|
|
120
|
+
},
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Sync and async triggers are both accepted. Omit `trigger` to keep the default
|
|
124
|
+
threshold.
|
|
125
|
+
|
|
126
|
+
## The compaction profile
|
|
127
|
+
|
|
128
|
+
A compaction profile is a standard THEORUM profile. A simple summarizer:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
registerProfile(
|
|
132
|
+
defineProfile({
|
|
133
|
+
id: "my.compactor",
|
|
134
|
+
identity: {
|
|
135
|
+
handle: "Compactor",
|
|
136
|
+
system: "Summarize this conversation concisely. Preserve unresolved "
|
|
137
|
+
+ "issues, decisions made, and key facts. Drop greetings and filler.",
|
|
138
|
+
},
|
|
139
|
+
model: {
|
|
140
|
+
...modelAllow("gemini35FlashLite"),
|
|
141
|
+
thinking: "minimal",
|
|
142
|
+
maxSteps: 1,
|
|
143
|
+
},
|
|
144
|
+
tools: { allow: [] },
|
|
145
|
+
inputs: { text: true },
|
|
146
|
+
outputs: { structured: "my.summary.schema" },
|
|
147
|
+
guardrails: {
|
|
148
|
+
canary: false,
|
|
149
|
+
sanitizeInput: false,
|
|
150
|
+
redactSensitive: false,
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Usage: `meter: 'history'` (default)
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
const nextReq: TurnRequest = {
|
|
160
|
+
profile: "my.agent",
|
|
161
|
+
input: {
|
|
162
|
+
text: userMessage,
|
|
163
|
+
history: conversationHistory,
|
|
164
|
+
// optional: historyTokens: hostHistoryCount,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Usage: `meter: 'input'`
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
// timing: 'before' — thread previous turn's full-prompt usage
|
|
173
|
+
input: {
|
|
174
|
+
text: userMessage,
|
|
175
|
+
history: conversationHistory,
|
|
176
|
+
inputTokens: previousTokensInput,
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// timing: 'after' — kernel reads this turn's tokens.input automatically
|
|
180
|
+
for await (const event of runTurn(req, provider)) {
|
|
181
|
+
if (event.type === "done" && event.compaction?.needed) {
|
|
182
|
+
const { history, tokens, meter, promptTokens } = event.compaction;
|
|
183
|
+
// meter === 'input'; tokens === promptTokens (when known)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
If the compaction profile uses a different provider, pass `compactionProvider`
|
|
189
|
+
on the `TurnRequest`.
|
|
190
|
+
|
|
191
|
+
## Exported API
|
|
192
|
+
|
|
193
|
+
| Export | Kind | Description |
|
|
194
|
+
| --- | --- | --- |
|
|
195
|
+
| `CompactionSpec` | type | Config on `ModelSpec` |
|
|
196
|
+
| `CompactionMeter` | type | `'history' \| 'input'` |
|
|
197
|
+
| `CompactionTriggerContext` | type | Args for optional `trigger` |
|
|
198
|
+
| `CompactionSignal` | type | `done.compaction`: `meter`, `tokens`, optional `promptTokens`, `history` |
|
|
199
|
+
| `CompactionSplit` / `CompactionTokens` | type | Split result / resolved meter count |
|
|
200
|
+
| `resolveHistoryTokens` | async function | Host `historyTokens` or local history estimate |
|
|
201
|
+
| `resolveCompactionTokens` | async function | Resolve `{ meter, tokens }` for a turn |
|
|
202
|
+
| `estimateHistoryTokens` | async function | tiktoken `o200k_base` + media stubs (lazy BPE) |
|
|
203
|
+
| `HISTORY_TEXT_ENCODING` | const | `'o200k_base'` |
|
|
204
|
+
| `HISTORY_MEDIA_TOKENS` | const | Media minima: image/document 258, audio 32, video 263 |
|
|
205
|
+
| `compactionNeeded` | function | `(tokens, spec) => boolean` |
|
|
206
|
+
| `shouldCompact` | async function | Custom `trigger` or `compactionNeeded` |
|
|
207
|
+
| `splitForCompaction` | async function | `(history, spec) => { toCompact, toRetain }` |
|
|
208
|
+
|
|
209
|
+
## Validation
|
|
210
|
+
|
|
211
|
+
At `registerProfile` time:
|
|
212
|
+
|
|
213
|
+
- `maxTokens` must be > 0
|
|
214
|
+
- `compactAt` must be in (0, 1)
|
|
215
|
+
- `previousExchanges` as a fraction must be < `compactAt`
|
|
216
|
+
- `previousExchanges` >= 1 must be an integer
|
|
217
|
+
- `meter`, when set, must be `'history'` or `'input'`
|
|
218
|
+
- The named compaction profile must already be registered
|
|
219
|
+
|
|
220
|
+
## 0.1.13
|
|
221
|
+
|
|
222
|
+
- Default meter is **history** (local BPE + media stubs, or host `historyTokens`).
|
|
223
|
+
- Optional `meter: 'input'` gates on full-prompt provider usage.
|
|
224
|
+
- Budget field is `maxTokens` (not `maxHistoryTokens`).
|
|
225
|
+
- `CompactionSignal` uses `meter` + `tokens` (no deprecated aliases).
|
|
226
|
+
- Optional `trigger` replaces the default threshold check.
|
|
227
|
+
- History BPE (`gpt-tokenizer` / `o200k_base`) loads lazily on first text estimate.
|
package/docs/SECRETS.md
CHANGED
|
@@ -30,6 +30,11 @@ const provider = createProvider(profile, {
|
|
|
30
30
|
openRouter: {
|
|
31
31
|
apiKey: hostResolvedOpenRouterKey,
|
|
32
32
|
},
|
|
33
|
+
// openAi / local (Ollama, llama.cpp, vLLM, LM Studio, …)
|
|
34
|
+
// Hosts that honor OLLAMA_HOST should resolve it themselves — THEORUM does not.
|
|
35
|
+
local: {
|
|
36
|
+
baseUrl: hostResolvedLocalBaseUrl, // optional; default http://127.0.0.1:11434
|
|
37
|
+
},
|
|
33
38
|
});
|
|
34
39
|
|
|
35
40
|
for await (const event of runTurn({ profile: profile.id, input: { text: '…' } }, provider)) {
|
|
@@ -37,7 +42,7 @@ for await (const event of runTurn({ profile: profile.id, input: { text: '…' }
|
|
|
37
42
|
}
|
|
38
43
|
```
|
|
39
44
|
|
|
40
|
-
`createProvider` picks the transport from `profile.model.protocol` / `provider` (and whether the profile is a speech role). Hosts do not choose a separate speech constructor.
|
|
45
|
+
`createProvider` picks the transport from `profile.model.protocol` / `provider` (and whether the profile is a speech role). Hosts do not choose a separate speech constructor. OpenRouter's Vercel AI SDK dependency loads only when an `openAi` + `openrouter` chat provider first calls `complete` — Google and local paths never import it.
|
|
41
46
|
|
|
42
47
|
## 3. Tracing
|
|
43
48
|
|
package/docs/STOP.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Turn stop and resume
|
|
2
|
+
|
|
3
|
+
Providers end turns for many reasons (completed, length, tool call, filter,
|
|
4
|
+
network drop). THEORUM normalizes those into `TurnStop` on terminal `done`
|
|
5
|
+
events so hosts can decide Continue / auto-continue without parsing each
|
|
6
|
+
adapter.
|
|
7
|
+
|
|
8
|
+
## `done.stop`
|
|
9
|
+
|
|
10
|
+
Adapters attach a `stop` when the turn ends cleanly enough to classify:
|
|
11
|
+
|
|
12
|
+
| `kind` | Meaning |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| `completed` | Normal completion |
|
|
15
|
+
| `length` | Output / budget cut off |
|
|
16
|
+
| `tool` | Model requested tool use |
|
|
17
|
+
| `filtered` | Content filter |
|
|
18
|
+
| `provider_error` | Upstream failure |
|
|
19
|
+
| `cancelled` | User / host abort |
|
|
20
|
+
| `stream_incomplete` | Stream ended without a terminal reason (tunnel drop, etc.) |
|
|
21
|
+
|
|
22
|
+
OpenRouter maps `finish_reason` (+ optional native reason). Google Interactions
|
|
23
|
+
maps terminal `status`. Local OpenAI-compat servers use the same OpenRouter
|
|
24
|
+
finish-reason mapping. Hosts classify client SSE drops with
|
|
25
|
+
`turnStopFromClientStreamEnd`.
|
|
26
|
+
|
|
27
|
+
## Profile resume policy
|
|
28
|
+
|
|
29
|
+
Under `outputs.resume`:
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
outputs: {
|
|
33
|
+
structured: null,
|
|
34
|
+
resume: {
|
|
35
|
+
allowContinue: ['length', 'stream_incomplete', 'provider_error'],
|
|
36
|
+
autoContinue: ['length', 'stream_incomplete'],
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- `allowContinue` — kinds eligible for a Continue CTA / `continueFrom` turn
|
|
42
|
+
(default: length, stream_incomplete, provider_error).
|
|
43
|
+
- `autoContinue` — kinds the host may silently resume **once** after a short
|
|
44
|
+
pause (`AUTO_CONTINUE_DELAY_MS`, 1500). Kernel does not loop; hosts call
|
|
45
|
+
`continueFrom` at most once. User `cancelled` is never auto-continued.
|
|
46
|
+
|
|
47
|
+
Helpers: `isResumeableStop`, `shouldAutoContinue`, `isUserCancelledStop`.
|
|
48
|
+
|
|
49
|
+
## Continuing a turn
|
|
50
|
+
|
|
51
|
+
Pass `continueFrom` on the next `TurnRequest`. The kernel appends the fixed
|
|
52
|
+
`CONTINUE_INSTRUCTION` (do not invent per-app continue prompts):
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
for await (const event of runTurn({
|
|
56
|
+
profile: "my.agent",
|
|
57
|
+
input: { text: "" },
|
|
58
|
+
continueFrom: {
|
|
59
|
+
stop: previousDone.stop,
|
|
60
|
+
partialText: bufferedAssistantText,
|
|
61
|
+
// optional: partialArtifact
|
|
62
|
+
},
|
|
63
|
+
}, provider)) {
|
|
64
|
+
// …
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Errors
|
|
69
|
+
|
|
70
|
+
`GenerationStopError` carries a `stop` for hosts that prefer throw/catch over
|
|
71
|
+
stream events. `isGenerationStopError` narrows it.
|
|
72
|
+
|
|
73
|
+
## Exported API
|
|
74
|
+
|
|
75
|
+
| Export | Kind | Description |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| `TurnStop` / `TurnStopKind` | type | Normalized stop on `done` |
|
|
78
|
+
| `TurnContinueFrom` | type | Partial state for resume |
|
|
79
|
+
| `ProfileResumeSpec` | type | `outputs.resume` policy |
|
|
80
|
+
| `CONTINUE_INSTRUCTION` | const | Fixed continue system text |
|
|
81
|
+
| `DEFAULT_AUTO_CONTINUE` | const | Default one-shot auto-continue kinds |
|
|
82
|
+
| `AUTO_CONTINUE_DELAY_MS` | const | Suggested pause before auto-continue |
|
|
83
|
+
| `isResumeableStop` / `shouldAutoContinue` / `isUserCancelledStop` | function | Host policy helpers |
|
|
84
|
+
| `turnStopFromOpenRouter` / `turnStopFromInteractionStatus` / `turnStopFromClientStreamEnd` | function | Provider / client mappers |
|
|
85
|
+
| `GenerationStopError` / `isGenerationStopError` | class / function | Optional throw path |
|
package/esm/mod.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export { describeError, isAbortError, publicError, TheorumError, throwIfAborted,
|
|
|
41
41
|
export type { QuotaSlotStatus } from './src/guardrails/quota.js';
|
|
42
42
|
export { clientIp, quotaMessage, releaseSlot, resetSlots, skipQuota, takeSlot, } from './src/guardrails/quota.js';
|
|
43
43
|
export { PROJECT_ID_MAX, sanitizeProjectId, sanitizeText, sanitizeTurnRequest, } from './src/guardrails/sanitize.js';
|
|
44
|
+
export type { CompactionSplit, CompactionTokens } from './src/kernel/engine/compaction.js';
|
|
45
|
+
export { compactionMeter, compactionNeeded, estimateHistoryTokens, HISTORY_MEDIA_TOKENS, HISTORY_TEXT_ENCODING, resolveCompactionTokens, resolveHistoryTokens, shouldCompact, splitForCompaction, } from './src/kernel/engine/compaction.js';
|
|
44
46
|
export { runTurn } from './src/kernel/engine/runner.js';
|
|
45
47
|
export { CATALOG, clampThinkingLevel, clampThinkingLevelForApiId, geminiKindForMime, getTool, listBuiltinIds, mimeAllowed, mimeEssence, modelEntryByApiId, registerTools, requireModelSpec, resetTools, } from './src/kernel/registry/catalog.js';
|
|
46
48
|
export type { ProfileDefinition } from './src/kernel/registry/profiles.js';
|
|
@@ -48,8 +50,10 @@ export { clearProfiles, defineProfile, getProfile, hasProfile, listProfiles, reg
|
|
|
48
50
|
export { projectProfile, resolveTurn } from './src/kernel/registry/resolve.js';
|
|
49
51
|
export { getStructured, registerStructured } from './src/kernel/registry/schemas.js';
|
|
50
52
|
export { executeTool } from './src/kernel/registry/tools.js';
|
|
53
|
+
export type { ProfileResumeSpec, TurnContinueFrom, TurnStop, TurnStopKind, } from './src/kernel/stop.js';
|
|
54
|
+
export { AUTO_CONTINUE_DELAY_MS, CONTINUE_INSTRUCTION, DEFAULT_AUTO_CONTINUE, GenerationStopError, isGenerationStopError, isResumeableStop, isUserCancelledStop, shouldAutoContinue, turnStopFromClientStreamEnd, turnStopFromInteractionStatus, turnStopFromOpenRouter, } from './src/kernel/stop.js';
|
|
51
55
|
export type * from './src/kernel/types.js';
|
|
52
56
|
export { jsonlSink, memorySink, noopSink, resolveTraceDir, sinkFromDir, writeTrace, } from './src/observability/trace.js';
|
|
53
57
|
export type { TraceRecord } from './src/observability/trace-record.js';
|
|
54
|
-
export type { CreateProviderOptions, GeminiTransport, GeminiVault } from './src/providers/mod.js';
|
|
55
|
-
export { createProvider } from './src/providers/mod.js';
|
|
58
|
+
export type { CreateProviderOptions, GeminiTransport, GeminiVault, LocalProviderConfig, } from './src/providers/mod.js';
|
|
59
|
+
export { createLocalProvider, createProvider, DEFAULT_LOCAL_BASE_URL, } from './src/providers/mod.js';
|
package/esm/mod.js
CHANGED
|
@@ -40,11 +40,13 @@ import "./_dnt.polyfills.js";
|
|
|
40
40
|
export { describeError, isAbortError, publicError, TheorumError, throwIfAborted, toErrorEvent, } from './src/guardrails/error.js';
|
|
41
41
|
export { clientIp, quotaMessage, releaseSlot, resetSlots, skipQuota, takeSlot, } from './src/guardrails/quota.js';
|
|
42
42
|
export { PROJECT_ID_MAX, sanitizeProjectId, sanitizeText, sanitizeTurnRequest, } from './src/guardrails/sanitize.js';
|
|
43
|
+
export { compactionMeter, compactionNeeded, estimateHistoryTokens, HISTORY_MEDIA_TOKENS, HISTORY_TEXT_ENCODING, resolveCompactionTokens, resolveHistoryTokens, shouldCompact, splitForCompaction, } from './src/kernel/engine/compaction.js';
|
|
43
44
|
export { runTurn } from './src/kernel/engine/runner.js';
|
|
44
45
|
export { CATALOG, clampThinkingLevel, clampThinkingLevelForApiId, geminiKindForMime, getTool, listBuiltinIds, mimeAllowed, mimeEssence, modelEntryByApiId, registerTools, requireModelSpec, resetTools, } from './src/kernel/registry/catalog.js';
|
|
45
46
|
export { clearProfiles, defineProfile, getProfile, hasProfile, listProfiles, registerProfile, registerProfiles, } from './src/kernel/registry/profiles.js';
|
|
46
47
|
export { projectProfile, resolveTurn } from './src/kernel/registry/resolve.js';
|
|
47
48
|
export { getStructured, registerStructured } from './src/kernel/registry/schemas.js';
|
|
48
49
|
export { executeTool } from './src/kernel/registry/tools.js';
|
|
50
|
+
export { AUTO_CONTINUE_DELAY_MS, CONTINUE_INSTRUCTION, DEFAULT_AUTO_CONTINUE, GenerationStopError, isGenerationStopError, isResumeableStop, isUserCancelledStop, shouldAutoContinue, turnStopFromClientStreamEnd, turnStopFromInteractionStatus, turnStopFromOpenRouter, } from './src/kernel/stop.js';
|
|
49
51
|
export { jsonlSink, memorySink, noopSink, resolveTraceDir, sinkFromDir, writeTrace, } from './src/observability/trace.js';
|
|
50
|
-
export { createProvider } from './src/providers/mod.js';
|
|
52
|
+
export { createLocalProvider, createProvider, DEFAULT_LOCAL_BASE_URL, } from './src/providers/mod.js';
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import { sanitizeTurnRequest } from '../../guardrails/sanitize.js';
|
|
16
16
|
import { bindCanary, eventHasCanary, mintCanary } from '../../kernel/engine/boundary.js';
|
|
17
17
|
import { runTurn } from '../../kernel/engine/runner.js';
|
|
18
|
-
import { clearProfiles, registerProfile
|
|
18
|
+
import { clearProfiles, registerProfile } from '../../kernel/registry/profiles.js';
|
|
19
19
|
import { pickSystemRole, resolveTurn } from '../../kernel/registry/resolve.js';
|
|
20
20
|
import { buildRecord } from '../../observability/trace-record.js';
|
|
21
21
|
const DEFAULT_CHUNKS = 200;
|
|
@@ -159,9 +159,7 @@ function aggregate(results) {
|
|
|
159
159
|
const ttfe = results.map((r) => r.ttfe).sort((a, b) => a - b);
|
|
160
160
|
const ttft = results.map((r) => r.ttft).sort((a, b) => a - b);
|
|
161
161
|
const total = results.map((r) => r.totalMs).sort((a, b) => a - b);
|
|
162
|
-
const tps = results
|
|
163
|
-
.map((r) => (r.textEvents / r.totalMs) * MS_PER_SEC)
|
|
164
|
-
.sort((a, b) => a - b);
|
|
162
|
+
const tps = results.map((r) => (r.textEvents / r.totalMs) * MS_PER_SEC).sort((a, b) => a - b);
|
|
165
163
|
const mean = (arr) => arr.reduce((a, b) => a + b, 0) / arr.length;
|
|
166
164
|
return {
|
|
167
165
|
ttfeMs: {
|