@owlmeans/llm 0.1.18-rc.44 → 0.1.18-rc.45
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 +13 -12
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/inquiry/SKILL.md +21 -21
- package/agent-meta/skills/llm/SKILL.md +24 -24
- package/agent-meta/skills/llm-prompt-caching/SKILL.md +4 -4
- package/build/execution/index.d.ts +4 -0
- package/build/execution/index.d.ts.map +1 -1
- package/build/execution/index.js +2 -0
- package/build/execution/index.js.map +1 -1
- package/build/execution/policy/types.d.ts +34 -0
- package/build/execution/policy/types.d.ts.map +1 -0
- package/build/execution/policy/types.js.map +1 -0
- package/build/execution/policy.d.ts +4 -0
- package/build/execution/policy.d.ts.map +1 -0
- package/build/execution/policy.js +49 -0
- package/build/execution/policy.js.map +1 -0
- package/build/execution/service.d.ts.map +1 -1
- package/build/execution/service.js +29 -28
- package/build/execution/service.js.map +1 -1
- package/build/execution/state/types.d.ts +27 -0
- package/build/execution/state/types.d.ts.map +1 -0
- package/build/execution/state/types.js +2 -0
- package/build/execution/state/types.js.map +1 -0
- package/build/execution/state.d.ts +4 -0
- package/build/execution/state.d.ts.map +1 -0
- package/build/execution/state.js +33 -0
- package/build/execution/state.js.map +1 -0
- package/build/execution/utils.d.ts +10 -42
- package/build/execution/utils.d.ts.map +1 -1
- package/build/execution/utils.js +20 -111
- package/build/execution/utils.js.map +1 -1
- package/build/helpers/cache/types.d.ts +20 -0
- package/build/helpers/cache/types.d.ts.map +1 -0
- package/build/helpers/cache/types.js +2 -0
- package/build/helpers/cache/types.js.map +1 -0
- package/build/helpers/cache.d.ts +5 -10
- package/build/helpers/cache.d.ts.map +1 -1
- package/build/helpers/cache.js +17 -19
- package/build/helpers/cache.js.map +1 -1
- package/build/helpers/index.d.ts +3 -0
- package/build/helpers/index.d.ts.map +1 -1
- package/build/helpers/index.js.map +1 -1
- package/build/helpers/json/types.d.ts +27 -0
- package/build/helpers/json/types.d.ts.map +1 -0
- package/build/helpers/json/types.js +2 -0
- package/build/helpers/json/types.js.map +1 -0
- package/build/helpers/json.d.ts +5 -21
- package/build/helpers/json.d.ts.map +1 -1
- package/build/helpers/json.js +94 -107
- package/build/helpers/json.js.map +1 -1
- package/build/helpers/retry/types.d.ts +48 -0
- package/build/helpers/retry/types.d.ts.map +1 -0
- package/build/helpers/retry/types.js +2 -0
- package/build/helpers/retry/types.js.map +1 -0
- package/build/helpers/retry.d.ts +7 -29
- package/build/helpers/retry.d.ts.map +1 -1
- package/build/helpers/retry.js +50 -62
- package/build/helpers/retry.js.map +1 -1
- package/build/helpers/spectate.js +3 -3
- package/build/helpers/spectate.js.map +1 -1
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/build/inquiry/index.d.ts +1 -0
- package/build/inquiry/index.d.ts.map +1 -1
- package/build/inquiry/index.js.map +1 -1
- package/build/inquiry/transport/types.d.ts +23 -0
- package/build/inquiry/transport/types.d.ts.map +1 -0
- package/build/inquiry/transport/types.js +2 -0
- package/build/inquiry/transport/types.js.map +1 -0
- package/build/inquiry/transport.d.ts +7 -6
- package/build/inquiry/transport.d.ts.map +1 -1
- package/build/inquiry/transport.js +27 -21
- package/build/inquiry/transport.js.map +1 -1
- package/build/model.d.ts.map +1 -1
- package/build/model.js +36 -36
- package/build/model.js.map +1 -1
- package/build/plugins/anthropic/consts.local.d.ts +7 -0
- package/build/plugins/anthropic/consts.local.d.ts.map +1 -0
- package/build/plugins/anthropic/consts.local.js +8 -0
- package/build/plugins/anthropic/consts.local.js.map +1 -0
- package/build/plugins/anthropic/support/types.d.ts +34 -0
- package/build/plugins/anthropic/support/types.d.ts.map +1 -0
- package/build/plugins/anthropic/support/types.js +2 -0
- package/build/plugins/anthropic/support/types.js.map +1 -0
- package/build/plugins/anthropic/support.d.ts +4 -0
- package/build/plugins/anthropic/support.d.ts.map +1 -0
- package/build/plugins/anthropic/support.js +17 -0
- package/build/plugins/anthropic/support.js.map +1 -0
- package/build/plugins/anthropic.d.ts +6 -25
- package/build/plugins/anthropic.d.ts.map +1 -1
- package/build/plugins/anthropic.js +36 -61
- package/build/plugins/anthropic.js.map +1 -1
- package/build/plugins/compatible.js +2 -2
- package/build/plugins/compatible.js.map +1 -1
- package/build/plugins/export.d.ts +4 -0
- package/build/plugins/export.d.ts.map +1 -1
- package/build/plugins/export.js +2 -0
- package/build/plugins/export.js.map +1 -1
- package/build/plugins/index.d.ts +6 -9
- package/build/plugins/index.d.ts.map +1 -1
- package/build/plugins/index.js +13 -43
- package/build/plugins/index.js.map +1 -1
- package/build/plugins/openai.d.ts +2 -30
- package/build/plugins/openai.d.ts.map +1 -1
- package/build/plugins/openai.js +12 -12
- package/build/plugins/openai.js.map +1 -1
- package/build/plugins/registry/types.d.ts +25 -0
- package/build/plugins/registry/types.d.ts.map +1 -0
- package/build/plugins/registry/types.js +2 -0
- package/build/plugins/registry/types.js.map +1 -0
- package/build/plugins/registry.d.ts +4 -0
- package/build/plugins/registry.d.ts.map +1 -0
- package/build/plugins/registry.js +44 -0
- package/build/plugins/registry.js.map +1 -0
- package/build/plugins/types.d.ts +18 -0
- package/build/plugins/types.d.ts.map +1 -1
- package/build/plugins/types.local.d.ts +2 -2
- package/build/plugins/types.local.d.ts.map +1 -1
- package/build/plugins/utils/types.d.ts +46 -0
- package/build/plugins/utils/types.d.ts.map +1 -0
- package/build/plugins/utils/types.js +2 -0
- package/build/plugins/utils/types.js.map +1 -0
- package/build/plugins/utils.d.ts +3 -38
- package/build/plugins/utils.d.ts.map +1 -1
- package/build/plugins/utils.js +31 -57
- package/build/plugins/utils.js.map +1 -1
- package/build/prompt/index.d.ts +1 -0
- package/build/prompt/index.d.ts.map +1 -1
- package/build/prompt/index.js.map +1 -1
- package/build/prompt/plugins.js +5 -5
- package/build/prompt/plugins.js.map +1 -1
- package/build/prompt/render/types.d.ts +25 -0
- package/build/prompt/render/types.d.ts.map +1 -0
- package/build/prompt/render/types.js +2 -0
- package/build/prompt/render/types.js.map +1 -0
- package/build/prompt/render.d.ts +5 -18
- package/build/prompt/render.d.ts.map +1 -1
- package/build/prompt/render.js +23 -30
- package/build/prompt/render.js.map +1 -1
- package/build/prompt/service.js +3 -3
- package/build/prompt/service.js.map +1 -1
- package/build/prompt/types.d.ts +2 -1
- package/build/prompt/types.d.ts.map +1 -1
- package/build/service.js +2 -2
- package/build/service.js.map +1 -1
- package/build/types.d.ts +2 -15
- package/build/types.d.ts.map +1 -1
- package/build/types.local.d.ts +1 -1
- package/build/types.local.d.ts.map +1 -1
- package/build/utils/config/types.d.ts +27 -0
- package/build/utils/config/types.d.ts.map +1 -0
- package/build/utils/config/types.js +2 -0
- package/build/utils/config/types.js.map +1 -0
- package/build/utils/config.d.ts +3 -23
- package/build/utils/config.d.ts.map +1 -1
- package/build/utils/config.js +18 -32
- package/build/utils/config.js.map +1 -1
- package/build/utils/effort/types.d.ts +23 -0
- package/build/utils/effort/types.d.ts.map +1 -0
- package/build/utils/effort/types.js +2 -0
- package/build/utils/effort/types.js.map +1 -0
- package/build/utils/effort.d.ts +3 -40
- package/build/utils/effort.d.ts.map +1 -1
- package/build/utils/effort.js +19 -28
- package/build/utils/effort.js.map +1 -1
- package/build/utils/{types.d.ts → null-report/types.d.ts} +11 -14
- package/build/utils/null-report/types.d.ts.map +1 -0
- package/build/utils/null-report/types.js +2 -0
- package/build/utils/null-report/types.js.map +1 -0
- package/build/utils/null-report.d.ts +1 -9
- package/build/utils/null-report.d.ts.map +1 -1
- package/build/utils/null-report.js +3 -1
- package/build/utils/null-report.js.map +1 -1
- package/build/utils/prompt/types.d.ts +51 -0
- package/build/utils/prompt/types.d.ts.map +1 -0
- package/build/utils/prompt/types.js +2 -0
- package/build/utils/prompt/types.js.map +1 -0
- package/build/utils/prompt.d.ts +3 -62
- package/build/utils/prompt.d.ts.map +1 -1
- package/build/utils/prompt.js +88 -127
- package/build/utils/prompt.js.map +1 -1
- package/build/utils/rungs/types.d.ts +26 -0
- package/build/utils/rungs/types.d.ts.map +1 -0
- package/build/utils/rungs/types.js +2 -0
- package/build/utils/{types.js.map → rungs/types.js.map} +1 -1
- package/build/utils/rungs.d.ts +3 -12
- package/build/utils/rungs.d.ts.map +1 -1
- package/build/utils/rungs.js +20 -21
- package/build/utils/rungs.js.map +1 -1
- package/build/utils/schema/consts.d.ts +5 -0
- package/build/utils/schema/consts.d.ts.map +1 -0
- package/build/utils/schema/consts.js +10 -0
- package/build/utils/schema/consts.js.map +1 -0
- package/build/utils/schema/types.d.ts +43 -0
- package/build/utils/schema/types.d.ts.map +1 -0
- package/build/utils/schema/types.js +2 -0
- package/build/utils/schema/types.js.map +1 -0
- package/build/utils/schema.d.ts +3 -52
- package/build/utils/schema.d.ts.map +1 -1
- package/build/utils/schema.js +121 -154
- package/build/utils/schema.js.map +1 -1
- package/build/utils/stream/types.d.ts +25 -0
- package/build/utils/stream/types.d.ts.map +1 -0
- package/build/utils/stream/types.js +2 -0
- package/build/utils/stream/types.js.map +1 -0
- package/build/utils/stream.d.ts +3 -21
- package/build/utils/stream.d.ts.map +1 -1
- package/build/utils/stream.js +32 -47
- package/build/utils/stream.js.map +1 -1
- package/package.json +7 -7
- package/src/execution/index.ts +4 -0
- package/src/execution/policy/types.ts +40 -0
- package/src/execution/policy.ts +73 -0
- package/src/execution/service.ts +30 -32
- package/src/execution/state/types.ts +27 -0
- package/src/execution/state.ts +42 -0
- package/src/execution/utils.ts +28 -121
- package/src/helpers/cache/types.ts +18 -0
- package/src/helpers/cache.ts +24 -20
- package/src/helpers/index.ts +3 -0
- package/src/helpers/json/types.ts +26 -0
- package/src/helpers/json.ts +84 -93
- package/src/helpers/retry/types.ts +49 -0
- package/src/helpers/retry.ts +56 -62
- package/src/helpers/spectate.ts +3 -3
- package/src/index.ts +4 -0
- package/src/inquiry/index.ts +1 -0
- package/src/inquiry/transport/types.ts +23 -0
- package/src/inquiry/transport.ts +35 -21
- package/src/model.ts +40 -39
- package/src/plugins/anthropic/consts.local.ts +8 -0
- package/src/plugins/anthropic/support/types.ts +34 -0
- package/src/plugins/anthropic/support.ts +31 -0
- package/src/plugins/anthropic.ts +42 -72
- package/src/plugins/compatible.ts +2 -2
- package/src/plugins/export.ts +4 -0
- package/src/plugins/index.ts +14 -48
- package/src/plugins/openai.ts +14 -14
- package/src/plugins/registry/types.ts +23 -0
- package/src/plugins/registry.ts +61 -0
- package/src/plugins/types.local.ts +3 -1
- package/src/plugins/types.ts +19 -0
- package/src/plugins/utils/types.ts +40 -0
- package/src/plugins/utils.ts +34 -58
- package/src/prompt/index.ts +1 -0
- package/src/prompt/plugins.ts +5 -5
- package/src/prompt/render/types.ts +25 -0
- package/src/prompt/render.ts +34 -36
- package/src/prompt/service.ts +3 -3
- package/src/prompt/types.ts +1 -2
- package/src/service.ts +2 -2
- package/src/types.local.ts +1 -1
- package/src/types.ts +1 -17
- package/src/utils/config/types.ts +27 -0
- package/src/utils/config.ts +22 -33
- package/src/utils/effort/types.ts +23 -0
- package/src/utils/effort.ts +35 -43
- package/src/utils/{types.ts → null-report/types.ts} +7 -17
- package/src/utils/null-report.ts +3 -9
- package/src/utils/prompt/types.ts +51 -0
- package/src/utils/prompt.ts +82 -120
- package/src/utils/rungs/types.ts +28 -0
- package/src/utils/rungs.ts +26 -25
- package/src/utils/schema/consts.ts +11 -0
- package/src/utils/schema/types.ts +44 -0
- package/src/utils/schema.ts +121 -160
- package/src/utils/stream/types.ts +27 -0
- package/src/utils/stream.ts +37 -49
- package/tests/anthropic-families.spec.ts +11 -11
- package/tests/helpers.spec.ts +25 -31
- package/tests/inquiry.spec.ts +12 -16
- package/tests/internals.spec.ts +33 -33
- package/tests/plugins.spec.ts +27 -27
- package/tests/structured-schema.spec.ts +8 -8
- package/build/utils/types.d.ts.map +0 -1
- /package/build/{utils → execution/policy}/types.js +0 -0
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ abstraction that resolves models from an inheritable policy.
|
|
|
20
20
|
## Installation
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
|
-
bun add @owlmeans/llm@^0.1.18-rc.
|
|
23
|
+
bun add @owlmeans/llm@^0.1.18-rc.45 @owlmeans/llm-common@^0.1.18-rc.39
|
|
24
24
|
bun add @langchain/core @langchain/openai @langchain/anthropic # peer dependencies
|
|
25
25
|
```
|
|
26
26
|
|
|
@@ -100,10 +100,10 @@ const resumed = context.executions().restore(state, { models: () => context.llm(
|
|
|
100
100
|
### Add a provider
|
|
101
101
|
|
|
102
102
|
```typescript
|
|
103
|
-
import {
|
|
103
|
+
import { llmPluginRegistry, openAiFamily } from '@owlmeans/llm'
|
|
104
104
|
import { StructuredMode } from '@owlmeans/llm-common'
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
llmPluginRegistry.register({
|
|
107
107
|
...openAiFamily, // shares owns/refine/toolChoice/responseFormat
|
|
108
108
|
type: 'my-gateway',
|
|
109
109
|
structuredMode: config => config.structuredOutput === true ? StructuredMode.Native : StructuredMode.Tool,
|
|
@@ -139,24 +139,25 @@ spectator entry), `filter` (reject a result and force a retry), `useCache` / `ca
|
|
|
139
139
|
| Reasoning eats the whole output budget | Retry doubles `maxTokens` toward `maxTokensCap` **and** shrinks an absolute reasoning cap |
|
|
140
140
|
| A weak cheap model keeps failing | Walks the `ModelConfig.fallback` chain, `FALLBACK_AFTER_ATTEMPTS` attempts per rung; a rung may be another provider, called in its own dialect |
|
|
141
141
|
| The same model keeps giving the same weak answer | Climbs `ModelConfig.effort` one level per attempt of the rung, up to the model's ceiling; a `TemperatureFactory` request climbs it too |
|
|
142
|
-
| Model stringifies arrays / over-wraps scalars | `coerceToSchema` reconciles the answer with the schema before validation |
|
|
143
|
-
| Model ignores the tool and answers in prose | `parseJsonContent` salvages JSON from fences and surrounding text |
|
|
142
|
+
| Model stringifies arrays / over-wraps scalars | `jsonHelper.coerceToSchema` reconciles the answer with the schema before validation |
|
|
143
|
+
| Model ignores the tool and answers in prose | `jsonHelper.parseJsonContent` salvages JSON from fences and surrounding text |
|
|
144
144
|
| A blank message poisons the request (Anthropic 400 `text content blocks must contain non-whitespace text`, typically an empty file read pasted into a prompt) | Whitespace-only text blocks are dropped before every call; blank tool results are stubbed to keep their `tool_use` pairing, and an all-blank input becomes one stub user message |
|
|
145
145
|
| Nothing usable came back | `NullCapture` — request, response, token accounting and finish reason, to the spectator |
|
|
146
|
-
| An error no retry can fix | `registerFatalError` / `LlmPlugin.isFatal` abort the loop immediately |
|
|
146
|
+
| An error no retry can fix | `retryHelper.registerFatalError` / `LlmPlugin.isFatal` abort the loop immediately |
|
|
147
147
|
|
|
148
148
|
### Helpers (usable alongside a model)
|
|
149
149
|
|
|
150
|
-
`
|
|
151
|
-
`coerceToSchema` · `
|
|
150
|
+
`retryHelper` (`withRetry`, `registerFatalError`, `isFatalError`) · `jsonHelper` (`parseJsonContent`,
|
|
151
|
+
`coerceToSchema`) · `cacheHelper` (`readCacheUsage`, `hasCacheActivity`) · `spectate` ·
|
|
152
|
+
`normalizeInput` · `resolveFallbacks`. Also exported from `@owlmeans/llm/helpers`.
|
|
152
153
|
|
|
153
154
|
Everything under `src/utils/` is library-private and deliberately not exported.
|
|
154
155
|
|
|
155
156
|
### Plugins
|
|
156
157
|
|
|
157
|
-
Exported from `@owlmeans/llm/plugins` as well as the root: `
|
|
158
|
-
`
|
|
159
|
-
`openAiPlugin`, `compatiblePlugin`, `openAiFamily`.
|
|
158
|
+
Exported from `@owlmeans/llm/plugins` as well as the root: `llmPluginRegistry` (the registry —
|
|
159
|
+
`plugins`, `register`, `resolvePlugin`, `pluginOf`, `pluginFor`, `effortSupportOf`),
|
|
160
|
+
`anthropicSupportHelper`, `anthropicPlugin`, `openAiPlugin`, `compatiblePlugin`, `openAiFamily`.
|
|
160
161
|
|
|
161
162
|
## Tests
|
|
162
163
|
|
|
@@ -185,7 +186,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
185
186
|
your project's skill store (`.agents/skills/`):
|
|
186
187
|
|
|
187
188
|
```sh
|
|
188
|
-
npx @owlmeans/agent-skills@^0.1.18-rc.
|
|
189
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.49
|
|
189
190
|
```
|
|
190
191
|
|
|
191
192
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/llm",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-10-
|
|
4
|
+
"version": "0.1.18-rc.45",
|
|
5
|
+
"generatedAt": "2026-10-05T21:03:06.389Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -48,17 +48,17 @@ interface InquiryAnswer {
|
|
|
48
48
|
value?: string | string[]
|
|
49
49
|
text?: string
|
|
50
50
|
declined?: boolean // nobody could decide — an ANSWER, never a failure
|
|
51
|
-
truncated?: boolean // set by capAnswer or stateAnswerOf, never by an answerer
|
|
51
|
+
truncated?: boolean // set by inquiryHelper.capAnswer or .stateAnswerOf, never by an answerer
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
interface InquiryTransport { ask: (inquiry: Inquiry, signal?: AbortSignal) => Promise<InquiryAnswer> }
|
|
55
55
|
interface InquiryConfig { transport?: string; policy: InquiryPolicy }
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
The pure
|
|
59
|
-
contract: `defaultAnswerFor(inquiry)` (the default, or a decline),
|
|
60
|
-
value, else the text, else `null`),
|
|
61
|
-
|
|
58
|
+
The pure `inquiryHelper` is the ONE reading of an answer every layer uses — a second reading is a second
|
|
59
|
+
contract: `inquiryHelper.defaultAnswerFor(inquiry)` (the default, or a decline), `.answeredWith(answer)` (the first
|
|
60
|
+
value, else the text, else `null`), `.isDeclined(answer)`, `.capAnswer(answer, max?)`,
|
|
61
|
+
`.stateAnswerOf(answer)`, `.renderInquiry(inquiry)` (a one-line label for a note or a run row).
|
|
62
62
|
|
|
63
63
|
## Three policies, and no fourth
|
|
64
64
|
|
|
@@ -69,7 +69,7 @@ policy.
|
|
|
69
69
|
| Policy | `ExecutionService.ask` does |
|
|
70
70
|
|---|---|
|
|
71
71
|
| `Ask` | Hands the question to the seated transport and waits for the answer |
|
|
72
|
-
| `Default` | Returns `defaultAnswerFor(inquiry)` and asks nobody. The caller is expected to RECORD the assumption where a user can read it |
|
|
72
|
+
| `Default` | Returns `inquiryHelper.defaultAnswerFor(inquiry)` and asks nobody. The caller is expected to RECORD the assumption where a user can read it |
|
|
73
73
|
| `Refuse` | Throws `InquiryDeclined` — nobody may be asked at all |
|
|
74
74
|
|
|
75
75
|
**No configuration means `Default`.** A run that was never given a channel must never block on one:
|
|
@@ -80,20 +80,20 @@ project pipeline, a scheduled job).
|
|
|
80
80
|
|
|
81
81
|
`DEFAULT_INQUIRY_ANSWER_CHARS` (2 000) is the only ceiling on a stored answer. The connector's
|
|
82
82
|
`CONNECT_INQUIRY_MAX_TEXT` equals it, the connector's answer schema caps `text` at it, and
|
|
83
|
-
`capAnswer` enforces it. Three ceilings for one value is how a user's 3 000-character answer is
|
|
83
|
+
`inquiryHelper.capAnswer` enforces it. Three ceilings for one value is how a user's 3 000-character answer is
|
|
84
84
|
accepted on the wire and silently halved further in — so never introduce a local cap; import this
|
|
85
85
|
one.
|
|
86
86
|
|
|
87
87
|
**Only the prose is ever cut.** A `value` is the decision itself — for a `Choice` it must equal one
|
|
88
88
|
of the question's own option values — so a shortened one is not a degraded answer but a different
|
|
89
|
-
answer, matching no option, which `answeredWith` would hand on as what the person chose. An
|
|
89
|
+
answer, matching no option, which `inquiryHelper.answeredWith` would hand on as what the person chose. An
|
|
90
90
|
over-long `value` is a defect upstream (the connector's schema refuses one rather than shortening
|
|
91
|
-
it): `capAnswer` passes it through whole and raises `truncated` on it.
|
|
91
|
+
it): `inquiryHelper.capAnswer` passes it through whole and raises `truncated` on it.
|
|
92
92
|
|
|
93
|
-
`capAnswer` **reports** the cut (`truncated: true`). A silent truncation is exactly the class of
|
|
93
|
+
`inquiryHelper.capAnswer` **reports** the cut (`truncated: true`). A silent truncation is exactly the class of
|
|
94
94
|
failure the primitive exists to prevent.
|
|
95
95
|
|
|
96
|
-
A resumable pipeline **state** stores less: `stateAnswerOf` keeps the decision whole and cuts the
|
|
96
|
+
A resumable pipeline **state** stores less: `inquiryHelper.stateAnswerOf` keeps the decision whole and cuts the
|
|
97
97
|
prose to `INQUIRY_STATE_TEXT_CHARS` (200), because a state is keys, markers and paths — two
|
|
98
98
|
full-size answers would make it prose. The full answer still goes back to whoever asked; long text
|
|
99
99
|
belongs in whatever document the application keeps for it (the converter writes
|
|
@@ -102,21 +102,21 @@ belongs in whatever document the application keeps for it (the converter writes
|
|
|
102
102
|
## The transport registry, and why an absent channel is fatal
|
|
103
103
|
|
|
104
104
|
```typescript
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
inquiryTransportRegistry.register(key, transport) // seat on attach
|
|
106
|
+
inquiryTransportRegistry.release(key) // release when the channel goes away
|
|
107
|
+
inquiryTransportRegistry.has(key)
|
|
108
|
+
inquiryTransportRegistry.transportFor(key | undefined) // throws InquiryUnavailable — never waits
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
Module-level and keyed by string, exactly like the delegate-transport and provider-plugin
|
|
112
112
|
registries beside it: a process holds many at once, and an execution names the one its run belongs
|
|
113
|
-
to. It is `
|
|
113
|
+
to. It is `inquiryTransportRegistry`, not a bare `transportRegistry`, because `@owlmeans/llm` and
|
|
114
114
|
`@owlmeans/llm-delegate` are re-exported into one namespace by `@owlmeans/viable`.
|
|
115
115
|
|
|
116
116
|
**A transport that cannot serve a question THROWS.** A declined answer is a decision; a channel that
|
|
117
117
|
is gone is terminal, and the two must never look alike.
|
|
118
118
|
|
|
119
|
-
`InquiryUnavailable` is registered fatal (`registerFatalError`) **beside the throw**, so no caller
|
|
119
|
+
`InquiryUnavailable` is registered fatal (`retryHelper.registerFatalError`) **beside the throw**, so no caller
|
|
120
120
|
has to remember: every retry ladder aborts at once instead of spending itself on a channel nobody is
|
|
121
121
|
behind. `InquiryDeclined` is deliberately NOT fatal — the run decides for itself and carries on.
|
|
122
122
|
|
|
@@ -148,8 +148,8 @@ is a tool the model tries once and remembers as broken.
|
|
|
148
148
|
|
|
149
149
|
The body never throws, with exactly one exception: `InquiryUnavailable` is **rethrown**. No channel
|
|
150
150
|
is an answerable situation (`{ error: 'Nobody can answer …' }`); a channel that WAS there and
|
|
151
|
-
vanished is not. That escape only works if the agent passes `fatal: e => isFatalError(e) != null`
|
|
152
|
-
into `safeInvokeTool` — **an agent installing this plugin must**, or the loop spends its whole turn
|
|
151
|
+
vanished is not. That escape only works if the agent passes `fatal: e => retryHelper.isFatalError(e) != null`
|
|
152
|
+
into `toolHelper.safeInvokeTool` — **an agent installing this plugin must**, or the loop spends its whole turn
|
|
153
153
|
budget on a dead channel.
|
|
154
154
|
|
|
155
155
|
## Parking a pipeline: `Waiting`
|
|
@@ -157,7 +157,7 @@ budget on a dead channel.
|
|
|
157
157
|
`PipelineRunContext.ask(inquiry)` has three outcomes, in order:
|
|
158
158
|
|
|
159
159
|
1. An answer already in the state (a resume) is returned at once — a question is never asked twice.
|
|
160
|
-
2. A live channel answers: the answer is recorded in the state (`stateAnswerOf(capAnswer(...))`
|
|
160
|
+
2. A live channel answers: the answer is recorded in the state (`inquiryHelper.stateAnswerOf(inquiryHelper.capAnswer(...))`
|
|
161
161
|
under `state[INQUIRY_ANSWERS_KEY][inquiry.id]`) and the FULL answer is returned.
|
|
162
162
|
3. Nobody is there: the run stops `Waiting` with the inquiry on its row, and the call never returns.
|
|
163
163
|
|
|
@@ -173,7 +173,7 @@ cannot park at all: it throws `PipelineNotResumableError`, because nothing would
|
|
|
173
173
|
Answers are merged **by the runner**, in both `invoke` and `resume` (`resume(runId, { answers })`),
|
|
174
174
|
never by a caller's mapping: the seed overwrites the restored state key by key, so a mapping that
|
|
175
175
|
forwarded the answers map would wipe the child's own recorded answers. The merge applies the same
|
|
176
|
-
`stateAnswerOf(capAnswer(...))` cut the live path does — a resume is how an answer usually arrives,
|
|
176
|
+
`inquiryHelper.stateAnswerOf(inquiryHelper.capAnswer(...))` cut the live path does — a resume is how an answer usually arrives,
|
|
177
177
|
so a ceiling enforced on the live path alone is enforced where the least text comes in. Answers are
|
|
178
178
|
also the one state key that ACCUMULATES, so `ctx.ask` re-reads the map at write time: steps with no
|
|
179
179
|
edge between them run in one superstep, and a write built from a pre-await copy loses a sibling's
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/llm
|
|
9
9
|
|
|
10
10
|
**Layer:** Core
|
|
11
|
-
**Install:** `"@owlmeans/llm": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/llm": "^0.1.18-rc.45"` in `dependencies` (plus the `@langchain/*` peers)
|
|
12
12
|
|
|
13
13
|
The inference runtime. Everything provider-specific is a **plugin**; the model itself only owns the
|
|
14
14
|
provider-independent parts (streaming discipline, retries, validation, observability). Serializable
|
|
@@ -27,15 +27,15 @@ symbol "because a test needs it".
|
|
|
27
27
|
| `makePromptService(options?, alias?)` · `appendPromptService(ctx, options?, alias?)` · `promptServiceApi(options, self)` | Skill registry + the composition plugin chain. Also at `@owlmeans/llm/prompt`. |
|
|
28
28
|
| `rolePlugin`, `skillsPlugin`, `resultsPlugin`, `contextPlugin`, `BUILT_IN_PROMPT_PLUGINS` | The built-in composition plugins. |
|
|
29
29
|
| `PromptContext.claim(key)` · `PromptComposeParams.utility` | Per-composition ownership of a key; a cheap model for one plugin-side call. |
|
|
30
|
-
| `renderSkill
|
|
31
|
-
| `plugins
|
|
30
|
+
| `promptRenderHelper.{renderSkill, sortSkills, joinChunks, compareAlias, prefixHash}` · `cacheHelper.{readCacheUsage, hasCacheActivity}` | Deterministic rendering primitives — reuse them, never re-implement — and normalized prompt-cache accounting from a completion. |
|
|
31
|
+
| `llmPluginRegistry.{plugins, register, resolvePlugin, pluginOf, pluginFor}` | The provider-plugin registry. Also at `@owlmeans/llm/plugins`. |
|
|
32
32
|
| `anthropicPlugin`, `openAiPlugin`, `compatiblePlugin`, `openAiFamily` | Built-in providers; `openAiFamily` is the shared OpenAI-client behaviour to spread into a new plugin. |
|
|
33
|
-
| `NO_SAMPLING_PREFIXES` / `rejectsSampling(model)` · `RESPONSES_API_PREFIXES` / `usesResponsesApi(model)` | Which families reject which sampling parameters — see the table below. Consumers pin presets against them. |
|
|
34
|
-
| `effortSupportOf(config)` · `OPENAI_EFFORT_SUPPORT`, `OPENAI_HIDDEN_PROPERTY_NAMES`, `ANTHROPIC_MODEL_SUPPORT` / `anthropicSupportOf(model)` · `REASONING_MIN_MAX_TOKENS` | Which `ModelConfig.effort` levels a model accepts, and the per-family Anthropic facts beside them — see "Fallback chains and provider effort". |
|
|
35
|
-
| `ThinkingOff`, `thinkingOffFor(config)` · `rejectsForcedTool(model)` | The Anthropic off switch a config sends (`disabled`, `between_tools`, or none), and whether a model refuses a pinned `tool_choice`. |
|
|
36
|
-
| `withRetry
|
|
33
|
+
| `NO_SAMPLING_PREFIXES` / `anthropicSupportHelper.rejectsSampling(model)` · `RESPONSES_API_PREFIXES` / `usesResponsesApi(model)` | Which families reject which sampling parameters — see the table below. Consumers pin presets against them. |
|
|
34
|
+
| `llmPluginRegistry.effortSupportOf(config)` · `OPENAI_EFFORT_SUPPORT`, `OPENAI_HIDDEN_PROPERTY_NAMES`, `ANTHROPIC_MODEL_SUPPORT` / `anthropicSupportHelper.anthropicSupportOf(model)` · `REASONING_MIN_MAX_TOKENS` | Which `ModelConfig.effort` levels a model accepts, and the per-family Anthropic facts beside them — see "Fallback chains and provider effort". |
|
|
35
|
+
| `ThinkingOff`, `anthropicSupportHelper.thinkingOffFor(config)` · `anthropicSupportHelper.rejectsForcedTool(model)` | The Anthropic off switch a config sends (`disabled`, `between_tools`, or none), and whether a model refuses a pinned `tool_choice`. |
|
|
36
|
+
| `retryHelper.{withRetry, registerFatalError, isFatalError}`, `spectate`, `normalizeInput`, `jsonHelper.{parseJsonContent, coerceToSchema}`, `resolveFallbacks`, `PROVIDER_NEUTRAL_FIELDS` | Helpers usable alongside a model. Also at `@owlmeans/llm/helpers`. |
|
|
37
37
|
| `LlmError`, `LlmModelError`, `LlmMissconfiguredError`, `LlmPluginError`, `LlmRetryExceededError` | `ResilientError` family. `LlmModelError` is the RETRYABLE one. |
|
|
38
|
-
| `mergePrompt
|
|
38
|
+
| `executionPolicyHelper.{mergePrompt, mergePolicy, resolveRole, effortPatch}`, `executionStateHelper.freezeResults` | Execution merge helpers; `mergePrompt` unions skills and takes the deepest role; `freezeResults` deep-freezes a results view. |
|
|
39
39
|
| `DEFAULT_MODEL_RETRIES`, `MODEL_STREAM_TIMEOUT_MS` (3 min idle), `FALLBACK_AFTER_ATTEMPTS`, `TEMPERATURE_PER_EFFORT_STEP`, `DEFAULT_EFFORT`, `EFFORT_TABLE`, `MAX_CACHE_BREAKPOINTS`, `MAX_SYSTEM_BREAKPOINTS`, `MIN_CACHEABLE_TOKENS`, `LLM_SERVICE`, `EXECUTION_SERVICE`, `PROMPT_SERVICE` | Tuning + aliases. |
|
|
40
40
|
|
|
41
41
|
## Provider differences are plugins, never `if`s
|
|
@@ -47,7 +47,7 @@ symbol "because a test needs it".
|
|
|
47
47
|
| `build` | the provider switch in the model factory |
|
|
48
48
|
| `owns` / `family` | `instanceof` checks; a `family` change between rungs re-renders the prompt |
|
|
49
49
|
| `refine` | the per-provider retry rebuild (budget doubling, reasoning shrink, effort climb) |
|
|
50
|
-
| `effort` | which `ModelConfig.effort` levels a model accepts (`effortSupportOf(config)`) |
|
|
50
|
+
| `effort` | which `ModelConfig.effort` levels a model accepts (`llmPluginRegistry.effortSupportOf(config)`) |
|
|
51
51
|
| `structuredMode` | native `response_format` vs a structured-output tool call |
|
|
52
52
|
| `toolChoice(toolName, config)` / `responseFormat` | the provider-specific call shapes; `config` is the ACTIVE rung's, so a model that refuses a pinned tool gets the automatic choice |
|
|
53
53
|
| `pinsTool(config)` / `strictTool(config, schema)` | whether `toolChoice` pins the tool (when not, `prepare` appends `toolCallInstruction`), and whether the tool goes `strict` |
|
|
@@ -69,7 +69,7 @@ hooks, through a predicate the package root exports:
|
|
|
69
69
|
|
|
70
70
|
| Family | Rejects | Predicate |
|
|
71
71
|
|---|---|---|
|
|
72
|
-
| Claude 4.7+ and the 5 family | `temperature`, `top_p`, `top_k` | `NO_SAMPLING_PREFIXES` / `rejectsSampling(model)` |
|
|
72
|
+
| Claude 4.7+ and the 5 family | `temperature`, `top_p`, `top_k` | `NO_SAMPLING_PREFIXES` / `anthropicSupportHelper.rejectsSampling(model)` |
|
|
73
73
|
| OpenAI Responses API (`gpt-6*`, `gpt-5*`, `codex-*`) | `temperature`, `top_p` | `RESPONSES_API_PREFIXES` / `usesResponsesApi(model)` |
|
|
74
74
|
|
|
75
75
|
A new OpenAI family goes into `RESPONSES_API_PREFIXES` the day it is pinned: outside it the model
|
|
@@ -82,7 +82,7 @@ primary's. Keep the predicate exported: consumers
|
|
|
82
82
|
pin presets against it (viable-agent's `tests/presets.test.ts` asserts no preset entry declares a
|
|
83
83
|
parameter its model rejects), and a second hand-written copy drifts when a family is added.
|
|
84
84
|
|
|
85
|
-
**Registration order is load-bearing.** Instance lookup (`pluginFor`) returns the FIRST plugin whose
|
|
85
|
+
**Registration order is load-bearing.** Instance lookup (`llmPluginRegistry.pluginFor`) returns the FIRST plugin whose
|
|
86
86
|
`owns` matches, and `compatible` is registered before `openai` because both build a `ChatOpenAI`:
|
|
87
87
|
assuming the tool-calling hack for an unlabelled model is safe everywhere, assuming native
|
|
88
88
|
JSON-schema support is not.
|
|
@@ -94,7 +94,7 @@ composes them into an ordered, cacheable system message; a caller's own leading
|
|
|
94
94
|
folded into the volatile `Context` block, so an unmigrated call site still works. **Do not build a
|
|
95
95
|
persona as a `SystemMessage` in a helper** — declare it as `PromptPolicy.role` plus registered
|
|
96
96
|
skills, or the knowledge duplicates and the cache prefix stops being stable. Skills accumulate down
|
|
97
|
-
the execution chain (project → task → helper) and the deepest declared `role` wins (`mergePrompt`).
|
|
97
|
+
the execution chain (project → task → helper) and the deepest declared `role` wins (`executionPolicyHelper.mergePrompt`).
|
|
98
98
|
Block order, the breakpoint budget, the provider facts behind them, and the plugin seams
|
|
99
99
|
`claim(key)` / `utility`: [[llm-prompt-caching]].
|
|
100
100
|
|
|
@@ -177,7 +177,7 @@ look retryable — costing the whole retry budget with the real message buried u
|
|
|
177
177
|
(`ContextOverflowError` for an input past the context window, and its siblings) carrying the
|
|
178
178
|
original **only under `cause`**, no `status` of its own — so `e.status === 400` misses it.
|
|
179
179
|
|
|
180
|
-
Use `isBadRequest` from `plugins/utils.ts`: it walks the `cause` chain for `status === 400`, bounded
|
|
180
|
+
Use `pluginUtils.isBadRequest` from `plugins/utils.ts`: it walks the `cause` chain for `status === 400`, bounded
|
|
181
181
|
in depth so a self-referential chain terminates. Both built-in `isFatal` implementations go through
|
|
182
182
|
it. A context overflow makes this urgent: `refine` escalates the **output** budget on each retry, so
|
|
183
183
|
an over-limit **input** can never improve — every attempt re-sends the identical oversized request,
|
|
@@ -216,13 +216,13 @@ model switch depends on preset data rather than code: the role must declare a `f
|
|
|
216
216
|
in the other direction — a per-call resolver consulted before the global ones and the plugin's
|
|
217
217
|
`isFatal`, for an error the caller knows no retry can fix.
|
|
218
218
|
|
|
219
|
-
### A loop ABOVE the model asks the same question with `isFatalError`
|
|
219
|
+
### A loop ABOVE the model asks the same question with `retryHelper.isFatalError`
|
|
220
220
|
|
|
221
221
|
A retry loop is not the only place that decides to carry on: a fix ladder rescues a failed repair and
|
|
222
222
|
climbs to a stronger model, an agent runner catches a round that threw and reports "gave up". Both
|
|
223
223
|
are right for a model that answered badly and wrong for a budget that ran out, and a blanket `catch`
|
|
224
224
|
cannot tell them apart — an exhausted balance becomes more expensive calls instead of a halt.
|
|
225
|
-
`isFatalError(e, fatal?)` runs the same resolvers, in the same order, that `withRetry` uses, and
|
|
225
|
+
`retryHelper.isFatalError(e, fatal?)` runs the same resolvers, in the same order, that `retryHelper.withRetry` uses, and
|
|
226
226
|
returns the error to abort WITH (a resolver may unwrap a carrier and hand back the real cause) or
|
|
227
227
|
`null` when nothing considers it terminal. Ask it rather than re-deriving the rule.
|
|
228
228
|
|
|
@@ -237,7 +237,7 @@ Four fields, and conflating them turns an escalation into a fatal 400 hours into
|
|
|
237
237
|
| `maxOutput` | what the PROVIDER accepts in one request — a fact about the model |
|
|
238
238
|
| `contextWindow` | total window (input + output); informational, never sent |
|
|
239
239
|
|
|
240
|
-
`resolveOutputCap` (`utils/config.ts`) reconciles them: the declared cap chooses the ceiling and the
|
|
240
|
+
`configUtils.resolveOutputCap` (`utils/config.ts`) reconciles them: the declared cap chooses the ceiling and the
|
|
241
241
|
capability trims it, and `DEFAULT_MAX_OUTPUT_CAP` applies only when neither is stated. `createModel`
|
|
242
242
|
also clamps `maxTokens` to `maxOutput` and warns about a cap above it. For an aggregated model
|
|
243
243
|
`maxOutput` is the limit of the `inferenceProvider` actually pinned, often far below what the model
|
|
@@ -301,14 +301,14 @@ lives — never an inline check on one id:
|
|
|
301
301
|
| Sonnet 5, Opus 4.8/4.7 | `disabled` | all | accepted | 1024 |
|
|
302
302
|
| Older models (Haiku 4.5, Sonnet 4.6, …) | none — they reason only when asked | per the effort table | accepted | 1024 |
|
|
303
303
|
|
|
304
|
-
`thinkingOffFor(config)` is the value `build` sends; `refine` carries it through `lc_kwargs` and
|
|
304
|
+
`anthropicSupportHelper.thinkingOffFor(config)` is the value `build` sends; `refine` carries it through `lc_kwargs` and
|
|
305
305
|
reads it back to keep the ceiling, so a climbed retry stops at `high` instead of answering 400. The
|
|
306
306
|
cache minimum is the default under a preset's own `cacheMinTokens`.
|
|
307
307
|
|
|
308
|
-
**Structured output on a model that refuses a pinned tool** (`rejectsForcedTool`): `toolChoice`
|
|
308
|
+
**Structured output on a model that refuses a pinned tool** (`anthropicSupportHelper.rejectsForcedTool`): `toolChoice`
|
|
309
309
|
answers `{ type: 'auto' }`, `pinsTool` answers `false`, and `prepare` appends
|
|
310
310
|
`toolCallInstruction(toolName)` to the per-call payload after the JSON mention — the prompt is all
|
|
311
|
-
that asks for the call. The tool goes `strict: true` only when `isStrictSchema(schema)` (every object
|
|
311
|
+
that asks for the call. The tool goes `strict: true` only when `schemaUtils.isStrictSchema(schema)` (every object
|
|
312
312
|
closed with `additionalProperties: false`, basic types, scalar `enum`/`const`, `anyOf`/`allOf`, the
|
|
313
313
|
listed formats, `minItems` 0/1; no `$ref`, no length/pattern/numeric constraints, no `nullable`): a
|
|
314
314
|
strict schema outside that subset is a 400 no retry fixes, while a non-strict one is still checked by
|
|
@@ -326,7 +326,7 @@ family, not Opus 4.8/4.7); the wire is unchanged.
|
|
|
326
326
|
**Effort climbs on retries, one level per attempt of the rung** (`LlmRefineParams.rungAttempt`), from
|
|
327
327
|
the rung's declared level — or its model's default once it has retried — to the model's ceiling. So
|
|
328
328
|
each fallback starts from its OWN level. A `TemperatureFactory` request climbs it too
|
|
329
|
-
(`temperatureSteps`: one level per `TEMPERATURE_PER_EFFORT_STEP` = 0.3, at least one), because the
|
|
329
|
+
(`effortUtils.temperatureSteps`: one level per `TEMPERATURE_PER_EFFORT_STEP` = 0.3, at least one), because the
|
|
330
330
|
models that take effort have mostly taken sampling away and "hotter" alone changes nothing on the
|
|
331
331
|
wire. Effort is part of Anthropic's cached prefix, so a climbed retry writes a new cache entry.
|
|
332
332
|
At `high` and above the OpenAI plugin floors the output budget at `REASONING_MIN_MAX_TOKENS` (25k,
|
|
@@ -361,7 +361,7 @@ JSON pointer (only keys of a `properties` map — a keyword in keyword position
|
|
|
361
361
|
fatal to every retry loop. Function calling (`structuredOutput: false`) and Anthropic's tool mode
|
|
362
362
|
keep every name and are not refused. Rename the field (`mandatory` for `required`). `strict: true`
|
|
363
363
|
is not sent to OpenAI: its strict mode also demands every property in `required`, which
|
|
364
|
-
`isStrictSchema` (Anthropic's subset) does not check.
|
|
364
|
+
`schemaUtils.isStrictSchema` (Anthropic's subset) does not check.
|
|
365
365
|
|
|
366
366
|
### Reasoning is off unless a preset asks for it — and it is billed against the same budget
|
|
367
367
|
|
|
@@ -378,7 +378,7 @@ the idle deadline reads as a dead connection and retries from scratch
|
|
|
378
378
|
`makeLlmModel` appends the literal `/no_think` to every request's prepared messages whenever the flag
|
|
379
379
|
is set AND the plugin's `suppressesThinking(config)` does not answer `true` — the soft switch for
|
|
380
380
|
models with no request-level control (Qwen3). The Anthropic plugin answers `true` only for
|
|
381
|
-
`rejectsSampling(model)`, and for those puts the family's off switch (`thinkingOffFor`, the table
|
|
381
|
+
`anthropicSupportHelper.rejectsSampling(model)`, and for those puts the family's off switch (`anthropicSupportHelper.thinkingOffFor`, the table
|
|
382
382
|
above) on the request in `build`, which `refine` carries through `lc_kwargs` on every attempt — on a
|
|
383
383
|
model that always thinks nothing is sent and the flag only keeps the directive out. Below that line
|
|
384
384
|
(`claude-haiku-4-5`, `claude-sonnet-4-6`) and under any plugin declaring no hook the flag injects
|
|
@@ -388,9 +388,9 @@ switches TO Anthropic inherits nothing and must name it (viable-agent's `presets
|
|
|
388
388
|
rung). Turning reasoning ON is a per-role decision.
|
|
389
389
|
|
|
390
390
|
`ADAPTIVE_MIN_MAX_TOKENS` (32k) is the output floor the Anthropic plugin's `build` applies to every
|
|
391
|
-
`rejectsSampling(model)` config — `disableThinking` is not consulted, so a role with reasoning turned
|
|
391
|
+
`anthropicSupportHelper.rejectsSampling(model)` config — `disableThinking` is not consulted, so a role with reasoning turned
|
|
392
392
|
off is floored just the same. It is a floor, not an override (a preset asking for more keeps it) and
|
|
393
|
-
it is clamped through `resolveOutputCap`, so it can never exceed what the provider accepts and turn a
|
|
393
|
+
it is clamped through `configUtils.resolveOutputCap`, so it can never exceed what the provider accepts and turn a
|
|
394
394
|
retryable empty answer into a fatal 400. Raising or removing it re-opens empty completions.
|
|
395
395
|
|
|
396
396
|
Two diagnostics the above depends on:
|
|
@@ -34,7 +34,7 @@ its text simply travels a different route.
|
|
|
34
34
|
## Rules
|
|
35
35
|
|
|
36
36
|
- **Order is the cache key.** `PROMPT_BLOCK_ORDER` is declared explicitly, skills sort by
|
|
37
|
-
`(order, alias)` with a code-unit comparison (`compareAlias`, never `localeCompare` —
|
|
37
|
+
`(order, alias)` with a code-unit comparison (`promptRenderHelper.compareAlias`, never `localeCompare` —
|
|
38
38
|
ICU data differs between hosts), and detected packages sort alphabetically.
|
|
39
39
|
- **Skill bodies are pure constants.** No timestamps, no absolute paths, no interpolated
|
|
40
40
|
request data. One varying byte invalidates the prefix for every call that shares it.
|
|
@@ -59,10 +59,10 @@ its text simply travels a different route.
|
|
|
59
59
|
may spend at most `MAX_SYSTEM_BREAKPOINTS` (2) — its only STABLE boundaries are the end
|
|
60
60
|
of role+skills and the end of packages — which always leaves two for the messages.
|
|
61
61
|
- **The last message is never cached.** `patchCache` stops one short of the end: the final
|
|
62
|
-
message is the per-call payload, and `ensureJsonMention` /
|
|
62
|
+
message is the per-call payload, and `promptUtils.ensureJsonMention` / `.applyNoThink` append to it.
|
|
63
63
|
- **Markers are placed in-place, on the caller's objects.** A caller that carries its
|
|
64
64
|
message array across calls (a coder's growing conversation, a fix loop) hands them back
|
|
65
|
-
still marked, and they accumulate. `prepare()` therefore calls `stripCacheMarkers()`
|
|
65
|
+
still marked, and they accumulate. `prepare()` therefore calls `promptUtils.stripCacheMarkers()`
|
|
66
66
|
first, so the per-request count depends on THIS call alone. Anything that places a
|
|
67
67
|
marker outside that pipeline must do the same.
|
|
68
68
|
- **A short prefix is not marked.** Below `MIN_CACHEABLE_TOKENS` (override per alias with
|
|
@@ -75,7 +75,7 @@ its text simply travels a different route.
|
|
|
75
75
|
> logs for `Prompt cache [` lines and a non-zero `read`.
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
`usage_metadata.input_token_details` is the only honest answer. `readCacheUsage(message)`
|
|
78
|
+
`usage_metadata.input_token_details` is the only honest answer. `cacheHelper.readCacheUsage(message)`
|
|
79
79
|
(`@owlmeans/llm/helpers`) normalizes it; `spectate` logs a line whenever a provider reports
|
|
80
80
|
any cache activity. **If `read` stays 0 across repeated calls that share a prefix, something
|
|
81
81
|
is invalidating it** — diff `PromptResult.blocks` between two calls to find what.
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
export type * from './types.js';
|
|
2
|
+
export type * from './policy/types.js';
|
|
3
|
+
export type * from './state/types.js';
|
|
4
|
+
export * from './policy.js';
|
|
5
|
+
export * from './state.js';
|
|
2
6
|
export * from './utils.js';
|
|
3
7
|
export * from './service.js';
|
|
4
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/execution/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/execution/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,mBAAmB,mBAAmB,CAAA;AACtC,mBAAmB,kBAAkB,CAAA;AACrC,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}
|
package/build/execution/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/execution/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/execution/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA;AAC1B,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { BaseChatModel } from '@langchain/core/language_models/chat_models';
|
|
2
|
+
import type { ExecutionEffort, ModelConfigOverride, ModelConfigPatch, ModelEffort, ModelPolicy, ModelRole, PromptPolicy } from '@owlmeans/llm-common';
|
|
3
|
+
/** How an execution's model policy and prompt policy are layered and resolved. */
|
|
4
|
+
export interface ExecutionPolicyHelper {
|
|
5
|
+
/** Overlay a partial policy onto a base one. Override maps are merged, not replaced. */
|
|
6
|
+
mergePolicy: (base: ModelPolicy, patch: Partial<ModelPolicy>) => ModelPolicy;
|
|
7
|
+
/**
|
|
8
|
+
* Overlay a prompt policy onto the one inherited from the parent level.
|
|
9
|
+
*
|
|
10
|
+
* Skills ACCUMULATE — a task adds to what the project declared, a helper adds to the
|
|
11
|
+
* task — because that is how a capability set is built up as work narrows. The role is
|
|
12
|
+
* replaced instead: the deepest level that names one owns the persona.
|
|
13
|
+
*
|
|
14
|
+
* The union preserves first-seen order and de-duplicates, so the composed prompt is
|
|
15
|
+
* byte-identical no matter how many levels contributed the same skill.
|
|
16
|
+
*/
|
|
17
|
+
mergePrompt: (base: PromptPolicy | undefined, patch: PromptPolicy | undefined) => PromptPolicy | undefined;
|
|
18
|
+
/** Apply the policy's role→role remap. */
|
|
19
|
+
resolveRole: (policy: ModelPolicy, role: ModelRole) => ModelRole;
|
|
20
|
+
effortPatch: (effort: ExecutionEffort) => ModelConfigPatch;
|
|
21
|
+
/**
|
|
22
|
+
* The effort `steps` levels above what `model` was built with (its model's default when it
|
|
23
|
+
* declares none), or `undefined` when that model accepts no effort.
|
|
24
|
+
*/
|
|
25
|
+
raisedEffort: (model: BaseChatModel, steps: number) => ModelEffort | undefined;
|
|
26
|
+
/** Normalize a {@link ModelConfigOverride} (alias or patch) to a patch. */
|
|
27
|
+
resolveModelConfig: (override: ModelConfigOverride) => ModelConfigPatch;
|
|
28
|
+
/**
|
|
29
|
+
* Merge effort < policy.modelOverride < call-site override into a single patch.
|
|
30
|
+
* Any field present in a higher-precedence source wins.
|
|
31
|
+
*/
|
|
32
|
+
mergeOverride: (effortBase: ModelConfigPatch, policyOverride: ModelConfigOverride | undefined, callOverride: ModelConfigOverride | undefined) => ModelConfigPatch;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/execution/policy/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAA;AAChF,OAAO,KAAK,EACV,eAAe,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAC1G,MAAM,sBAAsB,CAAA;AAE7B,kFAAkF;AAClF,MAAM,WAAW,qBAAqB;IACpC,wFAAwF;IACxF,WAAW,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,WAAW,CAAA;IAC5E;;;;;;;;;OASG;IACH,WAAW,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG,SAAS,EAAE,KAAK,EAAE,YAAY,GAAG,SAAS,KAAK,YAAY,GAAG,SAAS,CAAA;IAC1G,0CAA0C;IAC1C,WAAW,EAAE,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,KAAK,SAAS,CAAA;IAChE,WAAW,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,gBAAgB,CAAA;IAC1D;;;OAGG;IACH,YAAY,EAAE,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,CAAA;IAC9E,2EAA2E;IAC3E,kBAAkB,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,gBAAgB,CAAA;IACvE;;;OAGG;IACH,aAAa,EAAE,CACb,UAAU,EAAE,gBAAgB,EAC5B,cAAc,EAAE,mBAAmB,GAAG,SAAS,EAC/C,YAAY,EAAE,mBAAmB,GAAG,SAAS,KAC1C,gBAAgB,CAAA;CACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/execution/policy/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../../src/execution/policy.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAA;AAE9D,eAAO,MAAM,2BAA2B,QAAO,qBA4D9C,CAAA;AAED,eAAO,MAAM,qBAAqB,uBAAgC,CAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { EFFORT_TABLE } from '../consts.js';
|
|
2
|
+
import { llmPluginRegistry } from '../plugins/registry.js';
|
|
3
|
+
import { configUtils } from '../utils/config.js';
|
|
4
|
+
import { effortUtils } from '../utils/effort.js';
|
|
5
|
+
export const createExecutionPolicyHelper = () => {
|
|
6
|
+
const mergePolicy = (base, patch) => {
|
|
7
|
+
const utilityRole = patch.utilityRole ?? base.utilityRole;
|
|
8
|
+
return {
|
|
9
|
+
effort: patch.effort ?? base.effort,
|
|
10
|
+
roleOverrides: patch.roleOverrides != null || base.roleOverrides != null
|
|
11
|
+
? { ...base.roleOverrides, ...patch.roleOverrides }
|
|
12
|
+
: undefined,
|
|
13
|
+
modelOverrides: patch.modelOverrides != null || base.modelOverrides != null
|
|
14
|
+
? { ...base.modelOverrides, ...patch.modelOverrides }
|
|
15
|
+
: undefined,
|
|
16
|
+
// Added only when it exists, unlike the maps above: a project that never names a
|
|
17
|
+
// cheap tier must not gain a `utilityRole` key it would then carry into every snapshot.
|
|
18
|
+
...(utilityRole != null ? { utilityRole } : {}),
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
const mergePrompt = (base, patch) => {
|
|
22
|
+
if (base == null && patch == null) {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
const skills = [...new Set([...(base?.skills ?? []), ...(patch?.skills ?? [])])];
|
|
26
|
+
return {
|
|
27
|
+
...base,
|
|
28
|
+
...patch,
|
|
29
|
+
...(base?.role != null || patch?.role != null ? { role: patch?.role ?? base?.role } : {}),
|
|
30
|
+
...(skills.length > 0 ? { skills } : {}),
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const resolveRole = (policy, role) => policy.roleOverrides?.[role] ?? role;
|
|
34
|
+
const effortPatch = (effort) => EFFORT_TABLE[effort];
|
|
35
|
+
const raisedEffort = (model, steps) => {
|
|
36
|
+
const config = configUtils.readConfig(model);
|
|
37
|
+
const support = llmPluginRegistry.effortSupportOf(config);
|
|
38
|
+
return support != null ? effortUtils.raiseEffort(support, config.effort, steps) : undefined;
|
|
39
|
+
};
|
|
40
|
+
const resolveModelConfig = (override) => typeof override === 'string' ? { preset: override } : override;
|
|
41
|
+
const mergeOverride = (effortBase, policyOverride, callOverride) => {
|
|
42
|
+
const policy = policyOverride != null ? resolveModelConfig(policyOverride) : {};
|
|
43
|
+
const call = callOverride != null ? resolveModelConfig(callOverride) : {};
|
|
44
|
+
return { ...effortBase, ...policy, ...call };
|
|
45
|
+
};
|
|
46
|
+
return { mergePolicy, mergePrompt, resolveRole, effortPatch, raisedEffort, resolveModelConfig, mergeOverride };
|
|
47
|
+
};
|
|
48
|
+
export const executionPolicyHelper = createExecutionPolicyHelper();
|
|
49
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../src/execution/policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/execution/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAOtF,OAAO,KAAK,EACkB,gBAAgB,EAAE,uBAAuB,EAAE,cAAc,EACrD,oBAAoB,EACrD,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/execution/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAOtF,OAAO,KAAK,EACkB,gBAAgB,EAAE,uBAAuB,EAAE,cAAc,EACrD,oBAAoB,EACrD,MAAM,YAAY,CAAA;AAOnB;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,cAAc,GAAG,cAAc,WAClE,uBAAuB,QAC1B,MAAM,gBAAgB,CAAC,CAAC,CAAC,KAC9B,gBAAgB,CAAC,CAAC,CAoMpB,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,cAAc,GAAG,cAAc,UACrE,MAAM,YACJ,uBAAuB,KAC/B,gBAAgB,CAAC,CAAC,CAMpB,CAAA;AAED,eAAO,MAAM,sBAAsB,GACjC,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,cAAc,GAAG,cAAc,OAEtF,CAAC,UACC,MAAM,YACJ,uBAAuB,KAC/B,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAQ5B,CAAA"}
|