@klhapp/skillmux 1.5.2 → 1.7.0
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 +21 -0
- package/README.md +7 -7
- package/config.example.toml +5 -0
- package/config.remote.example.toml +4 -7
- package/docs/README.md +4 -4
- package/docs/assets/architecture.svg +1 -1
- package/docs/cli.md +4 -45
- package/docs/concepts.md +12 -14
- package/docs/configuration.md +10 -11
- package/docs/deployment.md +11 -8
- package/docs/getting-started.md +1 -1
- package/docs/mcp-routing.md +28 -23
- package/docs/ranked-shortlist-migration.md +160 -0
- package/docs/schema.json +37 -139
- package/docs/skill-management.md +7 -2
- package/docs/troubleshooting.md +12 -14
- package/package.json +1 -1
- package/src/adapters.ts +1 -422
- package/src/audit.ts +1 -3
- package/src/cli.ts +19 -229
- package/src/completions.ts +0 -4
- package/src/config-service.ts +19 -32
- package/src/config-watcher.ts +2 -6
- package/src/config.ts +61 -60
- package/src/db.ts +32 -18
- package/src/doctor.ts +1 -84
- package/src/eval.ts +143 -60
- package/src/init.ts +4 -5
- package/src/metrics.ts +1 -13
- package/src/router-core.ts +42 -149
- package/src/server.ts +13 -27
- package/src/stats.ts +126 -57
- package/src/types.ts +8 -39
- package/docs/calibration.md +0 -162
- package/src/calibrate.ts +0 -1594
- package/src/config-mutation.ts +0 -65
- package/src/dataset-generator.ts +0 -119
- package/src/decision.ts +0 -45
package/src/adapters.ts
CHANGED
|
@@ -1,25 +1,5 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
applyCalibrationRun,
|
|
5
|
-
computeCorpusFingerprint,
|
|
6
|
-
createInitialCalibrationRun,
|
|
7
|
-
finalizeCalibrationRun,
|
|
8
|
-
getCalibrationObservations,
|
|
9
|
-
getCalibrationRun,
|
|
10
|
-
insertCalibrationRun,
|
|
11
|
-
listCalibrationRuns,
|
|
12
|
-
loadDecisionCasesFromFile,
|
|
13
|
-
openCalibrateDb,
|
|
14
|
-
runCalibration,
|
|
15
|
-
saveCalibrationObservation,
|
|
16
|
-
summarizeDatasetProvenance,
|
|
17
|
-
type CalibrationResult,
|
|
18
|
-
type QueryObservation,
|
|
19
|
-
} from "./calibrate";
|
|
20
1
|
import { createClients } from "./clients";
|
|
21
|
-
import {
|
|
22
|
-
import { openIndex } from "./db";
|
|
2
|
+
import { expandHome, loadConfig, resolveConfigPath } from "./config";
|
|
23
3
|
import { CliError } from "./output";
|
|
24
4
|
import {
|
|
25
5
|
computeHash,
|
|
@@ -35,18 +15,11 @@ import {
|
|
|
35
15
|
type SetConfigResult,
|
|
36
16
|
} from "./config-service";
|
|
37
17
|
import type { ResolvedTarget } from "./context";
|
|
38
|
-
import {
|
|
39
|
-
configure,
|
|
40
|
-
retrieveAndRerankSnapshot,
|
|
41
|
-
syncVaultIfNeeded,
|
|
42
|
-
type StageTiming,
|
|
43
|
-
} from "./router-core";
|
|
44
18
|
import type { Clients, Config } from "./types";
|
|
45
19
|
|
|
46
20
|
export interface Capabilities {
|
|
47
21
|
config_read: boolean;
|
|
48
22
|
config_write: boolean;
|
|
49
|
-
calibration: boolean;
|
|
50
23
|
persistence: "writable" | "externally_managed";
|
|
51
24
|
reloadable_keys: string[];
|
|
52
25
|
restart_required_keys: string[];
|
|
@@ -58,43 +31,6 @@ export interface TargetAdapterOptions {
|
|
|
58
31
|
clients?: Clients;
|
|
59
32
|
}
|
|
60
33
|
|
|
61
|
-
/**
|
|
62
|
-
* Aggregate timing data collected during a single calibrateRun invocation.
|
|
63
|
-
*
|
|
64
|
-
* All durations are in milliseconds and are non-negative.
|
|
65
|
-
*
|
|
66
|
-
* Cumulative fields (cumulative_embedding_ms, cumulative_lexical_ms,
|
|
67
|
-
* cumulative_vector_ms, cumulative_reranker_ms, cumulative_checkpoint_ms)
|
|
68
|
-
* represent total worker time summed across all concurrent query retrievals
|
|
69
|
-
* or checkpoint writes. Because queries run concurrently, the sum of these
|
|
70
|
-
* cumulative fields may exceed wall_ms — they measure how much worker time
|
|
71
|
-
* each stage consumed, not how much wall-clock time it contributed.
|
|
72
|
-
*/
|
|
73
|
-
export interface CalibrationTimingSummary {
|
|
74
|
-
/** Total number of dataset cases. */
|
|
75
|
-
cases_total: number;
|
|
76
|
-
/** Cases actually retrieved in this invocation (not reused from a prior run). */
|
|
77
|
-
cases_executed: number;
|
|
78
|
-
/** Cases loaded from a prior interrupted run (resume observations). */
|
|
79
|
-
cases_reused: number;
|
|
80
|
-
/** Wall-clock duration of the full calibrateRun operation (ms). */
|
|
81
|
-
wall_ms: number;
|
|
82
|
-
/** Duration of the one-time vault synchronization before retrieval (ms). */
|
|
83
|
-
vault_sync_ms: number;
|
|
84
|
-
/** Cumulative worker time spent in embedding across all queries (ms). */
|
|
85
|
-
cumulative_embedding_ms: number;
|
|
86
|
-
/** Cumulative worker time spent in lexical search across all queries (ms). */
|
|
87
|
-
cumulative_lexical_ms: number;
|
|
88
|
-
/** Cumulative worker time spent in vector search across all queries (ms). */
|
|
89
|
-
cumulative_vector_ms: number;
|
|
90
|
-
/** Cumulative worker time spent in reranking across all queries (ms). */
|
|
91
|
-
cumulative_reranker_ms: number;
|
|
92
|
-
/** Cumulative worker time spent writing observation checkpoints (ms). */
|
|
93
|
-
cumulative_checkpoint_ms: number;
|
|
94
|
-
/** Duration of threshold selection and test-split certification (ms). */
|
|
95
|
-
policy_evaluation_ms: number;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
34
|
export interface TargetAdapter {
|
|
99
35
|
getCapabilities(): Promise<Capabilities>;
|
|
100
36
|
getConfigShow(): Promise<{ effective: Config; sources: Record<string, string>; active_revision: string }>;
|
|
@@ -103,23 +39,6 @@ export interface TargetAdapter {
|
|
|
103
39
|
configDiff(): Promise<{ diff: Record<string, { prior: unknown; resulting: unknown }> }>;
|
|
104
40
|
configSet(key: string, rawValStr: string, opts?: { dryRun?: boolean }): Promise<SetConfigResult>;
|
|
105
41
|
configStatus(): Promise<ConfigStatusResponse>;
|
|
106
|
-
calibrateRun(opts?: {
|
|
107
|
-
datasetPath?: string;
|
|
108
|
-
minAutoMatchPrecision?: number;
|
|
109
|
-
minRetrievalRecallAtK?: number;
|
|
110
|
-
minDeliveredShortlistRecallAtK?: number;
|
|
111
|
-
minAutoMatchCount?: number;
|
|
112
|
-
concurrency?: number;
|
|
113
|
-
resumeRunId?: string;
|
|
114
|
-
onProgress?: (completed: number, total: number) => void;
|
|
115
|
-
/** Opt-in timing collection. When true, onTimingSummary is called after a non-throwing result. */
|
|
116
|
-
timing?: boolean;
|
|
117
|
-
/** Called after calibration completes or fails-gates (not called on throws). */
|
|
118
|
-
onTimingSummary?: (summary: CalibrationTimingSummary) => void;
|
|
119
|
-
}): Promise<{ run_id?: string; result?: CalibrationResult }>;
|
|
120
|
-
calibrateList(): Promise<any[]>;
|
|
121
|
-
calibrateShow(runId: string): Promise<any>;
|
|
122
|
-
calibrateApply(runId: string): Promise<any>;
|
|
123
42
|
}
|
|
124
43
|
|
|
125
44
|
export function isLoopbackHost(hostname: string): boolean {
|
|
@@ -146,7 +65,6 @@ export class LocalAdapter implements TargetAdapter {
|
|
|
146
65
|
return {
|
|
147
66
|
config_read: true,
|
|
148
67
|
config_write: !isExternallyManaged,
|
|
149
|
-
calibration: true,
|
|
150
68
|
persistence: isExternallyManaged ? "externally_managed" : "writable",
|
|
151
69
|
reloadable_keys: RELOADABLE_KEYS,
|
|
152
70
|
restart_required_keys: RESTART_REQUIRED_KEYS,
|
|
@@ -198,306 +116,6 @@ export class LocalAdapter implements TargetAdapter {
|
|
|
198
116
|
async configStatus(): Promise<ConfigStatusResponse> {
|
|
199
117
|
return getLocalConfigStatus(this.configPath);
|
|
200
118
|
}
|
|
201
|
-
|
|
202
|
-
async calibrateRun(opts?: {
|
|
203
|
-
datasetPath?: string;
|
|
204
|
-
minAutoMatchPrecision?: number;
|
|
205
|
-
minRetrievalRecallAtK?: number;
|
|
206
|
-
minDeliveredShortlistRecallAtK?: number;
|
|
207
|
-
minAutoMatchCount?: number;
|
|
208
|
-
concurrency?: number;
|
|
209
|
-
resumeRunId?: string;
|
|
210
|
-
onProgress?: (completed: number, total: number) => void;
|
|
211
|
-
timing?: boolean;
|
|
212
|
-
onTimingSummary?: (summary: CalibrationTimingSummary) => void;
|
|
213
|
-
}): Promise<{ run_id?: string; result?: CalibrationResult }> {
|
|
214
|
-
const collectTiming = opts?.timing === true;
|
|
215
|
-
const wallStart = collectTiming ? performance.now() : 0;
|
|
216
|
-
|
|
217
|
-
const config = await loadConfig(this.configPath);
|
|
218
|
-
const clients = this.clients ?? createClients(config);
|
|
219
|
-
configure({ config, clients });
|
|
220
|
-
|
|
221
|
-
// Measure vault synchronization
|
|
222
|
-
let vault_sync_ms = 0;
|
|
223
|
-
if (collectTiming) {
|
|
224
|
-
const t0 = performance.now();
|
|
225
|
-
await syncVaultIfNeeded();
|
|
226
|
-
vault_sync_ms = Math.max(0, performance.now() - t0);
|
|
227
|
-
} else {
|
|
228
|
-
await syncVaultIfNeeded();
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const candidateLimit =
|
|
232
|
-
config.output?.ambiguous_candidate_limit ?? config.thresholds?.candidate_limit ?? 5;
|
|
233
|
-
const datasetFile = opts?.datasetPath ?? join(expandHome(config.state_dir), "queries.json");
|
|
234
|
-
const indexDb = openIndex(expandHome(config.state_dir));
|
|
235
|
-
let indexedSkills: Array<{ skill_id: string; content_sha256: string }>;
|
|
236
|
-
let corpusFingerprint: string;
|
|
237
|
-
try {
|
|
238
|
-
indexedSkills = indexDb
|
|
239
|
-
.query("SELECT skill_id, content_sha256 FROM skills ORDER BY skill_id")
|
|
240
|
-
.all() as Array<{ skill_id: string; content_sha256: string }>;
|
|
241
|
-
corpusFingerprint = computeCorpusFingerprint(indexDb);
|
|
242
|
-
} finally {
|
|
243
|
-
indexDb.close();
|
|
244
|
-
}
|
|
245
|
-
const cases = loadDecisionCasesFromFile(
|
|
246
|
-
datasetFile,
|
|
247
|
-
indexedSkills.map((skill) => skill.skill_id),
|
|
248
|
-
);
|
|
249
|
-
const fingerprint = rerankerFingerprint(config);
|
|
250
|
-
if (!fingerprint) {
|
|
251
|
-
throw new Error("A configured remote reranker is required to record calibration.");
|
|
252
|
-
}
|
|
253
|
-
const datasetText = await Bun.file(datasetFile).text();
|
|
254
|
-
const datasetHash = createHash("sha256").update(datasetText).digest("hex");
|
|
255
|
-
const embedFp = embeddingFingerprint(config);
|
|
256
|
-
const recallSettings = {
|
|
257
|
-
k_lexical: config.recall.k_lexical,
|
|
258
|
-
k_vector: config.recall.k_vector,
|
|
259
|
-
k_rerank: config.recall.k_rerank ?? Math.min(10, config.recall.k_lexical + config.recall.k_vector),
|
|
260
|
-
};
|
|
261
|
-
const minAutoMatchPrecision = opts?.minAutoMatchPrecision ?? 0.99;
|
|
262
|
-
const minAutoMatchCount = opts?.minAutoMatchCount ?? 30;
|
|
263
|
-
const minDeliveredShortlistRecallAtK =
|
|
264
|
-
opts?.minDeliveredShortlistRecallAtK ??
|
|
265
|
-
opts?.minRetrievalRecallAtK ??
|
|
266
|
-
0.95;
|
|
267
|
-
const minShortlistRecallAt5 = opts?.minRetrievalRecallAtK ?? 0.95;
|
|
268
|
-
const concurrency = opts?.concurrency ?? 4;
|
|
269
|
-
|
|
270
|
-
const db = openCalibrateDb(expandHome(config.state_dir));
|
|
271
|
-
let runId: string;
|
|
272
|
-
let initialObservations: Map<number, QueryObservation> | undefined = undefined;
|
|
273
|
-
|
|
274
|
-
// Cumulative timing accumulators (only used when collectTiming=true)
|
|
275
|
-
let cumulative_embedding_ms = 0;
|
|
276
|
-
let cumulative_lexical_ms = 0;
|
|
277
|
-
let cumulative_vector_ms = 0;
|
|
278
|
-
let cumulative_reranker_ms = 0;
|
|
279
|
-
let cumulative_checkpoint_ms = 0;
|
|
280
|
-
|
|
281
|
-
try {
|
|
282
|
-
if (opts?.resumeRunId) {
|
|
283
|
-
runId = opts.resumeRunId;
|
|
284
|
-
const existingRun = getCalibrationRun(db, runId);
|
|
285
|
-
if (!existingRun) {
|
|
286
|
-
throw new Error(`Calibration run "${runId}" not found`);
|
|
287
|
-
}
|
|
288
|
-
if (existingRun.status !== "running") {
|
|
289
|
-
throw new Error(`Cannot resume completed calibration run "${runId}"`);
|
|
290
|
-
}
|
|
291
|
-
if (existingRun.dataset_hash !== datasetHash) {
|
|
292
|
-
throw new Error("Dataset hash mismatch: cannot resume run with a different dataset");
|
|
293
|
-
}
|
|
294
|
-
if (existingRun.corpus_fingerprint !== corpusFingerprint) {
|
|
295
|
-
throw new Error("Corpus fingerprint mismatch: cannot resume run with modified vault skills");
|
|
296
|
-
}
|
|
297
|
-
if (existingRun.embedding_fingerprint !== embedFp) {
|
|
298
|
-
throw new Error("Embedding fingerprint mismatch: cannot resume run with different embedding configuration");
|
|
299
|
-
}
|
|
300
|
-
if (existingRun.reranker_fingerprint !== fingerprint) {
|
|
301
|
-
throw new Error("Reranker fingerprint mismatch: cannot resume run with different reranker configuration");
|
|
302
|
-
}
|
|
303
|
-
if (existingRun.candidate_limit !== candidateLimit) {
|
|
304
|
-
throw new Error("Candidate limit mismatch: cannot resume run with different candidate limit");
|
|
305
|
-
}
|
|
306
|
-
if (
|
|
307
|
-
existingRun.recall_settings &&
|
|
308
|
-
JSON.stringify(existingRun.recall_settings) !== JSON.stringify(recallSettings)
|
|
309
|
-
) {
|
|
310
|
-
throw new Error("Recall settings mismatch: cannot resume run with different recall parameters");
|
|
311
|
-
}
|
|
312
|
-
if (existingRun.min_auto_match_precision !== minAutoMatchPrecision) {
|
|
313
|
-
throw new Error("Certification gate mismatch: min_auto_match_precision differs from original run");
|
|
314
|
-
}
|
|
315
|
-
if ((existingRun.min_auto_match_count ?? 1) !== minAutoMatchCount) {
|
|
316
|
-
throw new Error("Certification gate mismatch: min_auto_match_count differs from original run");
|
|
317
|
-
}
|
|
318
|
-
if (
|
|
319
|
-
(existingRun.min_delivered_shortlist_recall_at_k ?? existingRun.min_shortlist_recall_at_5) !==
|
|
320
|
-
minDeliveredShortlistRecallAtK
|
|
321
|
-
) {
|
|
322
|
-
throw new Error(
|
|
323
|
-
"Certification gate mismatch: min_delivered_shortlist_recall_at_k differs from original run",
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
if (existingRun.min_shortlist_recall_at_5 !== minShortlistRecallAt5) {
|
|
327
|
-
throw new Error("Certification gate mismatch: min_retrieval_recall_at_k differs from original run");
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
initialObservations = getCalibrationObservations(db, runId);
|
|
331
|
-
if (initialObservations.size === 0 && existingRun.observations && existingRun.observations.length > 0) {
|
|
332
|
-
existingRun.observations.forEach((obs, idx) => initialObservations!.set(idx, obs));
|
|
333
|
-
}
|
|
334
|
-
} else {
|
|
335
|
-
runId = `run_${crypto.randomUUID()}`;
|
|
336
|
-
createInitialCalibrationRun(db, {
|
|
337
|
-
run_id: runId,
|
|
338
|
-
created_at: new Date().toISOString(),
|
|
339
|
-
status: "running",
|
|
340
|
-
reranker_fingerprint: fingerprint,
|
|
341
|
-
embedding_fingerprint: embedFp,
|
|
342
|
-
corpus_fingerprint: corpusFingerprint,
|
|
343
|
-
dataset_hash: datasetHash,
|
|
344
|
-
dataset_provenance: summarizeDatasetProvenance(cases),
|
|
345
|
-
recall_settings: recallSettings,
|
|
346
|
-
candidate_limit: candidateLimit,
|
|
347
|
-
min_auto_match_precision: minAutoMatchPrecision,
|
|
348
|
-
min_auto_match_count: minAutoMatchCount,
|
|
349
|
-
min_delivered_shortlist_recall_at_k: minDeliveredShortlistRecallAtK,
|
|
350
|
-
min_shortlist_recall_at_5: minShortlistRecallAt5,
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
const casesReused = initialObservations?.size ?? 0;
|
|
355
|
-
|
|
356
|
-
const onProgress = opts?.onProgress ?? ((completed, total) => {
|
|
357
|
-
process.stderr.write(`Calibration observations: ${completed}/${total}\n`);
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
const retrievalTiming = collectTiming
|
|
361
|
-
? {
|
|
362
|
-
onTiming: (timing: StageTiming) => {
|
|
363
|
-
cumulative_embedding_ms += timing.embedding_ms;
|
|
364
|
-
cumulative_lexical_ms += timing.lexical_ms;
|
|
365
|
-
cumulative_vector_ms += timing.vector_ms;
|
|
366
|
-
cumulative_reranker_ms += timing.reranker_ms;
|
|
367
|
-
},
|
|
368
|
-
}
|
|
369
|
-
: undefined;
|
|
370
|
-
|
|
371
|
-
const getRankedCandidates = async (query: string) => {
|
|
372
|
-
const res = await retrieveAndRerankSnapshot(
|
|
373
|
-
{ query, forceLexical: false },
|
|
374
|
-
retrievalTiming,
|
|
375
|
-
);
|
|
376
|
-
if (res.retrieval !== "reranked") {
|
|
377
|
-
throw new Error(
|
|
378
|
-
"Calibration requires successful hybrid retrieval and reranking for every query.",
|
|
379
|
-
);
|
|
380
|
-
}
|
|
381
|
-
return res.candidates.map((candidate) => ({
|
|
382
|
-
skill_id: candidate.skill_id,
|
|
383
|
-
score: candidate.score ?? 0,
|
|
384
|
-
}));
|
|
385
|
-
};
|
|
386
|
-
|
|
387
|
-
let policyEvaluationStart = 0;
|
|
388
|
-
|
|
389
|
-
const result = await runCalibration({
|
|
390
|
-
cases,
|
|
391
|
-
getRankedCandidates,
|
|
392
|
-
reranker: clients.rerank,
|
|
393
|
-
candidateLimit,
|
|
394
|
-
minAutoMatchPrecision,
|
|
395
|
-
minRetrievalRecallAtK: minShortlistRecallAt5,
|
|
396
|
-
minDeliveredShortlistRecallAtK,
|
|
397
|
-
minAutoMatchCount,
|
|
398
|
-
concurrency,
|
|
399
|
-
initialObservations,
|
|
400
|
-
onProgress,
|
|
401
|
-
onObservation: collectTiming
|
|
402
|
-
? (obs, caseIdx) => {
|
|
403
|
-
const t0 = performance.now();
|
|
404
|
-
saveCalibrationObservation(db, runId, caseIdx, obs);
|
|
405
|
-
const t1 = performance.now();
|
|
406
|
-
cumulative_checkpoint_ms += Math.max(0, t1 - t0);
|
|
407
|
-
}
|
|
408
|
-
: (obs, caseIdx) => {
|
|
409
|
-
saveCalibrationObservation(db, runId, caseIdx, obs);
|
|
410
|
-
},
|
|
411
|
-
onObservationsReady: collectTiming
|
|
412
|
-
? () => {
|
|
413
|
-
policyEvaluationStart = performance.now();
|
|
414
|
-
}
|
|
415
|
-
: undefined,
|
|
416
|
-
});
|
|
417
|
-
|
|
418
|
-
// policy_evaluation_ms: threshold selection + certification duration
|
|
419
|
-
const policyEvalEnd = collectTiming ? performance.now() : 0;
|
|
420
|
-
|
|
421
|
-
finalizeCalibrationRun(db, {
|
|
422
|
-
run_id: runId,
|
|
423
|
-
status: result.status,
|
|
424
|
-
failed_reason: result.failed_reason,
|
|
425
|
-
selected_thresholds: result.selected_thresholds,
|
|
426
|
-
tune_metrics: result.tune_metrics,
|
|
427
|
-
test_metrics: result.test_metrics,
|
|
428
|
-
observations: result.observations,
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
// Emit timing summary after a non-throwing result (completed or failed-gates).
|
|
432
|
-
// Never called when calibrateRun throws.
|
|
433
|
-
if (collectTiming && opts?.onTimingSummary) {
|
|
434
|
-
const wall_ms = Math.max(0, performance.now() - wallStart);
|
|
435
|
-
// policy_evaluation_ms covers threshold selection + test-split certification,
|
|
436
|
-
// the internal work runCalibration does after all observations are collected.
|
|
437
|
-
const policy_evaluation_ms = Math.max(0, policyEvalEnd - policyEvaluationStart);
|
|
438
|
-
|
|
439
|
-
const cases_reused = casesReused;
|
|
440
|
-
const cases_total = cases.length;
|
|
441
|
-
const cases_executed = cases_total - cases_reused;
|
|
442
|
-
|
|
443
|
-
opts.onTimingSummary({
|
|
444
|
-
cases_total,
|
|
445
|
-
cases_executed,
|
|
446
|
-
cases_reused,
|
|
447
|
-
wall_ms,
|
|
448
|
-
vault_sync_ms,
|
|
449
|
-
cumulative_embedding_ms,
|
|
450
|
-
cumulative_lexical_ms,
|
|
451
|
-
cumulative_vector_ms,
|
|
452
|
-
cumulative_reranker_ms,
|
|
453
|
-
cumulative_checkpoint_ms,
|
|
454
|
-
policy_evaluation_ms,
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
return { run_id: runId, result };
|
|
459
|
-
} finally {
|
|
460
|
-
db.close();
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
async calibrateList(): Promise<any[]> {
|
|
466
|
-
const config = await loadConfig(this.configPath);
|
|
467
|
-
const db = openCalibrateDb(expandHome(config.state_dir));
|
|
468
|
-
try {
|
|
469
|
-
return listCalibrationRuns(db);
|
|
470
|
-
} finally {
|
|
471
|
-
db.close();
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
async calibrateShow(runId: string): Promise<any> {
|
|
476
|
-
const config = await loadConfig(this.configPath);
|
|
477
|
-
const db = openCalibrateDb(expandHome(config.state_dir));
|
|
478
|
-
try {
|
|
479
|
-
const run = getCalibrationRun(db, runId);
|
|
480
|
-
if (!run) throw new Error(`Calibration run "${runId}" not found`);
|
|
481
|
-
return run;
|
|
482
|
-
} finally {
|
|
483
|
-
db.close();
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
async calibrateApply(runId: string): Promise<any> {
|
|
488
|
-
const config = await loadConfig(this.configPath);
|
|
489
|
-
const db = openCalibrateDb(expandHome(config.state_dir));
|
|
490
|
-
try {
|
|
491
|
-
const run = getCalibrationRun(db, runId);
|
|
492
|
-
if (!run) throw new Error(`Calibration run "${runId}" not found`);
|
|
493
|
-
await applyCalibrationRun(db, runId, expandHome(this.configPath), {
|
|
494
|
-
currentRerankerFingerprint: rerankerFingerprint(config),
|
|
495
|
-
});
|
|
496
|
-
return { ok: true, run_id: runId };
|
|
497
|
-
} finally {
|
|
498
|
-
db.close();
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
119
|
}
|
|
502
120
|
|
|
503
121
|
export class RemoteAdapter implements TargetAdapter {
|
|
@@ -663,45 +281,6 @@ export class RemoteAdapter implements TargetAdapter {
|
|
|
663
281
|
}
|
|
664
282
|
return data.runtime;
|
|
665
283
|
}
|
|
666
|
-
|
|
667
|
-
async calibrateRun(opts?: {
|
|
668
|
-
datasetPath?: string;
|
|
669
|
-
minAutoMatchPrecision?: number;
|
|
670
|
-
minRetrievalRecallAtK?: number;
|
|
671
|
-
minDeliveredShortlistRecallAtK?: number;
|
|
672
|
-
minAutoMatchCount?: number;
|
|
673
|
-
concurrency?: number;
|
|
674
|
-
resumeRunId?: string;
|
|
675
|
-
onProgress?: (completed: number, total: number) => void;
|
|
676
|
-
timing?: boolean;
|
|
677
|
-
onTimingSummary?: (summary: CalibrationTimingSummary) => void;
|
|
678
|
-
}): Promise<{ run_id?: string; result?: CalibrationResult }> {
|
|
679
|
-
void opts;
|
|
680
|
-
throw this.remoteCalibrationNotImplemented();
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
async calibrateList(): Promise<any[]> {
|
|
685
|
-
throw this.remoteCalibrationNotImplemented();
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
async calibrateShow(runId: string): Promise<any> {
|
|
689
|
-
void runId;
|
|
690
|
-
throw this.remoteCalibrationNotImplemented();
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
async calibrateApply(runId: string): Promise<any> {
|
|
694
|
-
void runId;
|
|
695
|
-
throw this.remoteCalibrationNotImplemented();
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
private remoteCalibrationNotImplemented(): CliError {
|
|
699
|
-
return new CliError(
|
|
700
|
-
`Remote calibration is not implemented for target ${this.serverUrl}; ` +
|
|
701
|
-
"run `skillmux calibrate` against a local target.",
|
|
702
|
-
2,
|
|
703
|
-
);
|
|
704
|
-
}
|
|
705
284
|
}
|
|
706
285
|
|
|
707
286
|
export function createTargetAdapter(target: ResolvedTarget, opts?: TargetAdapterOptions): TargetAdapter {
|
package/src/audit.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import type { AuditRow } from "./types";
|
|
2
2
|
|
|
3
|
-
/** Shape an audit row to exactly the
|
|
3
|
+
/** Shape an audit row to exactly the AuditRow fields — nothing extra survives. */
|
|
4
4
|
export function buildAuditRow(row: AuditRow): AuditRow {
|
|
5
5
|
const built: AuditRow = {
|
|
6
6
|
id: row.id,
|
|
7
7
|
ts: row.ts,
|
|
8
8
|
query: row.query,
|
|
9
|
-
outcome: row.outcome,
|
|
10
9
|
retrieval: row.retrieval,
|
|
11
10
|
candidates: row.candidates.map((c) => ({ skill_id: c.skill_id, score: c.score })),
|
|
12
|
-
selected_skill_id: row.selected_skill_id,
|
|
13
11
|
latency_ms: row.latency_ms,
|
|
14
12
|
};
|
|
15
13
|
if (row.degraded_from !== undefined && row.degraded_from !== null) {
|