braintrust 3.31.0 → 3.33.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 (62) hide show
  1. package/README.md +0 -43
  2. package/dev/dist/index.d.mts +767 -0
  3. package/dev/dist/index.d.ts +767 -0
  4. package/dev/dist/index.js +3115 -908
  5. package/dev/dist/index.mjs +2466 -259
  6. package/dist/apply-auto-instrumentation.js +325 -221
  7. package/dist/apply-auto-instrumentation.mjs +110 -6
  8. package/dist/auto-instrumentations/bundler/esbuild.cjs +182 -7
  9. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/next.cjs +182 -7
  11. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  12. package/dist/auto-instrumentations/bundler/rollup.cjs +182 -7
  13. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  14. package/dist/auto-instrumentations/bundler/vite.cjs +182 -7
  15. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  16. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +182 -7
  17. package/dist/auto-instrumentations/bundler/webpack.cjs +182 -7
  18. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  19. package/dist/auto-instrumentations/{chunk-U64OYU4Q.mjs → chunk-T2DMPWFI.mjs} +113 -6
  20. package/dist/auto-instrumentations/{chunk-LW4HDHXT.mjs → chunk-UHJ2DNYJ.mjs} +74 -2
  21. package/dist/auto-instrumentations/{chunk-OXINGIEZ.mjs → chunk-W5QNG3PV.mjs} +1 -1
  22. package/dist/auto-instrumentations/hook.mjs +209 -9
  23. package/dist/auto-instrumentations/index.cjs +115 -6
  24. package/dist/auto-instrumentations/index.d.mts +5 -1
  25. package/dist/auto-instrumentations/index.d.ts +5 -1
  26. package/dist/auto-instrumentations/index.mjs +5 -1
  27. package/dist/browser.d.mts +2324 -342
  28. package/dist/browser.d.ts +2324 -342
  29. package/dist/browser.js +3573 -626
  30. package/dist/browser.mjs +3573 -626
  31. package/dist/chunk-7P6ASYW6.mjs +9 -0
  32. package/dist/{chunk-V32LW47Z.js → chunk-AXJTOUOC.js} +3022 -1131
  33. package/dist/{chunk-4AQGZS6X.js → chunk-G3VHOHRC.js} +85 -11
  34. package/dist/chunk-MLKGABMK.js +9 -0
  35. package/dist/{chunk-AJT4FR25.mjs → chunk-MZLBAFVJ.mjs} +2051 -160
  36. package/dist/{chunk-CE3BLB2L.mjs → chunk-TZVZN2JJ.mjs} +84 -10
  37. package/dist/cli.js +2614 -297
  38. package/dist/custom-views.d.mts +112 -0
  39. package/dist/custom-views.d.ts +112 -0
  40. package/dist/custom-views.js +13 -0
  41. package/dist/custom-views.mjs +13 -0
  42. package/dist/edge-light.d.mts +1 -1
  43. package/dist/edge-light.d.ts +1 -1
  44. package/dist/edge-light.js +3573 -626
  45. package/dist/edge-light.mjs +3573 -626
  46. package/dist/index.d.mts +2324 -342
  47. package/dist/index.d.ts +2324 -342
  48. package/dist/index.js +1959 -969
  49. package/dist/index.mjs +1450 -460
  50. package/dist/instrumentation/index.d.mts +439 -245
  51. package/dist/instrumentation/index.d.ts +439 -245
  52. package/dist/instrumentation/index.js +2550 -258
  53. package/dist/instrumentation/index.mjs +2550 -258
  54. package/dist/vitest-evals-reporter.js +17 -16
  55. package/dist/vitest-evals-reporter.mjs +3 -2
  56. package/dist/workerd.d.mts +1 -1
  57. package/dist/workerd.d.ts +1 -1
  58. package/dist/workerd.js +3573 -626
  59. package/dist/workerd.mjs +3573 -626
  60. package/package.json +10 -2
  61. package/util/dist/index.d.mts +326 -0
  62. package/util/dist/index.d.ts +326 -0
