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/CHANGELOG.md
CHANGED
|
@@ -10,8 +10,235 @@ published package diverge.
|
|
|
10
10
|
This project adheres to [Semantic Versioning](https://semver.org/). Dates are
|
|
11
11
|
UTC.
|
|
12
12
|
|
|
13
|
+
Every `[Unreleased]` entry adds its symbols to `release-manifest.json` (rule P17
|
|
14
|
+
in `docs/ModelRig/development-rules.md`; enforced by `release-parity.test.ts`).
|
|
15
|
+
|
|
13
16
|
## [Unreleased]
|
|
14
17
|
|
|
18
|
+
## [0.11.0] — 2026-10-20
|
|
19
|
+
|
|
20
|
+
**`rig.recordCall()` — record a provider call ModelRig did not dispatch**
|
|
21
|
+
(OBS-1, acceptance ruling M12). When a call has to stay a direct provider call,
|
|
22
|
+
report it after it returns: `rig.recordCall({ provider, model, usage, servedTier?,
|
|
23
|
+
requestedTier?, latencyMs, ttfbMs?, failureClass?, webSearchCalls?, stepKey?, tags?,
|
|
24
|
+
providerRequestId?, finishReason? })` → `{ inferenceId, callId, costEstimateUsd,
|
|
25
|
+
pricingMissing }`. One row, built by the raw lane's own helpers: priced at list
|
|
26
|
+
for the SERVED tier (flex included), `route = provider/model`, `routeVersion 0`,
|
|
27
|
+
inside `run.scope`/`run.start` a ground-truth step joined by `inference_id` and
|
|
28
|
+
`run_id` stamped; `webSearchCalls` writes the separate `rung:"search"` cost row.
|
|
29
|
+
The row carries `tags.lane = "external"` and `tags.key_ownership = "byok"`
|
|
30
|
+
(SDK-owned, a caller tag cannot override them) and `meta.provenance =
|
|
31
|
+
"caller-reported"` (new optional `AttemptMetaV1` key, exported type
|
|
32
|
+
`AttemptProvenance`; additive, no version bump). Any non-empty provider is
|
|
33
|
+
accepted; an unpriced provider/model records $0 with `pricingMissing`.
|
|
34
|
+
Synchronous and never throws: invalid input warns once per rule with a fix and
|
|
35
|
+
returns `inferenceId: null`. Token counts and `latencyMs`/`ttfbMs` are rounded
|
|
36
|
+
to integers and clamped (tokens ≤ `Number.MAX_SAFE_INTEGER`, ms ≤ 2³¹−1); a
|
|
37
|
+
`provider/model` longer than 200 characters is refused (ingest would drop it). Types `RecordCallInput`, `RecordCallResult`,
|
|
38
|
+
`RecordCallUsage`. Docs: `not-captured.md` → *Calls your code makes itself:
|
|
39
|
+
record them*; `instrumentation.md`.
|
|
40
|
+
|
|
41
|
+
**Raw-lane rows are BYOK by construction.** Every `rig.runRaw` /
|
|
42
|
+
`rig.runRawStream` row (and its native-search cost row) now carries
|
|
43
|
+
`tags.key_ownership = "byok"`, SDK-owned and stamped last beside `lane: "raw"`
|
|
44
|
+
— the raw lane only ever dispatches on the caller's own provider key. Before
|
|
45
|
+
0.11.0 a raw call made in your own process carried no `key_ownership` tag;
|
|
46
|
+
the control plane now classifies `lane: "raw"` rows as BYOK on the lane. This
|
|
47
|
+
applies to every window settled from this release on; no customer usage window
|
|
48
|
+
had been settled before it.
|
|
49
|
+
|
|
50
|
+
**One lane classifier.** `isRoutedLane(lane)` (false for `"raw"` and
|
|
51
|
+
`"external"`), `callLaneOf(lane)` (`routes` | `raw` | `external`), `RAW_LANE`,
|
|
52
|
+
`EXTERNAL_LANE`, `RECORDED_BADGE`, `RECORDED_TOOLTIP` and the `CallLane` type are
|
|
53
|
+
exported — the same test the server rollups, `/coverage` (`computeCoverage`)
|
|
54
|
+
and the console use, so a recorded or raw call never enters ladder,
|
|
55
|
+
conformance or coverage-rung math. Docs: `not-captured.md`.
|
|
56
|
+
|
|
57
|
+
**`modelrig doctor` edge cases (QA N14).** A value that is not a `rig_sk_` key
|
|
58
|
+
at all (a deployment or provider token) is a usage error — "not a rig_sk_ key —
|
|
59
|
+
doctor checks customer API keys", exit 2 — and a `rig_sk_` key that is
|
|
60
|
+
truncated or padded fails with "malformed key"; neither is ever sent over the
|
|
61
|
+
wire (`PreflightResult.status` gains `"not_rig_key"` and `"malformed_key"`). A 2xx whoami whose body is
|
|
62
|
+
not JSON, or that names no org, is now a failure (exit 1, no ✓, says why) — it
|
|
63
|
+
used to print `(unknown org) ✓`. The first-flush "exporting to org" line no
|
|
64
|
+
longer prints `«?»`/`(?)` placeholders.
|
|
65
|
+
|
|
66
|
+
## [0.10.0] — 2026-10-19
|
|
67
|
+
|
|
68
|
+
**Behaviour change (raw lane) — migration note.** A `rig.runRaw` call on Gemini
|
|
69
|
+
that passed a non-empty `systemPrompt` alongside `cache.key` used to have the
|
|
70
|
+
system prompt silently DROPPED (the adapter could not place it beside
|
|
71
|
+
`cachedContent`). It is now delivered in the user turn — ahead of `userPrompt`,
|
|
72
|
+
separated by a blank line — and is therefore billed as input tokens on every
|
|
73
|
+
such call. If the cache resource already holds that text, pass
|
|
74
|
+
`systemPrompt: ""` to keep the request byte-identical to before (apps/api's raw
|
|
75
|
+
lane already does). A call with an empty `systemPrompt`, or without a handle, is
|
|
76
|
+
unchanged.
|
|
77
|
+
|
|
78
|
+
**Per-call user turn (`prompt.user`) + the Gemini cache-handle delivery rule**
|
|
79
|
+
(GitLab issue #5, R1). A route may now declare `prompt.user: <path>` — a second
|
|
80
|
+
template rendered with the same `variables`, capability conditionals and
|
|
81
|
+
`x-trust` rules as `prompt.system` — whose rendered text **is** the user turn of
|
|
82
|
+
every primary attempt (replacing the fixed trigger sentence) and rides ahead of
|
|
83
|
+
the directive on a same-model repair. `prompt.system` may be omitted when
|
|
84
|
+
`prompt.user` is declared. Absent ⇒ the fixed trigger ⇒ byte-identical
|
|
85
|
+
behaviour and an unchanged `bundle_sha`; declaring it changes the `bundle_sha`,
|
|
86
|
+
and `userTemplate` rides the resolved (mirror) bundle. **Cache rule:** a Gemini
|
|
87
|
+
dispatch carrying a cache handle (`cachedContent`) may not carry
|
|
88
|
+
`systemInstruction`; before this release the adapter silently DROPPED the
|
|
89
|
+
rendered template on such a dispatch (the `caching-lifecycle.md` "you do not
|
|
90
|
+
need to empty your template" reassurance was a trap). Now **with a handle,
|
|
91
|
+
everything rendered is delivered as the user turn** — the system prompt (if
|
|
92
|
+
any), a blank line, the user turn — on both lanes, and nothing is dropped. Every
|
|
93
|
+
Gemini attempt that reached the provider stamps `inferences.meta.prompt_delivery`
|
|
94
|
+
(`system` | `user` | `user+cache`, exported as `PromptDelivery`) so the control
|
|
95
|
+
plane can prove what was sent; a transport-error row (no response) carries none.
|
|
96
|
+
The loader warns on a `cache:` route with a Gemini candidate, a non-empty
|
|
97
|
+
`prompt.system` and no `prompt.user`. Docs: `caching-lifecycle.md` → *A cache
|
|
98
|
+
handle changes where the template is delivered*; `route-bundles.md` → *Per-call
|
|
99
|
+
user turn*.
|
|
100
|
+
|
|
101
|
+
**Prose on the route lane — `output: text`** (GitLab issue #1; incident
|
|
102
|
+
2026-10-11). A route can now declare that its output is prose/markdown rather
|
|
103
|
+
than JSON: `output: text` in the bundle YAML (or `output: { kind: text,
|
|
104
|
+
min_chars: N }` for the one declared length check). The response is returned
|
|
105
|
+
**verbatim** as `result.output` (a string): no `JSON.parse`, no schema, no
|
|
106
|
+
json-mode coaching, and the JSON repair rung is never entered — so a prose route
|
|
107
|
+
is never rejected `content_invalid` for not being JSON. The default is unchanged
|
|
108
|
+
(`json`; `output: json` may be written explicitly and is identical to absent):
|
|
109
|
+
every existing route behaves byte-identically and keeps its `bundle_sha`. A
|
|
110
|
+
text route's `bundle_sha` differs from its JSON twin, and `outputKind` rides the
|
|
111
|
+
resolved bundle, so the hosted mirror lane and call-config pins honour the prose
|
|
112
|
+
contract too. The loader refuses `output: text` combined with `schema:`,
|
|
113
|
+
`require: schema_conformant`, or `repair:` with a teaching error. A JSON route
|
|
114
|
+
that receives prose keeps today's `content_invalid` and now carries a
|
|
115
|
+
`fixHint` pointing at `output: text`. Every dispatched route-lane attempt stamps
|
|
116
|
+
`inferences.meta.output_kind` (`json` | `text`, the contract it was held to);
|
|
117
|
+
raw-lane rows carry no such key. `OutputKind` is exported. Docs:
|
|
118
|
+
`route-bundles.md` → *Prose and markdown output*.
|
|
119
|
+
|
|
120
|
+
**`responseSchema` on the raw lane (R2, GitLab #6).** `rig.runRaw` accepts a
|
|
121
|
+
JSON Schema object and forwards it as the provider's *native* structured-output
|
|
122
|
+
directive — gemini `responseJsonSchema` + JSON mime, openai/grok strict
|
|
123
|
+
`json_schema` (a non-object root is warned pre-dispatch), anthropic a forced
|
|
124
|
+
tool call — then validates the returned text client-side with the same Ajv the
|
|
125
|
+
routed lane uses. A miss is a **billed, terminal `content_invalid`**: one
|
|
126
|
+
`lane: "raw"` row with that class and a thrown `RigFailureError` whose `fixHint`
|
|
127
|
+
names the first errors; nothing is repaired, retried, or fallen through, so
|
|
128
|
+
`meta.validated` stays `false` (boundary B2 is narrowed from "no structure" to
|
|
129
|
+
"no repair"). Providers without a native schema path on this lane — deepseek and
|
|
130
|
+
the OpenAI-compat hosts (deepinfra / fireworks / zai) — fail closed
|
|
131
|
+
pre-dispatch, as does a schema that is not a plain object, does not compile, or
|
|
132
|
+
is declared alongside `responseFormat`. The row's `meta.schema_sha` is
|
|
133
|
+
`sha256:` of the canonical (sorted-key) schema JSON, so declared-vs-served drift
|
|
134
|
+
is queryable; the schema joins `input_hash` exactly as a route schema does. The
|
|
135
|
+
knob is one more cell of `RAW_KNOB_SUPPORT` — the guard, the published matrix,
|
|
136
|
+
`RAW_DEFAULTS`, and `llms.txt` all derive from it. Zero DDL (`meta` is jsonb).
|
|
137
|
+
|
|
138
|
+
**The generated tables now ship in the committed docs.** `route-bundles.md`
|
|
139
|
+
(raw-lane defaults, provider × knob matrix) and `routing-reliability.md`
|
|
140
|
+
(failure classes, the boundary) — and the nine use-case template pages — used
|
|
141
|
+
to carry a bare HTML-comment marker that only the modelrig.dev build filled in,
|
|
142
|
+
so GitLab, the OSS mirror and any offline agent reading the repo saw an empty
|
|
143
|
+
line where the table belonged. Every marker is now a fence around its current
|
|
144
|
+
render; the docs build refreshes the fenced body from the same renderer on every
|
|
145
|
+
run (the `docs/llms.txt` rule applied to the docs) and fails on a bare
|
|
146
|
+
placeholder, and a tripwire test asserts each committed body is byte-identical
|
|
147
|
+
to its renderer. Regenerate with `tsx scripts/build-docs-site.ts` and commit.
|
|
148
|
+
|
|
149
|
+
**Cost rows are priced at the SERVED tier, and Gemini has a `flex` tier
|
|
150
|
+
(GitLab #2).** `attemptCost` / `costComponents` now read `usage.servedTier`
|
|
151
|
+
(both lanes, every attempt — serving, failed, repair) and price `flex` /
|
|
152
|
+
`priority` from the model's published `pricing_v2` tiers instead of always
|
|
153
|
+
billing the standard base. `ServingTiers` gains `flex`, and a tier multiplier
|
|
154
|
+
may now be **per component** (`{ input: 0.5, output: 0.5, cache_read: 1.0 }`
|
|
155
|
+
beside the scalar form; an absent component is exempt, i.e. the standard
|
|
156
|
+
rate) — Google's flex/batch halve input + output but leave cache read "Same as
|
|
157
|
+
Standard", which one scalar cannot say. `gemini/gemini-3.1-pro-preview` (and
|
|
158
|
+
`-customtools`) carry `tiers.flex`, `tiers.priority: 1.8` and the >200k
|
|
159
|
+
context band, verified against the Gemini pricing page 2026-10-11. On the
|
|
160
|
+
InferWealth flex batch this recorded $1.11 where Google's list is ≈$0.68
|
|
161
|
+
(1,357,697 in / 1,289,855 cached / 59,625 out: in $0.068 + cache $0.258 + out
|
|
162
|
+
$0.358). A model that publishes no rate for a served tier still bills at base
|
|
163
|
+
(fail-closed, never a guessed multiplier); `standard` rows are byte-identical.
|
|
164
|
+
Known gap, stated: the >200k context band is **listed, not applied** — rows
|
|
165
|
+
whose prompt exceeds 200k tokens still price at the ≤200k rates and under-state
|
|
166
|
+
Google's list (follow-up issue). `meta.served_tier_source` (`provider` |
|
|
167
|
+
`requested_fallback`) now says whether the served tier came from the provider's
|
|
168
|
+
response or fell back to the requested tier, so a fallback-priced row is
|
|
169
|
+
identifiable. **Scope of the claim:** attempt cost rows (`cost_estimate_usd`)
|
|
170
|
+
and `meta.cost` price at the served tier; the server's managed re-pricing
|
|
171
|
+
(`modelrig-server` `compute-fee.ts` `listPriceCents`, the `/costs` rollup and
|
|
172
|
+
settlement draw) still prices at base — that money path is untouched here and
|
|
173
|
+
tracked for the next money-path MR. Zero DDL.
|
|
174
|
+
|
|
175
|
+
**Managed serving is released at the SDK/capability layer** (`destination:
|
|
176
|
+
modelrig-served`). The serving cells of the capability table and the published
|
|
177
|
+
`llms.txt` matrix drop their UNRELEASED tags; `config.managedServing` is
|
|
178
|
+
unchanged (set in code, default `false`, so an absent/`direct` route stays
|
|
179
|
+
byte-identical). New customer page `docs/managed-serving.md` — declare the
|
|
180
|
+
destination, enable it in code, mint a `serve`-scoped key, opt the organization
|
|
181
|
+
in (Account settings → Managed serving, or `PATCH /v1/orgs/:org/settings
|
|
182
|
+
{managed_serving_enabled}`), fund the prepaid balance, and what each 401/402/
|
|
183
|
+
403/502/503 refusal teaches; `route-bundles.md` documents the `destination`
|
|
184
|
+
field. The SDK side of the hosted keyless transport (K1/K1b) is in this package
|
|
185
|
+
(it is not in 0.9.0, where a served run uses the local seam). Hosted serving
|
|
186
|
+
also needs the org opt-in, a `serve`-scoped key and a funded prepaid balance.
|
|
187
|
+
|
|
188
|
+
Routes are visible — and pinnable — before their first call (RV-1). A rig
|
|
189
|
+
created with a `routesDir` and a control-plane export now registers each route
|
|
190
|
+
the moment `createRig()` runs: its metadata (route, version, candidates,
|
|
191
|
+
capture flag) plus its declared resolved bundle and `bundle_sha`, through the
|
|
192
|
+
same `/v1/ingest` call telemetry uses (the `ingest` scope is enough). The
|
|
193
|
+
hosted ingest now stores that bundle (it used to drop it in api mode), so a
|
|
194
|
+
call-config pin passes the eligibility gate instead of failing with "no
|
|
195
|
+
servable bundle". The bundle is route configuration (the authored prompt
|
|
196
|
+
template, never per-call values); it is stored even for a posture-`off` org.
|
|
197
|
+
A prompt version promoted in ModelRig keeps precedence over the YAML template.
|
|
198
|
+
Registration is fire-and-forget and fail-open: never awaited, never throws,
|
|
199
|
+
warns once on failure, and never changes what this process serves. The
|
|
200
|
+
exporter's first-flush routes mirror (which now also lands its bundle in api
|
|
201
|
+
mode) remains the retry path. Pair it with `GET /v1/routes/:route/call-config?include=effective`
|
|
202
|
+
(or MCP `get_call_config`) to see what a route will serve and why — pin vs your
|
|
203
|
+
YAML default — and whether recent calls drifted from a pin. A bundle that
|
|
204
|
+
arrives over `/v1/ingest` is stored as route configuration only: pins, the
|
|
205
|
+
effective view and bake-offs use it, but the hosted gateway keeps serving only
|
|
206
|
+
bundles from a trusted sync, so an `ingest` key cannot change what the gateway
|
|
207
|
+
serves. `stampBatch` now writes routes with and without a bundle as two
|
|
208
|
+
separate writes (a bulk insert needs every row to carry the same columns).
|
|
209
|
+
|
|
210
|
+
Silent telemetry drops are now loud — **no configuration required**. When an
|
|
211
|
+
ingest POST is rejected with `401`/`403` (a wrong, revoked, or wrong-org key),
|
|
212
|
+
the api sink emits ONE loud, legible warning — `[modelrig] AUTH FAILED —
|
|
213
|
+
telemetry is being DROPPED (401 at <host>, key …5WMM) …` — naming the host and
|
|
214
|
+
the redacted key and pointing at `app.modelrig.ai` and `modelrig doctor`. It
|
|
215
|
+
fires once per failure streak (a later successful send re-arms it), rides the
|
|
216
|
+
POST that is already failing (no extra request), and never prints the key or the
|
|
217
|
+
response body. This is the automatic safety net — the one key a customer already
|
|
218
|
+
sets is enough; there is no boot-preflight env var.
|
|
219
|
+
|
|
220
|
+
`modelrig doctor` — a new CLI command that confirms, in one line, which org your
|
|
221
|
+
`MODELRIG_API_KEY` resolves to (via the server's `GET /v1/whoami`): prints
|
|
222
|
+
`key …5WMM → org «YourOrg» ✓` on success, a loud `AUTH FAILED (401)` line on a
|
|
223
|
+
rejected key, and exits non-zero when the key is bad — so CI / a deploy step /
|
|
224
|
+
setup can gate on it before any traffic runs. Never prints the key (last-four
|
|
225
|
+
only).
|
|
226
|
+
|
|
227
|
+
**Which org am I exporting to? — the wrong-org signal (REL-1, M5).** A valid key
|
|
228
|
+
minted in the wrong org used to fail silently: every ingest POST is a 200 and the
|
|
229
|
+
runs land in another org's console. Now, on the first successful flush in each
|
|
230
|
+
process, the api sink calls `GET /v1/whoami` once (the same helper `modelrig
|
|
231
|
+
doctor` uses) and logs `[modelrig] exporting to org «Name» (slug) as key «name»
|
|
232
|
+
rig_sk_…last4`. It runs in the background, never blocks or drops a batch, and is
|
|
233
|
+
silent if whoami fails (a rejected key already has its own `AUTH FAILED` line).
|
|
234
|
+
New code option `createRig({ expectOrg: "<slug>" })`: when the key resolves to a
|
|
235
|
+
different org it logs `[modelrig] ORG MISMATCH: key resolves to «a», expectOrg is
|
|
236
|
+
«b» — telemetry is landing in «a»` loudly on every flush. The whoami call is
|
|
237
|
+
aborted after 5 s, so it never holds a process open. It never throws and never drops telemetry. It is a code option, not an
|
|
238
|
+
env var (P15). `modelrig doctor --expect-org <slug>` exits 1 and prints both
|
|
239
|
+
slugs on a mismatch, so a deploy or CI step can gate on it. Docs:
|
|
240
|
+
`instrumentation.md` → *Which org am I exporting to?*
|
|
241
|
+
|
|
15
242
|
## [0.9.0] — 2026-09-01
|
|
16
243
|
|
|
17
244
|
**Breaking** (the reason this is 0.9.0, not 0.8.2): the temporary `rig.feedback` /
|
package/README.md
CHANGED
|
@@ -89,10 +89,11 @@ import { createRig, loadConfigFromEnv } from "modelrig";
|
|
|
89
89
|
const rig = createRig(loadConfigFromEnv()); // keys from GEMINI_API_KEY etc.
|
|
90
90
|
const result = await rig.run("example.support_summarize", {
|
|
91
91
|
input: { ticket, product, priorContext }, // template variables
|
|
92
|
-
tags: {
|
|
93
|
-
budget: { envelope:
|
|
92
|
+
tags: { subject: customer, feature: "summarize" },
|
|
93
|
+
budget: { envelope: ticketId }, // hard-stop cost envelope
|
|
94
94
|
});
|
|
95
|
-
// result.output — schema-validated JSON (ajv), regardless of serving model
|
|
95
|
+
// result.output — schema-validated JSON (ajv), regardless of serving model;
|
|
96
|
+
// the verbatim string on an `output: text` (prose) route
|
|
96
97
|
// result.meta — servedCandidate, tokens (in/out/cached), cost, tiers,
|
|
97
98
|
// validation status, attempts by failure class
|
|
98
99
|
rig.close();
|
|
@@ -123,15 +124,23 @@ without it. ModelRig carries the handle and prices the hits — it never creates
|
|
|
123
124
|
refreshes, or deletes the resource, so a long job needs your own TTL heartbeat
|
|
124
125
|
(see the [caching lifecycle](https://modelrig.dev/caching-lifecycle.html) doc).
|
|
125
126
|
The Lane-B `rig.runRaw({ provider, model, apiKey, …, cache: { key } })` seam takes
|
|
126
|
-
the same field.
|
|
127
|
+
the same field. A Gemini handle forbids a system instruction on the request, so
|
|
128
|
+
**with a handle everything the route rendered is delivered as the user turn**
|
|
129
|
+
(never dropped); declare the per-call delta in a `prompt.user` template and let
|
|
130
|
+
the cache hold the shared prefix — every attempt stamps `meta.prompt_delivery`
|
|
131
|
+
(`system` | `user` | `user+cache`) so you can prove what was sent.
|
|
127
132
|
|
|
128
133
|
`rig.runRaw` also accepts
|
|
129
134
|
provider knobs, each additive — absent ⇒ byte-identical dispatch:
|
|
130
135
|
`grounding: { mode: "native" }` (native provider web grounding —
|
|
131
136
|
gemini; grok fail-closed until CE-9b), `serviceTier: "standard" | "flex" | "priority"`,
|
|
132
137
|
`reasoning: { level: "minimal" | "low" | "medium" | "high" }` (reasoning effort¹),
|
|
133
|
-
|
|
134
|
-
schema enforcement, so `meta.validated` stays `false`)
|
|
138
|
+
`responseFormat: "json_object"` (syntactic-JSON forcing¹ — MIME-shaping, NOT
|
|
139
|
+
schema enforcement, so `meta.validated` stays `false`), and `responseSchema`
|
|
140
|
+
(a JSON Schema object² — forwarded as the provider's native structured-output
|
|
141
|
+
directive AND Ajv-checked client-side; a miss is a billed, terminal
|
|
142
|
+
`content_invalid` — never repaired or retried — and the row carries
|
|
143
|
+
`meta.schema_sha`). A declared directive a
|
|
135
144
|
provider cannot honor FAILS CLOSED — a thrown `RigFailureError` with class
|
|
136
145
|
`invariant_violation`, pre-dispatch (no meter, no provider call) — never a silent
|
|
137
146
|
drop. **Which provider honors which knob is one table** — the runtime guard and
|
|
@@ -144,8 +153,10 @@ you don't duplicate the thinking-model set). A grounded step that also passes a
|
|
|
144
153
|
cache resource at creation (ModelRig sends `cachedContent` without tools, which
|
|
145
154
|
the provider requires).
|
|
146
155
|
|
|
147
|
-
_¹
|
|
148
|
-
|
|
156
|
+
_¹ Which provider honors `reasoning`, `grounding` and `responseFormat` is the
|
|
157
|
+
knob matrix — generated from the same table the runtime guard reads._
|
|
158
|
+
_² `responseSchema` needs 0.10.0 or newer (gemini / openai / grok / anthropic;
|
|
159
|
+
deepseek and the compat hosts fail closed) — see the knob matrix._
|
|
149
160
|
|
|
150
161
|
**runRaw — a BYOK passthrough (Lane B).** When you don't need a route — no
|
|
151
162
|
schema, no validation, no retries — call a `provider/model` directly with your
|
|
@@ -248,6 +259,10 @@ lines up with the console's built-ins:
|
|
|
248
259
|
| `session` | the conversation/session the call belongs to |
|
|
249
260
|
| `cost_center` | the team/budget to attribute spend to |
|
|
250
261
|
|
|
262
|
+
These five are the keys **you** set. `run_id` and `step` are stamped by the SDK
|
|
263
|
+
inside a run (don't set `run_id` yourself), and ModelRig owns a few more; the one
|
|
264
|
+
complete list is in the [instrumentation guide](./docs/instrumentation.md#reserved-tag-keys-the-one-list).
|
|
265
|
+
|
|
251
266
|
`modelrig validate` warns on near-misses — a declared `rig.yaml` dimension named
|
|
252
267
|
`customer`, `tenant`, `user_id`, `team`, … is nudged toward the reserved key it
|
|
253
268
|
resembles. They stay ordinary tags; nothing is required.
|
|
@@ -260,33 +275,40 @@ one **step** per model-call family, and **save the work products** as artifacts;
|
|
|
260
275
|
the console's **Runs** tab then shows the run → step → artifact chain you click
|
|
261
276
|
through, and `/projects` groups it by your tags.
|
|
262
277
|
|
|
278
|
+
<!-- BLOCK:RUN_WRAP -->
|
|
279
|
+
**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.
|
|
280
|
+
|
|
263
281
|
```ts
|
|
264
282
|
import { createRig, loadConfigFromEnv } from "modelrig";
|
|
265
283
|
|
|
266
284
|
const rig = createRig(loadConfigFromEnv());
|
|
267
285
|
|
|
268
|
-
//
|
|
269
|
-
//
|
|
286
|
+
// Every pipeline execution is a run. Inside the scope the SDK records each
|
|
287
|
+
// rig.run / rig.runRaw call as a step and stamps run_id and step on every row.
|
|
288
|
+
// run_id is reserved: don't set it yourself (put your own attempt id in a tag of
|
|
289
|
+
// your own, such as pipeline_attempt_id).
|
|
270
290
|
await rig.artifacts.run.scope(
|
|
271
|
-
{ pipeline: "support", episodeKey: ticketId },
|
|
291
|
+
{ pipeline: "support", episodeKey: ticketId, environment: process.env.NODE_ENV ?? "development" },
|
|
272
292
|
async () => {
|
|
273
293
|
const result = await rig.run("example.support_summarize", {
|
|
274
294
|
input: { ticket, product, priorContext },
|
|
275
|
-
tags: { subject: customer, feature: "summarize" },
|
|
276
|
-
stepKey: "summarize",
|
|
295
|
+
tags: { subject: customer, feature: "summarize" },
|
|
296
|
+
stepKey: "summarize", // names this step on /runs
|
|
277
297
|
});
|
|
278
|
-
|
|
279
|
-
rig.artifacts.artifact.save(result.output, { name: "summary", type: "step_output" });
|
|
298
|
+
rig.artifacts.artifact.save(result.output, { name: "summary", type: "step_output", stepKey: "summarize" });
|
|
280
299
|
},
|
|
281
300
|
);
|
|
282
|
-
|
|
301
|
+
|
|
302
|
+
await rig.close(); // flushes the exporter — call once at shutdown
|
|
283
303
|
```
|
|
304
|
+
<!-- /BLOCK:RUN_WRAP -->
|
|
284
305
|
|
|
285
306
|
- **Tags:** carry `subject` (what the run is *about* — an opaque id) and
|
|
286
307
|
`feature`. Inside a run context the SDK stamps `tags.run_id` (the run's
|
|
287
308
|
`episodeKey`, else its id) and `tags.step` (the current step key) on every
|
|
288
|
-
attempt row of both lanes
|
|
289
|
-
|
|
309
|
+
attempt row of both lanes; outside a run context nothing is stamped, so the row
|
|
310
|
+
is byte-identical. `run_id` is reserved: don't set it yourself — the full
|
|
311
|
+
reserved-key list is in the [instrumentation guide](./docs/instrumentation.md#reserved-tag-keys-the-one-list).
|
|
290
312
|
- **Raw lane too:** a `rig.runRaw` call inside a run context records a
|
|
291
313
|
ground-truth step (since 0.5.0), so grounded/cached BYOK pipelines fill the
|
|
292
314
|
same graph.
|
|
@@ -297,8 +319,7 @@ rig.close();
|
|
|
297
319
|
|
|
298
320
|
**On by default under a control plane.** The run/artifact namespace is **on by
|
|
299
321
|
default once a control plane is configured** — set `MODELRIG_API_KEY=rig_sk_…`
|
|
300
|
-
(or the self-host Supabase pair) and runs record automatically
|
|
301
|
-
hashes only. A **pure local-only rig** (no sink to ship to) stays off; force it
|
|
322
|
+
(or the self-host Supabase pair) and runs record automatically. A **pure local-only rig** (no sink to ship to) stays off; force it
|
|
302
323
|
on there with `MODELRIG_ARTIFACTS=1`. To **opt out of capture**, set posture
|
|
303
324
|
`off` in your account settings (the console or the `set_org_settings` MCP tool) —
|
|
304
325
|
the account posture is the customer control, not a deployment env var. With the
|
|
@@ -306,11 +327,12 @@ namespace off (a local-only rig) it is inert — `run.start` is a
|
|
|
306
327
|
no-op, `artifact.save` returns null, zero rows are written, and your pipeline
|
|
307
328
|
behaves byte-for-byte as before. `modelrig status` prints the current posture.
|
|
308
329
|
|
|
309
|
-
> **
|
|
310
|
-
>
|
|
311
|
-
>
|
|
312
|
-
>
|
|
313
|
-
>
|
|
330
|
+
> **What is kept.** Every saved artifact records a metadata row with a sha256
|
|
331
|
+
> integrity hash. Whether the serialized bytes are kept as well is your
|
|
332
|
+
> organization's [content custody](https://modelrig.dev/content-custody.html)
|
|
333
|
+
> posture: new organizations are created `managed` (bytes kept in your managed
|
|
334
|
+
> store); `metadata` keeps hashes only and `off` captures nothing. A
|
|
335
|
+
> `zeroRetention` run refuses every artifact fail-closed at the SDK gate.
|
|
314
336
|
|
|
315
337
|
The full artifact API — lineage (`link`), evaluations (`evaluate`), the reusable
|
|
316
338
|
per-step seam, and `run.scope` vs `run.start` — is the
|
|
@@ -345,8 +367,10 @@ on the quickstart:
|
|
|
345
367
|
|
|
346
368
|
## Honest limits
|
|
347
369
|
|
|
348
|
-
- **
|
|
349
|
-
|
|
370
|
+
- **Managed serving is opt-in and prepaid.** It draws from a prepaid balance your
|
|
371
|
+
organization funds in the console ([Managed serving](https://modelrig.dev/managed-serving.html));
|
|
372
|
+
with no balance, every call runs on your own provider keys (`destination: direct`,
|
|
373
|
+
the default).
|
|
350
374
|
- **`0.x` means the shape may still move.** Route and config contracts are
|
|
351
375
|
stable enough to build on, not frozen.
|
|
352
376
|
- **`modelrig serve` needs the `modelrig-server` package** — it is not bundled
|
package/data/PROVENANCE.md
CHANGED
|
@@ -13,6 +13,20 @@ this snapshot was copied by hand per implementation-plan §9. Replace this manua
|
|
|
13
13
|
copy with the sync script's output when Phase 0 tooling lands; until then, any
|
|
14
14
|
pin bump must re-copy this file and update this note.
|
|
15
15
|
|
|
16
|
+
HAND-ADDED `pricing_v2` BLOCKS (must survive a pin bump — re-apply after any
|
|
17
|
+
re-copy): `gemini/gemini-3.1-pro-preview` and `…-customtools` carry an additive
|
|
18
|
+
`pricing_v2` (GitLab issue #2, 2026-10-11, verified against
|
|
19
|
+
https://ai.google.dev/gemini-api/docs/pricing): `tiers.flex = { input: 0.5,
|
|
20
|
+
output: 0.5, cache_read: 1.0 }` (flex/batch halve input + output; cache read is
|
|
21
|
+
"Same as Standard"), `tiers.priority = 1.8` (every component), and the >200k
|
|
22
|
+
context band. The snapshot wins on shared keys and never carries `pricing_v2`
|
|
23
|
+
upstream, so the block lives on the snapshot entry itself; `getPricingV2` reads
|
|
24
|
+
it and the served-tier cost path (`src/run/cost.ts`) prices flex/priority rows
|
|
25
|
+
from it. Flat fields are untouched (base is byte-identical). `scripts/sync-upstream.ts
|
|
26
|
+
--apply` RE-GRAFTS every `pricing_v2` block from the current map onto the new
|
|
27
|
+
snapshot (and refuses to apply if upstream dropped an entry that carries one), so a
|
|
28
|
+
pin bump cannot silently drop them; the manual re-copy path above must do the same.
|
|
29
|
+
|
|
16
30
|
Key naming (verified against the snapshot):
|
|
17
31
|
- Gemini API models: `gemini/<model>` (bare ids resolve to vertex_ai pricing — do not use)
|
|
18
32
|
- OpenAI models: bare `<model>`
|
package/data/pricing-map.json
CHANGED
|
@@ -20338,6 +20338,19 @@
|
|
|
20338
20338
|
"tpm": 800000
|
|
20339
20339
|
},
|
|
20340
20340
|
"gemini/gemini-3.1-pro-preview": {
|
|
20341
|
+
"pricing_v2": {
|
|
20342
|
+
"base": { "input": 2e-06, "output": 1.2e-05, "cache_read": 2e-07 },
|
|
20343
|
+
"tiers": {
|
|
20344
|
+
"priority": 1.8,
|
|
20345
|
+
"flex": { "input": 0.5, "output": 0.5, "cache_read": 1.0 }
|
|
20346
|
+
},
|
|
20347
|
+
"context_bands": [
|
|
20348
|
+
{ "above_tokens": 200000, "rates": { "input": 4e-06, "output": 1.8e-05, "cache_read": 4e-07 } }
|
|
20349
|
+
],
|
|
20350
|
+
"source": "https://ai.google.dev/gemini-api/docs/pricing (Gemini 3.1 Pro Preview, verified 2026-10-11: flex/batch 0.5x input+output, cache read 'Same as Standard'; priority 1.8x every component incl. cache read; >200k band doubles input, output and cache read)",
|
|
20351
|
+
"as_of": "2026-10-11",
|
|
20352
|
+
"basis": "base · standard tier · text · prompts ≤ 200k tokens (the >200k context band is listed; cost rows price at the SERVED tier)"
|
|
20353
|
+
},
|
|
20341
20354
|
"cache_read_input_token_cost": 2e-07,
|
|
20342
20355
|
"cache_read_input_token_cost_above_200k_tokens": 4e-07,
|
|
20343
20356
|
"input_cost_per_token": 2e-06,
|
|
@@ -20395,6 +20408,19 @@
|
|
|
20395
20408
|
"web_search_billing_unit": "per_query"
|
|
20396
20409
|
},
|
|
20397
20410
|
"gemini/gemini-3.1-pro-preview-customtools": {
|
|
20411
|
+
"pricing_v2": {
|
|
20412
|
+
"base": { "input": 2e-06, "output": 1.2e-05, "cache_read": 2e-07 },
|
|
20413
|
+
"tiers": {
|
|
20414
|
+
"priority": 1.8,
|
|
20415
|
+
"flex": { "input": 0.5, "output": 0.5, "cache_read": 1.0 }
|
|
20416
|
+
},
|
|
20417
|
+
"context_bands": [
|
|
20418
|
+
{ "above_tokens": 200000, "rates": { "input": 4e-06, "output": 1.8e-05, "cache_read": 4e-07 } }
|
|
20419
|
+
],
|
|
20420
|
+
"source": "https://ai.google.dev/gemini-api/docs/pricing (Gemini 3.1 Pro Preview, verified 2026-10-11: flex/batch 0.5x input+output, cache read 'Same as Standard'; priority 1.8x every component incl. cache read; >200k band doubles input, output and cache read)",
|
|
20421
|
+
"as_of": "2026-10-11",
|
|
20422
|
+
"basis": "base · standard tier · text · prompts ≤ 200k tokens (the >200k context band is listed; cost rows price at the SERVED tier)"
|
|
20423
|
+
},
|
|
20398
20424
|
"cache_read_input_token_cost": 2e-07,
|
|
20399
20425
|
"cache_read_input_token_cost_above_200k_tokens": 4e-07,
|
|
20400
20426
|
"input_cost_per_token": 2e-06,
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - gemini: `finishReason` is `SAFETY` / `PROHIBITED_CONTENT` / `RECITATION`.
|
|
15
15
|
* The refusal TEXT itself is never carried (content) — only the boolean.
|
|
16
16
|
*/
|
|
17
|
-
import type { AttemptMetaV1, FinishReason } from "../telemetry/attempt-meta";
|
|
17
|
+
import type { AttemptMetaV1, FinishReason, PromptDelivery, ServedTierSource } from "../telemetry/attempt-meta";
|
|
18
18
|
import type { AdapterRequest, ProviderId, ToolCall } from "../types/provider";
|
|
19
19
|
/** Map a provider's native finish/stop reason to the `FinishReason` enum
|
|
20
20
|
* (spec §2). Unknown/absent ⇒ undefined (never inferred); an unrecognised
|
|
@@ -51,6 +51,13 @@ export interface AdapterMetaSignals {
|
|
|
51
51
|
};
|
|
52
52
|
/** true = the run loop streamed deltas (stream()); false/absent = buffered. */
|
|
53
53
|
readonly streaming?: boolean;
|
|
54
|
+
/** Issue #5 (R1): how the rendered prompt reached the model on this call —
|
|
55
|
+
* reported by the adapter that shaped the request (today: gemini, where a
|
|
56
|
+
* cache handle moves delivery). Absent ⇒ no `prompt_delivery` key. */
|
|
57
|
+
readonly promptDelivery?: PromptDelivery;
|
|
58
|
+
/** Issue #2 (L6 F5): did the provider ECHO the served tier, or did the adapter
|
|
59
|
+
* fall back to the requested tier? Stamped as `meta.served_tier_source`. */
|
|
60
|
+
readonly servedTierSource?: ServedTierSource;
|
|
54
61
|
}
|
|
55
62
|
/** Build one adapter's NATIVE `meta` (spec §2). Only defined signals appear —
|
|
56
63
|
* everything else is unknown and omitted. The producer takes it from here. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attempt-meta-map.d.ts","sourceRoot":"","sources":["../../src/adapters/attempt-meta-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"attempt-meta-map.d.ts","sourceRoot":"","sources":["../../src/adapters/attempt-meta-map.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC/G,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAE9E;;kCAEkC;AAClC,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,UAAU,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC7B,YAAY,GAAG,SAAS,CA6C1B;AAED;;;oDAGoD;AACpD,wBAAgB,YAAY,CAC1B,GAAG,EAAE,cAAc,GAClB;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAOlF;AAED;0EAC0E;AAC1E,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,GACzC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,SAAS,CAQ/C;AAED,2EAA2E;AAC3E,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,cAAc,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;IACzC,QAAQ,CAAC,SAAS,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrE,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAC7B;;0EAEsE;IACtE,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC;IACzC;gFAC4E;IAC5E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CAC9C;AAED;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EACpB,CAAC,EAAE,kBAAkB,GACpB,OAAO,CAAC,aAAa,CAAC,CA2BxB;AAED;;;uDAGuD;AACvD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CASzE"}
|
|
@@ -134,6 +134,10 @@ function buildAdapterMeta(provider, s) {
|
|
|
134
134
|
}
|
|
135
135
|
if (typeof s.streaming === "boolean")
|
|
136
136
|
meta.streaming = s.streaming;
|
|
137
|
+
if (s.promptDelivery !== undefined)
|
|
138
|
+
meta.prompt_delivery = s.promptDelivery;
|
|
139
|
+
if (s.servedTierSource !== undefined)
|
|
140
|
+
meta.served_tier_source = s.servedTierSource;
|
|
137
141
|
return meta;
|
|
138
142
|
}
|
|
139
143
|
/** A transport/SDK error's coarse envelope (spec §2 `http_status`/`error_code`).
|
|
@@ -27,8 +27,8 @@ function buildAdapters(config, registryFacts, compatStatic) {
|
|
|
27
27
|
}
|
|
28
28
|
if (config.keys.grok)
|
|
29
29
|
adapters.set("grok", (0, grok_1.createGrokAdapter)({ apiKey: config.keys.grok }));
|
|
30
|
-
// A1 aggregator hosts behind the generic openai-compatible adapter.
|
|
31
|
-
for (const hostId of ["deepinfra", "fireworks"]) {
|
|
30
|
+
// A1 aggregator hosts + Z.ai (B4a) behind the generic openai-compatible adapter.
|
|
31
|
+
for (const hostId of ["deepinfra", "fireworks", "zai"]) {
|
|
32
32
|
const apiKey = config.keys[hostId];
|
|
33
33
|
if (!apiKey)
|
|
34
34
|
continue;
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* Copyright (c) 2023 Berri AI, MIT License — retain this notice if LiteLLM's
|
|
14
14
|
* gemini transformation logic is consulted during the port.
|
|
15
15
|
*/
|
|
16
|
-
import type { AdapterRequest, CapabilityFlag, ProviderAdapter } from "../types/provider";
|
|
16
|
+
import type { AdapterRequest, CapabilityFlag, ProviderAdapter, ServiceTier } from "../types/provider";
|
|
17
17
|
import type { FailureClass } from "../types/failures";
|
|
18
|
+
import type { PromptDelivery, ServedTierSource } from "../telemetry/attempt-meta";
|
|
18
19
|
export interface GeminiAdapterConfig {
|
|
19
20
|
readonly apiKey: string;
|
|
20
21
|
}
|
|
@@ -53,9 +54,35 @@ export interface GeminiShapedRequest {
|
|
|
53
54
|
readonly model: string;
|
|
54
55
|
readonly contents: string;
|
|
55
56
|
readonly config: Record<string, unknown>;
|
|
57
|
+
/** Issue #5 (R1): how the rendered prompt was delivered — stamped on the
|
|
58
|
+
* attempt's `meta.prompt_delivery` so the control plane can prove it. */
|
|
59
|
+
readonly promptDelivery: PromptDelivery;
|
|
56
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* THE CACHE-HANDLE DELIVERY RULE (GitLab issue #5, R1). A request that sets
|
|
63
|
+
* `cachedContent` may not set `systemInstruction` (Google rejects both — the
|
|
64
|
+
* system instruction and tools are baked into the cache at creation). Before
|
|
65
|
+
* R1 the adapter silently DROPPED a non-empty system prompt here, so a route
|
|
66
|
+
* whose template held the per-call delta lost it whenever a handle was passed
|
|
67
|
+
* (caching-lifecycle.md's "you do not need to empty your template" was a trap).
|
|
68
|
+
* The rule now: **with a cache handle, everything rendered is delivered as the
|
|
69
|
+
* user turn** — the system prompt (when non-empty), a blank line, then the user
|
|
70
|
+
* turn — and nothing is dropped. The cache holds the shared prefix; the user
|
|
71
|
+
* turn carries what changes per call. Pure; exported for the adapter tests.
|
|
72
|
+
*/
|
|
73
|
+
export declare function cachedUserTurn(systemPrompt: string, userPrompt: string): string;
|
|
57
74
|
/** Pure request shaping — everything except the abort signal. */
|
|
58
75
|
export declare function buildGeminiRequest(model: string, req: ShapingInput): GeminiShapedRequest;
|
|
76
|
+
/** Best-effort served-tier extraction (ported): response.serviceTier, then
|
|
77
|
+
* usageMetadata.trafficType (Vertex path), else assume the requested tier —
|
|
78
|
+
* production bills flex when a flex request was accepted without a tier echo.
|
|
79
|
+
* Issue #2 (L6 F5): now that cost rows PRICE at the served tier, the fallback
|
|
80
|
+
* is disclosed — `source` lands on `meta.served_tier_source`. Pure; exported
|
|
81
|
+
* for the unit test. No behaviour change in the tier itself. */
|
|
82
|
+
export declare function extractGeminiServedTier(rawServiceTier: string | undefined, trafficType: string | undefined, requested: ServiceTier): {
|
|
83
|
+
readonly tier: ServiceTier;
|
|
84
|
+
readonly source: ServedTierSource;
|
|
85
|
+
};
|
|
59
86
|
/** A candidate's parts as we read them post-response — `functionCall`, `text`,
|
|
60
87
|
* and (if `includeThoughts` were ever set) a `thought`-flagged text part may all
|
|
61
88
|
* appear on the same candidate (a grounded / thinking turn). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,cAAc,EAGd,cAAc,EACd,eAAe,
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EACV,cAAc,EAGd,cAAc,EACd,eAAe,EACf,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,mBAAmB,CAAC;AAGlE,OAAO,KAAK,EAAiB,cAAc,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAKjG,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;mCAImC;AACnC,eAAO,MAAM,8BAA8B,EAAE,WAAW,CAAC,MAAM,CAI7D,CAAC;AA0BH,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG9E;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAyCxE;AAED;6EAC6E;AAC7E,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAE7D;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,iBAAiB,GAAG,IAAI,CAQrG;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC;6EACyE;IACzE,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;CACzC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/E;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,mBAAmB,CA4DxF;AAUD;;;;;gEAKgE;AAChE,wBAAgB,uBAAuB,CACrC,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,WAAW,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,CAOnE;AAED;;gEAEgE;AAChE,KAAK,mBAAmB,GAAG;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AACF,KAAK,oBAAoB,GAAG;IAAE,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAA;KAAE,CAAA;CAAE,CAAC;AAEvG;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,oBAAoB,GAAG,SAAS,GAAG,MAAM,CAM9F;AAED;;qEAEqE;AACrE,wBAAgB,8BAA8B,CAAC,SAAS,EAAE,oBAAoB,GAAG,SAAS,GAAG,OAAO,CAInG;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,oBAAoB,GAAG,SAAS,EAC3C,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,QAAQ,EAAE,OAAO,GAChB,MAAM,CAGR;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,mBAAmB,GAAG,eAAe,CA8MhF"}
|