@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/src/types.ts CHANGED
@@ -5,11 +5,12 @@ export interface RecallConfig {
5
5
  }
6
6
 
7
7
  export interface OutputConfig {
8
- ambiguous_candidate_limit: number;
8
+ top_k: number;
9
+ max_top_k: number;
9
10
  }
10
11
 
11
12
  export interface Thresholds {
12
- /** @deprecated Use output.ambiguous_candidate_limit instead */
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 Candidate {
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 MatchedResult {
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: Candidate[];
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
 
@@ -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.