@tangle-network/agent-eval 0.108.1 → 0.110.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 (175) hide show
  1. package/dist/analyst/index.d.ts +10 -12
  2. package/dist/analyst/index.js +8 -11
  3. package/dist/analyst/index.js.map +1 -1
  4. package/dist/{analyze-runs-DJYpep3L.d.ts → analyze-runs-Dmz6LA9e.d.ts} +4 -4
  5. package/dist/{baseline-Bbid3WoO.d.ts → baseline-DsNteOgR.d.ts} +32 -2
  6. package/dist/belief-state/index.d.ts +6 -6
  7. package/dist/benchmarks/index.d.ts +4 -4
  8. package/dist/benchmarks/index.js +7 -8
  9. package/dist/builder-eval/index.d.ts +4 -4
  10. package/dist/builder-eval/index.js +1 -2
  11. package/dist/builder-eval/index.js.map +1 -1
  12. package/dist/{calibration-BPmzuVPk.d.ts → calibration-Dz8TQV4y.d.ts} +2 -2
  13. package/dist/campaign/index.d.ts +161 -20
  14. package/dist/campaign/index.js +15 -8
  15. package/dist/{chunk-LOJ2QVCE.js → chunk-2IY4ILP4.js} +2 -2
  16. package/dist/{chunk-LIEJUH2I.js → chunk-6PL5MGDL.js} +9 -9
  17. package/dist/{chunk-2OGPXHOB.js → chunk-7NX6ZSBG.js} +36 -7
  18. package/dist/chunk-7NX6ZSBG.js.map +1 -0
  19. package/dist/{chunk-OVPVM4JC.js → chunk-GTERJI6Q.js} +4 -4
  20. package/dist/{chunk-YEHAEDUD.js → chunk-IMWDSFUM.js} +604 -2
  21. package/dist/chunk-IMWDSFUM.js.map +1 -0
  22. package/dist/{chunk-JZXGWLK5.js → chunk-MHNQWM4I.js} +62 -6
  23. package/dist/chunk-MHNQWM4I.js.map +1 -0
  24. package/dist/{chunk-QRVS7MX4.js → chunk-OW47B5WA.js} +3 -5
  25. package/dist/{chunk-QRVS7MX4.js.map → chunk-OW47B5WA.js.map} +1 -1
  26. package/dist/{chunk-DBDRR6GF.js → chunk-PLOMR3HP.js} +48 -2
  27. package/dist/chunk-PLOMR3HP.js.map +1 -0
  28. package/dist/{chunk-GDZAWO2I.js → chunk-QFGTU7MT.js} +2 -2
  29. package/dist/{chunk-6SKVFBTR.js → chunk-RNB2NICW.js} +115 -13
  30. package/dist/chunk-RNB2NICW.js.map +1 -0
  31. package/dist/{chunk-V7HNA47Z.js → chunk-RSVSSZKF.js} +5 -5
  32. package/dist/{chunk-5PK3626Q.js → chunk-XRGOKCMO.js} +88 -17
  33. package/dist/chunk-XRGOKCMO.js.map +1 -0
  34. package/dist/{code-agent-session-rnJKlqmT.d.ts → code-agent-session-yitf9I-F.d.ts} +1 -1
  35. package/dist/contract/index.d.ts +20 -23
  36. package/dist/contract/index.js +11 -13
  37. package/dist/contract/index.js.map +1 -1
  38. package/dist/{control-B8UthSBL.d.ts → control-U8LBKUES.d.ts} +5 -6
  39. package/dist/control.d.ts +8 -9
  40. package/dist/control.js +6 -8
  41. package/dist/{dataset-DS7ytHZU.d.ts → dataset-NENEzRgk.d.ts} +1 -1
  42. package/dist/{default-registry-BswHCXnU.d.ts → default-registry-Bcf1uKVI.d.ts} +1 -2
  43. package/dist/{emitter-C2rqGH_l.d.ts → emitter-BRchAAAx.d.ts} +2 -2
  44. package/dist/{failure-cluster-DH9Flgcf.d.ts → failure-cluster-C48PiReX.d.ts} +2 -2
  45. package/dist/feedback-trajectory-pDcz1lQ1.d.ts +348 -0
  46. package/dist/{gepa-B3x5Ulcv.d.ts → gepa-BUNP3606.d.ts} +143 -2
  47. package/dist/hosted/index.d.ts +7 -7
  48. package/dist/{index-pPtfoIJO.d.ts → index-Dc3VLGhp.d.ts} +2 -2
  49. package/dist/index.d.ts +645 -61
  50. package/dist/index.js +1282 -190
  51. package/dist/index.js.map +1 -1
  52. package/dist/{insight-report-B4xrdwEK.d.ts → insight-report-D4cXFsLt.d.ts} +1 -1
  53. package/dist/{integrity-DqGZg3st.d.ts → integrity-qemeBAyx.d.ts} +1 -1
  54. package/dist/{types-D1ytG0Yg.d.ts → kind-factory-20hcaYpf.d.ts} +169 -2
  55. package/dist/meta-eval/index.d.ts +5 -5
  56. package/dist/meta-eval/index.js +1 -2
  57. package/dist/meta-eval/index.js.map +1 -1
  58. package/dist/{multi-layer-verifier-CI4jdX-q.d.ts → multi-layer-verifier-BsqKuLyN.d.ts} +1 -1
  59. package/dist/multishot/index.d.ts +3 -3
  60. package/dist/openapi.json +1 -1
  61. package/dist/pipelines/index.d.ts +6 -7
  62. package/dist/pipelines/index.js +3 -6
  63. package/dist/pipelines/index.js.map +1 -1
  64. package/dist/{policy-edit-DQUXYMDm.d.ts → policy-edit-D2bBDZDf.d.ts} +2 -2
  65. package/dist/{pre-registration-BUhVPzE7.d.ts → pre-registration-BepVVa6P.d.ts} +3 -3
  66. package/dist/{provenance-DdDhf6cg.d.ts → provenance-DMvsfknv.d.ts} +3 -5
  67. package/dist/{query-0aTmbmQe.d.ts → query-Ck190MOd.d.ts} +2 -2
  68. package/dist/{release-report-DeJpsBiA.d.ts → release-report-oBfOz8ku.d.ts} +3 -3
  69. package/dist/reporting.d.ts +8 -8
  70. package/dist/{researcher-Wc7dx6GM.d.ts → researcher-CaH0CwFC.d.ts} +6 -6
  71. package/dist/rl.d.ts +568 -15
  72. package/dist/rl.js +4 -4
  73. package/dist/{rubric-predictive-validity-DPnyG-CE.d.ts → rubric-predictive-validity-C-fMteAW.d.ts} +1 -1
  74. package/dist/{run-record-I-Z3JNvO.d.ts → run-record-DksGsfgv.d.ts} +1 -1
  75. package/dist/{runtime-trajectory-iW9IhV3e.d.ts → runtime-trajectory-h5i0SZUj.d.ts} +1 -1
  76. package/dist/{schema-m0gsnbt3.d.ts → schema-SGWcK9wa.d.ts} +1 -1
  77. package/dist/{semantic-concept-judge-BmNZPB_j.d.ts → semantic-concept-judge-D7z6JCLZ.d.ts} +57 -4
  78. package/dist/{store-BcFXE6LG.d.ts → store-BsVi7ncX.d.ts} +1 -1
  79. package/dist/storyboard/index.d.ts +1 -1
  80. package/dist/{summary-report-QMZVe3P-.d.ts → summary-report-Bz-0-t8v.d.ts} +2 -2
  81. package/dist/{test-graded-scenario-DeODGLra.d.ts → test-graded-scenario-mzYBKspu.d.ts} +3 -3
  82. package/dist/traces.d.ts +54 -11
  83. package/dist/traces.js +25 -27
  84. package/dist/{types-BdIv5dvA.d.ts → types-v--ctu-b.d.ts} +2 -2
  85. package/dist/wire/index.d.ts +5 -6
  86. package/package.json +1 -71
  87. package/dist/adapters/http.d.ts +0 -142
  88. package/dist/adapters/http.js +0 -203
  89. package/dist/adapters/http.js.map +0 -1
  90. package/dist/adapters/langchain.d.ts +0 -95
  91. package/dist/adapters/langchain.js +0 -34
  92. package/dist/adapters/langchain.js.map +0 -1
  93. package/dist/adapters/otel.d.ts +0 -112
  94. package/dist/adapters/otel.js +0 -110
  95. package/dist/adapters/otel.js.map +0 -1
  96. package/dist/chunk-2OGPXHOB.js.map +0 -1
  97. package/dist/chunk-45EEMHTC.js +0 -35
  98. package/dist/chunk-45EEMHTC.js.map +0 -1
  99. package/dist/chunk-5BKGXME7.js +0 -65
  100. package/dist/chunk-5BKGXME7.js.map +0 -1
  101. package/dist/chunk-5PK3626Q.js.map +0 -1
  102. package/dist/chunk-6SK5VFYK.js +0 -100
  103. package/dist/chunk-6SK5VFYK.js.map +0 -1
  104. package/dist/chunk-6SKVFBTR.js.map +0 -1
  105. package/dist/chunk-DBDRR6GF.js.map +0 -1
  106. package/dist/chunk-DJWX3GVS.js +0 -81
  107. package/dist/chunk-DJWX3GVS.js.map +0 -1
  108. package/dist/chunk-FOUG2VVS.js +0 -855
  109. package/dist/chunk-FOUG2VVS.js.map +0 -1
  110. package/dist/chunk-JZXGWLK5.js.map +0 -1
  111. package/dist/chunk-K7QEIHHJ.js +0 -613
  112. package/dist/chunk-K7QEIHHJ.js.map +0 -1
  113. package/dist/chunk-KKHDIONI.js +0 -414
  114. package/dist/chunk-KKHDIONI.js.map +0 -1
  115. package/dist/chunk-KMPRBJK4.js +0 -74
  116. package/dist/chunk-KMPRBJK4.js.map +0 -1
  117. package/dist/chunk-Q2JRAWRI.js +0 -196
  118. package/dist/chunk-Q2JRAWRI.js.map +0 -1
  119. package/dist/chunk-RZTMDUO7.js +0 -49
  120. package/dist/chunk-RZTMDUO7.js.map +0 -1
  121. package/dist/chunk-STGVSCDH.js +0 -202
  122. package/dist/chunk-STGVSCDH.js.map +0 -1
  123. package/dist/chunk-YEHAEDUD.js.map +0 -1
  124. package/dist/control-runtime-Acf9CGhw.d.ts +0 -182
  125. package/dist/corpus-eBVwhCp1.d.ts +0 -560
  126. package/dist/counterfactual-DlOz8PBx.d.ts +0 -85
  127. package/dist/diagnose.d.ts +0 -252
  128. package/dist/diagnose.js +0 -382
  129. package/dist/diagnose.js.map +0 -1
  130. package/dist/feedback-trajectory-C9KCo8ag.d.ts +0 -169
  131. package/dist/governance/index.d.ts +0 -135
  132. package/dist/governance/index.js +0 -18
  133. package/dist/governance/index.js.map +0 -1
  134. package/dist/groundedness/index.d.ts +0 -112
  135. package/dist/groundedness/index.js +0 -77
  136. package/dist/groundedness/index.js.map +0 -1
  137. package/dist/harness-optimizer-mOl9XX_O.d.ts +0 -106
  138. package/dist/kind-factory-DvIGo_cP.d.ts +0 -171
  139. package/dist/knowledge/index.d.ts +0 -103
  140. package/dist/knowledge/index.js +0 -18
  141. package/dist/knowledge/index.js.map +0 -1
  142. package/dist/pareto-E-pembql.d.ts +0 -81
  143. package/dist/perf/index.d.ts +0 -123
  144. package/dist/perf/index.js +0 -18
  145. package/dist/perf/index.js.map +0 -1
  146. package/dist/prm/index.d.ts +0 -104
  147. package/dist/prm/index.js +0 -265
  148. package/dist/prm/index.js.map +0 -1
  149. package/dist/product-benchmark/index.d.ts +0 -247
  150. package/dist/product-benchmark/index.js +0 -37
  151. package/dist/product-benchmark/index.js.map +0 -1
  152. package/dist/red-team-KmmiqBlY.d.ts +0 -63
  153. package/dist/redact-B40YG2M_.d.ts +0 -45
  154. package/dist/rubric-Cc6UHvUb.d.ts +0 -73
  155. package/dist/run-critic-CmMf05uV.d.ts +0 -56
  156. package/dist/sink-fetch-B1Yg4Til.d.ts +0 -101
  157. package/dist/telemetry/file.d.ts +0 -19
  158. package/dist/telemetry/file.js +0 -45
  159. package/dist/telemetry/file.js.map +0 -1
  160. package/dist/telemetry/index.d.ts +0 -38
  161. package/dist/telemetry/index.js +0 -130
  162. package/dist/telemetry/index.js.map +0 -1
  163. package/dist/testing-C21CHsq2.d.ts +0 -20
  164. package/dist/testing.d.ts +0 -1
  165. package/dist/testing.js +0 -8
  166. package/dist/testing.js.map +0 -1
  167. package/dist/trajectory-2TkpSEVh.d.ts +0 -33
  168. package/dist/workflow/index.d.ts +0 -496
  169. package/dist/workflow/index.js +0 -2178
  170. package/dist/workflow/index.js.map +0 -1
  171. /package/dist/{chunk-LOJ2QVCE.js.map → chunk-2IY4ILP4.js.map} +0 -0
  172. /package/dist/{chunk-LIEJUH2I.js.map → chunk-6PL5MGDL.js.map} +0 -0
  173. /package/dist/{chunk-OVPVM4JC.js.map → chunk-GTERJI6Q.js.map} +0 -0
  174. /package/dist/{chunk-GDZAWO2I.js.map → chunk-QFGTU7MT.js.map} +0 -0
  175. /package/dist/{chunk-V7HNA47Z.js.map → chunk-RSVSSZKF.js.map} +0 -0
