@indexyz/dsh-custom-provider-reasoning-efforts 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +216 -6
- package/cordis.patch.yml +9 -0
- package/lib/apply.d.ts +60 -0
- package/lib/apply.d.ts.map +1 -0
- package/lib/apply.js +67 -0
- package/lib/apply.js.map +1 -0
- package/lib/declarations.d.ts +57 -0
- package/lib/declarations.d.ts.map +1 -0
- package/lib/declarations.js +114 -0
- package/lib/declarations.js.map +1 -0
- package/lib/discovery.d.ts +92 -0
- package/lib/discovery.d.ts.map +1 -0
- package/lib/discovery.js +189 -0
- package/lib/discovery.js.map +1 -0
- package/lib/index.d.ts +73 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +210 -0
- package/lib/index.js.map +1 -0
- package/lib/levels.d.ts +21 -0
- package/lib/levels.d.ts.map +1 -0
- package/lib/levels.js +22 -0
- package/lib/levels.js.map +1 -0
- package/lib/plan.d.ts +52 -0
- package/lib/plan.d.ts.map +1 -0
- package/lib/plan.js +91 -0
- package/lib/plan.js.map +1 -0
- package/lib/reasoning.d.ts +59 -0
- package/lib/reasoning.d.ts.map +1 -0
- package/lib/reasoning.js +155 -0
- package/lib/reasoning.js.map +1 -0
- package/lib/runtime.d.ts +115 -0
- package/lib/runtime.d.ts.map +1 -0
- package/lib/runtime.js +374 -0
- package/lib/runtime.js.map +1 -0
- package/package.json +71 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Read a Codex-shaped relay's reasoning levels from its /backend-api/codex/models manifest when its /v1/models inventory publishes none.
|
|
8
|
+
|
|
9
|
+
## 0.1.0 - 2026-10-17
|
|
10
|
+
|
|
11
|
+
- Initial release.
|
|
12
|
+
- Declare `reasoningEfforts` for a hand-written `llm-pi-ai` provider route, whose models otherwise resolve to `reasoning: false` because the installed pi-ai catalog ships no entry to inherit from.
|
|
13
|
+
- Carry the declarations in the plugin row's own `providers` table, keyed by provider route and exact model id, with each level mapped to the wire spelling it dispatches and `false` for a model that cannot reason; a malformed declaration fails the load, naming the route, the model, and the level.
|
|
14
|
+
- Write the missing field through the Harness settings service over the `llm-pi-ai` row's volatile `providers` field, so the change reaches the running adapter without a restart; only models the profile already declares are filled, and nothing is invented.
|
|
15
|
+
- Interrogate a custom route's `/v1/models` for the levels it advertises (`reasoning`, `supported_reasoning_levels`, and their sibling spellings) and map them onto the models the route declares, with endpoint failures contained to a warning.
|
|
16
|
+
- Add `/reasoning-efforts` with `status`, `apply`, `discover`, and `clear`, reporting every change, every already-current pair, and every skip with its reason.
|
|
17
|
+
- Run the pass once after startup, through the launcher's readiness signal when one exists, so a declaration lands without a restart and an unreachable gateway never blocks or fails boot.
|
package/README.md
CHANGED
|
@@ -1,8 +1,218 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @indexyz/dsh-custom-provider-reasoning-efforts
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
before its first real release. The real plugin is released as `0.1.0` from the
|
|
6
|
-
repository `5aaee9/pi-agent-extensions` by the `publish.yml` workflow.
|
|
3
|
+
Give a hand-written [dsh (DeepSeek Harness)](https://github.com/deepseek-ai/deepseek-harness)
|
|
4
|
+
provider route the reasoning levels its gateway actually offers.
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
```bash
|
|
7
|
+
dsh plugin --profile web add @indexyz/dsh-custom-provider-reasoning-efforts
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
It is built and tested against dsh `0.2.0-rc.2` and declares that range as
|
|
11
|
+
`engines.dsh` (`>=0.2.0-rc.2 <0.3.0-0`), which the plugin market reads before it
|
|
12
|
+
installs. Node `22.19.0` or newer is required as well.
|
|
13
|
+
|
|
14
|
+
To try a repository checkout instead (after `npm install` at the repository root):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
dsh plugin --profile web add ./dsh-custom-provider-reasoning-efforts
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Why a custom route loses its levels
|
|
21
|
+
|
|
22
|
+
`llm-pi-ai` resolves a model's reasoning capability from the profile entry's
|
|
23
|
+
`reasoningEfforts` field and, only when that field is absent, from the installed
|
|
24
|
+
pi-ai catalog. A route the catalog does not ship — `magpie` in the configuration
|
|
25
|
+
this plugin was written for — has no catalog entry at all, so:
|
|
26
|
+
|
|
27
|
+
| Step | Result |
|
|
28
|
+
| ----------------------------------------- | ------------------------------------------------------------ |
|
|
29
|
+
| `catalogModels("magpie").get(id)` | empty map, so `base` is undefined |
|
|
30
|
+
| `resolveModelReasoning(entry, undefined)` | `{reasoning: base?.reasoning ?? false}` → `reasoning: false` |
|
|
31
|
+
| `reasoningInfo(model)` | `if (!model.reasoning) return {}` |
|
|
32
|
+
| model-selection menu | no levels to offer |
|
|
33
|
+
|
|
34
|
+
The gateway may advertise exactly what is missing — `reasoning: true` and
|
|
35
|
+
`supported_reasoning_levels: [{effort: "low"}, {effort: "high"}, {effort: "max"}]`
|
|
36
|
+
— but the Harness endpoint probe parses only `id`, `name`, `contextWindow`, and
|
|
37
|
+
`maxTokens`, and it does not persist what it reads, so discovery cannot fill the
|
|
38
|
+
gap by itself. The one thing that fixes it is the field the profile already
|
|
39
|
+
supports, on every model entry of that route. This plugin writes it.
|
|
40
|
+
|
|
41
|
+
## Declarations
|
|
42
|
+
|
|
43
|
+
Declarations live in this plugin's own row in the profile patch
|
|
44
|
+
(`~/.dsh/profiles/<profile>/cordis.patch.yml`):
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
- insert:
|
|
48
|
+
- id: custom-provider-reasoning-efforts
|
|
49
|
+
name: "@indexyz/dsh-custom-provider-reasoning-efforts"
|
|
50
|
+
config:
|
|
51
|
+
providers:
|
|
52
|
+
magpie:
|
|
53
|
+
ollama-cloud/kimi-k3:
|
|
54
|
+
low: low
|
|
55
|
+
high: high
|
|
56
|
+
max: max
|
|
57
|
+
ollama-cloud/deepseek-v4.1-flash:
|
|
58
|
+
low: low
|
|
59
|
+
high: high
|
|
60
|
+
max: max
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Each model's value is either the levels it offers — keyed by pi-ai thinking level
|
|
64
|
+
(`off`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`) and mapped to the wire
|
|
65
|
+
spelling that level dispatches — or `false` for a model the route serves but that
|
|
66
|
+
cannot reason. `off: null` is the one level that may carry no wire value, because
|
|
67
|
+
not thinking is the parameter's absence. A declaration that is empty, offers no
|
|
68
|
+
level beyond `off`, puts `null` or an empty string on a thinking level, or names a
|
|
69
|
+
level pi-ai does not know fails the plugin's load with the route, the model, and
|
|
70
|
+
the level named.
|
|
71
|
+
|
|
72
|
+
The plugin writes only models the route already declares in the profile: it
|
|
73
|
+
never invents a route, a `baseURL`, a protocol, a credential reference, or a
|
|
74
|
+
model entry. A declaration for a route or model the profile does not carry is
|
|
75
|
+
reported by the command and skipped.
|
|
76
|
+
|
|
77
|
+
## The `/reasoning-efforts` command
|
|
78
|
+
|
|
79
|
+
```text
|
|
80
|
+
/reasoning-efforts # what is pending; writes nothing (same as `status`)
|
|
81
|
+
/reasoning-efforts status # same
|
|
82
|
+
/reasoning-efforts apply # write the declarations above
|
|
83
|
+
/reasoning-efforts discover # interrogate each custom route, then write
|
|
84
|
+
/reasoning-efforts clear # remove the reasoningEfforts the declarations name
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Every mode reports the route/model pairs it changed, how many were already
|
|
88
|
+
current, and every pair it skipped with the reason. `status`, `apply`, and
|
|
89
|
+
`clear` perform no network request. Nothing is written when the plan finds no
|
|
90
|
+
change, so a repeat run leaves the file untouched.
|
|
91
|
+
|
|
92
|
+
## Endpoint discovery
|
|
93
|
+
|
|
94
|
+
A route is a discovery candidate when its profile declares both an explicit,
|
|
95
|
+
non-empty `models` list and a `baseURL` — the shape of a hand-written route.
|
|
96
|
+
`discoverRoutes` names the candidates instead when a deployment wants otherwise.
|
|
97
|
+
Each candidate is interrogated once at `GET {baseURL}/models` (Anthropic Messages
|
|
98
|
+
at the root `/v1/models`), with `authorization: Bearer <key>` for the OpenAI
|
|
99
|
+
protocols and `x-api-key` plus `anthropic-version` for Anthropic Messages. The
|
|
100
|
+
key is resolved from the route's own `apiKeyEnv` through the credential store,
|
|
101
|
+
falling back to the process environment; when neither resolves, the probe is
|
|
102
|
+
sent unauthenticated rather than failing.
|
|
103
|
+
|
|
104
|
+
The reply's `data` array, or the `models` object some compatible gateways expose,
|
|
105
|
+
is read; reasoning is taken from `supported_reasoning_levels`,
|
|
106
|
+
`reasoning_levels`, `supported_reasoning_efforts`, or the `reasoning` object's
|
|
107
|
+
`efforts` / `supported_efforts`, with string entries or `{effort}` / `{level}` /
|
|
108
|
+
`{id}` / `{name}` objects. A name that is a pi-ai thinking level maps to itself
|
|
109
|
+
as its wire spelling and `off` maps to `null`; anything else is ignored. Only
|
|
110
|
+
models the route already declares are considered, so a gateway listing hundreds
|
|
111
|
+
of models cannot add any. An explicit declaration always wins over a discovered
|
|
112
|
+
one, and a model whose entry advertises no levels is left exactly as it is.
|
|
113
|
+
|
|
114
|
+
### The Codex manifest, for a relay whose inventory says nothing
|
|
115
|
+
|
|
116
|
+
A relay that proxies a Codex backend sometimes publishes an inventory with no
|
|
117
|
+
capability at all — only `id`, `display_name`, and `owned_by`, the shape
|
|
118
|
+
[`pi-provider-sub2api`](../pi-provider-sub2api) also works around. The same relay
|
|
119
|
+
usually answers the backend's own model list at
|
|
120
|
+
`GET {root}/backend-api/codex/models` (`root` is the route's `baseURL` with a
|
|
121
|
+
trailing `/v1` removed), where each entry carries `slug` and the efforts the
|
|
122
|
+
backend accepts:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"models": [
|
|
127
|
+
{
|
|
128
|
+
"slug": "gpt-6.1-sol",
|
|
129
|
+
"supported_reasoning_levels": [
|
|
130
|
+
{ "effort": "low" },
|
|
131
|
+
{ "effort": "medium" },
|
|
132
|
+
{ "effort": "high" },
|
|
133
|
+
{ "effort": "xhigh" },
|
|
134
|
+
{ "effort": "max" },
|
|
135
|
+
{ "effort": "ultra" }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
That manifest is a second discovery source. It is consulted only for an
|
|
143
|
+
`openai-responses` route whose own listing described no level for at least one
|
|
144
|
+
model the route declares, so a relay that already answers costs no extra request;
|
|
145
|
+
its levels are merged with the listing's (the listing's own spellings win) and
|
|
146
|
+
only the route's declared models are considered. A relay without the endpoint
|
|
147
|
+
answers `403`, `404`, or something that is not a manifest, which means "nothing
|
|
148
|
+
more to say" rather than a failure; an endpoint that is unreachable, or that
|
|
149
|
+
answers with its own error status, is reported. `ultra` is not a pi-ai thinking
|
|
150
|
+
level, so it is dropped from a discovered declaration — declare `max: ultra` in
|
|
151
|
+
the route's own `providers` entry to wire the `max` level to it.
|
|
152
|
+
|
|
153
|
+
Discovery is on by default and runs once after startup (see `applyOnLoad` and
|
|
154
|
+
`discover` below); `/reasoning-efforts discover` runs it on demand. Every
|
|
155
|
+
interrogation failure is contained to a warning and one line in the command's
|
|
156
|
+
answer — a gateway that is down never blocks startup or a declaration that does
|
|
157
|
+
not need the network.
|
|
158
|
+
|
|
159
|
+
## Configuration
|
|
160
|
+
|
|
161
|
+
| Field | Default | Meaning |
|
|
162
|
+
| ---------------- | ----------- | ---------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| `providers` | `{}` | Declarations keyed by provider route and then by exact model id; each value is an effort table or `false`. |
|
|
164
|
+
| `discover` | `true` | Whether the load-time pass interrogates endpoints as well as applying declarations. |
|
|
165
|
+
| `discoverRoutes` | `[]` | Routes to interrogate instead of the heuristic's own set; empty means the heuristic. |
|
|
166
|
+
| `namespace` | `llm-pi-ai` | The configurable namespace whose `providers` field is written. |
|
|
167
|
+
| `applyOnLoad` | `true` | Whether the plugin writes at load. Off leaves `/reasoning-efforts` as the only writer. |
|
|
168
|
+
|
|
169
|
+
## Behavior and limitations
|
|
170
|
+
|
|
171
|
+
- The write goes through the Harness settings service
|
|
172
|
+
(`settings.describe()` / `update()`), the same seam the Models page uses, so
|
|
173
|
+
the Loader validates the change and reconciles the running `llm-pi-ai` row.
|
|
174
|
+
`providers` is a `volatile` field, so the change is live: the next
|
|
175
|
+
`resolveModelInfo()` — and the model-selection menu's next read — sees the
|
|
176
|
+
levels. No restart is involved.
|
|
177
|
+
- Because that write is a configuration write, the `llm-pi-ai` config node is
|
|
178
|
+
re-serialized: comments inside that node do not survive a write, and the plugin
|
|
179
|
+
writes only when it has an actual change to make. The declarations themselves
|
|
180
|
+
are persisted, so they keep working if the plugin is later removed;
|
|
181
|
+
`/reasoning-efforts clear` is the way back.
|
|
182
|
+
- Only models the profile's own patch layer declares can be filled. A route that
|
|
183
|
+
comes from a lower layer, or a model the route does not list, is reported and
|
|
184
|
+
skipped.
|
|
185
|
+
- A gateway that advertises `reasoning: true` with no level list gets no
|
|
186
|
+
declaration: there is no level to offer, and inventing one would claim a
|
|
187
|
+
capability the endpoint did not declare.
|
|
188
|
+
- A level name is used as its own wire spelling. A gateway that accepts a
|
|
189
|
+
different spelling for a level needs that spelling written explicitly in the
|
|
190
|
+
declaration.
|
|
191
|
+
- Discovery reads `openai-completions`, `openai-responses`, and
|
|
192
|
+
`anthropic-messages` listings; any other protocol is reported as unsupported
|
|
193
|
+
and left to explicit declarations.
|
|
194
|
+
- The load-time pass waits for the launcher's readiness signal when the
|
|
195
|
+
composition provides one, runs asynchronously, and contains every failure to a
|
|
196
|
+
log line. It never throws out of the plugin's load and never delays startup.
|
|
197
|
+
|
|
198
|
+
## Development
|
|
199
|
+
|
|
200
|
+
The host half is TypeScript compiled to `lib/` with `tsc`; there is no browser
|
|
201
|
+
half.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
npm run check # typecheck, build, then test
|
|
205
|
+
npm run build # lib/ (Host half)
|
|
206
|
+
npm test # 96 Vitest cases
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
`test/smoke-release.ts` is the package's declared `release.smoke` check: the
|
|
210
|
+
release engine runs it against the packed and installed tarball, verifying the
|
|
211
|
+
manifest targets, the built host entry, and the bundle patch the `dsh` manifest
|
|
212
|
+
points at.
|
|
213
|
+
|
|
214
|
+
See the repository root for the workspace layout and release process.
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Bundle patch layer: the row loads this package's main entry as the Host half
|
|
2
|
+
# (a plain Cordis plugin exporting `apply`, `inject`, and `Config`), and its
|
|
3
|
+
# `providers` table is where a deployment declares the reasoning levels of a
|
|
4
|
+
# hand-written provider route.
|
|
5
|
+
- insert:
|
|
6
|
+
- id: custom-provider-reasoning-efforts
|
|
7
|
+
name: "@indexyz/dsh-custom-provider-reasoning-efforts"
|
|
8
|
+
config:
|
|
9
|
+
providers: {}
|
package/lib/apply.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write a planned change through the Harness settings service.
|
|
3
|
+
*
|
|
4
|
+
* This is the only writer. It reads the namespace's descriptor, plans against
|
|
5
|
+
* the profile patch's own `providers` value, and calls `settings.update()` with
|
|
6
|
+
* the whole planned value and the revision `describe()` returned, so the Loader
|
|
7
|
+
* validates the candidate and reconciles the running `llm-pi-ai` adapter
|
|
8
|
+
* exactly as it does a Models-page save — `providers` is a `volatile` field, so
|
|
9
|
+
* the change is live without a restart. Nothing is written when the plan finds
|
|
10
|
+
* no change, and a revision conflict is retried once against a fresh read.
|
|
11
|
+
*
|
|
12
|
+
* @module @indexyz/dsh-custom-provider-reasoning-efforts/apply
|
|
13
|
+
*/
|
|
14
|
+
import { type Change, type Intent, type Skip } from "./plan.ts";
|
|
15
|
+
/** One configurable namespace as the settings service describes it. */
|
|
16
|
+
export interface ProviderDescriptor {
|
|
17
|
+
/** Profile entry id addressed by the settings service. */
|
|
18
|
+
readonly ns: string;
|
|
19
|
+
/** Revision `describe()` returned; `update()` refuses a stale one. */
|
|
20
|
+
readonly revision: number;
|
|
21
|
+
/** The live resolved value, including provider defaults. */
|
|
22
|
+
readonly value?: unknown;
|
|
23
|
+
/** The profile patch's own raw value for this entry. */
|
|
24
|
+
readonly user?: unknown;
|
|
25
|
+
}
|
|
26
|
+
/** The part of the settings service this plugin uses. */
|
|
27
|
+
export interface SettingsSeam {
|
|
28
|
+
/** Whether the active profile accepts configuration edits. */
|
|
29
|
+
readonly writable: boolean;
|
|
30
|
+
/** Read the active namespaces with their revisions and layer values. */
|
|
31
|
+
describe(): readonly ProviderDescriptor[];
|
|
32
|
+
/** Write the namespace's volatile fields, guarded by the revision. */
|
|
33
|
+
update(ns: string, patch: object, expectedRevision?: number): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
/** What one pass did. */
|
|
36
|
+
export interface ApplyResult {
|
|
37
|
+
readonly kind: "applied" | "pending" | "unchanged" | "unavailable" | "readonly";
|
|
38
|
+
readonly namespace: string;
|
|
39
|
+
readonly changes: readonly Change[];
|
|
40
|
+
readonly skipped: readonly Skip[];
|
|
41
|
+
}
|
|
42
|
+
/** One pass's inputs. */
|
|
43
|
+
export interface ApplyOptions {
|
|
44
|
+
/** The configurable namespace whose `providers` field is written. */
|
|
45
|
+
readonly namespace: string;
|
|
46
|
+
/** The route/model pairs to apply. */
|
|
47
|
+
readonly intents: readonly Intent[];
|
|
48
|
+
/** Plan only; report the pending changes without writing. */
|
|
49
|
+
readonly dryRun?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Plan and, unless `dryRun`, write one namespace's declared reasoning efforts.
|
|
53
|
+
*
|
|
54
|
+
* @param seam - the settings service, or a fake with the same shape.
|
|
55
|
+
* @param options - the namespace, the intents, and whether to write.
|
|
56
|
+
* @returns what the pass did, with the changes and the skips.
|
|
57
|
+
* @throws Whatever `update()` rejected with, other than a revision conflict.
|
|
58
|
+
*/
|
|
59
|
+
export declare function applyIntents(seam: SettingsSeam, options: ApplyOptions): Promise<ApplyResult>;
|
|
60
|
+
//# sourceMappingURL=apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAiB,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAE/E,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,yDAAyD;AACzD,MAAM,WAAW,YAAY;IAC3B,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,wEAAwE;IACxE,QAAQ,IAAI,SAAS,kBAAkB,EAAE,CAAC;IAC1C,sEAAsE;IACtE,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7E;AAED,yBAAyB;AACzB,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC;IAChF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,SAAS,IAAI,EAAE,CAAC;CACnC;AAED,yBAAyB;AACzB,MAAM,WAAW,YAAY;IAC3B,qEAAqE;IACrE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,6DAA6D;IAC7D,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B;AAaD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,WAAW,CAAC,CA6BtB"}
|
package/lib/apply.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write a planned change through the Harness settings service.
|
|
3
|
+
*
|
|
4
|
+
* This is the only writer. It reads the namespace's descriptor, plans against
|
|
5
|
+
* the profile patch's own `providers` value, and calls `settings.update()` with
|
|
6
|
+
* the whole planned value and the revision `describe()` returned, so the Loader
|
|
7
|
+
* validates the candidate and reconciles the running `llm-pi-ai` adapter
|
|
8
|
+
* exactly as it does a Models-page save — `providers` is a `volatile` field, so
|
|
9
|
+
* the change is live without a restart. Nothing is written when the plan finds
|
|
10
|
+
* no change, and a revision conflict is retried once against a fresh read.
|
|
11
|
+
*
|
|
12
|
+
* @module @indexyz/dsh-custom-provider-reasoning-efforts/apply
|
|
13
|
+
*/
|
|
14
|
+
import { planProviders } from "./plan.js";
|
|
15
|
+
/** Whether an error is the settings service's revision conflict. */
|
|
16
|
+
function isConflict(error) {
|
|
17
|
+
if (error === null || typeof error !== "object")
|
|
18
|
+
return false;
|
|
19
|
+
if (Reflect.get(error, "name") === "SettingsConflictError")
|
|
20
|
+
return true;
|
|
21
|
+
return Reflect.get(error, "code") === "SETTINGS_CONFLICT";
|
|
22
|
+
}
|
|
23
|
+
function descriptorFor(seam, namespace) {
|
|
24
|
+
return seam.describe().find((entry) => entry.ns === namespace);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Plan and, unless `dryRun`, write one namespace's declared reasoning efforts.
|
|
28
|
+
*
|
|
29
|
+
* @param seam - the settings service, or a fake with the same shape.
|
|
30
|
+
* @param options - the namespace, the intents, and whether to write.
|
|
31
|
+
* @returns what the pass did, with the changes and the skips.
|
|
32
|
+
* @throws Whatever `update()` rejected with, other than a revision conflict.
|
|
33
|
+
*/
|
|
34
|
+
export async function applyIntents(seam, options) {
|
|
35
|
+
const namespace = options.namespace;
|
|
36
|
+
if (!seam.writable)
|
|
37
|
+
return { kind: "readonly", namespace, changes: [], skipped: [] };
|
|
38
|
+
let descriptor = descriptorFor(seam, namespace);
|
|
39
|
+
if (descriptor === undefined) {
|
|
40
|
+
return { kind: "unavailable", namespace, changes: [], skipped: [] };
|
|
41
|
+
}
|
|
42
|
+
let plan = planProviders(descriptor.user, options.intents);
|
|
43
|
+
if (plan.changes.length === 0) {
|
|
44
|
+
return { kind: "unchanged", namespace, changes: [], skipped: plan.skipped };
|
|
45
|
+
}
|
|
46
|
+
if (options.dryRun === true) {
|
|
47
|
+
return { kind: "pending", namespace, changes: plan.changes, skipped: plan.skipped };
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
await seam.update(namespace, { providers: plan.providers }, descriptor.revision);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
if (!isConflict(error))
|
|
54
|
+
throw error;
|
|
55
|
+
descriptor = descriptorFor(seam, namespace);
|
|
56
|
+
if (descriptor === undefined) {
|
|
57
|
+
return { kind: "unavailable", namespace, changes: [], skipped: plan.skipped };
|
|
58
|
+
}
|
|
59
|
+
plan = planProviders(descriptor.user, options.intents);
|
|
60
|
+
if (plan.changes.length === 0) {
|
|
61
|
+
return { kind: "unchanged", namespace, changes: [], skipped: plan.skipped };
|
|
62
|
+
}
|
|
63
|
+
await seam.update(namespace, { providers: plan.providers }, descriptor.revision);
|
|
64
|
+
}
|
|
65
|
+
return { kind: "applied", namespace, changes: plan.changes, skipped: plan.skipped };
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=apply.js.map
|
package/lib/apply.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The declaration model this plugin supplies to a hand-written provider route.
|
|
3
|
+
*
|
|
4
|
+
* A route the installed pi-ai catalog does not ship has no catalog entry to
|
|
5
|
+
* inherit reasoning capability from, so every one of its models resolves to
|
|
6
|
+
* `reasoning: false` unless its `llm-pi-ai` profile entry carries
|
|
7
|
+
* `reasoningEfforts`. This module is that field's authoring vocabulary: a
|
|
8
|
+
* declaration names the pi-ai thinking levels a model offers and the wire
|
|
9
|
+
* spelling each level dispatches, or says `false` for a model that cannot
|
|
10
|
+
* reason at all.
|
|
11
|
+
*
|
|
12
|
+
* @module @indexyz/dsh-custom-provider-reasoning-efforts/declarations
|
|
13
|
+
*/
|
|
14
|
+
/** One level's wire spelling; `null` is legal for `off` only and means dispatch sends nothing. */
|
|
15
|
+
export type EffortWire = string | null;
|
|
16
|
+
/** The levels one model offers, keyed by pi-ai thinking level. */
|
|
17
|
+
export type Efforts = Record<string, EffortWire>;
|
|
18
|
+
/** One model's declaration: the levels it offers, or `false` for a model that cannot reason. */
|
|
19
|
+
export type ModelDeclaration = false | Efforts;
|
|
20
|
+
/** Declarations keyed by provider route and then by exact model id. */
|
|
21
|
+
export type Declarations = Record<string, Record<string, ModelDeclaration>>;
|
|
22
|
+
/** A rejected declaration, naming the route, model, and level at fault. */
|
|
23
|
+
export declare class DeclarationError extends Error {
|
|
24
|
+
constructor(message: string);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Validate one model's declaration.
|
|
28
|
+
*
|
|
29
|
+
* A declaration is rejected when it is empty, offers no level beyond `off`,
|
|
30
|
+
* carries `null` or an empty string on a level that is not `off`, or names a
|
|
31
|
+
* key that is not a pi-ai thinking level. Every rejection names the route, the
|
|
32
|
+
* model, and — where one is at fault — the level.
|
|
33
|
+
*
|
|
34
|
+
* @param route - provider route the declaration belongs to, for diagnostics.
|
|
35
|
+
* @param model - exact model id the declaration belongs to, for diagnostics.
|
|
36
|
+
* @param raw - the declaration as the row configuration carried it.
|
|
37
|
+
* @returns the validated declaration.
|
|
38
|
+
* @throws DeclarationError when the declaration cannot be served.
|
|
39
|
+
*/
|
|
40
|
+
export declare function validateModelDeclaration(route: string, model: string, raw: unknown): ModelDeclaration;
|
|
41
|
+
/**
|
|
42
|
+
* Validate every declaration the row configuration carries, so a malformed
|
|
43
|
+
* table fails the plugin's load instead of a later write.
|
|
44
|
+
*
|
|
45
|
+
* @param raw - the `providers` table as the row configuration carried it.
|
|
46
|
+
* @returns the validated table.
|
|
47
|
+
* @throws DeclarationError naming the first declaration that cannot be served.
|
|
48
|
+
*/
|
|
49
|
+
export declare function validateDeclarations(raw: Declarations | undefined): Declarations;
|
|
50
|
+
/**
|
|
51
|
+
* Render a declaration's levels for a human-readable answer.
|
|
52
|
+
*
|
|
53
|
+
* @param value - one validated declaration.
|
|
54
|
+
* @returns the levels in escalation order, or the non-reasoning marker.
|
|
55
|
+
*/
|
|
56
|
+
export declare function describeEfforts(value: ModelDeclaration): string;
|
|
57
|
+
//# sourceMappingURL=declarations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declarations.d.ts","sourceRoot":"","sources":["../declarations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,kGAAkG;AAClG,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;AAEvC,kEAAkE;AAClE,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEjD,gGAAgG;AAChG,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,OAAO,CAAC;AAE/C,uEAAuE;AACvE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAE5E,2EAA2E;AAC3E,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,OAAO,GACX,gBAAgB,CA6ClB;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,GAAG,YAAY,CAoBhF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAI/D"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The declaration model this plugin supplies to a hand-written provider route.
|
|
3
|
+
*
|
|
4
|
+
* A route the installed pi-ai catalog does not ship has no catalog entry to
|
|
5
|
+
* inherit reasoning capability from, so every one of its models resolves to
|
|
6
|
+
* `reasoning: false` unless its `llm-pi-ai` profile entry carries
|
|
7
|
+
* `reasoningEfforts`. This module is that field's authoring vocabulary: a
|
|
8
|
+
* declaration names the pi-ai thinking levels a model offers and the wire
|
|
9
|
+
* spelling each level dispatches, or says `false` for a model that cannot
|
|
10
|
+
* reason at all.
|
|
11
|
+
*
|
|
12
|
+
* @module @indexyz/dsh-custom-provider-reasoning-efforts/declarations
|
|
13
|
+
*/
|
|
14
|
+
import { THINKING_LEVELS, isThinkingLevel } from "./levels.js";
|
|
15
|
+
/** A rejected declaration, naming the route, model, and level at fault. */
|
|
16
|
+
export class DeclarationError extends Error {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "DeclarationError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function isRecord(value) {
|
|
23
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Validate one model's declaration.
|
|
27
|
+
*
|
|
28
|
+
* A declaration is rejected when it is empty, offers no level beyond `off`,
|
|
29
|
+
* carries `null` or an empty string on a level that is not `off`, or names a
|
|
30
|
+
* key that is not a pi-ai thinking level. Every rejection names the route, the
|
|
31
|
+
* model, and — where one is at fault — the level.
|
|
32
|
+
*
|
|
33
|
+
* @param route - provider route the declaration belongs to, for diagnostics.
|
|
34
|
+
* @param model - exact model id the declaration belongs to, for diagnostics.
|
|
35
|
+
* @param raw - the declaration as the row configuration carried it.
|
|
36
|
+
* @returns the validated declaration.
|
|
37
|
+
* @throws DeclarationError when the declaration cannot be served.
|
|
38
|
+
*/
|
|
39
|
+
export function validateModelDeclaration(route, model, raw) {
|
|
40
|
+
const where = `route "${route}" model "${model}"`;
|
|
41
|
+
if (raw === false)
|
|
42
|
+
return false;
|
|
43
|
+
if (!isRecord(raw)) {
|
|
44
|
+
throw new DeclarationError(`${where}: a declaration must be a level table or false, got ${JSON.stringify(raw)}`);
|
|
45
|
+
}
|
|
46
|
+
const entries = Object.entries(raw);
|
|
47
|
+
if (entries.length === 0) {
|
|
48
|
+
throw new DeclarationError(`${where}: the declaration is empty; name the offered levels, or set it to false`);
|
|
49
|
+
}
|
|
50
|
+
const efforts = {};
|
|
51
|
+
let thinking = 0;
|
|
52
|
+
for (const [level, wire] of entries) {
|
|
53
|
+
if (!isThinkingLevel(level)) {
|
|
54
|
+
throw new DeclarationError(`${where}: "${level}" is not a pi-ai thinking level (${THINKING_LEVELS.join(", ")})`);
|
|
55
|
+
}
|
|
56
|
+
if (wire === null) {
|
|
57
|
+
if (level !== "off") {
|
|
58
|
+
throw new DeclarationError(`${where}: level "${level}" needs the wire value dispatch should send; only "off" may stay empty`);
|
|
59
|
+
}
|
|
60
|
+
efforts[level] = null;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (typeof wire !== "string" || wire.length === 0) {
|
|
64
|
+
throw new DeclarationError(`${where}: level "${level}" must be a non-empty string or null, got ${JSON.stringify(wire)}`);
|
|
65
|
+
}
|
|
66
|
+
efforts[level] = wire;
|
|
67
|
+
if (level !== "off")
|
|
68
|
+
thinking += 1;
|
|
69
|
+
}
|
|
70
|
+
if (thinking === 0) {
|
|
71
|
+
throw new DeclarationError(`${where}: the declaration offers no level beyond "off"; name a thinking level, or set it to false`);
|
|
72
|
+
}
|
|
73
|
+
return efforts;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate every declaration the row configuration carries, so a malformed
|
|
77
|
+
* table fails the plugin's load instead of a later write.
|
|
78
|
+
*
|
|
79
|
+
* @param raw - the `providers` table as the row configuration carried it.
|
|
80
|
+
* @returns the validated table.
|
|
81
|
+
* @throws DeclarationError naming the first declaration that cannot be served.
|
|
82
|
+
*/
|
|
83
|
+
export function validateDeclarations(raw) {
|
|
84
|
+
const declarations = {};
|
|
85
|
+
for (const [route, models] of Object.entries(raw ?? {})) {
|
|
86
|
+
if (route.length === 0)
|
|
87
|
+
throw new DeclarationError("a declaration names an empty provider route");
|
|
88
|
+
const validated = {};
|
|
89
|
+
for (const [model, value] of Object.entries(models)) {
|
|
90
|
+
if (model.length === 0) {
|
|
91
|
+
throw new DeclarationError(`route "${route}" names an empty model id`);
|
|
92
|
+
}
|
|
93
|
+
validated[model] = validateModelDeclaration(route, model, value);
|
|
94
|
+
}
|
|
95
|
+
if (Object.keys(validated).length === 0) {
|
|
96
|
+
throw new DeclarationError(`route "${route}" declares no models; remove the empty entry, or name its models`);
|
|
97
|
+
}
|
|
98
|
+
declarations[route] = validated;
|
|
99
|
+
}
|
|
100
|
+
return declarations;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Render a declaration's levels for a human-readable answer.
|
|
104
|
+
*
|
|
105
|
+
* @param value - one validated declaration.
|
|
106
|
+
* @returns the levels in escalation order, or the non-reasoning marker.
|
|
107
|
+
*/
|
|
108
|
+
export function describeEfforts(value) {
|
|
109
|
+
if (value === false)
|
|
110
|
+
return "no reasoning";
|
|
111
|
+
const levels = THINKING_LEVELS.filter((level) => level in value);
|
|
112
|
+
return levels.length === 0 ? "no levels" : levels.join("/");
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=declarations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"declarations.js","sourceRoot":"","sources":["../declarations.ts"],"names":[],"mappings":"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"}
|