modelrig 0.5.0 → 0.6.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.
Files changed (159) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/README.md +52 -3
  3. package/dist/adapters/anthropic.d.ts.map +1 -1
  4. package/dist/adapters/anthropic.js +36 -7
  5. package/dist/adapters/attempt-meta-map.d.ts +63 -0
  6. package/dist/adapters/attempt-meta-map.d.ts.map +1 -0
  7. package/dist/adapters/attempt-meta-map.js +154 -0
  8. package/dist/adapters/deepseek.d.ts.map +1 -1
  9. package/dist/adapters/deepseek.js +25 -2
  10. package/dist/adapters/gemini.d.ts.map +1 -1
  11. package/dist/adapters/gemini.js +29 -2
  12. package/dist/adapters/grok.d.ts.map +1 -1
  13. package/dist/adapters/grok.js +25 -2
  14. package/dist/adapters/openai-compat.d.ts.map +1 -1
  15. package/dist/adapters/openai-compat.js +25 -2
  16. package/dist/adapters/openai-stream.d.ts +2 -0
  17. package/dist/adapters/openai-stream.d.ts.map +1 -1
  18. package/dist/adapters/openai-stream.js +23 -3
  19. package/dist/adapters/openai.d.ts.map +1 -1
  20. package/dist/adapters/openai.js +36 -6
  21. package/dist/artifacts/artifact.d.ts.map +1 -1
  22. package/dist/artifacts/artifact.js +24 -5
  23. package/dist/artifacts/classification.d.ts +27 -0
  24. package/dist/artifacts/classification.d.ts.map +1 -0
  25. package/dist/artifacts/classification.js +38 -0
  26. package/dist/artifacts/index.d.ts +3 -1
  27. package/dist/artifacts/index.d.ts.map +1 -1
  28. package/dist/artifacts/index.js +10 -1
  29. package/dist/artifacts/run-context.d.ts +15 -3
  30. package/dist/artifacts/run-context.d.ts.map +1 -1
  31. package/dist/artifacts/run-context.js +49 -0
  32. package/dist/artifacts/scrub.d.ts +26 -0
  33. package/dist/artifacts/scrub.d.ts.map +1 -1
  34. package/dist/artifacts/scrub.js +53 -0
  35. package/dist/artifacts/step.d.ts +9 -0
  36. package/dist/artifacts/step.d.ts.map +1 -1
  37. package/dist/artifacts/step.js +9 -0
  38. package/dist/bakeoff/engine.d.ts +46 -4
  39. package/dist/bakeoff/engine.d.ts.map +1 -1
  40. package/dist/bakeoff/engine.js +269 -19
  41. package/dist/bakeoff/eval-cases.d.ts +5 -0
  42. package/dist/bakeoff/eval-cases.d.ts.map +1 -1
  43. package/dist/bakeoff/eval-cases.js +3 -0
  44. package/dist/bakeoff/judge.d.ts +39 -0
  45. package/dist/bakeoff/judge.d.ts.map +1 -0
  46. package/dist/bakeoff/judge.js +102 -0
  47. package/dist/bakeoff/quality.d.ts +44 -0
  48. package/dist/bakeoff/quality.d.ts.map +1 -0
  49. package/dist/bakeoff/quality.js +126 -0
  50. package/dist/bakeoff/reference.d.ts +34 -0
  51. package/dist/bakeoff/reference.d.ts.map +1 -0
  52. package/dist/bakeoff/reference.js +68 -0
  53. package/dist/bakeoff/replay.d.ts +25 -0
  54. package/dist/bakeoff/replay.d.ts.map +1 -1
  55. package/dist/bakeoff/replay.js +48 -1
  56. package/dist/bakeoff/report.d.ts +8 -1
  57. package/dist/bakeoff/report.d.ts.map +1 -1
  58. package/dist/bakeoff/report.js +83 -12
  59. package/dist/bakeoff/samples.d.ts +35 -2
  60. package/dist/bakeoff/samples.d.ts.map +1 -1
  61. package/dist/bakeoff/samples.js +67 -6
  62. package/dist/bakeoff/stats.d.ts +17 -1
  63. package/dist/bakeoff/stats.d.ts.map +1 -1
  64. package/dist/bakeoff/stats.js +67 -0
  65. package/dist/bakeoff/types.d.ts +76 -5
  66. package/dist/bakeoff/types.d.ts.map +1 -1
  67. package/dist/bakeoff/types.js +4 -3
  68. package/dist/cli-query.d.ts +14 -0
  69. package/dist/cli-query.d.ts.map +1 -0
  70. package/dist/cli-query.js +59 -0
  71. package/dist/cli.d.ts +2 -1
  72. package/dist/cli.d.ts.map +1 -1
  73. package/dist/cli.js +26 -23
  74. package/dist/config.d.ts +6 -0
  75. package/dist/config.d.ts.map +1 -1
  76. package/dist/config.js +14 -0
  77. package/dist/detect/detector.d.ts +82 -0
  78. package/dist/detect/detector.d.ts.map +1 -0
  79. package/dist/detect/detector.js +23 -0
  80. package/dist/index.d.ts +19 -14
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +38 -11
  83. package/dist/query/analytics.d.ts +84 -0
  84. package/dist/query/analytics.d.ts.map +1 -0
  85. package/dist/query/analytics.js +280 -0
  86. package/dist/query/columns.d.ts +59 -0
  87. package/dist/query/columns.d.ts.map +1 -0
  88. package/dist/query/columns.js +89 -0
  89. package/dist/query/grammar.d.ts +74 -7
  90. package/dist/query/grammar.d.ts.map +1 -1
  91. package/dist/query/grammar.js +179 -71
  92. package/dist/query/saved.d.ts +26 -0
  93. package/dist/query/saved.d.ts.map +1 -0
  94. package/dist/query/saved.js +51 -0
  95. package/dist/routes/loader.js +15 -10
  96. package/dist/routes/objective-loader.d.ts +35 -0
  97. package/dist/routes/objective-loader.d.ts.map +1 -0
  98. package/dist/routes/objective-loader.js +286 -0
  99. package/dist/run/accept.d.ts +43 -0
  100. package/dist/run/accept.d.ts.map +1 -0
  101. package/dist/run/accept.js +65 -0
  102. package/dist/run/cost.d.ts +13 -0
  103. package/dist/run/cost.d.ts.map +1 -1
  104. package/dist/run/cost.js +27 -0
  105. package/dist/run/dispatch.d.ts +24 -1
  106. package/dist/run/dispatch.d.ts.map +1 -1
  107. package/dist/run/dispatch.js +19 -0
  108. package/dist/run/ladder-key.d.ts +4 -0
  109. package/dist/run/ladder-key.d.ts.map +1 -1
  110. package/dist/run/ladder-key.js +3 -0
  111. package/dist/run/quality-gate.d.ts +39 -0
  112. package/dist/run/quality-gate.d.ts.map +1 -0
  113. package/dist/run/quality-gate.js +70 -0
  114. package/dist/run/raw.d.ts.map +1 -1
  115. package/dist/run/raw.js +17 -0
  116. package/dist/run/record-attempt.d.ts.map +1 -1
  117. package/dist/run/record-attempt.js +22 -1
  118. package/dist/run/routing-docs.d.ts.map +1 -1
  119. package/dist/run/routing-docs.js +11 -4
  120. package/dist/run/run.d.ts.map +1 -1
  121. package/dist/run/run.js +40 -36
  122. package/dist/settings/index.d.ts +1 -1
  123. package/dist/settings/index.d.ts.map +1 -1
  124. package/dist/settings/index.js +2 -1
  125. package/dist/settings/task-settings.d.ts +10 -0
  126. package/dist/settings/task-settings.d.ts.map +1 -1
  127. package/dist/settings/task-settings.js +17 -0
  128. package/dist/telemetry/attempt-meta.d.ts +178 -0
  129. package/dist/telemetry/attempt-meta.d.ts.map +1 -0
  130. package/dist/telemetry/attempt-meta.js +319 -0
  131. package/dist/telemetry/exporter.d.ts.map +1 -1
  132. package/dist/telemetry/exporter.js +6 -0
  133. package/dist/telemetry/ingest-protocol.d.ts +11 -0
  134. package/dist/telemetry/ingest-protocol.d.ts.map +1 -1
  135. package/dist/telemetry/ingest-protocol.js +3 -0
  136. package/dist/telemetry/otel-map.d.ts +10 -0
  137. package/dist/telemetry/otel-map.d.ts.map +1 -1
  138. package/dist/telemetry/otel-map.js +64 -0
  139. package/dist/telemetry/store-migrations.d.ts.map +1 -1
  140. package/dist/telemetry/store-migrations.js +15 -0
  141. package/dist/telemetry/store.d.ts.map +1 -1
  142. package/dist/telemetry/store.js +25 -7
  143. package/dist/types/failures-teaching.d.ts.map +1 -1
  144. package/dist/types/failures-teaching.js +20 -0
  145. package/dist/types/failures.d.ts +1 -1
  146. package/dist/types/failures.d.ts.map +1 -1
  147. package/dist/types/failures.js +1 -0
  148. package/dist/types/provider.d.ts +9 -0
  149. package/dist/types/provider.d.ts.map +1 -1
  150. package/dist/types/quality.d.ts +41 -0
  151. package/dist/types/quality.d.ts.map +1 -0
  152. package/dist/types/quality.js +12 -0
  153. package/dist/types/route.d.ts +89 -0
  154. package/dist/types/route.d.ts.map +1 -1
  155. package/dist/types/telemetry.d.ts +13 -0
  156. package/dist/types/telemetry.d.ts.map +1 -1
  157. package/dist/validate-cli.d.ts.map +1 -1
  158. package/dist/validate-cli.js +10 -0
  159. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,115 @@ published package diverge.
