modelrig 0.9.0 → 0.11.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 +227 -0
- package/README.md +51 -27
- package/data/PROVENANCE.md +14 -0
- package/data/pricing-map.json +26 -0
- package/dist/adapters/attempt-meta-map.d.ts +8 -1
- package/dist/adapters/attempt-meta-map.d.ts.map +1 -1
- package/dist/adapters/attempt-meta-map.js +4 -0
- package/dist/adapters/build-adapters.js +2 -2
- package/dist/adapters/gemini.d.ts +28 -1
- package/dist/adapters/gemini.d.ts.map +1 -1
- package/dist/adapters/gemini.js +38 -9
- package/dist/adapters/grok.d.ts.map +1 -1
- package/dist/adapters/grok.js +53 -4
- package/dist/adapters/openai-compat.d.ts +8 -2
- package/dist/adapters/openai-compat.d.ts.map +1 -1
- package/dist/adapters/openai-compat.js +8 -1
- package/dist/adapters/openai-stream.d.ts +10 -2
- package/dist/adapters/openai-stream.d.ts.map +1 -1
- package/dist/adapters/openai-stream.js +5 -1
- package/dist/adapters/openai.d.ts +10 -1
- package/dist/adapters/openai.d.ts.map +1 -1
- package/dist/adapters/openai.js +57 -7
- package/dist/adapters/responses-api.d.ts +61 -0
- package/dist/adapters/responses-api.d.ts.map +1 -0
- package/dist/adapters/responses-api.js +124 -0
- package/dist/artifacts/artifact.d.ts +20 -0
- package/dist/artifacts/artifact.d.ts.map +1 -1
- package/dist/artifacts/artifact.js +207 -171
- package/dist/artifacts/index.d.ts +3 -1
- package/dist/artifacts/index.d.ts.map +1 -1
- package/dist/artifacts/index.js +8 -1
- package/dist/bakeoff/groundedness.d.ts +58 -0
- package/dist/bakeoff/groundedness.d.ts.map +1 -0
- package/dist/bakeoff/groundedness.js +165 -0
- package/dist/bakeoff/quality.d.ts +14 -1
- package/dist/bakeoff/quality.d.ts.map +1 -1
- package/dist/bakeoff/quality.js +10 -0
- package/dist/bakeoff/replay.d.ts.map +1 -1
- package/dist/bakeoff/replay.js +4 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +8 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +23 -0
- package/dist/doctor-cli.d.ts +7 -0
- package/dist/doctor-cli.d.ts.map +1 -0
- package/dist/doctor-cli.js +68 -0
- package/dist/doctor-preflight.d.ts +64 -0
- package/dist/doctor-preflight.d.ts.map +1 -0
- package/dist/doctor-preflight.js +167 -0
- package/dist/feedback/coverage.d.ts.map +1 -1
- package/dist/feedback/coverage.js +9 -2
- package/dist/feedback/grade.d.ts +8 -1
- package/dist/feedback/grade.d.ts.map +1 -1
- package/dist/feedback/grade.js +4 -0
- package/dist/gateway/http-protocol.d.ts +19 -0
- package/dist/gateway/http-protocol.d.ts.map +1 -1
- package/dist/gateway/serve-client.d.ts +99 -0
- package/dist/gateway/serve-client.d.ts.map +1 -0
- package/dist/gateway/serve-client.js +360 -0
- package/dist/index.d.ts +31 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +71 -8
- package/dist/prompts/manifest.d.ts +128 -0
- package/dist/prompts/manifest.d.ts.map +1 -0
- package/dist/prompts/manifest.js +214 -0
- package/dist/prompts/prompt-fetch.d.ts +84 -0
- package/dist/prompts/prompt-fetch.d.ts.map +1 -0
- package/dist/prompts/prompt-fetch.js +120 -0
- package/dist/prompts/prompt-resolve.d.ts +76 -0
- package/dist/prompts/prompt-resolve.d.ts.map +1 -0
- package/dist/prompts/prompt-resolve.js +93 -0
- package/dist/prompts/slot-resolver.d.ts +91 -0
- package/dist/prompts/slot-resolver.d.ts.map +1 -0
- package/dist/prompts/slot-resolver.js +135 -0
- package/dist/prompts/tools-register.d.ts +60 -0
- package/dist/prompts/tools-register.d.ts.map +1 -0
- package/dist/prompts/tools-register.js +48 -0
- package/dist/prompts/version-hash.d.ts +29 -0
- package/dist/prompts/version-hash.d.ts.map +1 -0
- package/dist/prompts/version-hash.js +54 -0
- package/dist/prompts-export-cli.d.ts +21 -0
- package/dist/prompts-export-cli.d.ts.map +1 -0
- package/dist/prompts-export-cli.js +130 -0
- package/dist/registry/cache-policy.d.ts.map +1 -1
- package/dist/registry/cache-policy.js +22 -0
- package/dist/registry/pricing-shape.d.ts +38 -8
- package/dist/registry/pricing-shape.d.ts.map +1 -1
- package/dist/registry/pricing-shape.js +40 -12
- package/dist/registry/pricing.d.ts +6 -5
- package/dist/registry/pricing.d.ts.map +1 -1
- package/dist/registry/pricing.js +9 -5
- package/dist/routes/destination-loader.d.ts +16 -0
- package/dist/routes/destination-loader.d.ts.map +1 -0
- package/dist/routes/destination-loader.js +29 -0
- package/dist/routes/draft.d.ts +7 -0
- package/dist/routes/draft.d.ts.map +1 -1
- package/dist/routes/draft.js +12 -0
- package/dist/routes/loader.d.ts.map +1 -1
- package/dist/routes/loader.js +27 -54
- package/dist/routes/output-loader.d.ts +42 -0
- package/dist/routes/output-loader.d.ts.map +1 -0
- package/dist/routes/output-loader.js +112 -0
- package/dist/routes/prompt-loader.d.ts +49 -0
- package/dist/routes/prompt-loader.d.ts.map +1 -0
- package/dist/routes/prompt-loader.js +113 -0
- package/dist/routes/resolved-bundle.d.ts +19 -0
- package/dist/routes/resolved-bundle.d.ts.map +1 -1
- package/dist/routes/resolved-bundle.js +24 -0
- package/dist/routes/screen-loader.d.ts +1 -1
- package/dist/routes/screen-loader.d.ts.map +1 -1
- package/dist/routes/screen-loader.js +8 -1
- package/dist/run/accept.d.ts +3 -0
- package/dist/run/accept.d.ts.map +1 -1
- package/dist/run/accept.js +1 -0
- package/dist/run/cost.d.ts +31 -2
- package/dist/run/cost.d.ts.map +1 -1
- package/dist/run/cost.js +57 -2
- package/dist/run/destination.d.ts +39 -0
- package/dist/run/destination.d.ts.map +1 -0
- package/dist/run/destination.js +45 -0
- package/dist/run/dispatch.d.ts +25 -3
- package/dist/run/dispatch.d.ts.map +1 -1
- package/dist/run/dispatch.js +26 -4
- package/dist/run/raw-defaults.d.ts.map +1 -1
- package/dist/run/raw-defaults.js +6 -1
- package/dist/run/raw-finalize.d.ts +96 -0
- package/dist/run/raw-finalize.d.ts.map +1 -0
- package/dist/run/raw-finalize.js +283 -0
- package/dist/run/raw-knobs.d.ts +36 -0
- package/dist/run/raw-knobs.d.ts.map +1 -0
- package/dist/run/raw-knobs.js +99 -0
- package/dist/run/raw-request.d.ts +24 -0
- package/dist/run/raw-request.d.ts.map +1 -0
- package/dist/run/raw-request.js +69 -0
- package/dist/run/raw-schema.d.ts +76 -0
- package/dist/run/raw-schema.d.ts.map +1 -0
- package/dist/run/raw-schema.js +166 -0
- package/dist/run/raw-search-cost.d.ts +49 -0
- package/dist/run/raw-search-cost.d.ts.map +1 -0
- package/dist/run/raw-search-cost.js +79 -0
- package/dist/run/raw-support.d.ts +64 -6
- package/dist/run/raw-support.d.ts.map +1 -1
- package/dist/run/raw-support.js +84 -11
- package/dist/run/raw.d.ts +25 -26
- package/dist/run/raw.d.ts.map +1 -1
- package/dist/run/raw.js +30 -351
- package/dist/run/record-attempt.d.ts +8 -0
- package/dist/run/record-attempt.d.ts.map +1 -1
- package/dist/run/record-attempt.js +26 -12
- package/dist/run/record-call.d.ts +104 -0
- package/dist/run/record-call.d.ts.map +1 -0
- package/dist/run/record-call.js +277 -0
- package/dist/run/repair-attempt.d.ts +68 -0
- package/dist/run/repair-attempt.d.ts.map +1 -0
- package/dist/run/repair-attempt.js +180 -0
- package/dist/run/resolve-destination.d.ts +49 -0
- package/dist/run/resolve-destination.d.ts.map +1 -0
- package/dist/run/resolve-destination.js +76 -0
- package/dist/run/routing-docs.d.ts.map +1 -1
- package/dist/run/routing-docs.js +9 -4
- package/dist/run/run.d.ts +30 -1
- package/dist/run/run.d.ts.map +1 -1
- package/dist/run/run.js +75 -148
- package/dist/run/telemetry-row.d.ts +92 -0
- package/dist/run/telemetry-row.d.ts.map +1 -0
- package/dist/run/telemetry-row.js +81 -0
- package/dist/run/validate.d.ts +12 -1
- package/dist/run/validate.d.ts.map +1 -1
- package/dist/run/validate.js +31 -0
- package/dist/run/web-search-pricing.d.ts +43 -0
- package/dist/run/web-search-pricing.d.ts.map +1 -0
- package/dist/run/web-search-pricing.js +56 -0
- package/dist/settings/grading-guide.d.ts +27 -1
- package/dist/settings/grading-guide.d.ts.map +1 -1
- package/dist/status.d.ts +6 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +4 -1
- package/dist/telemetry/artifact-store.d.ts.map +1 -1
- package/dist/telemetry/artifact-store.js +11 -0
- package/dist/telemetry/attempt-meta.d.ts +66 -1
- package/dist/telemetry/attempt-meta.d.ts.map +1 -1
- package/dist/telemetry/attempt-meta.js +27 -1
- package/dist/telemetry/exporter-setup.d.ts +2 -0
- package/dist/telemetry/exporter-setup.d.ts.map +1 -1
- package/dist/telemetry/exporter-setup.js +6 -1
- package/dist/telemetry/grade-mappers.d.ts +5 -2
- package/dist/telemetry/grade-mappers.d.ts.map +1 -1
- package/dist/telemetry/grade-mappers.js +8 -2
- package/dist/telemetry/grades-sql.d.ts +4 -1
- package/dist/telemetry/grades-sql.d.ts.map +1 -1
- package/dist/telemetry/grades-sql.js +26 -3
- package/dist/telemetry/ingest-protocol.d.ts +26 -2
- package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
- package/dist/telemetry/ingest-protocol.js +32 -29
- package/dist/telemetry/lanes.d.ts +31 -0
- package/dist/telemetry/lanes.d.ts.map +1 -0
- package/dist/telemetry/lanes.js +41 -0
- package/dist/telemetry/org-identity.d.ts +47 -0
- package/dist/telemetry/org-identity.d.ts.map +1 -0
- package/dist/telemetry/org-identity.js +100 -0
- package/dist/telemetry/route-registration.d.ts +48 -0
- package/dist/telemetry/route-registration.d.ts.map +1 -0
- package/dist/telemetry/route-registration.js +77 -0
- package/dist/telemetry/sinks.d.ts +10 -1
- package/dist/telemetry/sinks.d.ts.map +1 -1
- package/dist/telemetry/sinks.js +52 -6
- package/dist/telemetry/store-migrations.d.ts.map +1 -1
- package/dist/telemetry/store-migrations.js +13 -0
- package/dist/tenancy/api-key-format.d.ts +16 -1
- package/dist/tenancy/api-key-format.d.ts.map +1 -1
- package/dist/tenancy/api-key-format.js +17 -0
- package/dist/types/provider.d.ts +20 -3
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/provider.js +1 -0
- package/dist/types/route.d.ts +76 -0
- package/dist/types/route.d.ts.map +1 -1
- package/dist/types/telemetry.d.ts +30 -2
- package/dist/types/telemetry.d.ts.map +1 -1
- package/docs/agent-skill/SKILL.md +75 -5
- package/docs/instrumentation.md +98 -36
- package/docs/llms.txt +96 -16
- package/docs/managed-serving.md +150 -0
- package/docs/mcp-oracle.md +75 -2
- package/docs/migration-playbook.md +5 -4
- package/docs/quickstart.md +169 -83
- package/package.json +3 -2
- package/registry/registry.json +82 -0
- package/dist/feedback/feedback.d.ts +0 -35
- package/dist/feedback/feedback.d.ts.map +0 -1
- package/dist/feedback/feedback.js +0 -39
package/docs/instrumentation.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Artifact instrumentation (runs, steps, artifacts)
|
|
2
2
|
|
|
3
|
+
**The question this page answers: how do I record what my pipeline actually did, run by run, step by step, without changing what it does?**
|
|
4
|
+
|
|
3
5
|
ModelRig can record the **execution graph** of any pipeline your code runs — the
|
|
4
6
|
run, its steps, and the artifacts each step produced (the prompt, the raw model
|
|
5
7
|
text, the parsed output, the evidence it grounded on) — with lineage,
|
|
@@ -19,11 +21,13 @@ byte-for-byte as before.) `modelrig status` prints the current posture (on/off +
|
|
|
19
21
|
second axis — the recommended, consent-gated [content custody](content-custody.md)
|
|
20
22
|
path — never blurred with the telemetry axis here.
|
|
21
23
|
|
|
22
|
-
> **
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
>
|
|
24
|
+
> **What `artifact.save` keeps.** This page is the metadata axis: every saved
|
|
25
|
+
> artifact records a metadata row with a sha256 integrity hash, whatever your
|
|
26
|
+
> content setting. Whether the serialized *bytes* are kept as well is your
|
|
27
|
+
> organization's [content custody](content-custody.md) posture: new
|
|
28
|
+
> organizations are created `managed` (bytes kept in your managed store); posture
|
|
29
|
+
> `metadata` keeps hashes only and `off` captures nothing. A `zeroRetention` run
|
|
30
|
+
> refuses every artifact fail-closed at the SDK gate.
|
|
27
31
|
|
|
28
32
|
## The namespace toggle
|
|
29
33
|
|
|
@@ -57,11 +61,11 @@ const rig = createRig(loadConfigFromEnv());
|
|
|
57
61
|
|
|
58
62
|
// 1. Open a run context around the pipeline. episodeKey bridges grades;
|
|
59
63
|
// it is a grouping key, not a unique id — a natural choice is your own
|
|
60
|
-
// per-run attempt id.
|
|
64
|
+
// per-run attempt id. environment fills the Env column on /runs.
|
|
61
65
|
const run = rig.artifacts.run.start({
|
|
62
66
|
pipeline: "my-pipeline",
|
|
63
67
|
episodeKey: runAttemptId,
|
|
64
|
-
environment: process.env.NODE_ENV,
|
|
68
|
+
environment: process.env.NODE_ENV, // e.g. "production", "staging", "test"
|
|
65
69
|
});
|
|
66
70
|
try {
|
|
67
71
|
for (const step of steps) {
|
|
@@ -111,6 +115,23 @@ right for one run at a time on a request/async path.
|
|
|
111
115
|
and honest. Never route a stage through the rig just to mint a step row; the
|
|
112
116
|
console renders "cost n/a" for un-routed steps rather than inventing a number.
|
|
113
117
|
|
|
118
|
+
### Record a call your code made itself (`rig.recordCall`)
|
|
119
|
+
|
|
120
|
+
_Needs `modelrig` 0.11.0 or newer._
|
|
121
|
+
|
|
122
|
+
If a stage's model call has to stay a direct provider call, report it instead:
|
|
123
|
+
`rig.recordCall({ provider, model, usage: { tokensIn, tokensOut, tokensCached }, servedTier, latencyMs, stepKey })`
|
|
124
|
+
right after the call returns. Inside a run context it records a step row for
|
|
125
|
+
that `stepKey` (stored as `ground-truth`, the only joined-inference value),
|
|
126
|
+
joined to the recorded inference, so the step gets a cost instead of "cost n/a".
|
|
127
|
+
The console shows that step and its call as **recorded · unverified**, never as
|
|
128
|
+
measured:
|
|
129
|
+
ModelRig neither dispatched nor validated the call, and the tokens are the ones
|
|
130
|
+
you reported. It is synchronous and never throws (bad input warns and returns
|
|
131
|
+
`inferenceId: null`); the returned `inferenceId` is gradeable with `rig.grade`.
|
|
132
|
+
Full contract, usage convention and cost notes:
|
|
133
|
+
[Not captured → record them](not-captured.md#calls-your-code-makes-itself-record-them).
|
|
134
|
+
|
|
114
135
|
## Tags, dimensions, and the graph
|
|
115
136
|
|
|
116
137
|
ModelRig has **two** grouping mechanisms, and they compose:
|
|
@@ -137,22 +158,29 @@ process** (`required dimension "project" missing from run tags — costs … wil
|
|
|
137
158
|
under-attributed on /costs`) — on the routed lane and the raw lane alike. It is a
|
|
138
159
|
hygiene warning, never an error: the run proceeds.
|
|
139
160
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
[tenants.md](./tenants.md).
|
|
147
|
-
|
|
148
|
-
`
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
161
|
+
### Reserved tag keys (the one list)
|
|
162
|
+
|
|
163
|
+
Any other key is yours. These are reserved:
|
|
164
|
+
|
|
165
|
+
| Key | Who sets it | What it means |
|
|
166
|
+
|---|---|---|
|
|
167
|
+
| `subject` | you | Your end-customer's **opaque** id. It powers the per-customer tenant statements on `/tenants`; hash it if it looks like PII ([tenants.md](./tenants.md)). A tag named `client` or `tenant` is only a cost dimension. |
|
|
168
|
+
| `feature` | you | The product feature the call serves. |
|
|
169
|
+
| `user` | you | The end user, as an opaque id. |
|
|
170
|
+
| `session` | you | The conversation or session the call belongs to. |
|
|
171
|
+
| `cost_center` | you | The team or budget to attribute spend to. |
|
|
172
|
+
| `run_id` | **the SDK**, inside a run | The run's `episodeKey`, or its id when you gave none. **Don't set it yourself.** A value you pass overrides the stamp, so the call no longer joins its run on `/runs` and the console's Run ID group-by disagrees with `/runs`. Put your own attempt id in a key of your own, such as `pipeline_attempt_id`. |
|
|
173
|
+
| `step` | **the SDK**, inside a run | The current step key: your explicit `stepKey`, else `tags.step`, else an auto `route#n` / `provider/model#n` key. Name steps with `stepKey`. |
|
|
174
|
+
| `lane`, `zdr_enforced`, `rung`, `key_ownership` | ModelRig | Set by the SDK or the hosted gateway. You can't override them. |
|
|
175
|
+
| `replay`, `replay_capture`, `variant`, `cutover` | ModelRig tooling | Set by bake-offs and replays. |
|
|
176
|
+
|
|
177
|
+
The five keys you set are first-class group-bys in the cost and coverage views, so use them verbatim; `modelrig validate` warns about near-misses such as `costcenter` or `team`. `project` and `environment` are common conventions, not reserved.
|
|
178
|
+
|
|
179
|
+
**Inside a `run.start` / `run.scope` context the SDK stamps `run_id` and `step`
|
|
180
|
+
on every attempt row of both lanes.** Outside a run context nothing is stamped,
|
|
181
|
+
so the row is byte-identical. This is why runs are the standard path: a
|
|
182
|
+
`run.start` alone fills both `/runs` (the graph) and `/projects` (the tag tree),
|
|
183
|
+
with no hand-written id.
|
|
156
184
|
|
|
157
185
|
**How they compose.** Inside a `run.start` context, both `rig.run` and
|
|
158
186
|
`rig.runRaw` record a **ground-truth step** and carry the run's `run_id` tag, so
|
|
@@ -234,8 +262,49 @@ Every entry point is fail-open: a telemetry-buffer write that fails is logged an
|
|
|
234
262
|
dropped, never thrown into your code. Instrumentation that runs *after* an
|
|
235
263
|
expensive model call must never be the thing that kills the run.
|
|
236
264
|
|
|
265
|
+
## Which org am I exporting to?
|
|
266
|
+
|
|
267
|
+
A valid key minted in the wrong org fails silently: every ingest call succeeds,
|
|
268
|
+
and the runs land in someone else's console. So the first time a process flushes
|
|
269
|
+
telemetry, the SDK asks `GET /v1/whoami` once and logs where it is exporting:
|
|
270
|
+
|
|
271
|
+
```text
|
|
272
|
+
[modelrig] exporting to org «Acme Analytics» (acme-analytics) as key «production exporter» rig_sk_…ab12
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The call runs in the background. It never delays or drops a batch, and if it
|
|
276
|
+
fails it says nothing (a rejected key already gets its own loud `AUTH FAILED`
|
|
277
|
+
line). To have the SDK check the org for you, pass the slug you expect in code:
|
|
278
|
+
|
|
279
|
+
| Config option | Type | Default | What it does |
|
|
280
|
+
|---|---|---|---|
|
|
281
|
+
| `expectOrg` | `string` (org slug) | absent | When the key resolves to a different org, logs `[modelrig] ORG MISMATCH: key resolves to «a», expectOrg is «b» — telemetry is landing in «a»` loudly on every flush once the org is known. It never throws and never drops telemetry: the rows still land in the key's org. |
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
const rig = createRig({ ...loadConfigFromEnv(), expectOrg: "acme-analytics" });
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
It is a code option, not an environment variable. To gate a deploy or CI step
|
|
288
|
+
on the same check, run `modelrig doctor --expect-org acme-analytics`. It exits 1 and
|
|
289
|
+
prints both slugs when the key belongs to a different org. More on keys,
|
|
290
|
+
orgs and the console picker: [Orgs and keys](https://modelrig.dev/orgs-and-keys.html).
|
|
291
|
+
|
|
237
292
|
## See it
|
|
238
293
|
|
|
294
|
+
Check first that the console is showing the org your key exports to:
|
|
295
|
+
|
|
296
|
+
<!-- BLOCK:CONFIRM_ORG -->
|
|
297
|
+
**Confirm the org before you trust an empty console.** Run `modelrig doctor` from the same environment your app runs in. It asks GET /v1/whoami which organization the key belongs to, and the name it prints must match the org badge in the console header. If it names a different organization, your data is landing there: switch the console to that organization, or mint a key in the one you meant (https://modelrig.dev/orgs-and-keys.html).
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
npx modelrig doctor
|
|
301
|
+
# [modelrig] key rig_sk_…AbCd → org «Your Org» ✓ (ingest https://api.modelrig.ai, …)
|
|
302
|
+
|
|
303
|
+
# No CLI in that environment? Ask the API directly:
|
|
304
|
+
curl -H "Authorization: Bearer $MODELRIG_API_KEY" https://api.modelrig.ai/v1/whoami
|
|
305
|
+
```
|
|
306
|
+
<!-- /BLOCK:CONFIRM_ORG -->
|
|
307
|
+
|
|
239
308
|
The console's **Runs** tab lists every instrumented run; `/runs/[id]` is the
|
|
240
309
|
semantic chain (artifacts grouped by step, with hashes, costs, and grade
|
|
241
310
|
badges) and `/artifacts/[id]` shows one artifact's lineage, versions, and
|
|
@@ -252,15 +321,15 @@ that decides how much confidence a swap proposal carries.
|
|
|
252
321
|
|
|
253
322
|
```ts
|
|
254
323
|
// One call — the inference id rides on every RunResult:
|
|
255
|
-
const result = await rig.run("example.support_summarize", { input, tags: {
|
|
324
|
+
const result = await rig.run("example.support_summarize", { input, tags: { subject } });
|
|
256
325
|
rig.grade({ kind: "inference", id: result.meta.inferenceId }, { score: 1, kind: "human" });
|
|
257
326
|
|
|
258
|
-
// A whole run
|
|
259
|
-
rig.grade({ kind: "run", id:
|
|
327
|
+
// A whole run: the subject id is the run's run_id (its episodeKey, or its id).
|
|
328
|
+
rig.grade({ kind: "run", id: runAttemptId }, { score: 0.2, comment: "CAGR off by 10x", kind: "human" });
|
|
260
329
|
```
|
|
261
330
|
|
|
262
331
|
- **The subject** is `{ kind: "inference", id }` (`result.meta.inferenceId`),
|
|
263
|
-
`{ kind: "run", id }` (the run's `run_id
|
|
332
|
+
`{ kind: "run", id }` (the run's `run_id` — its `episodeKey`, or its id), or
|
|
264
333
|
`{ kind: "artifact", id }`. **`opts`** is `{ score, scale?, comment?, kind,
|
|
265
334
|
source? }` — the `score` in `[0, 1]` is the primitive (up = 1, down = 0), and
|
|
266
335
|
the pass/fail verdict is derived from it against the route threshold. A subject
|
|
@@ -274,8 +343,8 @@ rig.grade({ kind: "run", id: run_id }, { score: 0.2, comment: "CAGR off by 10x",
|
|
|
274
343
|
network call and never a failure your code can see; the cloud mirror rides the
|
|
275
344
|
telemetry exporter's normal cadence.
|
|
276
345
|
- **Why it matters.** A route with **≥10 attached grades** reaches **Rung 1** on
|
|
277
|
-
`/coverage`; a route with no schema to conform to (
|
|
278
|
-
proposal confidence only this way. Grades are also what let `/insights`
|
|
346
|
+
`/coverage`; a route with no schema to conform to (an `output: text` prose
|
|
347
|
+
route, or a schema-less JSON route) gains proposal confidence only this way. Grades are also what let `/insights`
|
|
279
348
|
surface a task that is degrading.
|
|
280
349
|
- **From a non-SDK surface** (an end user clicking thumbs-down in your web app),
|
|
281
350
|
post the same fields to `POST /v1/grade` with a `rig_sk_` key carrying the
|
|
@@ -340,10 +409,3 @@ function recordStep(
|
|
|
340
409
|
Call `recordStep(step, { … })` once per stage inside the `run.start` / `run.end`
|
|
341
410
|
context above, and every entry point stays fail-open — a save that fails is
|
|
342
411
|
logged and dropped, never thrown into your pipeline.
|
|
343
|
-
|
|
344
|
-
> **In-repo example (reconstructed via onboarding-test run-2).** ModelRig's own
|
|
345
|
-
> customer-zero pipeline (the InferWealth v3 report generator) was stripped back
|
|
346
|
-
> to a clean slate to be re-onboarded through this exact doc. Its live seam is
|
|
347
|
-
> reconstructed as part of onboarding-test run-2; until then, the snippet above
|
|
348
|
-
> is the reference. Do not resurrect a deleted seam from git history — writing a
|
|
349
|
-
> fresh one is the point.
|
package/docs/llms.txt
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
> grounding, and caching measured empirically, with a capability-aware
|
|
5
5
|
> gateway that routes by them. Registry data CC BY 4.0; code Apache-2.0.
|
|
6
6
|
|
|
7
|
-
_This reference is as of modelrig v0.
|
|
7
|
+
_This reference is as of modelrig v0.11.0 (the published npm package). Any surface below that a newer workspace adds is marked "unreleased — workspace only" until its release; nothing is so marked as of this version._
|
|
8
8
|
|
|
9
9
|
## Docs
|
|
10
10
|
|
|
@@ -36,6 +36,12 @@ _This reference is as of modelrig v0.9.0 (the published npm package). Any surfac
|
|
|
36
36
|
- [Template: call disposition QA](https://modelrig.dev/template-call-disposition-qa.html): A starting point for checking a call's logged disposition and scoring it against your QA scorecard from the transcript: a starter schema with a TODO disposition slot and a placeholder scorecard object, a starter grading guide that checks every judgment is grounded in the transcript, a golden checklist for disputed and sensitive calls, and an escalation starter. Transcript in — you bring your own transcription; no accuracy claims.
|
|
37
37
|
- [Template: financial classification](https://modelrig.dev/template-financial-classification.html): A starting point for classifying a financial record into your categories and risk flags: a starter schema with TODO enum slots, a starter grading guide that checks the classification is grounded and the risk flag follows your rules, a golden checklist for edge-of-rule and sensitive records, and an escalation starter. Regulated data — the fit note quotes the published custody terms verbatim; no other compliance claim, no accuracy claims.
|
|
38
38
|
- [Template: medical classification](https://modelrig.dev/template-medical-classification.html): A starting point for classifying a clinical document into your categories: a starter schema with a TODO category slot, a starter grading guide that keeps the output a classification rather than a clinical judgment, a golden checklist for cross-category and clinically significant documents, and an escalation starter. Regulated data — the fit note quotes the published custody terms verbatim; no other compliance claim, no accuracy claims.
|
|
39
|
+
- [Managed serving](https://modelrig.dev/managed-serving.html): Serve a route's model on ModelRig's own credentials — destination: modelrig-served — paid from your prepaid balance at provider list cost plus the flat fee: declare the destination, enable managedServing in code, mint a serve-scoped key, opt the organization in, fund the balance; what each refusal teaches; the destination tag on the row; and the honest direct default that nothing here changes.
|
|
40
|
+
- [Orgs and keys](https://modelrig.dev/orgs-and-keys.html): One key = one org: how to see which organization a key exports to (`modelrig doctor`, `GET /v1/whoami`, the console badge), how the console picks the org it shows (the picker, `409 org_required`, `401 session_expired`), why a valid key in the wrong org looks like an empty console, and `doctor --expect-org` / `createRig({ expectOrg })`.
|
|
41
|
+
- [Choosing a lane](https://modelrig.dev/choosing-a-lane.html): Observe (wrap what you have in a run), the `rig.runRaw` seam (one adapter for a router you already own) or named routes (`rig.run`): when each fits, what you get, what you give up, what it costs — and why to start with observe.
|
|
42
|
+
- [Not captured](https://modelrig.dev/not-captured.html): What ModelRig does not see — direct provider SDK calls, streams read outside the SDK, fallbacks after a fail-closed schema, calls outside a run, embeddings — and the console tells: `cost n/a` steps, a missing `inferenceId`, a run total below your provider bill.
|
|
43
|
+
- [Deploying on hosted workers](https://modelrig.dev/deploying.html): Set MODELRIG_API_KEY on every process; Render's env-group vs service-override precedence; a change needs a finished redeploy and a new shell; rotate keys mint → set → redeploy → doctor → revoke; verify after every deploy with `doctor --expect-org`, then one run on /runs; notes for Vercel, Fly and Docker.
|
|
44
|
+
- [Troubleshooting an empty console](https://modelrig.dev/troubleshooting.html): The "console is empty" decision tree: is the process exporting (doctor: AUTH FAILED / no export sink / org ✓), is it the org you are viewing, are the calls inside a run, is the missing call on a ModelRig lane, and does your install match the docs.
|
|
39
45
|
- [Leaderboard](https://modelrig.dev/leaderboard.html): Models ranked by effective cost per 1,000 schema-conformant outputs, with declared-vs-probed discrepancy flags.
|
|
40
46
|
|
|
41
47
|
## Source
|
|
@@ -43,9 +49,77 @@ _This reference is as of modelrig v0.9.0 (the published npm package). Any surfac
|
|
|
43
49
|
- [Public repo](https://github.com/modelrig/modelrig): probe suite, registry data, contribution kits.
|
|
44
50
|
- [Registry JSON](https://github.com/modelrig/modelrig/blob/main/registry/registry.json): three layers per model (declared / probed / observed aggregates) with as_of stamps.
|
|
45
51
|
|
|
52
|
+
## Start here: from zero to your first run on /runs
|
|
53
|
+
|
|
54
|
+
_The default ingest host is https://api.modelrig.ai (override with `MODELRIG_INGEST_URL` only if you were given another). Choosing how your calls reach ModelRig: https://modelrig.dev/choosing-a-lane.html. Which org your data lands in: https://modelrig.dev/orgs-and-keys.html. Hosted workers: https://modelrig.dev/deploying.html. What is not captured: https://modelrig.dev/not-captured.html. Console empty: https://modelrig.dev/troubleshooting.html._
|
|
55
|
+
|
|
56
|
+
**Mint a key in the org you want the data in.** Sign in at https://app.modelrig.ai, check the organization name in the header badge, and create a key on the API keys page. The `rig_sk_` secret is shown once. One key = one org: every row the key exports lands in the organization it was minted in, whichever organization you happen to be viewing in the console. Set it on every service, worker and cron that creates a rig; your provider keys stay where they are.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Wherever your app reads configuration: .env.local, your platform's env vars, a secret manager. Never in code.
|
|
60
|
+
MODELRIG_API_KEY=rig_sk_…
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Confirm the org before you trust an empty console.** Run `modelrig doctor` from the same environment your app runs in. It asks GET /v1/whoami which organization the key belongs to, and the name it prints must match the org badge in the console header. If it names a different organization, your data is landing there: switch the console to that organization, or mint a key in the one you meant (https://modelrig.dev/orgs-and-keys.html).
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx modelrig doctor
|
|
67
|
+
# [modelrig] key rig_sk_…AbCd → org «Your Org» ✓ (ingest https://api.modelrig.ai, …)
|
|
68
|
+
|
|
69
|
+
# No CLI in that environment? Ask the API directly:
|
|
70
|
+
curl -H "Authorization: Bearer $MODELRIG_API_KEY" https://api.modelrig.ai/v1/whoami
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Wrap one pipeline execution in a run.** Every pipeline execution is a run. Wrap each execution in `run.scope` (or `run.start` / `run.end`), name one step per model call with `stepKey`, `artifact.save` the work products, and pass `environment` so the Env column on /runs is filled. Inside the run the SDK stamps `run_id` and `step` on every call. `run_id` is reserved: don't set it yourself.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import { createRig, loadConfigFromEnv } from "modelrig";
|
|
77
|
+
|
|
78
|
+
const rig = createRig(loadConfigFromEnv());
|
|
79
|
+
|
|
80
|
+
// Every pipeline execution is a run. Inside the scope the SDK records each
|
|
81
|
+
// rig.run / rig.runRaw call as a step and stamps run_id and step on every row.
|
|
82
|
+
// run_id is reserved: don't set it yourself (put your own attempt id in a tag of
|
|
83
|
+
// your own, such as pipeline_attempt_id).
|
|
84
|
+
await rig.artifacts.run.scope(
|
|
85
|
+
{ pipeline: "support", episodeKey: ticketId, environment: process.env.NODE_ENV ?? "development" },
|
|
86
|
+
async () => {
|
|
87
|
+
const result = await rig.run("example.support_summarize", {
|
|
88
|
+
input: { ticket, product, priorContext },
|
|
89
|
+
tags: { subject: customer, feature: "summarize" },
|
|
90
|
+
stepKey: "summarize", // names this step on /runs
|
|
91
|
+
});
|
|
92
|
+
rig.artifacts.artifact.save(result.output, { name: "summary", type: "step_output", stepKey: "summarize" });
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
await rig.close(); // flushes the exporter — call once at shutdown
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**What ModelRig does not see unless you record it.** ModelRig records the calls your code makes through the SDK (`rig.run`, `rig.runBundle`, `rig.runRaw`, `rig.runRawStream`) or the gateway, plus any call your code makes itself and reports with `rig.recordCall`. Nothing else is seen. Five things stay invisible or unattached unless you record them: (1) direct provider SDK calls, with or without a JSON schema; (2) streams you read from a provider client yourself; (3) a fallback your code makes after the raw lane refuses a `responseSchema` it cannot enforce (deepseek, deepinfra, fireworks and zai fail closed with `invariant_violation`); (4) calls made outside `run.scope` / `run.start`, which land on /inferences but on no run; (5) embeddings. A recorded call shows as "recorded · unverified": ModelRig neither dispatched nor validated it, and its tokens are the numbers you reported. Details and the console tells: https://modelrig.dev/not-captured.html.
|
|
100
|
+
|
|
101
|
+
**The console is empty: five checks, in order.** If /runs (or /inferences) is empty for the org in the header badge, work these checks in order. The full tree is https://modelrig.dev/troubleshooting.html.
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
1. npx modelrig doctor, from the environment your app runs in.
|
|
105
|
+
no export sink configured → this process does not see MODELRIG_API_KEY: set it, redeploy, open a new shell.
|
|
106
|
+
AUTH FAILED (401 / 403) → the key is mistyped, revoked or not accepted: mint a key, set it, redeploy, open a new shell.
|
|
107
|
+
2. The org doctor prints is not the org in the badge → wrong org: switch the console, or mint a key in the org you meant.
|
|
108
|
+
3. /inferences has rows but /runs is empty → the calls are not inside run.scope / run.start.
|
|
109
|
+
4. A run is missing a step or a cost → that call is not on a ModelRig lane (see not-captured).
|
|
110
|
+
5. A doc describes something your install lacks → compare `npm ls modelrig` with the version stamp in modelrig.dev/llms.txt.
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**After you change the key on a hosted worker.** A hosted process reads the environment it was started with. After you set or change MODELRIG_API_KEY, wait for the redeploy to finish, then open a new shell: a shell attached before the deploy still has the old environment. Confirm from inside it that the key resolves to the org you expect.
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx modelrig doctor --expect-org your-org-slug # exits non-zero if the key belongs to another org
|
|
117
|
+
```
|
|
118
|
+
|
|
46
119
|
## Capabilities (the full surface)
|
|
47
120
|
|
|
48
121
|
- **Runs & projects** — the run → step → artifact graph at /runs, grouped by tag at /projects (the standard path above): https://modelrig.dev/instrumentation.html.
|
|
122
|
+
- **Recorded calls (`rig.recordCall`, modelrig ≥ 0.11.0)** — a provider call your code makes itself (no lane fits) is reported after it returns: one row, priced at list for the served tier from the tokens you report, joined to the run/step, gradeable, badged "recorded · unverified" (ModelRig neither dispatched nor validated it), never in routed/conformance math: https://modelrig.dev/not-captured.html.
|
|
49
123
|
- **Tenants & statements** — a `subject` tag or project-scoped key becomes a per-customer tenant with a derived statement (attempted, verified, billable, true cost; never content or another org's data): https://modelrig.dev/tenants.html.
|
|
50
124
|
- **Published receipts** — a signed, login-free receipt of a bake-off or route at /r/<id>, verifiable by anyone at /v1/verify/<id>; internal names aliased, content never leaves: https://modelrig.dev/receipts.html.
|
|
51
125
|
- **Analytics query API** — a self-describing, read-only query surface over your own telemetry (`GET /v1/analytics/meta`, `POST /v1/analytics/query`): an allowlisted SELECT-only grammar that returns metadata only (enums, numbers, hashes), never content: https://modelrig.dev/optimization-loop.html.
|
|
@@ -124,7 +198,6 @@ _The runtime routing model — the CANDIDATE ladder of one `rig.run` call, disti
|
|
|
124
198
|
| id | capability | what is true today | tracking |
|
|
125
199
|
|---|---|---|---|
|
|
126
200
|
| B1 | Route-declared judge (hosted lane) | The SDK-lane `qualityGate` predicate (it rejects a schema-valid output as `quality_rejected`) ships; a route-DECLARED judge that runs on the hosted lane — a `judge:` block in the bundle with a rubric — is not built, and the bake-off-scoped `bakeoff.judge` (offline scoring) (which only measures value accuracy during a bake-off) is a separate SDK feature that never serves the hosted lane. | Route-declared judge — customer request on file (2026-08-21); follows the SDK predicate |
|
|
127
|
-
| B2 | Structure on the raw lane | `runRaw` never validates or repairs; `responseFormat: "json_object"` shapes MIME, not schema, so `meta.validated` stays false — structure on the raw lane is ruled out by design, and the path is to graduate that step to a route. | ruled schema-free on the raw lane; re-request on file |
|
|
128
201
|
| B6 | Run-level idempotency / dedupe key | Two identical `rig.run` calls dispatch twice; the only idempotency is the exporter's watermark. | — |
|
|
129
202
|
|
|
130
203
|
### Deferred
|
|
@@ -144,6 +217,7 @@ _The runtime routing model — the CANDIDATE ladder of one `rig.run` call, disti
|
|
|
144
217
|
|
|
145
218
|
| id | capability | what is true today | tracking |
|
|
146
219
|
|---|---|---|---|
|
|
220
|
+
| B2 | Repair on the raw lane | `runRaw` validates a declared `responseSchema` (the provider's native structured-output directive plus a client-side Ajv check; a miss is a billed, terminal `content_invalid`) but never repairs, never retries, and never falls through — `meta.validated` stays false because no ladder stands behind the check, `responseFormat: "json_object"` shapes MIME only, and a schema miss you want recovered means graduating that step to a route. | [route-bundles](https://modelrig.dev/route-bundles.html) — raw-lane responseSchema (validate, never repair) |
|
|
147
221
|
| B7 | Automatic model swap | No automated change to what serves a route happens without a bake-off and human approval (`modelrig swap execute <id> --approve`); you (or your agent) can change it directly at any time — instant, versioned, reversible. | [optimization-loop](https://modelrig.dev/optimization-loop.html) — human-approved swaps only |
|
|
148
222
|
| B8 | Provider cache resource lifecycle | ModelRig passes your cache handle through and prices the hits; it never creates, refreshes, or deletes the resource — your heartbeat does. | [caching-lifecycle](https://modelrig.dev/caching-lifecycle.html) — who owns what |
|
|
149
223
|
| B9 | Tool execution | ModelRig normalizes the tool-call protocol and returns `meta.toolCalls`; the caller runs the tool. | [route-bundles](https://modelrig.dev/route-bundles.html) — tools are normalized, never executed |
|
|
@@ -151,43 +225,48 @@ _The runtime routing model — the CANDIDATE ladder of one `rig.run` call, disti
|
|
|
151
225
|
|
|
152
226
|
## Build a route bundle correctly (for coding agents)
|
|
153
227
|
|
|
154
|
-
A route bundle is one YAML file: `route`, `version`, `candidates`, `schema` (a JSON Schema file path or null), `prompt` (a system template path + declared `variables`), and `
|
|
228
|
+
A route bundle is one YAML file: `route`, `version`, `candidates`, `schema` (a JSON Schema file path or null), `prompt` (a system template path + declared `variables`), `policy`, and optionally `output` (`json`, the default, or `text` for prose) and a per-call `prompt.user` template. Seven constraints decide whether it works — the first five fail loudly at load, the sixth is a documented delivery rule, the last fails SILENTLY:
|
|
155
229
|
|
|
156
230
|
- Schema and prompt files must EXIST at the paths the bundle names; paths resolve relative to the bundle file. A bundle referencing a missing schema fails at load, not at run.
|
|
231
|
+
- A route that returns PROSE or MARKDOWN (an editorial pass, a memo, a report) must declare `output: text` — the response is then returned verbatim as a string, never JSON-parsed, no repair rung. The default (`output: json`, even with `schema: null`) still requires parseable JSON and rejects prose as `content_invalid` with a fixHint naming this key. `output: text` cannot combine with `schema:`, `require: schema_conformant`, or `repair:` (load errors). The one declared prose check is `output: { kind: text, min_chars: N }`. See https://modelrig.dev/route-bundles.html#prose-and-markdown-output-output-text.
|
|
157
232
|
- Every `{{variable}}` the prompt (or `grounding.query`) uses must be declared in `prompt.variables`. Undeclared references fail at load time.
|
|
233
|
+
- A route that will be called with a GEMINI CACHE HANDLE (`rig.run(…, { cache: { key, provider: "gemini" } })`) must put the per-call delta in a `prompt.user` template (the user turn) and the shared prefix in the cache resource: Gemini forbids a system instruction next to `cachedContent`, so with a handle everything the route rendered is delivered AS THE USER TURN (never dropped; `meta.prompt_delivery` = `user+cache`). `prompt.system` may be omitted when `prompt.user` is declared; both share `prompt.variables`. The loader warns on a `cache:` route with a Gemini candidate, a non-empty `prompt.system` and no `prompt.user`. See https://modelrig.dev/caching-lifecycle.html#a-cache-handle-changes-where-the-template-is-delivered.
|
|
158
234
|
- Candidates come from the PROBED registry above, never from memory — declared capability and probed behaviour differ, which is ModelRig's entire thesis.
|
|
159
235
|
- `json: native` is a CLAIM ABOUT THE MODEL, not a preference: set it only for a model whose registry entry shows probed native strict enforcement (`structured_native`). Otherwise omit it and let the emulation ladder handle structure — a wrong `json: native` loads fine and then behaves unlike the code it replaced.
|
|
160
236
|
- `MODELRIG_API_KEY` is OPTIONAL for LOCAL route-running: everything runs locally without it, and it only decides where telemetry lands (the ModelRig console). But observing your runs IN that console requires a free account + key — for that it is not optional.
|
|
161
237
|
- `MODELRIG_API_KEY` gates the exporter PER PROCESS — set it on EVERY service, worker, and cron that constructs a rig; a process without it buffers to its own local SQLite and ships nothing. A multi-service deployment that sets the key on only the web tier silently loses every row a worker or cron produced.
|
|
162
|
-
- Every pipeline execution is a RUN — the standard path, not an appendix: wrap it in `rig.artifacts.run.start` / `run.scope`, name one `step()` per model-call family, `artifact.save` the work products, and tag `subject` / `feature`. Acceptance is the run appearing on /runs with its steps. Inside a run context, BOTH `rig.run` and `rig.runRaw` record a ground-truth STEP (the per-run tree at /runs/[id]); name it with `stepKey` on either lane (explicit > `tags.step` > an auto `route#n` / `provider/model#n` key). An `artifact.save` with no `stepKey` joins the ambient (most recent) step. TAGS are the flat filter/rollup (/costs); the GRAPH is the per-run tree — they compose, and inside a run context the SDK stamps `tags.run_id` (the run's episodeKey, else its id) on every row of both lanes
|
|
238
|
+
- Every pipeline execution is a RUN — the standard path, not an appendix: wrap it in `rig.artifacts.run.start` / `run.scope`, name one `step()` per model-call family, `artifact.save` the work products, and tag `subject` / `feature`. Acceptance is the run appearing on /runs with its steps. Inside a run context, BOTH `rig.run` and `rig.runRaw` record a ground-truth STEP (the per-run tree at /runs/[id]); name it with `stepKey` on either lane (explicit > `tags.step` > an auto `route#n` / `provider/model#n` key). An `artifact.save` with no `stepKey` joins the ambient (most recent) step. TAGS are the flat filter/rollup (/costs); the GRAPH is the per-run tree — they compose, and inside a run context the SDK stamps `tags.run_id` (the run's episodeKey, else its id) and `tags.step` (the current step key) on every row of both lanes. `run_id` is RESERVED: don't set it yourself (a value you pass would override the stamp and split the run's rows from /runs) — put your own attempt id in a tag of your own, such as `pipeline_attempt_id`. Name the step with `stepKey`. The raw lane records steps since modelrig 0.5.0 (before that, a run that wrapped raw calls got an empty tree); raw steps are metadata only (no artifact content on the raw lane).
|
|
163
239
|
|
|
164
240
|
## Caching — pass your handle, own the TTL heartbeat
|
|
165
241
|
|
|
166
242
|
- If the code you are migrating already caches with a provider (Gemini `cachedContents`, Anthropic `cache_control`, an OpenAI `prompt_cache_key`), carry it through the route — dropping it is a silent cost regression. A customer handle rides one optional field: `rig.run(task, { input, cache: { key, provider } })` (or `rig.runRaw({ …, apiKey, cache: { key } })` — runRaw is BYOK, pass your provider key); `provider` is required and the handle is applied only to that provider's candidates.
|
|
167
243
|
- CRITICAL for long jobs: an explicit cache (Gemini `cachedContents`) has a TTL, and a multi-hour pipeline outlives it. ModelRig passes your handle through and prices the hits, but it NEVER creates, refreshes, or deletes the resource — your code must run the TTL heartbeat (refresh or recreate), by design. See [Caching lifecycle](https://modelrig.dev/caching-lifecycle.html) for the three regimes, what invalidates a cache, and prefix hygiene.
|
|
168
244
|
- Route-level automatic caching is separate: set `cache: auto` on the route and ModelRig stamps the per-provider directive; a miss-streak detector exists but is UNWIRED (not yet connected to the monitor), so nothing yet alerts on a route that stopped caching. That handles Anthropic markers and prefix caching — the customer handle above is only for provider-native EXPLICIT resources you own.
|
|
169
|
-
- The Lane-B `rig.runRaw` seam also forwards `grounding: { mode: "native" }` (native provider web grounding), `serviceTier: standard|flex|priority`, `reasoning: { level: minimal|low|medium|high }`,
|
|
245
|
+
- The Lane-B `rig.runRaw` seam also forwards `grounding: { mode: "native" }` (native provider web grounding), `serviceTier: standard|flex|priority`, `reasoning: { level: minimal|low|medium|high }`, `responseFormat: "json_object"` (mime-shaping only), and `responseSchema: <JSON Schema object>` (the provider's NATIVE structured-output directive PLUS a client-side Ajv check — a miss is a billed, terminal `content_invalid`, never repaired or retried; `meta.schema_sha` on the row; declare one of responseFormat/responseSchema). Each is additive (absent ⇒ byte-identical); a directive a provider cannot honor FAILS CLOSED (`invariant_violation`, pre-dispatch), never a silent drop — which provider honors which knob is the matrix below. A grounded raw step that also passes `cache.key` must have the `googleSearch` tool baked into the cache resource at creation; a schema composes with the handle.
|
|
170
246
|
|
|
171
247
|
## Raw-lane provider × knob support (rig.runRaw)
|
|
172
248
|
|
|
173
|
-
_Since 0.4.0: `reasoning` beyond gemini, `responseFormat`, and the DeepInfra/Fireworks `prompt_cache_key` mapping
|
|
249
|
+
_Since 0.4.0: `reasoning` beyond gemini, `responseFormat`, and the DeepInfra/Fireworks `prompt_cache_key` mapping. **RELEASED:** native `web_search` grounding on openai AND grok via the Responses API — each cleared its own live recency+citations+price gate on 2026-09-09 (openai $0.01/call surcharge; grok $0.005 per CALL — $5/1k calls, a per-call model, retrieved content billed separately as input tokens; grok replaces the retired xAI Live Search surface, and its gate run scored 15/15 recency, 100% citation presence, avg 3.80 cites/response). The openai/grok prompt-cache hints were already true on 0.3.0._
|
|
174
250
|
|
|
175
251
|
The raw lane forwards these provider knobs; a knob a provider cannot honor FAILS CLOSED pre-dispatch (`invariant_violation`) — discoverable here, never a silent drop:
|
|
176
252
|
|
|
177
|
-
| Provider | grounding | reasoning | responseFormat | cache | serviceTier |
|
|
178
|
-
|
|
179
|
-
| `gemini` | native — Google Search (`googleSearch` tool) | `level` → `thinkingConfig.thinkingLevel`, thinking-capable models only (see `GEMINI_THINKING_CAPABLE_MODELS`) | `responseMimeType: application/json` (mime-shaping only — no schema, `validated` stays false) | `cache.key` = a `cachedContent` resource handle (customer-owned lifecycle) | `flex` / `standard` / `priority` honored |
|
|
180
|
-
| `openai` |
|
|
181
|
-
| `deepseek` | fail-closed | no effort control — reasoning is model selection (`deepseek-reasoner`) | `response_format: { type: json_object }` — the prompt must contain the word "JSON" (caller's responsibility on the raw lane) | automatic prefix cache — the key is not used | no tiers — always `standard` |
|
|
182
|
-
| `anthropic` | fail-closed | extended thinking is not wired on the raw lane (future work) | fail-closed — no json_object equivalent (structured output is a forced tool call) | prefix caching via `cache_control` markers — the key is not used | no tiers — always `standard` |
|
|
183
|
-
| `grok` |
|
|
184
|
-
| `deepinfra` | fail-closed | `level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably | `response_format: { type: json_object }` | `cache.key` → `prompt_cache_key` (routing hint) | no tiers — always `standard` |
|
|
185
|
-
| `fireworks` | fail-closed | `level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably | `response_format: { type: json_object }` | `cache.key` → `prompt_cache_key` (routing hint; caching is automatic host-side) | no tiers — always `standard` |
|
|
253
|
+
| Provider | grounding | reasoning | responseFormat | responseSchema | cache | serviceTier | destination |
|
|
254
|
+
|---|---|---|---|---|---|---|---|
|
|
255
|
+
| `gemini` | native — Google Search (`googleSearch` tool) | `level` → `thinkingConfig.thinkingLevel`, thinking-capable models only (see `GEMINI_THINKING_CAPABLE_MODELS`) | `responseMimeType: application/json` (mime-shaping only — no schema, `validated` stays false) | `responseJsonSchema` + `responseMimeType: application/json` (native); output Ajv-checked, a miss is `content_invalid` | `cache.key` = a `cachedContent` resource handle (customer-owned lifecycle) | `flex` / `standard` / `priority` honored | `direct` only — vendor key (BYOK / configured) |
|
|
256
|
+
| `openai` | native — `web_search` tool (Responses API) — released (live gate passed 2026-09-09) | `level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably | `response_format: { type: json_object }` | `response_format: { type: json_schema, strict: true }` (native; strict needs an object root — a non-object root is warned pre-dispatch); output Ajv-checked | `cache.key` → `prompt_cache_key`; `ttlSeconds ≥ 86400` → `prompt_cache_retention: 24h` | `flex` (allowlist) / `priority`; flex→standard is a customer-side degrade (§3.2) | `direct` only — vendor key (BYOK / configured) |
|
|
257
|
+
| `deepseek` | fail-closed | no effort control — reasoning is model selection (`deepseek-reasoner`) | `response_format: { type: json_object }` — the prompt must contain the word "JSON" (caller's responsibility on the raw lane) | fail-closed — json_object only; a schema would be silently dropped | automatic prefix cache — the key is not used | no tiers — always `standard` | `direct` only — vendor key (BYOK / configured) |
|
|
258
|
+
| `anthropic` | fail-closed | extended thinking is not wired on the raw lane (future work) | fail-closed — no json_object equivalent (structured output is a forced tool call) | forced tool call carrying the schema as `input_schema` (strict-first via the structured-outputs beta, advisory on a strict compile rejection); output Ajv-checked | prefix caching via `cache_control` markers — the key is not used | no tiers — always `standard` | `direct` only — vendor key (BYOK / configured) |
|
|
259
|
+
| `grok` | native — `web_search` tool (xAI Agent Tools / Responses API); replaces retired Live Search — released (live gate passed 2026-09-09) | coarsened (disclosed): `minimal,low → "low"`; `medium,high → "high"` → `reasoning_effort` | `response_format: { type: json_object }` | `response_format: { type: json_schema, strict: true }` (native); output Ajv-checked | `cache.key` → `prompt_cache_key` (routing hint) | no tiers — always `standard` | `direct` only — vendor key (BYOK / configured) |
|
|
260
|
+
| `deepinfra` | fail-closed | `level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably | `response_format: { type: json_object }` | fail-closed — the raw compat profile has no probed `structured_native`; the schema would degrade to json_object + a prompt guard | `cache.key` → `prompt_cache_key` (routing hint) | no tiers — always `standard` | `direct` · `modelrig-served` — ModelRig-owned key (managed serving RELEASED — GA, gated by `config.managedServing`, default off) |
|
|
261
|
+
| `fireworks` | fail-closed | `level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably | `response_format: { type: json_object }` | fail-closed — the raw compat profile has no probed `structured_native`; the schema would degrade to json_object + a prompt guard | `cache.key` → `prompt_cache_key` (routing hint; caching is automatic host-side) | no tiers — always `standard` | `direct` · `modelrig-served` — ModelRig-owned key (managed serving RELEASED — GA, gated by `config.managedServing`, default off) |
|
|
262
|
+
| `zai` | fail-closed — native GLM web_search wired in B4b | `level` → `reasoning_effort` (1:1); a non-reasoning model's rejection is classified teachably | response_format json_object | fail-closed — the raw compat profile has no probed `structured_native`; the schema would degrade to json_object + a prompt guard | `cache.key` → `prompt_cache_key` (routing hint) | no tiers — always `standard` | `direct` only — vendor key (BYOK / configured) |
|
|
186
263
|
|
|
187
264
|
**Fail-closed is discoverable, not a surprise.** A knob a provider cannot honor throws `RigFailureError` `invariant_violation` PRE-DISPATCH (no meter, no provider call, no telemetry row) — read this table before you call, not after.
|
|
188
265
|
**reasoning has NO per-model allowlist outside gemini.** openai/deepinfra/fireworks forward `level` verbatim and let the provider enforce; a rejection surfaces as a classified failure that names the fix. grok coarsens (disclosed above). deepseek reasoning is model selection (`deepseek-reasoner`); anthropic extended thinking is unwired.
|
|
189
266
|
**responseFormat is mime-shaping, not schema enforcement.** `json_object` forces syntactic JSON only — the raw lane still never validates or repairs, and `meta.validated` stays `false` (F4).
|
|
267
|
+
**responseSchema validates, never repairs.** A JSON Schema on `rig.runRaw` is forwarded as the provider's NATIVE structured-output directive AND checked client-side by the same Ajv the routed lane uses: a miss is thrown as `content_invalid` (a billed row with that class, `fixHint` naming the first errors) — the raw lane never repairs, never retries, never falls through, and `meta.validated` stays `false` (no ladder stands behind it). The row's `meta.schema_sha` is the sha256 of the canonical (sorted-key) schema JSON, so declared-vs-served drift is queryable. Declare ONE of `responseFormat` / `responseSchema`, not both.
|
|
190
268
|
**§3.2 flex degrade is customer-side.** ModelRig does not replicate the gemini direct-path infra-retry wrapper on the raw lane; under `flex` a capacity rejection surfaces classified (`capacity_shed`) for your own `→ standard` retry ladder — a NAMED difference for your migration record, not a silent one.
|
|
269
|
+
**`destination` (managed serving) is RELEASED (GA).** The serving-destination column names which providers *can* be `modelrig-served` (reached on ModelRig's own key) vs `direct` only. Managed serving is gated behind a config flag (`config.managedServing`, default off, set in code — not an env var); it defaults off, so until you enable it every call is `direct` (ABSENT ⇒ byte-identical).
|
|
191
270
|
|
|
192
271
|
## Raw-lane defaults (rig.runRaw)
|
|
193
272
|
|
|
@@ -196,13 +275,14 @@ What `rig.runRaw` sends when you omit a knob. `timeoutMs` is tier-aware since 0.
|
|
|
196
275
|
| Knob | Default | Note |
|
|
197
276
|
|---|---|---|
|
|
198
277
|
| `timeoutMs` | 300 s (standard, priority) · 900 s (flex) | tier-aware wall-clock deadline; an explicit `timeoutMs` always wins, and a `timeout` failure carries a `fixHint` naming the tier + budget |
|
|
199
|
-
| `serviceTier` | standard | forwarded to the adapter; `meta.servedTier` reports what actually served. On `flex`, set `timeoutMs` in minutes (default 900 s) |
|
|
278
|
+
| `serviceTier` | standard | forwarded to the adapter; `meta.servedTier` reports what actually served, and the cost row is priced at the SERVED tier (gemini flex = 0.5× input + output, cache read NOT discounted; priority = 1.8×). On `flex`, set `timeoutMs` in minutes (default 900 s) |
|
|
200
279
|
| `temperature` | adapter default — not sent | absent ⇒ no `temperature` on the request (byte-identical dispatch) |
|
|
201
280
|
| `topP` | adapter default — not sent | absent ⇒ no `topP` on the request (byte-identical dispatch) |
|
|
202
281
|
| `maxOutputTokens` | adapter default — not sent | absent ⇒ no output-token cap on the request (byte-identical dispatch) |
|
|
203
282
|
| `grounding` | absent ⇒ byte-identical | native provider grounding; provider support per the raw-lane knob matrix |
|
|
204
283
|
| `reasoning` | absent ⇒ byte-identical | reasoning/thinking effort; provider support per the raw-lane knob matrix |
|
|
205
284
|
| `responseFormat` | absent ⇒ byte-identical | syntactic-JSON forcing (mime-shaping only); provider support per the raw-lane knob matrix |
|
|
285
|
+
| `responseSchema` | absent ⇒ byte-identical (no schema sent, no validation) | JSON Schema forwarded natively AND Ajv-checked client-side — a miss is a billed, terminal `content_invalid` (never repaired); `meta.schema_sha` on the row; provider support per the raw-lane knob matrix |
|
|
206
286
|
| `cache` | none | customer cache handle passed through verbatim when supplied; ModelRig never creates, refreshes, or deletes the resource |
|
|
207
287
|
| `tags` | none | `lane=raw` and `rung=primary` are stamped by the SDK and cannot be overridden by a caller tag |
|
|
208
288
|
| `tools / toolChoice` | none | absent ⇒ no tools declared; on the raw lane a grounding-originated `functionCall` part is NOT a tool-call turn |
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Managed serving — `destination: modelrig-served`
|
|
2
|
+
|
|
3
|
+
Serve a route's model on ModelRig's own credentials instead of yours. One less
|
|
4
|
+
provider key to hold; the same route bundle, the same validate → repair →
|
|
5
|
+
fall-through pipeline, the same per-call telemetry — served through the hosted
|
|
6
|
+
gateway and paid from your organization's prepaid balance.
|
|
7
|
+
|
|
8
|
+
**The honest default is `direct`.** A route or run that declares no
|
|
9
|
+
`destination` is served exactly as today: your own provider key, ModelRig not in
|
|
10
|
+
the credential path, byte-identical behaviour. Nothing in this page changes a
|
|
11
|
+
route you have not opted in.
|
|
12
|
+
|
|
13
|
+
Requires `modelrig` **0.10.0 or newer** — the first published version that
|
|
14
|
+
carries the hosted serving transport. On an older package a `modelrig-served`
|
|
15
|
+
run is served through the local seam on whichever key the adapter already holds,
|
|
16
|
+
so upgrade before you rely on the keyless path.
|
|
17
|
+
|
|
18
|
+
## What "served" means
|
|
19
|
+
|
|
20
|
+
| | `destination: direct` (default) | `destination: modelrig-served` |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| Who calls the provider | your process, with your key | the hosted gateway, with ModelRig's key |
|
|
23
|
+
| What your process sends | the provider request | your `rig_sk_` key + the route slug + input — **never a provider key** |
|
|
24
|
+
| What you pay | your provider bill | provider list cost plus the flat fee, drawn from your prepaid balance at serve time |
|
|
25
|
+
| Telemetry row | written locally, exported if configured | written server-side (server-measured cost); `meta.inferenceId` points at it |
|
|
26
|
+
| Tag on the row | `destination: direct` | `destination: modelrig-served` |
|
|
27
|
+
|
|
28
|
+
Which providers *can* be served is a fact in the capability table — the
|
|
29
|
+
`destination` column in [Route bundles → provider × knob support](route-bundles.md#raw-lane-provider--knob-support-rigrunraw)
|
|
30
|
+
(and in `llms.txt`). A provider whose cell reads `direct` only cannot be
|
|
31
|
+
`modelrig-served`; declaring it fails closed before any call.
|
|
32
|
+
|
|
33
|
+
## Five steps
|
|
34
|
+
|
|
35
|
+
### 1. Declare the destination on the route (or the run)
|
|
36
|
+
|
|
37
|
+
```yaml
|
|
38
|
+
# modelrig/routes/example.support_summarize.yaml
|
|
39
|
+
route: example.support_summarize
|
|
40
|
+
version: 3
|
|
41
|
+
destination: modelrig-served # absent ⇒ direct (byte-identical)
|
|
42
|
+
candidates:
|
|
43
|
+
- { provider: <a served-capable provider>, model: <model> }
|
|
44
|
+
# …schema, prompt, policy unchanged
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
A per-run override wins over the route:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
await rig.run("example.support_summarize", {
|
|
51
|
+
input: { ticket },
|
|
52
|
+
destination: "modelrig-served", // this run only
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Precedence: `RunOptions.destination` → route `destination` → `direct`.
|
|
57
|
+
|
|
58
|
+
### 2. Enable managed serving in code
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { createRig, loadConfigFromEnv } from "modelrig";
|
|
62
|
+
|
|
63
|
+
const rig = createRig({ ...loadConfigFromEnv(), managedServing: true });
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`managedServing` is a **config field set in code, never an environment
|
|
67
|
+
variable**. It defaults to `false`: with it off, a `modelrig-served` route
|
|
68
|
+
throws a teaching `invariant_violation` *before* any provider call rather than
|
|
69
|
+
silently serving `direct`. This is the per-process switch; the per-organization
|
|
70
|
+
switch is step 4.
|
|
71
|
+
|
|
72
|
+
### 3. Mint a key with the `serve` scope
|
|
73
|
+
|
|
74
|
+
Managed serving spends money, so it is a **deliberate** grant: in the console
|
|
75
|
+
(Keys → new key) tick `serve`, and set that key as `MODELRIG_API_KEY` in the
|
|
76
|
+
process that runs the rig. The SDK sends this `rig_sk_` key as the only
|
|
77
|
+
credential on a served call. A key without `serve` is refused with a 403 that
|
|
78
|
+
names the scope.
|
|
79
|
+
|
|
80
|
+
### 4. Turn managed serving on for your organization
|
|
81
|
+
|
|
82
|
+
An **owner** flips it in the console — **Account settings → Managed serving**
|
|
83
|
+
(off for every new organization). Or from a coding agent / script, through the
|
|
84
|
+
settings API with an `org-settings`-scoped key:
|
|
85
|
+
|
|
86
|
+
```http
|
|
87
|
+
PATCH /v1/orgs/<your-org-id>/settings
|
|
88
|
+
{ "managed_serving_enabled": true }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
The server's owner-only gate decides; a member or admin is refused, and the
|
|
92
|
+
setting applies to the **next** served call — no redeploy.
|
|
93
|
+
|
|
94
|
+
### 5. Fund the prepaid balance
|
|
95
|
+
|
|
96
|
+
Served calls are debited at serve time: the server-measured provider cost plus
|
|
97
|
+
the flat fee, as two ledger entries anchored to the inference id (idempotent —
|
|
98
|
+
a retry never charges twice). Before dispatch the gateway checks that a
|
|
99
|
+
conservative reservation is available; otherwise it refuses with a 402 and
|
|
100
|
+
nothing is served. Top up in the console (Billing). A `direct` route never
|
|
101
|
+
needs a balance.
|
|
102
|
+
|
|
103
|
+
## What the refusals teach
|
|
104
|
+
|
|
105
|
+
Every refusal is a `RigFailureError` of class `invariant_violation` — terminal,
|
|
106
|
+
pre-dispatch where possible, and never a silent fall back to `direct`. The
|
|
107
|
+
`fixHint` on the error is the text below.
|
|
108
|
+
|
|
109
|
+
| Status | Means | `fixHint` says |
|
|
110
|
+
|---|---|---|
|
|
111
|
+
| (local, before any call) | `managedServing` is off | "managed serving is not enabled — set `managedServing: true` in your rig config … otherwise remove `destination` or set it to `direct`." |
|
|
112
|
+
| (local, before any call) | the resolved candidate cannot be served | "provider … cannot be modelrig-served; served providers: …. Declare a served-capable candidate, or set `destination: direct`." |
|
|
113
|
+
| **401** | the `rig_sk_` key was rejected | "the `rig_sk_` control-plane key (MODELRIG_API_KEY) was not accepted. Check the key and that it belongs to the org you expect, then retry." |
|
|
114
|
+
| **402** | no / insufficient prepaid balance | "your organization has no prepaid managed-serving balance. Top up your balance in the console, or serve this route with `destination: direct` (your own provider key)." Server side: "each managed call draws the server-measured provider cost + 2% fee. Add funds (at least … must be available to start a call), then retry." |
|
|
115
|
+
| **403** | the key lacks `serve`, **or** the organization has not turned managed serving on | "this organization/key is not entitled to managed serving. Enable managed serving for your org in the console, or serve this route with `destination: direct`." Server side the two cases are distinct: "this key does not carry the `serve` scope" vs "this organization is not provisioned for managed serving — an OWNER … turns it on (Account settings → Managed serving)". |
|
|
116
|
+
| **404** | the route slug is not mirrored for your organization | "deploy that route from your repo so its bundle syncs to the mirror, then retry — the hosted lane serves only your own organization's routes." |
|
|
117
|
+
| **502** | the entitlement or balance could not be read | "refused on purpose (fail-closed) rather than served on an unknown entitlement / without confirming funds; retry shortly, and check control-plane health if it persists." |
|
|
118
|
+
| **503** | the call ran but its charge could not be anchored, or the hosted lane is misconfigured | "refused on purpose (fail-closed) rather than billed against a shared idempotency key — retry shortly." |
|
|
119
|
+
|
|
120
|
+
A 404 here is a routing fact, not a permissions oracle: the hosted lane resolves
|
|
121
|
+
slugs **only inside the calling key's organization**, so another organization's
|
|
122
|
+
route is simply "not found".
|
|
123
|
+
|
|
124
|
+
## What you see on the row
|
|
125
|
+
|
|
126
|
+
A served call's inference row carries `destination: modelrig-served`, the
|
|
127
|
+
provider and model that actually served it, the served tier, the
|
|
128
|
+
server-measured cost (which is what the ledger drew), and the inference id the
|
|
129
|
+
charge is anchored to. It is the same row shape as every other call — the
|
|
130
|
+
console, the analytics surface, exports and tenant statements all read it
|
|
131
|
+
unchanged — so "which calls did ModelRig serve for me, and what did they cost"
|
|
132
|
+
is one filter on the `destination` tag.
|
|
133
|
+
|
|
134
|
+
## Data posture
|
|
135
|
+
|
|
136
|
+
On the served path your request reaches the serving provider on ModelRig's
|
|
137
|
+
credentials, which makes that provider ModelRig's subprocessor for the request
|
|
138
|
+
— the published security page states the posture, and the named list is part
|
|
139
|
+
of the security-questionnaire pack available on request. Regulated or PHI data
|
|
140
|
+
stays on `direct` (or managed custody) and is not sent through the served path
|
|
141
|
+
until the subprocessor agreement chain is confirmed. Content custody is a
|
|
142
|
+
separate axis with its own account setting (see
|
|
143
|
+
[Artifact content custody](content-custody.md)); the serving destination never
|
|
144
|
+
changes what is stored.
|
|
145
|
+
|
|
146
|
+
## See also
|
|
147
|
+
|
|
148
|
+
- [Route bundles](route-bundles.md) — the `destination` field and the provider × knob table.
|
|
149
|
+
- [Routing & reliability](routing-reliability.md) — what fails closed and why.
|
|
150
|
+
- [Quickstart](quickstart.md) — the `direct` path everything above is additive to.
|