create-quorum-router 0.1.7 → 0.1.9
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
CHANGED
|
@@ -12,11 +12,12 @@ npx --yes create-quorum-router@latest my-quorum-router-demo
|
|
|
12
12
|
cd my-quorum-router-demo
|
|
13
13
|
deno --version
|
|
14
14
|
deno task smoke
|
|
15
|
+
deno task calibration:demo
|
|
15
16
|
deno task intake
|
|
16
17
|
deno task supabase:status
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
Current package version: `create-quorum-router@0.1.
|
|
20
|
+
Current package version: `create-quorum-router@0.1.9`. Releases are published
|
|
20
21
|
from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
|
|
21
22
|
|
|
22
23
|
## What the generated project supports
|
|
@@ -24,6 +25,12 @@ from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
|
|
|
24
25
|
`deno task smoke` is deterministic fixture-only and does not call a provider
|
|
25
26
|
API.
|
|
26
27
|
|
|
28
|
+
`deno task calibration:demo` runs the bundled calibration-by-task API against
|
|
29
|
+
deterministic local observations. The report is advisory-only and is not
|
|
30
|
+
connected to routing weights, provider eligibility, or execution. The command
|
|
31
|
+
does not call provider APIs; on a new Deno installation, its first run resolves
|
|
32
|
+
the pinned Zod dependency before execution.
|
|
33
|
+
|
|
27
34
|
`deno task intake` is the first real setup command. It detects local provider
|
|
28
35
|
wrappers, checks OAuth/session status, runs safe list-only model inventory where
|
|
29
36
|
possible, writes redacted local health artifacts under `out/`, and recommends
|
|
@@ -32,6 +39,7 @@ the next command.
|
|
|
32
39
|
```bash
|
|
33
40
|
deno task check
|
|
34
41
|
deno task smoke
|
|
42
|
+
deno task calibration:demo
|
|
35
43
|
deno task intake
|
|
36
44
|
deno task auth:status
|
|
37
45
|
deno task auth:login
|
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.9.
|
|
5
5
|
|
|
6
6
|
QuorumRouter is **MIT**. It is **open source**. Commercial and production use
|
|
7
7
|
are permitted under the MIT License.
|
|
@@ -37,6 +37,7 @@ deno --version
|
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
deno task smoke
|
|
40
|
+
deno task calibration:demo
|
|
40
41
|
deno task intake
|
|
41
42
|
deno task auth:status
|
|
42
43
|
deno task models:list
|
|
@@ -47,6 +48,12 @@ deno task supabase:status
|
|
|
47
48
|
`smoke` proves the local scaffold runs with deterministic fixtures only. It does
|
|
48
49
|
**not** call a real provider API.
|
|
49
50
|
|
|
51
|
+
`calibration:demo` exercises the bundled calibration-by-task API with local
|
|
52
|
+
fixture observations. Calibration reports are advisory-only: the scaffold does
|
|
53
|
+
not use them to change routing weights, ranks, provider eligibility, quorum, or
|
|
54
|
+
execution. The command does not call provider APIs; on a new Deno installation,
|
|
55
|
+
its first run resolves the pinned Zod dependency before execution.
|
|
56
|
+
|
|
50
57
|
`intake` detects local provider wrappers, checks OAuth/session status, runs safe
|
|
51
58
|
model inventory/list-only probes where possible, writes local health traces
|
|
52
59
|
under `out/`, and recommends the next command.
|
|
@@ -218,6 +225,8 @@ not paste them into chat/logs and do not commit `.env`.
|
|
|
218
225
|
`src/provider_client.ts` — provider discovery and safe invocation.
|
|
219
226
|
- `src/best_route.ts`, `src/agent_chat.ts` — gated dogfood commands.
|
|
220
227
|
- `src/cost_aware.ts` — estimated-cost budget selection for Best Route.
|
|
228
|
+
- `src/calibration.ts`, `src/calibration_demo.ts` — strict advisory calibration
|
|
229
|
+
aggregation and an offline runnable example.
|
|
221
230
|
- `src/trace.ts`, `src/redact.ts`, `src/schema.ts`, `src/fixture_smoke.ts` —
|
|
222
231
|
trace/redaction/schema/fixture support.
|
|
223
232
|
- `out/.gitkeep` — local output directory placeholder.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"tasks": {
|
|
7
7
|
"check": "deno check main.ts src/*.ts",
|
|
8
8
|
"smoke": "deno run main.ts",
|
|
9
|
+
"calibration:demo": "deno run src/calibration_demo.ts",
|
|
9
10
|
"intake": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run=grok,codex,claude,gemini,qwen,devin src/cli.ts intake",
|
|
10
11
|
"auth:status": "deno run --allow-read --allow-env --allow-run=grok,codex,claude,gemini,qwen,devin src/cli.ts auth:status",
|
|
11
12
|
"auth:login": "deno run src/cli.ts auth:login",
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const MAX_CALIBRATION_OBSERVATIONS = 10_000;
|
|
4
|
+
export const MAX_CALIBRATION_IDENTIFIER_LENGTH = 256;
|
|
5
|
+
|
|
6
|
+
function stableMetric(value: number): number {
|
|
7
|
+
const rounded = Number(value.toPrecision(15));
|
|
8
|
+
return rounded === 0 ? 0 : rounded;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function boundedCanonicalText(maxLength: number) {
|
|
12
|
+
return z.string().trim().transform((value) => value.normalize("NFC")).pipe(
|
|
13
|
+
z.string().min(1).max(maxLength).refine(
|
|
14
|
+
(value) => !/[\p{Cc}\p{Cf}\p{Default_Ignorable_Code_Point}]/u.test(value),
|
|
15
|
+
"control and default-ignorable characters are not allowed",
|
|
16
|
+
),
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function hasValidRfc3339Offset(value: string): boolean {
|
|
21
|
+
if (value.endsWith("Z")) return true;
|
|
22
|
+
const match = /[+-](\d{2}):(\d{2})$/.exec(value);
|
|
23
|
+
if (!match) return false;
|
|
24
|
+
return Number(match[1]) <= 23 && Number(match[2]) <= 59;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ObservationIdSchema = boundedCanonicalText(
|
|
28
|
+
MAX_CALIBRATION_IDENTIFIER_LENGTH,
|
|
29
|
+
);
|
|
30
|
+
const TaskTypeSchema = boundedCanonicalText(MAX_CALIBRATION_IDENTIFIER_LENGTH);
|
|
31
|
+
const SourceLabelSchema = boundedCanonicalText(
|
|
32
|
+
MAX_CALIBRATION_IDENTIFIER_LENGTH,
|
|
33
|
+
);
|
|
34
|
+
const EvaluationTimestampSchema = z.string().datetime({ offset: true }).refine(
|
|
35
|
+
hasValidRfc3339Offset,
|
|
36
|
+
"evaluated_at must use a valid RFC 3339 UTC offset",
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export const TaskCalibrationSourceSchema = z.object({
|
|
40
|
+
provider: SourceLabelSchema,
|
|
41
|
+
model: SourceLabelSchema,
|
|
42
|
+
}).strict();
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Structurally validated calibration input supplied by the caller.
|
|
46
|
+
*
|
|
47
|
+
* `evaluation_basis` records an unverified provenance assertion; this schema
|
|
48
|
+
* cannot establish label independence, label quality, example matching, or
|
|
49
|
+
* freedom from leakage. `confidence` must be the model's probability, recorded
|
|
50
|
+
* before the correctness label is observed, that this specific answer is
|
|
51
|
+
* correct. The schema can enforce only the numeric range, not that provenance.
|
|
52
|
+
*/
|
|
53
|
+
export const TaskCalibrationObservationSchema = z.object({
|
|
54
|
+
observation_id: ObservationIdSchema,
|
|
55
|
+
task_type: TaskTypeSchema,
|
|
56
|
+
source: TaskCalibrationSourceSchema,
|
|
57
|
+
evaluation_basis: z.literal("caller_attested_external_ground_truth"),
|
|
58
|
+
correct: z.boolean(),
|
|
59
|
+
confidence: z.number().finite().min(0).max(1),
|
|
60
|
+
evaluated_at: EvaluationTimestampSchema,
|
|
61
|
+
}).strict();
|
|
62
|
+
|
|
63
|
+
export const TaskCalibrationObservationListSchema = z.array(
|
|
64
|
+
TaskCalibrationObservationSchema,
|
|
65
|
+
).max(MAX_CALIBRATION_OBSERVATIONS).superRefine((observations, context) => {
|
|
66
|
+
const ids = new Set<string>();
|
|
67
|
+
for (const [index, observation] of observations.entries()) {
|
|
68
|
+
if (ids.has(observation.observation_id)) {
|
|
69
|
+
context.addIssue({
|
|
70
|
+
code: "custom",
|
|
71
|
+
message: "observation_id must be unique",
|
|
72
|
+
path: [index, "observation_id"],
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
ids.add(observation.observation_id);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
export const TaskCalibrationOptionsSchema = z.object({
|
|
80
|
+
minimum_sample_count: z.number().finite().int().positive().max(
|
|
81
|
+
MAX_CALIBRATION_OBSERVATIONS,
|
|
82
|
+
).default(20),
|
|
83
|
+
}).strict();
|
|
84
|
+
|
|
85
|
+
export const TaskCalibrationGroupSchema = z.object({
|
|
86
|
+
task_type: TaskTypeSchema,
|
|
87
|
+
source: TaskCalibrationSourceSchema,
|
|
88
|
+
sample_count: z.number().int().positive().max(MAX_CALIBRATION_OBSERVATIONS),
|
|
89
|
+
accuracy: z.number().finite().min(0).max(1),
|
|
90
|
+
mean_confidence: z.number().finite().min(0).max(1),
|
|
91
|
+
brier_score: z.number().finite().min(0).max(1),
|
|
92
|
+
mean_calibration_bias: z.number().finite().min(-1).max(1),
|
|
93
|
+
// "sufficient" means only that the configured sample-count threshold is met.
|
|
94
|
+
sample_status: z.enum(["insufficient", "sufficient"]),
|
|
95
|
+
}).strict();
|
|
96
|
+
|
|
97
|
+
export const TaskCalibrationReportSchema = z.object({
|
|
98
|
+
schema_version: z.literal("quorum-router.calibration-by-task.v1"),
|
|
99
|
+
advisory_only: z.literal(true),
|
|
100
|
+
minimum_sample_count: z.number().int().positive().max(
|
|
101
|
+
MAX_CALIBRATION_OBSERVATIONS,
|
|
102
|
+
),
|
|
103
|
+
groups: z.array(TaskCalibrationGroupSchema).max(MAX_CALIBRATION_OBSERVATIONS),
|
|
104
|
+
}).strict().superRefine((report, context) => {
|
|
105
|
+
const groupKeys = new Set<string>();
|
|
106
|
+
for (const [index, group] of report.groups.entries()) {
|
|
107
|
+
const groupKey = JSON.stringify([
|
|
108
|
+
group.task_type,
|
|
109
|
+
group.source.provider,
|
|
110
|
+
group.source.model,
|
|
111
|
+
]);
|
|
112
|
+
if (groupKeys.has(groupKey)) {
|
|
113
|
+
context.addIssue({
|
|
114
|
+
code: "custom",
|
|
115
|
+
message: "calibration report groups must be unique",
|
|
116
|
+
path: ["groups", index],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
groupKeys.add(groupKey);
|
|
120
|
+
|
|
121
|
+
const expectedStatus = group.sample_count < report.minimum_sample_count
|
|
122
|
+
? "insufficient"
|
|
123
|
+
: "sufficient";
|
|
124
|
+
if (group.sample_status !== expectedStatus) {
|
|
125
|
+
context.addIssue({
|
|
126
|
+
code: "custom",
|
|
127
|
+
message: "sample_status must match the configured sample threshold",
|
|
128
|
+
path: ["groups", index, "sample_status"],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const expectedBias = stableMetric(
|
|
133
|
+
group.mean_confidence - group.accuracy,
|
|
134
|
+
);
|
|
135
|
+
if (group.mean_calibration_bias !== expectedBias) {
|
|
136
|
+
context.addIssue({
|
|
137
|
+
code: "custom",
|
|
138
|
+
message: "mean_calibration_bias must equal mean_confidence - accuracy",
|
|
139
|
+
path: ["groups", index, "mean_calibration_bias"],
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
export type TaskCalibrationSource = z.infer<
|
|
146
|
+
typeof TaskCalibrationSourceSchema
|
|
147
|
+
>;
|
|
148
|
+
export type TaskCalibrationObservation = z.infer<
|
|
149
|
+
typeof TaskCalibrationObservationSchema
|
|
150
|
+
>;
|
|
151
|
+
export type TaskCalibrationOptions = z.input<
|
|
152
|
+
typeof TaskCalibrationOptionsSchema
|
|
153
|
+
>;
|
|
154
|
+
export type TaskCalibrationGroup = z.infer<
|
|
155
|
+
typeof TaskCalibrationGroupSchema
|
|
156
|
+
>;
|
|
157
|
+
export type TaskCalibrationReport = z.infer<
|
|
158
|
+
typeof TaskCalibrationReportSchema
|
|
159
|
+
>;
|
|
160
|
+
|
|
161
|
+
function stableSum(values: readonly number[]): number {
|
|
162
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
163
|
+
let total = 0;
|
|
164
|
+
let compensation = 0;
|
|
165
|
+
|
|
166
|
+
for (const value of sorted) {
|
|
167
|
+
const next = total + value;
|
|
168
|
+
compensation += Math.abs(total) >= Math.abs(value)
|
|
169
|
+
? (total - next) + value
|
|
170
|
+
: (value - next) + total;
|
|
171
|
+
total = next;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return total + compensation;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function compareCodeUnits(left: string, right: string): number {
|
|
178
|
+
if (left < right) return -1;
|
|
179
|
+
if (left > right) return 1;
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function aggregateTaskCalibration(
|
|
184
|
+
observations: readonly unknown[],
|
|
185
|
+
options: TaskCalibrationOptions = {},
|
|
186
|
+
): TaskCalibrationReport {
|
|
187
|
+
const parsedObservations = TaskCalibrationObservationListSchema.parse(
|
|
188
|
+
observations,
|
|
189
|
+
);
|
|
190
|
+
const { minimum_sample_count } = TaskCalibrationOptionsSchema.parse(options);
|
|
191
|
+
const grouped = new Map<string, TaskCalibrationObservation[]>();
|
|
192
|
+
|
|
193
|
+
for (const observation of parsedObservations) {
|
|
194
|
+
const key = JSON.stringify([
|
|
195
|
+
observation.task_type,
|
|
196
|
+
observation.source.provider,
|
|
197
|
+
observation.source.model,
|
|
198
|
+
]);
|
|
199
|
+
const group = grouped.get(key);
|
|
200
|
+
if (group) {
|
|
201
|
+
group.push(observation);
|
|
202
|
+
} else {
|
|
203
|
+
grouped.set(key, [observation]);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const groups = [...grouped.values()].map((group) => {
|
|
208
|
+
const sample_count = group.length;
|
|
209
|
+
const correctCount = group.reduce(
|
|
210
|
+
(total, observation) => total + Number(observation.correct),
|
|
211
|
+
0,
|
|
212
|
+
);
|
|
213
|
+
const confidenceTotal = stableSum(
|
|
214
|
+
group.map((observation) => observation.confidence),
|
|
215
|
+
);
|
|
216
|
+
const brierTotal = stableSum(
|
|
217
|
+
group.map((observation) => {
|
|
218
|
+
const outcome = Number(observation.correct);
|
|
219
|
+
return (observation.confidence - outcome) ** 2;
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
222
|
+
const accuracy = stableMetric(correctCount / sample_count);
|
|
223
|
+
const mean_confidence = stableMetric(confidenceTotal / sample_count);
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
task_type: group[0].task_type,
|
|
227
|
+
source: group[0].source,
|
|
228
|
+
sample_count,
|
|
229
|
+
accuracy,
|
|
230
|
+
mean_confidence,
|
|
231
|
+
brier_score: stableMetric(brierTotal / sample_count),
|
|
232
|
+
mean_calibration_bias: stableMetric(mean_confidence - accuracy),
|
|
233
|
+
sample_status: sample_count < minimum_sample_count
|
|
234
|
+
? "insufficient" as const
|
|
235
|
+
: "sufficient" as const,
|
|
236
|
+
};
|
|
237
|
+
}).sort((left, right) =>
|
|
238
|
+
compareCodeUnits(left.task_type, right.task_type) ||
|
|
239
|
+
compareCodeUnits(left.source.provider, right.source.provider) ||
|
|
240
|
+
compareCodeUnits(left.source.model, right.source.model)
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
return TaskCalibrationReportSchema.parse({
|
|
244
|
+
schema_version: "quorum-router.calibration-by-task.v1",
|
|
245
|
+
advisory_only: true,
|
|
246
|
+
minimum_sample_count,
|
|
247
|
+
groups,
|
|
248
|
+
});
|
|
249
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { aggregateTaskCalibration } from "./calibration.ts";
|
|
2
|
+
|
|
3
|
+
const evaluated_at = "2026-07-13T00:00:00Z";
|
|
4
|
+
const report = aggregateTaskCalibration([
|
|
5
|
+
{
|
|
6
|
+
observation_id: "demo-1",
|
|
7
|
+
task_type: "code-review",
|
|
8
|
+
source: { provider: "OpenAI", model: "example-model" },
|
|
9
|
+
evaluation_basis: "caller_attested_external_ground_truth",
|
|
10
|
+
correct: true,
|
|
11
|
+
confidence: 0.8,
|
|
12
|
+
evaluated_at,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
observation_id: "demo-2",
|
|
16
|
+
task_type: "code-review",
|
|
17
|
+
source: { provider: "OpenAI", model: "example-model" },
|
|
18
|
+
evaluation_basis: "caller_attested_external_ground_truth",
|
|
19
|
+
correct: false,
|
|
20
|
+
confidence: 0.6,
|
|
21
|
+
evaluated_at,
|
|
22
|
+
},
|
|
23
|
+
], { minimum_sample_count: 2 });
|
|
24
|
+
|
|
25
|
+
console.log(JSON.stringify(report, null, 2));
|