@crewhaus/spec 0.2.0 → 0.2.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/dist/index.d.ts +2397 -106
- package/dist/index.js +104 -3
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -241,6 +241,98 @@ const modelTiersBlock = z
|
|
|
241
241
|
})
|
|
242
242
|
.strict()
|
|
243
243
|
.optional();
|
|
244
|
+
/**
|
|
245
|
+
* Adaptive model routing — an opt-in `model_pool` of user-declared candidate
|
|
246
|
+
* models the runtime selects among PER TURN, with a selection `policy` that can
|
|
247
|
+
* improve the more the harness runs. The N-candidate generalisation of
|
|
248
|
+
* `model_tiers` (which is the two-candidate special case), so the two are
|
|
249
|
+
* mutually exclusive on an agent block (enforced by a refine on each shape).
|
|
250
|
+
*
|
|
251
|
+
* The user always declares the SET; the runtime only ever picks WITHIN it —
|
|
252
|
+
* `agent.model` (and the optimizer's model-path exclusion) is untouched, so
|
|
253
|
+
* learning tunes selection policy, never the candidate roster.
|
|
254
|
+
*
|
|
255
|
+
* - `candidates` — ≥2 model-router grammar strings, each with free-form
|
|
256
|
+
* `tags` (e.g. `cheap`, `strong`) the heuristic routes on. Declare
|
|
257
|
+
* cheapest→strongest; tags override that order.
|
|
258
|
+
* - `policy` — `static` (first candidate), `heuristic` (deterministic
|
|
259
|
+
* difficulty routing, the default), or `learned` (reward-scoreboard arm
|
|
260
|
+
* selection that improves with usage).
|
|
261
|
+
* - `objective` — weights for the learned reward (quality / cost / latency);
|
|
262
|
+
* defaults to quality-dominant (0.7 / 0.2 / 0.1).
|
|
263
|
+
* - `routing` — difficulty thresholds (shared with `model_tiers`) plus the
|
|
264
|
+
* `strongTag`/`cheapTag` the heuristic prefers.
|
|
265
|
+
* - `learning` — read only for `policy: learned`: the per-arm exploration
|
|
266
|
+
* floor and the cost/latency reward references.
|
|
267
|
+
*
|
|
268
|
+
* Omitted entirely → single-model behaviour, byte-identical bundles.
|
|
269
|
+
*/
|
|
270
|
+
const modelPoolBlock = z
|
|
271
|
+
.object({
|
|
272
|
+
candidates: z
|
|
273
|
+
.array(z
|
|
274
|
+
.object({
|
|
275
|
+
model: z.string().min(1),
|
|
276
|
+
tags: z.array(z.string().min(1)).default([]),
|
|
277
|
+
})
|
|
278
|
+
.strict())
|
|
279
|
+
.min(2),
|
|
280
|
+
policy: z.enum(["static", "heuristic", "learned"]).default("heuristic"),
|
|
281
|
+
objective: z
|
|
282
|
+
.object({
|
|
283
|
+
quality: z.number().min(0).optional(),
|
|
284
|
+
cost: z.number().min(0).optional(),
|
|
285
|
+
latency: z.number().min(0).optional(),
|
|
286
|
+
})
|
|
287
|
+
.strict()
|
|
288
|
+
.optional(),
|
|
289
|
+
routing: z
|
|
290
|
+
.object({
|
|
291
|
+
contextTokenThreshold: z.number().int().positive().optional(),
|
|
292
|
+
toolsToDefault: z.boolean().optional(),
|
|
293
|
+
firstTurnToDefault: z.boolean().optional(),
|
|
294
|
+
priorToolDensityThreshold: z.number().int().positive().optional(),
|
|
295
|
+
strongTag: z.string().min(1).optional(),
|
|
296
|
+
cheapTag: z.string().min(1).optional(),
|
|
297
|
+
})
|
|
298
|
+
.strict()
|
|
299
|
+
.optional(),
|
|
300
|
+
learning: z
|
|
301
|
+
.object({
|
|
302
|
+
minSamplesPerArm: z.number().int().positive().optional(),
|
|
303
|
+
costRefUsd: z.number().positive().optional(),
|
|
304
|
+
latencyRefMs: z.number().int().positive().optional(),
|
|
305
|
+
})
|
|
306
|
+
.strict()
|
|
307
|
+
.optional(),
|
|
308
|
+
})
|
|
309
|
+
.strict()
|
|
310
|
+
.optional();
|
|
311
|
+
/**
|
|
312
|
+
* Mutual-exclusion refine shared by every agent block that carries model
|
|
313
|
+
* routing: `model_pool` is the superset of both the two-tier router and the
|
|
314
|
+
* ordered failover chain, so declaring it alongside either is an error rather
|
|
315
|
+
* than an ambiguous double-route. (Per-candidate failover chains compose in a
|
|
316
|
+
* later release; this release keeps precedence unambiguous.)
|
|
317
|
+
*/
|
|
318
|
+
function refineModelSelection(agent, ctx) {
|
|
319
|
+
if (agent.model_pool === undefined)
|
|
320
|
+
return;
|
|
321
|
+
if (agent.model_tiers !== undefined) {
|
|
322
|
+
ctx.addIssue({
|
|
323
|
+
code: z.ZodIssueCode.custom,
|
|
324
|
+
message: "agent.model_pool and agent.model_tiers are mutually exclusive — model_pool is the N-candidate generalisation of the two-tier router",
|
|
325
|
+
path: ["model_tiers"],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
if (agent.model_fallbacks !== undefined) {
|
|
329
|
+
ctx.addIssue({
|
|
330
|
+
code: z.ZodIssueCode.custom,
|
|
331
|
+
message: "agent.model_pool and agent.model_fallbacks are mutually exclusive in this release — per-candidate failover chains are a future addition",
|
|
332
|
+
path: ["model_fallbacks"],
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
244
336
|
/**
|
|
245
337
|
* Section 17 — optional override for the model used by
|
|
246
338
|
* `compaction-autocompact` when summarising long conversations. Defaults
|
|
@@ -630,9 +722,12 @@ const cliSchema = z
|
|
|
630
722
|
circuit_breaker: circuitBreakerBlock,
|
|
631
723
|
// Item 26 — opt-in two-tier turn-difficulty router.
|
|
632
724
|
model_tiers: modelTiersBlock,
|
|
725
|
+
// Adaptive model routing — N-candidate pool with a selection policy.
|
|
726
|
+
model_pool: modelPoolBlock,
|
|
633
727
|
sub_agents: subAgentsBlock,
|
|
634
728
|
})
|
|
635
|
-
.strict()
|
|
729
|
+
.strict()
|
|
730
|
+
.superRefine(refineModelSelection),
|
|
636
731
|
tools: z.array(z.string().min(1)).optional(),
|
|
637
732
|
tool_config: toolConfigBlock,
|
|
638
733
|
mcp_servers: mcpServersBlock,
|
|
@@ -751,11 +846,14 @@ const channelAgentSchema = z
|
|
|
751
846
|
circuit_breaker: circuitBreakerBlock,
|
|
752
847
|
// Item 26 — opt-in two-tier turn-difficulty router.
|
|
753
848
|
model_tiers: modelTiersBlock,
|
|
849
|
+
// Adaptive model routing — N-candidate pool with a selection policy.
|
|
850
|
+
model_pool: modelPoolBlock,
|
|
754
851
|
tools: z.array(z.string().min(1)).optional(),
|
|
755
852
|
tool_config: toolConfigBlock,
|
|
756
853
|
sub_agents: subAgentsBlock,
|
|
757
854
|
})
|
|
758
|
-
.strict()
|
|
855
|
+
.strict()
|
|
856
|
+
.superRefine(refineModelSelection);
|
|
759
857
|
const channelSchema = z
|
|
760
858
|
.object({
|
|
761
859
|
name: safeName,
|
|
@@ -850,8 +948,11 @@ const managedAgentSchema = z
|
|
|
850
948
|
circuit_breaker: circuitBreakerBlock,
|
|
851
949
|
// Item 26 — opt-in two-tier turn-difficulty router.
|
|
852
950
|
model_tiers: modelTiersBlock,
|
|
951
|
+
// Adaptive model routing — N-candidate pool with a selection policy.
|
|
952
|
+
model_pool: modelPoolBlock,
|
|
853
953
|
})
|
|
854
|
-
.strict()
|
|
954
|
+
.strict()
|
|
955
|
+
.superRefine(refineModelSelection);
|
|
855
956
|
const managedSchema = z
|
|
856
957
|
.object({
|
|
857
958
|
name: safeName,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crewhaus/spec",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "User-facing spec schema (Zod) + YAML parser",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"test": "bun test src"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@crewhaus/errors": "0.2.
|
|
18
|
+
"@crewhaus/errors": "0.2.1",
|
|
19
19
|
"yaml": "^2.6.0",
|
|
20
20
|
"zod": "^3.23.8"
|
|
21
21
|
},
|