10
10
  This project adheres to [Semantic Versioning](https://semver.org/). Dates are
11
11
  UTC.
12
12
 
13
+ ## [0.6.0] — 2026-08-23
14
+
15
+ Released with the docs/registry mirror in one R-A ceremony. Headline for router
16
+ codebases: the candidate ladder can now fall through on **quality** (`qualityGate`
17
+ → `quality_rejected`), bake-offs measure **regret against a reference arm** under a
18
+ declared objective (legacy routes stay cost-first, byte-identical), the console
19
+ gains the **project tree** (`/projects`) and the **analytics API**
20
+ (`/v1/analytics/meta` + `/query`, metadata only — never content), and the
21
+ OpenAI-compatible proxy accepts the `developer` role. ZERO DDL across the line.
22
+
23
+ Regret-bounded bake-offs (RB-1, near-optimal) — the bake-off objective flips from
24
+ "cheapest that passes" to **near-optimal at a reasonable price**. New additive
25
+ route YAML: `objective` (`mode: quality-first | balanced | cost-first`,
26
+ `max_regret`, `price_ceiling`, `lambda`), `bakeoff` (`reference: auto | provider/model`,
27
+ `reference_envelope_usd`, `judge: { model, max_samples }`), and `value_checks`
28
+ (JSON-pointer assertions). Every bake-off can run a **reference (ceiling) arm**
29
+ and report each arm's **regret vs the reference with a seeded-bootstrap CI and n**.
30
+ Quality per sample resolves eval-case golden → `value_checks` → an opt-in capped,
31
+ metered judge (judge model ∈ declared candidates) → 1/0 conformance; it composes
32
+ with Q1's `qualityGate` (deterministic checks first, then the gate/judge). The
33
+ proposal gate is objective-aware: `cost-first` keeps Q1's gate byte-identical
34
+ (golden); a declared `quality-first`/`balanced` objective with no bound reports
35
+ regret but proposes nothing (no silent actuation). `modelrig bakeoff --objective
36
+ <mode>` overrides per run. The report/PR body state regret, cost and the ceiling
37
+ on one line. ZERO DDL. See `docs/bakeoffs.md` §5.
38
+
39
+ Analytics surface (WI-1b) — the read side of the attempt envelope. `GET
40
+ /v1/analytics/meta` describes every dimension and metric with its classification
41
+ and `since` version; `POST /v1/analytics/query` runs the SELECT-only grammar over
42
+ an org's inferences (metadata only — enums, numbers, hashes, identifiers; never
43
+ content; bake-off replay rows excluded). `modelrig query "<grammar>"` /
44
+ `--saved <id>` run the same grammar locally. Console: saved questions on
45
+ `/costs`, `/coverage`, `/routes/[route]`. OTLP-out gains `modelrig.meta.*` span
46
+ attributes for the metadata-safe envelope fields. Behaviour change: a free-text
47
+ `actor` is dropped with one warning (metadata-safe rule). New root exports:
48
+ `parseQuery`, `compileQuery`, `evaluateAnalyticsQuery`, `describeAnalyticsSchema`,
49
+ `planAnalyticsRead`, `runTelemetryQuery`, `SAVED_QUERIES`, `savedQueryById`, the
50
+ column registries and their types.
51
+ ZERO DDL.
52
+
53
+ Quality gate (#223, Q1) — the candidate ladder can now fall through on *quality*,
54
+ not just shape. Pass `rig.run(route, { qualityGate })` and a schema-valid output
55
+ your predicate rejects is recorded as a new `quality_rejected` failure (its own
56
+ retry budget, never the repair rung) and the ladder advances. The same predicate
57
+ scores bake-offs (`rig.bakeoff({ qualityGate })`) — a quality pass rate and an
58
+ `effectiveUsdPer1kPassing` metric beside the conformant one. SDK lane only: a
59
+ function cannot cross the hosted boundary; the route-declared hosted judge is Q2.
60
+
61
+ ### Added — quality gate (#223, Q1)
62
+
63
+ - **`RunOptions.qualityGate?`** — a builder predicate
64
+ `(output, meta) => { ok, score?, reason? }` (sync or async) evaluated on a
65
+ schema-VALID output before it is accepted. `ok:false` records a
66
+ `quality_rejected` attempt (message carries the reason + score), captures the
67
+ rejected text for replay, and falls the ladder through on the class's OWN retry
68
+ budget (`policy.retries.quality_rejected`, default 0) — the repair rung is NEVER
69
+ entered from this path. A throwing predicate rejects fail-closed (`reason:
70
+ "qualityGate threw: …"`, one warning per process). Tool-call turns and
71
+ `schema: null` routes are handled per spec §4.1. `runStream` inherits it.
72
+ - **`FailureClass "quality_rejected"`** — retryable (own non-fungible budget),
73
+ never terminal, with an errors-that-teach entry (`docsUrl`
74
+ → routing-reliability.html#quality_rejected) and a suggested `policy.retries`
75
+ edit. `policy.retries.quality_rejected` is accepted by the loader.
76
+ - **`RunMeta.quality?`** `{ score, verdict, judgedBy }` — present when a gate ran
77
+ (`verdict: "pass"` on a returned run; `judgedBy: null` for an SDK predicate).
78
+ SDK-owned `tags.quality` / `tags.quality_score` are stamped on the attempt row.
79
+ - **`rig.bakeoff` / `rig.bakeoffFromEvalCases` gain `qualityGate?`** — scored on
80
+ each replayed sample into `ReplaySampleResult.valueAccuracy` + `qualityPassed`;
81
+ `VariantMetrics` gains `qualityPassCi95` and `effectiveUsdPer1kPassing` (beside,
82
+ never redefining, `effectiveUsdPer1kConformant`), and `computeProposal` gains a
83
+ third clause (challenger quality-pass CI-lower ≥ incumbent's). The `modelrig
84
+ bakeoff` CLI cannot carry a function — use programmatic `rig.bakeoff` until Q2.
85
+ - **Exports:** `QualityGate`, `QualityVerdict`, `QualityGateMeta` from the root.
86
+ - **Boundary B1 rewritten** to "Route-declared judge (hosted lane)" (`not_built`,
87
+ tripwire `/\bjudge\??\s*:/`) now the SDK predicate ships; the routing-reliability
88
+ page, llms.txt (C11), README, `/definitions`, and the v4 setup prompt are swept
89
+ to match.
90
+
91
+ Project tree (G2b) — the console gains `/projects` (picker) and `/projects/[value]`
92
+ (project → run → step → call → attempts, cost + quality + lane on every node) over
93
+ one new read, `GET /v1/console/tree`. Server + console only; the SDK change below
94
+ is documentation. ZERO DDL.
95
+
96
+ - **`@step` / `step()` sets the ambient step key.** Documented in `step.ts` that
97
+ entering a step scope mints `label#n` and makes it the run context's ambient
98
+ step: an `artifact.save` with no explicit `stepKey` attaches to it — for saves
99
+ inside the scope AND for saves after it, until the next step mints a new key
100
+ (the ambient key is a running "most recent step", not a lexical bracket).
101
+ `run-context.ts` clarifies that within one run's parallel step waves only an
102
+ explicit `stepKey` prevents cross-attribution (`run.scope()` separates distinct
103
+ runs, not overlapping steps in one run).
104
+
105
+ Screening (B0 + B1) — two bug-class fixes and a shared detector contract.
106
+ - **Bake-off review samples are scrubbed before they persist.** A `--keep-outputs`
107
+ bake-off now runs each sample's input and output through the rig's content-custody
108
+ scrub mode (`detect` counts onto `arm_meta.scrub`, `redact` masks, `block` keeps a
109
+ metadata-only sample). Default `off` ⇒ rows byte-identical to before.
110
+ - **Proxy role enum.** `messages[].role` must be `system|developer|user|assistant|tool`
111
+ (`developer` normalises to the system slot); anything else is rejected with a
112
+ teaching message before dispatch (HTTP 422). Previously an arbitrary role was cast
113
+ through.
114
+ - **Raw-lane multi-turn encoding (behaviour change).** A genuine multi-turn transcript
115
+ is now flattened into the adapter's user prompt as one JSON object per line instead
116
+ of `role: content` text, so a crafted turn can never read as a synthetic system
117
+ line. Single-user-turn (and system + single user) requests are byte-identical.
118
+ - **Shared `Detector` interface.** `Detector`, `DetectionSpan`, `detectSpans`, and
119
+ `scrubDetectors` are exported from the package root; `scrubContent` is unchanged.
120
+ `buildBakeoffSamples` gains a scrub-resolver argument.
121
+
13
122
  ## [0.5.0] — 2026-08-22
14
123
 
15
124
  Released with the docs/registry mirror in one R-A ceremony. Headline for router
package/README.md CHANGED
@@ -179,9 +179,55 @@ Every attempt is recorded in a local SQLite file (`.modelrig/telemetry.db`)
179
179
  tagged with your `tags`. Failures are typed —
180
180
  `RigFailureError.failure.class` is one of `content_invalid`, `capacity_shed`,
181
181
  `network`, `refusal`, `cache_invalid`, `timeout`, `config_auth`, `budget_exhausted`,
182
- `invariant_violation` — with a separate retry budget per class, so a flaky
182
+ `invariant_violation`, `quality_rejected` — with a separate retry budget per class, so a flaky
183
183
  network never spends your content-validation retries. What each class means, whether it retries, and how it backs off is on [Routing & reliability](https://modelrig.dev/routing-reliability.html#5-failure-classes).
184
184
 
185
+ ## Metadata capture ("capture now, analyze later")
186
+
187
+ Every inference records a versioned, **metadata-safe** attempt envelope
188
+ (`AttemptMeta@v1`) on `inferences.meta` — in both the routed and the raw lane,
189
+ from the same producer. It is the metadata the adapters already see and used to
190
+ discard: finish reason, refusal, reasoning tokens, the provider-reported model
191
+ snapshot and request id, coarse HTTP status / error code, sampling echo, tool
192
+ names (declared kept, undeclared hashed) with counts, per-component cost, request
193
+ and output **hashes** (never the content), request/response byte sizes, response
194
+ char count, and a coarse context bucket.
195
+
196
+ **It never contains free text.** Every field is an enum, a number, a boolean, a
197
+ hash, or an identifier. The producer drops any string that is over 128 chars or
198
+ contains whitespace, and the hosted ingest endpoint re-applies the same rule as a
199
+ backstop — so analysis later is possible without prompts or outputs ever being
200
+ stored here. Building the envelope never blocks or changes a run: on any failure
201
+ it is simply absent (`meta` is null).
202
+
203
+ Runs carry a companion `RunMeta@v1` on `runs.meta`, set at `run.start`:
204
+
205
+ ```ts
206
+ const h = rig.run.start({
207
+ pipeline: "nightly-report",
208
+ meta: { v: 1, trigger: "cron", actor: "scheduler", config_hash: "…" },
209
+ // pipeline_sha auto-fills from git when you don't set it (never fails the run)
210
+ });
211
+ ```
212
+
213
+ ### Reserved dimensions
214
+
215
+ Business dimensions live in `tags`. Five keys are **well-known** and become
216
+ first-class group-bys in cost and coverage views — use them verbatim so your data
217
+ lines up with the console's built-ins:
218
+
219
+ | key | meaning |
220
+ |---|---|
221
+ | `subject` | the customer/tenant the work is for — an **opaque** id, hashed if it looks like PII |
222
+ | `feature` | the product feature the call serves |
223
+ | `user` | the end user (an opaque id) |
224
+ | `session` | the conversation/session the call belongs to |
225
+ | `cost_center` | the team/budget to attribute spend to |
226
+
227
+ `modelrig validate` warns on near-misses — a declared `rig.yaml` dimension named
228
+ `customer`, `tenant`, `user_id`, `team`, … is nudged toward the reserved key it
229
+ resembles. They stay ordinary tags; nothing is required.
230
+
185
231
  ## CLI
186
232
 
187
233
  The package ships a `modelrig` binary:
@@ -217,8 +263,11 @@ on the quickstart:
217
263
  - **`modelrig serve` needs the `modelrig-server` package** — it is not bundled
218
264
  here.
219
265
  - **What the ladder does and does not do.** Fall-through is structural and
220
- infrastructural, never a quality judgment; the full boundary (quality gate,
221
- raw-lane structure, latency ordering…) is generated and tripwire-tested on
266
+ infrastructural, plus an OPTIONAL SDK-lane `qualityGate` predicate you supply
267
+ (a schema-valid output it rejects becomes
268
+ `quality_rejected`); the route-declared
269
+ hosted judge is not built yet, and the full boundary (raw-lane structure, latency
270
+ ordering…) is generated and tripwire-tested on
222
271
  [Routing & reliability](https://modelrig.dev/routing-reliability.html#10-not-yet-implemented-the-boundary).
223
272
 
224
273
  ## License
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAIV,cAAc,EACd,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAEhE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAG9D,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AASD;yDACyD;AACzD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAclF;AAED,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAwB3E;AAED,UAAU,aAAa;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC;CACxB;AAED;;mEAEmE;AACnE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,aAAa,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;KAAE,CAAC;CACzD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;wFACoF;IACpF,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;+DAC2D;IAC3D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,oBAAoB,EAAE,CAAC;IAC3D,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtF,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,QAAQ,CAAC,WAAW,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,uBAAuB,CAAC;CACnG;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,YAAY,EACjB,MAAM,UAAQ,GACb,qBAAqB,CAuDvB;AAED;iEACiE;AACjE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IAC9C,QAAQ,CAAC,cAAc,CAAC,EAAE;QACxB,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;QAC5C,QAAQ,CAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;KAC7C,CAAC;CACH;AAED,0EAA0E;AAC1E,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;CACrC;AAED;;;;;;GAMG;AACH,wBAAgB,4BAA4B,CAC1C,CAAC,EAAE,sBAAsB,GACxB,6BAA6B,CAa/B;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,eAAe,CAuMtF"}
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@ exports.normalizeAnthropicInputUsage = normalizeAnthropicInputUsage;
22
22
  exports.createAnthropicAdapter = createAnthropicAdapter;
23
23
  const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
24
24
  const tool_mapping_1 = require("./tool-mapping");
25
+ const attempt_meta_map_1 = require("./attempt-meta-map");
25
26
  const sampling_1 = require("./sampling");
26
27
  const DEFAULT_MAX_TOKENS = 32000;
27
28
  const OUTPUT_TOOL_NAME = "route_output";
@@ -159,6 +160,8 @@ function createAnthropicAdapter(config) {
159
160
  supportsTools: () => true,
160
161
  async call(req) {
161
162
  const model = req.candidate.model;
163
+ // WI-1: native attempt envelope for this call (see openai.ts for the pattern).
164
+ let attemptMeta;
162
165
  // F1: post-stream failures still billed tokens — attach usage.
163
166
  const fail = (cls, message, fixHint, usage) => ({
164
167
  ok: false,
@@ -170,6 +173,7 @@ function createAnthropicAdapter(config) {
170
173
  ...(fixHint !== undefined ? { fixHint } : {}),
171
174
  },
172
175
  ...(usage !== undefined ? { usage } : {}),
176
+ ...(attemptMeta !== undefined ? { meta: attemptMeta } : {}),
173
177
  });
174
178
  const wantJson = req.outputSchema !== null;
175
179
  try {
@@ -210,9 +214,20 @@ function createAnthropicAdapter(config) {
210
214
  };
211
215
  let completionTokens = 0;
212
216
  let stopReason = null;
217
+ // WI-1 native fields: message.id + message.model arrive at message_start.
218
+ let responseModel = "";
219
+ let responseId = "";
213
220
  for await (const event of events) {
214
- if (event.type === "message_start" && event.message?.usage !== undefined) {
215
- inputUsage = normalizeAnthropicInputUsage(event.message.usage);
221
+ if (event.type === "message_start") {
222
+ if (event.message?.usage !== undefined) {
223
+ inputUsage = normalizeAnthropicInputUsage(event.message.usage);
224
+ }
225
+ if (responseModel === "" && typeof event.message?.model === "string") {
226
+ responseModel = event.message.model;
227
+ }
228
+ if (responseId === "" && typeof event.message?.id === "string") {
229
+ responseId = event.message.id;
230
+ }
216
231
  }
217
232
  else if (event.type === "content_block_start" &&
218
233
  wantTools &&
@@ -257,13 +272,25 @@ function createAnthropicAdapter(config) {
257
272
  };
258
273
  // Tool-call turn (spec §4.4): `arguments` is the raw JSON string the
259
274
  // model streamed (OpenAI parity) — text may be empty, skip the guards.
260
- if (wantTools && toolBlocks.size > 0) {
261
- const toolCalls = [...toolBlocks.values()].map((b, i) => ({
275
+ const toolCalls = wantTools && toolBlocks.size > 0
276
+ ? [...toolBlocks.values()].map((b, i) => ({
262
277
  id: b.id !== "" ? b.id : `call_${i}`,
263
278
  type: "function",
264
279
  function: { name: b.name, arguments: b.json === "" ? "{}" : b.json },
265
- }));
266
- return { ok: true, text, usage, toolCalls };
280
+ }))
281
+ : undefined;
282
+ // WI-1: native attempt envelope. refusal derives from stop_reason=refusal.
283
+ attemptMeta = (0, attempt_meta_map_1.buildAdapterMeta)("anthropic", {
284
+ req,
285
+ finishReasonRaw: stopReason,
286
+ refusal: stopReason === "refusal",
287
+ modelSnapshot: responseModel,
288
+ providerRequestId: responseId,
289
+ toolCalls,
290
+ streaming: false,
291
+ });
292
+ if (toolCalls !== undefined) {
293
+ return { ok: true, text, usage, toolCalls, meta: attemptMeta };
267
294
  }
268
295
  if (stopReason === "refusal") {
269
296
  return fail("refusal", "Anthropic refused this request (stop_reason=refusal)", undefined, usage);
@@ -274,11 +301,12 @@ function createAnthropicAdapter(config) {
274
301
  if (stopReason === "max_tokens" && wantJson) {
275
302
  return fail("content_invalid", "Anthropic structured output truncated (stop_reason=max_tokens)", "raise maxOutputTokens for this route", usage);
276
303
  }
277
- return { ok: true, text, usage };
304
+ return { ok: true, text, usage, meta: attemptMeta };
278
305
  }
279
306
  catch (error) {
280
307
  const message = error instanceof Error ? error.message : String(error);
281
308
  const { cls, fixHint } = classifyAnthropicFailure(message);
309
+ attemptMeta = (0, attempt_meta_map_1.transportErrorMeta)(error);
282
310
  return {
283
311
  ok: false,
284
312
  failure: {
@@ -289,6 +317,7 @@ function createAnthropicAdapter(config) {
289
317
  cause: error,
290
318
  ...(fixHint !== undefined ? { fixHint } : {}),
291
319
  },
320
+ meta: attemptMeta,
292
321
  };
293
322
  }
294
323
  },
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Per-adapter NATIVE metadata mapping (WI-1, spec §2). ONE place that turns a
3
+ * provider's native finish/refusal/usage/id signals into the additive
4
+ * `AdapterResult.meta` (`Partial<AttemptMetaV1>`). Adapters map ONLY what the
5
+ * provider reports; the producer (`telemetry/attempt-meta.ts`) adds
6
+ * hashes/sizes/cost and enforces the classification rule. Absent = unknown;
7
+ * nothing here is inferred.
8
+ *
9
+ * `refusal` derivation, per adapter:
10
+ * - openai / grok / deepseek / deepinfra / fireworks (OpenAI dialect): the
11
+ * stream carried a non-empty `choices[].delta.refusal` string, OR
12
+ * `finish_reason === "content_filter"`.
13
+ * - anthropic: `stop_reason === "refusal"`.
14
+ * - gemini: `finishReason` is `SAFETY` / `PROHIBITED_CONTENT` / `RECITATION`.
15
+ * The refusal TEXT itself is never carried (content) — only the boolean.
16
+ */
17
+ import type { AttemptMetaV1, FinishReason } from "../telemetry/attempt-meta";
18
+ import type { AdapterRequest, ProviderId, ToolCall } from "../types/provider";
19
+ /** Map a provider's native finish/stop reason to the `FinishReason` enum
20
+ * (spec §2). Unknown/absent ⇒ undefined (never inferred); an unrecognised
21
+ * non-empty value ⇒ `"other"`. */
22
+ export declare function mapFinishReason(provider: ProviderId, raw: string | null | undefined): FinishReason | undefined;
23
+ /** The declared sampling echo (spec §2 `sampling`) — the values ModelRig sent,
24
+ * read back from the request. topP → top_p, maxOutputTokens → max_output_tokens
25
+ * (the raw lane's top-level or the routed lane's `sampling` block). Numbers
26
+ * only; undefined when the caller declared none. */
27
+ export declare function samplingEcho(req: AdapterRequest): {
28
+ temperature?: number;
29
+ top_p?: number;
30
+ max_output_tokens?: number;
31
+ } | undefined;
32
+ /** Aggregate normalized tool calls into `{ name, count }` (RAW names — the
33
+ * producer hashes undeclared ones). Undefined when the turn made none. */
34
+ export declare function aggregateToolCalls(toolCalls: readonly ToolCall[] | undefined): {
35
+ name: string;
36
+ count: number;
37
+ }[] | undefined;
38
+ /** The signals an adapter collects on ONE completed call. All optional. */
39
+ export interface AdapterMetaSignals {
40
+ readonly req: AdapterRequest;
41
+ readonly finishReasonRaw?: string | null;
42
+ readonly refusal?: boolean;
43
+ readonly reasoningTokens?: number;
44
+ readonly modelSnapshot?: string;
45
+ readonly providerRequestId?: string;
46
+ readonly region?: string;
47
+ readonly toolCalls?: readonly ToolCall[];
48
+ readonly grounding?: {
49
+ citations?: number;
50
+ searchProvider?: string;
51
+ };
52
+ /** true = the run loop streamed deltas (stream()); false/absent = buffered. */
53
+ readonly streaming?: boolean;
54
+ }
55
+ /** Build one adapter's NATIVE `meta` (spec §2). Only defined signals appear —
56
+ * everything else is unknown and omitted. The producer takes it from here. */
57
+ export declare function buildAdapterMeta(provider: ProviderId, s: AdapterMetaSignals): Partial<AttemptMetaV1>;
58
+ /** A transport/SDK error's coarse envelope (spec §2 `http_status`/`error_code`).
59
+ * Reads `error.status` (number) and a short `error.code` / `error.error.code`
60
+ * slug — NEVER the message (that is free text). The producer's classification
61
+ * rule drops a code that turns out to be free text. */
62
+ export declare function transportErrorMeta(error: unknown): Partial<AttemptMetaV1>;
63
+ //# sourceMappingURL=attempt-meta-map.d.ts.map
@@ -0,0 +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;AAC7E,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;CAC9B;AAED;8EAC8E;AAC9E,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EACpB,CAAC,EAAE,kBAAkB,GACpB,OAAO,CAAC,aAAa,CAAC,CAyBxB;AAED;;;uDAGuD;AACvD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CASzE"}
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ /**
3
+ * Per-adapter NATIVE metadata mapping (WI-1, spec §2). ONE place that turns a
4
+ * provider's native finish/refusal/usage/id signals into the additive
5
+ * `AdapterResult.meta` (`Partial<AttemptMetaV1>`). Adapters map ONLY what the
6
+ * provider reports; the producer (`telemetry/attempt-meta.ts`) adds
7
+ * hashes/sizes/cost and enforces the classification rule. Absent = unknown;
8
+ * nothing here is inferred.
9
+ *
10
+ * `refusal` derivation, per adapter:
11
+ * - openai / grok / deepseek / deepinfra / fireworks (OpenAI dialect): the
12
+ * stream carried a non-empty `choices[].delta.refusal` string, OR
13
+ * `finish_reason === "content_filter"`.
14
+ * - anthropic: `stop_reason === "refusal"`.
15
+ * - gemini: `finishReason` is `SAFETY` / `PROHIBITED_CONTENT` / `RECITATION`.
16
+ * The refusal TEXT itself is never carried (content) — only the boolean.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.mapFinishReason = mapFinishReason;
20
+ exports.samplingEcho = samplingEcho;
21
+ exports.aggregateToolCalls = aggregateToolCalls;
22
+ exports.buildAdapterMeta = buildAdapterMeta;
23
+ exports.transportErrorMeta = transportErrorMeta;
24
+ /** Map a provider's native finish/stop reason to the `FinishReason` enum
25
+ * (spec §2). Unknown/absent ⇒ undefined (never inferred); an unrecognised
26
+ * non-empty value ⇒ `"other"`. */
27
+ function mapFinishReason(provider, raw) {
28
+ if (raw === null || raw === undefined || raw === "")
29
+ return undefined;
30
+ if (provider === "anthropic") {
31
+ switch (raw) {
32
+ case "end_turn":
33
+ case "stop_sequence":
34
+ return "stop";
35
+ case "max_tokens":
36
+ return "length";
37
+ case "tool_use":
38
+ return "tool_calls";
39
+ case "refusal":
40
+ return "refusal";
41
+ default:
42
+ return "other";
43
+ }
44
+ }
45
+ if (provider === "gemini") {
46
+ switch (raw) {
47
+ case "STOP":
48
+ return "stop";
49
+ case "MAX_TOKENS":
50
+ return "length";
51
+ case "SAFETY":
52
+ case "PROHIBITED_CONTENT":
53
+ case "RECITATION":
54
+ return "content_filter";
55
+ default:
56
+ return "other";
57
+ }
58
+ }
59
+ // OpenAI dialect (openai / grok / deepseek / deepinfra / fireworks).
60
+ switch (raw) {
61
+ case "stop":
62
+ return "stop";
63
+ case "length":
64
+ return "length";
65
+ case "tool_calls":
66
+ case "function_call":
67
+ return "tool_calls";
68
+ case "content_filter":
69
+ return "content_filter";
70
+ default:
71
+ return "other";
72
+ }
73
+ }
74
+ /** The declared sampling echo (spec §2 `sampling`) — the values ModelRig sent,
75
+ * read back from the request. topP → top_p, maxOutputTokens → max_output_tokens
76
+ * (the raw lane's top-level or the routed lane's `sampling` block). Numbers
77
+ * only; undefined when the caller declared none. */
78
+ function samplingEcho(req) {
79
+ const s = {};
80
+ if (typeof req.sampling?.temperature === "number")
81
+ s.temperature = req.sampling.temperature;
82
+ if (typeof req.sampling?.topP === "number")
83
+ s.top_p = req.sampling.topP;
84
+ const maxOut = req.sampling?.maxOutputTokens ?? req.maxOutputTokens;
85
+ if (typeof maxOut === "number")
86
+ s.max_output_tokens = maxOut;
87
+ return Object.keys(s).length > 0 ? s : undefined;
88
+ }
89
+ /** Aggregate normalized tool calls into `{ name, count }` (RAW names — the
90
+ * producer hashes undeclared ones). Undefined when the turn made none. */
91
+ function aggregateToolCalls(toolCalls) {
92
+ if (toolCalls === undefined || toolCalls.length === 0)
93
+ return undefined;
94
+ const counts = new Map();
95
+ for (const tc of toolCalls) {
96
+ const name = tc.function.name;
97
+ counts.set(name, (counts.get(name) ?? 0) + 1);
98
+ }
99
+ return [...counts.entries()].map(([name, count]) => ({ name, count }));
100
+ }
101
+ /** Build one adapter's NATIVE `meta` (spec §2). Only defined signals appear —
102
+ * everything else is unknown and omitted. The producer takes it from here. */
103
+ function buildAdapterMeta(provider, s) {
104
+ const meta = {};
105
+ const finish = mapFinishReason(provider, s.finishReasonRaw);
106
+ if (finish !== undefined)
107
+ meta.finish_reason = finish;
108
+ if (typeof s.refusal === "boolean")
109
+ meta.refusal = s.refusal;
110
+ if (typeof s.reasoningTokens === "number" && s.reasoningTokens > 0) {
111
+ meta.reasoning_tokens = s.reasoningTokens;
112
+ }
113
+ if (typeof s.modelSnapshot === "string" && s.modelSnapshot !== "")
114
+ meta.model_snapshot = s.modelSnapshot;
115
+ if (typeof s.providerRequestId === "string" && s.providerRequestId !== "") {
116
+ meta.provider_request_id = s.providerRequestId;
117
+ }
118
+ if (typeof s.region === "string" && s.region !== "")
119
+ meta.region = s.region;
120
+ const sampling = samplingEcho(s.req);
121
+ if (sampling !== undefined)
122
+ meta.sampling = sampling;
123
+ const tools = aggregateToolCalls(s.toolCalls);
124
+ if (tools !== undefined)
125
+ meta.tool_calls = tools;
126
+ if (s.grounding !== undefined) {
127
+ const g = {};
128
+ if (typeof s.grounding.citations === "number")
129
+ g.citations = s.grounding.citations;
130
+ if (typeof s.grounding.searchProvider === "string")
131
+ g.search_provider = s.grounding.searchProvider;
132
+ if (Object.keys(g).length > 0)
133
+ meta.grounding = g;
134
+ }
135
+ if (typeof s.streaming === "boolean")
136
+ meta.streaming = s.streaming;
137
+ return meta;
138
+ }
139
+ /** A transport/SDK error's coarse envelope (spec §2 `http_status`/`error_code`).
140
+ * Reads `error.status` (number) and a short `error.code` / `error.error.code`
141
+ * slug — NEVER the message (that is free text). The producer's classification
142
+ * rule drops a code that turns out to be free text. */
143
+ function transportErrorMeta(error) {
144
+ const meta = {};
145
+ if (typeof error === "object" && error !== null) {
146
+ const e = error;
147
+ if (typeof e.status === "number" && Number.isFinite(e.status))
148
+ meta.http_status = e.status;
149
+ const code = typeof e.code === "string" ? e.code : e.error?.code;
150
+ if (typeof code === "string" && code !== "")
151
+ meta.error_code = code;
152
+ }
153
+ return meta;
154
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../../src/adapters/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAIV,cAAc,EACd,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMhE,OAAO,KAAK,EAAuB,cAAc,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAMhG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAMhF;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAiB1E;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1D,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;KAAE,CAAC;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAOD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,oBAAoB,CAkC1F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CA8JpF"}
1
+ {"version":3,"file":"deepseek.d.ts","sourceRoot":"","sources":["../../src/adapters/deepseek.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAIV,cAAc,EACd,eAAe,EAChB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAMhE,OAAO,KAAK,EAAuB,cAAc,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAQhG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAKD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAMhF;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAiB1E;AAED,UAAU,WAAW;IACnB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,WAAW,EAAE,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAA;KAAE,CAAC;IAC1D,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;KAAE,CAAC;IAC5D,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAOD,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,oBAAoB,CAkC1F;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,eAAe,CAoLpF"}
@@ -24,6 +24,7 @@ exports.createDeepSeekAdapter = createDeepSeekAdapter;
24
24
  const openai_1 = __importDefault(require("openai"));
25
25
  const tool_mapping_1 = require("./tool-mapping");
26
26
  const openai_stream_1 = require("./openai-stream");
27
+ const attempt_meta_map_1 = require("./attempt-meta-map");
27
28
  const sampling_1 = require("./sampling");
28
29
  const DEEPSEEK_BASE_URL = "https://api.deepseek.com";
29
30
  const DEFAULT_MAX_TOKENS = 64000; // R1/V3 max — full capacity for long reports (ported)
@@ -102,6 +103,8 @@ function createDeepSeekAdapter(config) {
102
103
  supportsTools: () => true,
103
104
  async call(req) {
104
105
  const model = req.candidate.model;
106
+ // WI-1: native attempt envelope for this call (see openai.ts for the pattern).
107
+ let attemptMeta;
105
108
  // F1: post-stream failures still billed tokens — attach usage.
106
109
  const fail = (cls, message, fixHint, usage) => ({
107
110
  ok: false,
@@ -113,6 +116,7 @@ function createDeepSeekAdapter(config) {
113
116
  ...(fixHint !== undefined ? { fixHint } : {}),
114
117
  },
115
118
  ...(usage !== undefined ? { usage } : {}),
119
+ ...(attemptMeta !== undefined ? { meta: attemptMeta } : {}),
116
120
  });
117
121
  try {
118
122
  const client = new openai_1.default({
@@ -130,12 +134,18 @@ function createDeepSeekAdapter(config) {
130
134
  let finishReason = null;
131
135
  // TTFB (Phase 4 WS3): first streamed chunk = first body byte.
132
136
  let ttfbMs;
137
+ let responseModel = "";
138
+ let responseId = "";
133
139
  const toolCallAcc = (0, tool_mapping_1.createOpenAIToolCallAccumulator)();
134
140
  // Only delta.content is accumulated — a reasoner's reasoning_content
135
141
  // (chain-of-thought) must never leak into the JSON buffer (ported).
136
142
  for await (const chunk of stream) {
137
143
  if (ttfbMs === undefined)
138
144
  ttfbMs = Date.now() - dispatchedAt;
145
+ if (responseModel === "" && typeof chunk.model === "string")
146
+ responseModel = chunk.model;
147
+ if (responseId === "" && typeof chunk.id === "string")
148
+ responseId = chunk.id;
139
149
  const choice = chunk.choices[0];
140
150
  if (choice?.delta?.content)
141
151
  text += choice.delta.content;
@@ -159,8 +169,19 @@ function createDeepSeekAdapter(config) {
159
169
  };
160
170
  // Tool-call turn (spec §4.4): surface normalized calls, skip content guards.
161
171
  const toolCalls = toolCallAcc.finalize();
172
+ // WI-1: native attempt envelope. DeepSeek has no `refusal` delta — a
173
+ // refusal surfaces as finish_reason content_filter.
174
+ attemptMeta = (0, attempt_meta_map_1.buildAdapterMeta)("deepseek", {
175
+ req,
176
+ finishReasonRaw: finishReason,
177
+ refusal: finishReason === "content_filter",
178
+ modelSnapshot: responseModel,
179
+ providerRequestId: responseId,
180
+ toolCalls,
181
+ streaming: false,
182
+ });
162
183
  if (toolCalls !== undefined) {
163
- return { ok: true, text, usage, toolCalls };
184
+ return { ok: true, text, usage, toolCalls, meta: attemptMeta };
164
185
  }
165
186
  if (text.trim().length === 0) {
166
187
  return fail("content_invalid", `empty response from DeepSeek (finish_reason=${finishReason ?? "none"})`, undefined, usage);
@@ -168,11 +189,12 @@ function createDeepSeekAdapter(config) {
168
189
  if (finishReason === "length" && req.outputSchema !== null) {
169
190
  return fail("content_invalid", "DeepSeek JSON output truncated (finish_reason=length)", "raise maxOutputTokens for this route", usage);
170
191
  }
171
- return { ok: true, text, usage };
192
+ return { ok: true, text, usage, meta: attemptMeta };
172
193
  }
173
194
  catch (error) {
174
195
  const message = error instanceof Error ? error.message : String(error);
175
196
  const { cls, fixHint } = classifyDeepSeekFailure(message);
197
+ attemptMeta = (0, attempt_meta_map_1.transportErrorMeta)(error);
176
198
  return {
177
199
  ok: false,
178
200
  failure: {
@@ -183,6 +205,7 @@ function createDeepSeekAdapter(config) {
183
205
  cause: error,
184
206
  ...(fixHint !== undefined ? { fixHint } : {}),
185
207
  },
208
+ meta: attemptMeta,
186
209
  };
187
210
  }
188
211
  },
@@ -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,EAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,mBAAmB,CAAC;AAKlE,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;CAC1C;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,mBAAmB,CAqDxF;AA0BD;;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,CA+KhF"}
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,EAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,mBAAmB,CAAC;AAQlE,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;CAC1C;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,YAAY,GAAG,mBAAmB,CAqDxF;AA0BD;;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,CA4MhF"}