modelrig 0.8.0 → 0.9.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 +156 -0
- package/README.md +27 -2
- package/dist/artifacts/artifact.d.ts +12 -13
- package/dist/artifacts/artifact.d.ts.map +1 -1
- package/dist/artifacts/artifact.js +17 -23
- package/dist/artifacts/index.d.ts +13 -5
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/artifacts/index.js +11 -4
- package/dist/artifacts/run-context.d.ts +3 -24
- package/dist/artifacts/run-context.d.ts.map +1 -1
- package/dist/artifacts/run-context.js +3 -39
- package/dist/bakeoff/engine.d.ts +29 -0
- package/dist/bakeoff/engine.d.ts.map +1 -1
- package/dist/bakeoff/engine.js +47 -0
- package/dist/bakeoff/eval-cases-rig.d.ts +36 -0
- package/dist/bakeoff/eval-cases-rig.d.ts.map +1 -0
- package/dist/bakeoff/eval-cases-rig.js +27 -0
- package/dist/bakeoff/eval-cases.d.ts +19 -0
- package/dist/bakeoff/eval-cases.d.ts.map +1 -1
- package/dist/bakeoff/eval-cases.js +18 -1
- package/dist/bakeoff/golden-judge.d.ts +47 -0
- package/dist/bakeoff/golden-judge.d.ts.map +1 -0
- package/dist/bakeoff/golden-judge.js +49 -0
- package/dist/bakeoff/quality.d.ts +30 -0
- package/dist/bakeoff/quality.d.ts.map +1 -1
- package/dist/bakeoff/quality.js +21 -0
- package/dist/bakeoff/replay.d.ts +11 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +8 -0
- package/dist/bakeoff/report.d.ts +7 -0
- package/dist/bakeoff/report.d.ts.map +1 -1
- package/dist/bakeoff/report.js +26 -0
- package/dist/cli-judge.d.ts +12 -0
- package/dist/cli-judge.d.ts.map +1 -0
- package/dist/cli-judge.js +48 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +14 -42
- package/dist/config.d.ts +54 -29
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +86 -47
- package/dist/detect/detector.d.ts +4 -2
- package/dist/detect/detector.d.ts.map +1 -1
- package/dist/detect/safe-harbor.d.ts +57 -0
- package/dist/detect/safe-harbor.d.ts.map +1 -0
- package/dist/detect/safe-harbor.js +278 -0
- package/dist/feedback/coverage.d.ts +11 -8
- package/dist/feedback/coverage.d.ts.map +1 -1
- package/dist/feedback/coverage.js +29 -16
- package/dist/feedback/grade.d.ts +51 -0
- package/dist/feedback/grade.d.ts.map +1 -0
- package/dist/feedback/grade.js +63 -0
- package/dist/gateway/http-protocol.d.ts +1 -1
- package/dist/gateway/http-protocol.d.ts.map +1 -1
- package/dist/gateway/http-protocol.js +1 -1
- package/dist/index.d.ts +44 -27
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +110 -43
- package/dist/init/observe-cli.js +1 -1
- package/dist/init/observe.d.ts +2 -2
- package/dist/init/observe.d.ts.map +1 -1
- package/dist/init/observe.js +11 -7
- package/dist/mask/arm.d.ts +112 -0
- package/dist/mask/arm.d.ts.map +1 -0
- package/dist/mask/arm.js +179 -0
- package/dist/mask/index.d.ts +2 -0
- package/dist/mask/index.d.ts.map +1 -1
- package/dist/mask/index.js +7 -1
- package/dist/mask/mask-policy-cli.d.ts +12 -4
- package/dist/mask/mask-policy-cli.d.ts.map +1 -1
- package/dist/mask/mask-policy-cli.js +34 -9
- package/dist/query/grammar.d.ts +2 -2
- package/dist/query/grammar.d.ts.map +1 -1
- package/dist/query/grammar.js +3 -1
- package/dist/query/saved.d.ts +13 -0
- package/dist/query/saved.d.ts.map +1 -1
- package/dist/query/saved.js +67 -1
- package/dist/receipts/types.d.ts +1 -1
- package/dist/receipts/types.d.ts.map +1 -1
- package/dist/receipts/types.js +1 -0
- package/dist/registry/build.d.ts +43 -1
- package/dist/registry/build.d.ts.map +1 -1
- package/dist/registry/build.js +52 -0
- package/dist/registry/pricing-shape.d.ts +29 -0
- package/dist/registry/pricing-shape.d.ts.map +1 -1
- package/dist/registry/pricing-shape.js +10 -1
- package/dist/registry/rows.d.ts.map +1 -1
- package/dist/registry/rows.js +1 -0
- package/dist/registry/select.d.ts +6 -1
- package/dist/registry/select.d.ts.map +1 -1
- package/dist/registry/select.js +11 -0
- package/dist/routes/call-config.d.ts +199 -0
- package/dist/routes/call-config.d.ts.map +1 -0
- package/dist/routes/call-config.js +343 -0
- package/dist/routes/loader.js +5 -3
- package/dist/routes/objective-loader.d.ts +12 -0
- package/dist/routes/objective-loader.d.ts.map +1 -1
- package/dist/routes/objective-loader.js +82 -0
- package/dist/routes/resolved-bundle.d.ts +50 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -1
- package/dist/routes/resolved-bundle.js +52 -0
- package/dist/routes/screen-loader.d.ts +28 -0
- package/dist/routes/screen-loader.d.ts.map +1 -1
- package/dist/routes/screen-loader.js +51 -0
- package/dist/run/accept.d.ts +2 -0
- package/dist/run/accept.d.ts.map +1 -1
- package/dist/run/accept.js +1 -0
- package/dist/run/dispatch.d.ts +42 -13
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +107 -25
- package/dist/run/grounding.d.ts +28 -2
- package/dist/run/grounding.d.ts.map +1 -1
- package/dist/run/grounding.js +54 -10
- package/dist/run/record-attempt.d.ts +4 -0
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +4 -0
- package/dist/run/routing-docs.js +1 -1
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +21 -26
- package/dist/savings/odometer.d.ts +4 -0
- package/dist/savings/odometer.d.ts.map +1 -1
- package/dist/savings/odometer.js +20 -1
- package/dist/savings/swap-execute.d.ts +7 -0
- package/dist/savings/swap-execute.d.ts.map +1 -1
- package/dist/savings/swap-execute.js +30 -0
- package/dist/savings/swaps-client.d.ts +10 -0
- package/dist/savings/swaps-client.d.ts.map +1 -1
- package/dist/screen/fixtures/probe-corpora.d.ts +23 -0
- package/dist/screen/fixtures/probe-corpora.d.ts.map +1 -0
- package/dist/screen/fixtures/probe-corpora.js +69 -0
- package/dist/screen/index.d.ts +21 -0
- package/dist/screen/index.d.ts.map +1 -0
- package/dist/screen/index.js +64 -0
- package/dist/screen/model-screen.d.ts +111 -0
- package/dist/screen/model-screen.d.ts.map +1 -0
- package/dist/screen/model-screen.js +178 -0
- package/dist/screen/patterns.d.ts +51 -0
- package/dist/screen/patterns.d.ts.map +1 -0
- package/dist/screen/patterns.js +151 -0
- package/dist/screen/phi-gate.d.ts +52 -0
- package/dist/screen/phi-gate.d.ts.map +1 -0
- package/dist/screen/phi-gate.js +86 -0
- package/dist/screen/policy.d.ts +71 -0
- package/dist/screen/policy.d.ts.map +1 -0
- package/dist/screen/policy.js +334 -0
- package/dist/screen/probe.d.ts +118 -0
- package/dist/screen/probe.d.ts.map +1 -0
- package/dist/screen/probe.js +132 -0
- package/dist/screen/screen-cli.d.ts +16 -0
- package/dist/screen/screen-cli.d.ts.map +1 -0
- package/dist/screen/screen-cli.js +178 -0
- package/dist/screen/screen.d.ts +99 -0
- package/dist/screen/screen.d.ts.map +1 -0
- package/dist/screen/screen.js +0 -0
- package/dist/screen/sidecar.d.ts +69 -0
- package/dist/screen/sidecar.d.ts.map +1 -0
- package/dist/screen/sidecar.js +145 -0
- package/dist/screen/types.d.ts +43 -2
- package/dist/screen/types.d.ts.map +1 -1
- package/dist/settings/api-fetcher.d.ts +14 -3
- package/dist/settings/api-fetcher.d.ts.map +1 -1
- package/dist/settings/api-fetcher.js +61 -10
- package/dist/settings/grading-guide.d.ts +76 -0
- package/dist/settings/grading-guide.d.ts.map +1 -0
- package/dist/settings/grading-guide.js +23 -0
- package/dist/settings/index.d.ts +3 -2
- package/dist/settings/index.d.ts.map +1 -1
- package/dist/settings/index.js +2 -1
- package/dist/settings/task-settings.d.ts +44 -7
- package/dist/settings/task-settings.d.ts.map +1 -1
- package/dist/settings/task-settings.js +53 -12
- package/dist/status.d.ts +26 -20
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +47 -29
- package/dist/telemetry/artifact-store.d.ts +5 -6
- package/dist/telemetry/artifact-store.d.ts.map +1 -1
- package/dist/telemetry/artifact-store.js +10 -24
- package/dist/telemetry/attempt-meta.d.ts +21 -0
- package/dist/telemetry/attempt-meta.d.ts.map +1 -1
- package/dist/telemetry/attempt-meta.js +9 -0
- package/dist/telemetry/exporter.d.ts +2 -2
- package/dist/telemetry/exporter.d.ts.map +1 -1
- package/dist/telemetry/exporter.js +22 -41
- package/dist/telemetry/grade-mappers.d.ts +22 -0
- package/dist/telemetry/grade-mappers.d.ts.map +1 -0
- package/dist/telemetry/grade-mappers.js +58 -0
- package/dist/telemetry/grades-sql.d.ts +53 -0
- package/dist/telemetry/grades-sql.d.ts.map +1 -0
- package/dist/telemetry/grades-sql.js +133 -0
- package/dist/telemetry/ingest-protocol.d.ts +29 -46
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +16 -54
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +51 -0
- package/dist/telemetry/store.d.ts +9 -6
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +8 -50
- package/dist/tenancy/api-key-format.d.ts +30 -1
- package/dist/tenancy/api-key-format.d.ts.map +1 -1
- package/dist/tenancy/api-key-format.js +33 -1
- package/dist/types/provider.d.ts +3 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +25 -0
- package/dist/types/route.d.ts +78 -2
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +43 -46
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/validate-cli.d.ts.map +1 -1
- package/dist/validate-cli.js +32 -0
- package/dist/verify/verify.d.ts +5 -0
- package/dist/verify/verify.d.ts.map +1 -1
- package/dist/verify/verify.js +2 -0
- package/dist/watch/evaluate.d.ts +84 -1
- package/dist/watch/evaluate.d.ts.map +1 -1
- package/dist/watch/evaluate.js +28 -0
- package/dist/watch/monitor.d.ts +8 -2
- package/dist/watch/monitor.d.ts.map +1 -1
- package/dist/watch/monitor.js +13 -5
- package/dist/watch/regret-monitor-cli.d.ts +16 -0
- package/dist/watch/regret-monitor-cli.d.ts.map +1 -0
- package/dist/watch/regret-monitor-cli.js +137 -0
- package/dist/watch/regret-monitor.d.ts +168 -0
- package/dist/watch/regret-monitor.d.ts.map +1 -0
- package/dist/watch/regret-monitor.js +304 -0
- package/dist/watch/upgrade.d.ts +62 -0
- package/dist/watch/upgrade.d.ts.map +1 -0
- package/dist/watch/upgrade.js +57 -0
- package/docs/agent-skill/SKILL.md +109 -5
- package/docs/how-it-fits.md +3 -2
- package/docs/instrumentation.md +149 -21
- package/docs/llms.txt +64 -20
- package/docs/mcp-oracle.md +249 -0
- package/docs/quickstart.md +103 -9
- package/package.json +16 -3
- package/registry/leaderboard.json +74 -0
- package/registry/registry.json +150 -112
- package/screen.d.ts +16 -0
- package/templates/call-disposition-qa.json +77 -0
- package/templates/catalog-cleansing.json +77 -0
- package/templates/compliance-review.json +76 -0
- package/templates/cs-next-action.json +76 -0
- package/templates/document-extraction.json +78 -0
- package/templates/financial-classification.json +75 -0
- package/templates/lead-qualification.json +76 -0
- package/templates/medical-classification.json +71 -0
- package/templates/ticket-triage.json +76 -0
- package/dist/telemetry/supabase-exporter.d.ts +0 -64
- package/dist/telemetry/supabase-exporter.d.ts.map +0 -1
- package/dist/telemetry/supabase-exporter.js +0 -211
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# The MCP oracle
|
|
2
|
+
|
|
3
|
+
> `modelrig-oracle` is published on npm (0.1.0, 2026-08-21) — the
|
|
4
|
+
> `npx modelrig-oracle` commands on this page work everywhere. Nothing else on
|
|
5
|
+
> this site depends on it: llms.txt carries the same probed facts as plain text.
|
|
6
|
+
|
|
7
|
+
**What it is:** an MCP server that lets a coding agent ask what models can
|
|
8
|
+
actually do — measured, dated, and priced by what conformance really costs —
|
|
9
|
+
and then act on the answer.
|
|
10
|
+
|
|
11
|
+
**Why it exists:** the fastest path from "which model should serve this?" to a
|
|
12
|
+
working route runs through the agent already sitting in your editor. Asking it
|
|
13
|
+
to read a docs page and hope is worse than giving it a tool.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx modelrig-oracle # stdio; this is the whole install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Register it once:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{ "mcpServers": { "modelrig": { "command": "npx", "args": ["modelrig-oracle"] } } }
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Read tools — no account, no key
|
|
28
|
+
|
|
29
|
+
These answer from the packaged registry. Nothing leaves your machine except an
|
|
30
|
+
anonymous record of the question's *shape* (see [Demand signal](#demand-signal)).
|
|
31
|
+
|
|
32
|
+
| Tool | Answers |
|
|
33
|
+
|---|---|
|
|
34
|
+
| `query_registry` | "Which models can serve strict JSON under $1/M?" — capability predicates plus a price bound, ranked by effective cost of conformance. |
|
|
35
|
+
| `get_leaderboard` | Standings, optionally for one capability. Cheap-but-unreliable ranks below pricier-but-correct, because the ranking is cost per *conformant* output. |
|
|
36
|
+
| `explain_pricing` | List price for one model, the effective cost of conformance beside it, and why they differ. |
|
|
37
|
+
| `get_call_notes` | Per-model call-shaping quirks: parameters it rejects, shapes it needs, defaults that surprise. Plus any measured disagreement with the provider's claims. |
|
|
38
|
+
|
|
39
|
+
Every answer carries `scope`, `probe_data_as_of`, `models_in_registry`, and a
|
|
40
|
+
staleness flag. When nothing matches, the answer explains *why* rather than
|
|
41
|
+
returning an empty list — the difference between "no such model" and "we
|
|
42
|
+
haven't measured one" is the whole point.
|
|
43
|
+
|
|
44
|
+
### The four capability predicates
|
|
45
|
+
|
|
46
|
+
| Predicate | Means |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `structured-native` | The provider enforces your JSON Schema on its side — probed native rung rate above zero. |
|
|
49
|
+
| `structured-coached` | Output matched the schema on at least 90% of probed samples, whether or not the provider enforced it. |
|
|
50
|
+
| `grounding` | At least half of probed answers cited real retrieved sources rather than answering from memory. |
|
|
51
|
+
| `caching` | Repeat calls actually reported cached input tokens — a measured discount, not a claim. |
|
|
52
|
+
|
|
53
|
+
## Act tools — token-scoped
|
|
54
|
+
|
|
55
|
+
Set `MODELRIG_ORACLE_TOKEN` to enable these. Each is narrower than it sounds,
|
|
56
|
+
on purpose.
|
|
57
|
+
|
|
58
|
+
| Tool | Does | Never does |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| `get_proposals` | Lists open proposals for your routes — each carries an `evidence.change_spec` (the machine-readable diff + `apply_instructions` a coding agent applies) plus its evidence and savings. | Change anything. |
|
|
61
|
+
| `create_route` | Writes a **draft** route bundle — YAML, schema, prompt — into your drafts directory, candidates seeded from the registry, your current model first. | Edit your source, register a route, overwrite an existing draft. |
|
|
62
|
+
| `test_route` | Makes **one** real billed call through a route, under a fresh budget envelope (default $1). | Run unbounded. |
|
|
63
|
+
| `run_bakeoff` | Replays captured inputs through challenger variants, each arm under its own envelope (default $2). | Switch what serves your route. |
|
|
64
|
+
| `run_optimizer` | Runs **one** explicit optimizer cycle for a route — reflect, score candidates on your golden eval cases, file a proposal carrying a `change_spec`. Pass `{ estimate: true }` first for the cost preview. Needs the `oracle-act` scope. | Apply anything; run on a schedule (there is none). |
|
|
65
|
+
| `decide_proposal` | Records a human's approve/reject on a proposal. Needs the `decision` scope. | Execute or apply the swap. |
|
|
66
|
+
| `execute_swap` | Records an approved swap and **returns** the route-YAML diff to apply via a PR. Needs the `decision` scope. | Write any file; commit to your repo. |
|
|
67
|
+
| `record_grade` | Attaches a 0–1 grade (with an optional comment) to one inference or a whole run — the signal the next cycle learns from. Needs the `grade` scope on the HTTP path. | Spend a billed call; guess attribution for an unknown subject. |
|
|
68
|
+
| `get_task_settings` / `set_task_settings` | Reads / writes a task's governance settings and its **grading guide** (the rubric the task's grader is taught from). `set` needs the `task-settings` scope. | Change route config (the pass threshold lives in the route, and is refused here). |
|
|
69
|
+
| `get_call_config` | Reads a route's **current live** call-config — its version, mode, and the model(s) it serves. Read-only. | Change anything. |
|
|
70
|
+
| `set_call_config` | **Sets what serves a route right now** — pins a model or starts a live A/B — on the human's behalf. A real, versioned control-plane change. | Bypass the eligibility gate or the `route-config` scope; imply it is a draft. |
|
|
71
|
+
| `rollback_call_config` | Restores a prior call-config version (re-appended as a new version — history is never deleted). | Delete history; escape the scope check. |
|
|
72
|
+
|
|
73
|
+
**Budget envelopes are not optional.** Every spending tool either takes a cap
|
|
74
|
+
or gets the default, and refuses anything above $25 per call — larger spends
|
|
75
|
+
belong at a CLI where a human is present for the decision.
|
|
76
|
+
|
|
77
|
+
**`create_route` writes files and nothing else.** No control-plane mutation, no
|
|
78
|
+
source edits, no silent registration. A draft is a proposal you read.
|
|
79
|
+
|
|
80
|
+
## Setting a route's live config
|
|
81
|
+
|
|
82
|
+
This is the half of the pivot where the agent sets routes *for the human*.
|
|
83
|
+
`create_route` deliberately never touches the control plane — it drafts. The
|
|
84
|
+
call-config tools deliberately **do**: they change what serves live traffic right
|
|
85
|
+
now, on the human's behalf, and say so unmistakably.
|
|
86
|
+
|
|
87
|
+
| Tool | Endpoint | Scope |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `get_call_config` | `GET /v1/routes/:route/call-config` | any org-scoped key — a read needs none special |
|
|
90
|
+
| `set_call_config` | `PATCH /v1/routes/:route/call-config` | `route-config` |
|
|
91
|
+
| `rollback_call_config` | `POST /v1/routes/:route/call-config/rollback` | `route-config` |
|
|
92
|
+
|
|
93
|
+
**Read it first.** `get_call_config(route)` returns the current `version`, `mode`
|
|
94
|
+
(`pin` or `experiment`), and the `config` that serves the route this instant.
|
|
95
|
+
Version `0` means there is no override yet — the route resolves to its deployed
|
|
96
|
+
(mirrored) bundle default. History (audit + rollback targets) is at
|
|
97
|
+
`GET /v1/routes/:route/call-config/history`.
|
|
98
|
+
|
|
99
|
+
**Pin a model:**
|
|
100
|
+
|
|
101
|
+
```jsonc
|
|
102
|
+
set_call_config({
|
|
103
|
+
route: "example.support_summarize",
|
|
104
|
+
mode: "pin",
|
|
105
|
+
provider: "anthropic",
|
|
106
|
+
model: "claude-sonnet-4",
|
|
107
|
+
note: "cheaper on this route, same conformance"
|
|
108
|
+
})
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Start a live A/B (experiment):**
|
|
112
|
+
|
|
113
|
+
```jsonc
|
|
114
|
+
set_call_config({
|
|
115
|
+
route: "example.support_summarize",
|
|
116
|
+
mode: "experiment",
|
|
117
|
+
arms: [
|
|
118
|
+
{ ref: { provider: "anthropic", model: "claude-sonnet-4" }, weight: 9 },
|
|
119
|
+
{ ref: { provider: "openai", model: "gpt-x" }, weight: 1, name: "challenger" }
|
|
120
|
+
],
|
|
121
|
+
stickyKey: "user_id", // optional — pins a caller to one arm
|
|
122
|
+
fallback: { provider: "anthropic", model: "claude-sonnet-4" }
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The response names the **new version number**, a plain-language summary of what
|
|
127
|
+
now serves the route, and an explicit **rollback hint** (`rollback_call_config`
|
|
128
|
+
with the prior version, or the console). It never reads like a draft or a
|
|
129
|
+
proposal — it is a live change, recorded as `source='user'` (you acting as the
|
|
130
|
+
human's delegated agent).
|
|
131
|
+
|
|
132
|
+
### This is explicit, reversible, and server-gated
|
|
133
|
+
|
|
134
|
+
- **Explicit.** Unlike `create_route`, this mutates the control plane. The tool
|
|
135
|
+
descriptions and the responses make that impossible to miss — nothing here is
|
|
136
|
+
a draft awaiting your move.
|
|
137
|
+
- **Reversible.** Writes are append-only versions. `rollback_call_config(route,
|
|
138
|
+
toVersion)` re-appends a prior version as a new one — nothing is ever deleted —
|
|
139
|
+
and the human can override or roll back in the console at any time.
|
|
140
|
+
- **The server stays the authority.** These tools are thin clients over the
|
|
141
|
+
existing `/v1` endpoints. They add no control-plane logic and cannot bypass:
|
|
142
|
+
- the **eligibility gate** — a pinned (or arm, or fallback) model that does not
|
|
143
|
+
satisfy the route's `require` is refused with a teaching **422**, not silently
|
|
144
|
+
accepted. An agent setting a model still goes through the same gate a human
|
|
145
|
+
does.
|
|
146
|
+
- the **`route-config` scope** — a `rig_sk_` key without it authenticates fine
|
|
147
|
+
but may not write, and gets a **403** with the fix hint (mint or rotate a key
|
|
148
|
+
with `route-config`, or set the config in the console). Reads need no special
|
|
149
|
+
scope.
|
|
150
|
+
- **org tenancy** — the org is resolved from the key, never from the request; a
|
|
151
|
+
foreign-org route reads as "no config in your org", never a cross-tenant probe.
|
|
152
|
+
|
|
153
|
+
Both refusals travel back to the agent with the server's own `error` and
|
|
154
|
+
`fix_hint`, verbatim.
|
|
155
|
+
|
|
156
|
+
## The improvement loop — proposals your agent applies
|
|
157
|
+
|
|
158
|
+
The call-config tools change *what model* serves a route. The **improvement
|
|
159
|
+
loop** goes one level up: ModelRig proposes a change (a prompt rewrite, or a
|
|
160
|
+
model/parameter pin), proves it on **your** golden eval cases, and hands your
|
|
161
|
+
coding agent a machine-readable spec to apply **in your repo, with your
|
|
162
|
+
credentials and review gates**. ModelRig writes nothing to your repo.
|
|
163
|
+
|
|
164
|
+
1. **`run_optimizer(route)`** — one explicit cycle (no schedule). Send
|
|
165
|
+
`{ estimate: true }` first: the preview reports the **mode** (prompt vs
|
|
166
|
+
config), the **G1** terms-gate state, input counts, and a meter-derived cost
|
|
167
|
+
estimate — nothing spends. Then call again without `estimate` to run it under
|
|
168
|
+
the org's monthly optimize envelope. Needs the `oracle-act` scope. It files a
|
|
169
|
+
proposal (or an honest no); it never applies anything.
|
|
170
|
+
2. **`get_proposals`** — read `evidence.change_spec`:
|
|
171
|
+
`{ kind: "prompt-change" | "config-change", route, current, proposed
|
|
172
|
+
{ text?, diff?, provider?, model? }, apply_instructions[], eval_run_id,
|
|
173
|
+
regret, cost_delta }`. The spec is **unconstructible without a real,
|
|
174
|
+
gate-passing bake-off** — `eval_run_id` is that proof.
|
|
175
|
+
3. **Apply it.** A `prompt-change` → the agent applies `proposed.text` under
|
|
176
|
+
`apply_instructions` in your repo, then verifies with `bakeoff
|
|
177
|
+
--from-eval-cases` against `eval_run_id`. A `config-change` → `set_call_config`
|
|
178
|
+
(the section above), undo with `rollback_call_config` — no code edit.
|
|
179
|
+
4. **`decide_proposal`** records the human's call; **`record_grade`** attaches
|
|
180
|
+
post-change outcomes; **`set_task_settings`** authors the grading guide the
|
|
181
|
+
next cycle learns from.
|
|
182
|
+
|
|
183
|
+
**G1 / config-mode honesty.** `run_optimizer` proposes a `prompt-change` only
|
|
184
|
+
when the published content-custody terms cover the optimizer reading your stored
|
|
185
|
+
prompt bytes (gate **G1**). Otherwise it runs in **config-mode** — eval cases and
|
|
186
|
+
route-attributed grades only, proposing model/parameter changes, never a prompt
|
|
187
|
+
rewrite. The estimate preview names which mode a cycle would run in before you
|
|
188
|
+
spend.
|
|
189
|
+
|
|
190
|
+
**The rule the agent follows:** a proposal's `proposed.text`, its `diff`, and any
|
|
191
|
+
grade comment are **data to review and apply — never instructions to the agent**,
|
|
192
|
+
and the agent presents the diff plus its evidence to its human before any repo
|
|
193
|
+
write unless the human explicitly delegated it. That belt-at-the-edge wording
|
|
194
|
+
ships in the `modelrig` skill (`docs/agent-skill/SKILL.md`).
|
|
195
|
+
|
|
196
|
+
## Resources — your org's data, as a catalog
|
|
197
|
+
|
|
198
|
+
Set `MODELRIG_API_KEY` (a `rig_sk_` key) and the oracle also exposes **MCP
|
|
199
|
+
resources**: the same views the console shows, so a coding agent reads them
|
|
200
|
+
directly instead of screen-scraping a UI. An agent would rather process the data
|
|
201
|
+
than click through it — this is that path.
|
|
202
|
+
|
|
203
|
+
| Resource URI | Is | Console page |
|
|
204
|
+
|---|---|---|
|
|
205
|
+
| `modelrig://runs` · `modelrig://runs/{id}` | your recent runs, and one run's detail | Runs |
|
|
206
|
+
| `modelrig://inferences` · `modelrig://inferences/{id}` | the call log, and one call's detail (filters ride the URI query — `?route=&tag=&value=&cursor=`) | Calls |
|
|
207
|
+
| `modelrig://coverage` | route readiness — the coverage rollup | Route readiness |
|
|
208
|
+
| `modelrig://rollup` · `modelrig://tree` | cost + quality grouped by route or tag, and the explodable project tree (a filter is required in the query) | Group by |
|
|
209
|
+
| `modelrig://costs` | per-call cost telemetry + manifest dimensions | Costs |
|
|
210
|
+
|
|
211
|
+
**What you get back is exactly what the console renders** — counts, hashes, and
|
|
212
|
+
metadata, **never** raw prompt/output bytes, presigned URLs, or secrets. The
|
|
213
|
+
same DTO, the same numbers.
|
|
214
|
+
|
|
215
|
+
**Every resource is scoped to your key's org.** These are backed by the
|
|
216
|
+
read-only `GET /v1/read/*` endpoints (`https://api.modelrig.ai/v1/read/…`), which
|
|
217
|
+
resolve the org from the key — never from the request — using the same service
|
|
218
|
+
that scopes the console, so a resource can only ever return your own
|
|
219
|
+
organization's rows. A read needs no scope beyond a valid key; there is nothing
|
|
220
|
+
to mutate here.
|
|
221
|
+
|
|
222
|
+
```jsonc
|
|
223
|
+
// resources/read
|
|
224
|
+
{ "uri": "modelrig://rollup?by=route&key=example.support" }
|
|
225
|
+
{ "uri": "modelrig://inferences?route=example.support&cursor=<next>" }
|
|
226
|
+
{ "uri": "modelrig://runs/0f2c…" }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
These pair with the write path above: read the state as resources, decide, then
|
|
230
|
+
`set_call_config` to act — both keyed by the same `rig_sk_` key, both org-scoped
|
|
231
|
+
by the server.
|
|
232
|
+
|
|
233
|
+
## Demand signal
|
|
234
|
+
|
|
235
|
+
Every call logs one row: which tool, which capability or model was asked about,
|
|
236
|
+
and whether we had an answer. That is the entire payload — no prompts, no
|
|
237
|
+
schemas, no question text, and on stdio no account linkage at all (the session
|
|
238
|
+
key is random per process and resolves to nobody).
|
|
239
|
+
|
|
240
|
+
Unmatched rows are the point. They tell us which models people need that we
|
|
241
|
+
haven't probed, which is how the coverage queue gets prioritized. If you want
|
|
242
|
+
to make that signal louder for a specific model, file a
|
|
243
|
+
[probe request](https://github.com/modelrig/modelrig/blob/main/docs/probe-request.md).
|
|
244
|
+
|
|
245
|
+
## When the oracle disagrees with the console
|
|
246
|
+
|
|
247
|
+
It shouldn't — both answer from the same selection module, deliberately, so
|
|
248
|
+
that "which models can do X" has exactly one implementation. If you ever see
|
|
249
|
+
them differ, that's a bug worth reporting.
|
package/docs/quickstart.md
CHANGED
|
@@ -8,7 +8,7 @@ and three things happen at once:
|
|
|
8
8
|
- **See** — the run lands in an **inspectable artifact store** (runs → steps →
|
|
9
9
|
artifacts, lineaged and integrity-hashed; see the
|
|
10
10
|
[instrumentation guide](instrumentation.md)), with cost/tokens/tier and a
|
|
11
|
-
|
|
11
|
+
grade recorded against your tags.
|
|
12
12
|
- **Prove** — you get schema-validated output, served only by a model you
|
|
13
13
|
authorized, and you can replay that traffic against cheaper candidates in a
|
|
14
14
|
bake-off before you change anything.
|
|
@@ -87,8 +87,14 @@ reviewed. The full autonomy ladder and same-model guarantees are in the
|
|
|
87
87
|
pnpm add modelrig # or npm i modelrig — Node >= 20
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
-
It
|
|
91
|
-
toolchain.
|
|
90
|
+
It installs a native module (`better-sqlite3`) — prebuilt for macOS/Linux on
|
|
91
|
+
x64/arm64; an unusual platform compiles it and needs a C++ toolchain.
|
|
92
|
+
|
|
93
|
+
**Deploying:** `modelrig` installs prebuilt (no separate build step), but it
|
|
94
|
+
needs a **Node** runtime (not Edge/Workers), and if your deploy bundles server
|
|
95
|
+
code you must mark it external — Next.js `serverExternalPackages: ['modelrig']`,
|
|
96
|
+
esbuild `--external:better-sqlite3`, webpack `externals`. Set `MODELRIG_API_KEY`
|
|
97
|
+
on every service, worker, and cron that runs a rig.
|
|
92
98
|
|
|
93
99
|
```ts
|
|
94
100
|
import { createRig, loadConfigFromEnv } from "modelrig";
|
|
@@ -235,6 +241,83 @@ per-step seam, and `run.scope` vs `run.start` — is the
|
|
|
235
241
|
[instrumentation guide](instrumentation.md); this section is the standard path
|
|
236
242
|
that links to it.
|
|
237
243
|
|
|
244
|
+
## Change your route's model live (no redeploy)
|
|
245
|
+
|
|
246
|
+
A route's **call-config** — which model/provider serves it, and its sampling — can be
|
|
247
|
+
changed *while your app runs*, without editing YAML or redeploying. You set it in the
|
|
248
|
+
console (the route's **call-config card**), or your coding agent sets it for you over
|
|
249
|
+
MCP (`set_call_config`, below); the SDK reads the current config **just-before-call** and
|
|
250
|
+
merges it over your bundle. Two modes:
|
|
251
|
+
|
|
252
|
+
- **`pin`** — one model serves the route right now.
|
|
253
|
+
- **`experiment`** — a weighted live A/B across arms, with the chosen arm recorded on
|
|
254
|
+
every row so the console can score it.
|
|
255
|
+
|
|
256
|
+
**Read call-config just-before-call.** Build a resolver once (it caches with a short TTL
|
|
257
|
+
and revalidates by ETag — no per-call network cost when fresh), then overlay the current
|
|
258
|
+
config onto a bundle and serve it through `rig.runBundle()`:
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
import {
|
|
262
|
+
createRig, loadConfigFromEnv,
|
|
263
|
+
createApiCallConfigFetcher, createCallConfigResolver,
|
|
264
|
+
getCallConfig, // pin-only convenience: route + bundle → runnable bundle
|
|
265
|
+
} from "modelrig";
|
|
266
|
+
|
|
267
|
+
const rig = createRig(loadConfigFromEnv());
|
|
268
|
+
|
|
269
|
+
// Build once, reuse across calls. TTL is the cache-freshness window (~30–60s).
|
|
270
|
+
const resolver = createCallConfigResolver({
|
|
271
|
+
fetcher: createApiCallConfigFetcher({
|
|
272
|
+
ingestUrl: process.env.MODELRIG_INGEST_URL ?? "https://api.modelrig.ai",
|
|
273
|
+
apiKey: process.env.MODELRIG_API_KEY!, // a read needs no special scope
|
|
274
|
+
}),
|
|
275
|
+
ttlMs: 30_000,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// `bundle` is the route's deploy-time bundle you already hold (see runBundle below).
|
|
279
|
+
// getCallConfig merges the live PIN over it; with no override it returns the SAME
|
|
280
|
+
// bundle reference — byte-identical to today.
|
|
281
|
+
const runnable = await getCallConfig("example.support_summarize", { resolver, bundle });
|
|
282
|
+
const result = await rig.runBundle(runnable, { input, tags });
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Fail-open, always.** The read **never throws**: on any error it serves the last-known-good
|
|
286
|
+
config, and a route with **no override resolves byte-identical** to the deploy-time bundle
|
|
287
|
+
(the same bundle reference, unchanged). Turning this on adds no failure mode to the hot path.
|
|
288
|
+
|
|
289
|
+
**Where it applies.** Call-config is overlaid onto a **bundle** you serve through
|
|
290
|
+
`rig.run()` / `rig.runBundle()` — **not** `rig.runRaw`. The raw lane is BYOK and has no
|
|
291
|
+
route or bundle to overlay, so a routeless raw-lane caller that wants live routing must
|
|
292
|
+
first own a route/bundle (build one with `bundleFromResolved`, or resolve one from disk)
|
|
293
|
+
and serve it through `runBundle`.
|
|
294
|
+
|
|
295
|
+
**A/B — draw an arm locally, record it.** For `mode='experiment'`, resolve the config and
|
|
296
|
+
draw the arm for this one call with `resolveCallConfigForRun`, then thread the arm name onto
|
|
297
|
+
`rig.runBundle(..., { arm })` so it lands on `inferences.meta.arm`:
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
const resolved = await resolver.getCallConfig("example.support_summarize");
|
|
301
|
+
const { bundle: runnable, arm } = resolveCallConfigForRun(bundle, resolved, {
|
|
302
|
+
input, // stickyKey (if set) reads its unit from here…
|
|
303
|
+
// unitKey: userId, // …or pass the unit key explicitly (sticky assignment)
|
|
304
|
+
});
|
|
305
|
+
const result = await rig.runBundle(runnable, { input, tags, arm });
|
|
306
|
+
// arm is null for a pin / no-override / non-experiment call ⇒ no meta.arm (byte-identical).
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Sticky assignment means the **same unit always draws the same arm**, so the A/B is measured
|
|
310
|
+
honestly. The console reads per-arm outcomes at `GET /v1/console/ab-readout?route=<route>`.
|
|
311
|
+
How `meta.arm` is populated and overlaid is in the
|
|
312
|
+
[instrumentation guide](instrumentation.md#live-call-config-and-ab-arms).
|
|
313
|
+
|
|
314
|
+
**Set it from your coding agent (MCP).** The `modelrig-oracle` MCP server exposes
|
|
315
|
+
`get_call_config`, `set_call_config` (`pin` or `experiment`), and `rollback_call_config`, so
|
|
316
|
+
the agent in your editor can pin a model or start an A/B **on your behalf** — a real,
|
|
317
|
+
versioned, reversible control-plane change (not a draft). Writes need a `rig_sk_` key with
|
|
318
|
+
the **`route-config`** scope; reads need none. The full flow, the eligibility gate, and the
|
|
319
|
+
rollback contract are in [The MCP oracle → Setting a route's live config](mcp-oracle.md#setting-a-routes-live-config).
|
|
320
|
+
|
|
238
321
|
## When it fails, it fails typed
|
|
239
322
|
|
|
240
323
|
```ts
|
|
@@ -280,12 +363,23 @@ worker, and cron that constructs a rig — a process without it buffers to its o
|
|
|
280
363
|
local SQLite and ships nothing. A multi-service deployment that sets the key on
|
|
281
364
|
only the web tier silently loses every row a worker or cron produced.
|
|
282
365
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
[
|
|
287
|
-
|
|
288
|
-
|
|
366
|
+
Turning **Optimization on** for a route (`capture: true`) is the **recommended
|
|
367
|
+
default** — your traffic is what makes the routing smarter and the bill smaller,
|
|
368
|
+
and everything it keeps you can see, export and delete in the console. Keeping the
|
|
369
|
+
prompts, outputs and evidence too is [content custody](content-custody.md), which
|
|
370
|
+
is **on by default** — every new org is created managed, having accepted the
|
|
371
|
+
published terms at signup — and captures into your managed store, scrubbed for the
|
|
372
|
+
PII/PHI you classify. Either way the **opt-out is one account setting** (the
|
|
373
|
+
console or the `set_org_settings` MCP tool): set posture `off` to capture nothing
|
|
374
|
+
or `metadata` to keep hashes only, or use a `zero_retention` route.
|
|
375
|
+
Pure router (capture off) stays real and selectable, at the same flat 2%.
|
|
376
|
+
|
|
377
|
+
Telemetry says what happened; a **grade** says whether it was good. Attach a
|
|
378
|
+
0–1 score (up = 1, down = 0) to a call or a whole run with `rig.grade(...)` —
|
|
379
|
+
from the SDK or over HTTP for a non-SDK surface (see the
|
|
380
|
+
[grade protocol](grade-protocol.md)) — and every run gets an automatic
|
|
381
|
+
`run-outcome@v1` grade, with an explicit, bounded model judge available when
|
|
382
|
+
you want one (see the [grade protocol](grade-protocol.md#native-run-grades)).
|
|
289
383
|
|
|
290
384
|
## Runtime requirements
|
|
291
385
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modelrig",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "ModelRig — capability-aware AI gateway: routes-as-code, provider adapters, typed failures, budget envelopes, and local-first telemetry.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -24,10 +24,11 @@
|
|
|
24
24
|
},
|
|
25
25
|
"main": "dist/index.js",
|
|
26
26
|
"types": "dist/index.d.ts",
|
|
27
|
-
"//exports": "ADDITIVE sub-path map (RP-1). `.` preserves the historical bare entry; `./receipts` (Receipt@v1 shape + canonicalize/hash/classification) is exposed ONLY here, never re-exported from index.ts (keeps the public surface — and index.ts's LOC — untouched). `./data/*` + `./registry/*` keep the runtime data/registry files reachable now that an exports map turns resolution into an allowlist. The root `receipts.d.ts` re-export
|
|
27
|
+
"//exports": "ADDITIVE sub-path map (RP-1). `.` preserves the historical bare entry; `./receipts` (Receipt@v1 shape + canonicalize/hash/classification) is exposed ONLY here, never re-exported from index.ts (keeps the public surface — and index.ts's LOC — untouched). `./screen` (IP-2a-1) exposes the SC-0/SC-1 screening surface the same way — the server's grader assembly reuses assemblePrompt + createPatternDetector rather than forking the delimiting logic; still never re-exported from index.ts. `./data/*` + `./registry/*` keep the runtime data/registry files reachable now that an exports map turns resolution into an allowlist. The root `receipts.d.ts` / `screen.d.ts` re-export shims exist ALONGSIDE this so classic-`Node` moduleResolution consumers (e.g. modelrig-server) type-resolve the sub-paths too.",
|
|
28
28
|
"exports": {
|
|
29
29
|
".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
|
|
30
30
|
"./receipts": { "types": "./dist/receipts/types.d.ts", "default": "./dist/receipts/types.js" },
|
|
31
|
+
"./screen": { "types": "./dist/screen/index.d.ts", "default": "./dist/screen/index.js" },
|
|
31
32
|
"./package.json": "./package.json",
|
|
32
33
|
"./data/*": "./data/*",
|
|
33
34
|
"./registry/*": "./registry/*"
|
|
@@ -40,6 +41,7 @@
|
|
|
40
41
|
"dist",
|
|
41
42
|
"data",
|
|
42
43
|
"receipts.d.ts",
|
|
44
|
+
"screen.d.ts",
|
|
43
45
|
"registry/registry.json",
|
|
44
46
|
"registry/leaderboard.json",
|
|
45
47
|
"README.md",
|
|
@@ -49,8 +51,18 @@
|
|
|
49
51
|
"docs/instrumentation.md",
|
|
50
52
|
"docs/how-it-fits.md",
|
|
51
53
|
"docs/migration-playbook.md",
|
|
54
|
+
"docs/mcp-oracle.md",
|
|
52
55
|
"docs/agent-skill/SKILL.md",
|
|
53
|
-
"docs/llms.txt"
|
|
56
|
+
"docs/llms.txt",
|
|
57
|
+
"templates/ticket-triage.json",
|
|
58
|
+
"templates/document-extraction.json",
|
|
59
|
+
"templates/cs-next-action.json",
|
|
60
|
+
"templates/lead-qualification.json",
|
|
61
|
+
"templates/compliance-review.json",
|
|
62
|
+
"templates/catalog-cleansing.json",
|
|
63
|
+
"templates/call-disposition-qa.json",
|
|
64
|
+
"templates/financial-classification.json",
|
|
65
|
+
"templates/medical-classification.json"
|
|
54
66
|
],
|
|
55
67
|
"scripts": {
|
|
56
68
|
"build": "tsc",
|
|
@@ -67,6 +79,7 @@
|
|
|
67
79
|
"yaml": "^2.6.0"
|
|
68
80
|
},
|
|
69
81
|
"devDependencies": {
|
|
82
|
+
"@modelrig/glossary": "workspace:*",
|
|
70
83
|
"@types/better-sqlite3": "^7.6.12",
|
|
71
84
|
"tsx": "^4.21.0",
|
|
72
85
|
"typescript": "^5.2.0",
|