modelrig 0.7.0 → 0.8.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 +107 -0
- package/README.md +63 -0
- package/dist/bakeoff/disagreement.d.ts +93 -0
- package/dist/bakeoff/disagreement.d.ts.map +1 -0
- package/dist/bakeoff/disagreement.js +140 -0
- package/dist/bakeoff/engine.d.ts.map +1 -1
- package/dist/bakeoff/engine.js +58 -2
- package/dist/bakeoff/pairwise.d.ts +102 -0
- package/dist/bakeoff/pairwise.d.ts.map +1 -0
- package/dist/bakeoff/pairwise.js +214 -0
- package/dist/bakeoff/replay.d.ts +6 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +6 -2
- package/dist/bakeoff/report.d.ts +7 -0
- package/dist/bakeoff/report.d.ts.map +1 -1
- package/dist/bakeoff/report.js +46 -0
- package/dist/bakeoff/types.d.ts +24 -0
- package/dist/bakeoff/types.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +9 -9
- package/dist/mask/apply.d.ts +80 -0
- package/dist/mask/apply.d.ts.map +1 -0
- package/dist/mask/apply.js +228 -0
- package/dist/mask/detect.d.ts +38 -0
- package/dist/mask/detect.d.ts.map +1 -0
- package/dist/mask/detect.js +167 -0
- package/dist/mask/index.d.ts +21 -0
- package/dist/mask/index.d.ts.map +1 -0
- package/dist/mask/index.js +54 -0
- package/dist/mask/mask-policy-cli.d.ts +11 -0
- package/dist/mask/mask-policy-cli.d.ts.map +1 -0
- package/dist/mask/mask-policy-cli.js +33 -0
- package/dist/mask/policy.d.ts +38 -0
- package/dist/mask/policy.d.ts.map +1 -0
- package/dist/mask/policy.js +187 -0
- package/dist/mask/proof.d.ts +54 -0
- package/dist/mask/proof.d.ts.map +1 -0
- package/dist/mask/proof.js +120 -0
- package/dist/mask/receipt.d.ts +30 -0
- package/dist/mask/receipt.d.ts.map +1 -0
- package/dist/mask/receipt.js +49 -0
- package/dist/mask/seam.d.ts +53 -0
- package/dist/mask/seam.d.ts.map +1 -0
- package/dist/mask/seam.js +120 -0
- package/dist/mask/types.d.ts +81 -0
- package/dist/mask/types.d.ts.map +1 -0
- package/dist/mask/types.js +10 -0
- package/dist/mask/vault.d.ts +50 -0
- package/dist/mask/vault.d.ts.map +1 -0
- package/dist/mask/vault.js +81 -0
- package/dist/receipts/types.d.ts +65 -0
- package/dist/receipts/types.d.ts.map +1 -0
- package/dist/receipts/types.js +134 -0
- package/dist/routes/loader-warnings.d.ts +8 -0
- package/dist/routes/loader-warnings.d.ts.map +1 -1
- package/dist/routes/loader-warnings.js +23 -0
- package/dist/routes/loader.d.ts +1 -1
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +24 -25
- package/dist/routes/mask-loader.d.ts +18 -0
- package/dist/routes/mask-loader.d.ts.map +1 -0
- package/dist/routes/mask-loader.js +39 -0
- package/dist/routes/objective-loader.d.ts.map +1 -1
- package/dist/routes/objective-loader.js +51 -17
- package/dist/routes/screen-loader.d.ts +23 -0
- package/dist/routes/screen-loader.d.ts.map +1 -0
- package/dist/routes/screen-loader.js +79 -0
- package/dist/routes/template.d.ts +10 -0
- package/dist/routes/template.d.ts.map +1 -1
- package/dist/routes/template.js +10 -3
- package/dist/run/accept.d.ts +4 -0
- package/dist/run/accept.d.ts.map +1 -1
- package/dist/run/accept.js +2 -0
- package/dist/run/dispatch.d.ts +35 -0
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +33 -0
- package/dist/run/grounding.d.ts +47 -2
- package/dist/run/grounding.d.ts.map +1 -1
- package/dist/run/grounding.js +113 -6
- package/dist/run/raw.d.ts.map +1 -1
- package/dist/run/raw.js +6 -1
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +12 -1
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +19 -16
- package/dist/screen/provenance.d.ts +59 -0
- package/dist/screen/provenance.d.ts.map +1 -0
- package/dist/screen/provenance.js +104 -0
- package/dist/screen/types.d.ts +71 -0
- package/dist/screen/types.d.ts.map +1 -0
- package/dist/screen/types.js +9 -0
- package/dist/status-cli.d.ts +11 -0
- package/dist/status-cli.d.ts.map +1 -0
- package/dist/status-cli.js +21 -0
- package/dist/status-runs.d.ts +38 -0
- package/dist/status-runs.d.ts.map +1 -0
- package/dist/status-runs.js +80 -0
- package/dist/status.d.ts +7 -2
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +11 -2
- package/dist/telemetry/ingest-protocol.d.ts +8 -0
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +11 -0
- package/dist/telemetry/store.d.ts.map +1 -1
- package/dist/telemetry/store.js +12 -3
- package/dist/telemetry/supabase-exporter.d.ts +64 -0
- package/dist/telemetry/supabase-exporter.d.ts.map +1 -0
- package/dist/telemetry/supabase-exporter.js +211 -0
- package/dist/types/provider.d.ts +11 -0
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +9 -0
- package/dist/types/route.d.ts +55 -0
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +7 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/validate-cli.d.ts.map +1 -1
- package/dist/validate-cli.js +13 -0
- package/dist/verify/types.d.ts +6 -0
- package/dist/verify/types.d.ts.map +1 -1
- package/docs/agent-skill/SKILL.md +131 -0
- package/docs/how-it-fits.md +34 -0
- package/docs/instrumentation.md +221 -0
- package/docs/llms.txt +276 -0
- package/docs/migration-playbook.md +512 -0
- package/docs/quickstart.md +333 -0
- package/package.json +18 -2
- package/receipts.d.ts +12 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# ModelRig quickstart — see, prove, and save on every model call
|
|
2
|
+
|
|
3
|
+
ModelRig is the operating layer for your AI workstream. Each LLM task becomes a
|
|
4
|
+
**versioned route file** (prompt template + JSON schema + candidate models +
|
|
5
|
+
policy) that you call with one line — `rig.run("task.name", {input, tags})` —
|
|
6
|
+
and three things happen at once:
|
|
7
|
+
|
|
8
|
+
- **See** — the run lands in an **inspectable artifact store** (runs → steps →
|
|
9
|
+
artifacts, lineaged and integrity-hashed; see the
|
|
10
|
+
[instrumentation guide](instrumentation.md)), with cost/tokens/tier and a
|
|
11
|
+
verdict recorded against your tags.
|
|
12
|
+
- **Prove** — you get schema-validated output, served only by a model you
|
|
13
|
+
authorized, and you can replay that traffic against cheaper candidates in a
|
|
14
|
+
bake-off before you change anything.
|
|
15
|
+
- **Save** — a winning candidate is a change to your YAML you review; nothing
|
|
16
|
+
swaps a model on its own, and your routes are always YAML in your git.
|
|
17
|
+
|
|
18
|
+
The rest of this page is Step 0 (two minutes, once), then the two ways to
|
|
19
|
+
instrument your project — point your coding agent at it, or do it by hand.
|
|
20
|
+
|
|
21
|
+
## Step 0 — create your org and key (two minutes, human-only)
|
|
22
|
+
|
|
23
|
+
Account setup stays human: your coding agent never creates accounts, signs in,
|
|
24
|
+
or holds credentials. Once, up front:
|
|
25
|
+
|
|
26
|
+
1. Sign up and create your organization at
|
|
27
|
+
[app.modelrig.ai](https://app.modelrig.ai) — email and an org name is the
|
|
28
|
+
whole form.
|
|
29
|
+
2. Issue an API key in the console (the `rig_sk_…` value is shown once — copy it).
|
|
30
|
+
3. Export it beside your provider keys: `MODELRIG_API_KEY=rig_sk_…`.
|
|
31
|
+
|
|
32
|
+
That key is the only ModelRig credential your app ever holds — never a database
|
|
33
|
+
URL, never a service-role key. Provider keys never leave your process.
|
|
34
|
+
|
|
35
|
+
**No account needed for the open lane:** with only your provider keys set,
|
|
36
|
+
routes, probes and telemetry run entirely on your machine — the hosted console
|
|
37
|
+
and synced telemetry are what the key unlocks. You can add it later.
|
|
38
|
+
|
|
39
|
+
There are two ways your calls are keyed and billed, and you pick per route:
|
|
40
|
+
|
|
41
|
+
- **Bring your own keys (BYOK).** Your provider keys stay in your environment;
|
|
42
|
+
ModelRig routes to the provider directly. The first 1,000,000 requests each
|
|
43
|
+
month are free, then a flat 2% of list price. On the raw lane (`rig.runRaw`)
|
|
44
|
+
the `timeoutMs` default is tier-aware — **flex ⇒ set `timeoutMs` in minutes**
|
|
45
|
+
(default 900 s since 0.5.0); see the
|
|
46
|
+
[raw-lane defaults table](route-bundles.md#raw-lane-defaults-rigrunraw).
|
|
47
|
+
- **Managed keys.** ModelRig fronts the provider's inference cost, so managed
|
|
48
|
+
calls are provider cost + 2% from the first request — no key for you to hold.
|
|
49
|
+
|
|
50
|
+
Same flat 2% margin either way; your negotiated provider discounts stay yours.
|
|
51
|
+
|
|
52
|
+
## Point your coding agent at it
|
|
53
|
+
|
|
54
|
+
The fastest migration is the one your agent performs. From inside your project,
|
|
55
|
+
paste this into Claude Code, Codex, or Cursor:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Migrate this project to ModelRig routes (https://modelrig.dev).
|
|
59
|
+
|
|
60
|
+
0. If MODELRIG_API_KEY is not set and I want hosted telemetry, STOP and ask me
|
|
61
|
+
to create an org and issue a rig_sk_ key at https://app.modelrig.ai. Never
|
|
62
|
+
create accounts, sign in, or handle credentials yourself. Without the key,
|
|
63
|
+
proceed in local-only mode — that is fully supported.
|
|
64
|
+
1. Read https://modelrig.dev/quickstart.html and
|
|
65
|
+
https://modelrig.dev/route-bundles.html.
|
|
66
|
+
2. Find every place this codebase calls an LLM API directly.
|
|
67
|
+
3. For each call site, define a route bundle YAML (schema, candidate models,
|
|
68
|
+
policy, prompt template) and replace the direct call with
|
|
69
|
+
rig.run("<route.name>"). Keep prompt and schema semantics unchanged.
|
|
70
|
+
4. Before naming any model capability, check the probed registry — declared
|
|
71
|
+
flags and probed behavior differ; trust the probed layer.
|
|
72
|
+
5. Run the project's existing tests and report every file you changed.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Your agent reads the docs, finds your LLM call sites, and turns them into
|
|
76
|
+
routes. It **won't** create accounts, sign in, or touch your API keys (those
|
|
77
|
+
stay in the environment you set in Step 0), and it swaps call sites to
|
|
78
|
+
`rig.run()` without changing prompt or schema semantics — same model, human-
|
|
79
|
+
reviewed. The full autonomy ladder and same-model guarantees are in the
|
|
80
|
+
[migration playbook](migration-playbook.md).
|
|
81
|
+
|
|
82
|
+
## Or set it up by hand
|
|
83
|
+
|
|
84
|
+
### 1. Install
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pnpm add modelrig # or npm i modelrig — Node >= 20
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
It builds a native module (`better-sqlite3`) on install, so the machine needs a
|
|
91
|
+
toolchain.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { createRig, loadConfigFromEnv } from "modelrig";
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Environment (read once, in ModelRig's config layer only):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
GEMINI_API_KEY=… # only providers you configure get adapters
|
|
101
|
+
OPENAI_API_KEY=…
|
|
102
|
+
DEEPSEEK_API_KEY=…
|
|
103
|
+
|
|
104
|
+
# Where your telemetry goes — pick ONE lane:
|
|
105
|
+
MODELRIG_API_KEY=rig_sk_… # hosted: rows go to ModelRig,
|
|
106
|
+
# scoped to the org that key names
|
|
107
|
+
# SET IT ON EVERY service/worker/cron
|
|
108
|
+
# (omit it and everything below still works, locally)
|
|
109
|
+
MODELRIG_ROUTES_DIR=./modelrig/routes # default
|
|
110
|
+
MODELRIG_TELEMETRY_DB=./.modelrig/telemetry.db # default
|
|
111
|
+
MODELRIG_ENVELOPE_BUDGET_USD=25 # default envelope budget
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Provider keys never leave your process on either lane. A `rig_sk_` key is issued
|
|
115
|
+
in the console at [app.modelrig.ai](https://app.modelrig.ai) and is the only
|
|
116
|
+
ModelRig credential your app ever holds — never a database URL, never a
|
|
117
|
+
service-role key.
|
|
118
|
+
|
|
119
|
+
### 2. Define a route bundle
|
|
120
|
+
|
|
121
|
+
One YAML file per route under `modelrig/routes/`:
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
# modelrig/routes/example.support_summarize.yaml
|
|
125
|
+
route: example.support_summarize # the task handle you pass to rig.run
|
|
126
|
+
version: 1 # bump on ANY change
|
|
127
|
+
schema: ./schemas/support_summarize.schema.json # null for unstructured routes
|
|
128
|
+
candidates: # THE candidate set — the invariant boundary.
|
|
129
|
+
- provider: openai # Nothing outside this list can ever serve
|
|
130
|
+
model: gpt-5.4-mini # the route: enforced by a branded type at
|
|
131
|
+
- provider: gemini # compile time and a runtime guard.
|
|
132
|
+
model: gemini-3.1-flash-lite
|
|
133
|
+
require: [schema_conformant] # hard constraints on candidate eligibility
|
|
134
|
+
prefer: [cost] # advisory ordering (cheapest first)
|
|
135
|
+
prompt:
|
|
136
|
+
system: ./prompts/support_summarize.system.md
|
|
137
|
+
variables: [ticket, product, priorContext]
|
|
138
|
+
policy:
|
|
139
|
+
retries: { content_invalid: 2, network: 4, capacity_shed: 3 }
|
|
140
|
+
timeout_ms: 60000
|
|
141
|
+
tier: flex # requested tier; served tier is recorded
|
|
142
|
+
json: native # native strict schema | json_mode (rung 2)
|
|
143
|
+
sampling: { temperature: 0.1, max_output_tokens: 8192 }
|
|
144
|
+
# ^ preserve the original call's sampling;
|
|
145
|
+
# absent = the adapter's own defaults
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Prompt templates support `{{variable}}` substitution and capability-conditional
|
|
149
|
+
blocks resolved against the **serving** candidate's flags:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
Summarise this ticket for the next agent: {{ticket}}
|
|
153
|
+
|
|
154
|
+
{{#if capability.grounded_native}}
|
|
155
|
+
Use web search to verify recent events.
|
|
156
|
+
{{/if}}
|
|
157
|
+
{{#unless capability.structured_native}}
|
|
158
|
+
CRITICAL: respond with a single JSON object, no markdown fences.
|
|
159
|
+
{{/unless}}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Variables must be declared in `prompt.variables`; referencing an undeclared
|
|
163
|
+
variable fails at load time, not at run time.
|
|
164
|
+
|
|
165
|
+
The `schema` file is a JSON Schema. ModelRig validates output under **JSON
|
|
166
|
+
Schema 2020-12** as the primary dialect and **also accepts draft-07** (a schema
|
|
167
|
+
declaring either `$schema` compiles). Validation runs non-strict with union
|
|
168
|
+
types allowed, and `format` is annotation-only — it is not asserted (an
|
|
169
|
+
`email`/`date-time` format is documentation, not a constraint). Author 2020-12
|
|
170
|
+
unless you have a draft-07 schema already; both work.
|
|
171
|
+
|
|
172
|
+
### 3. Call it
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
const rig = createRig(loadConfigFromEnv());
|
|
176
|
+
|
|
177
|
+
const result = await rig.run("example.support_summarize", {
|
|
178
|
+
input: { ticket: rawTicket, product: "Acme Cloud", priorContext: history },
|
|
179
|
+
tags: { run_id: "run-123", step: "summarize", customer: "acme" },
|
|
180
|
+
budget: { envelope: "run-123" }, // hard-stop cost envelope for the run
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
result.output; // JSON, already validated against your schema
|
|
184
|
+
result.meta.provider; // which candidate actually served
|
|
185
|
+
result.meta.costEstimateUsd; // priced from the pinned LiteLLM snapshot
|
|
186
|
+
result.meta.servedTier; // vs meta.requestedTier — downgrades visible
|
|
187
|
+
result.meta.attemptsByClass; // retries consumed, per failure class
|
|
188
|
+
|
|
189
|
+
rig.close();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 4. Wrap it in a run — the standard path
|
|
193
|
+
|
|
194
|
+
**Every pipeline execution is a run.** A run is the standard way ModelRig records
|
|
195
|
+
what your workstream did — not an appendix. Wrap the execution in a run context,
|
|
196
|
+
name one **step** per model-call family, and **save the work products** as
|
|
197
|
+
artifacts; then the console's **Runs** tab shows the run → step → artifact chain
|
|
198
|
+
you can click through, and `/projects` groups it by tag.
|
|
199
|
+
|
|
200
|
+
```ts
|
|
201
|
+
const rig = createRig(loadConfigFromEnv());
|
|
202
|
+
|
|
203
|
+
// One run per pipeline execution. run.scope isolates concurrent runs; use
|
|
204
|
+
// run.start()/run.end() for one run at a time on a request path.
|
|
205
|
+
await rig.artifacts.run.scope(
|
|
206
|
+
{ pipeline: "support", episodeKey: ticketId },
|
|
207
|
+
async () => {
|
|
208
|
+
const result = await rig.run("example.support_summarize", {
|
|
209
|
+
input: { ticket, product, priorContext },
|
|
210
|
+
tags: { subject: customer, feature: "summarize" }, // run_id auto-stamped
|
|
211
|
+
stepKey: "summarize", // names this step on /runs
|
|
212
|
+
});
|
|
213
|
+
// Save the work product — prompt/raw/parsed all save the same way.
|
|
214
|
+
rig.artifacts.artifact.save(result.output, { name: "summary", type: "step_output" });
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
rig.close();
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
- **Tags:** carry `subject` (what the run is *about*) and `feature`. You no longer
|
|
221
|
+
hand-write `run_id` — **inside a run context the SDK stamps `tags.run_id`** (the
|
|
222
|
+
run's `episodeKey`, else its id) on every attempt row of both lanes; a value you
|
|
223
|
+
pass always wins, and outside a run context nothing is stamped. It also stamps
|
|
224
|
+
`tags.step` (the current step key) the same way; a value you pass always wins,
|
|
225
|
+
so name the step yourself with `stepKey` (or `tags.step`) when you want a
|
|
226
|
+
specific label.
|
|
227
|
+
- **Raw lane:** a `rig.runRaw` call inside a run context records a ground-truth
|
|
228
|
+
step too (since 0.5.0), so grounded/cached pipelines fill the same graph.
|
|
229
|
+
- **See it:** open `/runs` — the run, its steps, its artifacts. `modelrig status`
|
|
230
|
+
prints `runs recorded: N (last: …)` as the local mirror, and `modelrig validate`
|
|
231
|
+
warns if a rig records calls but never starts a run.
|
|
232
|
+
|
|
233
|
+
The full artifact API — lineage (`link`), evaluations (`evaluate`), the reusable
|
|
234
|
+
per-step seam, and `run.scope` vs `run.start` — is the
|
|
235
|
+
[instrumentation guide](instrumentation.md); this section is the standard path
|
|
236
|
+
that links to it.
|
|
237
|
+
|
|
238
|
+
## When it fails, it fails typed
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
import { RigFailureError } from "modelrig";
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
await rig.run("example.support_summarize", opts);
|
|
245
|
+
} catch (err) {
|
|
246
|
+
if (err instanceof RigFailureError) {
|
|
247
|
+
err.failure.class; // content_invalid | capacity_shed | network | refusal
|
|
248
|
+
// | cache_invalid | timeout | config_auth
|
|
249
|
+
// | budget_exhausted | invariant_violation
|
|
250
|
+
// | quality_rejected
|
|
251
|
+
err.failure.fixHint; // machine-readable remediation when available
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Retry budgets are **per class and non-fungible** — exhausting `network`
|
|
257
|
+
retries never consumes the `content_invalid` budget. Terminal classes
|
|
258
|
+
(`budget_exhausted`, `invariant_violation`) are never retried.
|
|
259
|
+
`config_auth` (bad or missing credentials) is **non-retryable by contract**:
|
|
260
|
+
it cannot appear in a bundle's retry budgets, is never retried on the same
|
|
261
|
+
candidate, and the run loop advances straight to the next candidate — a dead
|
|
262
|
+
key never burns backoff time. Failures that still billed tokens (refusals,
|
|
263
|
+
truncations) carry their token usage, so envelopes and telemetry account the
|
|
264
|
+
real spend.
|
|
265
|
+
|
|
266
|
+
Each class — what it means, whether it retries, and how it backs off — is on [Routing & reliability](routing-reliability.md#5-failure-classes); how the whole candidate ladder runs (ordering, validate → repair → fall-through, and what is not built) is on that page too.
|
|
267
|
+
|
|
268
|
+
## Where the telemetry goes
|
|
269
|
+
|
|
270
|
+
Every attempt (not just every run) writes one row to package-owned SQLite,
|
|
271
|
+
tagged with your `tags` — always, with no key. Setting `MODELRIG_API_KEY` turns
|
|
272
|
+
on **api export mode**: a background exporter ships batches to the ingest API
|
|
273
|
+
(`api.modelrig.ai`, override with `MODELRIG_INGEST_URL`), which the console
|
|
274
|
+
reads — fire-and-forget, queue-on-failure; the inference path never blocks on it.
|
|
275
|
+
Inspect live rows in your local SQLite file, or in the console once the key is
|
|
276
|
+
set — never a database URL of your own.
|
|
277
|
+
|
|
278
|
+
`MODELRIG_API_KEY` gates the exporter **per process**. Set it on EVERY service,
|
|
279
|
+
worker, and cron that constructs a rig — a process without it buffers to its own
|
|
280
|
+
local SQLite and ships nothing. A multi-service deployment that sets the key on
|
|
281
|
+
only the web tier silently loses every row a worker or cron produced.
|
|
282
|
+
|
|
283
|
+
Telemetry says what happened; **verdicts** say whether it was good. Attach a
|
|
284
|
+
thumbs up/down to a call or a whole run with `rig.feedback(...)` — from the SDK
|
|
285
|
+
or over HTTP for a non-SDK surface (see the
|
|
286
|
+
[feedback protocol](feedback-protocol.md)) — and every run gets an automatic
|
|
287
|
+
`run-outcome@v1` verdict, with an explicit, bounded model judge available when
|
|
288
|
+
you want one (see [run verdicts](run-verdicts.md)).
|
|
289
|
+
|
|
290
|
+
## Runtime requirements
|
|
291
|
+
|
|
292
|
+
ModelRig assumes a **long-lived Node process with a writable disk**, and a few
|
|
293
|
+
things follow from that. Check them before you deploy:
|
|
294
|
+
|
|
295
|
+
- **Node ≥ 20**, and a native build toolchain at install time — it compiles
|
|
296
|
+
`better-sqlite3` (a C/C++ addon) for the local telemetry buffer. On a slim
|
|
297
|
+
container add the build essentials (`python3`, `make`, a C++ compiler).
|
|
298
|
+
- **A writable disk.** Telemetry and captures go to a local SQLite file
|
|
299
|
+
(`MODELRIG_TELEMETRY_DB`, default `./.modelrig/telemetry.db`); the background
|
|
300
|
+
exporter batches from it. A read-only filesystem breaks this.
|
|
301
|
+
- **Serverless (Vercel functions, AWS Lambda, Google Cloud Functions):**
|
|
302
|
+
supported, with two required adjustments. (1) Point the telemetry DB at the
|
|
303
|
+
only writable location — `MODELRIG_TELEMETRY_DB=/tmp/modelrig/telemetry.db`.
|
|
304
|
+
(2) `await rig.close()` **once per invocation** before the handler returns:
|
|
305
|
+
the function may freeze or be killed the moment you respond, and `close()` is
|
|
306
|
+
what drains the final rows (H1) — skip it and you silently lose the last
|
|
307
|
+
attempts of every invocation. Expect a native-module cold-start cost on the
|
|
308
|
+
first call after a scale-up.
|
|
309
|
+
- **Edge runtimes (Vercel Edge, Cloudflare Workers) are unsupported.** They have
|
|
310
|
+
no native-addon support and no filesystem, so `better-sqlite3` cannot load at
|
|
311
|
+
all. Run ModelRig in a Node runtime; if an edge function must call a route,
|
|
312
|
+
put ModelRig behind a Node service it calls over HTTP.
|
|
313
|
+
|
|
314
|
+
## When it doesn't work
|
|
315
|
+
|
|
316
|
+
The first three failures below stop the bundle loading; the fourth lets it load
|
|
317
|
+
and then misbehave, so it is the one to watch for. The full list, in the
|
|
318
|
+
loader's own words, is in [route-bundles.md](./route-bundles.md#when-it-doesnt-work).
|
|
319
|
+
|
|
320
|
+
- **The bundle won't load, naming a schema or prompt path.** The file isn't at
|
|
321
|
+
the path the bundle names — paths resolve relative to the bundle. Create it
|
|
322
|
+
there, or set `schema: null` for a free-form task.
|
|
323
|
+
- **The bundle won't load, naming an undeclared variable.** A `{{name}}` in the
|
|
324
|
+
template isn't in `prompt.variables`. Add it (or fix the typo).
|
|
325
|
+
- **The first call succeeds locally but nothing appears in the console.**
|
|
326
|
+
`MODELRIG_API_KEY` isn't set in that environment. Everything runs without it;
|
|
327
|
+
it only decides where telemetry lands.
|
|
328
|
+
- **The call runs but the output isn't what the old code produced.** Usually a
|
|
329
|
+
wrong `json: native` — a claim about the model, valid only for one whose
|
|
330
|
+
registry entry shows probed `structured_native`. Check the probed registry,
|
|
331
|
+
not memory.
|
|
332
|
+
|
|
333
|
+
Still stuck? Open an issue at <https://github.com/modelrig/modelrig/issues>.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "modelrig",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.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,17 +24,33 @@
|
|
|
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 shim exists ALONGSIDE this so classic-`Node` moduleResolution consumers (e.g. modelrig-server) type-resolve the sub-path too.",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": { "types": "./dist/index.d.ts", "default": "./dist/index.js" },
|
|
30
|
+
"./receipts": { "types": "./dist/receipts/types.d.ts", "default": "./dist/receipts/types.js" },
|
|
31
|
+
"./package.json": "./package.json",
|
|
32
|
+
"./data/*": "./data/*",
|
|
33
|
+
"./registry/*": "./registry/*"
|
|
34
|
+
},
|
|
27
35
|
"//files": "ALLOWLIST, not a denylist, and the difference is not academic. Without this field npm falls back to .npmignore, then to THIS DIRECTORY's .gitignore — never the repo root's. The root ignores `.env.*`, which is why git has never committed .env.local; the package-level .gitignore says nothing about it, so `npm pack` happily included it. A publish would have shipped 13 live secrets, the Supabase service-role key among them, into an immutable public tarball. Anything added here must be something we would hand a stranger. See __tests__/publish-safety.test.ts.",
|
|
36
|
+
"//files-docs": "GS-3: the customer-facing runs/onboarding docs ship so `npm i modelrig` + read-the-package teaches run→step→artifact OFFLINE (the C1-WALK newbie walkthrough surfaced that the tarball taught nothing about runs). These are the SAME files publish-oss already mirrors PUBLIC via the docs/** allowlist (oss-publish-policy §3) — shipping them in the tarball is consistent, not a new disclosure. Curated named set (NOT docs/**) so internal phase-*/verification reports stay out. docs/llms.txt is GENERATED by scripts/build-docs-site.ts (it also writes the committed copy here) and tripwire-tested against llmsTxt() so the shipped copy can never go stale. publish-safety.test.ts asserts each ships AND carries no leak pattern.",
|
|
28
37
|
"//files-registry": "registry/{registry,leaderboard}.json are fail-open runtime reads (facts.ts/rows.ts degrade to the static baseline when absent) — shipped by R-1 F2 RULING so a clean install routes on the PROBED capability profile the docs and the DVP report describe, not the static fallback. Enumerated file entries, NOT the registry/ dir (it also holds build scripts + tests). Public CC BY 4.0.",
|
|
29
38
|
"//files-data": "`data/` is a RUNTIME dependency, not a build input (R-1 clean-room audit, A1): getPricing() reads data/pricing-map.json from the package root at first cost lookup, with NO existsSync guard — so an absent data/ throws, not degrades. 0.1.0 shipped dist-only and broke the flagship 'effective cost' story on a clean `npm i`. It is public CC BY 4.0 pricing data (data/PROVENANCE.md) — safe to hand a stranger. The tarball clean-room test (__tests__/tarball-cleanroom.test.ts) installs the packed artifact and asserts getPricing() returns a real rate, so this can never silently regress again.",
|
|
30
39
|
"files": [
|
|
31
40
|
"dist",
|
|
32
41
|
"data",
|
|
42
|
+
"receipts.d.ts",
|
|
33
43
|
"registry/registry.json",
|
|
34
44
|
"registry/leaderboard.json",
|
|
35
45
|
"README.md",
|
|
36
46
|
"CHANGELOG.md",
|
|
37
|
-
"LICENSE"
|
|
47
|
+
"LICENSE",
|
|
48
|
+
"docs/quickstart.md",
|
|
49
|
+
"docs/instrumentation.md",
|
|
50
|
+
"docs/how-it-fits.md",
|
|
51
|
+
"docs/migration-playbook.md",
|
|
52
|
+
"docs/agent-skill/SKILL.md",
|
|
53
|
+
"docs/llms.txt"
|
|
38
54
|
],
|
|
39
55
|
"scripts": {
|
|
40
56
|
"build": "tsc",
|
package/receipts.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Root type shim for the `modelrig/receipts` sub-path export (RP-1).
|
|
3
|
+
*
|
|
4
|
+
* The package.json `exports` map points `modelrig/receipts` at
|
|
5
|
+
* `dist/receipts/types` for runtime (Node honours `exports`) and for modern
|
|
6
|
+
* (`Node16`/`NodeNext`/`Bundler`) TypeScript consumers via its `types`
|
|
7
|
+
* condition. Classic `moduleResolution: "Node"` consumers — including this
|
|
8
|
+
* monorepo's `modelrig-server` — ignore `exports` and resolve a bare subpath
|
|
9
|
+
* against the package root, so this file is what they type-resolve. Both paths
|
|
10
|
+
* land on the same declarations; there is no second source of truth.
|
|
11
|
+
*/
|
|
12
|
+
export * from "./dist/receipts/types";
|