create-quorum-router 0.1.12 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/bin/create-quorum-router.js +1 -1
- package/package.json +1 -1
- package/templates/basic/README.md +13 -3
- package/templates/basic/src/calibration.ts +498 -0
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ deno task intake
|
|
|
17
17
|
deno task supabase:status
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Current package version: `create-quorum-router@0.1.
|
|
20
|
+
Current package version: `create-quorum-router@0.1.13`. Releases are published
|
|
21
21
|
from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
|
|
22
22
|
|
|
23
23
|
## What the generated project supports
|
|
@@ -25,11 +25,14 @@ from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
|
|
|
25
25
|
`deno task smoke` is deterministic fixture-only and does not call a provider
|
|
26
26
|
API.
|
|
27
27
|
|
|
28
|
-
`deno task calibration:demo` runs the bundled calibration-by-task API
|
|
29
|
-
deterministic local observations.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
`deno task calibration:demo` runs the bundled flat calibration-by-task API
|
|
29
|
+
against deterministic local observations. For narrower diagnostics, generated
|
|
30
|
+
projects also export `aggregateHierarchicalTaskCalibration()` and
|
|
31
|
+
`resolveHierarchicalTaskCalibration()` with
|
|
32
|
+
`prompt_pattern → task_subtype → task_type` sample-count fallback. Reports are
|
|
33
|
+
advisory-only and are not connected to routing weights, provider eligibility, or
|
|
34
|
+
execution. The command does not call provider APIs; on a new Deno installation,
|
|
35
|
+
its first run resolves the pinned Zod dependency before execution.
|
|
33
36
|
|
|
34
37
|
`deno task intake` is the first real setup command. It detects local provider
|
|
35
38
|
wrappers, checks OAuth/session status, runs safe list-only model inventory where
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# QuorumRouter generated workspace
|
|
2
2
|
|
|
3
3
|
This generated workspace contains the MIT-licensed QuorumRouter current release.
|
|
4
|
-
npm latest targets v0.1.
|
|
4
|
+
npm latest targets v0.1.13.
|
|
5
5
|
|
|
6
6
|
QuorumRouter is **MIT**. It is **open source**. Commercial and production use
|
|
7
7
|
are permitted under the MIT License.
|
|
@@ -54,6 +54,15 @@ not use them to change routing weights, ranks, provider eligibility, quorum, or
|
|
|
54
54
|
execution. The command does not call provider APIs; on a new Deno installation,
|
|
55
55
|
its first run resolves the pinned Zod dependency before execution.
|
|
56
56
|
|
|
57
|
+
For narrower diagnostics, import `aggregateHierarchicalTaskCalibration()` and
|
|
58
|
+
`resolveHierarchicalTaskCalibration()` from `src/calibration.ts`. For example,
|
|
59
|
+
classify an observation with `task_type: "code_review"`,
|
|
60
|
+
`task_subtype: "typescript"`, and `prompt_pattern: "schema-boundary-review"`.
|
|
61
|
+
Resolution checks `prompt_pattern → task_subtype → task_type`, using the first
|
|
62
|
+
group that reaches the configured sample threshold. Groups never cross exact
|
|
63
|
+
provider/model source boundaries, labels must not contain raw prompts, and the
|
|
64
|
+
result remains advisory-only.
|
|
65
|
+
|
|
57
66
|
`intake` detects local provider wrappers, checks OAuth/session status, runs safe
|
|
58
67
|
model inventory/list-only probes where possible, writes local health traces
|
|
59
68
|
under `out/`, and recommends the next command.
|
|
@@ -228,8 +237,9 @@ not paste them into chat/logs and do not commit `.env`.
|
|
|
228
237
|
`src/provider_client.ts` — provider discovery and safe invocation.
|
|
229
238
|
- `src/best_route.ts`, `src/agent_chat.ts` — gated dogfood commands.
|
|
230
239
|
- `src/cost_aware.ts` — estimated-cost budget selection for Best Route.
|
|
231
|
-
- `src/calibration.ts`, `src/calibration_demo.ts` — strict
|
|
232
|
-
aggregation and an offline runnable
|
|
240
|
+
- `src/calibration.ts`, `src/calibration_demo.ts` — strict flat and hierarchical
|
|
241
|
+
advisory aggregation, parent fallback resolution, and an offline runnable
|
|
242
|
+
example.
|
|
233
243
|
- `src/trace.ts`, `src/redact.ts`, `src/schema.ts`, `src/fixture_smoke.ts` —
|
|
234
244
|
trace/redaction/schema/fixture support.
|
|
235
245
|
- `out/.gitkeep` — local output directory placeholder.
|
|
@@ -28,6 +28,10 @@ const ObservationIdSchema = boundedCanonicalText(
|
|
|
28
28
|
MAX_CALIBRATION_IDENTIFIER_LENGTH,
|
|
29
29
|
);
|
|
30
30
|
const TaskTypeSchema = boundedCanonicalText(MAX_CALIBRATION_IDENTIFIER_LENGTH);
|
|
31
|
+
const HierarchicalTaxonomyLabelSchema = boundedCanonicalText(128).refine(
|
|
32
|
+
(value) => /^[a-z0-9](?:[a-z0-9._:/-]{0,126}[a-z0-9])?$/.test(value),
|
|
33
|
+
"hierarchical taxonomy labels must be lowercase canonical ASCII identifiers",
|
|
34
|
+
);
|
|
31
35
|
const SourceLabelSchema = boundedCanonicalText(
|
|
32
36
|
MAX_CALIBRATION_IDENTIFIER_LENGTH,
|
|
33
37
|
);
|
|
@@ -247,3 +251,497 @@ export function aggregateTaskCalibration(
|
|
|
247
251
|
groups,
|
|
248
252
|
});
|
|
249
253
|
}
|
|
254
|
+
|
|
255
|
+
export const MAX_HIERARCHICAL_CALIBRATION_GROUPS =
|
|
256
|
+
MAX_CALIBRATION_OBSERVATIONS * 3;
|
|
257
|
+
|
|
258
|
+
export const HierarchicalCalibrationScopeSchema = z.enum([
|
|
259
|
+
"task_type",
|
|
260
|
+
"task_subtype",
|
|
261
|
+
"prompt_pattern",
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
export const HierarchicalTaskCalibrationObservationSchema = z.object({
|
|
265
|
+
observation_id: ObservationIdSchema,
|
|
266
|
+
task_type: TaskTypeSchema,
|
|
267
|
+
task_subtype: HierarchicalTaxonomyLabelSchema.optional(),
|
|
268
|
+
prompt_pattern: HierarchicalTaxonomyLabelSchema.optional(),
|
|
269
|
+
source: TaskCalibrationSourceSchema,
|
|
270
|
+
evaluation_basis: z.literal("caller_attested_external_ground_truth"),
|
|
271
|
+
correct: z.boolean(),
|
|
272
|
+
confidence: z.number().finite().min(0).max(1),
|
|
273
|
+
evaluated_at: EvaluationTimestampSchema,
|
|
274
|
+
}).strict().superRefine((observation, context) => {
|
|
275
|
+
if (
|
|
276
|
+
observation.prompt_pattern !== undefined &&
|
|
277
|
+
observation.task_subtype === undefined
|
|
278
|
+
) {
|
|
279
|
+
context.addIssue({
|
|
280
|
+
code: "custom",
|
|
281
|
+
message: "prompt_pattern requires task_subtype",
|
|
282
|
+
path: ["prompt_pattern"],
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
export const HierarchicalTaskCalibrationObservationListSchema = z.array(
|
|
288
|
+
HierarchicalTaskCalibrationObservationSchema,
|
|
289
|
+
).max(MAX_CALIBRATION_OBSERVATIONS).superRefine((observations, context) => {
|
|
290
|
+
const ids = new Set<string>();
|
|
291
|
+
for (const [index, observation] of observations.entries()) {
|
|
292
|
+
if (ids.has(observation.observation_id)) {
|
|
293
|
+
context.addIssue({
|
|
294
|
+
code: "custom",
|
|
295
|
+
message: "observation_id must be unique",
|
|
296
|
+
path: [index, "observation_id"],
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
ids.add(observation.observation_id);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
export const HierarchicalTaskCalibrationGroupSchema = z.object({
|
|
304
|
+
scope: HierarchicalCalibrationScopeSchema,
|
|
305
|
+
task_type: TaskTypeSchema,
|
|
306
|
+
task_subtype: HierarchicalTaxonomyLabelSchema.optional(),
|
|
307
|
+
prompt_pattern: HierarchicalTaxonomyLabelSchema.optional(),
|
|
308
|
+
source: TaskCalibrationSourceSchema,
|
|
309
|
+
sample_count: z.number().int().positive().max(MAX_CALIBRATION_OBSERVATIONS),
|
|
310
|
+
accuracy: z.number().finite().min(0).max(1),
|
|
311
|
+
mean_confidence: z.number().finite().min(0).max(1),
|
|
312
|
+
brier_score: z.number().finite().min(0).max(1),
|
|
313
|
+
mean_calibration_bias: z.number().finite().min(-1).max(1),
|
|
314
|
+
sample_status: z.enum(["insufficient", "sufficient"]),
|
|
315
|
+
}).strict().superRefine((group, context) => {
|
|
316
|
+
const validLabels =
|
|
317
|
+
(group.scope === "task_type" && group.task_subtype === undefined &&
|
|
318
|
+
group.prompt_pattern === undefined) ||
|
|
319
|
+
(group.scope === "task_subtype" && group.task_subtype !== undefined &&
|
|
320
|
+
group.prompt_pattern === undefined) ||
|
|
321
|
+
(group.scope === "prompt_pattern" && group.task_subtype !== undefined &&
|
|
322
|
+
group.prompt_pattern !== undefined);
|
|
323
|
+
if (!validLabels) {
|
|
324
|
+
context.addIssue({
|
|
325
|
+
code: "custom",
|
|
326
|
+
message: "scope must match task_subtype and prompt_pattern labels",
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
export const HierarchicalTaskCalibrationReportSchema = z.object({
|
|
332
|
+
schema_version: z.literal("quorum-router.hierarchical-calibration.v1"),
|
|
333
|
+
advisory_only: z.literal(true),
|
|
334
|
+
minimum_sample_count: z.number().int().positive().max(
|
|
335
|
+
MAX_CALIBRATION_OBSERVATIONS,
|
|
336
|
+
),
|
|
337
|
+
groups: z.array(HierarchicalTaskCalibrationGroupSchema).max(
|
|
338
|
+
MAX_HIERARCHICAL_CALIBRATION_GROUPS,
|
|
339
|
+
),
|
|
340
|
+
}).strict().superRefine((report, context) => {
|
|
341
|
+
const groupKeys = new Set<string>();
|
|
342
|
+
for (const [index, group] of report.groups.entries()) {
|
|
343
|
+
const groupKey = JSON.stringify([
|
|
344
|
+
group.scope,
|
|
345
|
+
group.task_type,
|
|
346
|
+
group.task_subtype ?? null,
|
|
347
|
+
group.prompt_pattern ?? null,
|
|
348
|
+
group.source.provider,
|
|
349
|
+
group.source.model,
|
|
350
|
+
]);
|
|
351
|
+
if (groupKeys.has(groupKey)) {
|
|
352
|
+
context.addIssue({
|
|
353
|
+
code: "custom",
|
|
354
|
+
message: "hierarchical calibration report groups must be unique",
|
|
355
|
+
path: ["groups", index],
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
groupKeys.add(groupKey);
|
|
359
|
+
|
|
360
|
+
const expectedStatus = group.sample_count < report.minimum_sample_count
|
|
361
|
+
? "insufficient"
|
|
362
|
+
: "sufficient";
|
|
363
|
+
if (group.sample_status !== expectedStatus) {
|
|
364
|
+
context.addIssue({
|
|
365
|
+
code: "custom",
|
|
366
|
+
message: "sample_status must match the configured sample threshold",
|
|
367
|
+
path: ["groups", index, "sample_status"],
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
if (
|
|
371
|
+
group.mean_calibration_bias !==
|
|
372
|
+
stableMetric(group.mean_confidence - group.accuracy)
|
|
373
|
+
) {
|
|
374
|
+
context.addIssue({
|
|
375
|
+
code: "custom",
|
|
376
|
+
message: "mean_calibration_bias must equal mean_confidence - accuracy",
|
|
377
|
+
path: ["groups", index, "mean_calibration_bias"],
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
export const HierarchicalTaskCalibrationQuerySchema = z.object({
|
|
384
|
+
task_type: TaskTypeSchema,
|
|
385
|
+
task_subtype: HierarchicalTaxonomyLabelSchema.optional(),
|
|
386
|
+
prompt_pattern: HierarchicalTaxonomyLabelSchema.optional(),
|
|
387
|
+
source: TaskCalibrationSourceSchema,
|
|
388
|
+
}).strict().superRefine((query, context) => {
|
|
389
|
+
if (query.prompt_pattern !== undefined && query.task_subtype === undefined) {
|
|
390
|
+
context.addIssue({
|
|
391
|
+
code: "custom",
|
|
392
|
+
message: "prompt_pattern requires task_subtype",
|
|
393
|
+
path: ["prompt_pattern"],
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const HierarchicalCalibrationCandidateSchema = z.object({
|
|
399
|
+
scope: HierarchicalCalibrationScopeSchema,
|
|
400
|
+
sample_count: z.number().int().positive().max(MAX_CALIBRATION_OBSERVATIONS)
|
|
401
|
+
.nullable(),
|
|
402
|
+
sample_status: z.enum(["missing", "insufficient", "sufficient"]),
|
|
403
|
+
}).strict().superRefine((candidate, context) => {
|
|
404
|
+
if (
|
|
405
|
+
(candidate.sample_status === "missing") !==
|
|
406
|
+
(candidate.sample_count === null)
|
|
407
|
+
) {
|
|
408
|
+
context.addIssue({
|
|
409
|
+
code: "custom",
|
|
410
|
+
message: "missing candidates must have a null sample_count",
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
export const HierarchicalTaskCalibrationSelectionSchema = z.object({
|
|
416
|
+
schema_version: z.literal("quorum-router.hierarchical-selection.v1"),
|
|
417
|
+
advisory_only: z.literal(true),
|
|
418
|
+
query: HierarchicalTaskCalibrationQuerySchema,
|
|
419
|
+
requested_scope: HierarchicalCalibrationScopeSchema,
|
|
420
|
+
resolution_status: z.enum(["selected", "no_sufficient_group"]),
|
|
421
|
+
selected_scope: HierarchicalCalibrationScopeSchema.nullable(),
|
|
422
|
+
candidates: z.array(HierarchicalCalibrationCandidateSchema).min(1).max(3),
|
|
423
|
+
selected_group: HierarchicalTaskCalibrationGroupSchema.optional(),
|
|
424
|
+
}).strict().superRefine((selection, context) => {
|
|
425
|
+
const queryScope = selection.query.prompt_pattern !== undefined
|
|
426
|
+
? "prompt_pattern"
|
|
427
|
+
: selection.query.task_subtype !== undefined
|
|
428
|
+
? "task_subtype"
|
|
429
|
+
: "task_type";
|
|
430
|
+
if (selection.requested_scope !== queryScope) {
|
|
431
|
+
context.addIssue({
|
|
432
|
+
code: "custom",
|
|
433
|
+
message: "requested_scope must match the query labels",
|
|
434
|
+
path: ["requested_scope"],
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const expectedScopes = selection.requested_scope === "prompt_pattern"
|
|
439
|
+
? ["prompt_pattern", "task_subtype", "task_type"]
|
|
440
|
+
: selection.requested_scope === "task_subtype"
|
|
441
|
+
? ["task_subtype", "task_type"]
|
|
442
|
+
: ["task_type"];
|
|
443
|
+
if (
|
|
444
|
+
JSON.stringify(selection.candidates.map((candidate) => candidate.scope)) !==
|
|
445
|
+
JSON.stringify(expectedScopes)
|
|
446
|
+
) {
|
|
447
|
+
context.addIssue({
|
|
448
|
+
code: "custom",
|
|
449
|
+
message: "candidates must follow the requested scope fallback order",
|
|
450
|
+
path: ["candidates"],
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const firstSufficient = selection.candidates.find((candidate) =>
|
|
455
|
+
candidate.sample_status === "sufficient"
|
|
456
|
+
);
|
|
457
|
+
if (firstSufficient === undefined) {
|
|
458
|
+
if (
|
|
459
|
+
selection.resolution_status !== "no_sufficient_group" ||
|
|
460
|
+
selection.selected_scope !== null ||
|
|
461
|
+
selection.selected_group !== undefined
|
|
462
|
+
) {
|
|
463
|
+
context.addIssue({
|
|
464
|
+
code: "custom",
|
|
465
|
+
message: "no sufficient candidate must produce no selection",
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
} else if (
|
|
469
|
+
selection.resolution_status !== "selected" ||
|
|
470
|
+
selection.selected_scope !== firstSufficient.scope ||
|
|
471
|
+
selection.selected_group?.scope !== firstSufficient.scope ||
|
|
472
|
+
selection.selected_group.sample_count !== firstSufficient.sample_count ||
|
|
473
|
+
selection.selected_group.sample_status !== "sufficient" ||
|
|
474
|
+
selection.selected_group.task_type !== selection.query.task_type ||
|
|
475
|
+
selection.selected_group.source.provider !==
|
|
476
|
+
selection.query.source.provider ||
|
|
477
|
+
selection.selected_group.source.model !== selection.query.source.model ||
|
|
478
|
+
(firstSufficient.scope !== "task_type" &&
|
|
479
|
+
selection.selected_group.task_subtype !== selection.query.task_subtype) ||
|
|
480
|
+
(firstSufficient.scope === "prompt_pattern" &&
|
|
481
|
+
selection.selected_group.prompt_pattern !==
|
|
482
|
+
selection.query.prompt_pattern)
|
|
483
|
+
) {
|
|
484
|
+
context.addIssue({
|
|
485
|
+
code: "custom",
|
|
486
|
+
message: "selection must use the first sufficient candidate",
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
export const HierarchicalTaskCalibrationDecisionSchema = z.object({
|
|
492
|
+
report: HierarchicalTaskCalibrationReportSchema,
|
|
493
|
+
selection: HierarchicalTaskCalibrationSelectionSchema,
|
|
494
|
+
}).strict().superRefine((decision, context) => {
|
|
495
|
+
const { query } = decision.selection;
|
|
496
|
+
const expectedGroups = decision.selection.candidates.map((candidate) =>
|
|
497
|
+
decision.report.groups.find((group) =>
|
|
498
|
+
group.scope === candidate.scope && group.task_type === query.task_type &&
|
|
499
|
+
group.source.provider === query.source.provider &&
|
|
500
|
+
group.source.model === query.source.model &&
|
|
501
|
+
(candidate.scope === "task_type" ||
|
|
502
|
+
group.task_subtype === query.task_subtype) &&
|
|
503
|
+
(candidate.scope !== "prompt_pattern" ||
|
|
504
|
+
group.prompt_pattern === query.prompt_pattern)
|
|
505
|
+
)
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
decision.selection.candidates.forEach((candidate, index) => {
|
|
509
|
+
const group = expectedGroups[index];
|
|
510
|
+
const expected = group === undefined
|
|
511
|
+
? { sample_count: null, sample_status: "missing" }
|
|
512
|
+
: {
|
|
513
|
+
sample_count: group.sample_count,
|
|
514
|
+
sample_status: group.sample_status,
|
|
515
|
+
};
|
|
516
|
+
if (
|
|
517
|
+
candidate.sample_count !== expected.sample_count ||
|
|
518
|
+
candidate.sample_status !== expected.sample_status
|
|
519
|
+
) {
|
|
520
|
+
context.addIssue({
|
|
521
|
+
code: "custom",
|
|
522
|
+
message: "selection candidate must match the aggregate report",
|
|
523
|
+
path: ["selection", "candidates", index],
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
const expectedSelected = expectedGroups.find((group) =>
|
|
529
|
+
group?.sample_status === "sufficient"
|
|
530
|
+
);
|
|
531
|
+
if (
|
|
532
|
+
JSON.stringify(decision.selection.selected_group) !==
|
|
533
|
+
JSON.stringify(expectedSelected)
|
|
534
|
+
) {
|
|
535
|
+
context.addIssue({
|
|
536
|
+
code: "custom",
|
|
537
|
+
message: "selected_group must match the first sufficient report group",
|
|
538
|
+
path: ["selection", "selected_group"],
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
export type HierarchicalCalibrationScope = z.infer<
|
|
544
|
+
typeof HierarchicalCalibrationScopeSchema
|
|
545
|
+
>;
|
|
546
|
+
export type HierarchicalTaskCalibrationObservation = z.infer<
|
|
547
|
+
typeof HierarchicalTaskCalibrationObservationSchema
|
|
548
|
+
>;
|
|
549
|
+
export type HierarchicalTaskCalibrationGroup = z.infer<
|
|
550
|
+
typeof HierarchicalTaskCalibrationGroupSchema
|
|
551
|
+
>;
|
|
552
|
+
export type HierarchicalTaskCalibrationReport = z.infer<
|
|
553
|
+
typeof HierarchicalTaskCalibrationReportSchema
|
|
554
|
+
>;
|
|
555
|
+
export type HierarchicalTaskCalibrationQuery = z.infer<
|
|
556
|
+
typeof HierarchicalTaskCalibrationQuerySchema
|
|
557
|
+
>;
|
|
558
|
+
export type HierarchicalTaskCalibrationSelection = z.infer<
|
|
559
|
+
typeof HierarchicalTaskCalibrationSelectionSchema
|
|
560
|
+
>;
|
|
561
|
+
export type HierarchicalTaskCalibrationDecision = z.infer<
|
|
562
|
+
typeof HierarchicalTaskCalibrationDecisionSchema
|
|
563
|
+
>;
|
|
564
|
+
|
|
565
|
+
type HierarchicalGroupSeed = {
|
|
566
|
+
scope: HierarchicalCalibrationScope;
|
|
567
|
+
task_type: string;
|
|
568
|
+
task_subtype?: string;
|
|
569
|
+
prompt_pattern?: string;
|
|
570
|
+
source: TaskCalibrationSource;
|
|
571
|
+
observations: HierarchicalTaskCalibrationObservation[];
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
function summarizeHierarchicalGroup(
|
|
575
|
+
seed: HierarchicalGroupSeed,
|
|
576
|
+
minimumSampleCount: number,
|
|
577
|
+
): HierarchicalTaskCalibrationGroup {
|
|
578
|
+
const sample_count = seed.observations.length;
|
|
579
|
+
const correctCount = seed.observations.reduce(
|
|
580
|
+
(total, observation) => total + Number(observation.correct),
|
|
581
|
+
0,
|
|
582
|
+
);
|
|
583
|
+
const confidenceTotal = stableSum(
|
|
584
|
+
seed.observations.map((observation) => observation.confidence),
|
|
585
|
+
);
|
|
586
|
+
const brierTotal = stableSum(seed.observations.map((observation) => {
|
|
587
|
+
const outcome = Number(observation.correct);
|
|
588
|
+
return (observation.confidence - outcome) ** 2;
|
|
589
|
+
}));
|
|
590
|
+
const accuracy = stableMetric(correctCount / sample_count);
|
|
591
|
+
const mean_confidence = stableMetric(confidenceTotal / sample_count);
|
|
592
|
+
|
|
593
|
+
return HierarchicalTaskCalibrationGroupSchema.parse({
|
|
594
|
+
scope: seed.scope,
|
|
595
|
+
task_type: seed.task_type,
|
|
596
|
+
...(seed.task_subtype === undefined
|
|
597
|
+
? {}
|
|
598
|
+
: { task_subtype: seed.task_subtype }),
|
|
599
|
+
...(seed.prompt_pattern === undefined
|
|
600
|
+
? {}
|
|
601
|
+
: { prompt_pattern: seed.prompt_pattern }),
|
|
602
|
+
source: seed.source,
|
|
603
|
+
sample_count,
|
|
604
|
+
accuracy,
|
|
605
|
+
mean_confidence,
|
|
606
|
+
brier_score: stableMetric(brierTotal / sample_count),
|
|
607
|
+
mean_calibration_bias: stableMetric(mean_confidence - accuracy),
|
|
608
|
+
sample_status: sample_count < minimumSampleCount
|
|
609
|
+
? "insufficient"
|
|
610
|
+
: "sufficient",
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export function aggregateHierarchicalTaskCalibration(
|
|
615
|
+
observations: readonly unknown[],
|
|
616
|
+
options: TaskCalibrationOptions = {},
|
|
617
|
+
): HierarchicalTaskCalibrationReport {
|
|
618
|
+
const parsed = HierarchicalTaskCalibrationObservationListSchema.parse(
|
|
619
|
+
observations,
|
|
620
|
+
);
|
|
621
|
+
const { minimum_sample_count } = TaskCalibrationOptionsSchema.parse(options);
|
|
622
|
+
const grouped = new Map<string, HierarchicalGroupSeed>();
|
|
623
|
+
|
|
624
|
+
function add(
|
|
625
|
+
observation: HierarchicalTaskCalibrationObservation,
|
|
626
|
+
scope: HierarchicalCalibrationScope,
|
|
627
|
+
) {
|
|
628
|
+
const task_subtype = scope === "task_type"
|
|
629
|
+
? undefined
|
|
630
|
+
: observation.task_subtype;
|
|
631
|
+
const prompt_pattern = scope === "prompt_pattern"
|
|
632
|
+
? observation.prompt_pattern
|
|
633
|
+
: undefined;
|
|
634
|
+
const key = JSON.stringify([
|
|
635
|
+
scope,
|
|
636
|
+
observation.task_type,
|
|
637
|
+
task_subtype ?? null,
|
|
638
|
+
prompt_pattern ?? null,
|
|
639
|
+
observation.source.provider,
|
|
640
|
+
observation.source.model,
|
|
641
|
+
]);
|
|
642
|
+
const existing = grouped.get(key);
|
|
643
|
+
if (existing) {
|
|
644
|
+
existing.observations.push(observation);
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
grouped.set(key, {
|
|
648
|
+
scope,
|
|
649
|
+
task_type: observation.task_type,
|
|
650
|
+
...(task_subtype === undefined ? {} : { task_subtype }),
|
|
651
|
+
...(prompt_pattern === undefined ? {} : { prompt_pattern }),
|
|
652
|
+
source: observation.source,
|
|
653
|
+
observations: [observation],
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
for (const observation of parsed) {
|
|
658
|
+
add(observation, "task_type");
|
|
659
|
+
if (observation.task_subtype !== undefined) {
|
|
660
|
+
add(observation, "task_subtype");
|
|
661
|
+
}
|
|
662
|
+
if (observation.prompt_pattern !== undefined) {
|
|
663
|
+
add(observation, "prompt_pattern");
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const scopeOrder: Record<HierarchicalCalibrationScope, number> = {
|
|
668
|
+
task_type: 0,
|
|
669
|
+
task_subtype: 1,
|
|
670
|
+
prompt_pattern: 2,
|
|
671
|
+
};
|
|
672
|
+
const groups = [...grouped.values()].map((seed) =>
|
|
673
|
+
summarizeHierarchicalGroup(seed, minimum_sample_count)
|
|
674
|
+
).sort((left, right) =>
|
|
675
|
+
compareCodeUnits(left.task_type, right.task_type) ||
|
|
676
|
+
compareCodeUnits(left.source.provider, right.source.provider) ||
|
|
677
|
+
compareCodeUnits(left.source.model, right.source.model) ||
|
|
678
|
+
scopeOrder[left.scope] - scopeOrder[right.scope] ||
|
|
679
|
+
compareCodeUnits(left.task_subtype ?? "", right.task_subtype ?? "") ||
|
|
680
|
+
compareCodeUnits(left.prompt_pattern ?? "", right.prompt_pattern ?? "")
|
|
681
|
+
);
|
|
682
|
+
|
|
683
|
+
return HierarchicalTaskCalibrationReportSchema.parse({
|
|
684
|
+
schema_version: "quorum-router.hierarchical-calibration.v1",
|
|
685
|
+
advisory_only: true,
|
|
686
|
+
minimum_sample_count,
|
|
687
|
+
groups,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
export function resolveHierarchicalTaskCalibration(
|
|
692
|
+
report: HierarchicalTaskCalibrationReport,
|
|
693
|
+
query: HierarchicalTaskCalibrationQuery,
|
|
694
|
+
): HierarchicalTaskCalibrationSelection {
|
|
695
|
+
const parsedReport = HierarchicalTaskCalibrationReportSchema.parse(report);
|
|
696
|
+
const parsedQuery = HierarchicalTaskCalibrationQuerySchema.parse(query);
|
|
697
|
+
const requested_scope: HierarchicalCalibrationScope =
|
|
698
|
+
parsedQuery.prompt_pattern !== undefined
|
|
699
|
+
? "prompt_pattern"
|
|
700
|
+
: parsedQuery.task_subtype !== undefined
|
|
701
|
+
? "task_subtype"
|
|
702
|
+
: "task_type";
|
|
703
|
+
const scopes: HierarchicalCalibrationScope[] = requested_scope ===
|
|
704
|
+
"prompt_pattern"
|
|
705
|
+
? ["prompt_pattern", "task_subtype", "task_type"]
|
|
706
|
+
: requested_scope === "task_subtype"
|
|
707
|
+
? ["task_subtype", "task_type"]
|
|
708
|
+
: ["task_type"];
|
|
709
|
+
|
|
710
|
+
const matchingGroups = scopes.map((scope) =>
|
|
711
|
+
parsedReport.groups.find((group) =>
|
|
712
|
+
group.scope === scope && group.task_type === parsedQuery.task_type &&
|
|
713
|
+
group.source.provider === parsedQuery.source.provider &&
|
|
714
|
+
group.source.model === parsedQuery.source.model &&
|
|
715
|
+
(scope === "task_type" ||
|
|
716
|
+
group.task_subtype === parsedQuery.task_subtype) &&
|
|
717
|
+
(scope !== "prompt_pattern" ||
|
|
718
|
+
group.prompt_pattern === parsedQuery.prompt_pattern)
|
|
719
|
+
)
|
|
720
|
+
);
|
|
721
|
+
const candidates = scopes.map((scope, index) => {
|
|
722
|
+
const group = matchingGroups[index];
|
|
723
|
+
return group === undefined
|
|
724
|
+
? { scope, sample_count: null, sample_status: "missing" as const }
|
|
725
|
+
: {
|
|
726
|
+
scope,
|
|
727
|
+
sample_count: group.sample_count,
|
|
728
|
+
sample_status: group.sample_status,
|
|
729
|
+
};
|
|
730
|
+
});
|
|
731
|
+
const selected_group = matchingGroups.find((group) =>
|
|
732
|
+
group?.sample_status === "sufficient"
|
|
733
|
+
);
|
|
734
|
+
|
|
735
|
+
return HierarchicalTaskCalibrationSelectionSchema.parse({
|
|
736
|
+
schema_version: "quorum-router.hierarchical-selection.v1",
|
|
737
|
+
advisory_only: true,
|
|
738
|
+
query: parsedQuery,
|
|
739
|
+
requested_scope,
|
|
740
|
+
resolution_status: selected_group === undefined
|
|
741
|
+
? "no_sufficient_group"
|
|
742
|
+
: "selected",
|
|
743
|
+
selected_scope: selected_group?.scope ?? null,
|
|
744
|
+
candidates,
|
|
745
|
+
...(selected_group === undefined ? {} : { selected_group }),
|
|
746
|
+
});
|
|
747
|
+
}
|