package/dist/index.mjs CHANGED
@@ -54,6 +54,7 @@ import {
54
54
  UUIDGenerator,
55
55
  X_CACHED_HEADER,
56
56
  _exportsForTestingOnly,
57
+ _internalExportParentSynchronously,
57
58
  _internalGetGlobalState,
58
59
  _internalResumeSpan,
59
60
  _internalSetInitialState,
@@ -117,6 +118,7 @@ import {
117
118
  renderMessage,
118
119
  renderPromptParams,
119
120
  renderTemplateContent,
121
+ resolveUseLegacyUuidIds,
120
122
  runWithAutoInstrumentationSuppressed,
121
123
  setFetch,
122
124
  setMaskingFunction,
@@ -143,10 +145,9 @@ import {
143
145
  wrapMastraAgent,
144
146
  wrapTraced,
145
147
  zodToJsonSchema
146
- } from "./chunk-AJT4FR25.mjs";
148
+ } from "./chunk-MZLBAFVJ.mjs";
147
149
  import {
148
150
  INSTRUMENTATION_NAMES,
149
- __export,
150
151
  aiSDKChannels,
151
152
  anthropicChannels,
152
153
  bedrockRuntimeChannels,
@@ -156,16 +157,19 @@ import {
156
157
  cohereChannels,
157
158
  cursorSDKChannels,
158
159
  debugLogger,
160
+ elevenLabsChannels,
159
161
  genkitChannels,
160
162
  gitHubCopilotChannels,
161
163
  googleADKChannels,
162
164
  googleGenAIChannels,
165
+ googleGenerativeAIChannels,
163
166
  groqChannels,
164
167
  harnessAgentChannels,
165
168
  huggingFaceChannels,
166
169
  huggingFaceTransformersChannels,
167
170
  isSupportedHuggingFaceTransformersTask,
168
171
  isomorph_default,
172
+ langGraphSDKChannels,
169
173
  langSmithChannels,
170
174
  mistralChannels,
171
175
  ollamaChannels,
@@ -177,7 +181,10 @@ import {
177
181
  strandsAgentSDKChannels,
178
182
  voyageAIChannels,
179
183
  withSpanInstrumentationName
180
- } from "./chunk-CE3BLB2L.mjs";
184
+ } from "./chunk-TZVZN2JJ.mjs";
185
+ import {
186
+ __export
187
+ } from "./chunk-7P6ASYW6.mjs";
181
188
 
182
189
  // src/exports.ts
183
190
  var exports_exports = {};
@@ -190,8 +197,6 @@ __export(exports_exports, {
190
197
  BRAINTRUST_PARENT_KEY: () => BRAINTRUST_PARENT_KEY,
191
198
  BaseAttachment: () => BaseAttachment,
192
199
  BaseExperiment: () => BaseExperiment,
193
- BatchScorer: () => BatchScorer,
194
- BatchTask: () => BatchTask,
195
200
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
196
201
  BraintrustMiddleware: () => BraintrustMiddleware,
197
202
  BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
@@ -205,8 +210,6 @@ __export(exports_exports, {
205
210
  DEFAULT_MAX_REQUEST_SIZE: () => DEFAULT_MAX_REQUEST_SIZE,
206
211
  Dataset: () => Dataset,
207
212
  DatasetPipeline: () => DatasetPipeline,
208
- DurableEvalMemoryStore: () => DurableEvalMemoryStore,
209
- DurableEvalRedisStore: () => DurableEvalRedisStore,
210
213
  ERR_PERMALINK: () => ERR_PERMALINK,
211
214
  Eval: () => Eval,
212
215
  EvalResultWithSummary: () => EvalResultWithSummary,
@@ -241,6 +244,10 @@ __export(exports_exports, {
241
244
  TestBackgroundLogger: () => TestBackgroundLogger,
242
245
  ToolBuilder: () => ToolBuilder,
243
246
  UUIDGenerator: () => UUIDGenerator,
247
+ WorkflowEvalMemoryStore: () => WorkflowEvalMemoryStore,
248
+ WorkflowEvalRedisStore: () => WorkflowEvalRedisStore,
249
+ WorkflowScorer: () => WorkflowScorer,
250
+ WorkflowTask: () => WorkflowTask,
244
251
  X_CACHED_HEADER: () => X_CACHED_HEADER,
245
252
  _exportsForTestingOnly: () => _exportsForTestingOnly,
246
253
  _internalGetGlobalState: () => _internalGetGlobalState,
@@ -265,12 +272,13 @@ __export(exports_exports, {
265
272
  currentSpan: () => currentSpan,
266
273
  deepCopyEvent: () => deepCopyEvent,
267
274
  defaultErrorScoreHandler: () => defaultErrorScoreHandler,
268
- defineDurableEval: () => defineDurableEval,
275
+ defineWorkflowEval: () => defineWorkflowEval,
269
276
  deserializePlainStringAsJSON: () => deserializePlainStringAsJSON,
270
277
  devNullWritableStream: () => devNullWritableStream,
271
278
  evaluatorDefinitionSchema: () => evaluatorDefinitionSchema,
272
279
  evaluatorDefinitionsSchema: () => evaluatorDefinitionsSchema,
273
280
  extractTraceContextFromHeaders: () => extractTraceContextFromHeaders,
281
+ failOpenAIAgentsTrace: () => failOpenAIAgentsTrace,
274
282
  flush: () => flush,
275
283
  getContextManager: () => getContextManager,
276
284
  getIdGenerator: () => getIdGenerator,
@@ -317,12 +325,14 @@ __export(exports_exports, {
317
325
  setFetch: () => setFetch,
318
326
  setMaskingFunction: () => setMaskingFunction,
319
327
  spanComponentsToObjectId: () => spanComponentsToObjectId,
328
+ startOpenAIAgentsTrace: () => startOpenAIAgentsTrace,
320
329
  startSpan: () => startSpan,
321
330
  summarize: () => summarize,
322
331
  templateRegistry: () => templateRegistry,
323
332
  toolFunctionDefinitionSchema: () => ToolFunctionDefinition,
324
333
  traceable: () => traceable,
325
334
  traced: () => traced,
335
+ updateOpenAIAgentsTrace: () => updateOpenAIAgentsTrace,
326
336
  updateSpan: () => updateSpan,
327
337
  uploadLogs3OverflowPayload: () => uploadLogs3OverflowPayload,
328
338
  utf8ByteLength: () => utf8ByteLength,
@@ -343,12 +353,15 @@ __export(exports_exports, {
343
353
  wrapCohere: () => wrapCohere,
344
354
  wrapCopilotClient: () => wrapCopilotClient,
345
355
  wrapCursorSDK: () => wrapCursorSDK,
356
+ wrapElevenLabs: () => wrapElevenLabs,
346
357
  wrapGenkit: () => wrapGenkit,
347
358
  wrapGoogleADK: () => wrapGoogleADK,
348
359
  wrapGoogleGenAI: () => wrapGoogleGenAI,
360
+ wrapGoogleGenerativeAI: () => wrapGoogleGenerativeAI,
349
361
  wrapGroq: () => wrapGroq,
350
362
  wrapHuggingFace: () => wrapHuggingFace,
351
363
  wrapHuggingFaceTransformers: () => wrapHuggingFaceTransformers,
364
+ wrapLangGraphSDK: () => wrapLangGraphSDK,
352
365
  wrapLangSmithClient: () => wrapLangSmithClient,
353
366
  wrapLangSmithRunTrees: () => wrapLangSmithRunTrees,
354
367
  wrapLangSmithTraceable: () => wrapLangSmithTraceable,
@@ -715,6 +728,143 @@ async function completeOpenAIBatchTrace(args) {
715
728
  );
716
729
  }
717
730
 
731
+ // src/openai-agents-api.ts
732
+ function isRecord(value) {
733
+ return typeof value === "object" && value !== null && !Array.isArray(value);
734
+ }
735
+ function validateStartArgs(value) {
736
+ if (!isRecord(value)) {
737
+ throw new TypeError(
738
+ "startOpenAIAgentsTrace expected an OpenAI Agents parameters object"
739
+ );
740
+ }
741
+ const agent = value.agent;
742
+ if (agent !== void 0 && agent !== null && !isRecord(agent)) {
743
+ throw new TypeError(
744
+ "startOpenAIAgentsTrace expected agent to be an object or null"
745
+ );
746
+ }
747
+ if (isRecord(agent) && (agent.id !== void 0 && typeof agent.id !== "string" || agent.model !== void 0 && typeof agent.model !== "string" || agent.name !== void 0 && agent.name !== null && typeof agent.name !== "string")) {
748
+ throw new TypeError(
749
+ "startOpenAIAgentsTrace received invalid agent identity fields"
750
+ );
751
+ }
752
+ if (value.agent_id !== void 0 && typeof value.agent_id !== "string") {
753
+ throw new TypeError(
754
+ "startOpenAIAgentsTrace expected agent_id to be a string"
755
+ );
756
+ }
757
+ if (value.metadata !== void 0 && value.metadata !== null && !isRecord(value.metadata)) {
758
+ throw new TypeError(
759
+ "startOpenAIAgentsTrace expected metadata to be an object or null"
760
+ );
761
+ }
762
+ return value;
763
+ }
764
+ function validateEvent(event) {
765
+ if (!isRecord(event) || typeof event.type !== "string" || event.type.length === 0) {
766
+ throw new TypeError(
767
+ "updateOpenAIAgentsTrace expected an OpenAI Agents event object"
768
+ );
769
+ }
770
+ return event;
771
+ }
772
+ function createTraceState() {
773
+ const parent = _internalExportParentSynchronously(getSpanParentObject());
774
+ if (!parent) {
775
+ return null;
776
+ }
777
+ const parentComponents = SpanComponentsV4.fromStr(parent);
778
+ const rowId = newId();
779
+ const useLegacyIds = resolveUseLegacyUuidIds();
780
+ const spanId = useLegacyIds ? newId() : newId().replaceAll("-", "").slice(0, 16);
781
+ const rootSpanId = useLegacyIds ? spanId : newId().replaceAll("-", "");
782
+ const root = new SpanComponentsV4({
783
+ object_type: parentComponents.data.object_type,
784
+ ...parentComponents.data.object_id ? { object_id: parentComponents.data.object_id } : {
785
+ compute_object_metadata_args: parentComponents.data.compute_object_metadata_args ?? {}
786
+ },
787
+ propagated_event: parentComponents.data.propagated_event,
788
+ root_span_id: rootSpanId,
789
+ row_id: rowId,
790
+ span_id: spanId
791
+ }).toStr();
792
+ return {
793
+ callItems: {},
794
+ ended: false,
795
+ eventIds: [],
796
+ openTools: {},
797
+ root,
798
+ rootKey: spanId,
799
+ rootParent: parent,
800
+ startTime: getCurrentUnixTimestamp(),
801
+ subagents: {},
802
+ turnSubagents: {},
803
+ version: 1
804
+ };
805
+ }
806
+ function startOpenAIAgentsTrace(args) {
807
+ const validatedArgs = validateStartArgs(args);
808
+ const state = createTraceState();
809
+ if (!state) {
810
+ return null;
811
+ }
812
+ try {
813
+ void openAIChannels.agentsTraceStart.invoke(
814
+ async () => state,
815
+ void 0,
816
+ [{ args: validatedArgs, state }],
817
+ {}
818
+ ).catch((error) => {
819
+ debugLogger.debug(
820
+ "OpenAI Agents API instrumentation could not start:",
821
+ error
822
+ );
823
+ });
824
+ } catch (error) {
825
+ debugLogger.debug(
826
+ "OpenAI Agents API instrumentation could not start:",
827
+ error
828
+ );
829
+ }
830
+ return state;
831
+ }
832
+ async function updateOpenAIAgentsTrace(token, event) {
833
+ const state = token;
834
+ const validatedEvent = validateEvent(event);
835
+ try {
836
+ return await openAIChannels.agentsTraceCapture.invoke(
837
+ async () => state,
838
+ void 0,
839
+ [{ state, event: validatedEvent }],
840
+ {}
841
+ );
842
+ } catch (error) {
843
+ debugLogger.debug(
844
+ "OpenAI Agents API instrumentation could not update:",
845
+ error
846
+ );
847
+ return token;
848
+ }
849
+ }
850
+ async function failOpenAIAgentsTrace(token, error) {
851
+ const state = token;
852
+ try {
853
+ return await openAIChannels.agentsTraceFail.invoke(
854
+ async () => state,
855
+ void 0,
856
+ [{ state, error }],
857
+ {}
858
+ );
859
+ } catch (instrumentationError) {
860
+ debugLogger.debug(
861
+ "OpenAI Agents API instrumentation could not record failure:",
862
+ instrumentationError
863
+ );
864
+ return token;
865
+ }
866
+ }
867
+
718
868
  // src/functions/invoke.ts
719
869
  async function invoke(args) {
720
870
  const {
@@ -2164,7 +2314,31 @@ var MAX_STORED_LLM_INPUTS = 100;
2164
2314
  var MAX_STORED_REASONING_BLOCKS = 100;
2165
2315
  var MAX_STORED_SPAN_REFERENCES = 1e4;
2166
2316
  var MAX_STORED_STEP_STARTS = 1e4;
2317
+ var EVE_HANDLED_EVENT_TYPES = /* @__PURE__ */ new Set([
2318
+ "action.result",
2319
+ "actions.requested",
2320
+ "message.completed",
2321
+ "message.received",
2322
+ "reasoning.completed",
2323
+ "result.completed",
2324
+ "session.completed",
2325
+ "session.failed",
2326
+ "session.started",
2327
+ "step.completed",
2328
+ "step.failed",
2329
+ "step.started",
2330
+ "subagent.called",
2331
+ "subagent.completed",
2332
+ "turn.completed",
2333
+ "turn.failed",
2334
+ "turn.started"
2335
+ ]);
2167
2336
  function braintrustEveHook(options) {
2337
+ if (!options || typeof options.defineState !== "function") {
2338
+ throw new TypeError(
2339
+ "braintrustEveHook requires Eve's defineState function"
2340
+ );
2341
+ }
2168
2342
  const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState);
2169
2343
  const bridge = new EveBridge(state);
2170
2344
  return {
@@ -2176,15 +2350,27 @@ function braintrustEveHook(options) {
2176
2350
  };
2177
2351
  }
2178
2352
  function createLegacyEveInstrumentation(options) {
2353
+ if (!options || typeof options.defineState !== "function") {
2354
+ throw new TypeError(
2355
+ "braintrustEveInstrumentation requires Eve's defineState function"
2356
+ );
2357
+ }
2179
2358
  const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState);
2180
2359
  return {
2181
2360
  events: {
2182
2361
  "step.started": (input) => {
2183
2362
  try {
2363
+ if (!isEveInstrumentationStepStartedEventInput(input)) {
2364
+ debugLogger.warn(
2365
+ "Ignoring malformed Eve instrumentation step.started event"
2366
+ );
2367
+ return void 0;
2368
+ }
2184
2369
  captureEveModelInput(state, input);
2185
2370
  } catch (error) {
2186
2371
  debugLogger.warn("Error in Eve LLM input capture:", error);
2187
2372
  }
2373
+ return void 0;
2188
2374
  }
2189
2375
  },
2190
2376
  recordInputs: false,
@@ -2193,7 +2379,77 @@ function createLegacyEveInstrumentation(options) {
2193
2379
  };
2194
2380
  }
2195
2381
  function isEveHandleMessageStreamEvent(event) {
2196
- return isObject(event) && typeof event["type"] === "string";
2382
+ if (!isObject(event) || typeof event["type"] !== "string") {
2383
+ return false;
2384
+ }
2385
+ const type = event["type"];
2386
+ if (type === "session.completed") {
2387
+ return true;
2388
+ }
2389
+ if (!isObject(event["data"])) {
2390
+ return false;
2391
+ }
2392
+ const data = event["data"];
2393
+ if (type === "session.started") {
2394
+ return true;
2395
+ }
2396
+ if (type === "session.failed") {
2397
+ return typeof data["sessionId"] === "string" && typeof data["code"] === "string" && typeof data["message"] === "string";
2398
+ }
2399
+ if (type === "session.waiting") {
2400
+ return data["wait"] === "next-user-message";
2401
+ }
2402
+ if (typeof data["turnId"] !== "string" || typeof data["sequence"] !== "number" || !Number.isFinite(data["sequence"])) {
2403
+ return false;
2404
+ }
2405
+ if (type === "turn.started" || type === "turn.completed" || type === "subagent.called" || type === "subagent.completed") {
2406
+ if (type === "subagent.called") {
2407
+ return typeof data["callId"] === "string" && typeof data["childSessionId"] === "string" && typeof data["name"] === "string";
2408
+ }
2409
+ if (type === "subagent.completed") {
2410
+ return typeof data["callId"] === "string" && typeof data["subagentName"] === "string";
2411
+ }
2412
+ return true;
2413
+ }
2414
+ if (type === "turn.failed") {
2415
+ return typeof data["code"] === "string" && typeof data["message"] === "string";
2416
+ }
2417
+ if (type === "message.received") {
2418
+ return typeof data["message"] === "string";
2419
+ }
2420
+ if (typeof data["stepIndex"] !== "number" || !Number.isFinite(data["stepIndex"]) || data["stepIndex"] < 0) {
2421
+ return false;
2422
+ }
2423
+ switch (type) {
2424
+ case "step.started":
2425
+ return true;
2426
+ case "message.completed":
2427
+ return typeof data["finishReason"] === "string" && (typeof data["message"] === "string" || data["message"] === null);
2428
+ case "reasoning.completed":
2429
+ return typeof data["reasoning"] === "string";
2430
+ case "result.completed":
2431
+ return true;
2432
+ case "step.completed":
2433
+ return typeof data["finishReason"] === "string";
2434
+ case "step.failed":
2435
+ return typeof data["code"] === "string" && typeof data["message"] === "string";
2436
+ case "actions.requested":
2437
+ return Array.isArray(data["actions"]);
2438
+ case "action.result":
2439
+ return isObject(data["result"]) && (data["status"] === "completed" || data["status"] === "failed" || data["status"] === "rejected");
2440
+ default:
2441
+ return false;
2442
+ }
2443
+ }
2444
+ function isEveInstrumentationStepStartedEventInput(input) {
2445
+ if (!isObject(input)) {
2446
+ return false;
2447
+ }
2448
+ const modelInput = input["modelInput"];
2449
+ const session = input["session"];
2450
+ const step = input["step"];
2451
+ const turn = input["turn"];
2452
+ return isObject(modelInput) && Array.isArray(modelInput["messages"]) && isObject(session) && typeof session["id"] === "string" && isObject(step) && typeof step["index"] === "number" && Number.isFinite(step["index"]) && step["index"] >= 0 && isObject(turn) && typeof turn["id"] === "string" && typeof turn["sequence"] === "number" && Number.isFinite(turn["sequence"]);
2197
2453
  }
2198
2454
  var ResumedEveSpan = class {
2199
2455
  constructor(reference) {
@@ -2362,7 +2618,17 @@ var EveBridge = class {
2362
2618
  });
2363
2619
  }
2364
2620
  async handle(event, ctx, hookMetadata) {
2621
+ try {
2622
+ await this.handleUnchecked(event, ctx, hookMetadata);
2623
+ } catch (error) {
2624
+ debugLogger.warn("Error in Eve hook instrumentation:", error);
2625
+ }
2626
+ }
2627
+ async handleUnchecked(event, ctx, hookMetadata) {
2365
2628
  if (!isEveHandleMessageStreamEvent(event)) {
2629
+ if (isObject(event) && typeof event["type"] === "string" && EVE_HANDLED_EVENT_TYPES.has(event["type"])) {
2630
+ debugLogger.warn(`Ignoring malformed Eve hook ${event["type"]} event`);
2631
+ }
2366
2632
  return;
2367
2633
  }
2368
2634
  const run = async () => {
@@ -2688,9 +2954,14 @@ var EveBridge = class {
2688
2954
  }
2689
2955
  for (const action of traceActions) {
2690
2956
  const name = action.kind === "tool-call" ? action.toolName : action.subagentName ?? action.name ?? "agent";
2957
+ let args = "null";
2958
+ try {
2959
+ args = JSON.stringify(action.input) ?? "null";
2960
+ } catch {
2961
+ }
2691
2962
  toolCallsById.set(action.callId, {
2692
2963
  function: {
2693
- arguments: JSON.stringify(action.input),
2964
+ arguments: args,
2694
2965
  name
2695
2966
  },
2696
2967
  id: action.callId,
@@ -3559,17 +3830,33 @@ function consumeCapturedEveModelInput(state, sessionId, turnId, stepIndex) {
3559
3830
  }
3560
3831
  }
3561
3832
  function capturedModelInput(modelInput) {
3562
- const { instructions, messages } = modelInput;
3563
- const value = [];
3564
- if (typeof instructions === "string") {
3565
- value.push({ content: instructions, role: "system" });
3566
- } else if (Array.isArray(instructions)) {
3567
- value.push(...instructions.map(capturedEveModelMessage));
3568
- } else if (instructions) {
3569
- value.push(capturedEveModelMessage(instructions));
3570
- }
3571
- value.push(...messages.map(capturedEveModelMessage));
3572
3833
  try {
3834
+ if (!isObject(modelInput) || !Array.isArray(modelInput["messages"])) {
3835
+ return void 0;
3836
+ }
3837
+ const value = [];
3838
+ const instructions = modelInput["instructions"];
3839
+ if (typeof instructions === "string") {
3840
+ value.push({ content: instructions, role: "system" });
3841
+ } else if (Array.isArray(instructions)) {
3842
+ for (const instruction of instructions) {
3843
+ const captured = capturedEveModelMessage(instruction);
3844
+ if (captured?.role === "system") {
3845
+ value.push(captured);
3846
+ }
3847
+ }
3848
+ } else {
3849
+ const captured = capturedEveModelMessage(instructions);
3850
+ if (captured?.role === "system") {
3851
+ value.push(captured);
3852
+ }
3853
+ }
3854
+ for (const message of modelInput["messages"]) {
3855
+ const captured = capturedEveModelMessage(message);
3856
+ if (captured) {
3857
+ value.push(captured);
3858
+ }
3859
+ }
3573
3860
  const cloned = JSON.parse(JSON.stringify(value));
3574
3861
  if (!Array.isArray(cloned)) {
3575
3862
  return void 0;
@@ -3580,116 +3867,202 @@ function capturedModelInput(modelInput) {
3580
3867
  }
3581
3868
  }
3582
3869
  function capturedEveModelMessage(message) {
3583
- const { content, role } = message;
3870
+ if (!isObject(message)) {
3871
+ return void 0;
3872
+ }
3873
+ const role = message["role"];
3874
+ if (role !== "system" && role !== "user" && role !== "assistant" && role !== "tool") {
3875
+ return void 0;
3876
+ }
3877
+ const content = message["content"];
3584
3878
  if (typeof content === "string") {
3585
3879
  return { content, role };
3586
3880
  }
3587
- return { content: content.map(capturedEveModelContentPart), role };
3881
+ if (!Array.isArray(content)) {
3882
+ return void 0;
3883
+ }
3884
+ const capturedContent = [];
3885
+ for (const part of content) {
3886
+ const captured = capturedEveModelContentPart(part);
3887
+ if (captured) {
3888
+ capturedContent.push(captured);
3889
+ }
3890
+ }
3891
+ return { content: capturedContent, role };
3588
3892
  }
3589
3893
  function capturedEveModelContentPart(part) {
3590
- switch (part.type) {
3894
+ if (!isObject(part) || typeof part["type"] !== "string") {
3895
+ return void 0;
3896
+ }
3897
+ switch (part["type"]) {
3591
3898
  case "text":
3592
- case "reasoning":
3593
- return { text: part.text, type: part.type };
3899
+ case "reasoning": {
3900
+ const text = part["text"];
3901
+ return typeof text === "string" ? { text, type: part["type"] } : void 0;
3902
+ }
3594
3903
  case "image":
3904
+ if (!Object.hasOwn(part, "image")) {
3905
+ return void 0;
3906
+ }
3595
3907
  return {
3596
- image: part.image,
3597
- ...part.mediaType !== void 0 ? { mediaType: part.mediaType } : {},
3908
+ image: part["image"],
3909
+ ...typeof part["mediaType"] === "string" ? { mediaType: part["mediaType"] } : {},
3598
3910
  type: "image"
3599
3911
  };
3600
3912
  case "file":
3601
- case "reasoning-file":
3913
+ case "reasoning-file": {
3914
+ if (!Object.hasOwn(part, "data") || typeof part["mediaType"] !== "string") {
3915
+ return void 0;
3916
+ }
3602
3917
  return {
3603
- data: part.data,
3604
- ...part.type === "file" && part.filename !== void 0 ? { filename: part.filename } : {},
3605
- mediaType: part.mediaType,
3606
- type: part.type
3918
+ data: part["data"],
3919
+ ...part["type"] === "file" && typeof part["filename"] === "string" ? { filename: part["filename"] } : {},
3920
+ mediaType: part["mediaType"],
3921
+ type: part["type"]
3607
3922
  };
3923
+ }
3608
3924
  case "custom":
3609
3925
  return {
3610
- ..."kind" in part ? { kind: part.kind } : {},
3926
+ ...typeof part["kind"] === "string" ? { kind: part["kind"] } : {},
3611
3927
  type: "custom"
3612
3928
  };
3613
- case "tool-call":
3929
+ case "tool-call": {
3930
+ if (typeof part["toolCallId"] !== "string" || typeof part["toolName"] !== "string") {
3931
+ return void 0;
3932
+ }
3614
3933
  return {
3615
- input: part.input,
3616
- ...part.providerExecuted !== void 0 ? { providerExecuted: part.providerExecuted } : {},
3617
- toolCallId: part.toolCallId,
3618
- toolName: part.toolName,
3934
+ input: part["input"],
3935
+ ...typeof part["providerExecuted"] === "boolean" ? { providerExecuted: part["providerExecuted"] } : {},
3936
+ toolCallId: part["toolCallId"],
3937
+ toolName: part["toolName"],
3619
3938
  type: "tool-call"
3620
3939
  };
3940
+ }
3621
3941
  case "tool-result": {
3622
- const output = part.output;
3942
+ const output = part["output"];
3943
+ if (typeof part["toolCallId"] !== "string" || typeof part["toolName"] !== "string" || !isObject(output) || typeof output["type"] !== "string") {
3944
+ return void 0;
3945
+ }
3623
3946
  let capturedOutput;
3624
- switch (output.type) {
3947
+ switch (output["type"]) {
3625
3948
  case "text":
3626
- case "error-text":
3627
- capturedOutput = { type: output.type, value: output.value };
3949
+ case "error-text": {
3950
+ if (typeof output["value"] !== "string") {
3951
+ return void 0;
3952
+ }
3953
+ capturedOutput = {
3954
+ type: output["type"],
3955
+ value: output["value"]
3956
+ };
3628
3957
  break;
3958
+ }
3629
3959
  case "json":
3630
3960
  case "error-json":
3631
- capturedOutput = { type: output.type, value: output.value };
3961
+ capturedOutput = {
3962
+ type: output["type"],
3963
+ value: output["value"]
3964
+ };
3632
3965
  break;
3633
3966
  case "execution-denied":
3634
3967
  capturedOutput = {
3635
- ...output.reason !== void 0 ? { reason: output.reason } : {},
3968
+ ...typeof output["reason"] === "string" ? { reason: output["reason"] } : {},
3636
3969
  type: "execution-denied"
3637
3970
  };
3638
3971
  break;
3639
- case "content":
3972
+ case "content": {
3973
+ if (!Array.isArray(output["value"])) {
3974
+ return void 0;
3975
+ }
3976
+ const value = [];
3977
+ for (const outputPart of output["value"]) {
3978
+ const captured = capturedEveModelContentPart(outputPart);
3979
+ if (captured) {
3980
+ value.push(captured);
3981
+ }
3982
+ }
3640
3983
  capturedOutput = {
3641
3984
  type: "content",
3642
- value: output.value.map(capturedEveModelContentPart)
3985
+ value
3643
3986
  };
3644
3987
  break;
3988
+ }
3989
+ default:
3990
+ return void 0;
3645
3991
  }
3646
3992
  return {
3647
3993
  output: capturedOutput,
3648
- toolCallId: part.toolCallId,
3649
- toolName: part.toolName,
3994
+ toolCallId: part["toolCallId"],
3995
+ toolName: part["toolName"],
3650
3996
  type: "tool-result"
3651
3997
  };
3652
3998
  }
3653
- case "tool-approval-request":
3999
+ case "tool-approval-request": {
4000
+ if (typeof part["approvalId"] !== "string" || typeof part["toolCallId"] !== "string") {
4001
+ return void 0;
4002
+ }
3654
4003
  return {
3655
- approvalId: part.approvalId,
3656
- ...part.isAutomatic !== void 0 ? { isAutomatic: part.isAutomatic } : {},
3657
- ...part.signature !== void 0 ? { signature: part.signature } : {},
3658
- toolCallId: part.toolCallId,
4004
+ approvalId: part["approvalId"],
4005
+ ...typeof part["isAutomatic"] === "boolean" ? { isAutomatic: part["isAutomatic"] } : {},
4006
+ ...typeof part["signature"] === "string" ? { signature: part["signature"] } : {},
4007
+ toolCallId: part["toolCallId"],
3659
4008
  type: "tool-approval-request"
3660
4009
  };
3661
- case "tool-approval-response":
4010
+ }
4011
+ case "tool-approval-response": {
4012
+ if (typeof part["approvalId"] !== "string" || typeof part["approved"] !== "boolean") {
4013
+ return void 0;
4014
+ }
3662
4015
  return {
3663
- approvalId: part.approvalId,
3664
- approved: part.approved,
3665
- ...part.providerExecuted !== void 0 ? { providerExecuted: part.providerExecuted } : {},
3666
- ...part.reason !== void 0 ? { reason: part.reason } : {},
4016
+ approvalId: part["approvalId"],
4017
+ approved: part["approved"],
4018
+ ...typeof part["providerExecuted"] === "boolean" ? { providerExecuted: part["providerExecuted"] } : {},
4019
+ ...typeof part["reason"] === "string" ? { reason: part["reason"] } : {},
3667
4020
  type: "tool-approval-response"
3668
4021
  };
4022
+ }
3669
4023
  case "file-data":
3670
- case "image-data":
4024
+ case "image-data": {
4025
+ if (typeof part["data"] !== "string" || typeof part["mediaType"] !== "string") {
4026
+ return void 0;
4027
+ }
3671
4028
  return {
3672
- data: part.data,
3673
- ...part.type === "file-data" && part.filename !== void 0 ? { filename: part.filename } : {},
3674
- mediaType: part.mediaType,
3675
- type: part.type
4029
+ data: part["data"],
4030
+ ...part["type"] === "file-data" && typeof part["filename"] === "string" ? { filename: part["filename"] } : {},
4031
+ mediaType: part["mediaType"],
4032
+ type: part["type"]
3676
4033
  };
4034
+ }
3677
4035
  case "file-url":
3678
- case "image-url":
4036
+ case "image-url": {
4037
+ if (typeof part["url"] !== "string") {
4038
+ return void 0;
4039
+ }
3679
4040
  return {
3680
- ...part.type === "file-url" && part.mediaType !== void 0 ? { mediaType: part.mediaType } : {},
3681
- type: part.type,
3682
- url: part.url
4041
+ ...part["type"] === "file-url" && typeof part["mediaType"] === "string" ? { mediaType: part["mediaType"] } : {},
4042
+ type: part["type"],
4043
+ url: part["url"]
3683
4044
  };
4045
+ }
3684
4046
  case "file-id":
3685
- case "image-file-id":
3686
- return { fileId: part.fileId, type: part.type };
4047
+ case "image-file-id": {
4048
+ const fileId = part["fileId"];
4049
+ if (typeof fileId !== "string" && !isObject(fileId)) {
4050
+ return void 0;
4051
+ }
4052
+ return { fileId, type: part["type"] };
4053
+ }
3687
4054
  case "file-reference":
3688
- case "image-file-reference":
4055
+ case "image-file-reference": {
4056
+ if (!isObject(part["providerReference"])) {
4057
+ return void 0;
4058
+ }
3689
4059
  return {
3690
- providerReference: part.providerReference,
3691
- type: part.type
4060
+ providerReference: part["providerReference"],
4061
+ type: part["type"]
3692
4062
  };
4063
+ }
4064
+ default:
4065
+ return void 0;
3693
4066
  }
3694
4067
  }
3695
4068
  function isCapturedModelInput(input) {
@@ -3825,18 +4198,54 @@ function createEveInstrumentationProvider(options = {}) {
3825
4198
  recordOutputs: true
3826
4199
  }),
3827
4200
  events: {
3828
- "action.completed": (event, context) => bridge.handleActionTerminal(event, context),
3829
- "action.failed": (event, context) => bridge.handleActionTerminal(event, context),
3830
- "action.started": (event, context) => bridge.handleActionStarted(event, context),
3831
- "model.call.completed": (event, context) => bridge.handleModelTerminal(event, context),
3832
- "model.call.failed": (event, context) => bridge.handleModelTerminal(event, context),
3833
- "model.call.started": (event, context) => bridge.handleModelStarted(event, context),
3834
- "step.attempt.completed": (event) => bridge.handleStepAttemptTerminal(event),
3835
- "step.attempt.failed": (event) => bridge.handleStepAttemptTerminal(event),
3836
- "turn.cancelled": (event, context) => bridge.handleTurnTerminal(event, context),
3837
- "turn.completed": (event, context) => bridge.handleTurnTerminal(event, context),
3838
- "turn.failed": (event, context) => bridge.handleTurnTerminal(event, context),
3839
- "turn.started": (event, context) => bridge.handleTurnStarted(event, context)
4201
+ "action.completed": providerHandler(
4202
+ "action.completed",
4203
+ bridge.handleActionTerminal.bind(bridge)
4204
+ ),
4205
+ "action.failed": providerHandler(
4206
+ "action.failed",
4207
+ bridge.handleActionTerminal.bind(bridge)
4208
+ ),
4209
+ "action.started": providerHandler(
4210
+ "action.started",
4211
+ bridge.handleActionStarted.bind(bridge)
4212
+ ),
4213
+ "model.call.completed": providerHandler(
4214
+ "model.call.completed",
4215
+ bridge.handleModelTerminal.bind(bridge)
4216
+ ),
4217
+ "model.call.failed": providerHandler(
4218
+ "model.call.failed",
4219
+ bridge.handleModelTerminal.bind(bridge)
4220
+ ),
4221
+ "model.call.started": providerHandler(
4222
+ "model.call.started",
4223
+ bridge.handleModelStarted.bind(bridge)
4224
+ ),
4225
+ "step.attempt.completed": providerHandler(
4226
+ "step.attempt.completed",
4227
+ (event) => bridge.handleStepAttemptTerminal(event)
4228
+ ),
4229
+ "step.attempt.failed": providerHandler(
4230
+ "step.attempt.failed",
4231
+ (event) => bridge.handleStepAttemptTerminal(event)
4232
+ ),
4233
+ "turn.cancelled": providerHandler(
4234
+ "turn.cancelled",
4235
+ bridge.handleTurnTerminal.bind(bridge)
4236
+ ),
4237
+ "turn.completed": providerHandler(
4238
+ "turn.completed",
4239
+ bridge.handleTurnTerminal.bind(bridge)
4240
+ ),
4241
+ "turn.failed": providerHandler(
4242
+ "turn.failed",
4243
+ bridge.handleTurnTerminal.bind(bridge)
4244
+ ),
4245
+ "turn.started": providerHandler(
4246
+ "turn.started",
4247
+ bridge.handleTurnStarted.bind(bridge)
4248
+ )
3840
4249
  },
3841
4250
  flush,
3842
4251
  setup: options.setup
@@ -4192,17 +4601,20 @@ function modelOutput(event) {
4192
4601
  const reasoning = [];
4193
4602
  const toolCalls2 = [];
4194
4603
  for (const part of content) {
4195
- if (part.type === "text") {
4196
- text += part.text;
4197
- } else if (part.type === "reasoning" && part.text.trim().length > 0) {
4198
- reasoning.push({ content: part.text });
4199
- } else if (part.type === "tool-call") {
4604
+ if (!isObject(part)) {
4605
+ continue;
4606
+ }
4607
+ if (part["type"] === "text" && typeof part["text"] === "string") {
4608
+ text += part["text"];
4609
+ } else if (part["type"] === "reasoning" && typeof part["text"] === "string" && part["text"].trim().length > 0) {
4610
+ reasoning.push({ content: part["text"] });
4611
+ } else if (part["type"] === "tool-call" && typeof part["toolName"] === "string" && typeof part["callId"] === "string") {
4200
4612
  toolCalls2.push({
4201
4613
  function: {
4202
- arguments: safeJsonStringify(part.input),
4203
- name: part.toolName
4614
+ arguments: safeJsonStringify(part["input"]),
4615
+ name: part["toolName"]
4204
4616
  },
4205
- id: part.callId,
4617
+ id: part["callId"],
4206
4618
  type: "function"
4207
4619
  });
4208
4620
  }
@@ -4221,13 +4633,13 @@ function modelOutput(event) {
4221
4633
  ];
4222
4634
  }
4223
4635
  function usageMetrics(usage) {
4224
- const promptTokens = nonNegativeNumber(usage.inputTokens);
4225
- const completionTokens = nonNegativeNumber(usage.outputTokens);
4636
+ const promptTokens = nonNegativeNumber(usage?.inputTokens);
4637
+ const completionTokens = nonNegativeNumber(usage?.outputTokens);
4226
4638
  const cachedTokens = nonNegativeNumber(
4227
- usage.inputTokenDetails?.cacheReadTokens
4639
+ usage?.inputTokenDetails?.cacheReadTokens
4228
4640
  );
4229
4641
  const cacheCreationTokens = nonNegativeNumber(
4230
- usage.inputTokenDetails?.cacheWriteTokens
4642
+ usage?.inputTokenDetails?.cacheWriteTokens
4231
4643
  );
4232
4644
  return {
4233
4645
  ...promptTokens !== void 0 ? { prompt_tokens: promptTokens } : {},
@@ -4264,10 +4676,76 @@ function turnIdempotencyKey(sessionId, turnId) {
4264
4676
  function actionIdempotencyKey(sessionId, turnId, callId) {
4265
4677
  return `action:${sessionId}:${turnId}:${callId}`;
4266
4678
  }
4679
+ function providerHandler(type, handler) {
4680
+ return async (event, context) => {
4681
+ try {
4682
+ if (!isEveProviderEvent(event, type) || !isEveProviderContext(context)) {
4683
+ debugLogger.warn(`Ignoring malformed Eve provider ${type} event`);
4684
+ return;
4685
+ }
4686
+ await handler(event, context);
4687
+ } catch (error) {
4688
+ debugLogger.warn(`Error in Eve provider ${type}:`, error);
4689
+ }
4690
+ };
4691
+ }
4692
+ function isEveProviderEvent(event, type) {
4693
+ if (!isObject(event) || event["type"] !== type || typeof event["idempotencyKey"] !== "string" || event["idempotencyKey"].length === 0) {
4694
+ return false;
4695
+ }
4696
+ if (type === "turn.started") {
4697
+ const parentLineage = event["parentLineage"];
4698
+ return typeof event["rootSessionId"] === "string" && typeof event["sessionId"] === "string" && typeof event["turnId"] === "string" && typeof event["sequence"] === "number" && Number.isFinite(event["sequence"]) && (parentLineage === void 0 || isEveProviderParentLineage(parentLineage));
4699
+ }
4700
+ if (type === "turn.cancelled" || type === "turn.completed" || type === "turn.failed") {
4701
+ return typeof event["sessionId"] === "string" && typeof event["turnId"] === "string";
4702
+ }
4703
+ if (!isEveProviderScope(event["scope"])) {
4704
+ return false;
4705
+ }
4706
+ if (type === "model.call.started") {
4707
+ const model = event["model"];
4708
+ return isObject(model) && typeof model["modelId"] === "string" && typeof model["provider"] === "string";
4709
+ }
4710
+ if (type === "model.call.completed") {
4711
+ return typeof event["finishReason"] === "string" && isObject(event["usage"]) && (event["content"] === void 0 || Array.isArray(event["content"]));
4712
+ }
4713
+ if (type === "model.call.failed") {
4714
+ return true;
4715
+ }
4716
+ if (type === "action.started") {
4717
+ return typeof event["callId"] === "string" && typeof event["name"] === "string" && (event["kind"] === "load-skill" || event["kind"] === "remote-agent-call" || event["kind"] === "subagent-call" || event["kind"] === "tool-call");
4718
+ }
4719
+ if (type === "action.completed") {
4720
+ const output = event["output"];
4721
+ return event["outcome"] === "completed" && isObject(output) && (output["type"] === "result" || output["type"] === "error");
4722
+ }
4723
+ if (type === "action.failed") {
4724
+ return event["outcome"] === "abandoned" || event["outcome"] === "cancelled" || event["outcome"] === "failed" || event["outcome"] === "rejected";
4725
+ }
4726
+ return type === "step.attempt.completed" || type === "step.attempt.failed";
4727
+ }
4728
+ function isEveProviderContext(value) {
4729
+ if (!isObject(value) || !isObject(value["state"])) {
4730
+ return false;
4731
+ }
4732
+ return typeof value["state"]["get"] === "function" && typeof value["state"]["set"] === "function";
4733
+ }
4734
+ function isEveProviderScope(value) {
4735
+ return isObject(value) && typeof value["attemptId"] === "string" && typeof value["attemptIndex"] === "number" && Number.isFinite(value["attemptIndex"]) && typeof value["sessionId"] === "string" && typeof value["stepIndex"] === "number" && Number.isFinite(value["stepIndex"]) && typeof value["turnId"] === "string" && (value["rootSessionId"] === void 0 || typeof value["rootSessionId"] === "string");
4736
+ }
4737
+ function isEveProviderParentLineage(value) {
4738
+ return isObject(value) && typeof value["callId"] === "string" && typeof value["sessionId"] === "string" && typeof value["turnId"] === "string";
4739
+ }
4267
4740
 
4268
4741
  // src/instrumentation/plugins/eve-instrumentation.ts
4269
4742
  var EVE_INSTRUMENTATION_PROVIDER = /* @__PURE__ */ Symbol.for("eve.instrumentation.provider");
4270
4743
  function braintrustEveInstrumentation(options) {
4744
+ if (!options || typeof options !== "object") {
4745
+ throw new TypeError(
4746
+ "braintrustEveInstrumentation requires an options object"
4747
+ );
4748
+ }
4271
4749
  const definition = "defineState" in options ? createLegacyEveInstrumentation(options) : createEveInstrumentationProvider(options);
4272
4750
  const declaration = {
4273
4751
  ...definition,
@@ -5214,7 +5692,77 @@ function isModuleNamespace5(value) {
5214
5692
  }
5215
5693
  }
5216
5694
 
5695
+ // src/wrappers/google-generative-ai.ts
5696
+ function wrapGoogleGenerativeAI(client) {
5697
+ if (!isObject(client) || typeof client.getGenerativeModel !== "function")
5698
+ return client;
5699
+ return wrapClient(client);
5700
+ }
5701
+ var proxies = /* @__PURE__ */ new WeakMap();
5702
+ function wrapClient(client) {
5703
+ const cached = proxies.get(client);
5704
+ if (cached) return cached;
5705
+ const proxy = new Proxy(client, {
5706
+ get(target, prop, receiver) {
5707
+ const value = Reflect.get(target, prop, receiver);
5708
+ if (typeof value !== "function") return value;
5709
+ if (prop === "getGenerativeModel" || prop === "getGenerativeModelFromCachedContent" || prop === "startChat") {
5710
+ return (...args) => wrapClient(Reflect.apply(value, target, args));
5711
+ }
5712
+ switch (prop) {
5713
+ case "generateContent":
5714
+ return (...args) => googleGenerativeAIChannels.generateContent.invoke(
5715
+ value,
5716
+ target,
5717
+ args,
5718
+ {}
5719
+ );
5720
+ case "generateContentStream":
5721
+ return (...args) => googleGenerativeAIChannels.generateContentStream.invoke(
5722
+ value,
5723
+ target,
5724
+ args,
5725
+ {}
5726
+ );
5727
+ case "embedContent":
5728
+ return (...args) => googleGenerativeAIChannels.embedContent.invoke(
5729
+ value,
5730
+ target,
5731
+ args,
5732
+ {}
5733
+ );
5734
+ case "batchEmbedContents":
5735
+ return (...args) => googleGenerativeAIChannels.batchEmbedContents.invoke(
5736
+ value,
5737
+ target,
5738
+ args,
5739
+ {}
5740
+ );
5741
+ case "sendMessage":
5742
+ return (...args) => googleGenerativeAIChannels.sendMessage.invoke(
5743
+ value,
5744
+ target,
5745
+ args,
5746
+ {}
5747
+ );
5748
+ case "sendMessageStream":
5749
+ return (...args) => googleGenerativeAIChannels.sendMessageStream.invoke(
5750
+ value,
5751
+ target,
5752
+ args,
5753
+ {}
5754
+ );
5755
+ }
5756
+ return value.bind(target);
5757
+ }
5758
+ });
5759
+ proxies.set(client, proxy);
5760
+ proxies.set(proxy, proxy);
5761
+ return proxy;
5762
+ }
5763
+
5217
5764
  // src/wrappers/google-genai.ts
5765
+ var patchedHttpResponses = /* @__PURE__ */ new WeakSet();
5218
5766
  function wrapGoogleGenAI(googleGenAI) {
5219
5767
  if (!googleGenAI || typeof googleGenAI !== "object") {
5220
5768
  console.warn("Invalid Google GenAI module. Not wrapping.");
@@ -5226,6 +5774,21 @@ function wrapGoogleGenAI(googleGenAI) {
5226
5774
  );
5227
5775
  return googleGenAI;
5228
5776
  }
5777
+ const httpResponse = googleGenAI.HttpResponse;
5778
+ if (typeof httpResponse === "function" && isObject(httpResponse.prototype) && typeof httpResponse.prototype.json === "function" && !patchedHttpResponses.has(httpResponse.prototype)) {
5779
+ const prototype = httpResponse.prototype;
5780
+ const originalJson = prototype.json;
5781
+ if (Reflect.set(prototype, "json", function() {
5782
+ return googleGenAIChannels.httpResponseJson.invoke(
5783
+ originalJson,
5784
+ this,
5785
+ [],
5786
+ {}
5787
+ );
5788
+ })) {
5789
+ patchedHttpResponses.add(prototype);
5790
+ }
5791
+ }
5229
5792
  return new Proxy(googleGenAI, {
5230
5793
  get(target, prop, receiver) {
5231
5794
  if (prop === "GoogleGenAI") {
@@ -5476,17 +6039,17 @@ function wrapGenkit(genkit) {
5476
6039
  console.warn("Unsupported Genkit object. Not wrapping.");
5477
6040
  return genkit;
5478
6041
  }
5479
- function isRecord(value) {
6042
+ function isRecord2(value) {
5480
6043
  return typeof value === "object" && value !== null;
5481
6044
  }
5482
6045
  function isPropertyBag(value) {
5483
- return isRecord(value) || typeof value === "function";
6046
+ return isRecord2(value) || typeof value === "function";
5484
6047
  }
5485
6048
  function hasFunction(value, methodName) {
5486
6049
  return isPropertyBag(value) && methodName in value && typeof value[methodName] === "function";
5487
6050
  }
5488
6051
  function isGenkitInstance(value) {
5489
- return isRecord(value) && (hasFunction(value, "generate") || hasFunction(value, "generateStream") || hasFunction(value, "defineFlow") || hasFunction(value, "defineTool"));
6052
+ return isRecord2(value) && (hasFunction(value, "generate") || hasFunction(value, "generateStream") || hasFunction(value, "defineFlow") || hasFunction(value, "defineTool"));
5490
6053
  }
5491
6054
  function isGenkitModule(value) {
5492
6055
  return hasFunction(value, "genkit");
@@ -5540,7 +6103,7 @@ function patchGenkitRegistry(instance) {
5540
6103
  patchGenkitRegistryConstructor(registry);
5541
6104
  }
5542
6105
  function patchGenkitRegistryLookup(registry) {
5543
- if (!isRecord(registry) || hasRegistryPatchedFlag(registry) || !hasFunction(registry, "lookupAction")) {
6106
+ if (!isRecord2(registry) || hasRegistryPatchedFlag(registry) || !hasFunction(registry, "lookupAction")) {
5544
6107
  return;
5545
6108
  }
5546
6109
  const originalLookupAction = registry.lookupAction;
@@ -5566,7 +6129,7 @@ function patchGenkitRegistryLookup(registry) {
5566
6129
  }
5567
6130
  }
5568
6131
  function patchGenkitRegistryConstructor(registry) {
5569
- if (!isRecord(registry)) {
6132
+ if (!isRecord2(registry)) {
5570
6133
  return;
5571
6134
  }
5572
6135
  const constructor = registry.constructor;
@@ -5582,7 +6145,7 @@ function patchGenkitRegistryConstructor(registry) {
5582
6145
  configurable: true,
5583
6146
  value: (...args) => {
5584
6147
  const childRegistry = originalWithParent.apply(constructor, args);
5585
- if (args.some((arg) => isRecord(arg) && hasRegistryPatchedFlag(arg))) {
6148
+ if (args.some((arg) => isRecord2(arg) && hasRegistryPatchedFlag(arg))) {
5586
6149
  patchGenkitRegistryLookup(childRegistry);
5587
6150
  patchGenkitRegistryConstructor(childRegistry);
5588
6151
  }
@@ -5681,7 +6244,7 @@ function hasRegistryConstructorPatchedFlag(value) {
5681
6244
  );
5682
6245
  }
5683
6246
  function isPromiseLike(value) {
5684
- return isRecord(value) && "then" in value && typeof value.then === "function";
6247
+ return isRecord2(value) && "then" in value && typeof value.then === "function";
5685
6248
  }
5686
6249
 
5687
6250
  // src/wrappers/huggingface.ts
@@ -6158,14 +6721,14 @@ function wrapMistral(mistral) {
6158
6721
  console.warn("Unsupported Mistral library. Not wrapping.");
6159
6722
  return mistral;
6160
6723
  }
6161
- function isRecord2(value) {
6724
+ function isRecord3(value) {
6162
6725
  return typeof value === "object" && value !== null;
6163
6726
  }
6164
6727
  function hasFunction3(value, methodName) {
6165
- return isRecord2(value) && methodName in value && typeof value[methodName] === "function";
6728
+ return isRecord3(value) && methodName in value && typeof value[methodName] === "function";
6166
6729
  }
6167
6730
  function isSupportedMistralClient(value) {
6168
- if (!isRecord2(value)) {
6731
+ if (!isRecord3(value)) {
6169
6732
  return false;
6170
6733
  }
6171
6734
  return value.chat !== void 0 && hasChat(value.chat) || value.embeddings !== void 0 && hasEmbeddings(value.embeddings) || value.fim !== void 0 && hasFim(value.fim) || value.agents !== void 0 && hasAgents(value.agents) || value.classifiers !== void 0 && hasClassifiers(value.classifiers);
@@ -6340,17 +6903,53 @@ function wrapAgentsStream(stream) {
6340
6903
  });
6341
6904
  }
6342
6905
 
6343
- // src/wrappers/ollama.ts
6344
- function wrapOllama(ollama) {
6345
- if (isSupportedOllamaClient(ollama)) {
6346
- return ollamaProxy(ollama);
6906
+ // src/wrappers/langgraph-sdk.ts
6907
+ var clients = /* @__PURE__ */ new WeakMap();
6908
+ function wrapLangGraphSDK(client) {
6909
+ const runsClient = isObject(client) ? client.runs : void 0;
6910
+ if (!isObject(client) || !isObject(runsClient) || !["wait", "stream"].every((key) => typeof runsClient[key] === "function")) {
6911
+ debugLogger.warn("Unsupported LangGraph SDK client. Not wrapping.");
6912
+ return client;
6347
6913
  }
6348
- debugLogger.warn("Unsupported Ollama library. Not wrapping.");
6349
- return ollama;
6350
- }
6351
- var ollamaProxyCache = /* @__PURE__ */ new WeakMap();
6352
- function isSupportedOllamaClient(value) {
6353
- return isObject(value) && ["chat", "generate", "embed"].some(
6914
+ const sdk = client;
6915
+ const cached = clients.get(sdk);
6916
+ if (cached) return cached;
6917
+ const runs = new Proxy(sdk.runs, {
6918
+ get(target, key) {
6919
+ switch (key) {
6920
+ case "wait":
6921
+ return (...args) => langGraphSDKChannels.wait.invoke(target.wait, target, args, {});
6922
+ case "stream":
6923
+ return (...args) => langGraphSDKChannels.stream.invoke(target.stream, target, args, {});
6924
+ default:
6925
+ const value = Reflect.get(target, key, target);
6926
+ return typeof value === "function" ? value.bind(target) : value;
6927
+ }
6928
+ }
6929
+ });
6930
+ const wrapped = new Proxy(sdk, {
6931
+ get(target, key) {
6932
+ if (key === "runs") return runs;
6933
+ const value = Reflect.get(target, key, target);
6934
+ return typeof value === "function" ? value.bind(target) : value;
6935
+ }
6936
+ });
6937
+ clients.set(sdk, wrapped);
6938
+ clients.set(wrapped, wrapped);
6939
+ return wrapped;
6940
+ }
6941
+
6942
+ // src/wrappers/ollama.ts
6943
+ function wrapOllama(ollama) {
6944
+ if (isSupportedOllamaClient(ollama)) {
6945
+ return ollamaProxy(ollama);
6946
+ }
6947
+ debugLogger.warn("Unsupported Ollama library. Not wrapping.");
6948
+ return ollama;
6949
+ }
6950
+ var ollamaProxyCache = /* @__PURE__ */ new WeakMap();
6951
+ function isSupportedOllamaClient(value) {
6952
+ return isObject(value) && ["chat", "generate", "embed"].some(
6354
6953
  (name) => typeof value[name] === "function"
6355
6954
  );
6356
6955
  }
@@ -6426,14 +7025,14 @@ function wrapCohere(cohere) {
6426
7025
  return cohere;
6427
7026
  }
6428
7027
  var cohereProxyCache = /* @__PURE__ */ new WeakMap();
6429
- function isRecord3(value) {
7028
+ function isRecord4(value) {
6430
7029
  return typeof value === "object" && value !== null;
6431
7030
  }
6432
7031
  function hasFunction4(value, methodName) {
6433
- return isRecord3(value) && methodName in value && typeof value[methodName] === "function";
7032
+ return isRecord4(value) && methodName in value && typeof value[methodName] === "function";
6434
7033
  }
6435
7034
  function isSupportedCohereClient(value) {
6436
- if (!isRecord3(value)) {
7035
+ if (!isRecord4(value)) {
6437
7036
  return false;
6438
7037
  }
6439
7038
  return hasFunction4(value, "chat") || hasFunction4(value, "chatStream") || hasFunction4(value, "embed") || hasFunction4(value, "rerank");
@@ -6571,20 +7170,20 @@ function wrapGroq(groq) {
6571
7170
  console.warn("Unsupported Groq library. Not wrapping.");
6572
7171
  return groq;
6573
7172
  }
6574
- function isRecord4(value) {
7173
+ function isRecord5(value) {
6575
7174
  return typeof value === "object" && value !== null;
6576
7175
  }
6577
7176
  function hasFunction6(value, methodName) {
6578
- return isRecord4(value) && methodName in value && typeof value[methodName] === "function";
7177
+ return isRecord5(value) && methodName in value && typeof value[methodName] === "function";
6579
7178
  }
6580
7179
  function hasChat2(value) {
6581
- return isRecord4(value) && isRecord4(value.completions) && hasFunction6(value.completions, "create");
7180
+ return isRecord5(value) && isRecord5(value.completions) && hasFunction6(value.completions, "create");
6582
7181
  }
6583
7182
  function hasEmbeddings2(value) {
6584
7183
  return hasFunction6(value, "create");
6585
7184
  }
6586
7185
  function isSupportedGroqClient(value) {
6587
- return isRecord4(value) && (value.chat !== void 0 && hasChat2(value.chat) || value.embeddings !== void 0 && hasEmbeddings2(value.embeddings));
7186
+ return isRecord5(value) && (value.chat !== void 0 && hasChat2(value.chat) || value.embeddings !== void 0 && hasEmbeddings2(value.embeddings));
6588
7187
  }
6589
7188
  function groqProxy(groq) {
6590
7189
  const privateMethodWorkaroundCache = /* @__PURE__ */ new WeakMap();
@@ -6667,11 +7266,11 @@ var BEDROCK_RUNTIME_OPERATION_METHODS = /* @__PURE__ */ new Set([
6667
7266
  "invokeModelWithBidirectionalStream",
6668
7267
  "invokeModelWithResponseStream"
6669
7268
  ]);
6670
- function isRecord5(value) {
7269
+ function isRecord6(value) {
6671
7270
  return typeof value === "object" && value !== null;
6672
7271
  }
6673
7272
  function isSupportedBedrockRuntimeClient(value) {
6674
- return isRecord5(value) && typeof value.send === "function";
7273
+ return isRecord6(value) && typeof value.send === "function";
6675
7274
  }
6676
7275
  function bedrockRuntimeProxy(client) {
6677
7276
  const cached = bedrockRuntimeProxyCache.get(client);
@@ -8956,12 +9555,272 @@ function waterfall(tasks, callback) {
8956
9555
  }
8957
9556
  var waterfall$1 = awaitify(waterfall);
8958
9557
 
9558
+ // src/framework.ts
9559
+ import { v5 as uuidv5 } from "uuid";
9560
+
8959
9561
  // src/trace.ts
9562
+ var spanFilterFields = /* @__PURE__ */ new Set([
9563
+ "spanType",
9564
+ "name",
9565
+ "hasError",
9566
+ "metadata",
9567
+ "duration"
9568
+ ]);
9569
+ function isRecord7(value) {
9570
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
9571
+ return false;
9572
+ }
9573
+ const prototype = Object.getPrototypeOf(value);
9574
+ return prototype === Object.prototype || prototype === null;
9575
+ }
9576
+ function validateMetadataValue(value, ancestors) {
9577
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
9578
+ return;
9579
+ }
9580
+ if (typeof value === "number") {
9581
+ if (Number.isFinite(value)) {
9582
+ return;
9583
+ }
9584
+ throw new Error("filters.metadata numbers must be finite");
9585
+ }
9586
+ if (typeof value !== "object" || value === null) {
9587
+ throw new Error("filters.metadata values must be JSON-serializable");
9588
+ }
9589
+ if (!Array.isArray(value) && !isRecord7(value)) {
9590
+ throw new Error("filters.metadata values must be JSON-serializable");
9591
+ }
9592
+ if (ancestors.has(value)) {
9593
+ throw new Error("filters.metadata must not contain cycles");
9594
+ }
9595
+ ancestors.add(value);
9596
+ if (Array.isArray(value)) {
9597
+ for (let index = 0; index < value.length; index++) {
9598
+ validateMetadataValue(value[index], ancestors);
9599
+ }
9600
+ } else {
9601
+ if (Object.getOwnPropertySymbols(value).length > 0) {
9602
+ throw new Error("filters.metadata keys must be strings");
9603
+ }
9604
+ for (const nested of Object.values(value)) {
9605
+ validateMetadataValue(nested, ancestors);
9606
+ }
9607
+ }
9608
+ ancestors.delete(value);
9609
+ }
9610
+ function compileMetadataLeaves(metadata, path = [], ancestors = /* @__PURE__ */ new WeakSet()) {
9611
+ if (Object.getOwnPropertySymbols(metadata).length > 0) {
9612
+ throw new Error("filters.metadata keys must be strings");
9613
+ }
9614
+ if (ancestors.has(metadata)) {
9615
+ throw new Error("filters.metadata must not contain cycles");
9616
+ }
9617
+ ancestors.add(metadata);
9618
+ const leaves = [];
9619
+ for (const [key, value] of Object.entries(metadata)) {
9620
+ const childPath = [...path, key];
9621
+ if (isRecord7(value)) {
9622
+ leaves.push(...compileMetadataLeaves(value, childPath, ancestors));
9623
+ } else {
9624
+ validateMetadataValue(value, ancestors);
9625
+ leaves.push([childPath, value]);
9626
+ }
9627
+ }
9628
+ ancestors.delete(metadata);
9629
+ return leaves;
9630
+ }
9631
+ function normalizeSpanFilters(filters, spanType) {
9632
+ if (filters != null && !isRecord7(filters)) {
9633
+ throw new Error("filters must be an object");
9634
+ }
9635
+ const values = { ...filters ?? {} };
9636
+ if (spanType !== void 0) {
9637
+ if (!Array.isArray(spanType) || !spanType.every((item) => typeof item === "string")) {
9638
+ throw new Error("spanType must be an array of strings");
9639
+ }
9640
+ if (Object.hasOwn(values, "spanType")) {
9641
+ throw new Error(
9642
+ "spanType cannot be provided both directly and in filters"
9643
+ );
9644
+ }
9645
+ if (spanType.length > 0) {
9646
+ values.spanType = spanType;
9647
+ }
9648
+ }
9649
+ if (Object.keys(values).some((field) => !spanFilterFields.has(field))) {
9650
+ throw new Error("Unsupported span filter fields");
9651
+ }
9652
+ for (const field of ["spanType", "name"]) {
9653
+ if (Object.hasOwn(values, field)) {
9654
+ const items = values[field];
9655
+ if (!Array.isArray(items) || !items.every((item) => typeof item === "string")) {
9656
+ throw new Error(`filters.${field} must be an array of strings`);
9657
+ }
9658
+ }
9659
+ }
9660
+ if (Object.hasOwn(values, "hasError") && typeof values.hasError !== "boolean") {
9661
+ throw new Error("filters.hasError must be a boolean");
9662
+ }
9663
+ let compiledMetadataLeaves = [];
9664
+ if (Object.hasOwn(values, "metadata")) {
9665
+ if (!isRecord7(values.metadata)) {
9666
+ throw new Error("filters.metadata must be an object");
9667
+ }
9668
+ compiledMetadataLeaves = compileMetadataLeaves(values.metadata);
9669
+ }
9670
+ if (Object.hasOwn(values, "duration")) {
9671
+ if (!isRecord7(values.duration) || Object.keys(values.duration).some(
9672
+ (bound) => bound !== "min" && bound !== "max"
9673
+ )) {
9674
+ throw new Error("filters.duration must contain only min and/or max");
9675
+ }
9676
+ if (Object.values(values.duration).some(
9677
+ (bound) => typeof bound !== "number" || !Number.isFinite(bound)
9678
+ )) {
9679
+ throw new Error("filters.duration bounds must be finite numbers");
9680
+ }
9681
+ }
9682
+ const normalized = {};
9683
+ if (Array.isArray(values.spanType)) {
9684
+ normalized.spanType = values.spanType;
9685
+ }
9686
+ if (Array.isArray(values.name)) {
9687
+ normalized.name = values.name;
9688
+ }
9689
+ if (typeof values.hasError === "boolean") {
9690
+ normalized.hasError = values.hasError;
9691
+ }
9692
+ if (isRecord7(values.metadata)) {
9693
+ normalized.metadata = values.metadata;
9694
+ }
9695
+ if (isRecord7(values.duration)) {
9696
+ normalized.duration = {};
9697
+ if (typeof values.duration.min === "number") {
9698
+ normalized.duration.min = values.duration.min;
9699
+ }
9700
+ if (typeof values.duration.max === "number") {
9701
+ normalized.duration.max = values.duration.max;
9702
+ }
9703
+ }
9704
+ return { filters: normalized, metadataLeaves: compiledMetadataLeaves };
9705
+ }
9706
+ function metadataEqual(actual, expected) {
9707
+ if (expected === null) {
9708
+ return actual === null || actual === void 0;
9709
+ }
9710
+ if (Array.isArray(expected)) {
9711
+ return Array.isArray(actual) && actual.length === expected.length && expected.every((value, index) => metadataEqual(actual[index], value));
9712
+ }
9713
+ if (isRecord7(expected)) {
9714
+ if (!isRecord7(actual)) {
9715
+ return false;
9716
+ }
9717
+ const expectedKeys = Object.keys(expected);
9718
+ const actualKeys = Object.keys(actual);
9719
+ return actualKeys.length === expectedKeys.length && expectedKeys.every(
9720
+ (key) => Object.hasOwn(actual, key) && metadataEqual(actual[key], expected[key])
9721
+ );
9722
+ }
9723
+ return actual === expected;
9724
+ }
9725
+ function matchesSpanFilters(span, compiledFilters) {
9726
+ const { filters, metadataLeaves } = compiledFilters;
9727
+ if (filters.spanType !== void 0 && !filters.spanType.includes(span.span_attributes?.type ?? "")) {
9728
+ return false;
9729
+ }
9730
+ if (filters.name !== void 0 && !filters.name.includes(span.span_attributes?.name ?? "")) {
9731
+ return false;
9732
+ }
9733
+ if (filters.hasError !== void 0 && (span.error !== null && span.error !== void 0) !== filters.hasError) {
9734
+ return false;
9735
+ }
9736
+ for (const [path, expected] of metadataLeaves) {
9737
+ let actual = span.metadata;
9738
+ for (const key of path) {
9739
+ actual = isRecord7(actual) && Object.hasOwn(actual, key) ? actual[key] : void 0;
9740
+ }
9741
+ if (!metadataEqual(actual, expected)) {
9742
+ return false;
9743
+ }
9744
+ }
9745
+ if (filters.duration && Object.keys(filters.duration).length > 0) {
9746
+ const metrics = span.metrics;
9747
+ const start = isRecord7(metrics) ? metrics.start : void 0;
9748
+ const end = isRecord7(metrics) ? metrics.end : void 0;
9749
+ if (typeof start !== "number" || !Number.isFinite(start) || typeof end !== "number" || !Number.isFinite(end)) {
9750
+ return false;
9751
+ }
9752
+ const elapsed = end - start;
9753
+ if (filters.duration.min !== void 0 && elapsed < filters.duration.min) {
9754
+ return false;
9755
+ }
9756
+ if (filters.duration.max !== void 0 && elapsed > filters.duration.max) {
9757
+ return false;
9758
+ }
9759
+ }
9760
+ return true;
9761
+ }
9762
+ function btqlComparison(op, name, value) {
9763
+ return {
9764
+ op,
9765
+ left: { op: "ident", name },
9766
+ right: { op: "literal", value }
9767
+ };
9768
+ }
9769
+ function btqlNullCheck(op, name) {
9770
+ return { op, expr: { op: "ident", name } };
9771
+ }
9772
+ function spanFilterClauses(compiledFilters) {
9773
+ const { filters, metadataLeaves } = compiledFilters;
9774
+ const children = [];
9775
+ for (const [field, attribute] of [
9776
+ ["spanType", "type"],
9777
+ ["name", "name"]
9778
+ ]) {
9779
+ const values = filters[field];
9780
+ if (values !== void 0) {
9781
+ children.push(
9782
+ values.length > 0 ? btqlComparison("in", ["span_attributes", attribute], values) : { op: "literal", value: false }
9783
+ );
9784
+ }
9785
+ }
9786
+ if (filters.hasError !== void 0) {
9787
+ children.push(
9788
+ btqlNullCheck(filters.hasError ? "isnotnull" : "isnull", ["error"])
9789
+ );
9790
+ }
9791
+ for (const [path, value] of metadataLeaves) {
9792
+ const name = ["metadata", ...path];
9793
+ children.push(
9794
+ value === null ? btqlNullCheck("isnull", name) : btqlComparison("eq", name, value)
9795
+ );
9796
+ }
9797
+ const elapsed = {
9798
+ op: "sub",
9799
+ left: { op: "ident", name: ["metrics", "end"] },
9800
+ right: { op: "ident", name: ["metrics", "start"] }
9801
+ };
9802
+ if (filters.duration?.min !== void 0) {
9803
+ children.push({
9804
+ op: "ge",
9805
+ left: elapsed,
9806
+ right: { op: "literal", value: filters.duration.min }
9807
+ });
9808
+ }
9809
+ if (filters.duration?.max !== void 0) {
9810
+ children.push({
9811
+ op: "le",
9812
+ left: elapsed,
9813
+ right: { op: "literal", value: filters.duration.max }
9814
+ });
9815
+ }
9816
+ return children;
9817
+ }
8960
9818
  var SpanFetcher = class _SpanFetcher extends ObjectFetcher {
8961
- constructor(objectType, _objectId, rootSpanId, _state, spanTypeFilter, includeScorers = false, brainstoreRealtime = true) {
9819
+ constructor(objectType, _objectId, rootSpanId, _state, spanTypeFilter, includeScorers = false, brainstoreRealtime = true, filters) {
9820
+ const normalizedFilters = normalizeSpanFilters(filters, spanTypeFilter);
8962
9821
  const filterExpr = _SpanFetcher.buildFilter(
8963
9822
  rootSpanId,
8964
- spanTypeFilter,
9823
+ normalizedFilters,
8965
9824
  includeScorers
8966
9825
  );
8967
9826
  super(
@@ -8982,42 +9841,22 @@ var SpanFetcher = class _SpanFetcher extends ObjectFetcher {
8982
9841
  rootSpanId;
8983
9842
  _state;
8984
9843
  spanTypeFilter;
8985
- static buildFilter(rootSpanId, spanTypeFilter, includeScorers = false) {
9844
+ static buildFilter(rootSpanId, filters, includeScorers = false) {
9845
+ const purpose = ["span_attributes", "purpose"];
8986
9846
  const children = [
8987
- // Base filter: root_span_id = 'value'
8988
- {
8989
- op: "eq",
8990
- left: { op: "ident", name: ["root_span_id"] },
8991
- right: { op: "literal", value: rootSpanId }
8992
- }
9847
+ btqlComparison("eq", ["root_span_id"], rootSpanId)
8993
9848
  ];
8994
9849
  if (!includeScorers) {
8995
9850
  children.push({
8996
9851
  op: "or",
8997
9852
  children: [
8998
- {
8999
- op: "isnull",
9000
- expr: { op: "ident", name: ["span_attributes", "purpose"] }
9001
- },
9002
- {
9003
- op: "ne",
9004
- left: { op: "ident", name: ["span_attributes", "purpose"] },
9005
- right: { op: "literal", value: "scorer" }
9006
- }
9853
+ btqlNullCheck("isnull", purpose),
9854
+ btqlComparison("ne", purpose, "scorer")
9007
9855
  ]
9008
9856
  });
9009
9857
  }
9010
- if (spanTypeFilter && spanTypeFilter.length > 0) {
9011
- children.push({
9012
- op: "in",
9013
- left: { op: "ident", name: ["span_attributes", "type"] },
9014
- right: { op: "literal", value: spanTypeFilter }
9015
- });
9016
- }
9017
- return {
9018
- op: "and",
9019
- children
9020
- };
9858
+ children.push(...spanFilterClauses(filters));
9859
+ return { op: "and", children };
9021
9860
  }
9022
9861
  get id() {
9023
9862
  return Promise.resolve(this._objectId);
@@ -9032,67 +9871,80 @@ var CachedSpanFetcher = class {
9032
9871
  fetchFn;
9033
9872
  constructor(objectTypeOrFetchFn, objectId, rootSpanId, getState, brainstoreRealtime = true) {
9034
9873
  if (typeof objectTypeOrFetchFn === "function") {
9035
- this.fetchFn = (spanType) => objectTypeOrFetchFn(spanType);
9874
+ this.fetchFn = async (filters) => (await objectTypeOrFetchFn(filters.filters.spanType)).filter(
9875
+ (span) => matchesSpanFilters(span, filters)
9876
+ );
9036
9877
  } else {
9037
9878
  const objectType = objectTypeOrFetchFn;
9038
- this.fetchFn = async (spanType, includeScorers) => {
9879
+ this.fetchFn = async (filters, includeScorers) => {
9039
9880
  const state = await getState();
9040
9881
  const fetcher = new SpanFetcher(
9041
9882
  objectType,
9042
9883
  objectId,
9043
9884
  rootSpanId,
9044
9885
  state,
9045
- spanType,
9886
+ void 0,
9046
9887
  includeScorers,
9047
- brainstoreRealtime
9888
+ brainstoreRealtime,
9889
+ filters.filters
9048
9890
  );
9049
- const rows = await fetcher.fetchedData();
9050
- return rows.map((row) => ({
9051
- input: row.input,
9052
- output: row.output,
9053
- expected: row.expected,
9054
- error: row.error,
9055
- scores: row.scores,
9056
- metrics: row.metrics,
9057
- metadata: row.metadata,
9058
- span_id: row.span_id,
9059
- span_parents: row.span_parents,
9060
- is_root: row.is_root,
9061
- span_attributes: row.span_attributes,
9062
- id: row.id,
9063
- _xact_id: row._xact_id,
9064
- _pagination_key: row._pagination_key,
9065
- root_span_id: row.root_span_id,
9066
- created: row.created,
9067
- tags: row.tags
9068
- }));
9891
+ let spans = (await fetcher.fetchedData()).map(
9892
+ (row) => ({ ...row })
9893
+ );
9894
+ if (filters.filters.metadata !== void 0) {
9895
+ spans = spans.filter((span) => matchesSpanFilters(span, filters));
9896
+ }
9897
+ return spans;
9069
9898
  };
9070
9899
  }
9071
9900
  }
9072
9901
  async getSpans({
9073
- spanType,
9902
+ spanType: requestedSpanType,
9903
+ filters,
9074
9904
  includeScorers = false
9075
9905
  } = {}) {
9906
+ const normalizedFilters = normalizeSpanFilters(filters, requestedSpanType);
9907
+ const spanType = normalizedFilters.filters.spanType;
9908
+ const hasAdvancedFilters = Object.keys(normalizedFilters.filters).some(
9909
+ (field) => field !== "spanType"
9910
+ );
9911
+ if (spanType?.length === 0) {
9912
+ return [];
9913
+ }
9076
9914
  if (includeScorers) {
9077
- return this.fetchFn(spanType, true);
9915
+ return this.fetchFn(normalizedFilters, true);
9078
9916
  }
9079
9917
  if (this.allFetched) {
9080
- return this.getFromCache(spanType);
9918
+ const spans = this.getFromCache(spanType);
9919
+ return hasAdvancedFilters ? spans.filter((span) => matchesSpanFilters(span, normalizedFilters)) : spans;
9081
9920
  }
9082
- if (!spanType || spanType.length === 0) {
9921
+ if (hasAdvancedFilters && spanType && spanType.every((type) => this.spanCache.has(type))) {
9922
+ return this.getFromCache(spanType).filter(
9923
+ (span) => matchesSpanFilters(span, normalizedFilters)
9924
+ );
9925
+ }
9926
+ if (hasAdvancedFilters) {
9927
+ return this.fetchFn(normalizedFilters, false);
9928
+ }
9929
+ if (!spanType) {
9930
+ this.spanCache.clear();
9083
9931
  await this.fetchSpans(void 0);
9084
- this.allFetched = true;
9932
+ if (this.spanCache.size > 0) {
9933
+ this.allFetched = true;
9934
+ }
9085
9935
  return this.getFromCache(void 0);
9086
9936
  }
9087
- const missingTypes = spanType.filter((t) => !this.spanCache.has(t));
9088
- if (missingTypes.length === 0) {
9089
- return this.getFromCache(spanType);
9937
+ const missingTypes = spanType.filter((type) => !this.spanCache.has(type));
9938
+ if (missingTypes.length > 0) {
9939
+ await this.fetchSpans(missingTypes);
9090
9940
  }
9091
- await this.fetchSpans(missingTypes);
9092
9941
  return this.getFromCache(spanType);
9093
9942
  }
9094
9943
  async fetchSpans(spanType) {
9095
- const spans = await this.fetchFn(spanType, false);
9944
+ const spans = await this.fetchFn(
9945
+ normalizeSpanFilters(spanType ? { spanType } : void 0),
9946
+ false
9947
+ );
9096
9948
  for (const span of spans) {
9097
9949
  const type = span.span_attributes?.type ?? "";
9098
9950
  const existing = this.spanCache.get(type) ?? [];
@@ -9172,34 +10024,20 @@ var LocalTrace = class {
9172
10024
  */
9173
10025
  async getSpans({
9174
10026
  spanType,
10027
+ filters,
9175
10028
  includeScorers = false
9176
10029
  } = {}) {
10030
+ const normalizedFilters = normalizeSpanFilters(filters, spanType);
9177
10031
  const cachedSpans = this.state.spanCache.getByRootSpanId(this.rootSpanId);
9178
10032
  if (cachedSpans && cachedSpans.length > 0) {
9179
- let spans = includeScorers ? cachedSpans : cachedSpans.filter(
9180
- (span) => span.span_attributes?.purpose !== "scorer"
9181
- );
9182
- if (spanType && spanType.length > 0) {
9183
- spans = spans.filter(
9184
- (span) => spanType.includes(span.span_attributes?.type ?? "")
9185
- );
9186
- }
9187
- return spans.map((span) => ({
9188
- input: span.input,
9189
- output: span.output,
9190
- expected: span.expected,
9191
- error: span.error,
9192
- scores: span.scores,
9193
- metrics: span.metrics,
9194
- metadata: span.metadata,
9195
- span_id: span.span_id,
9196
- span_parents: span.span_parents,
9197
- is_root: span.is_root,
9198
- span_attributes: span.span_attributes,
9199
- tags: span.tags
9200
- }));
9201
- }
9202
- return this.cachedFetcher.getSpans({ spanType, includeScorers });
10033
+ return cachedSpans.filter(
10034
+ (span) => (includeScorers || span.span_attributes?.purpose !== "scorer") && matchesSpanFilters({ ...span }, normalizedFilters)
10035
+ ).map((span) => ({ ...span }));
10036
+ }
10037
+ return this.cachedFetcher.getSpans({
10038
+ filters: normalizedFilters.filters,
10039
+ includeScorers
10040
+ });
9203
10041
  }
9204
10042
  /**
9205
10043
  * Get the thread (preprocessed messages) for this trace.
@@ -9619,7 +10457,7 @@ async function Eval(name, evaluator, reporterOrOpts) {
9619
10457
  data,
9620
10458
  { disabled: Boolean(options.parent || options.noSendLogs) }
9621
10459
  );
9622
- if (experiment && typeof process !== "undefined" && globalThis.BRAINTRUST_CONTEXT_MANAGER !== void 0) {
10460
+ if (experiment) {
9623
10461
  await experiment._waitForId();
9624
10462
  }
9625
10463
  if (experiment && options.onStart) {
@@ -9944,6 +10782,10 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
9944
10782
  } : void 0;
9945
10783
  const parsedDatumOrigin = ObjectReference.safeParse(datum.origin);
9946
10784
  const origin = inlineDatasetOrigin ?? (parsedDatumOrigin?.success ? parsedDatumOrigin.data : void 0);
10785
+ const upsertId = datum.upsert_id && trialIndex > 0 ? uuidv5(
10786
+ `braintrust:eval:${datum.upsert_id}:trial:${trialIndex}`,
10787
+ uuidv5.URL
10788
+ ) : datum.upsert_id;
9947
10789
  const baseEvent = {
9948
10790
  name: "eval",
9949
10791
  spanAttributes: {
@@ -9954,7 +10796,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
9954
10796
  expected: "expected" in datum ? datum.expected : void 0,
9955
10797
  tags: datum.tags,
9956
10798
  origin,
9957
- ...datum.upsert_id ? { id: datum.upsert_id } : {}
10799
+ ...upsertId ? { id: upsertId } : {}
9958
10800
  }
9959
10801
  };
9960
10802
  const callback = async (rootSpan) => {
@@ -10463,14 +11305,14 @@ var defaultReporter = {
10463
11305
  }
10464
11306
  };
10465
11307
 
10466
- // src/durable-eval.ts
11308
+ // src/workflow-eval.ts
10467
11309
  var encoder = new TextEncoder();
10468
11310
  var decoder = new TextDecoder();
10469
- var BATCH_TASK_KIND = "braintrust.durable.batch-task";
10470
- var BATCH_SCORER_KIND = "braintrust.durable.batch-scorer";
10471
- var DEFAULT_BATCH_SIZE = 1e3;
10472
- var DurableEvalMemoryStore = class {
11311
+ var WORKFLOW_TASK_KIND = "braintrust.workflow.task";
11312
+ var WORKFLOW_SCORER_KIND = "braintrust.workflow.scorer";
11313
+ var WorkflowEvalMemoryStore = class {
10473
11314
  values = /* @__PURE__ */ new Map();
11315
+ sets = /* @__PURE__ */ new Map();
10474
11316
  async read(key) {
10475
11317
  return this.values.get(key)?.slice();
10476
11318
  }
@@ -10483,8 +11325,16 @@ var DurableEvalMemoryStore = class {
10483
11325
  this.values.set(key, value.slice());
10484
11326
  return { value: value.slice(), created: true };
10485
11327
  }
11328
+ async addToSet(key, member) {
11329
+ let members = this.sets.get(key);
11330
+ if (!members) this.sets.set(key, members = /* @__PURE__ */ new Set());
11331
+ members.add(member);
11332
+ }
11333
+ async getSetSize(key) {
11334
+ return this.sets.get(key)?.size ?? 0;
11335
+ }
10486
11336
  };
10487
- var DurableEvalRedisStore = class {
11337
+ var WorkflowEvalRedisStore = class {
10488
11338
  client;
10489
11339
  keyPrefix;
10490
11340
  ttlMs;
@@ -10493,14 +11343,16 @@ var DurableEvalRedisStore = class {
10493
11343
  this.keyPrefix = options.keyPrefix ?? "braintrust-eval:";
10494
11344
  this.ttlMs = options.ttlMs ?? 1e3 * 60 * 60 * 24 * 7;
10495
11345
  if (!Number.isInteger(this.ttlMs) || this.ttlMs < 1) {
10496
- throw new Error("DurableEvalRedisStore ttlMs must be a positive integer");
11346
+ throw new Error(
11347
+ "WorkflowEvalRedisStore ttlMs must be a positive integer"
11348
+ );
10497
11349
  }
10498
11350
  }
10499
11351
  async read(key) {
10500
11352
  const value = await this.client.get(`${this.keyPrefix}${key}`);
10501
11353
  if (value == null) return void 0;
10502
11354
  if (typeof value !== "string") {
10503
- throw new Error("DurableEvalRedisStore expected GET to return a string");
11355
+ throw new Error("WorkflowEvalRedisStore expected GET to return a string");
10504
11356
  }
10505
11357
  return base64ToUint8Array(value);
10506
11358
  }
@@ -10517,7 +11369,7 @@ var DurableEvalRedisStore = class {
10517
11369
  await set.call(client, redisKey, encoded, { px: this.ttlMs });
10518
11370
  } else {
10519
11371
  throw new Error(
10520
- "DurableEvalRedisStore requires a node-redis, ioredis, or @upstash/redis client"
11372
+ "WorkflowEvalRedisStore requires a node-redis, ioredis, or @upstash/redis client"
10521
11373
  );
10522
11374
  }
10523
11375
  }
@@ -10535,49 +11387,73 @@ var DurableEvalRedisStore = class {
10535
11387
  setOptions = [{ px: this.ttlMs, nx: true, get: true }];
10536
11388
  } else {
10537
11389
  throw new Error(
10538
- "DurableEvalRedisStore getOrSet requires a node-redis, ioredis, or @upstash/redis client"
11390
+ "WorkflowEvalRedisStore getOrSet requires a node-redis, ioredis, or @upstash/redis client"
10539
11391
  );
10540
11392
  }
10541
11393
  const existing = await set.call(client, redisKey, encoded, ...setOptions);
10542
11394
  if (existing === null) return { value: value.slice(), created: true };
10543
11395
  if (typeof existing !== "string") {
10544
11396
  throw new Error(
10545
- "DurableEvalRedisStore expected atomic SET to return a string or null"
11397
+ "WorkflowEvalRedisStore expected atomic SET to return a string or null"
10546
11398
  );
10547
11399
  }
10548
11400
  return { value: base64ToUint8Array(existing), created: false };
10549
11401
  }
11402
+ async addToSet(key, member) {
11403
+ await this.evalSet(
11404
+ "redis.call('SADD', KEYS[1], ARGV[1]); redis.call('PEXPIRE', KEYS[1], ARGV[2]); return 1",
11405
+ key,
11406
+ [member, String(this.ttlMs)]
11407
+ );
11408
+ }
11409
+ async getSetSize(key) {
11410
+ return this.evalSet("return redis.call('SCARD', KEYS[1])", key, []);
11411
+ }
11412
+ async evalSet(script, key, args) {
11413
+ const client = this.client;
11414
+ const redisKey = `${this.keyPrefix}${key}`;
11415
+ const result = typeof client.defineCommand === "function" ? await client.eval(script, 1, redisKey, ...args) : typeof client.sendCommand === "function" ? await client.eval(script, { keys: [redisKey], arguments: args }) : await client.eval(script, [redisKey], args);
11416
+ if (typeof result !== "number") {
11417
+ throw new Error(
11418
+ "WorkflowEvalRedisStore expected EVAL to return a number"
11419
+ );
11420
+ }
11421
+ return result;
11422
+ }
10550
11423
  };
10551
- var BatchTask = class {
11424
+ var WorkflowTask = class {
10552
11425
  constructor(processor) {
10553
11426
  this.processor = processor;
10554
11427
  }
10555
11428
  processor;
10556
- kind = BATCH_TASK_KIND;
11429
+ kind = WORKFLOW_TASK_KIND;
10557
11430
  };
10558
- var BatchScorer = class {
11431
+ var WorkflowScorer = class {
10559
11432
  constructor(processor) {
10560
11433
  this.processor = processor;
10561
11434
  this.name = processor.name;
10562
11435
  }
10563
11436
  processor;
10564
- kind = BATCH_SCORER_KIND;
11437
+ kind = WORKFLOW_SCORER_KIND;
10565
11438
  name;
10566
11439
  };
10567
- function defineDurableEval(projectName, evaluator) {
11440
+ function defineWorkflowEval(projectName, evaluator) {
11441
+ if (evaluator.maxConcurrency !== void 0 && (!Number.isInteger(evaluator.maxConcurrency) || evaluator.maxConcurrency < 1)) {
11442
+ throw new Error("maxConcurrency must be a positive integer");
11443
+ }
10568
11444
  const definition = {
10569
11445
  projectName,
10570
11446
  evalName: evaluator.experimentName ?? projectName,
10571
11447
  evaluator
10572
11448
  };
10573
11449
  return {
10574
- start: (options = {}) => startDurableEval(definition, options),
10575
- status: (options) => getDurableEvalStatus(definition, options),
10576
- poll: (options) => pollDurableEval(definition, options),
10577
- processBatchResult: (result) => processDurableBatchResult(definition, result)
11450
+ start: (options = {}) => startWorkflowEval(definition, options),
11451
+ status: (options) => getWorkflowEvalStatus(definition, options),
11452
+ poll: (options) => pollWorkflowEval(definition, options),
11453
+ processSubmissionResult: (result) => processWorkflowSubmissionResult(definition, result)
10578
11454
  };
10579
11455
  }
10580
- async function startDurableEval(definition, options) {
11456
+ async function startWorkflowEval(definition, options) {
10581
11457
  const store = definition.evaluator.store;
10582
11458
  const runId = newId();
10583
11459
  const key = runKey(definition.projectName, definition.evalName, runId);
@@ -10597,7 +11473,7 @@ async function startDurableEval(definition, options) {
10597
11473
  update: true
10598
11474
  }
10599
11475
  );
10600
- if (!isBatchTask(definition.evaluator.task) && !(definition.evaluator.scores ?? []).some(isBatchScorer)) {
11476
+ if (!isWorkflowTask(definition.evaluator.task) && !(definition.evaluator.scores ?? []).some(isWorkflowScorer)) {
10601
11477
  const result = await runEvaluator(
10602
11478
  experiment,
10603
11479
  {
@@ -10624,67 +11500,91 @@ async function startDurableEval(definition, options) {
10624
11500
  parameters: assertJsonValue(parameters, "eval parameters"),
10625
11501
  status: "completed",
10626
11502
  summary: result.summary,
11503
+ caseCount: 0,
10627
11504
  cases: [],
10628
- batches: []
11505
+ submissions: []
10629
11506
  };
10630
11507
  await experiment?.flush();
10631
11508
  await writeRunRecord(store, key, state2);
10632
11509
  return currentStatus(definition, state2);
10633
11510
  }
11511
+ const cases = await materializeCases(definition, data, experiment);
10634
11512
  const state = {
10635
11513
  runId,
10636
11514
  experimentName,
10637
11515
  noSendLogs: options.noSendLogs ?? false,
10638
11516
  parameters: assertJsonValue(parameters, "eval parameters"),
10639
11517
  status: "running",
10640
- cases: await materializeCases(definition, data, experiment),
10641
- batches: []
11518
+ caseCount: cases.length,
11519
+ cases,
11520
+ submissions: []
10642
11521
  };
11522
+ await writeJson(
11523
+ store,
11524
+ `${key}/case-ids`,
11525
+ cases.map(({ id }) => id)
11526
+ );
10643
11527
  await writeCaseBaseRecords(store, key, state.cases);
10644
11528
  await writeRunRecord(store, key, state);
10645
- return advanceDurableEval(definition, state, store, key, experiment);
11529
+ return advanceWorkflowEval(definition, state, store, key, experiment);
10646
11530
  }
10647
- async function getDurableEvalStatus(definition, options) {
11531
+ async function getWorkflowEvalStatus(definition, options) {
10648
11532
  const store = definition.evaluator.store;
10649
- const state = await readRunState(
10650
- definition,
10651
- store,
10652
- runKey(definition.projectName, definition.evalName, options.runId)
11533
+ const key = runKey(
11534
+ definition.projectName,
11535
+ definition.evalName,
11536
+ options.runId
10653
11537
  );
10654
- if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
11538
+ const state = await readJson(store, key);
11539
+ if (!state) throw new Error(`Workflow eval run ${options.runId} is missing`);
10655
11540
  return currentStatus(definition, state);
10656
11541
  }
10657
- async function processDurableBatchResult(definition, result) {
10658
- if (!result.batchId && !result.externalId) {
10659
- throw new Error("Batch results require batchId or externalId");
11542
+ async function processWorkflowSubmissionResult(definition, result) {
11543
+ if (!result.submissionId && !result.externalId) {
11544
+ throw new Error("Submission results require submissionId or externalId");
10660
11545
  }
10661
11546
  const store = definition.evaluator.store;
10662
11547
  const key = runKey(definition.projectName, definition.evalName, result.runId);
10663
- const state = await readRunState(definition, store, key);
10664
- if (!state) throw new Error(`Durable eval run ${result.runId} is missing`);
10665
- const byBatch = result.batchId ? state.batches.find((candidate) => candidate.id === result.batchId) : void 0;
10666
- const byExternal = result.externalId ? state.batches.find(
10667
- (candidate) => candidate.externalId === result.externalId
11548
+ const run = await readJson(store, key);
11549
+ if (!run) throw new Error(`Workflow eval run ${result.runId} is missing`);
11550
+ const externalSubmissionId = result.externalId ? await readJson(
11551
+ store,
11552
+ `${key}/external/${encodedKeyPart(result.externalId)}`
10668
11553
  ) : void 0;
10669
- if (byBatch && byExternal && byBatch.id !== byExternal.id) {
10670
- throw new Error("batchId and externalId identify different batches");
10671
- }
10672
- const batch = byBatch ?? byExternal;
10673
- if (!batch) throw new Error("No submitted batch matches this result");
10674
- if (batch.status !== "complete") {
10675
- const records = await collectBatch(definition, state, batch);
10676
- batch.status = "complete";
10677
- await writeCaseRecords(store, key, records);
10678
- await writeBatchRecords(store, key, [batch]);
10679
- }
10680
- return advanceDurableEval(
11554
+ if (result.submissionId && externalSubmissionId && result.submissionId !== externalSubmissionId) {
11555
+ throw new Error(
11556
+ "submissionId and externalId identify different submissions"
11557
+ );
11558
+ }
11559
+ const submissionId = result.submissionId ?? externalSubmissionId;
11560
+ const submission = submissionId ? await readJson(
11561
+ store,
11562
+ submissionRecordKey(key, submissionId)
11563
+ ) : void 0;
11564
+ if (!submission) throw new Error("No submission matches this result");
11565
+ if (result.externalId && submission.externalId !== result.externalId) {
11566
+ throw new Error(
11567
+ "submissionId and externalId identify different submissions"
11568
+ );
11569
+ }
11570
+ if (run.status === "completed") return currentStatus(definition, run);
11571
+ const state = await readRunState(definition, store, key, [
11572
+ submission.itemId
11573
+ ]);
11574
+ if (submission.status !== "complete") {
11575
+ const record = await collectSubmission(definition, state, submission);
11576
+ await writeCaseRecords(store, key, [record]);
11577
+ submission.status = "complete";
11578
+ }
11579
+ await writeSubmissionRecords(store, key, [submission]);
11580
+ return advanceWorkflowEval(
10681
11581
  definition,
10682
- await readRunState(definition, store, key),
11582
+ await readRunState(definition, store, key, [submission.itemId]),
10683
11583
  store,
10684
11584
  key
10685
11585
  );
10686
11586
  }
10687
- async function pollDurableEval(definition, options) {
11587
+ async function pollWorkflowEval(definition, options) {
10688
11588
  const store = definition.evaluator.store;
10689
11589
  const key = runKey(
10690
11590
  definition.projectName,
@@ -10692,39 +11592,53 @@ async function pollDurableEval(definition, options) {
10692
11592
  options.runId
10693
11593
  );
10694
11594
  const state = await readRunState(definition, store, key);
10695
- if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
10696
- const batches = state.batches.filter((batch) => {
10697
- if (batch.status === "complete") return false;
10698
- return processorForStage(definition, batch.kind, batch.scorerName).completion.mode === "poll";
10699
- });
10700
- const results = await Promise.all(
10701
- batches.map(async (batch) => ({
10702
- batch,
10703
- result: await processorForStage(definition, batch.kind, batch.scorerName).completion.poll(batch.submissionData, {
10704
- runId: state.runId,
10705
- batchId: batch.id
10706
- })
10707
- }))
10708
- );
10709
- const changedCases = /* @__PURE__ */ new Map();
10710
- const changedBatches = [];
10711
- for (const { batch, result } of results) {
11595
+ if (!state) throw new Error(`Workflow eval run ${options.runId} is missing`);
11596
+ const submissions = state.submissions.filter((submission) => {
11597
+ if (submission.status === "complete") return false;
11598
+ return processorForStage(definition, submission.kind, submission.scorerName).completion.mode === "poll";
11599
+ });
11600
+ const workers = queue(async (submission) => {
11601
+ const completion = processorForStage(
11602
+ definition,
11603
+ submission.kind,
11604
+ submission.scorerName
11605
+ ).completion;
11606
+ if (completion.mode !== "poll") return;
11607
+ const result = await completion.poll(submission.submissionData, {
11608
+ runId: state.runId,
11609
+ submissionId: submission.id
11610
+ });
10712
11611
  if (result.status === "failed") throw asError(result.error);
10713
- if (result.status !== "complete") continue;
10714
- for (const record of await collectBatch(definition, state, batch)) {
10715
- changedCases.set(record.id, record);
10716
- }
10717
- batch.status = "complete";
10718
- changedBatches.push(batch);
10719
- }
10720
- if (changedBatches.length > 0) {
10721
- await writeCaseRecords(store, key, [...changedCases.values()]);
10722
- await writeBatchRecords(store, key, changedBatches);
11612
+ if (result.status === "complete") {
11613
+ const record = await collectSubmission(definition, state, submission);
11614
+ await writeCaseRecords(store, key, [record]);
11615
+ submission.status = "complete";
11616
+ await writeSubmissionRecords(store, key, [submission]);
11617
+ }
11618
+ }, definition.evaluator.maxConcurrency ?? 10);
11619
+ const results = await Promise.allSettled(
11620
+ submissions.map((submission) => workers.pushAsync(submission))
11621
+ );
11622
+ let status;
11623
+ const errors = results.flatMap(
11624
+ (result) => result.status === "rejected" ? [asError(result.reason)] : []
11625
+ );
11626
+ try {
11627
+ status = await advanceWorkflowEval(
11628
+ definition,
11629
+ await readRunState(definition, store, key),
11630
+ store,
11631
+ key
11632
+ );
11633
+ } catch (error) {
11634
+ errors.push(asError(error));
10723
11635
  }
10724
- const currentState = changedBatches.length > 0 ? await readRunState(definition, store, key) : state;
10725
- return advanceDurableEval(definition, currentState, store, key);
11636
+ if (errors.length === 1) throw errors[0];
11637
+ if (errors.length > 1)
11638
+ throw new AggregateError(errors, "Workflow submission callbacks failed");
11639
+ return status;
10726
11640
  }
10727
- async function openDurableExperiment(definition, state) {
11641
+ async function openWorkflowExperiment(definition, state) {
10728
11642
  const data = [];
10729
11643
  return await _internalInitEvaluatorExperiment(
10730
11644
  definition.projectName,
@@ -10737,45 +11651,49 @@ async function openDurableExperiment(definition, state) {
10737
11651
  }
10738
11652
  );
10739
11653
  }
10740
- async function advanceDurableEval(definition, state, store, key, existingExperiment) {
11654
+ async function advanceWorkflowEval(definition, state, store, key, existingExperiment) {
10741
11655
  if (state.status === "completed") return currentStatus(definition, state);
10742
- const experiment = existingExperiment === void 0 ? await openDurableExperiment(definition, state) : existingExperiment;
11656
+ const experiment = existingExperiment === void 0 ? await openWorkflowExperiment(definition, state) : existingExperiment;
11657
+ const caseIds = state.cases.map(({ id }) => id);
10743
11658
  await runTaskStage(definition, state, store, key, experiment);
10744
11659
  await logCompletedTasks(definition, state, store, key, experiment);
10745
- state = await readRunState(definition, store, key) ?? state;
10746
- if (state.cases.some((record) => !record.taskComplete || !record.taskLogged)) {
10747
- return currentStatus(definition, state);
10748
- }
11660
+ state = await readRunState(definition, store, key, caseIds) ?? state;
10749
11661
  await runScoreStages(definition, state, store, key, experiment);
10750
- state = await readRunState(definition, store, key) ?? state;
11662
+ state = await readRunState(definition, store, key, caseIds) ?? state;
10751
11663
  const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
10752
11664
  ({ name }) => name
10753
11665
  );
10754
11666
  const classifierNames = (definition.evaluator.classifiers ?? []).map(
10755
11667
  classifierName
10756
11668
  );
10757
- if (state.cases.some(
10758
- (record) => scorerNames.some(
10759
- (name) => !Object.hasOwn(record.scores, name) || !Object.hasOwn(record.loggedScores, name)
10760
- ) || classifierNames.some(
10761
- (name) => !Object.hasOwn(record.loggedClassifications, name)
10762
- )
10763
- )) {
11669
+ await Promise.all(
11670
+ state.cases.map(async (record) => {
11671
+ if (record.taskComplete && record.taskLogged && scorerNames.every(
11672
+ (name) => Object.hasOwn(record.scores, name) && Object.hasOwn(record.loggedScores, name)
11673
+ ) && classifierNames.every(
11674
+ (name) => Object.hasOwn(record.loggedClassifications, name)
11675
+ )) {
11676
+ await store.addToSet(`${key}/progress/cases`, record.id);
11677
+ }
11678
+ })
11679
+ );
11680
+ if (await store.getSetSize(`${key}/progress/cases`) !== state.caseCount) {
10764
11681
  return currentStatus(definition, state);
10765
11682
  }
10766
11683
  if (!await claimAction(store, key, "finish")) {
10767
- const latest = await readRunState(definition, store, key);
11684
+ const latest = await readJson(store, key);
10768
11685
  return currentStatus(definition, latest ?? state);
10769
11686
  }
11687
+ state = await readRunState(definition, store, key);
10770
11688
  state.summary = await finishExperiment(definition, state, experiment);
10771
11689
  state.status = "completed";
10772
11690
  await writeRunRecord(store, key, state);
10773
11691
  return currentStatus(definition, state);
10774
11692
  }
10775
- function currentStatus(definition, state) {
11693
+ async function currentStatus(definition, state) {
10776
11694
  if (state.status === "completed") {
10777
11695
  if (!state.summary) {
10778
- throw new Error(`Durable eval run ${state.runId} has no saved summary`);
11696
+ throw new Error(`Workflow eval run ${state.runId} has no saved summary`);
10779
11697
  }
10780
11698
  return {
10781
11699
  status: "completed",
@@ -10784,11 +11702,20 @@ function currentStatus(definition, state) {
10784
11702
  summary: state.summary
10785
11703
  };
10786
11704
  }
11705
+ const key = runKey(definition.projectName, definition.evalName, state.runId);
11706
+ const store = definition.evaluator.store;
10787
11707
  const pending = { poll: 0, webhook: 0 };
10788
- for (const batch of state.batches) {
10789
- if (batch.status === "complete") continue;
10790
- pending[processorForStage(definition, batch.kind, batch.scorerName).completion.mode]++;
10791
- }
11708
+ await Promise.all(
11709
+ ["poll", "webhook"].map(async (mode) => {
11710
+ const completed = await store.getSetSize(
11711
+ `${key}/progress/${mode}/complete`
11712
+ );
11713
+ const submitted = await store.getSetSize(
11714
+ `${key}/progress/${mode}/submitted`
11715
+ );
11716
+ pending[mode] = submitted - completed;
11717
+ })
11718
+ );
10792
11719
  return { status: "waiting", runId: state.runId, pending };
10793
11720
  }
10794
11721
  async function startCaseRoot(definition, state, record, experiment) {
@@ -10849,7 +11776,7 @@ async function logTaskResult(definition, state, record, experiment, task) {
10849
11776
  expected: "expected" in datum ? datum.expected : void 0,
10850
11777
  metadata: {
10851
11778
  ...record.metadata,
10852
- durable_eval: {
11779
+ workflow_eval: {
10853
11780
  run_id: state.runId,
10854
11781
  case_id: record.caseId,
10855
11782
  trial_index: record.trialIndex
@@ -10880,8 +11807,8 @@ async function logCompletedTasks(definition, state, store, key, experiment) {
10880
11807
  }
10881
11808
  }
10882
11809
  async function runTaskStage(definition, state, store, key, experiment) {
10883
- if (isBatchTask(definition.evaluator.task)) {
10884
- await ensureBatches(definition, state, store, key, "task");
11810
+ if (isWorkflowTask(definition.evaluator.task)) {
11811
+ await ensureSubmissions(definition, state, store, key, "task");
10885
11812
  return;
10886
11813
  }
10887
11814
  const task = definition.evaluator.task;
@@ -10907,8 +11834,9 @@ async function runScoreStages(definition, state, store, key, experiment) {
10907
11834
  changed.clear();
10908
11835
  };
10909
11836
  for (const { name, scorer } of scorers) {
10910
- if (isBatchScorer(scorer)) {
11837
+ if (isWorkflowScorer(scorer)) {
10911
11838
  for (const record of state.cases) {
11839
+ if (!record.taskComplete || !record.taskLogged) continue;
10912
11840
  if (Object.hasOwn(record.scores, name) && !Object.hasOwn(record.loggedScores, name)) {
10913
11841
  if (!await claimAction(store, key, "score-log", record.id, name)) {
10914
11842
  continue;
@@ -10924,10 +11852,11 @@ async function runScoreStages(definition, state, store, key, experiment) {
10924
11852
  }
10925
11853
  }
10926
11854
  await persistChangedCases();
10927
- await ensureBatches(definition, state, store, key, "score", name);
11855
+ await ensureSubmissions(definition, state, store, key, "score", name);
10928
11856
  continue;
10929
11857
  }
10930
11858
  for (const record of state.cases) {
11859
+ if (!record.taskComplete || !record.taskLogged) continue;
10931
11860
  if (Object.hasOwn(record.loggedScores, name)) continue;
10932
11861
  if (!await claimAction(store, key, "score", record.id, name)) continue;
10933
11862
  await evaluateAndLogScore(
@@ -10944,6 +11873,7 @@ async function runScoreStages(definition, state, store, key, experiment) {
10944
11873
  for (const [index, classifier] of (definition.evaluator.classifiers ?? []).entries()) {
10945
11874
  const name = classifierName(classifier, index);
10946
11875
  for (const record of state.cases) {
11876
+ if (!record.taskComplete || !record.taskLogged) continue;
10947
11877
  if (Object.hasOwn(record.loggedClassifications, name)) continue;
10948
11878
  if (!await claimAction(store, key, "classification", record.id, name)) {
10949
11879
  continue;
@@ -10966,13 +11896,14 @@ function scorerArgs(record) {
10966
11896
  return {
10967
11897
  ...datum,
10968
11898
  metadata: record.metadata,
11899
+ tags: record.tags,
10969
11900
  output: record.output
10970
11901
  };
10971
11902
  }
10972
11903
  function resumeCaseRoot(definition, record, experiment) {
10973
11904
  if (!experiment) return NOOP_SPAN;
10974
11905
  if (!record.rootSpan) {
10975
- throw new Error(`Durable eval case ${record.caseId} has no root span`);
11906
+ throw new Error(`Workflow eval case ${record.caseId} has no root span`);
10976
11907
  }
10977
11908
  return _internalResumeSpan({
10978
11909
  exported: record.rootSpan,
@@ -11060,117 +11991,110 @@ async function evaluateAndLogClassification(definition, state, record, name, cla
11060
11991
  root.end();
11061
11992
  }
11062
11993
  }
11063
- async function ensureBatches(definition, state, store, key, kind, scorerName2) {
11994
+ async function ensureSubmissions(definition, state, store, key, kind, scorerName2) {
11064
11995
  const processor = processorForStage(definition, kind, scorerName2);
11065
- const plans = plannedBatches(
11996
+ const plans = plannedSubmissions(
11066
11997
  definition,
11067
11998
  state.runId,
11068
11999
  state.cases.map(({ id }) => id)
11069
12000
  );
11070
12001
  const casesById = new Map(state.cases.map((record) => [record.id, record]));
11071
- for (const plan of plans) {
11072
- if (plan.kind !== kind || plan.scorerName !== scorerName2) continue;
11073
- if (state.batches.some(({ id }) => id === plan.id)) continue;
11074
- const records = plan.itemIds.map((id) => casesById.get(id));
11075
- const ready = records.every(
11076
- (record) => kind === "task" ? !record.taskComplete : !Object.hasOwn(record.scores, scorerName2)
11077
- );
11078
- if (!ready) continue;
11079
- const batchId = plan.id;
12002
+ const existingIds = new Set(state.submissions.map(({ id }) => id));
12003
+ const workers = queue(async (plan) => {
12004
+ if (plan.kind !== kind || plan.scorerName !== scorerName2) return;
12005
+ if (existingIds.has(plan.id)) return;
12006
+ const record = casesById.get(plan.itemId);
12007
+ const ready = kind === "task" ? !record.taskComplete : record.taskComplete && record.taskLogged && !Object.hasOwn(record.scores, scorerName2);
12008
+ if (!ready) return;
12009
+ const submissionId = plan.id;
11080
12010
  const claim = await store.getOrSet(
11081
- claimRecordKey(key, "batch", batchId),
11082
- encoder.encode(batchId)
11083
- );
11084
- if (!claim.created) continue;
11085
- const context = { runId: state.runId, batchId };
11086
- const items = records.map(
11087
- (record) => kind === "task" ? taskBatchItem(record, state.parameters) : scorerBatchItem(record)
12011
+ claimRecordKey(key, "submission", submissionId),
12012
+ encoder.encode(submissionId)
11088
12013
  );
12014
+ if (!claim.created) return;
12015
+ const context = { runId: state.runId, submissionId };
12016
+ const item = kind === "task" ? taskSubmissionItem(record, state.parameters) : scorerSubmissionItem(record);
11089
12017
  const submissionData = assertJsonValue(
11090
- await processor.submit(items, context),
11091
- `submission data for batch ${batchId}`
12018
+ await processor.submit(item, context),
12019
+ `submission data for submission ${submissionId}`
11092
12020
  );
11093
12021
  const externalId = processor.completion.mode === "webhook" ? processor.completion.getExternalId(submissionData, context) : void 0;
11094
12022
  if (externalId !== void 0 && !externalId.trim()) {
11095
- throw new Error(`Batch ${batchId} produced an empty externalId`);
12023
+ throw new Error(
12024
+ `Submission ${submissionId} produced an empty externalId`
12025
+ );
11096
12026
  }
11097
- const batch = {
11098
- id: batchId,
12027
+ const submission = {
12028
+ id: submissionId,
11099
12029
  kind,
11100
12030
  scorerName: scorerName2,
11101
- itemIds: records.map((record) => record.id),
12031
+ itemId: record.id,
11102
12032
  submissionData,
11103
12033
  externalId,
11104
- status: "submitted"
12034
+ status: "submitted",
12035
+ completionMode: processor.completion.mode
11105
12036
  };
11106
- state.batches.push(batch);
11107
- await writeBatchRecords(store, key, [batch]);
11108
- }
12037
+ state.submissions.push(submission);
12038
+ await writeSubmissionRecords(store, key, [submission]);
12039
+ }, definition.evaluator.maxConcurrency ?? 10);
12040
+ const results = await Promise.allSettled(
12041
+ plans.map((plan) => workers.pushAsync(plan))
12042
+ );
12043
+ const errors = results.flatMap(
12044
+ (result) => result.status === "rejected" ? [asError(result.reason)] : []
12045
+ );
12046
+ if (errors.length === 1) throw errors[0];
12047
+ if (errors.length > 1)
12048
+ throw new AggregateError(errors, "Workflow submissions failed");
11109
12049
  }
11110
- async function collectBatch(definition, state, batch) {
11111
- const processor = processorForStage(definition, batch.kind, batch.scorerName);
11112
- const context = { runId: state.runId, batchId: batch.id };
11113
- const results = await processor.collect(batch.submissionData, context);
11114
- if (!Array.isArray(results)) {
11115
- throw new Error(`collect for batch ${batch.id} must return an array`);
12050
+ async function collectSubmission(definition, state, submission) {
12051
+ const processor = processorForStage(
12052
+ definition,
12053
+ submission.kind,
12054
+ submission.scorerName
12055
+ );
12056
+ const context = { runId: state.runId, submissionId: submission.id };
12057
+ const result = await processor.collect(submission.submissionData, context);
12058
+ if (typeof result !== "object" || result === null || Array.isArray(result)) {
12059
+ throw new Error(
12060
+ `collect for submission ${submission.id} must return a result object`
12061
+ );
11116
12062
  }
11117
- const expectedIds = new Set(batch.itemIds);
11118
- const seen = /* @__PURE__ */ new Set();
11119
- const records = [];
11120
- for (const result of results) {
11121
- const id = resultItemId(result);
11122
- if (!expectedIds.has(id)) {
11123
- throw new Error(`Batch ${batch.id} returned unknown item ${id}`);
11124
- }
11125
- if (seen.has(id)) {
11126
- throw new Error(`Batch ${batch.id} returned item ${id} more than once`);
11127
- }
11128
- seen.add(id);
11129
- const record = state.cases.find((candidate) => candidate.id === id);
11130
- records.push(record);
11131
- if (batch.kind === "task") {
11132
- record.output = assertJsonValue(
11133
- result.output,
11134
- `task output for item ${id}`
11135
- );
11136
- if ("metadata" in result && result.metadata !== void 0) {
11137
- record.metadata = assertJsonValue(
11138
- {
11139
- ...record.metadata,
11140
- ...result.metadata
11141
- },
11142
- `metadata for ${id}`
11143
- );
11144
- }
11145
- if ("tags" in result && result.tags !== void 0)
11146
- record.tags = result.tags;
11147
- record.taskComplete = true;
11148
- } else {
11149
- record.scores[batch.scorerName] = assertJsonValue(
11150
- result.score,
11151
- `score output for item ${id}`
12063
+ const record = state.cases.find(
12064
+ (candidate) => candidate.id === submission.itemId
12065
+ );
12066
+ if (submission.kind === "task") {
12067
+ record.output = assertJsonValue(
12068
+ result.output,
12069
+ `task output for item ${record.id}`
12070
+ );
12071
+ if (result.metadata !== void 0) {
12072
+ record.metadata = assertJsonValue(
12073
+ { ...record.metadata, ...result.metadata },
12074
+ `metadata for ${record.id}`
11152
12075
  );
11153
12076
  }
11154
- }
11155
- const missing = batch.itemIds.filter((id) => !seen.has(id));
11156
- if (missing.length > 0) {
11157
- throw new Error(
11158
- `Batch ${batch.id} did not return results for: ${missing.join(", ")}`
12077
+ if (result.tags !== void 0) record.tags = result.tags;
12078
+ record.taskComplete = true;
12079
+ } else {
12080
+ record.scores[submission.scorerName] = assertJsonValue(
12081
+ result.score,
12082
+ `score output for item ${record.id}`
11159
12083
  );
11160
12084
  }
11161
- return records;
12085
+ return record;
11162
12086
  }
11163
12087
  function processorForStage(definition, kind, scorerName2) {
11164
12088
  if (kind === "task") {
11165
- if (!isBatchTask(definition.evaluator.task)) {
11166
- throw new Error("Definition no longer contains the batch task");
12089
+ if (!isWorkflowTask(definition.evaluator.task)) {
12090
+ throw new Error("Definition no longer contains the submission task");
11167
12091
  }
11168
12092
  return definition.evaluator.task.processor;
11169
12093
  }
11170
12094
  const scorer = resolveScorers(definition.evaluator.scores ?? []).find(
11171
12095
  ({ name }) => name === scorerName2
11172
12096
  )?.scorer;
11173
- if (!isBatchScorer(scorer)) {
12097
+ if (!isWorkflowScorer(scorer)) {
11174
12098
  throw new Error(`Definition no longer contains scorer ${scorerName2}`);
11175
12099
  }
11176
12100
  return scorer.processor;
@@ -11192,15 +12116,15 @@ async function materializeCases(definition, data, experiment) {
11192
12116
  const caseId = datum.id ?? datum.upsert_id ?? (evaluator.caseId ? await evaluator.caseId(datum) : void 0);
11193
12117
  if (!caseId) {
11194
12118
  throw new Error(
11195
- "Every durable eval case requires id, upsert_id, or caseId"
12119
+ "Every workflow eval case requires id, upsert_id, or caseId"
11196
12120
  );
11197
12121
  }
11198
12122
  if (seen.has(caseId))
11199
- throw new Error(`Duplicate durable eval case id: ${caseId}`);
12123
+ throw new Error(`Duplicate workflow eval case id: ${caseId}`);
11200
12124
  seen.add(caseId);
11201
12125
  const trialCount = datum.trialCount ?? evaluator.trialCount ?? 1;
11202
12126
  if (!Number.isInteger(trialCount) || trialCount < 1) {
11203
- throw new Error(`Invalid trialCount for durable eval case ${caseId}`);
12127
+ throw new Error(`Invalid trialCount for workflow eval case ${caseId}`);
11204
12128
  }
11205
12129
  for (let trialIndex = 0; trialIndex < trialCount; trialIndex++) {
11206
12130
  records.push({
@@ -11224,7 +12148,7 @@ async function materializeCases(definition, data, experiment) {
11224
12148
  }
11225
12149
  return records;
11226
12150
  }
11227
- function taskBatchItem(record, parameters) {
12151
+ function taskSubmissionItem(record, parameters) {
11228
12152
  const datum = record.datum;
11229
12153
  return {
11230
12154
  id: record.id,
@@ -11236,7 +12160,7 @@ function taskBatchItem(record, parameters) {
11236
12160
  trialIndex: record.trialIndex
11237
12161
  };
11238
12162
  }
11239
- function scorerBatchItem(record) {
12163
+ function scorerSubmissionItem(record) {
11240
12164
  const datum = record.datum;
11241
12165
  return {
11242
12166
  id: record.id,
@@ -11307,12 +12231,12 @@ async function finishExperiment(definition, state, experiment) {
11307
12231
  }
11308
12232
  function resolveScorers(scorers) {
11309
12233
  return scorers.map((scorer, index) => ({
11310
- name: isBatchScorer(scorer) ? scorer.name : scorer.name || `scorer_${index}`,
12234
+ name: isWorkflowScorer(scorer) ? scorer.name : scorer.name || `scorer_${index}`,
11311
12235
  scorer
11312
12236
  }));
11313
12237
  }
11314
12238
  function runKey(projectName, evalName, runId) {
11315
- return `durable-eval/v1/runs/${contentVersion(encoder.encode(`${projectName}\0${evalName}\0${runId}`))}`;
12239
+ return `workflow-eval/v1/runs/${contentVersion(encoder.encode(`${projectName}\0${evalName}\0${runId}`))}`;
11316
12240
  }
11317
12241
  function encodedKeyPart(value) {
11318
12242
  return uint8ArrayToBase64(encoder.encode(value)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
@@ -11321,8 +12245,8 @@ function caseRecordKey(key, caseId, kind, ...names) {
11321
12245
  const suffix = names.map(encodedKeyPart).join("/");
11322
12246
  return `${key}/cases/${encodedKeyPart(caseId)}/${kind}${suffix ? `/${suffix}` : ""}`;
11323
12247
  }
11324
- function batchRecordKey(key, batchId) {
11325
- return `${key}/batches/${encodedKeyPart(batchId)}`;
12248
+ function submissionRecordKey(key, submissionId) {
12249
+ return `${key}/submissions/${encodedKeyPart(submissionId)}`;
11326
12250
  }
11327
12251
  function claimRecordKey(key, kind, ...parts) {
11328
12252
  const identity = stableStringify([kind, parts]);
@@ -11334,33 +12258,24 @@ async function claimAction(store, key, kind, ...parts) {
11334
12258
  encoder.encode("claimed")
11335
12259
  )).created;
11336
12260
  }
11337
- function plannedBatches(definition, runId, caseIds) {
12261
+ function plannedSubmissions(definition, runId, caseIds) {
11338
12262
  const stages = [];
11339
- if (isBatchTask(definition.evaluator.task)) stages.push({ kind: "task" });
12263
+ if (isWorkflowTask(definition.evaluator.task)) stages.push({ kind: "task" });
11340
12264
  for (const { name, scorer } of resolveScorers(
11341
12265
  definition.evaluator.scores ?? []
11342
12266
  )) {
11343
- if (isBatchScorer(scorer)) {
12267
+ if (isWorkflowScorer(scorer)) {
11344
12268
  stages.push({ kind: "score", scorerName: name });
11345
12269
  }
11346
12270
  }
11347
12271
  const plans = [];
11348
12272
  for (const { kind, scorerName: scorerName2 } of stages) {
11349
- const batchSize = processorForStage(definition, kind, scorerName2).batchSize ?? DEFAULT_BATCH_SIZE;
11350
- if (!Number.isInteger(batchSize) || batchSize < 1) {
11351
- throw new Error(
11352
- `Invalid batchSize for ${scorerName2 ?? "task"}: ${batchSize}`
11353
- );
11354
- }
11355
- for (let offset = 0; offset < caseIds.length; offset += batchSize) {
11356
- const itemIds = caseIds.slice(offset, offset + batchSize);
12273
+ for (const itemId of caseIds) {
11357
12274
  plans.push({
11358
- id: deterministicId(
11359
- stableStringify([runId, kind, scorerName2, itemIds])
11360
- ),
12275
+ id: deterministicId(stableStringify([runId, kind, scorerName2, itemId])),
11361
12276
  kind,
11362
12277
  scorerName: scorerName2,
11363
- itemIds
12278
+ itemId
11364
12279
  });
11365
12280
  }
11366
12281
  }
@@ -11420,7 +12335,7 @@ async function readCaseRecord(definition, store, key, id) {
11420
12335
  }))
11421
12336
  )
11422
12337
  ]);
11423
- if (!base) throw new Error(`Durable eval case ${id} is missing`);
12338
+ if (!base) throw new Error(`Workflow eval case ${id} is missing`);
11424
12339
  const scores = /* @__PURE__ */ Object.create(null);
11425
12340
  for (const { name, value } of scoreValues) {
11426
12341
  if (value !== void 0) scores[name] = value;
@@ -11451,32 +12366,34 @@ async function readCaseRecord(definition, store, key, id) {
11451
12366
  loggedClassifications
11452
12367
  };
11453
12368
  }
11454
- async function readRunState(definition, store, key) {
12369
+ async function readRunState(definition, store, key, caseIds) {
11455
12370
  const record = await readJson(store, key);
11456
12371
  if (!record) return void 0;
11457
- const plans = plannedBatches(definition, record.runId, record.caseIds);
11458
- const [cases, batchRecords] = await Promise.all([
12372
+ const selectedIds = caseIds ?? (record.caseCount === 0 ? [] : await readJson(store, `${key}/case-ids`));
12373
+ if (!selectedIds)
12374
+ throw new Error(`Workflow eval run ${record.runId} has no case index`);
12375
+ const plans = plannedSubmissions(definition, record.runId, selectedIds);
12376
+ const [cases, submissionRecords] = await Promise.all([
11459
12377
  Promise.all(
11460
- record.caseIds.map((id) => readCaseRecord(definition, store, key, id))
12378
+ selectedIds.map((id) => readCaseRecord(definition, store, key, id))
11461
12379
  ),
11462
12380
  Promise.all(
11463
12381
  plans.map(async ({ id }) => {
11464
- return readJson(store, batchRecordKey(key, id));
12382
+ return readJson(
12383
+ store,
12384
+ submissionRecordKey(key, id)
12385
+ );
11465
12386
  })
11466
12387
  )
11467
12388
  ]);
11468
- const batches = batchRecords.filter(
12389
+ const submissions = submissionRecords.filter(
11469
12390
  (value) => value !== void 0
11470
12391
  );
11471
- const { caseIds: _caseIds, ...state } = record;
11472
- return { ...state, cases, batches };
12392
+ return { ...record, cases, submissions };
11473
12393
  }
11474
12394
  async function writeRunRecord(store, key, state) {
11475
- const { cases, batches: _batches, ...record } = state;
11476
- await writeJson(store, key, {
11477
- ...record,
11478
- caseIds: cases.map(({ id }) => id)
11479
- });
12395
+ const { cases: _cases, submissions: _submissions, ...record } = state;
12396
+ await writeJson(store, key, record);
11480
12397
  }
11481
12398
  async function writeCaseBaseRecords(store, key, records) {
11482
12399
  await Promise.all(
@@ -11548,11 +12465,25 @@ async function writeCaseRecords(store, key, records) {
11548
12465
  }
11549
12466
  await Promise.all(writes);
11550
12467
  }
11551
- async function writeBatchRecords(store, key, records) {
12468
+ async function writeSubmissionRecords(store, key, records) {
11552
12469
  await Promise.all(
11553
- records.map(
11554
- (record) => writeJson(store, batchRecordKey(key, record.id), record)
11555
- )
12470
+ records.map(async (record) => {
12471
+ if (record.externalId !== void 0) {
12472
+ const locator = await store.getOrSet(
12473
+ `${key}/external/${encodedKeyPart(record.externalId)}`,
12474
+ encoder.encode(JSON.stringify(record.id))
12475
+ );
12476
+ if (JSON.parse(decoder.decode(locator.value)) !== record.id) {
12477
+ throw new Error(`Duplicate externalId: ${record.externalId}`);
12478
+ }
12479
+ }
12480
+ const progressKey = `${key}/progress/${record.completionMode}`;
12481
+ await store.addToSet(`${progressKey}/submitted`, record.id);
12482
+ if (record.status === "complete") {
12483
+ await store.addToSet(`${progressKey}/complete`, record.id);
12484
+ }
12485
+ await writeJson(store, submissionRecordKey(key, record.id), record);
12486
+ })
11556
12487
  );
11557
12488
  }
11558
12489
  function deterministicId(value) {
@@ -11597,17 +12528,11 @@ function assertJsonValue(value, label) {
11597
12528
  throw new Error(`${label} must be JSON serializable`, { cause: error });
11598
12529
  }
11599
12530
  }
11600
- function resultItemId(value) {
11601
- if (typeof value !== "object" || value === null || !("id" in value) || typeof value.id !== "string") {
11602
- throw new Error("Batch results must contain a string id");
11603
- }
11604
- return value.id;
11605
- }
11606
- function isBatchTask(value) {
11607
- return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_TASK_KIND;
12531
+ function isWorkflowTask(value) {
12532
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === WORKFLOW_TASK_KIND;
11608
12533
  }
11609
- function isBatchScorer(value) {
11610
- return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_SCORER_KIND;
12534
+ function isWorkflowScorer(value) {
12535
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === WORKFLOW_SCORER_KIND;
11611
12536
  }
11612
12537
  function asError(error) {
11613
12538
  return error instanceof Error ? error : new Error(String(error));
@@ -12534,6 +13459,65 @@ var evaluatorDefinitionsSchema = z5.record(
12534
13459
  evaluatorDefinitionSchema
12535
13460
  );
12536
13461
 
13462
+ // src/wrappers/elevenlabs.ts
13463
+ var clients2 = /* @__PURE__ */ new WeakMap();
13464
+ function wrapElevenLabs(client) {
13465
+ if (!isObject(client) || !isObject(client.textToSpeech) || typeof client.textToSpeech.convert !== "function") {
13466
+ debugLogger.warn("Unsupported ElevenLabs client. Not wrapping.");
13467
+ return client;
13468
+ }
13469
+ const cached = clients2.get(client);
13470
+ if (cached) return cached;
13471
+ const sdk = client;
13472
+ const speech = new Proxy(sdk.textToSpeech, {
13473
+ get(target, prop, receiver) {
13474
+ switch (prop) {
13475
+ case "convert":
13476
+ case "stream":
13477
+ return (...args) => elevenLabsChannels[prop].invoke(target[prop], target, args, {});
13478
+ case "convertWithTimestamps":
13479
+ return (...args) => elevenLabsChannels.convertWithTimestamps.invoke(
13480
+ target.convertWithTimestamps,
13481
+ target,
13482
+ args,
13483
+ {}
13484
+ );
13485
+ case "streamWithTimestamps":
13486
+ return (...args) => elevenLabsChannels.streamWithTimestamps.invoke(
13487
+ target.streamWithTimestamps,
13488
+ target,
13489
+ args,
13490
+ {}
13491
+ );
13492
+ default:
13493
+ return Reflect.get(target, prop, receiver);
13494
+ }
13495
+ }
13496
+ });
13497
+ const transcription = new Proxy(sdk.speechToText, {
13498
+ get(target, prop, receiver) {
13499
+ if (prop === "convert")
13500
+ return (request, options) => elevenLabsChannels.transcribe.invoke(
13501
+ target.convert,
13502
+ target,
13503
+ [request, options],
13504
+ {}
13505
+ );
13506
+ return Reflect.get(target, prop, receiver);
13507
+ }
13508
+ });
13509
+ const proxy = new Proxy(sdk, {
13510
+ get(target, prop, receiver) {
13511
+ if (prop === "textToSpeech") return speech;
13512
+ if (prop === "speechToText") return transcription;
13513
+ return Reflect.get(target, prop, receiver);
13514
+ }
13515
+ });
13516
+ clients2.set(client, proxy);
13517
+ clients2.set(proxy, proxy);
13518
+ return proxy;
13519
+ }
13520
+
12537
13521
  // src/node/index.ts
12538
13522
  configureNode();
12539
13523
  export {
@@ -12545,8 +13529,6 @@ export {
12545
13529
  BRAINTRUST_PARENT_KEY,
12546
13530
  BaseAttachment,
12547
13531
  BaseExperiment,
12548
- BatchScorer,
12549
- BatchTask,
12550
13532
  BraintrustLangChainCallbackHandler,
12551
13533
  BraintrustMiddleware,
12552
13534
  BraintrustObservabilityExporter,
@@ -12560,8 +13542,6 @@ export {
12560
13542
  DEFAULT_MAX_REQUEST_SIZE,
12561
13543
  Dataset,
12562
13544
  DatasetPipeline,
12563
- DurableEvalMemoryStore,
12564
- DurableEvalRedisStore,
12565
13545
  ERR_PERMALINK,
12566
13546
  Eval,
12567
13547
  EvalResultWithSummary,
@@ -12596,6 +13576,10 @@ export {
12596
13576
  TestBackgroundLogger,
12597
13577
  ToolBuilder,
12598
13578
  UUIDGenerator,
13579
+ WorkflowEvalMemoryStore,
13580
+ WorkflowEvalRedisStore,
13581
+ WorkflowScorer,
13582
+ WorkflowTask,
12599
13583
  X_CACHED_HEADER,
12600
13584
  _exportsForTestingOnly,
12601
13585
  _internalGetGlobalState,
@@ -12621,12 +13605,13 @@ export {
12621
13605
  deepCopyEvent,
12622
13606
  exports_exports as default,
12623
13607
  defaultErrorScoreHandler,
12624
- defineDurableEval,
13608
+ defineWorkflowEval,
12625
13609
  deserializePlainStringAsJSON,
12626
13610
  devNullWritableStream,
12627
13611
  evaluatorDefinitionSchema,
12628
13612
  evaluatorDefinitionsSchema,
12629
13613
  extractTraceContextFromHeaders,
13614
+ failOpenAIAgentsTrace,
12630
13615
  flush,
12631
13616
  getContextManager,
12632
13617
  getIdGenerator,
@@ -12673,12 +13658,14 @@ export {
12673
13658
  setFetch,
12674
13659
  setMaskingFunction,
12675
13660
  spanComponentsToObjectId,
13661
+ startOpenAIAgentsTrace,
12676
13662
  startSpan,
12677
13663
  summarize,
12678
13664
  templateRegistry,
12679
13665
  ToolFunctionDefinition as toolFunctionDefinitionSchema,
12680
13666
  traceable,
12681
13667
  traced,
13668
+ updateOpenAIAgentsTrace,
12682
13669
  updateSpan,
12683
13670
  uploadLogs3OverflowPayload,
12684
13671
  utf8ByteLength,
@@ -12699,12 +13686,15 @@ export {
12699
13686
  wrapCohere,
12700
13687
  wrapCopilotClient,
12701
13688
  wrapCursorSDK,
13689
+ wrapElevenLabs,
12702
13690
  wrapGenkit,
12703
13691
  wrapGoogleADK,
12704
13692
  wrapGoogleGenAI,
13693
+ wrapGoogleGenerativeAI,
12705
13694
  wrapGroq,
12706
13695
  wrapHuggingFace,
12707
13696
  wrapHuggingFaceTransformers,
13697
+ wrapLangGraphSDK,
12708
13698
  wrapLangSmithClient,
12709
13699
  wrapLangSmithRunTrees,
12710
13700
  wrapLangSmithTraceable,