@@ -1,203 +0,0 @@
1
- import "../chunk-PZ5AY32C.js";
2
-
3
- // src/adapters/http.ts
4
- function resolveAuth(auth) {
5
- if (!auth) return Promise.resolve(null);
6
- if (typeof auth === "string") return Promise.resolve(auth);
7
- return Promise.resolve(auth());
8
- }
9
- function httpDispatch(opts) {
10
- if (!opts.url && !opts.resolveUrl) {
11
- throw new Error("httpDispatch: pass exactly one of `url` or `resolveUrl`.");
12
- }
13
- if (opts.url && opts.resolveUrl) {
14
- throw new Error("httpDispatch: pass exactly one of `url` or `resolveUrl`, not both.");
15
- }
16
- const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1e3;
17
- const maxRetries = opts.retries ?? 2;
18
- const f = opts.fetchImpl ?? ((...args) => fetch(...args));
19
- return async (scenario, ctx) => {
20
- const url = opts.url ?? opts.resolveUrl({ scenario, placement: ctx.placement, cellId: ctx.cellId });
21
- const authValue = await resolveAuth(opts.auth);
22
- const body = {
23
- scenario,
24
- cellId: ctx.cellId,
25
- rep: ctx.rep,
26
- generation: ctx.generation,
27
- seed: ctx.seed,
28
- placement: ctx.placement,
29
- cycleId: ctx.cycleId
30
- };
31
- let lastError;
32
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
33
- const ourTimeout = AbortSignal.timeout(timeoutMs);
34
- const combinedSignal = AbortSignal.any([ctx.signal, ourTimeout]);
35
- try {
36
- const res = await f(url, {
37
- method: "POST",
38
- headers: {
39
- "Content-Type": "application/json",
40
- ...authValue ? {
41
- Authorization: authValue.startsWith("Bearer ") ? authValue : `Bearer ${authValue}`
42
- } : {},
43
- ...opts.headers
44
- },
45
- body: JSON.stringify(body),
46
- signal: combinedSignal
47
- });
48
- if (!res.ok) {
49
- const retryable = res.status >= 500 || res.status === 408 || res.status === 429;
50
- if (!retryable || attempt === maxRetries) {
51
- const text = await res.text().catch(() => "");
52
- throw new Error(`httpDispatch ${url} failed (${res.status}): ${text.slice(0, 500)}`);
53
- }
54
- await sleep(2 ** attempt * 200 + Math.random() * 200);
55
- continue;
56
- }
57
- const parsed = await res.json();
58
- return parsed.artifact;
59
- } catch (err) {
60
- if (ctx.signal.aborted) throw err;
61
- lastError = err;
62
- if (attempt === maxRetries) throw err;
63
- await sleep(2 ** attempt * 200 + Math.random() * 200);
64
- }
65
- }
66
- throw lastError ?? new Error("httpDispatch exhausted retries");
67
- };
68
- }
69
- function sleep(ms) {
70
- return new Promise((resolve) => {
71
- const t = setTimeout(resolve, ms);
72
- if (typeof t.unref === "function")
73
- t.unref();
74
- });
75
- }
76
- async function runDispatchServer(opts) {
77
- if (opts.auth === void 0) {
78
- throw new Error(
79
- "runDispatchServer: 'auth' is required (pass a bearer-token string, or `auth: false` explicitly for a closed-network test deployment)."
80
- );
81
- }
82
- const path = opts.path ?? "/dispatch";
83
- const maxBytes = opts.maxBodyBytes ?? 10 * 1024 * 1024;
84
- const expectedAuth = typeof opts.auth === "string" ? `Bearer ${opts.auth.replace(/^Bearer\s+/, "")}` : null;
85
- const { createServer } = await import("http");
86
- const server = createServer(async (req, res) => {
87
- const start = Date.now();
88
- let cellId = "unknown";
89
- let success = false;
90
- let errCaught;
91
- try {
92
- if (req.method !== "POST" || req.url?.split("?")[0] !== path) {
93
- res.statusCode = 404;
94
- res.end("not found");
95
- return;
96
- }
97
- if (expectedAuth) {
98
- const got = req.headers.authorization;
99
- if (got !== expectedAuth) {
100
- res.statusCode = 401;
101
- res.end("unauthorized");
102
- return;
103
- }
104
- }
105
- const chunks = [];
106
- let totalBytes = 0;
107
- const aborter = new AbortController();
108
- req.on("close", () => {
109
- if (!res.writableEnded) aborter.abort();
110
- });
111
- for await (const chunk of req) {
112
- const buf = chunk;
113
- totalBytes += buf.length;
114
- if (totalBytes > maxBytes) {
115
- res.statusCode = 413;
116
- res.end("payload too large");
117
- return;
118
- }
119
- chunks.push(buf);
120
- }
121
- const body = JSON.parse(
122
- Buffer.concat(chunks).toString("utf8")
123
- );
124
- cellId = body.cellId;
125
- const ctx = opts.contextFactory ? await opts.contextFactory(body, aborter.signal) : {
126
- cellId: body.cellId,
127
- rep: body.rep,
128
- generation: body.generation,
129
- seed: body.seed,
130
- signal: aborter.signal,
131
- placement: body.placement,
132
- cycleId: body.cycleId,
133
- trace: NOOP_TRACE,
134
- artifacts: NOOP_ARTIFACTS,
135
- cost: NOOP_COST
136
- };
137
- const artifact = await opts.dispatch(body.scenario, ctx);
138
- const responseBody = { artifact };
139
- res.statusCode = 200;
140
- res.setHeader("content-type", "application/json");
141
- res.end(JSON.stringify(responseBody));
142
- success = true;
143
- } catch (err) {
144
- errCaught = err;
145
- if (err?.name === "AbortError") {
146
- res.statusCode = 499;
147
- res.end("client aborted");
148
- return;
149
- }
150
- res.statusCode = 500;
151
- res.setHeader("content-type", "application/json");
152
- res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
153
- } finally {
154
- opts.onRequest?.({
155
- cellId,
156
- durationMs: Date.now() - start,
157
- success,
158
- error: errCaught
159
- });
160
- }
161
- });
162
- await new Promise((resolve, reject) => {
163
- server.once("error", reject);
164
- server.listen(opts.port, opts.host ?? "0.0.0.0", () => resolve());
165
- });
166
- const addr = server.address();
167
- const boundPort = typeof addr === "object" && addr ? addr.port : opts.port;
168
- return {
169
- port: boundPort,
170
- close: () => new Promise((resolve, reject) => {
171
- server.close((err) => err ? reject(err) : resolve());
172
- })
173
- };
174
- }
175
- var NOOP_TRACE = {
176
- span: () => ({
177
- end: () => {
178
- },
179
- setAttribute: () => {
180
- },
181
- setStatus: () => {
182
- },
183
- recordException: () => {
184
- },
185
- addEvent: () => {
186
- }
187
- })
188
- };
189
- var NOOP_ARTIFACTS = {
190
- write: async () => void 0,
191
- read: async () => void 0,
192
- list: async () => []
193
- };
194
- var NOOP_COST = {
195
- record: () => {
196
- },
197
- total: () => 0
198
- };
199
- export {
200
- httpDispatch,
201
- runDispatchServer
202
- };
203
- //# sourceMappingURL=http.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/adapters/http.ts"],"sourcesContent":["/**\n * # `@tangle-network/agent-eval/adapters/http` — distributed Dispatch over HTTP.\n *\n * Decouples coordinator and worker. The coordinator (running\n * `runImprovementLoop` or `runCampaign`) can live anywhere — your VPC, a dev\n * laptop, a cron VM. The workers (running the actual agent) can live anywhere else — different\n * regions, different clouds, different boxes — as long as they speak HTTP.\n *\n * Both sides:\n *\n * - **`httpDispatch({ url | resolveUrl, ... })`** — client. Returns a\n * `Dispatch` that POSTs `{ scenario, ctx }` to a worker URL and parses\n * the artifact back. AbortSignal-aware, retries on idempotent errors,\n * bounded timeout per call.\n * - **`runDispatchServer({ dispatch, port, ... })`** — server. Wraps your\n * local `Dispatch` as an HTTP endpoint. Handles auth, JSON parsing,\n * error mapping, and cancellation when the client aborts.\n *\n * # Topology examples\n *\n * **Single-worker:** coordinator on box A, worker on box B. Set\n * `httpDispatch({ url: 'https://box-b/dispatch' })`.\n *\n * **Multi-region:** N workers across regions. Use `httpDispatch({ resolveUrl })`\n * with a function that picks the URL per cell from `ctx.placement`. Combined\n * with `cellPlacement` on `RunCampaignOptions`, the substrate fans cells\n * across geographies in parallel.\n *\n * **Coordinator-as-a-service:** coordinator runs as a long-lived process or service\n * (holds optimization state across generations); workers are stateless\n * HTTP services that can scale horizontally per cell.\n */\n\nimport type { Dispatch, DispatchContext, Scenario } from '../contract'\n\n// ── Client ───────────────────────────────────────────────────────────\n\n// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TArtifact is unused\n// in this options interface but kept as a parameter so callers can write\n// `HttpDispatchOptions<MyScenario, MyArtifact>` symmetrically with\n// `Dispatch<MyScenario, MyArtifact>`. Marking it unused at the position\n// where it bites.\nexport interface HttpDispatchOptions<TScenario extends Scenario, _TArtifact> {\n /** Static endpoint URL. Mutually exclusive with `resolveUrl`. */\n url?: string\n /**\n * Dynamic per-cell URL resolver. Receives the scenario + the substrate\n * placement key (from `RunCampaignOptions.cellPlacement`) and returns the\n * worker URL to invoke. Mutually exclusive with `url`.\n */\n resolveUrl?: (input: { scenario: TScenario; placement?: string; cellId: string }) => string\n /** Bearer token or static auth string set as `Authorization`. */\n auth?: string | (() => string | Promise<string>)\n /** Extra headers merged into every request. */\n headers?: Record<string, string>\n /** Per-call timeout in ms. Default 5 minutes. */\n timeoutMs?: number\n /** How many idempotent retries on 5xx / network errors. Default 2. */\n retries?: number\n /** Optional fetch override (auth wrappers, custom agent, mocks). */\n fetchImpl?: typeof fetch\n}\n\nexport interface HttpDispatchRequestBody<TScenario extends Scenario> {\n scenario: TScenario\n cellId: string\n rep: number\n generation?: number\n seed: number\n placement?: string\n cycleId?: string\n}\n\nexport interface HttpDispatchResponseBody<TArtifact> {\n artifact: TArtifact\n}\n\nfunction resolveAuth(auth: HttpDispatchOptions<Scenario, unknown>['auth']): Promise<string | null> {\n if (!auth) return Promise.resolve(null)\n if (typeof auth === 'string') return Promise.resolve(auth)\n return Promise.resolve(auth())\n}\n\n/**\n * Wrap a remote HTTP endpoint as a `Dispatch`. The remote side should run\n * `runDispatchServer` (or any service that speaks the same wire shape).\n *\n * Cancellation: the substrate's per-cell `AbortSignal` is forwarded; the\n * server's `runDispatchServer` translates the resulting `AbortError` into\n * a 499 (client-closed) so the client doesn't retry.\n */\nexport function httpDispatch<TScenario extends Scenario, TArtifact>(\n opts: HttpDispatchOptions<TScenario, TArtifact>,\n): Dispatch<TScenario, TArtifact> {\n if (!opts.url && !opts.resolveUrl) {\n throw new Error('httpDispatch: pass exactly one of `url` or `resolveUrl`.')\n }\n if (opts.url && opts.resolveUrl) {\n throw new Error('httpDispatch: pass exactly one of `url` or `resolveUrl`, not both.')\n }\n const timeoutMs = opts.timeoutMs ?? 5 * 60 * 1000\n const maxRetries = opts.retries ?? 2\n const f: typeof fetch = opts.fetchImpl ?? ((...args) => fetch(...args))\n\n return async (scenario, ctx) => {\n const url =\n opts.url ?? opts.resolveUrl!({ scenario, placement: ctx.placement, cellId: ctx.cellId })\n const authValue = await resolveAuth(opts.auth)\n const body: HttpDispatchRequestBody<TScenario> = {\n scenario,\n cellId: ctx.cellId,\n rep: ctx.rep,\n generation: ctx.generation,\n seed: ctx.seed,\n placement: ctx.placement,\n cycleId: ctx.cycleId,\n }\n\n let lastError: unknown\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n // Compose the request signal: caller's signal OR our timeout.\n const ourTimeout = AbortSignal.timeout(timeoutMs)\n const combinedSignal = AbortSignal.any([ctx.signal, ourTimeout])\n try {\n const res = await f(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(authValue\n ? {\n Authorization: authValue.startsWith('Bearer ')\n ? authValue\n : `Bearer ${authValue}`,\n }\n : {}),\n ...opts.headers,\n },\n body: JSON.stringify(body),\n signal: combinedSignal,\n })\n if (!res.ok) {\n // 4xx is non-retryable (caller error, auth, bad scenario shape).\n // 5xx / 408 / 429 / 502 / 503 / 504 are retryable.\n const retryable = res.status >= 500 || res.status === 408 || res.status === 429\n if (!retryable || attempt === maxRetries) {\n const text = await res.text().catch(() => '')\n throw new Error(`httpDispatch ${url} failed (${res.status}): ${text.slice(0, 500)}`)\n }\n // exponential backoff with jitter\n await sleep(2 ** attempt * 200 + Math.random() * 200)\n continue\n }\n const parsed = (await res.json()) as HttpDispatchResponseBody<TArtifact>\n return parsed.artifact\n } catch (err) {\n // Caller-driven abort is terminal — never retry.\n if (ctx.signal.aborted) throw err\n lastError = err\n if (attempt === maxRetries) throw err\n await sleep(2 ** attempt * 200 + Math.random() * 200)\n }\n }\n throw lastError ?? new Error('httpDispatch exhausted retries')\n }\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => {\n const t = setTimeout(resolve, ms)\n // Don't keep node process alive purely for backoff sleeps.\n if (typeof (t as { unref?: () => void }).unref === 'function')\n (t as { unref: () => void }).unref()\n })\n}\n\n// ── Server ───────────────────────────────────────────────────────────\n\nexport interface RunDispatchServerOptions<TScenario extends Scenario, TArtifact> {\n /** The Dispatch this server exposes — what runs when a request lands. */\n dispatch: Dispatch<TScenario, TArtifact>\n /** TCP port to bind. */\n port: number\n /** Optional bind host; defaults to 0.0.0.0. */\n host?: string\n /** Required for any non-test deployment: the bearer token clients must\n * send. The substrate refuses to start without auth unless `auth: false`\n * is set explicitly (intended ONLY for closed-network/internal testing). */\n auth: string | false\n /** Path the server listens on. Default `/dispatch`. */\n path?: string\n /**\n * Per-request handler that wraps `dispatch` with whatever context the\n * worker side needs to construct a `DispatchContext` — typically the\n * trace writer, artifact writer, and cost meter. The substrate provides\n * synthetic-but-typed defaults if not supplied; production deployments\n * should wire real ones (e.g. ship traces to your OTel collector).\n */\n contextFactory?: (\n req: HttpDispatchRequestBody<TScenario>,\n signal: AbortSignal,\n ) => Promise<DispatchContext>\n /** Optional max payload size for the request body (bytes). Default 10 MB. */\n maxBodyBytes?: number\n /** Hook for observability — called on every successful or failed turn. */\n onRequest?: (event: {\n cellId: string\n durationMs: number\n success: boolean\n error?: unknown\n }) => void\n}\n\nexport interface DispatchServerHandle {\n /** The actual bound port (useful when `port: 0` requests an ephemeral port). */\n port: number\n /** Stop accepting new connections and drain existing ones. */\n close: () => Promise<void>\n}\n\n/**\n * Start an HTTP server exposing a local `Dispatch` over the wire. Pair with\n * `httpDispatch` on the driver side.\n *\n * Wire shape:\n *\n * POST /dispatch\n * Authorization: Bearer <token>\n * Body: HttpDispatchRequestBody\n * 200 OK: HttpDispatchResponseBody\n * 401: missing/invalid auth\n * 408: per-request timeout exceeded\n * 499: client aborted before completion\n * 500: dispatch threw\n *\n * The server is `node:http`-based to keep the runtime dependency surface\n * minimal — works in plain Node, sandbox, or any container.\n */\nexport async function runDispatchServer<TScenario extends Scenario, TArtifact>(\n opts: RunDispatchServerOptions<TScenario, TArtifact>,\n): Promise<DispatchServerHandle> {\n if (opts.auth === undefined) {\n throw new Error(\n \"runDispatchServer: 'auth' is required (pass a bearer-token string, or `auth: false` explicitly for a closed-network test deployment).\",\n )\n }\n const path = opts.path ?? '/dispatch'\n const maxBytes = opts.maxBodyBytes ?? 10 * 1024 * 1024\n const expectedAuth =\n typeof opts.auth === 'string' ? `Bearer ${opts.auth.replace(/^Bearer\\s+/, '')}` : null\n\n // Lazy-import node:http so the file is usable from non-Node bundlers\n // that import the client side only (e.g. an edge driver shipping\n // httpDispatch alone). Server side is opt-in by calling this function.\n const { createServer } = await import('node:http')\n\n const server = createServer(async (req, res) => {\n const start = Date.now()\n let cellId = 'unknown'\n let success = false\n let errCaught: unknown\n\n try {\n if (req.method !== 'POST' || req.url?.split('?')[0] !== path) {\n res.statusCode = 404\n res.end('not found')\n return\n }\n if (expectedAuth) {\n const got = req.headers.authorization\n if (got !== expectedAuth) {\n res.statusCode = 401\n res.end('unauthorized')\n return\n }\n }\n\n // Read body up to maxBytes\n const chunks: Buffer[] = []\n let totalBytes = 0\n const aborter = new AbortController()\n req.on('close', () => {\n if (!res.writableEnded) aborter.abort()\n })\n\n for await (const chunk of req) {\n const buf = chunk as Buffer\n totalBytes += buf.length\n if (totalBytes > maxBytes) {\n res.statusCode = 413\n res.end('payload too large')\n return\n }\n chunks.push(buf)\n }\n\n const body = JSON.parse(\n Buffer.concat(chunks).toString('utf8'),\n ) as HttpDispatchRequestBody<TScenario>\n cellId = body.cellId\n\n const ctx: DispatchContext = opts.contextFactory\n ? await opts.contextFactory(body, aborter.signal)\n : {\n cellId: body.cellId,\n rep: body.rep,\n generation: body.generation,\n seed: body.seed,\n signal: aborter.signal,\n placement: body.placement,\n cycleId: body.cycleId,\n trace: NOOP_TRACE,\n artifacts: NOOP_ARTIFACTS,\n cost: NOOP_COST,\n }\n\n const artifact = await opts.dispatch(body.scenario, ctx)\n const responseBody: HttpDispatchResponseBody<TArtifact> = { artifact }\n\n res.statusCode = 200\n res.setHeader('content-type', 'application/json')\n res.end(JSON.stringify(responseBody))\n success = true\n } catch (err) {\n errCaught = err\n // Client-cancelled — they don't care about the result.\n if ((err as Error)?.name === 'AbortError') {\n res.statusCode = 499\n res.end('client aborted')\n return\n }\n res.statusCode = 500\n res.setHeader('content-type', 'application/json')\n res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }))\n } finally {\n opts.onRequest?.({\n cellId,\n durationMs: Date.now() - start,\n success,\n error: errCaught,\n })\n }\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(opts.port, opts.host ?? '0.0.0.0', () => resolve())\n })\n\n const addr = server.address()\n const boundPort = typeof addr === 'object' && addr ? addr.port : opts.port\n\n return {\n port: boundPort,\n close: () =>\n new Promise<void>((resolve, reject) => {\n server.close((err) => (err ? reject(err) : resolve()))\n }),\n }\n}\n\n// ── No-op default ctx machinery (worker can replace via contextFactory) ──\n\nconst NOOP_TRACE = {\n span: () => ({\n end: () => {},\n setAttribute: () => {},\n setStatus: () => {},\n recordException: () => {},\n addEvent: () => {},\n }),\n} as unknown as DispatchContext['trace']\n\nconst NOOP_ARTIFACTS = {\n write: async () => undefined,\n read: async () => undefined,\n list: async () => [],\n} as unknown as DispatchContext['artifacts']\n\nconst NOOP_COST = {\n record: () => {},\n total: () => 0,\n} as unknown as DispatchContext['cost']\n"],"mappings":"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","names":[]}
@@ -1,95 +0,0 @@
1
- import { S as Scenario, J as JudgeScore, D as DispatchFn, a as JudgeConfig } from '../types-BdIv5dvA.js';
2
- import '../run-record-I-Z3JNvO.js';
3
- import '@tangle-network/agent-interface';
4
- import '../errors-oeQrLqXC.js';
5
- import '../schema-m0gsnbt3.js';
6
-
7
- /**
8
- * # `@tangle-network/agent-eval/adapters/langchain` — wrap any LangChain
9
- * Runnable as a `Dispatch` (or `JudgeConfig`).
10
- *
11
- * **Why structural, not pinned**: we don't depend on `@langchain/core` at
12
- * install time. The adapter accepts anything with the canonical LangChain
13
- * Runnable shape (`invoke(input, config?)`), so it works with their
14
- * `Runnable`, `RunnableSequence`, `RunnableMap`, `RunnablePassthrough`,
15
- * and any custom Runnable-shaped object. No version pin, no peer dep,
16
- * no bundle-bloat risk.
17
- *
18
- * **Why this exists**: the most-asked question from foreign agent
19
- * builders is "I'm already on LangChain — how do I plug in?". The answer
20
- * is one function. Wrap your existing Runnable, pass the Dispatch into
21
- * `runEval` / `runImprovementLoop`, ship.
22
- */
23
-
24
- interface RunnableLike<TInput, TOutput> {
25
- invoke(input: TInput, config?: {
26
- signal?: AbortSignal;
27
- [key: string]: unknown;
28
- }): Promise<TOutput>;
29
- }
30
- interface LangchainDispatchOptions<TScenario extends Scenario, TArtifact> {
31
- /** The Runnable (or RunnableSequence, or anything `.invoke`able). */
32
- runnable: RunnableLike<TScenario, TArtifact>;
33
- /**
34
- * Optional config merged into every `invoke` call — tags, metadata,
35
- * callbacks, runName. The substrate's per-cell `AbortSignal` is
36
- * always merged in last (and so wins).
37
- */
38
- config?: Record<string, unknown>;
39
- }
40
- /**
41
- * Wrap a LangChain Runnable as a `Dispatch`. The Runnable's input must
42
- * accept the scenario (typically you'll shape it via
43
- * `RunnableMap`/`RunnableLambda` upstream); its output is the artifact
44
- * the engine + judges see.
45
- *
46
- * @example
47
- * const chain = prompt.pipe(model).pipe(parser)
48
- * const dispatch = langchainDispatch({ runnable: chain })
49
- * await runEval({ scenarios, dispatch, judges: [...], storage, runDir })
50
- */
51
- declare function langchainDispatch<TScenario extends Scenario, TArtifact>(opts: LangchainDispatchOptions<TScenario, TArtifact>): DispatchFn<TScenario, TArtifact>;
52
- interface LangchainJudgeOptions<TArtifact, TScenario extends Scenario> {
53
- /** Judge name; appears in `CampaignResult.aggregates.byJudge`. */
54
- name: string;
55
- /**
56
- * Dimensions the judge scores. Used both for the judge's own prompt
57
- * (if it reads them) and for the aggregator's `byJudge` rollup.
58
- */
59
- dimensions: {
60
- key: string;
61
- description: string;
62
- }[];
63
- /**
64
- * A Runnable that takes `{ artifact, scenario }` and returns a
65
- * partial `JudgeScore` — the dimensions map at minimum. `composite`
66
- * is computed by averaging `dimensions` when the Runnable doesn't
67
- * provide it; `notes` defaults to an empty string.
68
- */
69
- runnable: RunnableLike<{
70
- artifact: TArtifact;
71
- scenario: TScenario;
72
- }, Partial<JudgeScore>>;
73
- appliesTo?: (scenario: TScenario) => boolean;
74
- }
75
- /**
76
- * Wrap a LangChain Runnable as a `JudgeConfig`. The Runnable can be any
77
- * structured-output chain (e.g. `prompt.pipe(model).pipe(StructuredOutputParser)`)
78
- * that returns a `Partial<JudgeScore>`.
79
- *
80
- * The substrate's invariant — throw on judge failure, never silently
81
- * fold errors into a zero — is preserved: any error from the Runnable
82
- * propagates and the substrate records a failed cell.
83
- *
84
- * @example
85
- * const scorePrompt = ChatPromptTemplate.fromTemplate(`...`)
86
- * const judgeChain = scorePrompt.pipe(judgeModel).pipe(jsonParser)
87
- * const judge = langchainJudge({
88
- * name: 'marketing-quality',
89
- * dimensions: [{ key: 'hook_strength', description: '...' }, ...],
90
- * runnable: judgeChain,
91
- * })
92
- */
93
- declare function langchainJudge<TArtifact, TScenario extends Scenario>(opts: LangchainJudgeOptions<TArtifact, TScenario>): JudgeConfig<TArtifact, TScenario>;
94
-
95
- export { type LangchainDispatchOptions, type LangchainJudgeOptions, type RunnableLike, langchainDispatch, langchainJudge };
@@ -1,34 +0,0 @@
1
- import "../chunk-PZ5AY32C.js";
2
-
3
- // src/adapters/langchain.ts
4
- function langchainDispatch(opts) {
5
- return async (scenario, ctx) => {
6
- return opts.runnable.invoke(scenario, {
7
- ...opts.config,
8
- signal: ctx.signal
9
- });
10
- };
11
- }
12
- function langchainJudge(opts) {
13
- return {
14
- name: opts.name,
15
- dimensions: opts.dimensions,
16
- appliesTo: opts.appliesTo,
17
- async score({ artifact, scenario, signal }) {
18
- const result = await opts.runnable.invoke({ artifact, scenario }, { signal });
19
- const dims = result.dimensions ?? {};
20
- const dimValues = Object.values(dims);
21
- const composite = result.composite ?? (dimValues.length > 0 ? dimValues.reduce((a, b) => a + b, 0) / dimValues.length : 0);
22
- return {
23
- dimensions: dims,
24
- composite,
25
- notes: result.notes ?? ""
26
- };
27
- }
28
- };
29
- }
30
- export {
31
- langchainDispatch,
32
- langchainJudge
33
- };
34
- //# sourceMappingURL=langchain.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/adapters/langchain.ts"],"sourcesContent":["/**\n * # `@tangle-network/agent-eval/adapters/langchain` — wrap any LangChain\n * Runnable as a `Dispatch` (or `JudgeConfig`).\n *\n * **Why structural, not pinned**: we don't depend on `@langchain/core` at\n * install time. The adapter accepts anything with the canonical LangChain\n * Runnable shape (`invoke(input, config?)`), so it works with their\n * `Runnable`, `RunnableSequence`, `RunnableMap`, `RunnablePassthrough`,\n * and any custom Runnable-shaped object. No version pin, no peer dep,\n * no bundle-bloat risk.\n *\n * **Why this exists**: the most-asked question from foreign agent\n * builders is \"I'm already on LangChain — how do I plug in?\". The answer\n * is one function. Wrap your existing Runnable, pass the Dispatch into\n * `runEval` / `runImprovementLoop`, ship.\n */\n\nimport type { Dispatch, JudgeConfig, JudgeScore, Scenario } from '../contract'\n\n// ── Minimal structural type ──────────────────────────────────────────\n//\n// Whatever has `invoke(input, config?)` qualifies. We accept any\n// config shape (LangChain's RunnableConfig has many optional fields)\n// — the only thing we need is the AbortSignal seam, which LangChain's\n// RunnableConfig already supports as `signal?: AbortSignal`.\n\nexport interface RunnableLike<TInput, TOutput> {\n invoke(input: TInput, config?: { signal?: AbortSignal; [key: string]: unknown }): Promise<TOutput>\n}\n\n// ── Dispatch wrapper ────────────────────────────────────────────────\n\nexport interface LangchainDispatchOptions<TScenario extends Scenario, TArtifact> {\n /** The Runnable (or RunnableSequence, or anything `.invoke`able). */\n runnable: RunnableLike<TScenario, TArtifact>\n /**\n * Optional config merged into every `invoke` call — tags, metadata,\n * callbacks, runName. The substrate's per-cell `AbortSignal` is\n * always merged in last (and so wins).\n */\n config?: Record<string, unknown>\n}\n\n/**\n * Wrap a LangChain Runnable as a `Dispatch`. The Runnable's input must\n * accept the scenario (typically you'll shape it via\n * `RunnableMap`/`RunnableLambda` upstream); its output is the artifact\n * the engine + judges see.\n *\n * @example\n * const chain = prompt.pipe(model).pipe(parser)\n * const dispatch = langchainDispatch({ runnable: chain })\n * await runEval({ scenarios, dispatch, judges: [...], storage, runDir })\n */\nexport function langchainDispatch<TScenario extends Scenario, TArtifact>(\n opts: LangchainDispatchOptions<TScenario, TArtifact>,\n): Dispatch<TScenario, TArtifact> {\n return async (scenario, ctx) => {\n return opts.runnable.invoke(scenario, {\n ...opts.config,\n signal: ctx.signal,\n })\n }\n}\n\n// ── Judge wrapper ───────────────────────────────────────────────────\n\nexport interface LangchainJudgeOptions<TArtifact, TScenario extends Scenario> {\n /** Judge name; appears in `CampaignResult.aggregates.byJudge`. */\n name: string\n /**\n * Dimensions the judge scores. Used both for the judge's own prompt\n * (if it reads them) and for the aggregator's `byJudge` rollup.\n */\n dimensions: { key: string; description: string }[]\n /**\n * A Runnable that takes `{ artifact, scenario }` and returns a\n * partial `JudgeScore` — the dimensions map at minimum. `composite`\n * is computed by averaging `dimensions` when the Runnable doesn't\n * provide it; `notes` defaults to an empty string.\n */\n runnable: RunnableLike<{ artifact: TArtifact; scenario: TScenario }, Partial<JudgeScore>>\n appliesTo?: (scenario: TScenario) => boolean\n}\n\n/**\n * Wrap a LangChain Runnable as a `JudgeConfig`. The Runnable can be any\n * structured-output chain (e.g. `prompt.pipe(model).pipe(StructuredOutputParser)`)\n * that returns a `Partial<JudgeScore>`.\n *\n * The substrate's invariant — throw on judge failure, never silently\n * fold errors into a zero — is preserved: any error from the Runnable\n * propagates and the substrate records a failed cell.\n *\n * @example\n * const scorePrompt = ChatPromptTemplate.fromTemplate(`...`)\n * const judgeChain = scorePrompt.pipe(judgeModel).pipe(jsonParser)\n * const judge = langchainJudge({\n * name: 'marketing-quality',\n * dimensions: [{ key: 'hook_strength', description: '...' }, ...],\n * runnable: judgeChain,\n * })\n */\nexport function langchainJudge<TArtifact, TScenario extends Scenario>(\n opts: LangchainJudgeOptions<TArtifact, TScenario>,\n): JudgeConfig<TArtifact, TScenario> {\n return {\n name: opts.name,\n dimensions: opts.dimensions,\n appliesTo: opts.appliesTo,\n async score({ artifact, scenario, signal }) {\n const result = await opts.runnable.invoke({ artifact, scenario }, { signal })\n const dims = (result.dimensions ?? {}) as Record<string, number>\n const dimValues = Object.values(dims)\n const composite =\n result.composite ??\n (dimValues.length > 0 ? dimValues.reduce((a, b) => a + b, 0) / dimValues.length : 0)\n return {\n dimensions: dims,\n composite,\n notes: result.notes ?? '',\n }\n },\n }\n}\n"],"mappings":";;;AAsDO,SAAS,kBACd,MACgC;AAChC,SAAO,OAAO,UAAU,QAAQ;AAC9B,WAAO,KAAK,SAAS,OAAO,UAAU;AAAA,MACpC,GAAG,KAAK;AAAA,MACR,QAAQ,IAAI;AAAA,IACd,CAAC;AAAA,EACH;AACF;AAwCO,SAAS,eACd,MACmC;AACnC,SAAO;AAAA,IACL,MAAM,KAAK;AAAA,IACX,YAAY,KAAK;AAAA,IACjB,WAAW,KAAK;AAAA,IAChB,MAAM,MAAM,EAAE,UAAU,UAAU,OAAO,GAAG;AAC1C,YAAM,SAAS,MAAM,KAAK,SAAS,OAAO,EAAE,UAAU,SAAS,GAAG,EAAE,OAAO,CAAC;AAC5E,YAAM,OAAQ,OAAO,cAAc,CAAC;AACpC,YAAM,YAAY,OAAO,OAAO,IAAI;AACpC,YAAM,YACJ,OAAO,cACN,UAAU,SAAS,IAAI,UAAU,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,UAAU,SAAS;AACpF,aAAO;AAAA,QACL,YAAY;AAAA,QACZ;AAAA,QACA,OAAO,OAAO,SAAS;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -1,112 +0,0 @@
1
- import { TraceSpanEvent, HostedClient } from '../hosted/index.js';
2
- import '../types-BdIv5dvA.js';
3
- import '../run-record-I-Z3JNvO.js';
4
- import '@tangle-network/agent-interface';
5
- import '../errors-oeQrLqXC.js';
6
- import '../schema-m0gsnbt3.js';
7
- import '../insight-report-B4xrdwEK.js';
8
- import '../summary-report-QMZVe3P-.js';
9
- import '../failure-cluster-DH9Flgcf.js';
10
- import '../store-BcFXE6LG.js';
11
- import '../judge-calibration-7C-IDmKr.js';
12
-
13
- /**
14
- * # `@tangle-network/agent-eval/adapters/otel` — OTel→hosted bridge.
15
- *
16
- * Forwards OpenTelemetry-shaped spans into the hosted-tier ingest endpoint
17
- * via `createHostedClient`. Works with anything that emits OTel
18
- * `ReadableSpan`s — `@opentelemetry/sdk-trace-base` directly, OpenLLMetry,
19
- * Phoenix's OTel exporter, TraceAI from future-agi, any OTel collector
20
- * pipeline.
21
- *
22
- * **Pattern:**
23
- *
24
- * ```ts
25
- * import { createHostedClient } from '@tangle-network/agent-eval/hosted'
26
- * import { createOtelBridge } from '@tangle-network/agent-eval/adapters/otel'
27
- *
28
- * const client = createHostedClient({ endpoint, apiKey, tenantId })
29
- * const bridge = createOtelBridge({ client, defaultRunId: substrateRunId })
30
- *
31
- * // Wherever your OTel SpanProcessor hands you a finished span:
32
- * processor.onEnd = (span) => { void bridge.ingest([span]) }
33
- * // …or in a SpanProcessor.onShutdown / batch flush:
34
- * await bridge.ingest(batchedSpans)
35
- * ```
36
- *
37
- * No `@opentelemetry/*` dependency is declared here — the adapter accepts
38
- * a structurally-typed `OtelLikeSpan`. This keeps the substrate dep graph
39
- * lean while remaining compatible with any OTel SDK that produces
40
- * `ReadableSpan`-shaped instances. If a consumer's span shape exposes
41
- * `parentSpanId` as a top-level field rather than via `parentSpanContext()`,
42
- * the adapter accepts both forms.
43
- */
44
-
45
- /**
46
- * `[seconds, nanoseconds]` — the OTel SDK's `HrTime` shape. Spans emitted
47
- * by the OTel SDK carry timestamps in this representation; we convert to
48
- * a single unix-nano number for the wire format.
49
- */
50
- type HrTime = [number, number];
51
- /** Standard OTel `SpanStatusCode` numeric values: 0 = UNSET, 1 = OK, 2 = ERROR. */
52
- declare const OTEL_STATUS_UNSET = 0;
53
- declare const OTEL_STATUS_OK = 1;
54
- declare const OTEL_STATUS_ERROR = 2;
55
- type OtelAttributeValue = string | number | boolean | null | undefined | Array<string> | Array<number> | Array<boolean>;
56
- /**
57
- * Structural surface compatible with `@opentelemetry/sdk-trace-base`'s
58
- * `ReadableSpan`. Consumers pass instances they get from their OTel SDK.
59
- */
60
- interface OtelLikeSpan {
61
- spanContext: () => {
62
- traceId: string;
63
- spanId: string;
64
- traceFlags?: number;
65
- };
66
- /** Set on the span itself by some SDKs (OTLP-shape). Other SDKs expose
67
- * the parent via `parentSpanContext()` instead — the adapter checks both. */
68
- parentSpanId?: string;
69
- parentSpanContext?: () => {
70
- spanId: string;
71
- } | undefined;
72
- name: string;
73
- startTime: HrTime;
74
- endTime: HrTime;
75
- attributes: Record<string, OtelAttributeValue>;
76
- events?: Array<{
77
- name: string;
78
- time: HrTime;
79
- attributes?: Record<string, OtelAttributeValue>;
80
- }>;
81
- status?: {
82
- code: number;
83
- message?: string;
84
- };
85
- }
86
- /** `[seconds, nanoseconds]` → unix-nano number. */
87
- declare function hrTimeToUnixNano(hr: HrTime): number;
88
- interface OtelBridgeOptions {
89
- /** Hosted client to forward spans to. */
90
- client: HostedClient;
91
- /** When set, spans missing a `tangle.runId` attribute receive this value
92
- * on the way out. Useful when the OTel emitter doesn't know which
93
- * substrate run it's serving. */
94
- defaultRunId?: string;
95
- /** Max spans per ingest call. Default 200. The hosted ingest endpoint
96
- * caps at 5000 per call; we batch smaller by default to keep individual
97
- * retries cheap. */
98
- batchSize?: number;
99
- /** Called when a batch fails to ingest. Defaults to a console.warn. Hook
100
- * this when you need backpressure or to spill to a fallback. */
101
- onError?: (err: unknown, batch: TraceSpanEvent[]) => void | Promise<void>;
102
- }
103
- interface OtelBridge {
104
- /** Convert + ingest a batch of OTel-shape spans. */
105
- ingest(spans: OtelLikeSpan[]): Promise<void>;
106
- /** Convert one OTel span to the wire-format event. Useful for tests or
107
- * custom batching pipelines. */
108
- spanToEvent(span: OtelLikeSpan): TraceSpanEvent;
109
- }
110
- declare function createOtelBridge(opts: OtelBridgeOptions): OtelBridge;
111
-
112
- export { type HrTime, OTEL_STATUS_ERROR, OTEL_STATUS_OK, OTEL_STATUS_UNSET, type OtelAttributeValue, type OtelBridge, type OtelBridgeOptions, type OtelLikeSpan, createOtelBridge, hrTimeToUnixNano };
@@ -1,110 +0,0 @@
1
- import "../chunk-PZ5AY32C.js";
2
-
3
- // src/adapters/otel.ts
4
- var OTEL_STATUS_UNSET = 0;
5
- var OTEL_STATUS_OK = 1;
6
- var OTEL_STATUS_ERROR = 2;
7
- function hrTimeToUnixNano(hr) {
8
- const [seconds, nanos] = hr;
9
- return seconds * 1e9 + nanos;
10
- }
11
- function statusCodeName(code) {
12
- if (code === OTEL_STATUS_OK) return "OK";
13
- if (code === OTEL_STATUS_ERROR) return "ERROR";
14
- return "UNSET";
15
- }
16
- function cleanAttributes(attrs) {
17
- const out = {};
18
- if (!attrs) return out;
19
- for (const [k, v] of Object.entries(attrs)) {
20
- if (v === null || v === void 0) continue;
21
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
22
- out[k] = v;
23
- continue;
24
- }
25
- if (Array.isArray(v)) {
26
- out[k] = JSON.stringify(v);
27
- }
28
- }
29
- return out;
30
- }
31
- function readPivotString(attrs, key) {
32
- const v = attrs[key];
33
- return typeof v === "string" ? v : void 0;
34
- }
35
- function readPivotNumber(attrs, key) {
36
- const v = attrs[key];
37
- return typeof v === "number" ? v : void 0;
38
- }
39
- function resolveParentSpanId(span) {
40
- if (span.parentSpanId) return span.parentSpanId;
41
- const ctx = span.parentSpanContext?.();
42
- return ctx?.spanId;
43
- }
44
- function createOtelBridge(opts) {
45
- const batchSize = opts.batchSize ?? 200;
46
- const onError = opts.onError ?? ((err) => {
47
- console.warn("[otel-bridge] ingest batch failed:", err);
48
- });
49
- function convert(span) {
50
- const ctx = span.spanContext();
51
- const attributes = cleanAttributes(span.attributes);
52
- const runId = readPivotString(attributes, "tangle.runId") ?? opts.defaultRunId;
53
- const scenarioId = readPivotString(attributes, "tangle.scenarioId");
54
- const cellId = readPivotString(attributes, "tangle.cellId");
55
- const generation = readPivotNumber(attributes, "tangle.generation");
56
- if (runId && !attributes["tangle.runId"]) {
57
- attributes["tangle.runId"] = runId;
58
- }
59
- const event = {
60
- traceId: ctx.traceId,
61
- spanId: ctx.spanId,
62
- name: span.name,
63
- startTimeUnixNano: hrTimeToUnixNano(span.startTime),
64
- endTimeUnixNano: hrTimeToUnixNano(span.endTime),
65
- attributes
66
- };
67
- const parentSpanId = resolveParentSpanId(span);
68
- if (parentSpanId) event.parentSpanId = parentSpanId;
69
- if (span.events && span.events.length > 0) {
70
- event.events = span.events.map((e) => {
71
- const eventAttrs = cleanAttributes(e.attributes);
72
- const node = {
73
- timeUnixNano: hrTimeToUnixNano(e.time),
74
- name: e.name
75
- };
76
- if (Object.keys(eventAttrs).length > 0) node.attributes = eventAttrs;
77
- return node;
78
- });
79
- }
80
- if (span.status) {
81
- event.status = { code: statusCodeName(span.status.code), message: span.status.message };
82
- }
83
- if (runId) event["tangle.runId"] = runId;
84
- if (scenarioId) event["tangle.scenarioId"] = scenarioId;
85
- if (cellId) event["tangle.cellId"] = cellId;
86
- if (generation !== void 0) event["tangle.generation"] = generation;
87
- return event;
88
- }
89
- async function ingest(spans) {
90
- if (spans.length === 0) return;
91
- const events = spans.map(convert);
92
- for (let i = 0; i < events.length; i += batchSize) {
93
- const batch = events.slice(i, i + batchSize);
94
- try {
95
- await opts.client.ingestTraces(batch);
96
- } catch (err) {
97
- await onError(err, batch);
98
- }
99
- }
100
- }
101
- return { ingest, spanToEvent: convert };
102
- }
103
- export {
104
- OTEL_STATUS_ERROR,
105
- OTEL_STATUS_OK,
106
- OTEL_STATUS_UNSET,
107
- createOtelBridge,
108
- hrTimeToUnixNano
109
- };
110
- //# sourceMappingURL=otel.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/adapters/otel.ts"],"sourcesContent":["/**\n * # `@tangle-network/agent-eval/adapters/otel` — OTel→hosted bridge.\n *\n * Forwards OpenTelemetry-shaped spans into the hosted-tier ingest endpoint\n * via `createHostedClient`. Works with anything that emits OTel\n * `ReadableSpan`s — `@opentelemetry/sdk-trace-base` directly, OpenLLMetry,\n * Phoenix's OTel exporter, TraceAI from future-agi, any OTel collector\n * pipeline.\n *\n * **Pattern:**\n *\n * ```ts\n * import { createHostedClient } from '@tangle-network/agent-eval/hosted'\n * import { createOtelBridge } from '@tangle-network/agent-eval/adapters/otel'\n *\n * const client = createHostedClient({ endpoint, apiKey, tenantId })\n * const bridge = createOtelBridge({ client, defaultRunId: substrateRunId })\n *\n * // Wherever your OTel SpanProcessor hands you a finished span:\n * processor.onEnd = (span) => { void bridge.ingest([span]) }\n * // …or in a SpanProcessor.onShutdown / batch flush:\n * await bridge.ingest(batchedSpans)\n * ```\n *\n * No `@opentelemetry/*` dependency is declared here — the adapter accepts\n * a structurally-typed `OtelLikeSpan`. This keeps the substrate dep graph\n * lean while remaining compatible with any OTel SDK that produces\n * `ReadableSpan`-shaped instances. If a consumer's span shape exposes\n * `parentSpanId` as a top-level field rather than via `parentSpanContext()`,\n * the adapter accepts both forms.\n */\n\nimport type { HostedClient } from '../hosted/client'\nimport type { TraceSpanEvent } from '../hosted/types'\n\n// ── OTel-compatible structural types ─────────────────────────────────\n\n/**\n * `[seconds, nanoseconds]` — the OTel SDK's `HrTime` shape. Spans emitted\n * by the OTel SDK carry timestamps in this representation; we convert to\n * a single unix-nano number for the wire format.\n */\nexport type HrTime = [number, number]\n\n/** Standard OTel `SpanStatusCode` numeric values: 0 = UNSET, 1 = OK, 2 = ERROR. */\nexport const OTEL_STATUS_UNSET = 0\nexport const OTEL_STATUS_OK = 1\nexport const OTEL_STATUS_ERROR = 2\n\nexport type OtelAttributeValue =\n | string\n | number\n | boolean\n | null\n | undefined\n | Array<string>\n | Array<number>\n | Array<boolean>\n\n/**\n * Structural surface compatible with `@opentelemetry/sdk-trace-base`'s\n * `ReadableSpan`. Consumers pass instances they get from their OTel SDK.\n */\nexport interface OtelLikeSpan {\n spanContext: () => { traceId: string; spanId: string; traceFlags?: number }\n /** Set on the span itself by some SDKs (OTLP-shape). Other SDKs expose\n * the parent via `parentSpanContext()` instead — the adapter checks both. */\n parentSpanId?: string\n parentSpanContext?: () => { spanId: string } | undefined\n name: string\n startTime: HrTime\n endTime: HrTime\n attributes: Record<string, OtelAttributeValue>\n events?: Array<{\n name: string\n time: HrTime\n attributes?: Record<string, OtelAttributeValue>\n }>\n status?: { code: number; message?: string }\n}\n\n// ── Conversion ───────────────────────────────────────────────────────\n\n/** `[seconds, nanoseconds]` → unix-nano number. */\nexport function hrTimeToUnixNano(hr: HrTime): number {\n const [seconds, nanos] = hr\n return seconds * 1_000_000_000 + nanos\n}\n\nfunction statusCodeName(code: number | undefined): 'OK' | 'ERROR' | 'UNSET' {\n if (code === OTEL_STATUS_OK) return 'OK'\n if (code === OTEL_STATUS_ERROR) return 'ERROR'\n return 'UNSET'\n}\n\n/**\n * Normalise OTel attributes to the scalar shape the wire format accepts.\n *\n * - null / undefined → dropped (no representation in the wire format)\n * - string / number / boolean → passed through\n * - Array<scalar> → JSON-stringified (the wire format is scalar-only;\n * dropping arrays would silently lose information, so we serialise\n * them deterministically)\n *\n * OTel SDK / TraceAI emit array attributes routinely (e.g. `tool.names`,\n * `http.request.header.set-cookie`). Stringification keeps the data flowing\n * through to the dashboard; consumers parse with `JSON.parse` if they need\n * the structured form back.\n */\nfunction cleanAttributes(\n attrs: Record<string, OtelAttributeValue> | undefined,\n): Record<string, string | number | boolean> {\n const out: Record<string, string | number | boolean> = {}\n if (!attrs) return out\n for (const [k, v] of Object.entries(attrs)) {\n if (v === null || v === undefined) continue\n if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {\n out[k] = v\n continue\n }\n if (Array.isArray(v)) {\n out[k] = JSON.stringify(v)\n }\n }\n return out\n}\n\nfunction readPivotString(\n attrs: Record<string, OtelAttributeValue>,\n key: string,\n): string | undefined {\n const v = attrs[key]\n return typeof v === 'string' ? v : undefined\n}\n\nfunction readPivotNumber(\n attrs: Record<string, OtelAttributeValue>,\n key: string,\n): number | undefined {\n const v = attrs[key]\n return typeof v === 'number' ? v : undefined\n}\n\nfunction resolveParentSpanId(span: OtelLikeSpan): string | undefined {\n if (span.parentSpanId) return span.parentSpanId\n const ctx = span.parentSpanContext?.()\n return ctx?.spanId\n}\n\n// ── Bridge ───────────────────────────────────────────────────────────\n\nexport interface OtelBridgeOptions {\n /** Hosted client to forward spans to. */\n client: HostedClient\n /** When set, spans missing a `tangle.runId` attribute receive this value\n * on the way out. Useful when the OTel emitter doesn't know which\n * substrate run it's serving. */\n defaultRunId?: string\n /** Max spans per ingest call. Default 200. The hosted ingest endpoint\n * caps at 5000 per call; we batch smaller by default to keep individual\n * retries cheap. */\n batchSize?: number\n /** Called when a batch fails to ingest. Defaults to a console.warn. Hook\n * this when you need backpressure or to spill to a fallback. */\n onError?: (err: unknown, batch: TraceSpanEvent[]) => void | Promise<void>\n}\n\nexport interface OtelBridge {\n /** Convert + ingest a batch of OTel-shape spans. */\n ingest(spans: OtelLikeSpan[]): Promise<void>\n /** Convert one OTel span to the wire-format event. Useful for tests or\n * custom batching pipelines. */\n spanToEvent(span: OtelLikeSpan): TraceSpanEvent\n}\n\nexport function createOtelBridge(opts: OtelBridgeOptions): OtelBridge {\n const batchSize = opts.batchSize ?? 200\n const onError =\n opts.onError ??\n ((err) => {\n console.warn('[otel-bridge] ingest batch failed:', err)\n })\n\n function convert(span: OtelLikeSpan): TraceSpanEvent {\n const ctx = span.spanContext()\n const attributes = cleanAttributes(span.attributes)\n // Pull pivot attributes off the cleaned attribute map so they round-trip\n // through the wire format's first-class fields. They REMAIN in\n // `attributes` as well so downstream OTel viewers see the same values.\n const runId = readPivotString(attributes, 'tangle.runId') ?? opts.defaultRunId\n const scenarioId = readPivotString(attributes, 'tangle.scenarioId')\n const cellId = readPivotString(attributes, 'tangle.cellId')\n const generation = readPivotNumber(attributes, 'tangle.generation')\n\n if (runId && !attributes['tangle.runId']) {\n attributes['tangle.runId'] = runId\n }\n\n const event: TraceSpanEvent = {\n traceId: ctx.traceId,\n spanId: ctx.spanId,\n name: span.name,\n startTimeUnixNano: hrTimeToUnixNano(span.startTime),\n endTimeUnixNano: hrTimeToUnixNano(span.endTime),\n attributes,\n }\n const parentSpanId = resolveParentSpanId(span)\n if (parentSpanId) event.parentSpanId = parentSpanId\n if (span.events && span.events.length > 0) {\n event.events = span.events.map((e) => {\n const eventAttrs = cleanAttributes(e.attributes)\n const node: {\n timeUnixNano: number\n name: string\n attributes?: Record<string, string | number | boolean>\n } = {\n timeUnixNano: hrTimeToUnixNano(e.time),\n name: e.name,\n }\n if (Object.keys(eventAttrs).length > 0) node.attributes = eventAttrs\n return node\n })\n }\n if (span.status) {\n event.status = { code: statusCodeName(span.status.code), message: span.status.message }\n }\n if (runId) event['tangle.runId'] = runId\n if (scenarioId) event['tangle.scenarioId'] = scenarioId\n if (cellId) event['tangle.cellId'] = cellId\n if (generation !== undefined) event['tangle.generation'] = generation\n return event\n }\n\n async function ingest(spans: OtelLikeSpan[]): Promise<void> {\n if (spans.length === 0) return\n const events = spans.map(convert)\n for (let i = 0; i < events.length; i += batchSize) {\n const batch = events.slice(i, i + batchSize)\n try {\n await opts.client.ingestTraces(batch)\n } catch (err) {\n await onError(err, batch)\n }\n }\n }\n\n return { ingest, spanToEvent: convert }\n}\n"],"mappings":";;;AA6CO,IAAM,oBAAoB;AAC1B,IAAM,iBAAiB;AACvB,IAAM,oBAAoB;AAqC1B,SAAS,iBAAiB,IAAoB;AACnD,QAAM,CAAC,SAAS,KAAK,IAAI;AACzB,SAAO,UAAU,MAAgB;AACnC;AAEA,SAAS,eAAe,MAAoD;AAC1E,MAAI,SAAS,eAAgB,QAAO;AACpC,MAAI,SAAS,kBAAmB,QAAO;AACvC,SAAO;AACT;AAgBA,SAAS,gBACP,OAC2C;AAC3C,QAAM,MAAiD,CAAC;AACxD,MAAI,CAAC,MAAO,QAAO;AACnB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,QAAI,MAAM,QAAQ,MAAM,OAAW;AACnC,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,YAAY,OAAO,MAAM,WAAW;AAC5E,UAAI,CAAC,IAAI;AACT;AAAA,IACF;AACA,QAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,UAAI,CAAC,IAAI,KAAK,UAAU,CAAC;AAAA,IAC3B;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,gBACP,OACA,KACoB;AACpB,QAAM,IAAI,MAAM,GAAG;AACnB,SAAO,OAAO,MAAM,WAAW,IAAI;AACrC;AAEA,SAAS,gBACP,OACA,KACoB;AACpB,QAAM,IAAI,MAAM,GAAG;AACnB,SAAO,OAAO,MAAM,WAAW,IAAI;AACrC;AAEA,SAAS,oBAAoB,MAAwC;AACnE,MAAI,KAAK,aAAc,QAAO,KAAK;AACnC,QAAM,MAAM,KAAK,oBAAoB;AACrC,SAAO,KAAK;AACd;AA4BO,SAAS,iBAAiB,MAAqC;AACpE,QAAM,YAAY,KAAK,aAAa;AACpC,QAAM,UACJ,KAAK,YACJ,CAAC,QAAQ;AACR,YAAQ,KAAK,sCAAsC,GAAG;AAAA,EACxD;AAEF,WAAS,QAAQ,MAAoC;AACnD,UAAM,MAAM,KAAK,YAAY;AAC7B,UAAM,aAAa,gBAAgB,KAAK,UAAU;AAIlD,UAAM,QAAQ,gBAAgB,YAAY,cAAc,KAAK,KAAK;AAClE,UAAM,aAAa,gBAAgB,YAAY,mBAAmB;AAClE,UAAM,SAAS,gBAAgB,YAAY,eAAe;AAC1D,UAAM,aAAa,gBAAgB,YAAY,mBAAmB;AAElE,QAAI,SAAS,CAAC,WAAW,cAAc,GAAG;AACxC,iBAAW,cAAc,IAAI;AAAA,IAC/B;AAEA,UAAM,QAAwB;AAAA,MAC5B,SAAS,IAAI;AAAA,MACb,QAAQ,IAAI;AAAA,MACZ,MAAM,KAAK;AAAA,MACX,mBAAmB,iBAAiB,KAAK,SAAS;AAAA,MAClD,iBAAiB,iBAAiB,KAAK,OAAO;AAAA,MAC9C;AAAA,IACF;AACA,UAAM,eAAe,oBAAoB,IAAI;AAC7C,QAAI,aAAc,OAAM,eAAe;AACvC,QAAI,KAAK,UAAU,KAAK,OAAO,SAAS,GAAG;AACzC,YAAM,SAAS,KAAK,OAAO,IAAI,CAAC,MAAM;AACpC,cAAM,aAAa,gBAAgB,EAAE,UAAU;AAC/C,cAAM,OAIF;AAAA,UACF,cAAc,iBAAiB,EAAE,IAAI;AAAA,UACrC,MAAM,EAAE;AAAA,QACV;AACA,YAAI,OAAO,KAAK,UAAU,EAAE,SAAS,EAAG,MAAK,aAAa;AAC1D,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AACA,QAAI,KAAK,QAAQ;AACf,YAAM,SAAS,EAAE,MAAM,eAAe,KAAK,OAAO,IAAI,GAAG,SAAS,KAAK,OAAO,QAAQ;AAAA,IACxF;AACA,QAAI,MAAO,OAAM,cAAc,IAAI;AACnC,QAAI,WAAY,OAAM,mBAAmB,IAAI;AAC7C,QAAI,OAAQ,OAAM,eAAe,IAAI;AACrC,QAAI,eAAe,OAAW,OAAM,mBAAmB,IAAI;AAC3D,WAAO;AAAA,EACT;AAEA,iBAAe,OAAO,OAAsC;AAC1D,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,SAAS,MAAM,IAAI,OAAO;AAChC,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,WAAW;AACjD,YAAM,QAAQ,OAAO,MAAM,GAAG,IAAI,SAAS;AAC3C,UAAI;AACF,cAAM,KAAK,OAAO,aAAa,KAAK;AAAA,MACtC,SAAS,KAAK;AACZ,cAAM,QAAQ,KAAK,KAAK;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,QAAQ,aAAa,QAAQ;AACxC;","names":[]}