@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/types.ts
CHANGED
|
@@ -5,11 +5,12 @@ export interface RecallConfig {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
export interface OutputConfig {
|
|
8
|
-
|
|
8
|
+
top_k: number;
|
|
9
|
+
max_top_k: number;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
export interface Thresholds {
|
|
12
|
-
/** @deprecated Use output.
|
|
13
|
+
/** @deprecated Use output.top_k instead */
|
|
13
14
|
candidate_limit?: number;
|
|
14
15
|
match_score?: number;
|
|
15
16
|
match_margin?: number;
|
|
@@ -78,8 +79,6 @@ export interface RemoteInferenceConfig {
|
|
|
78
79
|
timeout_ms: number;
|
|
79
80
|
embedding: RemoteEmbeddingConfig;
|
|
80
81
|
reranker?: RemoteRerankerConfig;
|
|
81
|
-
thresholds?: Required<Omit<Thresholds, "candidate_limit">>;
|
|
82
|
-
calibration?: { run_id: string };
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
export type InferenceConfig = LocalInferenceConfig | RemoteInferenceConfig;
|
|
@@ -114,19 +113,15 @@ export interface Config {
|
|
|
114
113
|
local_vault_paths: string[];
|
|
115
114
|
state_dir: string;
|
|
116
115
|
recall: RecallConfig;
|
|
117
|
-
thresholds: Thresholds;
|
|
118
116
|
output: OutputConfig;
|
|
119
117
|
inference: InferenceConfig;
|
|
120
118
|
server?: ServerConfig;
|
|
121
119
|
}
|
|
122
120
|
|
|
123
|
-
export interface
|
|
121
|
+
export interface RankedCandidate {
|
|
122
|
+
rank: number;
|
|
124
123
|
skill_id: string;
|
|
125
|
-
title: string;
|
|
126
124
|
description: string;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export interface RankedCandidate extends Candidate {
|
|
130
125
|
score: number | null;
|
|
131
126
|
}
|
|
132
127
|
|
|
@@ -140,40 +135,16 @@ export type DegradationReason =
|
|
|
140
135
|
| "reranker_unavailable"
|
|
141
136
|
| "reranker_protocol_error";
|
|
142
137
|
|
|
143
|
-
export interface
|
|
144
|
-
outcome: "matched";
|
|
145
|
-
retrieval: "exact" | "reranked";
|
|
146
|
-
degraded_from?: "reranked" | "hybrid";
|
|
147
|
-
degradation_reason?: DegradationReason;
|
|
148
|
-
skill_id: string;
|
|
149
|
-
title: string;
|
|
150
|
-
content_sha256: string;
|
|
151
|
-
score: number;
|
|
152
|
-
margin: number;
|
|
153
|
-
body: string;
|
|
154
|
-
files: string[];
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface AmbiguousResult {
|
|
158
|
-
outcome: "ambiguous";
|
|
138
|
+
export interface ResolveResult {
|
|
159
139
|
retrieval: RetrievalCapability;
|
|
160
140
|
degraded_from?: "reranked" | "hybrid";
|
|
161
141
|
degradation_reason?: DegradationReason;
|
|
162
|
-
candidates:
|
|
142
|
+
candidates: RankedCandidate[];
|
|
163
143
|
}
|
|
164
144
|
|
|
165
|
-
export interface NoMatchResult {
|
|
166
|
-
outcome: "no_match";
|
|
167
|
-
retrieval: RetrievalCapability;
|
|
168
|
-
degraded_from?: "reranked" | "hybrid";
|
|
169
|
-
degradation_reason?: DegradationReason;
|
|
170
|
-
message: string;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export type ResolveResult = MatchedResult | AmbiguousResult | NoMatchResult;
|
|
174
|
-
|
|
175
145
|
export interface ResolveSkillInput {
|
|
176
146
|
query: string;
|
|
147
|
+
top_k?: number;
|
|
177
148
|
/** Test/ops escape hatch: use lexical retrieval only. Not exposed on the MCP wire. */
|
|
178
149
|
forceLexical?: boolean;
|
|
179
150
|
}
|
|
@@ -199,12 +170,10 @@ export interface AuditRow {
|
|
|
199
170
|
id: number;
|
|
200
171
|
ts: string;
|
|
201
172
|
query: string;
|
|
202
|
-
outcome: "matched" | "ambiguous" | "no_match";
|
|
203
173
|
retrieval: RetrievalCapability;
|
|
204
174
|
degraded_from?: "reranked" | "hybrid" | null;
|
|
205
175
|
degradation_reason?: DegradationReason | null;
|
|
206
176
|
candidates: AuditCandidate[];
|
|
207
|
-
selected_skill_id: string | null;
|
|
208
177
|
latency_ms: number;
|
|
209
178
|
}
|
|
210
179
|
|
package/docs/calibration.md
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# Policy calibration
|
|
2
|
-
|
|
3
|
-
Calibration selects the three reranker-score thresholds that turn an ordered
|
|
4
|
-
shortlist into `matched`, `ambiguous`, or `no_match`. It is an operator action,
|
|
5
|
-
not background learning, and it currently runs only against a local Skillmux
|
|
6
|
-
target.
|
|
7
|
-
|
|
8
|
-
Read [MCP routing](mcp-routing.md#retrieval-pipeline) before calibrating a new
|
|
9
|
-
retrieval deployment.
|
|
10
|
-
|
|
11
|
-
## Lifecycle
|
|
12
|
-
|
|
13
|
-
The complete workflow is:
|
|
14
|
-
|
|
15
|
-
```text
|
|
16
|
-
install CLI → configure vault/index/embedding/reranker → obtain labelled dataset
|
|
17
|
-
→ calibrate run → review calibrate show RUN_ID → calibrate apply RUN_ID
|
|
18
|
-
→ live-reloaded policy handles subsequent requests
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
First configure and index the same vault, embedding model, and reranker that
|
|
22
|
-
will serve requests. Supply a reviewed dataset, or generate a starting point
|
|
23
|
-
and review every label:
|
|
24
|
-
|
|
25
|
-
```sh
|
|
26
|
-
skillmux calibrate generate-dataset --out ./eval/queries.json
|
|
27
|
-
skillmux calibrate run --dataset ./eval/queries.json
|
|
28
|
-
skillmux calibrate show RUN_ID
|
|
29
|
-
skillmux calibrate apply RUN_ID
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
Skillmux retrieves candidates and reranks exactly once for each evaluation
|
|
33
|
-
query. It runs four queries at a time by default. Set a different positive
|
|
34
|
-
worker limit with `--concurrency N`. The CLI writes completed-case progress to
|
|
35
|
-
stderr without exposing query text.
|
|
36
|
-
|
|
37
|
-
### Timing report
|
|
38
|
-
|
|
39
|
-
Add `--timing` to any `calibrate run` invocation to write an aggregate
|
|
40
|
-
performance report to **stderr** after the run finishes (with a completed or
|
|
41
|
-
failed-gates result; a thrown error produces no report). Stdout remains valid
|
|
42
|
-
JSON under `--json --timing`.
|
|
43
|
-
|
|
44
|
-
```sh
|
|
45
|
-
skillmux calibrate run --dataset ./eval/queries.json --timing
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
The report uses stable snake_case field names in milliseconds:
|
|
49
|
-
|
|
50
|
-
| Field | Description |
|
|
51
|
-
|---|---|
|
|
52
|
-
| `cases_total` | Total dataset cases |
|
|
53
|
-
| `cases_executed` | Cases retrieved in this invocation |
|
|
54
|
-
| `cases_reused` | Cases loaded from a prior interrupted run (resume) |
|
|
55
|
-
| `wall_ms` | Wall-clock duration of the full calibrateRun operation |
|
|
56
|
-
| `vault_sync_ms` | One-time vault synchronization before retrieval |
|
|
57
|
-
| `cumulative_embedding_ms` | Total worker time in embedding across all queries |
|
|
58
|
-
| `cumulative_lexical_ms` | Total worker time in lexical search across all queries |
|
|
59
|
-
| `cumulative_vector_ms` | Total worker time in vector search across all queries |
|
|
60
|
-
| `cumulative_reranker_ms` | Total worker time in reranking across all queries |
|
|
61
|
-
| `cumulative_checkpoint_ms` | Total worker time writing observation checkpoints |
|
|
62
|
-
| `policy_evaluation_ms` | Threshold selection and test-split certification |
|
|
63
|
-
|
|
64
|
-
**Cumulative vs wall time.** The cumulative fields (`cumulative_embedding_ms`,
|
|
65
|
-
`cumulative_lexical_ms`, `cumulative_vector_ms`, `cumulative_reranker_ms`,
|
|
66
|
-
`cumulative_checkpoint_ms`) are total _worker time_ summed across all concurrent
|
|
67
|
-
query retrievals. Because multiple queries run at the same time, the sum of these
|
|
68
|
-
fields typically exceeds `wall_ms`. They measure how much time each stage
|
|
69
|
-
consumed across all workers, not how much wall-clock time each stage accounted
|
|
70
|
-
for. `cases_executed + cases_reused = cases_total`.
|
|
71
|
-
|
|
72
|
-
Timing collection is fully disabled when `--timing` is absent; it does not affect
|
|
73
|
-
calibration results, resume behavior, checkpoint durability, or JSON schemas.
|
|
74
|
-
Skillmux checkpoints each observation in the calibration evidence database.
|
|
75
|
-
If inference fails or you interrupt the process, find the `running` run with
|
|
76
|
-
`calibrate list` and resume it with the same dataset and certification flags:
|
|
77
|
-
|
|
78
|
-
```sh
|
|
79
|
-
skillmux calibrate run --dataset ./eval/queries.json --resume RUN_ID
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Resume rejects changes to the dataset, corpus, inference models, recall
|
|
83
|
-
settings, candidate limit, or certification gates. After all observations
|
|
84
|
-
exist, Skillmux searches thresholds on the `tune` split and certifies the
|
|
85
|
-
selected policy on the frozen `test` split. Calibration starts only when an
|
|
86
|
-
operator invokes `calibrate run`.
|
|
87
|
-
|
|
88
|
-
The operator owns the labels: supply or review the cases, start the run,
|
|
89
|
-
inspect its evidence, and explicitly apply an acceptable result. A successful
|
|
90
|
-
run never changes live thresholds by itself.
|
|
91
|
-
|
|
92
|
-
## Reading a run
|
|
93
|
-
|
|
94
|
-
A `run_id` identifies one immutable calibration attempt and its evidence.
|
|
95
|
-
`calibrate show RUN_ID` is read-only. It reports:
|
|
96
|
-
|
|
97
|
-
- selected thresholds and tune/test metrics;
|
|
98
|
-
- auto-match precision confidence and sample counts;
|
|
99
|
-
- retrieval and delivered-shortlist recall;
|
|
100
|
-
- a closed failure reason when certification fails;
|
|
101
|
-
- reranker, embedding, corpus, and dataset fingerprints;
|
|
102
|
-
- dataset provenance and the number of human-labelled cases; and
|
|
103
|
-
- the attempt count for the dataset hash.
|
|
104
|
-
|
|
105
|
-
`calibrate apply RUN_ID` accepts only a completed, test-certified run. It
|
|
106
|
-
rechecks the reranker fingerprint, rejects thresholds masked by environment
|
|
107
|
-
variables, atomically updates the TOML file, and lets the config watcher
|
|
108
|
-
activate the new snapshot.
|
|
109
|
-
|
|
110
|
-
## Dataset responsibilities
|
|
111
|
-
|
|
112
|
-
Each case needs a query, expected outcome, relevant skill ids, and a fixed
|
|
113
|
-
`tune` or `test` split. Unknown skill ids are rejected. Keep a skill entirely
|
|
114
|
-
within one split so the test set measures generalization rather than memorized
|
|
115
|
-
skill wording.
|
|
116
|
-
|
|
117
|
-
Generated datasets are scaffolding, not ground truth. Review paraphrases,
|
|
118
|
-
near-miss negatives, and ambiguous cases before using them for certification.
|
|
119
|
-
Audit-derived cases require an explicit human label and provenance. Raw audit
|
|
120
|
-
queries are excluded unless the importer is deliberately configured to retain
|
|
121
|
-
them.
|
|
122
|
-
|
|
123
|
-
## When to recalibrate
|
|
124
|
-
|
|
125
|
-
Re-run calibration after a material change to the corpus, embedding or
|
|
126
|
-
retrieval behavior, reranker adapter or model, or after collecting enough new
|
|
127
|
-
human-labelled feedback. Do not recalibrate per user request. Every rerun gets
|
|
128
|
-
a new `run_id`; the active policy remains unchanged until one is applied.
|
|
129
|
-
|
|
130
|
-
## Local and remote targets
|
|
131
|
-
|
|
132
|
-
Here, `local` and `remote` name CLI administration targets, not inference
|
|
133
|
-
locations or MCP transports. Calibration is local-target-only in this release.
|
|
134
|
-
Local commands operate on the
|
|
135
|
-
configured local vault, index, inference endpoints, dataset path, evidence
|
|
136
|
-
database, and TOML file. Human output always prints `Target: local`; JSON output
|
|
137
|
-
uses `"target": "local"`.
|
|
138
|
-
|
|
139
|
-
Remote servers advertise `"calibration": false`. Every
|
|
140
|
-
`/admin/v1/calibrations` route returns HTTP `501` with
|
|
141
|
-
`error: "not_implemented"`, and the CLI rejects remote calibration before
|
|
142
|
-
uploading or claiming to execute a local dataset path. This also prevents raw
|
|
143
|
-
evaluation queries from being exposed through the admin API.
|
|
144
|
-
|
|
145
|
-
## Reference starting profile
|
|
146
|
-
|
|
147
|
-
Reranker scores are not portable across models, adapters, model revisions, or
|
|
148
|
-
corpora. The profile below is published only to make the checked-in BGE example
|
|
149
|
-
concrete; it is not a certified substitute for calibration.
|
|
150
|
-
|
|
151
|
-
| Model | Adapter | `match_score` | `match_margin` | `candidate_floor` |
|
|
152
|
-
|---|---|---:|---:|---:|
|
|
153
|
-
| `BAAI/bge-reranker-v2-m3` | `jina-v1` | `0.90` | `0.20` | `0.40` |
|
|
154
|
-
|
|
155
|
-
Provenance: the small synthetic corpus and labelled decision cases in
|
|
156
|
-
[`tests/router-core.spec.test.ts`](../tests/router-core.spec.test.ts), with the
|
|
157
|
-
wire contract captured by
|
|
158
|
-
[`tests/fixtures/reranker/jina-v1-request.json`](../tests/fixtures/reranker/jina-v1-request.json).
|
|
159
|
-
That fixture is below the default 30-auto-match certification minimum, so the
|
|
160
|
-
values are a smoke-test/reference profile, not a completed calibration run.
|
|
161
|
-
Run the lifecycle above against the deployment's real corpus before enabling
|
|
162
|
-
automatic matches in production.
|