braintrust 3.32.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 (60) hide show
  1. package/README.md +0 -43
  2. package/dev/dist/index.d.mts +31 -0
  3. package/dev/dist/index.d.ts +31 -0
  4. package/dev/dist/index.js +2275 -920
  5. package/dev/dist/index.mjs +1610 -255
  6. package/dist/apply-auto-instrumentation.js +263 -224
  7. package/dist/apply-auto-instrumentation.mjs +45 -6
  8. package/dist/auto-instrumentations/bundler/esbuild.cjs +78 -5
  9. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/next.cjs +78 -5
  11. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  12. package/dist/auto-instrumentations/bundler/rollup.cjs +78 -5
  13. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  14. package/dist/auto-instrumentations/bundler/vite.cjs +78 -5
  15. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  16. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +78 -5
  17. package/dist/auto-instrumentations/bundler/webpack.cjs +78 -5
  18. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  19. package/dist/auto-instrumentations/{chunk-AFND2U7E.mjs → chunk-T2DMPWFI.mjs} +75 -5
  20. package/dist/auto-instrumentations/{chunk-RI4Y55ZF.mjs → chunk-UHJ2DNYJ.mjs} +6 -1
  21. package/dist/auto-instrumentations/{chunk-QEEPRBIS.mjs → chunk-W5QNG3PV.mjs} +1 -1
  22. package/dist/auto-instrumentations/hook.mjs +86 -7
  23. package/dist/auto-instrumentations/index.cjs +76 -5
  24. package/dist/auto-instrumentations/index.d.mts +3 -1
  25. package/dist/auto-instrumentations/index.d.ts +3 -1
  26. package/dist/auto-instrumentations/index.mjs +3 -1
  27. package/dist/browser.d.mts +202 -342
  28. package/dist/browser.d.ts +202 -342
  29. package/dist/browser.js +2586 -623
  30. package/dist/browser.mjs +2586 -623
  31. package/dist/chunk-7P6ASYW6.mjs +9 -0
  32. package/dist/{chunk-BTRLPQG5.js → chunk-AXJTOUOC.js} +2250 -1172
  33. package/dist/{chunk-AW4QECG5.js → chunk-G3VHOHRC.js} +40 -9
  34. package/dist/chunk-MLKGABMK.js +9 -0
  35. package/dist/{chunk-4QSAHP7D.mjs → chunk-MZLBAFVJ.mjs} +1236 -158
  36. package/dist/{chunk-WV6QZI2W.mjs → chunk-TZVZN2JJ.mjs} +39 -8
  37. package/dist/cli.js +1695 -293
  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 +2586 -623
  45. package/dist/edge-light.mjs +2586 -623
  46. package/dist/index.d.mts +202 -342
  47. package/dist/index.d.ts +202 -342
  48. package/dist/index.js +1842 -986
  49. package/dist/index.mjs +1310 -454
  50. package/dist/instrumentation/index.d.mts +5 -245
  51. package/dist/instrumentation/index.d.ts +5 -245
  52. package/dist/instrumentation/index.js +1694 -254
  53. package/dist/instrumentation/index.mjs +1694 -254
  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 +2586 -623
  59. package/dist/workerd.mjs +2586 -623
  60. package/package.json +10 -2
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-4QSAHP7D.mjs";
148
+ } from "./chunk-MZLBAFVJ.mjs";
147
149
  import {
148
150
  INSTRUMENTATION_NAMES,
149
- __export,
150
151
  aiSDKChannels,
151
152
  anthropicChannels,
152
153
  bedrockRuntimeChannels,
@@ -168,6 +169,7 @@ import {
168
169
  huggingFaceTransformersChannels,
169
170
  isSupportedHuggingFaceTransformersTask,
170
171
  isomorph_default,
172
+ langGraphSDKChannels,
171
173
  langSmithChannels,
172
174
  mistralChannels,
173
175
  ollamaChannels,
@@ -179,7 +181,10 @@ import {
179
181
  strandsAgentSDKChannels,
180
182
  voyageAIChannels,
181
183
  withSpanInstrumentationName
182
- } from "./chunk-WV6QZI2W.mjs";
184
+ } from "./chunk-TZVZN2JJ.mjs";
185
+ import {
186
+ __export
187
+ } from "./chunk-7P6ASYW6.mjs";
183
188
 
184
189
  // src/exports.ts
185
190
  var exports_exports = {};
@@ -192,8 +197,6 @@ __export(exports_exports, {
192
197
  BRAINTRUST_PARENT_KEY: () => BRAINTRUST_PARENT_KEY,
193
198
  BaseAttachment: () => BaseAttachment,
194
199
  BaseExperiment: () => BaseExperiment,
195
- BatchScorer: () => BatchScorer,
196
- BatchTask: () => BatchTask,
197
200
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
198
201
  BraintrustMiddleware: () => BraintrustMiddleware,
199
202
  BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
@@ -207,8 +210,6 @@ __export(exports_exports, {
207
210
  DEFAULT_MAX_REQUEST_SIZE: () => DEFAULT_MAX_REQUEST_SIZE,
208
211
  Dataset: () => Dataset,
209
212
  DatasetPipeline: () => DatasetPipeline,
210
- DurableEvalMemoryStore: () => DurableEvalMemoryStore,
211
- DurableEvalRedisStore: () => DurableEvalRedisStore,
212
213
  ERR_PERMALINK: () => ERR_PERMALINK,
213
214
  Eval: () => Eval,
214
215
  EvalResultWithSummary: () => EvalResultWithSummary,
@@ -243,6 +244,10 @@ __export(exports_exports, {
243
244
  TestBackgroundLogger: () => TestBackgroundLogger,
244
245
  ToolBuilder: () => ToolBuilder,
245
246
  UUIDGenerator: () => UUIDGenerator,
247
+ WorkflowEvalMemoryStore: () => WorkflowEvalMemoryStore,
248
+ WorkflowEvalRedisStore: () => WorkflowEvalRedisStore,
249
+ WorkflowScorer: () => WorkflowScorer,
250
+ WorkflowTask: () => WorkflowTask,
246
251
  X_CACHED_HEADER: () => X_CACHED_HEADER,
247
252
  _exportsForTestingOnly: () => _exportsForTestingOnly,
248
253
  _internalGetGlobalState: () => _internalGetGlobalState,
@@ -267,12 +272,13 @@ __export(exports_exports, {
267
272
  currentSpan: () => currentSpan,
268
273
  deepCopyEvent: () => deepCopyEvent,
269
274
  defaultErrorScoreHandler: () => defaultErrorScoreHandler,
270
- defineDurableEval: () => defineDurableEval,
275
+ defineWorkflowEval: () => defineWorkflowEval,
271
276
  deserializePlainStringAsJSON: () => deserializePlainStringAsJSON,
272
277
  devNullWritableStream: () => devNullWritableStream,
273
278
  evaluatorDefinitionSchema: () => evaluatorDefinitionSchema,
274
279
  evaluatorDefinitionsSchema: () => evaluatorDefinitionsSchema,
275
280
  extractTraceContextFromHeaders: () => extractTraceContextFromHeaders,
281
+ failOpenAIAgentsTrace: () => failOpenAIAgentsTrace,
276
282
  flush: () => flush,
277
283
  getContextManager: () => getContextManager,
278
284
  getIdGenerator: () => getIdGenerator,
@@ -319,12 +325,14 @@ __export(exports_exports, {
319
325
  setFetch: () => setFetch,
320
326
  setMaskingFunction: () => setMaskingFunction,
321
327
  spanComponentsToObjectId: () => spanComponentsToObjectId,
328
+ startOpenAIAgentsTrace: () => startOpenAIAgentsTrace,
322
329
  startSpan: () => startSpan,
323
330
  summarize: () => summarize,
324
331
  templateRegistry: () => templateRegistry,
325
332
  toolFunctionDefinitionSchema: () => ToolFunctionDefinition,
326
333
  traceable: () => traceable,
327
334
  traced: () => traced,
335
+ updateOpenAIAgentsTrace: () => updateOpenAIAgentsTrace,
328
336
  updateSpan: () => updateSpan,
329
337
  uploadLogs3OverflowPayload: () => uploadLogs3OverflowPayload,
330
338
  utf8ByteLength: () => utf8ByteLength,
@@ -353,6 +361,7 @@ __export(exports_exports, {
353
361
  wrapGroq: () => wrapGroq,
354
362
  wrapHuggingFace: () => wrapHuggingFace,
355
363
  wrapHuggingFaceTransformers: () => wrapHuggingFaceTransformers,
364
+ wrapLangGraphSDK: () => wrapLangGraphSDK,
356
365
  wrapLangSmithClient: () => wrapLangSmithClient,
357
366
  wrapLangSmithRunTrees: () => wrapLangSmithRunTrees,
358
367
  wrapLangSmithTraceable: () => wrapLangSmithTraceable,
@@ -719,6 +728,143 @@ async function completeOpenAIBatchTrace(args) {
719
728
  );
720
729
  }
721
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
+
722
868
  // src/functions/invoke.ts
723
869
  async function invoke(args) {
724
870
  const {
@@ -2168,7 +2314,31 @@ var MAX_STORED_LLM_INPUTS = 100;
2168
2314
  var MAX_STORED_REASONING_BLOCKS = 100;
2169
2315
  var MAX_STORED_SPAN_REFERENCES = 1e4;
2170
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
+ ]);
2171
2336
  function braintrustEveHook(options) {
2337
+ if (!options || typeof options.defineState !== "function") {
2338
+ throw new TypeError(
2339
+ "braintrustEveHook requires Eve's defineState function"
2340
+ );
2341
+ }
2172
2342
  const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState);
2173
2343
  const bridge = new EveBridge(state);
2174
2344
  return {
@@ -2180,15 +2350,27 @@ function braintrustEveHook(options) {
2180
2350
  };
2181
2351
  }
2182
2352
  function createLegacyEveInstrumentation(options) {
2353
+ if (!options || typeof options.defineState !== "function") {
2354
+ throw new TypeError(
2355
+ "braintrustEveInstrumentation requires Eve's defineState function"
2356
+ );
2357
+ }
2183
2358
  const state = options.defineState(EVE_TRACE_STATE_KEY, emptyEveTraceState);
2184
2359
  return {
2185
2360
  events: {
2186
2361
  "step.started": (input) => {
2187
2362
  try {
2363
+ if (!isEveInstrumentationStepStartedEventInput(input)) {
2364
+ debugLogger.warn(
2365
+ "Ignoring malformed Eve instrumentation step.started event"
2366
+ );
2367
+ return void 0;
2368
+ }
2188
2369
  captureEveModelInput(state, input);
2189
2370
  } catch (error) {
2190
2371
  debugLogger.warn("Error in Eve LLM input capture:", error);
2191
2372
  }
2373
+ return void 0;
2192
2374
  }
2193
2375
  },
2194
2376
  recordInputs: false,
@@ -2197,7 +2379,77 @@ function createLegacyEveInstrumentation(options) {
2197
2379
  };
2198
2380
  }
2199
2381
  function isEveHandleMessageStreamEvent(event) {
2200
- 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"]);
2201
2453
  }
2202
2454
  var ResumedEveSpan = class {
2203
2455
  constructor(reference) {
@@ -2366,7 +2618,17 @@ var EveBridge = class {
2366
2618
  });
2367
2619
  }
2368
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) {
2369
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
+ }
2370
2632
  return;
2371
2633
  }
2372
2634
  const run = async () => {
@@ -2692,9 +2954,14 @@ var EveBridge = class {
2692
2954
  }
2693
2955
  for (const action of traceActions) {
2694
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
+ }
2695
2962
  toolCallsById.set(action.callId, {
2696
2963
  function: {
2697
- arguments: JSON.stringify(action.input),
2964
+ arguments: args,
2698
2965
  name
2699
2966
  },
2700
2967
  id: action.callId,
@@ -3563,17 +3830,33 @@ function consumeCapturedEveModelInput(state, sessionId, turnId, stepIndex) {
3563
3830
  }
3564
3831
  }
3565
3832
  function capturedModelInput(modelInput) {
3566
- const { instructions, messages } = modelInput;
3567
- const value = [];
3568
- if (typeof instructions === "string") {
3569
- value.push({ content: instructions, role: "system" });
3570
- } else if (Array.isArray(instructions)) {
3571
- value.push(...instructions.map(capturedEveModelMessage));
3572
- } else if (instructions) {
3573
- value.push(capturedEveModelMessage(instructions));
3574
- }
3575
- value.push(...messages.map(capturedEveModelMessage));
3576
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
+ }
3577
3860
  const cloned = JSON.parse(JSON.stringify(value));
3578
3861
  if (!Array.isArray(cloned)) {
3579
3862
  return void 0;
@@ -3584,116 +3867,202 @@ function capturedModelInput(modelInput) {
3584
3867
  }
3585
3868
  }
3586
3869
  function capturedEveModelMessage(message) {
3587
- 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"];
3588
3878
  if (typeof content === "string") {
3589
3879
  return { content, role };
3590
3880
  }
3591
- 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 };
3592
3892
  }
3593
3893
  function capturedEveModelContentPart(part) {
3594
- switch (part.type) {
3894
+ if (!isObject(part) || typeof part["type"] !== "string") {
3895
+ return void 0;
3896
+ }
3897
+ switch (part["type"]) {
3595
3898
  case "text":
3596
- case "reasoning":
3597
- 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
+ }
3598
3903
  case "image":
3904
+ if (!Object.hasOwn(part, "image")) {
3905
+ return void 0;
3906
+ }
3599
3907
  return {
3600
- image: part.image,
3601
- ...part.mediaType !== void 0 ? { mediaType: part.mediaType } : {},
3908
+ image: part["image"],
3909
+ ...typeof part["mediaType"] === "string" ? { mediaType: part["mediaType"] } : {},
3602
3910
  type: "image"
3603
3911
  };
3604
3912
  case "file":
3605
- case "reasoning-file":
3913
+ case "reasoning-file": {
3914
+ if (!Object.hasOwn(part, "data") || typeof part["mediaType"] !== "string") {
3915
+ return void 0;
3916
+ }
3606
3917
  return {
3607
- data: part.data,
3608
- ...part.type === "file" && part.filename !== void 0 ? { filename: part.filename } : {},
3609
- mediaType: part.mediaType,
3610
- 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"]
3611
3922
  };
3923
+ }
3612
3924
  case "custom":
3613
3925
  return {
3614
- ..."kind" in part ? { kind: part.kind } : {},
3926
+ ...typeof part["kind"] === "string" ? { kind: part["kind"] } : {},
3615
3927
  type: "custom"
3616
3928
  };
3617
- case "tool-call":
3929
+ case "tool-call": {
3930
+ if (typeof part["toolCallId"] !== "string" || typeof part["toolName"] !== "string") {
3931
+ return void 0;
3932
+ }
3618
3933
  return {
3619
- input: part.input,
3620
- ...part.providerExecuted !== void 0 ? { providerExecuted: part.providerExecuted } : {},
3621
- toolCallId: part.toolCallId,
3622
- toolName: part.toolName,
3934
+ input: part["input"],
3935
+ ...typeof part["providerExecuted"] === "boolean" ? { providerExecuted: part["providerExecuted"] } : {},
3936
+ toolCallId: part["toolCallId"],
3937
+ toolName: part["toolName"],
3623
3938
  type: "tool-call"
3624
3939
  };
3940
+ }
3625
3941
  case "tool-result": {
3626
- 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
+ }
3627
3946
  let capturedOutput;
3628
- switch (output.type) {
3947
+ switch (output["type"]) {
3629
3948
  case "text":
3630
- case "error-text":
3631
- 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
+ };
3632
3957
  break;
3958
+ }
3633
3959
  case "json":
3634
3960
  case "error-json":
3635
- capturedOutput = { type: output.type, value: output.value };
3961
+ capturedOutput = {
3962
+ type: output["type"],
3963
+ value: output["value"]
3964
+ };
3636
3965
  break;
3637
3966
  case "execution-denied":
3638
3967
  capturedOutput = {
3639
- ...output.reason !== void 0 ? { reason: output.reason } : {},
3968
+ ...typeof output["reason"] === "string" ? { reason: output["reason"] } : {},
3640
3969
  type: "execution-denied"
3641
3970
  };
3642
3971
  break;
3643
- 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
+ }
3644
3983
  capturedOutput = {
3645
3984
  type: "content",
3646
- value: output.value.map(capturedEveModelContentPart)
3985
+ value
3647
3986
  };
3648
3987
  break;
3988
+ }
3989
+ default:
3990
+ return void 0;
3649
3991
  }
3650
3992
  return {
3651
3993
  output: capturedOutput,
3652
- toolCallId: part.toolCallId,
3653
- toolName: part.toolName,
3994
+ toolCallId: part["toolCallId"],
3995
+ toolName: part["toolName"],
3654
3996
  type: "tool-result"
3655
3997
  };
3656
3998
  }
3657
- case "tool-approval-request":
3999
+ case "tool-approval-request": {
4000
+ if (typeof part["approvalId"] !== "string" || typeof part["toolCallId"] !== "string") {
4001
+ return void 0;
4002
+ }
3658
4003
  return {
3659
- approvalId: part.approvalId,
3660
- ...part.isAutomatic !== void 0 ? { isAutomatic: part.isAutomatic } : {},
3661
- ...part.signature !== void 0 ? { signature: part.signature } : {},
3662
- 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"],
3663
4008
  type: "tool-approval-request"
3664
4009
  };
3665
- 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
+ }
3666
4015
  return {
3667
- approvalId: part.approvalId,
3668
- approved: part.approved,
3669
- ...part.providerExecuted !== void 0 ? { providerExecuted: part.providerExecuted } : {},
3670
- ...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"] } : {},
3671
4020
  type: "tool-approval-response"
3672
4021
  };
4022
+ }
3673
4023
  case "file-data":
3674
- case "image-data":
4024
+ case "image-data": {
4025
+ if (typeof part["data"] !== "string" || typeof part["mediaType"] !== "string") {
4026
+ return void 0;
4027
+ }
3675
4028
  return {
3676
- data: part.data,
3677
- ...part.type === "file-data" && part.filename !== void 0 ? { filename: part.filename } : {},
3678
- mediaType: part.mediaType,
3679
- 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"]
3680
4033
  };
4034
+ }
3681
4035
  case "file-url":
3682
- case "image-url":
4036
+ case "image-url": {
4037
+ if (typeof part["url"] !== "string") {
4038
+ return void 0;
4039
+ }
3683
4040
  return {
3684
- ...part.type === "file-url" && part.mediaType !== void 0 ? { mediaType: part.mediaType } : {},
3685
- type: part.type,
3686
- url: part.url
4041
+ ...part["type"] === "file-url" && typeof part["mediaType"] === "string" ? { mediaType: part["mediaType"] } : {},
4042
+ type: part["type"],
4043
+ url: part["url"]
3687
4044
  };
4045
+ }
3688
4046
  case "file-id":
3689
- case "image-file-id":
3690
- 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
+ }
3691
4054
  case "file-reference":
3692
- case "image-file-reference":
4055
+ case "image-file-reference": {
4056
+ if (!isObject(part["providerReference"])) {
4057
+ return void 0;
4058
+ }
3693
4059
  return {
3694
- providerReference: part.providerReference,
3695
- type: part.type
4060
+ providerReference: part["providerReference"],
4061
+ type: part["type"]
3696
4062
  };
4063
+ }
4064
+ default:
4065
+ return void 0;
3697
4066
  }
3698
4067
  }
3699
4068
  function isCapturedModelInput(input) {
@@ -3829,18 +4198,54 @@ function createEveInstrumentationProvider(options = {}) {
3829
4198
  recordOutputs: true
3830
4199
  }),
3831
4200
  events: {
3832
- "action.completed": (event, context) => bridge.handleActionTerminal(event, context),
3833
- "action.failed": (event, context) => bridge.handleActionTerminal(event, context),
3834
- "action.started": (event, context) => bridge.handleActionStarted(event, context),
3835
- "model.call.completed": (event, context) => bridge.handleModelTerminal(event, context),
3836
- "model.call.failed": (event, context) => bridge.handleModelTerminal(event, context),
3837
- "model.call.started": (event, context) => bridge.handleModelStarted(event, context),
3838
- "step.attempt.completed": (event) => bridge.handleStepAttemptTerminal(event),
3839
- "step.attempt.failed": (event) => bridge.handleStepAttemptTerminal(event),
3840
- "turn.cancelled": (event, context) => bridge.handleTurnTerminal(event, context),
3841
- "turn.completed": (event, context) => bridge.handleTurnTerminal(event, context),
3842
- "turn.failed": (event, context) => bridge.handleTurnTerminal(event, context),
3843
- "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
+ )
3844
4249
  },
3845
4250
  flush,
3846
4251
  setup: options.setup
@@ -4196,17 +4601,20 @@ function modelOutput(event) {
4196
4601
  const reasoning = [];
4197
4602
  const toolCalls2 = [];
4198
4603
  for (const part of content) {
4199
- if (part.type === "text") {
4200
- text += part.text;
4201
- } else if (part.type === "reasoning" && part.text.trim().length > 0) {
4202
- reasoning.push({ content: part.text });
4203
- } 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") {
4204
4612
  toolCalls2.push({
4205
4613
  function: {
4206
- arguments: safeJsonStringify(part.input),
4207
- name: part.toolName
4614
+ arguments: safeJsonStringify(part["input"]),
4615
+ name: part["toolName"]
4208
4616
  },
4209
- id: part.callId,
4617
+ id: part["callId"],
4210
4618
  type: "function"
4211
4619
  });
4212
4620
  }
@@ -4225,13 +4633,13 @@ function modelOutput(event) {
4225
4633
  ];
4226
4634
  }
4227
4635
  function usageMetrics(usage) {
4228
- const promptTokens = nonNegativeNumber(usage.inputTokens);
4229
- const completionTokens = nonNegativeNumber(usage.outputTokens);
4636
+ const promptTokens = nonNegativeNumber(usage?.inputTokens);
4637
+ const completionTokens = nonNegativeNumber(usage?.outputTokens);
4230
4638
  const cachedTokens = nonNegativeNumber(
4231
- usage.inputTokenDetails?.cacheReadTokens
4639
+ usage?.inputTokenDetails?.cacheReadTokens
4232
4640
  );
4233
4641
  const cacheCreationTokens = nonNegativeNumber(
4234
- usage.inputTokenDetails?.cacheWriteTokens
4642
+ usage?.inputTokenDetails?.cacheWriteTokens
4235
4643
  );
4236
4644
  return {
4237
4645
  ...promptTokens !== void 0 ? { prompt_tokens: promptTokens } : {},
@@ -4268,10 +4676,76 @@ function turnIdempotencyKey(sessionId, turnId) {
4268
4676
  function actionIdempotencyKey(sessionId, turnId, callId) {
4269
4677
  return `action:${sessionId}:${turnId}:${callId}`;
4270
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
+ }
4271
4740
 
4272
4741
  // src/instrumentation/plugins/eve-instrumentation.ts
4273
4742
  var EVE_INSTRUMENTATION_PROVIDER = /* @__PURE__ */ Symbol.for("eve.instrumentation.provider");
4274
4743
  function braintrustEveInstrumentation(options) {
4744
+ if (!options || typeof options !== "object") {
4745
+ throw new TypeError(
4746
+ "braintrustEveInstrumentation requires an options object"
4747
+ );
4748
+ }
4275
4749
  const definition = "defineState" in options ? createLegacyEveInstrumentation(options) : createEveInstrumentationProvider(options);
4276
4750
  const declaration = {
4277
4751
  ...definition,
@@ -5288,6 +5762,7 @@ function wrapClient(client) {
5288
5762
  }
5289
5763
 
5290
5764
  // src/wrappers/google-genai.ts
5765
+ var patchedHttpResponses = /* @__PURE__ */ new WeakSet();
5291
5766
  function wrapGoogleGenAI(googleGenAI) {
5292
5767
  if (!googleGenAI || typeof googleGenAI !== "object") {
5293
5768
  console.warn("Invalid Google GenAI module. Not wrapping.");
@@ -5299,6 +5774,21 @@ function wrapGoogleGenAI(googleGenAI) {
5299
5774
  );
5300
5775
  return googleGenAI;
5301
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
+ }
5302
5792
  return new Proxy(googleGenAI, {
5303
5793
  get(target, prop, receiver) {
5304
5794
  if (prop === "GoogleGenAI") {
@@ -5549,17 +6039,17 @@ function wrapGenkit(genkit) {
5549
6039
  console.warn("Unsupported Genkit object. Not wrapping.");
5550
6040
  return genkit;
5551
6041
  }
5552
- function isRecord(value) {
6042
+ function isRecord2(value) {
5553
6043
  return typeof value === "object" && value !== null;
5554
6044
  }
5555
6045
  function isPropertyBag(value) {
5556
- return isRecord(value) || typeof value === "function";
6046
+ return isRecord2(value) || typeof value === "function";
5557
6047
  }
5558
6048
  function hasFunction(value, methodName) {
5559
6049
  return isPropertyBag(value) && methodName in value && typeof value[methodName] === "function";
5560
6050
  }
5561
6051
  function isGenkitInstance(value) {
5562
- 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"));
5563
6053
  }
5564
6054
  function isGenkitModule(value) {
5565
6055
  return hasFunction(value, "genkit");
@@ -5613,7 +6103,7 @@ function patchGenkitRegistry(instance) {
5613
6103
  patchGenkitRegistryConstructor(registry);
5614
6104
  }
5615
6105
  function patchGenkitRegistryLookup(registry) {
5616
- if (!isRecord(registry) || hasRegistryPatchedFlag(registry) || !hasFunction(registry, "lookupAction")) {
6106
+ if (!isRecord2(registry) || hasRegistryPatchedFlag(registry) || !hasFunction(registry, "lookupAction")) {
5617
6107
  return;
5618
6108
  }
5619
6109
  const originalLookupAction = registry.lookupAction;
@@ -5639,7 +6129,7 @@ function patchGenkitRegistryLookup(registry) {
5639
6129
  }
5640
6130
  }
5641
6131
  function patchGenkitRegistryConstructor(registry) {
5642
- if (!isRecord(registry)) {
6132
+ if (!isRecord2(registry)) {
5643
6133
  return;
5644
6134
  }
5645
6135
  const constructor = registry.constructor;
@@ -5655,7 +6145,7 @@ function patchGenkitRegistryConstructor(registry) {
5655
6145
  configurable: true,
5656
6146
  value: (...args) => {
5657
6147
  const childRegistry = originalWithParent.apply(constructor, args);
5658
- if (args.some((arg) => isRecord(arg) && hasRegistryPatchedFlag(arg))) {
6148
+ if (args.some((arg) => isRecord2(arg) && hasRegistryPatchedFlag(arg))) {
5659
6149
  patchGenkitRegistryLookup(childRegistry);
5660
6150
  patchGenkitRegistryConstructor(childRegistry);
5661
6151
  }
@@ -5754,7 +6244,7 @@ function hasRegistryConstructorPatchedFlag(value) {
5754
6244
  );
5755
6245
  }
5756
6246
  function isPromiseLike(value) {
5757
- return isRecord(value) && "then" in value && typeof value.then === "function";
6247
+ return isRecord2(value) && "then" in value && typeof value.then === "function";
5758
6248
  }
5759
6249
 
5760
6250
  // src/wrappers/huggingface.ts
@@ -6231,14 +6721,14 @@ function wrapMistral(mistral) {
6231
6721
  console.warn("Unsupported Mistral library. Not wrapping.");
6232
6722
  return mistral;
6233
6723
  }
6234
- function isRecord2(value) {
6724
+ function isRecord3(value) {
6235
6725
  return typeof value === "object" && value !== null;
6236
6726
  }
6237
6727
  function hasFunction3(value, methodName) {
6238
- return isRecord2(value) && methodName in value && typeof value[methodName] === "function";
6728
+ return isRecord3(value) && methodName in value && typeof value[methodName] === "function";
6239
6729
  }
6240
6730
  function isSupportedMistralClient(value) {
6241
- if (!isRecord2(value)) {
6731
+ if (!isRecord3(value)) {
6242
6732
  return false;
6243
6733
  }
6244
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);
@@ -6413,6 +6903,42 @@ function wrapAgentsStream(stream) {
6413
6903
  });
6414
6904
  }
6415
6905
 
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;
6913
+ }
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
+
6416
6942
  // src/wrappers/ollama.ts
6417
6943
  function wrapOllama(ollama) {
6418
6944
  if (isSupportedOllamaClient(ollama)) {
@@ -6499,14 +7025,14 @@ function wrapCohere(cohere) {
6499
7025
  return cohere;
6500
7026
  }
6501
7027
  var cohereProxyCache = /* @__PURE__ */ new WeakMap();
6502
- function isRecord3(value) {
7028
+ function isRecord4(value) {
6503
7029
  return typeof value === "object" && value !== null;
6504
7030
  }
6505
7031
  function hasFunction4(value, methodName) {
6506
- return isRecord3(value) && methodName in value && typeof value[methodName] === "function";
7032
+ return isRecord4(value) && methodName in value && typeof value[methodName] === "function";
6507
7033
  }
6508
7034
  function isSupportedCohereClient(value) {
6509
- if (!isRecord3(value)) {
7035
+ if (!isRecord4(value)) {
6510
7036
  return false;
6511
7037
  }
6512
7038
  return hasFunction4(value, "chat") || hasFunction4(value, "chatStream") || hasFunction4(value, "embed") || hasFunction4(value, "rerank");
@@ -6644,20 +7170,20 @@ function wrapGroq(groq) {
6644
7170
  console.warn("Unsupported Groq library. Not wrapping.");
6645
7171
  return groq;
6646
7172
  }
6647
- function isRecord4(value) {
7173
+ function isRecord5(value) {
6648
7174
  return typeof value === "object" && value !== null;
6649
7175
  }
6650
7176
  function hasFunction6(value, methodName) {
6651
- return isRecord4(value) && methodName in value && typeof value[methodName] === "function";
7177
+ return isRecord5(value) && methodName in value && typeof value[methodName] === "function";
6652
7178
  }
6653
7179
  function hasChat2(value) {
6654
- return isRecord4(value) && isRecord4(value.completions) && hasFunction6(value.completions, "create");
7180
+ return isRecord5(value) && isRecord5(value.completions) && hasFunction6(value.completions, "create");
6655
7181
  }
6656
7182
  function hasEmbeddings2(value) {
6657
7183
  return hasFunction6(value, "create");
6658
7184
  }
6659
7185
  function isSupportedGroqClient(value) {
6660
- 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));
6661
7187
  }
6662
7188
  function groqProxy(groq) {
6663
7189
  const privateMethodWorkaroundCache = /* @__PURE__ */ new WeakMap();
@@ -6740,11 +7266,11 @@ var BEDROCK_RUNTIME_OPERATION_METHODS = /* @__PURE__ */ new Set([
6740
7266
  "invokeModelWithBidirectionalStream",
6741
7267
  "invokeModelWithResponseStream"
6742
7268
  ]);
6743
- function isRecord5(value) {
7269
+ function isRecord6(value) {
6744
7270
  return typeof value === "object" && value !== null;
6745
7271
  }
6746
7272
  function isSupportedBedrockRuntimeClient(value) {
6747
- return isRecord5(value) && typeof value.send === "function";
7273
+ return isRecord6(value) && typeof value.send === "function";
6748
7274
  }
6749
7275
  function bedrockRuntimeProxy(client) {
6750
7276
  const cached = bedrockRuntimeProxyCache.get(client);
@@ -9029,12 +9555,272 @@ function waterfall(tasks, callback) {
9029
9555
  }
9030
9556
  var waterfall$1 = awaitify(waterfall);
9031
9557
 
9558
+ // src/framework.ts
9559
+ import { v5 as uuidv5 } from "uuid";
9560
+
9032
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
+ }
9033
9818
  var SpanFetcher = class _SpanFetcher extends ObjectFetcher {
9034
- 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);
9035
9821
  const filterExpr = _SpanFetcher.buildFilter(
9036
9822
  rootSpanId,
9037
- spanTypeFilter,
9823
+ normalizedFilters,
9038
9824
  includeScorers
9039
9825
  );
9040
9826
  super(
@@ -9055,42 +9841,22 @@ var SpanFetcher = class _SpanFetcher extends ObjectFetcher {
9055
9841
  rootSpanId;
9056
9842
  _state;
9057
9843
  spanTypeFilter;
9058
- static buildFilter(rootSpanId, spanTypeFilter, includeScorers = false) {
9844
+ static buildFilter(rootSpanId, filters, includeScorers = false) {
9845
+ const purpose = ["span_attributes", "purpose"];
9059
9846
  const children = [
9060
- // Base filter: root_span_id = 'value'
9061
- {
9062
- op: "eq",
9063
- left: { op: "ident", name: ["root_span_id"] },
9064
- right: { op: "literal", value: rootSpanId }
9065
- }
9847
+ btqlComparison("eq", ["root_span_id"], rootSpanId)
9066
9848
  ];
9067
9849
  if (!includeScorers) {
9068
9850
  children.push({
9069
9851
  op: "or",
9070
9852
  children: [
9071
- {
9072
- op: "isnull",
9073
- expr: { op: "ident", name: ["span_attributes", "purpose"] }
9074
- },
9075
- {
9076
- op: "ne",
9077
- left: { op: "ident", name: ["span_attributes", "purpose"] },
9078
- right: { op: "literal", value: "scorer" }
9079
- }
9853
+ btqlNullCheck("isnull", purpose),
9854
+ btqlComparison("ne", purpose, "scorer")
9080
9855
  ]
9081
9856
  });
9082
9857
  }
9083
- if (spanTypeFilter && spanTypeFilter.length > 0) {
9084
- children.push({
9085
- op: "in",
9086
- left: { op: "ident", name: ["span_attributes", "type"] },
9087
- right: { op: "literal", value: spanTypeFilter }
9088
- });
9089
- }
9090
- return {
9091
- op: "and",
9092
- children
9093
- };
9858
+ children.push(...spanFilterClauses(filters));
9859
+ return { op: "and", children };
9094
9860
  }
9095
9861
  get id() {
9096
9862
  return Promise.resolve(this._objectId);
@@ -9105,67 +9871,80 @@ var CachedSpanFetcher = class {
9105
9871
  fetchFn;
9106
9872
  constructor(objectTypeOrFetchFn, objectId, rootSpanId, getState, brainstoreRealtime = true) {
9107
9873
  if (typeof objectTypeOrFetchFn === "function") {
9108
- this.fetchFn = (spanType) => objectTypeOrFetchFn(spanType);
9874
+ this.fetchFn = async (filters) => (await objectTypeOrFetchFn(filters.filters.spanType)).filter(
9875
+ (span) => matchesSpanFilters(span, filters)
9876
+ );
9109
9877
  } else {
9110
9878
  const objectType = objectTypeOrFetchFn;
9111
- this.fetchFn = async (spanType, includeScorers) => {
9879
+ this.fetchFn = async (filters, includeScorers) => {
9112
9880
  const state = await getState();
9113
9881
  const fetcher = new SpanFetcher(
9114
9882
  objectType,
9115
9883
  objectId,
9116
9884
  rootSpanId,
9117
9885
  state,
9118
- spanType,
9886
+ void 0,
9119
9887
  includeScorers,
9120
- brainstoreRealtime
9888
+ brainstoreRealtime,
9889
+ filters.filters
9121
9890
  );
9122
- const rows = await fetcher.fetchedData();
9123
- return rows.map((row) => ({
9124
- input: row.input,
9125
- output: row.output,
9126
- expected: row.expected,
9127
- error: row.error,
9128
- scores: row.scores,
9129
- metrics: row.metrics,
9130
- metadata: row.metadata,
9131
- span_id: row.span_id,
9132
- span_parents: row.span_parents,
9133
- is_root: row.is_root,
9134
- span_attributes: row.span_attributes,
9135
- id: row.id,
9136
- _xact_id: row._xact_id,
9137
- _pagination_key: row._pagination_key,
9138
- root_span_id: row.root_span_id,
9139
- created: row.created,
9140
- tags: row.tags
9141
- }));
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;
9142
9898
  };
9143
9899
  }
9144
9900
  }
9145
9901
  async getSpans({
9146
- spanType,
9902
+ spanType: requestedSpanType,
9903
+ filters,
9147
9904
  includeScorers = false
9148
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
+ }
9149
9914
  if (includeScorers) {
9150
- return this.fetchFn(spanType, true);
9915
+ return this.fetchFn(normalizedFilters, true);
9151
9916
  }
9152
9917
  if (this.allFetched) {
9153
- return this.getFromCache(spanType);
9918
+ const spans = this.getFromCache(spanType);
9919
+ return hasAdvancedFilters ? spans.filter((span) => matchesSpanFilters(span, normalizedFilters)) : spans;
9154
9920
  }
9155
- 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();
9156
9931
  await this.fetchSpans(void 0);
9157
- this.allFetched = true;
9932
+ if (this.spanCache.size > 0) {
9933
+ this.allFetched = true;
9934
+ }
9158
9935
  return this.getFromCache(void 0);
9159
9936
  }
9160
- const missingTypes = spanType.filter((t) => !this.spanCache.has(t));
9161
- if (missingTypes.length === 0) {
9162
- return this.getFromCache(spanType);
9937
+ const missingTypes = spanType.filter((type) => !this.spanCache.has(type));
9938
+ if (missingTypes.length > 0) {
9939
+ await this.fetchSpans(missingTypes);
9163
9940
  }
9164
- await this.fetchSpans(missingTypes);
9165
9941
  return this.getFromCache(spanType);
9166
9942
  }
9167
9943
  async fetchSpans(spanType) {
9168
- const spans = await this.fetchFn(spanType, false);
9944
+ const spans = await this.fetchFn(
9945
+ normalizeSpanFilters(spanType ? { spanType } : void 0),
9946
+ false
9947
+ );
9169
9948
  for (const span of spans) {
9170
9949
  const type = span.span_attributes?.type ?? "";
9171
9950
  const existing = this.spanCache.get(type) ?? [];
@@ -9245,34 +10024,20 @@ var LocalTrace = class {
9245
10024
  */
9246
10025
  async getSpans({
9247
10026
  spanType,
10027
+ filters,
9248
10028
  includeScorers = false
9249
10029
  } = {}) {
10030
+ const normalizedFilters = normalizeSpanFilters(filters, spanType);
9250
10031
  const cachedSpans = this.state.spanCache.getByRootSpanId(this.rootSpanId);
9251
10032
  if (cachedSpans && cachedSpans.length > 0) {
9252
- let spans = includeScorers ? cachedSpans : cachedSpans.filter(
9253
- (span) => span.span_attributes?.purpose !== "scorer"
9254
- );
9255
- if (spanType && spanType.length > 0) {
9256
- spans = spans.filter(
9257
- (span) => spanType.includes(span.span_attributes?.type ?? "")
9258
- );
9259
- }
9260
- return spans.map((span) => ({
9261
- input: span.input,
9262
- output: span.output,
9263
- expected: span.expected,
9264
- error: span.error,
9265
- scores: span.scores,
9266
- metrics: span.metrics,
9267
- metadata: span.metadata,
9268
- span_id: span.span_id,
9269
- span_parents: span.span_parents,
9270
- is_root: span.is_root,
9271
- span_attributes: span.span_attributes,
9272
- tags: span.tags
9273
- }));
9274
- }
9275
- 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
+ });
9276
10041
  }
9277
10042
  /**
9278
10043
  * Get the thread (preprocessed messages) for this trace.
@@ -9692,7 +10457,7 @@ async function Eval(name, evaluator, reporterOrOpts) {
9692
10457
  data,
9693
10458
  { disabled: Boolean(options.parent || options.noSendLogs) }
9694
10459
  );
9695
- if (experiment && typeof process !== "undefined" && globalThis.BRAINTRUST_CONTEXT_MANAGER !== void 0) {
10460
+ if (experiment) {
9696
10461
  await experiment._waitForId();
9697
10462
  }
9698
10463
  if (experiment && options.onStart) {
@@ -10017,6 +10782,10 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
10017
10782
  } : void 0;
10018
10783
  const parsedDatumOrigin = ObjectReference.safeParse(datum.origin);
10019
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;
10020
10789
  const baseEvent = {
10021
10790
  name: "eval",
10022
10791
  spanAttributes: {
@@ -10027,7 +10796,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
10027
10796
  expected: "expected" in datum ? datum.expected : void 0,
10028
10797
  tags: datum.tags,
10029
10798
  origin,
10030
- ...datum.upsert_id ? { id: datum.upsert_id } : {}
10799
+ ...upsertId ? { id: upsertId } : {}
10031
10800
  }
10032
10801
  };
10033
10802
  const callback = async (rootSpan) => {
@@ -10536,14 +11305,14 @@ var defaultReporter = {
10536
11305
  }
10537
11306
  };
10538
11307
 
10539
- // src/durable-eval.ts
11308
+ // src/workflow-eval.ts
10540
11309
  var encoder = new TextEncoder();
10541
11310
  var decoder = new TextDecoder();
10542
- var BATCH_TASK_KIND = "braintrust.durable.batch-task";
10543
- var BATCH_SCORER_KIND = "braintrust.durable.batch-scorer";
10544
- var DEFAULT_BATCH_SIZE = 1e3;
10545
- var DurableEvalMemoryStore = class {
11311
+ var WORKFLOW_TASK_KIND = "braintrust.workflow.task";
11312
+ var WORKFLOW_SCORER_KIND = "braintrust.workflow.scorer";
11313
+ var WorkflowEvalMemoryStore = class {
10546
11314
  values = /* @__PURE__ */ new Map();
11315
+ sets = /* @__PURE__ */ new Map();
10547
11316
  async read(key) {
10548
11317
  return this.values.get(key)?.slice();
10549
11318
  }
@@ -10556,8 +11325,16 @@ var DurableEvalMemoryStore = class {
10556
11325
  this.values.set(key, value.slice());
10557
11326
  return { value: value.slice(), created: true };
10558
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
+ }
10559
11336
  };
10560
- var DurableEvalRedisStore = class {
11337
+ var WorkflowEvalRedisStore = class {
10561
11338
  client;
10562
11339
  keyPrefix;
10563
11340
  ttlMs;
@@ -10566,14 +11343,16 @@ var DurableEvalRedisStore = class {
10566
11343
  this.keyPrefix = options.keyPrefix ?? "braintrust-eval:";
10567
11344
  this.ttlMs = options.ttlMs ?? 1e3 * 60 * 60 * 24 * 7;
10568
11345
  if (!Number.isInteger(this.ttlMs) || this.ttlMs < 1) {
10569
- throw new Error("DurableEvalRedisStore ttlMs must be a positive integer");
11346
+ throw new Error(
11347
+ "WorkflowEvalRedisStore ttlMs must be a positive integer"
11348
+ );
10570
11349
  }
10571
11350
  }
10572
11351
  async read(key) {
10573
11352
  const value = await this.client.get(`${this.keyPrefix}${key}`);
10574
11353
  if (value == null) return void 0;
10575
11354
  if (typeof value !== "string") {
10576
- throw new Error("DurableEvalRedisStore expected GET to return a string");
11355
+ throw new Error("WorkflowEvalRedisStore expected GET to return a string");
10577
11356
  }
10578
11357
  return base64ToUint8Array(value);
10579
11358
  }
@@ -10590,7 +11369,7 @@ var DurableEvalRedisStore = class {
10590
11369
  await set.call(client, redisKey, encoded, { px: this.ttlMs });
10591
11370
  } else {
10592
11371
  throw new Error(
10593
- "DurableEvalRedisStore requires a node-redis, ioredis, or @upstash/redis client"
11372
+ "WorkflowEvalRedisStore requires a node-redis, ioredis, or @upstash/redis client"
10594
11373
  );
10595
11374
  }
10596
11375
  }
@@ -10608,49 +11387,73 @@ var DurableEvalRedisStore = class {
10608
11387
  setOptions = [{ px: this.ttlMs, nx: true, get: true }];
10609
11388
  } else {
10610
11389
  throw new Error(
10611
- "DurableEvalRedisStore getOrSet requires a node-redis, ioredis, or @upstash/redis client"
11390
+ "WorkflowEvalRedisStore getOrSet requires a node-redis, ioredis, or @upstash/redis client"
10612
11391
  );
10613
11392
  }
10614
11393
  const existing = await set.call(client, redisKey, encoded, ...setOptions);
10615
11394
  if (existing === null) return { value: value.slice(), created: true };
10616
11395
  if (typeof existing !== "string") {
10617
11396
  throw new Error(
10618
- "DurableEvalRedisStore expected atomic SET to return a string or null"
11397
+ "WorkflowEvalRedisStore expected atomic SET to return a string or null"
10619
11398
  );
10620
11399
  }
10621
11400
  return { value: base64ToUint8Array(existing), created: false };
10622
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
+ }
10623
11423
  };
10624
- var BatchTask = class {
11424
+ var WorkflowTask = class {
10625
11425
  constructor(processor) {
10626
11426
  this.processor = processor;
10627
11427
  }
10628
11428
  processor;
10629
- kind = BATCH_TASK_KIND;
11429
+ kind = WORKFLOW_TASK_KIND;
10630
11430
  };
10631
- var BatchScorer = class {
11431
+ var WorkflowScorer = class {
10632
11432
  constructor(processor) {
10633
11433
  this.processor = processor;
10634
11434
  this.name = processor.name;
10635
11435
  }
10636
11436
  processor;
10637
- kind = BATCH_SCORER_KIND;
11437
+ kind = WORKFLOW_SCORER_KIND;
10638
11438
  name;
10639
11439
  };
10640
- 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
+ }
10641
11444
  const definition = {
10642
11445
  projectName,
10643
11446
  evalName: evaluator.experimentName ?? projectName,
10644
11447
  evaluator
10645
11448
  };
10646
11449
  return {
10647
- start: (options = {}) => startDurableEval(definition, options),
10648
- status: (options) => getDurableEvalStatus(definition, options),
10649
- poll: (options) => pollDurableEval(definition, options),
10650
- 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)
10651
11454
  };
10652
11455
  }
10653
- async function startDurableEval(definition, options) {
11456
+ async function startWorkflowEval(definition, options) {
10654
11457
  const store = definition.evaluator.store;
10655
11458
  const runId = newId();
10656
11459
  const key = runKey(definition.projectName, definition.evalName, runId);
@@ -10670,7 +11473,7 @@ async function startDurableEval(definition, options) {
10670
11473
  update: true
10671
11474
  }
10672
11475
  );
10673
- if (!isBatchTask(definition.evaluator.task) && !(definition.evaluator.scores ?? []).some(isBatchScorer)) {
11476
+ if (!isWorkflowTask(definition.evaluator.task) && !(definition.evaluator.scores ?? []).some(isWorkflowScorer)) {
10674
11477
  const result = await runEvaluator(
10675
11478
  experiment,
10676
11479
  {
@@ -10697,67 +11500,91 @@ async function startDurableEval(definition, options) {
10697
11500
  parameters: assertJsonValue(parameters, "eval parameters"),
10698
11501
  status: "completed",
10699
11502
  summary: result.summary,
11503
+ caseCount: 0,
10700
11504
  cases: [],
10701
- batches: []
11505
+ submissions: []
10702
11506
  };
10703
11507
  await experiment?.flush();
10704
11508
  await writeRunRecord(store, key, state2);
10705
11509
  return currentStatus(definition, state2);
10706
11510
  }
11511
+ const cases = await materializeCases(definition, data, experiment);
10707
11512
  const state = {
10708
11513
  runId,
10709
11514
  experimentName,
10710
11515
  noSendLogs: options.noSendLogs ?? false,
10711
11516
  parameters: assertJsonValue(parameters, "eval parameters"),
10712
11517
  status: "running",
10713
- cases: await materializeCases(definition, data, experiment),
10714
- batches: []
11518
+ caseCount: cases.length,
11519
+ cases,
11520
+ submissions: []
10715
11521
  };
11522
+ await writeJson(
11523
+ store,
11524
+ `${key}/case-ids`,
11525
+ cases.map(({ id }) => id)
11526
+ );
10716
11527
  await writeCaseBaseRecords(store, key, state.cases);
10717
11528
  await writeRunRecord(store, key, state);
10718
- return advanceDurableEval(definition, state, store, key, experiment);
11529
+ return advanceWorkflowEval(definition, state, store, key, experiment);
10719
11530
  }
10720
- async function getDurableEvalStatus(definition, options) {
11531
+ async function getWorkflowEvalStatus(definition, options) {
10721
11532
  const store = definition.evaluator.store;
10722
- const state = await readRunState(
10723
- definition,
10724
- store,
10725
- runKey(definition.projectName, definition.evalName, options.runId)
11533
+ const key = runKey(
11534
+ definition.projectName,
11535
+ definition.evalName,
11536
+ options.runId
10726
11537
  );
10727
- 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`);
10728
11540
  return currentStatus(definition, state);
10729
11541
  }
10730
- async function processDurableBatchResult(definition, result) {
10731
- if (!result.batchId && !result.externalId) {
10732
- 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");
10733
11545
  }
10734
11546
  const store = definition.evaluator.store;
10735
11547
  const key = runKey(definition.projectName, definition.evalName, result.runId);
10736
- const state = await readRunState(definition, store, key);
10737
- if (!state) throw new Error(`Durable eval run ${result.runId} is missing`);
10738
- const byBatch = result.batchId ? state.batches.find((candidate) => candidate.id === result.batchId) : void 0;
10739
- const byExternal = result.externalId ? state.batches.find(
10740
- (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)}`
11553
+ ) : void 0;
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)
10741
11563
  ) : void 0;
10742
- if (byBatch && byExternal && byBatch.id !== byExternal.id) {
10743
- throw new Error("batchId and externalId identify different batches");
10744
- }
10745
- const batch = byBatch ?? byExternal;
10746
- if (!batch) throw new Error("No submitted batch matches this result");
10747
- if (batch.status !== "complete") {
10748
- const records = await collectBatch(definition, state, batch);
10749
- batch.status = "complete";
10750
- await writeCaseRecords(store, key, records);
10751
- await writeBatchRecords(store, key, [batch]);
10752
- }
10753
- return advanceDurableEval(
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(
10754
11581
  definition,
10755
- await readRunState(definition, store, key),
11582
+ await readRunState(definition, store, key, [submission.itemId]),
10756
11583
  store,
10757
11584
  key
10758
11585
  );
10759
11586
  }
10760
- async function pollDurableEval(definition, options) {
11587
+ async function pollWorkflowEval(definition, options) {
10761
11588
  const store = definition.evaluator.store;
10762
11589
  const key = runKey(
10763
11590
  definition.projectName,
@@ -10765,39 +11592,53 @@ async function pollDurableEval(definition, options) {
10765
11592
  options.runId
10766
11593
  );
10767
11594
  const state = await readRunState(definition, store, key);
10768
- if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
10769
- const batches = state.batches.filter((batch) => {
10770
- if (batch.status === "complete") return false;
10771
- return processorForStage(definition, batch.kind, batch.scorerName).completion.mode === "poll";
10772
- });
10773
- const results = await Promise.all(
10774
- batches.map(async (batch) => ({
10775
- batch,
10776
- result: await processorForStage(definition, batch.kind, batch.scorerName).completion.poll(batch.submissionData, {
10777
- runId: state.runId,
10778
- batchId: batch.id
10779
- })
10780
- }))
10781
- );
10782
- const changedCases = /* @__PURE__ */ new Map();
10783
- const changedBatches = [];
10784
- 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
+ });
10785
11611
  if (result.status === "failed") throw asError(result.error);
10786
- if (result.status !== "complete") continue;
10787
- for (const record of await collectBatch(definition, state, batch)) {
10788
- changedCases.set(record.id, record);
10789
- }
10790
- batch.status = "complete";
10791
- changedBatches.push(batch);
10792
- }
10793
- if (changedBatches.length > 0) {
10794
- await writeCaseRecords(store, key, [...changedCases.values()]);
10795
- 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));
10796
11635
  }
10797
- const currentState = changedBatches.length > 0 ? await readRunState(definition, store, key) : state;
10798
- 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;
10799
11640
  }
10800
- async function openDurableExperiment(definition, state) {
11641
+ async function openWorkflowExperiment(definition, state) {
10801
11642
  const data = [];
10802
11643
  return await _internalInitEvaluatorExperiment(
10803
11644
  definition.projectName,
@@ -10810,45 +11651,49 @@ async function openDurableExperiment(definition, state) {
10810
11651
  }
10811
11652
  );
10812
11653
  }
10813
- async function advanceDurableEval(definition, state, store, key, existingExperiment) {
11654
+ async function advanceWorkflowEval(definition, state, store, key, existingExperiment) {
10814
11655
  if (state.status === "completed") return currentStatus(definition, state);
10815
- 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);
10816
11658
  await runTaskStage(definition, state, store, key, experiment);
10817
11659
  await logCompletedTasks(definition, state, store, key, experiment);
10818
- state = await readRunState(definition, store, key) ?? state;
10819
- if (state.cases.some((record) => !record.taskComplete || !record.taskLogged)) {
10820
- return currentStatus(definition, state);
10821
- }
11660
+ state = await readRunState(definition, store, key, caseIds) ?? state;
10822
11661
  await runScoreStages(definition, state, store, key, experiment);
10823
- state = await readRunState(definition, store, key) ?? state;
11662
+ state = await readRunState(definition, store, key, caseIds) ?? state;
10824
11663
  const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
10825
11664
  ({ name }) => name
10826
11665
  );
10827
11666
  const classifierNames = (definition.evaluator.classifiers ?? []).map(
10828
11667
  classifierName
10829
11668
  );
10830
- if (state.cases.some(
10831
- (record) => scorerNames.some(
10832
- (name) => !Object.hasOwn(record.scores, name) || !Object.hasOwn(record.loggedScores, name)
10833
- ) || classifierNames.some(
10834
- (name) => !Object.hasOwn(record.loggedClassifications, name)
10835
- )
10836
- )) {
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) {
10837
11681
  return currentStatus(definition, state);
10838
11682
  }
10839
11683
  if (!await claimAction(store, key, "finish")) {
10840
- const latest = await readRunState(definition, store, key);
11684
+ const latest = await readJson(store, key);
10841
11685
  return currentStatus(definition, latest ?? state);
10842
11686
  }
11687
+ state = await readRunState(definition, store, key);
10843
11688
  state.summary = await finishExperiment(definition, state, experiment);
10844
11689
  state.status = "completed";
10845
11690
  await writeRunRecord(store, key, state);
10846
11691
  return currentStatus(definition, state);
10847
11692
  }
10848
- function currentStatus(definition, state) {
11693
+ async function currentStatus(definition, state) {
10849
11694
  if (state.status === "completed") {
10850
11695
  if (!state.summary) {
10851
- 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`);
10852
11697
  }
10853
11698
  return {
10854
11699
  status: "completed",
@@ -10857,11 +11702,20 @@ function currentStatus(definition, state) {
10857
11702
  summary: state.summary
10858
11703
  };
10859
11704
  }
11705
+ const key = runKey(definition.projectName, definition.evalName, state.runId);
11706
+ const store = definition.evaluator.store;
10860
11707
  const pending = { poll: 0, webhook: 0 };
10861
- for (const batch of state.batches) {
10862
- if (batch.status === "complete") continue;
10863
- pending[processorForStage(definition, batch.kind, batch.scorerName).completion.mode]++;
10864
- }
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
+ );
10865
11719
  return { status: "waiting", runId: state.runId, pending };
10866
11720
  }
10867
11721
  async function startCaseRoot(definition, state, record, experiment) {
@@ -10922,7 +11776,7 @@ async function logTaskResult(definition, state, record, experiment, task) {
10922
11776
  expected: "expected" in datum ? datum.expected : void 0,
10923
11777
  metadata: {
10924
11778
  ...record.metadata,
10925
- durable_eval: {
11779
+ workflow_eval: {
10926
11780
  run_id: state.runId,
10927
11781
  case_id: record.caseId,
10928
11782
  trial_index: record.trialIndex
@@ -10953,8 +11807,8 @@ async function logCompletedTasks(definition, state, store, key, experiment) {
10953
11807
  }
10954
11808
  }
10955
11809
  async function runTaskStage(definition, state, store, key, experiment) {
10956
- if (isBatchTask(definition.evaluator.task)) {
10957
- await ensureBatches(definition, state, store, key, "task");
11810
+ if (isWorkflowTask(definition.evaluator.task)) {
11811
+ await ensureSubmissions(definition, state, store, key, "task");
10958
11812
  return;
10959
11813
  }
10960
11814
  const task = definition.evaluator.task;
@@ -10980,8 +11834,9 @@ async function runScoreStages(definition, state, store, key, experiment) {
10980
11834
  changed.clear();
10981
11835
  };
10982
11836
  for (const { name, scorer } of scorers) {
10983
- if (isBatchScorer(scorer)) {
11837
+ if (isWorkflowScorer(scorer)) {
10984
11838
  for (const record of state.cases) {
11839
+ if (!record.taskComplete || !record.taskLogged) continue;
10985
11840
  if (Object.hasOwn(record.scores, name) && !Object.hasOwn(record.loggedScores, name)) {
10986
11841
  if (!await claimAction(store, key, "score-log", record.id, name)) {
10987
11842
  continue;
@@ -10997,10 +11852,11 @@ async function runScoreStages(definition, state, store, key, experiment) {
10997
11852
  }
10998
11853
  }
10999
11854
  await persistChangedCases();
11000
- await ensureBatches(definition, state, store, key, "score", name);
11855
+ await ensureSubmissions(definition, state, store, key, "score", name);
11001
11856
  continue;
11002
11857
  }
11003
11858
  for (const record of state.cases) {
11859
+ if (!record.taskComplete || !record.taskLogged) continue;
11004
11860
  if (Object.hasOwn(record.loggedScores, name)) continue;
11005
11861
  if (!await claimAction(store, key, "score", record.id, name)) continue;
11006
11862
  await evaluateAndLogScore(
@@ -11017,6 +11873,7 @@ async function runScoreStages(definition, state, store, key, experiment) {
11017
11873
  for (const [index, classifier] of (definition.evaluator.classifiers ?? []).entries()) {
11018
11874
  const name = classifierName(classifier, index);
11019
11875
  for (const record of state.cases) {
11876
+ if (!record.taskComplete || !record.taskLogged) continue;
11020
11877
  if (Object.hasOwn(record.loggedClassifications, name)) continue;
11021
11878
  if (!await claimAction(store, key, "classification", record.id, name)) {
11022
11879
  continue;
@@ -11039,13 +11896,14 @@ function scorerArgs(record) {
11039
11896
  return {
11040
11897
  ...datum,
11041
11898
  metadata: record.metadata,
11899
+ tags: record.tags,
11042
11900
  output: record.output
11043
11901
  };
11044
11902
  }
11045
11903
  function resumeCaseRoot(definition, record, experiment) {
11046
11904
  if (!experiment) return NOOP_SPAN;
11047
11905
  if (!record.rootSpan) {
11048
- 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`);
11049
11907
  }
11050
11908
  return _internalResumeSpan({
11051
11909
  exported: record.rootSpan,
@@ -11133,117 +11991,110 @@ async function evaluateAndLogClassification(definition, state, record, name, cla
11133
11991
  root.end();
11134
11992
  }
11135
11993
  }
11136
- async function ensureBatches(definition, state, store, key, kind, scorerName2) {
11994
+ async function ensureSubmissions(definition, state, store, key, kind, scorerName2) {
11137
11995
  const processor = processorForStage(definition, kind, scorerName2);
11138
- const plans = plannedBatches(
11996
+ const plans = plannedSubmissions(
11139
11997
  definition,
11140
11998
  state.runId,
11141
11999
  state.cases.map(({ id }) => id)
11142
12000
  );
11143
12001
  const casesById = new Map(state.cases.map((record) => [record.id, record]));
11144
- for (const plan of plans) {
11145
- if (plan.kind !== kind || plan.scorerName !== scorerName2) continue;
11146
- if (state.batches.some(({ id }) => id === plan.id)) continue;
11147
- const records = plan.itemIds.map((id) => casesById.get(id));
11148
- const ready = records.every(
11149
- (record) => kind === "task" ? !record.taskComplete : !Object.hasOwn(record.scores, scorerName2)
11150
- );
11151
- if (!ready) continue;
11152
- 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;
11153
12010
  const claim = await store.getOrSet(
11154
- claimRecordKey(key, "batch", batchId),
11155
- encoder.encode(batchId)
11156
- );
11157
- if (!claim.created) continue;
11158
- const context = { runId: state.runId, batchId };
11159
- const items = records.map(
11160
- (record) => kind === "task" ? taskBatchItem(record, state.parameters) : scorerBatchItem(record)
12011
+ claimRecordKey(key, "submission", submissionId),
12012
+ encoder.encode(submissionId)
11161
12013
  );
12014
+ if (!claim.created) return;
12015
+ const context = { runId: state.runId, submissionId };
12016
+ const item = kind === "task" ? taskSubmissionItem(record, state.parameters) : scorerSubmissionItem(record);
11162
12017
  const submissionData = assertJsonValue(
11163
- await processor.submit(items, context),
11164
- `submission data for batch ${batchId}`
12018
+ await processor.submit(item, context),
12019
+ `submission data for submission ${submissionId}`
11165
12020
  );
11166
12021
  const externalId = processor.completion.mode === "webhook" ? processor.completion.getExternalId(submissionData, context) : void 0;
11167
12022
  if (externalId !== void 0 && !externalId.trim()) {
11168
- throw new Error(`Batch ${batchId} produced an empty externalId`);
12023
+ throw new Error(
12024
+ `Submission ${submissionId} produced an empty externalId`
12025
+ );
11169
12026
  }
11170
- const batch = {
11171
- id: batchId,
12027
+ const submission = {
12028
+ id: submissionId,
11172
12029
  kind,
11173
12030
  scorerName: scorerName2,
11174
- itemIds: records.map((record) => record.id),
12031
+ itemId: record.id,
11175
12032
  submissionData,
11176
12033
  externalId,
11177
- status: "submitted"
12034
+ status: "submitted",
12035
+ completionMode: processor.completion.mode
11178
12036
  };
11179
- state.batches.push(batch);
11180
- await writeBatchRecords(store, key, [batch]);
11181
- }
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");
11182
12049
  }
11183
- async function collectBatch(definition, state, batch) {
11184
- const processor = processorForStage(definition, batch.kind, batch.scorerName);
11185
- const context = { runId: state.runId, batchId: batch.id };
11186
- const results = await processor.collect(batch.submissionData, context);
11187
- if (!Array.isArray(results)) {
11188
- 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
+ );
11189
12062
  }
11190
- const expectedIds = new Set(batch.itemIds);
11191
- const seen = /* @__PURE__ */ new Set();
11192
- const records = [];
11193
- for (const result of results) {
11194
- const id = resultItemId(result);
11195
- if (!expectedIds.has(id)) {
11196
- throw new Error(`Batch ${batch.id} returned unknown item ${id}`);
11197
- }
11198
- if (seen.has(id)) {
11199
- throw new Error(`Batch ${batch.id} returned item ${id} more than once`);
11200
- }
11201
- seen.add(id);
11202
- const record = state.cases.find((candidate) => candidate.id === id);
11203
- records.push(record);
11204
- if (batch.kind === "task") {
11205
- record.output = assertJsonValue(
11206
- result.output,
11207
- `task output for item ${id}`
11208
- );
11209
- if ("metadata" in result && result.metadata !== void 0) {
11210
- record.metadata = assertJsonValue(
11211
- {
11212
- ...record.metadata,
11213
- ...result.metadata
11214
- },
11215
- `metadata for ${id}`
11216
- );
11217
- }
11218
- if ("tags" in result && result.tags !== void 0)
11219
- record.tags = result.tags;
11220
- record.taskComplete = true;
11221
- } else {
11222
- record.scores[batch.scorerName] = assertJsonValue(
11223
- result.score,
11224
- `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}`
11225
12075
  );
11226
12076
  }
11227
- }
11228
- const missing = batch.itemIds.filter((id) => !seen.has(id));
11229
- if (missing.length > 0) {
11230
- throw new Error(
11231
- `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}`
11232
12083
  );
11233
12084
  }
11234
- return records;
12085
+ return record;
11235
12086
  }
11236
12087
  function processorForStage(definition, kind, scorerName2) {
11237
12088
  if (kind === "task") {
11238
- if (!isBatchTask(definition.evaluator.task)) {
11239
- 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");
11240
12091
  }
11241
12092
  return definition.evaluator.task.processor;
11242
12093
  }
11243
12094
  const scorer = resolveScorers(definition.evaluator.scores ?? []).find(
11244
12095
  ({ name }) => name === scorerName2
11245
12096
  )?.scorer;
11246
- if (!isBatchScorer(scorer)) {
12097
+ if (!isWorkflowScorer(scorer)) {
11247
12098
  throw new Error(`Definition no longer contains scorer ${scorerName2}`);
11248
12099
  }
11249
12100
  return scorer.processor;
@@ -11265,15 +12116,15 @@ async function materializeCases(definition, data, experiment) {
11265
12116
  const caseId = datum.id ?? datum.upsert_id ?? (evaluator.caseId ? await evaluator.caseId(datum) : void 0);
11266
12117
  if (!caseId) {
11267
12118
  throw new Error(
11268
- "Every durable eval case requires id, upsert_id, or caseId"
12119
+ "Every workflow eval case requires id, upsert_id, or caseId"
11269
12120
  );
11270
12121
  }
11271
12122
  if (seen.has(caseId))
11272
- throw new Error(`Duplicate durable eval case id: ${caseId}`);
12123
+ throw new Error(`Duplicate workflow eval case id: ${caseId}`);
11273
12124
  seen.add(caseId);
11274
12125
  const trialCount = datum.trialCount ?? evaluator.trialCount ?? 1;
11275
12126
  if (!Number.isInteger(trialCount) || trialCount < 1) {
11276
- throw new Error(`Invalid trialCount for durable eval case ${caseId}`);
12127
+ throw new Error(`Invalid trialCount for workflow eval case ${caseId}`);
11277
12128
  }
11278
12129
  for (let trialIndex = 0; trialIndex < trialCount; trialIndex++) {
11279
12130
  records.push({
@@ -11297,7 +12148,7 @@ async function materializeCases(definition, data, experiment) {
11297
12148
  }
11298
12149
  return records;
11299
12150
  }
11300
- function taskBatchItem(record, parameters) {
12151
+ function taskSubmissionItem(record, parameters) {
11301
12152
  const datum = record.datum;
11302
12153
  return {
11303
12154
  id: record.id,
@@ -11309,7 +12160,7 @@ function taskBatchItem(record, parameters) {
11309
12160
  trialIndex: record.trialIndex
11310
12161
  };
11311
12162
  }
11312
- function scorerBatchItem(record) {
12163
+ function scorerSubmissionItem(record) {
11313
12164
  const datum = record.datum;
11314
12165
  return {
11315
12166
  id: record.id,
@@ -11380,12 +12231,12 @@ async function finishExperiment(definition, state, experiment) {
11380
12231
  }
11381
12232
  function resolveScorers(scorers) {
11382
12233
  return scorers.map((scorer, index) => ({
11383
- name: isBatchScorer(scorer) ? scorer.name : scorer.name || `scorer_${index}`,
12234
+ name: isWorkflowScorer(scorer) ? scorer.name : scorer.name || `scorer_${index}`,
11384
12235
  scorer
11385
12236
  }));
11386
12237
  }
11387
12238
  function runKey(projectName, evalName, runId) {
11388
- 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}`))}`;
11389
12240
  }
11390
12241
  function encodedKeyPart(value) {
11391
12242
  return uint8ArrayToBase64(encoder.encode(value)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
@@ -11394,8 +12245,8 @@ function caseRecordKey(key, caseId, kind, ...names) {
11394
12245
  const suffix = names.map(encodedKeyPart).join("/");
11395
12246
  return `${key}/cases/${encodedKeyPart(caseId)}/${kind}${suffix ? `/${suffix}` : ""}`;
11396
12247
  }
11397
- function batchRecordKey(key, batchId) {
11398
- return `${key}/batches/${encodedKeyPart(batchId)}`;
12248
+ function submissionRecordKey(key, submissionId) {
12249
+ return `${key}/submissions/${encodedKeyPart(submissionId)}`;
11399
12250
  }
11400
12251
  function claimRecordKey(key, kind, ...parts) {
11401
12252
  const identity = stableStringify([kind, parts]);
@@ -11407,33 +12258,24 @@ async function claimAction(store, key, kind, ...parts) {
11407
12258
  encoder.encode("claimed")
11408
12259
  )).created;
11409
12260
  }
11410
- function plannedBatches(definition, runId, caseIds) {
12261
+ function plannedSubmissions(definition, runId, caseIds) {
11411
12262
  const stages = [];
11412
- if (isBatchTask(definition.evaluator.task)) stages.push({ kind: "task" });
12263
+ if (isWorkflowTask(definition.evaluator.task)) stages.push({ kind: "task" });
11413
12264
  for (const { name, scorer } of resolveScorers(
11414
12265
  definition.evaluator.scores ?? []
11415
12266
  )) {
11416
- if (isBatchScorer(scorer)) {
12267
+ if (isWorkflowScorer(scorer)) {
11417
12268
  stages.push({ kind: "score", scorerName: name });
11418
12269
  }
11419
12270
  }
11420
12271
  const plans = [];
11421
12272
  for (const { kind, scorerName: scorerName2 } of stages) {
11422
- const batchSize = processorForStage(definition, kind, scorerName2).batchSize ?? DEFAULT_BATCH_SIZE;
11423
- if (!Number.isInteger(batchSize) || batchSize < 1) {
11424
- throw new Error(
11425
- `Invalid batchSize for ${scorerName2 ?? "task"}: ${batchSize}`
11426
- );
11427
- }
11428
- for (let offset = 0; offset < caseIds.length; offset += batchSize) {
11429
- const itemIds = caseIds.slice(offset, offset + batchSize);
12273
+ for (const itemId of caseIds) {
11430
12274
  plans.push({
11431
- id: deterministicId(
11432
- stableStringify([runId, kind, scorerName2, itemIds])
11433
- ),
12275
+ id: deterministicId(stableStringify([runId, kind, scorerName2, itemId])),
11434
12276
  kind,
11435
12277
  scorerName: scorerName2,
11436
- itemIds
12278
+ itemId
11437
12279
  });
11438
12280
  }
11439
12281
  }
@@ -11493,7 +12335,7 @@ async function readCaseRecord(definition, store, key, id) {
11493
12335
  }))
11494
12336
  )
11495
12337
  ]);
11496
- if (!base) throw new Error(`Durable eval case ${id} is missing`);
12338
+ if (!base) throw new Error(`Workflow eval case ${id} is missing`);
11497
12339
  const scores = /* @__PURE__ */ Object.create(null);
11498
12340
  for (const { name, value } of scoreValues) {
11499
12341
  if (value !== void 0) scores[name] = value;
@@ -11524,32 +12366,34 @@ async function readCaseRecord(definition, store, key, id) {
11524
12366
  loggedClassifications
11525
12367
  };
11526
12368
  }
11527
- async function readRunState(definition, store, key) {
12369
+ async function readRunState(definition, store, key, caseIds) {
11528
12370
  const record = await readJson(store, key);
11529
12371
  if (!record) return void 0;
11530
- const plans = plannedBatches(definition, record.runId, record.caseIds);
11531
- 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([
11532
12377
  Promise.all(
11533
- record.caseIds.map((id) => readCaseRecord(definition, store, key, id))
12378
+ selectedIds.map((id) => readCaseRecord(definition, store, key, id))
11534
12379
  ),
11535
12380
  Promise.all(
11536
12381
  plans.map(async ({ id }) => {
11537
- return readJson(store, batchRecordKey(key, id));
12382
+ return readJson(
12383
+ store,
12384
+ submissionRecordKey(key, id)
12385
+ );
11538
12386
  })
11539
12387
  )
11540
12388
  ]);
11541
- const batches = batchRecords.filter(
12389
+ const submissions = submissionRecords.filter(
11542
12390
  (value) => value !== void 0
11543
12391
  );
11544
- const { caseIds: _caseIds, ...state } = record;
11545
- return { ...state, cases, batches };
12392
+ return { ...record, cases, submissions };
11546
12393
  }
11547
12394
  async function writeRunRecord(store, key, state) {
11548
- const { cases, batches: _batches, ...record } = state;
11549
- await writeJson(store, key, {
11550
- ...record,
11551
- caseIds: cases.map(({ id }) => id)
11552
- });
12395
+ const { cases: _cases, submissions: _submissions, ...record } = state;
12396
+ await writeJson(store, key, record);
11553
12397
  }
11554
12398
  async function writeCaseBaseRecords(store, key, records) {
11555
12399
  await Promise.all(
@@ -11621,11 +12465,25 @@ async function writeCaseRecords(store, key, records) {
11621
12465
  }
11622
12466
  await Promise.all(writes);
11623
12467
  }
11624
- async function writeBatchRecords(store, key, records) {
12468
+ async function writeSubmissionRecords(store, key, records) {
11625
12469
  await Promise.all(
11626
- records.map(
11627
- (record) => writeJson(store, batchRecordKey(key, record.id), record)
11628
- )
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
+ })
11629
12487
  );
11630
12488
  }
11631
12489
  function deterministicId(value) {
@@ -11670,17 +12528,11 @@ function assertJsonValue(value, label) {
11670
12528
  throw new Error(`${label} must be JSON serializable`, { cause: error });
11671
12529
  }
11672
12530
  }
11673
- function resultItemId(value) {
11674
- if (typeof value !== "object" || value === null || !("id" in value) || typeof value.id !== "string") {
11675
- throw new Error("Batch results must contain a string id");
11676
- }
11677
- return value.id;
11678
- }
11679
- function isBatchTask(value) {
11680
- 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;
11681
12533
  }
11682
- function isBatchScorer(value) {
11683
- 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;
11684
12536
  }
11685
12537
  function asError(error) {
11686
12538
  return error instanceof Error ? error : new Error(String(error));
@@ -12608,13 +13460,13 @@ var evaluatorDefinitionsSchema = z5.record(
12608
13460
  );
12609
13461
 
12610
13462
  // src/wrappers/elevenlabs.ts
12611
- var clients = /* @__PURE__ */ new WeakMap();
13463
+ var clients2 = /* @__PURE__ */ new WeakMap();
12612
13464
  function wrapElevenLabs(client) {
12613
13465
  if (!isObject(client) || !isObject(client.textToSpeech) || typeof client.textToSpeech.convert !== "function") {
12614
13466
  debugLogger.warn("Unsupported ElevenLabs client. Not wrapping.");
12615
13467
  return client;
12616
13468
  }
12617
- const cached = clients.get(client);
13469
+ const cached = clients2.get(client);
12618
13470
  if (cached) return cached;
12619
13471
  const sdk = client;
12620
13472
  const speech = new Proxy(sdk.textToSpeech, {
@@ -12661,8 +13513,8 @@ function wrapElevenLabs(client) {
12661
13513
  return Reflect.get(target, prop, receiver);
12662
13514
  }
12663
13515
  });
12664
- clients.set(client, proxy);
12665
- clients.set(proxy, proxy);
13516
+ clients2.set(client, proxy);
13517
+ clients2.set(proxy, proxy);
12666
13518
  return proxy;
12667
13519
  }
12668
13520
 
@@ -12677,8 +13529,6 @@ export {
12677
13529
  BRAINTRUST_PARENT_KEY,
12678
13530
  BaseAttachment,
12679
13531
  BaseExperiment,
12680
- BatchScorer,
12681
- BatchTask,
12682
13532
  BraintrustLangChainCallbackHandler,
12683
13533
  BraintrustMiddleware,
12684
13534
  BraintrustObservabilityExporter,
@@ -12692,8 +13542,6 @@ export {
12692
13542
  DEFAULT_MAX_REQUEST_SIZE,
12693
13543
  Dataset,
12694
13544
  DatasetPipeline,
12695
- DurableEvalMemoryStore,
12696
- DurableEvalRedisStore,
12697
13545
  ERR_PERMALINK,
12698
13546
  Eval,
12699
13547
  EvalResultWithSummary,
@@ -12728,6 +13576,10 @@ export {
12728
13576
  TestBackgroundLogger,
12729
13577
  ToolBuilder,
12730
13578
  UUIDGenerator,
13579
+ WorkflowEvalMemoryStore,
13580
+ WorkflowEvalRedisStore,
13581
+ WorkflowScorer,
13582
+ WorkflowTask,
12731
13583
  X_CACHED_HEADER,
12732
13584
  _exportsForTestingOnly,
12733
13585
  _internalGetGlobalState,
@@ -12753,12 +13605,13 @@ export {
12753
13605
  deepCopyEvent,
12754
13606
  exports_exports as default,
12755
13607
  defaultErrorScoreHandler,
12756
- defineDurableEval,
13608
+ defineWorkflowEval,
12757
13609
  deserializePlainStringAsJSON,
12758
13610
  devNullWritableStream,
12759
13611
  evaluatorDefinitionSchema,
12760
13612
  evaluatorDefinitionsSchema,
12761
13613
  extractTraceContextFromHeaders,
13614
+ failOpenAIAgentsTrace,
12762
13615
  flush,
12763
13616
  getContextManager,
12764
13617
  getIdGenerator,
@@ -12805,12 +13658,14 @@ export {
12805
13658
  setFetch,
12806
13659
  setMaskingFunction,
12807
13660
  spanComponentsToObjectId,
13661
+ startOpenAIAgentsTrace,
12808
13662
  startSpan,
12809
13663
  summarize,
12810
13664
  templateRegistry,
12811
13665
  ToolFunctionDefinition as toolFunctionDefinitionSchema,
12812
13666
  traceable,
12813
13667
  traced,
13668
+ updateOpenAIAgentsTrace,
12814
13669
  updateSpan,
12815
13670
  uploadLogs3OverflowPayload,
12816
13671
  utf8ByteLength,
@@ -12839,6 +13694,7 @@ export {
12839
13694
  wrapGroq,
12840
13695
  wrapHuggingFace,
12841
13696
  wrapHuggingFaceTransformers,
13697
+ wrapLangGraphSDK,
12842
13698
  wrapLangSmithClient,
12843
13699
  wrapLangSmithRunTrees,
12844
13700
  wrapLangSmithTraceable,