braintrust 3.27.0 → 3.28.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 (53) hide show
  1. package/dev/dist/index.d.mts +519 -186
  2. package/dev/dist/index.d.ts +519 -186
  3. package/dev/dist/index.js +1837 -1009
  4. package/dev/dist/index.mjs +1172 -344
  5. package/dist/apply-auto-instrumentation.js +262 -210
  6. package/dist/apply-auto-instrumentation.mjs +54 -2
  7. package/dist/auto-instrumentations/bundler/esbuild.cjs +81 -2
  8. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  9. package/dist/auto-instrumentations/bundler/next.cjs +81 -2
  10. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  11. package/dist/auto-instrumentations/bundler/rollup.cjs +81 -2
  12. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  13. package/dist/auto-instrumentations/bundler/vite.cjs +81 -2
  14. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  15. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +81 -2
  16. package/dist/auto-instrumentations/bundler/webpack.cjs +81 -2
  17. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  18. package/dist/auto-instrumentations/{chunk-XEYKUBLY.mjs → chunk-26PKVUKB.mjs} +80 -2
  19. package/dist/auto-instrumentations/{chunk-BW33ULMW.mjs → chunk-HD35AM3M.mjs} +1 -1
  20. package/dist/auto-instrumentations/{chunk-ZNHTSSGI.mjs → chunk-NP7V4XB2.mjs} +2 -1
  21. package/dist/auto-instrumentations/hook.mjs +236 -30
  22. package/dist/auto-instrumentations/index.cjs +2 -1
  23. package/dist/auto-instrumentations/index.mjs +1 -1
  24. package/dist/browser.d.mts +628 -53
  25. package/dist/browser.d.ts +628 -53
  26. package/dist/browser.js +2301 -284
  27. package/dist/browser.mjs +2301 -284
  28. package/dist/{chunk-MF7NU6BT.js → chunk-BBE7SNRV.js} +34 -4
  29. package/dist/{chunk-QRHGVBKU.js → chunk-OBBWQW6K.js} +1799 -1023
  30. package/dist/{chunk-YKD22IMR.mjs → chunk-UPFNQCGB.mjs} +966 -190
  31. package/dist/{chunk-CZM5JIQL.mjs → chunk-ZHUHZWFY.mjs} +33 -3
  32. package/dist/cli.js +1224 -398
  33. package/dist/edge-light.d.mts +1 -1
  34. package/dist/edge-light.d.ts +1 -1
  35. package/dist/edge-light.js +2301 -284
  36. package/dist/edge-light.mjs +2301 -284
  37. package/dist/index.d.mts +1212 -637
  38. package/dist/index.d.ts +1212 -637
  39. package/dist/index.js +1880 -590
  40. package/dist/index.mjs +1450 -160
  41. package/dist/instrumentation/index.d.mts +190 -6
  42. package/dist/instrumentation/index.d.ts +190 -6
  43. package/dist/instrumentation/index.js +823 -116
  44. package/dist/instrumentation/index.mjs +823 -116
  45. package/dist/vitest-evals-reporter.js +16 -16
  46. package/dist/vitest-evals-reporter.mjs +2 -2
  47. package/dist/workerd.d.mts +1 -1
  48. package/dist/workerd.d.ts +1 -1
  49. package/dist/workerd.js +2301 -284
  50. package/dist/workerd.mjs +2301 -284
  51. package/package.json +2 -3
  52. package/util/dist/index.d.mts +1545 -100
  53. package/util/dist/index.d.ts +1545 -100
package/dist/workerd.js CHANGED
@@ -38,6 +38,8 @@ __export(workerd_exports, {
38
38
  BRAINTRUST_PARENT_KEY: () => BRAINTRUST_PARENT_KEY,
39
39
  BaseAttachment: () => BaseAttachment,
40
40
  BaseExperiment: () => BaseExperiment,
41
+ BatchScorer: () => BatchScorer,
42
+ BatchTask: () => BatchTask,
41
43
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
42
44
  BraintrustMiddleware: () => BraintrustMiddleware,
43
45
  BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
@@ -51,6 +53,8 @@ __export(workerd_exports, {
51
53
  DEFAULT_MAX_REQUEST_SIZE: () => DEFAULT_MAX_REQUEST_SIZE,
52
54
  Dataset: () => Dataset2,
53
55
  DatasetPipeline: () => DatasetPipeline,
56
+ DurableEvalMemoryStore: () => DurableEvalMemoryStore,
57
+ DurableEvalRedisStore: () => DurableEvalRedisStore,
54
58
  ERR_PERMALINK: () => ERR_PERMALINK,
55
59
  Eval: () => Eval,
56
60
  EvalResultWithSummary: () => EvalResultWithSummary,
@@ -109,6 +113,7 @@ __export(workerd_exports, {
109
113
  deepCopyEvent: () => deepCopyEvent,
110
114
  default: () => exports_exports,
111
115
  defaultErrorScoreHandler: () => defaultErrorScoreHandler,
116
+ defineDurableEval: () => defineDurableEval,
112
117
  deserializePlainStringAsJSON: () => deserializePlainStringAsJSON,
113
118
  devNullWritableStream: () => devNullWritableStream,
114
119
  evaluatorDefinitionSchema: () => evaluatorDefinitionSchema,
@@ -204,7 +209,8 @@ __export(workerd_exports, {
204
209
  wrapPiCodingAgentSDK: () => wrapPiCodingAgentSDK,
205
210
  wrapStrandsAgentSDK: () => wrapStrandsAgentSDK,
206
211
  wrapTraced: () => wrapTraced,
207
- wrapVitest: () => wrapVitest
212
+ wrapVitest: () => wrapVitest,
213
+ wrapVoyageAI: () => wrapVoyageAI
208
214
  });
209
215
  module.exports = __toCommonJS(workerd_exports);
210
216
 
@@ -1771,12 +1777,12 @@ function base64ToUint8Array(base64) {
1771
1777
  return uint8Array;
1772
1778
  }
1773
1779
  function uint8ArrayToString(uint8Array) {
1774
- const decoder = new TextDecoder("utf-8");
1775
- return decoder.decode(uint8Array);
1780
+ const decoder2 = new TextDecoder("utf-8");
1781
+ return decoder2.decode(uint8Array);
1776
1782
  }
1777
1783
  function stringToUint8Array(str) {
1778
- const encoder = new TextEncoder();
1779
- return encoder.encode(str);
1784
+ const encoder2 = new TextEncoder();
1785
+ return encoder2.encode(str);
1780
1786
  }
1781
1787
 
1782
1788
  // util/span_identifier_v3.ts
@@ -2680,7 +2686,8 @@ var AclObjectType = import_v36.z.union([
2680
2686
  "org_member",
2681
2687
  "project_log",
2682
2688
  "org_project",
2683
- "org_audit_logs"
2689
+ "org_audit_logs",
2690
+ "project_group"
2684
2691
  ]),
2685
2692
  import_v36.z.null()
2686
2693
  ]);
@@ -2809,7 +2816,8 @@ var AsyncScoringState = import_v36.z.union([
2809
2816
  token: import_v36.z.string(),
2810
2817
  function_ids: import_v36.z.array(import_v36.z.unknown()),
2811
2818
  skip_logging: import_v36.z.union([import_v36.z.boolean(), import_v36.z.null()]).optional(),
2812
- triggered_functions: import_v36.z.union([import_v36.z.record(TriggeredFunctionState), import_v36.z.null()]).optional()
2819
+ triggered_functions: import_v36.z.union([import_v36.z.record(TriggeredFunctionState), import_v36.z.null()]).optional(),
2820
+ last_triggered_xact_id: import_v36.z.union([import_v36.z.string(), import_v36.z.number(), import_v36.z.null()]).optional()
2813
2821
  }),
2814
2822
  import_v36.z.object({ status: import_v36.z.literal("disabled") }),
2815
2823
  import_v36.z.null(),
@@ -2878,7 +2886,7 @@ var FunctionTypeEnum = import_v36.z.enum([
2878
2886
  "parameters",
2879
2887
  "sandbox"
2880
2888
  ]);
2881
- var NullableSavedFunctionId = import_v36.z.union([
2889
+ var FacetPreprocessorId = import_v36.z.union([
2882
2890
  import_v36.z.object({
2883
2891
  type: import_v36.z.literal("function"),
2884
2892
  id: import_v36.z.string(),
@@ -2889,10 +2897,23 @@ var NullableSavedFunctionId = import_v36.z.union([
2889
2897
  name: import_v36.z.string(),
2890
2898
  function_type: FunctionTypeEnum.optional().default("scorer")
2891
2899
  }),
2900
+ import_v36.z.object({ type: import_v36.z.literal("inline"), code: import_v36.z.string().min(1) }),
2892
2901
  import_v36.z.null()
2893
2902
  ]);
2903
+ var SavedFunctionId = import_v36.z.union([
2904
+ import_v36.z.object({
2905
+ type: import_v36.z.literal("function"),
2906
+ id: import_v36.z.string(),
2907
+ version: import_v36.z.string().optional()
2908
+ }),
2909
+ import_v36.z.object({
2910
+ type: import_v36.z.literal("global"),
2911
+ name: import_v36.z.string(),
2912
+ function_type: FunctionTypeEnum.optional().default("scorer")
2913
+ })
2914
+ ]);
2894
2915
  var TopicMapGenerationSettings = import_v36.z.object({
2895
- algorithm: import_v36.z.enum(["hdbscan", "kmeans"]),
2916
+ algorithm: import_v36.z.enum(["hdbscan", "kmeans", "community"]),
2896
2917
  dimension_reduction: import_v36.z.enum(["umap", "pca", "none"]),
2897
2918
  sample_size: import_v36.z.number().int().gt(0).optional(),
2898
2919
  n_clusters: import_v36.z.number().int().gt(0).optional(),
@@ -2904,6 +2925,7 @@ var TopicMapGenerationSettings = import_v36.z.object({
2904
2925
  var TopicMapData = import_v36.z.object({
2905
2926
  type: import_v36.z.literal("topic_map"),
2906
2927
  source_facet: import_v36.z.string(),
2928
+ source_facet_function: SavedFunctionId.and(import_v36.z.unknown()).optional(),
2907
2929
  embedding_model: import_v36.z.string(),
2908
2930
  bundle_key: import_v36.z.string().optional(),
2909
2931
  report_key: import_v36.z.string().optional(),
@@ -2917,7 +2939,7 @@ var TopicMapData = import_v36.z.object({
2917
2939
  });
2918
2940
  var BatchedFacetData = import_v36.z.object({
2919
2941
  type: import_v36.z.literal("batched_facet"),
2920
- preprocessor: NullableSavedFunctionId.and(import_v36.z.unknown()).optional(),
2942
+ preprocessor: FacetPreprocessorId.optional(),
2921
2943
  facets: import_v36.z.array(
2922
2944
  import_v36.z.object({
2923
2945
  name: import_v36.z.string(),
@@ -3178,18 +3200,6 @@ var ObjectReferenceNullish = import_v36.z.union([
3178
3200
  }),
3179
3201
  import_v36.z.null()
3180
3202
  ]);
3181
- var SavedFunctionId = import_v36.z.union([
3182
- import_v36.z.object({
3183
- type: import_v36.z.literal("function"),
3184
- id: import_v36.z.string(),
3185
- version: import_v36.z.string().optional()
3186
- }),
3187
- import_v36.z.object({
3188
- type: import_v36.z.literal("global"),
3189
- name: import_v36.z.string(),
3190
- function_type: FunctionTypeEnum.optional().default("scorer")
3191
- })
3192
- ]);
3193
3203
  var DatasetEvent = import_v36.z.object({
3194
3204
  id: import_v36.z.string(),
3195
3205
  _xact_id: import_v36.z.string(),
@@ -3411,7 +3421,7 @@ var ExtendedSavedFunctionId = import_v36.z.union([
3411
3421
  ]);
3412
3422
  var FacetData = import_v36.z.object({
3413
3423
  type: import_v36.z.literal("facet"),
3414
- preprocessor: NullableSavedFunctionId.and(import_v36.z.unknown()).optional(),
3424
+ preprocessor: FacetPreprocessorId.optional(),
3415
3425
  prompt: import_v36.z.string(),
3416
3426
  model: import_v36.z.string().optional(),
3417
3427
  embedding_model: import_v36.z.string().optional(),
@@ -3916,6 +3926,19 @@ var MessageRole = import_v36.z.enum([
3916
3926
  "model",
3917
3927
  "developer"
3918
3928
  ]);
3929
+ var NullableSavedFunctionId = import_v36.z.union([
3930
+ import_v36.z.object({
3931
+ type: import_v36.z.literal("function"),
3932
+ id: import_v36.z.string(),
3933
+ version: import_v36.z.string().optional()
3934
+ }),
3935
+ import_v36.z.object({
3936
+ type: import_v36.z.literal("global"),
3937
+ name: import_v36.z.string(),
3938
+ function_type: FunctionTypeEnum.optional().default("scorer")
3939
+ }),
3940
+ import_v36.z.null()
3941
+ ]);
3919
3942
  var ObjectReference = import_v36.z.object({
3920
3943
  object_type: import_v36.z.enum([
3921
3944
  "project_logs",
@@ -3937,6 +3960,7 @@ var TraceScope = import_v36.z.object({
3937
3960
  });
3938
3961
  var OnlineScoreConfig = import_v36.z.union([
3939
3962
  import_v36.z.object({
3963
+ status: AutomationStatus.optional(),
3940
3964
  sampling_rate: import_v36.z.number().gte(0).lte(1),
3941
3965
  scorers: import_v36.z.array(SavedFunctionId),
3942
3966
  btql_filter: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
@@ -4017,6 +4041,72 @@ var Project = import_v36.z.object({
4017
4041
  user_id: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4018
4042
  settings: ProjectSettings.optional()
4019
4043
  });
4044
+ var WindowedAutomationConfig = import_v36.z.object({
4045
+ event_type: import_v36.z.literal("windowed"),
4046
+ product_origin: import_v36.z.union([import_v36.z.literal("patterns"), import_v36.z.null()]).optional(),
4047
+ status: AutomationStatus.optional(),
4048
+ threshold: import_v36.z.object({
4049
+ calculation: import_v36.z.object({
4050
+ type: import_v36.z.literal("btql"),
4051
+ btql_query: import_v36.z.string().min(1),
4052
+ output: import_v36.z.object({
4053
+ type: import_v36.z.literal("scalar"),
4054
+ value_column: import_v36.z.string().min(1)
4055
+ })
4056
+ }),
4057
+ policy: import_v36.z.object({
4058
+ condition: import_v36.z.object({
4059
+ type: import_v36.z.literal("threshold"),
4060
+ operator: import_v36.z.enum(["lt", "lte", "gt", "gte", "eq", "neq"]),
4061
+ threshold: import_v36.z.number()
4062
+ }),
4063
+ pending_seconds: import_v36.z.number().int().gte(0).lte(2592e3),
4064
+ no_data_behavior: import_v36.z.enum(["keep_last", "resolve", "alert"]),
4065
+ renotify_interval_seconds: import_v36.z.union([import_v36.z.number(), import_v36.z.null()]).optional(),
4066
+ notify_on_recovery: import_v36.z.boolean().optional().default(true)
4067
+ })
4068
+ }).optional(),
4069
+ window: import_v36.z.object({
4070
+ window_seconds: import_v36.z.number().int().gte(1).lte(2592e3),
4071
+ schedule: import_v36.z.union([
4072
+ import_v36.z.object({
4073
+ type: import_v36.z.literal("interval"),
4074
+ evaluation_interval_seconds: import_v36.z.number().int().gte(1).lte(2592e3)
4075
+ }),
4076
+ import_v36.z.object({
4077
+ type: import_v36.z.literal("cron"),
4078
+ cron_expression: import_v36.z.string().min(1),
4079
+ timezone: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional()
4080
+ })
4081
+ ]),
4082
+ evaluation_delay_seconds: import_v36.z.number().int().gte(0).lte(2592e3)
4083
+ }),
4084
+ loop: import_v36.z.object({
4085
+ prompt: import_v36.z.string().min(1).max(1e4),
4086
+ include_trigger_input: import_v36.z.boolean().optional().default(false),
4087
+ agent_slug: import_v36.z.string().min(1),
4088
+ auto_approve_tools: import_v36.z.array(import_v36.z.string().min(1)).optional().default([]),
4089
+ harness: import_v36.z.enum(["native", "codex", "claude-code"]).optional(),
4090
+ model: import_v36.z.string().min(1).optional(),
4091
+ reasoning_effort: import_v36.z.enum(["none", "minimal", "low", "medium", "high", "xhigh", "max"]).optional()
4092
+ }).optional(),
4093
+ actions: import_v36.z.array(
4094
+ import_v36.z.union([
4095
+ import_v36.z.object({
4096
+ type: import_v36.z.literal("webhook"),
4097
+ url: import_v36.z.string(),
4098
+ formatting_prompt: import_v36.z.string().min(1).max(1e4).optional()
4099
+ }),
4100
+ import_v36.z.object({
4101
+ type: import_v36.z.literal("slack"),
4102
+ workspace_id: import_v36.z.string(),
4103
+ channel: import_v36.z.string(),
4104
+ message_template: import_v36.z.string().optional(),
4105
+ formatting_prompt: import_v36.z.string().min(1).max(1e4).optional()
4106
+ })
4107
+ ])
4108
+ ).max(20).optional().default([])
4109
+ });
4020
4110
  var TopicAutomationFacetModel = import_v36.z.union([
4021
4111
  import_v36.z.enum(["brain-facet-latest", "brain-facet-1", "brain-facet-2"]),
4022
4112
  import_v36.z.null()
@@ -4058,7 +4148,8 @@ var TopicDigestAutomationConfig = import_v36.z.object({
4058
4148
  type: import_v36.z.literal("slack"),
4059
4149
  workspace_id: import_v36.z.string(),
4060
4150
  channel: import_v36.z.string(),
4061
- message_template: import_v36.z.string().optional()
4151
+ message_template: import_v36.z.string().optional(),
4152
+ formatting_prompt: import_v36.z.string().min(1).max(1e4).optional()
4062
4153
  }),
4063
4154
  topic_map_function_ids: import_v36.z.array(import_v36.z.string()).max(10).optional()
4064
4155
  });
@@ -4072,15 +4163,21 @@ var ProjectAutomation = import_v36.z.object({
4072
4163
  config: import_v36.z.union([
4073
4164
  import_v36.z.object({
4074
4165
  event_type: import_v36.z.literal("logs"),
4166
+ status: AutomationStatus.optional(),
4075
4167
  btql_filter: import_v36.z.string(),
4076
4168
  interval_seconds: import_v36.z.number().gte(1).lte(2592e3),
4077
4169
  action: import_v36.z.union([
4078
- import_v36.z.object({ type: import_v36.z.literal("webhook"), url: import_v36.z.string() }),
4170
+ import_v36.z.object({
4171
+ type: import_v36.z.literal("webhook"),
4172
+ url: import_v36.z.string(),
4173
+ formatting_prompt: import_v36.z.string().min(1).max(1e4).optional()
4174
+ }),
4079
4175
  import_v36.z.object({
4080
4176
  type: import_v36.z.literal("slack"),
4081
4177
  workspace_id: import_v36.z.string(),
4082
4178
  channel: import_v36.z.string(),
4083
- message_template: import_v36.z.string().optional()
4179
+ message_template: import_v36.z.string().optional(),
4180
+ formatting_prompt: import_v36.z.string().min(1).max(1e4).optional()
4084
4181
  })
4085
4182
  ])
4086
4183
  }),
@@ -4131,21 +4228,38 @@ var ProjectAutomation = import_v36.z.object({
4131
4228
  }),
4132
4229
  import_v36.z.object({
4133
4230
  event_type: import_v36.z.literal("environment_update"),
4231
+ status: AutomationStatus.optional(),
4134
4232
  environment_filter: import_v36.z.array(import_v36.z.string()).optional(),
4135
4233
  action: import_v36.z.union([
4136
- import_v36.z.object({ type: import_v36.z.literal("webhook"), url: import_v36.z.string() }),
4234
+ import_v36.z.object({
4235
+ type: import_v36.z.literal("webhook"),
4236
+ url: import_v36.z.string(),
4237
+ formatting_prompt: import_v36.z.string().min(1).max(1e4).optional()
4238
+ }),
4137
4239
  import_v36.z.object({
4138
4240
  type: import_v36.z.literal("slack"),
4139
4241
  workspace_id: import_v36.z.string(),
4140
4242
  channel: import_v36.z.string(),
4141
- message_template: import_v36.z.string().optional()
4243
+ message_template: import_v36.z.string().optional(),
4244
+ formatting_prompt: import_v36.z.string().min(1).max(1e4).optional()
4142
4245
  })
4143
4246
  ])
4144
4247
  }),
4248
+ WindowedAutomationConfig,
4145
4249
  TopicAutomationConfig,
4146
4250
  TopicDigestAutomationConfig
4147
4251
  ])
4148
4252
  });
4253
+ var ProjectGroup = import_v36.z.object({
4254
+ id: import_v36.z.string().uuid(),
4255
+ org_id: import_v36.z.string().uuid(),
4256
+ user_id: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4257
+ created: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4258
+ name: import_v36.z.string(),
4259
+ description: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4260
+ deleted_at: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4261
+ member_projects: import_v36.z.array(import_v36.z.string().uuid()).max(1e4)
4262
+ });
4149
4263
  var ProjectLogsEvent = import_v36.z.object({
4150
4264
  id: import_v36.z.string(),
4151
4265
  _xact_id: import_v36.z.string(),
@@ -4363,7 +4477,8 @@ var RunEval = import_v36.z.object({
4363
4477
  dataset_environment: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4364
4478
  _internal_btql: import_v36.z.union([import_v36.z.object({}).partial().passthrough(), import_v36.z.null()]).optional()
4365
4479
  }),
4366
- import_v36.z.object({ data: import_v36.z.array(import_v36.z.unknown()) })
4480
+ import_v36.z.object({ data: import_v36.z.array(import_v36.z.unknown()) }),
4481
+ import_v36.z.object({ experiment_name: import_v36.z.string() })
4367
4482
  ]),
4368
4483
  name: import_v36.z.string().optional(),
4369
4484
  parameters: import_v36.z.object({}).partial().passthrough().optional(),
@@ -4568,7 +4683,9 @@ var View = import_v36.z.object({
4568
4683
  "for_review_datasets"
4569
4684
  ]),
4570
4685
  name: import_v36.z.string(),
4686
+ description: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4571
4687
  created: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4688
+ updated_at: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
4572
4689
  view_data: ViewData.optional(),
4573
4690
  options: ViewOptions.optional(),
4574
4691
  user_id: import_v36.z.union([import_v36.z.string(), import_v36.z.null()]).optional(),
@@ -4902,7 +5019,7 @@ var BraintrustStream = class _BraintrustStream {
4902
5019
  }
4903
5020
  };
4904
5021
  function btStreamParser() {
4905
- const decoder = new TextDecoder();
5022
+ const decoder2 = new TextDecoder();
4906
5023
  let parser;
4907
5024
  return new TransformStream({
4908
5025
  async start(controller) {
@@ -4919,7 +5036,7 @@ function btStreamParser() {
4919
5036
  },
4920
5037
  async transform(chunk, controller) {
4921
5038
  if (chunk instanceof Uint8Array) {
4922
- parser.feed(decoder.decode(chunk, { stream: true }));
5039
+ parser.feed(decoder2.decode(chunk, { stream: true }));
4923
5040
  } else if (typeof chunk === "string") {
4924
5041
  parser.feed(chunk);
4925
5042
  } else {
@@ -4927,7 +5044,7 @@ function btStreamParser() {
4927
5044
  }
4928
5045
  },
4929
5046
  async flush(controller) {
4930
- const tail = decoder.decode();
5047
+ const tail = decoder2.decode();
4931
5048
  if (tail) {
4932
5049
  parser.feed(tail);
4933
5050
  }
@@ -4936,7 +5053,7 @@ function btStreamParser() {
4936
5053
  });
4937
5054
  }
4938
5055
  function createFinalValuePassThroughStream(onFinal, onError) {
4939
- const decoder = new TextDecoder();
5056
+ const decoder2 = new TextDecoder();
4940
5057
  const textChunks = [];
4941
5058
  const jsonChunks = [];
4942
5059
  const reasoningChunks = [];
@@ -4949,10 +5066,10 @@ function createFinalValuePassThroughStream(onFinal, onError) {
4949
5066
  data: chunk
4950
5067
  });
4951
5068
  } else if (chunk instanceof Uint8Array) {
4952
- textChunks.push(decoder.decode(chunk));
5069
+ textChunks.push(decoder2.decode(chunk));
4953
5070
  controller.enqueue({
4954
5071
  type: "text_delta",
4955
- data: decoder.decode(chunk)
5072
+ data: decoder2.decode(chunk)
4956
5073
  });
4957
5074
  } else if (braintrustStreamChunkSchema.safeParse(chunk).success) {
4958
5075
  const chunkType = chunk.type;
@@ -5693,12 +5810,13 @@ var INSTRUMENTATION_NAMES = {
5693
5810
  OPENROUTER: "openrouter",
5694
5811
  OPENROUTER_AGENT: "openrouter-agent",
5695
5812
  PI_CODING_AGENT: "pi-coding-agent",
5696
- STRANDS_AGENT_SDK: "strands-agent-sdk"
5813
+ STRANDS_AGENT_SDK: "strands-agent-sdk",
5814
+ VOYAGEAI: "voyageai"
5697
5815
  };
5698
5816
  var INTERNAL_SPAN_INSTRUMENTATION_NAME = /* @__PURE__ */ Symbol.for(
5699
5817
  "braintrust.spanInstrumentationName"
5700
5818
  );
5701
- var SDK_VERSION = true ? "3.27.0" : "0.0.0";
5819
+ var SDK_VERSION = true ? "3.28.0" : "0.0.0";
5702
5820
  function withSpanInstrumentationName(args, instrumentationName) {
5703
5821
  return {
5704
5822
  ...args,
@@ -5893,6 +6011,9 @@ function applyMaskingToField(maskingFunction, data, fieldName) {
5893
6011
  var INITIAL_SPAN_WRITE_AS_MERGE = /* @__PURE__ */ Symbol(
5894
6012
  "braintrust.initial-span-write-as-merge"
5895
6013
  );
6014
+ var RESUME_SPAN_WITHOUT_INITIAL_WRITE = /* @__PURE__ */ Symbol(
6015
+ "braintrust.resume-span-without-initial-write"
6016
+ );
5896
6017
  var INTERNAL_SPAN_CONTEXT = /* @__PURE__ */ Symbol("braintrust.internal-span-context");
5897
6018
  var BRAINTRUST_CURRENT_SPAN_STORE = /* @__PURE__ */ Symbol.for(
5898
6019
  "braintrust.currentSpanStore"
@@ -7116,6 +7237,30 @@ function updateSpan({
7116
7237
  event
7117
7238
  });
7118
7239
  }
7240
+ function _internalResumeSpan({
7241
+ exported,
7242
+ state
7243
+ }) {
7244
+ const resolvedState = state ?? _globalState;
7245
+ const components = SpanComponentsV4.fromStr(exported);
7246
+ const { row_id, root_span_id, span_id } = components.data;
7247
+ if (!row_id || !root_span_id || !span_id) {
7248
+ throw new Error("Only exported root spans can be resumed");
7249
+ }
7250
+ return new SpanImpl({
7251
+ state: resolvedState,
7252
+ parentObjectType: components.data.object_type,
7253
+ parentObjectId: new LazyValue(
7254
+ spanComponentsToObjectIdLambda(resolvedState, components)
7255
+ ),
7256
+ parentComputeObjectMetadataArgs: void 0,
7257
+ parentSpanIds: { parentSpanIds: [], rootSpanId: root_span_id },
7258
+ spanId: span_id,
7259
+ event: { id: row_id },
7260
+ propagatedEvent: components.data.propagated_event ?? void 0,
7261
+ [RESUME_SPAN_WITHOUT_INITIAL_WRITE]: true
7262
+ });
7263
+ }
7119
7264
  function spanComponentsToObjectIdLambda(state, components) {
7120
7265
  if (components.data.object_id) {
7121
7266
  const ret = components.data.object_id;
@@ -9996,7 +10141,7 @@ var ObjectFetcher = class {
9996
10141
  const objectId = await this.id;
9997
10142
  const batchLimit = batchSize ?? DEFAULT_FETCH_BATCH_SIZE;
9998
10143
  const internalLimit = getInternalBtqlLimit(this._internal_btql);
9999
- const limit = batchSize !== void 0 ? batchSize : internalLimit ?? batchLimit;
10144
+ let remainingLimit = internalLimit;
10000
10145
  const internalBtqlWithoutReservedQueryKeys = Object.fromEntries(
10001
10146
  Object.entries(this._internal_btql ?? {}).filter(
10002
10147
  ([key]) => key !== "cursor" && key !== "limit" && key !== "select" && key !== "from"
@@ -10005,6 +10150,10 @@ var ObjectFetcher = class {
10005
10150
  let cursor = void 0;
10006
10151
  let iterations = 0;
10007
10152
  while (true) {
10153
+ if (remainingLimit !== void 0 && remainingLimit <= 0) {
10154
+ return;
10155
+ }
10156
+ const limit = remainingLimit === void 0 ? batchLimit : Math.min(batchLimit, remainingLimit);
10008
10157
  const resp = await state.apiConn().post(
10009
10158
  `btql`,
10010
10159
  {
@@ -10044,7 +10193,14 @@ var ObjectFetcher = class {
10044
10193
  const respJson = await resp.json();
10045
10194
  const mutate = this.mutateRecord;
10046
10195
  for (const record of respJson.data ?? []) {
10047
- yield mutate ? mutate(record) : record;
10196
+ if (remainingLimit !== void 0 && remainingLimit <= 0) {
10197
+ return;
10198
+ }
10199
+ const mutatedRecord = mutate ? mutate(record) : record;
10200
+ if (remainingLimit !== void 0) {
10201
+ remainingLimit--;
10202
+ }
10203
+ yield mutatedRecord;
10048
10204
  }
10049
10205
  if (!respJson.cursor) {
10050
10206
  break;
@@ -10564,7 +10720,9 @@ var SpanImpl = class _SpanImpl {
10564
10720
  this._rootSpanId = resolvedIds.rootSpanId;
10565
10721
  this._spanParents = resolvedIds.spanParents;
10566
10722
  this.isMerge = args[INITIAL_SPAN_WRITE_AS_MERGE] === true;
10567
- this.logInternal({ event, internalData });
10723
+ if (!args[RESUME_SPAN_WITHOUT_INITIAL_WRITE]) {
10724
+ this.logInternal({ event, internalData });
10725
+ }
10568
10726
  this.isMerge = true;
10569
10727
  }
10570
10728
  getParentInfo() {
@@ -13254,6 +13412,22 @@ function processInputAttachments(input) {
13254
13412
  };
13255
13413
  }
13256
13414
  }
13415
+ const voyageBase64Key = node.type === "image_base64" ? Object.hasOwn(node, "imageBase64") ? "imageBase64" : "image_base64" : node.type === "video_base64" ? Object.hasOwn(node, "videoBase64") ? "videoBase64" : "video_base64" : void 0;
13416
+ const voyageBase64Value = voyageBase64Key ? node[voyageBase64Key] : void 0;
13417
+ if (voyageBase64Key && typeof voyageBase64Value === "string" && voyageBase64Value.startsWith("data:")) {
13418
+ const mediaType = inferMediaTypeFromDataUrl(
13419
+ voyageBase64Value,
13420
+ node.type === "video_base64" ? "video/mp4" : "image/png"
13421
+ );
13422
+ const filename = `${node.type === "video_base64" ? "video" : "image"}.${getExtensionFromMediaType(mediaType)}`;
13423
+ const attachment = toAttachment(voyageBase64Value, mediaType, filename);
13424
+ if (attachment) {
13425
+ return {
13426
+ ...node,
13427
+ [voyageBase64Key]: attachment
13428
+ };
13429
+ }
13430
+ }
13257
13431
  if (node.type === "file" && node.file && typeof node.file === "object" && typeof node.file.file_data === "string" && node.file.file_data.startsWith("data:")) {
13258
13432
  const mediaType = inferMediaTypeFromDataUrl(
13259
13433
  node.file.file_data,
@@ -14601,12 +14775,30 @@ function logInstrumentationError(context, error) {
14601
14775
 
14602
14776
  // src/wrappers/anthropic-tokens-util.ts
14603
14777
  function finalizeAnthropicTokens(metrics) {
14604
- const prompt_tokens = (metrics.prompt_tokens || 0) + (metrics.prompt_cached_tokens || 0) + (metrics.prompt_cache_creation_tokens || 0);
14605
- return {
14778
+ const hasSplitCacheCreationTokens = metrics.prompt_cache_creation_5m_tokens !== void 0 || metrics.prompt_cache_creation_1h_tokens !== void 0;
14779
+ const splitCacheCreationTokens = (metrics.prompt_cache_creation_5m_tokens || 0) + (metrics.prompt_cache_creation_1h_tokens || 0);
14780
+ const aggregateCacheCreationTokens = metrics.prompt_cache_creation_tokens || 0;
14781
+ const effectiveCacheCreationTokens = Math.max(
14782
+ aggregateCacheCreationTokens,
14783
+ splitCacheCreationTokens
14784
+ );
14785
+ const prompt_tokens = (metrics.prompt_tokens || 0) + (metrics.prompt_cached_tokens || 0) + effectiveCacheCreationTokens;
14786
+ const finalized = {
14606
14787
  ...metrics,
14607
14788
  prompt_tokens,
14608
14789
  tokens: prompt_tokens + (metrics.completion_tokens || 0)
14609
14790
  };
14791
+ if (hasSplitCacheCreationTokens && splitCacheCreationTokens >= aggregateCacheCreationTokens) {
14792
+ delete finalized.prompt_cache_creation_tokens;
14793
+ }
14794
+ return finalized;
14795
+ }
14796
+ function toNumericMetrics(metrics) {
14797
+ return Object.fromEntries(
14798
+ Object.entries(metrics).filter(
14799
+ (entry) => entry[1] !== void 0
14800
+ )
14801
+ );
14610
14802
  }
14611
14803
  function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens = 0) {
14612
14804
  const cacheTokens = {};
@@ -15212,6 +15404,18 @@ function parseMetricsFromUsage2(usage) {
15212
15404
  saveIfExistsTo("output_tokens", "completion_tokens");
15213
15405
  saveIfExistsTo("cache_read_input_tokens", "prompt_cached_tokens");
15214
15406
  saveIfExistsTo("cache_creation_input_tokens", "prompt_cache_creation_tokens");
15407
+ if (isObject(usage.cache_creation)) {
15408
+ const cacheCreation = usage.cache_creation;
15409
+ for (const [source, target] of [
15410
+ ["ephemeral_5m_input_tokens", "prompt_cache_creation_5m_tokens"],
15411
+ ["ephemeral_1h_input_tokens", "prompt_cache_creation_1h_tokens"]
15412
+ ]) {
15413
+ const value = cacheCreation[source];
15414
+ if (typeof value === "number") {
15415
+ metrics[target] = value;
15416
+ }
15417
+ }
15418
+ }
15215
15419
  if (isObject(usage.server_tool_use)) {
15216
15420
  for (const [name, value] of Object.entries(usage.server_tool_use)) {
15217
15421
  if (typeof value === "number") {
@@ -20510,37 +20714,97 @@ function seedTaskToolUseIdMapping(taskIdToToolUseId, message) {
20510
20714
  taskIdToToolUseId.set(message.task_id, message.tool_use_id);
20511
20715
  }
20512
20716
  }
20513
- function extractUsageFromMessage(message) {
20514
- const metrics = {};
20515
- let usage;
20516
- if (message.type === "assistant") {
20517
- usage = message.message?.usage;
20518
- } else if (message.type === "result") {
20519
- usage = message.usage;
20520
- }
20717
+ function tokenCount(value) {
20718
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0 ? value : void 0;
20719
+ }
20720
+ function copyUsage(usage) {
20521
20721
  if (!usage || typeof usage !== "object") {
20522
- return metrics;
20722
+ return void 0;
20723
+ }
20724
+ const copy = {};
20725
+ for (const key of [
20726
+ "input_tokens",
20727
+ "output_tokens",
20728
+ "cache_read_input_tokens",
20729
+ "cache_creation_input_tokens"
20730
+ ]) {
20731
+ const value = tokenCount(Reflect.get(usage, key));
20732
+ if (value !== void 0) {
20733
+ copy[key] = value;
20734
+ }
20735
+ }
20736
+ const cacheCreation = Reflect.get(usage, "cache_creation");
20737
+ if (cacheCreation && typeof cacheCreation === "object") {
20738
+ const cacheCreationCopy = {};
20739
+ for (const key of [
20740
+ "ephemeral_5m_input_tokens",
20741
+ "ephemeral_1h_input_tokens"
20742
+ ]) {
20743
+ const value = tokenCount(Reflect.get(cacheCreation, key));
20744
+ if (value !== void 0) {
20745
+ cacheCreationCopy[key] = value;
20746
+ }
20747
+ }
20748
+ if (Object.keys(cacheCreationCopy).length > 0) {
20749
+ copy.cache_creation = cacheCreationCopy;
20750
+ }
20751
+ }
20752
+ return Object.keys(copy).length > 0 ? copy : void 0;
20753
+ }
20754
+ function mergeUsage(base, override) {
20755
+ if (!base || !override) {
20756
+ return override ?? base;
20757
+ }
20758
+ const cacheCreation = base.cache_creation || override.cache_creation ? { ...base.cache_creation, ...override.cache_creation } : void 0;
20759
+ return {
20760
+ ...base,
20761
+ ...override,
20762
+ ...cacheCreation && { cache_creation: cacheCreation }
20763
+ };
20764
+ }
20765
+ function extractUsage(usage, includeOutput) {
20766
+ const metrics = {};
20767
+ if (!usage) {
20768
+ return {};
20523
20769
  }
20524
20770
  const inputTokens = getNumberProperty(usage, "input_tokens");
20525
20771
  if (inputTokens !== void 0) {
20526
20772
  metrics.prompt_tokens = inputTokens;
20527
20773
  }
20528
- const outputTokens = getNumberProperty(usage, "output_tokens");
20529
- if (outputTokens !== void 0) {
20530
- metrics.completion_tokens = outputTokens;
20774
+ if (includeOutput) {
20775
+ const outputTokens = getNumberProperty(usage, "output_tokens");
20776
+ if (outputTokens !== void 0) {
20777
+ metrics.completion_tokens = outputTokens;
20778
+ }
20531
20779
  }
20532
20780
  const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
20533
20781
  const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
20534
- if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
20535
- Object.assign(
20536
- metrics,
20537
- extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
20538
- );
20782
+ Object.assign(
20783
+ metrics,
20784
+ extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
20785
+ );
20786
+ const cacheCreation5mTokens = getNumberProperty(
20787
+ usage.cache_creation,
20788
+ "ephemeral_5m_input_tokens"
20789
+ );
20790
+ const cacheCreation1hTokens = getNumberProperty(
20791
+ usage.cache_creation,
20792
+ "ephemeral_1h_input_tokens"
20793
+ );
20794
+ if (cacheCreation5mTokens !== void 0) {
20795
+ metrics.prompt_cache_creation_5m_tokens = cacheCreation5mTokens;
20539
20796
  }
20540
- if (Object.keys(metrics).length > 0) {
20541
- Object.assign(metrics, finalizeAnthropicTokens(metrics));
20797
+ if (cacheCreation1hTokens !== void 0) {
20798
+ metrics.prompt_cache_creation_1h_tokens = cacheCreation1hTokens;
20542
20799
  }
20543
- return metrics;
20800
+ if (Object.keys(metrics).length === 0) {
20801
+ return {};
20802
+ }
20803
+ const finalized = finalizeAnthropicTokens(metrics);
20804
+ if (metrics.completion_tokens === void 0) {
20805
+ delete finalized.tokens;
20806
+ }
20807
+ return toNumericMetrics(finalized);
20544
20808
  }
20545
20809
  function buildLLMInput(promptMessages, conversationHistory) {
20546
20810
  const inputParts = [...promptMessages, ...conversationHistory];
@@ -20574,16 +20838,16 @@ function buildRootPromptMessages(prompt, capturedPromptMessages) {
20574
20838
  function formatCapturedMessages(messages) {
20575
20839
  return messages.length > 0 ? messages : [];
20576
20840
  }
20577
- async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, existingSpan) {
20841
+ async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, usage, hasFinalOutputUsage, existingSpan) {
20578
20842
  if (messages.length === 0) {
20579
20843
  return void 0;
20580
20844
  }
20581
20845
  const lastMessage = messages[messages.length - 1];
20582
- if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
20846
+ if (lastMessage.type !== "assistant") {
20583
20847
  return void 0;
20584
20848
  }
20585
- const model = lastMessage.message.model || options.model;
20586
- const usage = extractUsageFromMessage(lastMessage);
20849
+ const model = lastMessage.message?.model || options.model;
20850
+ const metrics = options.includePartialMessages ? extractUsage(usage, hasFinalOutputUsage) : {};
20587
20851
  const input = buildLLMInput(promptMessages, conversationHistory);
20588
20852
  const outputs = messages.map(
20589
20853
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
@@ -20605,8 +20869,8 @@ async function createLLMSpanForMessages(messages, promptMessages, conversationHi
20605
20869
  );
20606
20870
  span.log({
20607
20871
  input,
20608
- metadata: model ? { model } : void 0,
20609
- metrics: usage,
20872
+ metadata: { ...model && { model }, provider: "anthropic" },
20873
+ ...Object.keys(metrics).length > 0 ? { metrics } : {},
20610
20874
  output: outputs
20611
20875
  });
20612
20876
  const spanExport = await span.export();
@@ -20696,11 +20960,17 @@ function prepareLocalToolHandlersInMcpServers(mcpServers) {
20696
20960
  }
20697
20961
  return { hasLocalToolHandlers, localToolHookNames };
20698
20962
  }
20699
- function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
20963
+ function createToolTracingHooks(resolveParentSpan, taskIdToToolUseId, toolUseToParent, activeToolSpans, mcpServers, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
20700
20964
  const preToolUse = async (input, toolUseID) => {
20701
20965
  if (input.hook_event_name !== "PreToolUse" || !toolUseID) {
20702
20966
  return {};
20703
20967
  }
20968
+ if (!toolUseToParent.has(toolUseID) && input.agent_id) {
20969
+ const parentToolUseId = taskIdToToolUseId.get(input.agent_id);
20970
+ if (parentToolUseId) {
20971
+ toolUseToParent.set(toolUseID, parentToolUseId);
20972
+ }
20973
+ }
20704
20974
  if (skipLocalToolHooks && (isLocalToolUse(input.tool_name, mcpServers) || localToolHookNames.has(input.tool_name))) {
20705
20975
  return {};
20706
20976
  }
@@ -20896,9 +21166,6 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
20896
21166
  }
20897
21167
  const metadata = {
20898
21168
  ...subAgentDetailsToMetadata(details),
20899
- ...input.agent_transcript_path && {
20900
- "claude_agent_sdk.agent_transcript_path": input.agent_transcript_path
20901
- },
20902
21169
  "claude_agent_sdk.stop_hook_active": input.stop_hook_active
20903
21170
  };
20904
21171
  try {
@@ -20920,7 +21187,7 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
20920
21187
  subagentStop
20921
21188
  };
20922
21189
  }
20923
- function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
21190
+ function injectTracingHooks(options, resolveParentSpan, taskIdToToolUseId, toolUseToParent, activeToolSpans, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
20924
21191
  const {
20925
21192
  preToolUse,
20926
21193
  postToolUse,
@@ -20929,6 +21196,8 @@ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localTo
20929
21196
  subagentStop
20930
21197
  } = createToolTracingHooks(
20931
21198
  resolveParentSpan,
21199
+ taskIdToToolUseId,
21200
+ toolUseToParent,
20932
21201
  activeToolSpans,
20933
21202
  options.mcpServers,
20934
21203
  localToolHookNames,
@@ -21010,6 +21279,13 @@ async function finalizeCurrentMessageGroup(state) {
21010
21279
  }
21011
21280
  }
21012
21281
  const existingLlmSpan = state.activeLlmSpansByParentToolUse.get(parentKey);
21282
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
21283
+ const messageId = lastMessage?.message?.id;
21284
+ const usage = state.options.includePartialMessages ? mergeUsage(
21285
+ copyUsage(lastMessage?.message?.usage),
21286
+ messageId ? state.usageByMessageId.get(messageId) : void 0
21287
+ ) : void 0;
21288
+ const hasFinalOutputUsage = messageId !== void 0 && state.finalOutputUsageMessageIds.has(messageId);
21013
21289
  const llmSpanResult = await createLLMSpanForMessages(
21014
21290
  state.currentMessages,
21015
21291
  promptMessages,
@@ -21017,6 +21293,8 @@ async function finalizeCurrentMessageGroup(state) {
21017
21293
  state.options,
21018
21294
  state.currentMessageStartTime,
21019
21295
  parentSpan,
21296
+ usage,
21297
+ hasFinalOutputUsage,
21020
21298
  existingLlmSpan
21021
21299
  );
21022
21300
  if (llmSpanResult) {
@@ -21034,9 +21312,17 @@ async function finalizeCurrentMessageGroup(state) {
21034
21312
  }
21035
21313
  }
21036
21314
  state.activeLlmSpansByParentToolUse.delete(parentKey);
21037
- const lastMessage = state.currentMessages[state.currentMessages.length - 1];
21038
- if (lastMessage?.message?.usage) {
21039
- state.accumulatedOutputTokens += getNumberProperty(lastMessage.message.usage, "output_tokens") || 0;
21315
+ if (messageId) {
21316
+ state.usageByMessageId.delete(messageId);
21317
+ state.finalOutputUsageMessageIds.delete(messageId);
21318
+ for (const [
21319
+ parent,
21320
+ activeMessageId
21321
+ ] of state.activePartialMessageIdByParentKey) {
21322
+ if (activeMessageId === messageId) {
21323
+ state.activePartialMessageIdByParentKey.delete(parent);
21324
+ }
21325
+ }
21040
21326
  }
21041
21327
  state.currentMessages.length = 0;
21042
21328
  }
@@ -21127,6 +21413,10 @@ async function ensureActiveLlmSpanForParentToolUse(rootSpan, activeLlmSpansByPar
21127
21413
  );
21128
21414
  llmParentSpan = await subAgentSpan.export();
21129
21415
  }
21416
+ const racedLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
21417
+ if (racedLlmSpan) {
21418
+ return racedLlmSpan;
21419
+ }
21130
21420
  const llmSpan = startSpan(
21131
21421
  withSpanInstrumentationName(
21132
21422
  {
@@ -21246,7 +21536,49 @@ async function maybeHandleTaskLifecycleMessage(state, message) {
21246
21536
  }
21247
21537
  return true;
21248
21538
  }
21539
+ function handlePartialUsageMessage(state, message) {
21540
+ if (message.type !== "stream_event") {
21541
+ return false;
21542
+ }
21543
+ const event = message.event;
21544
+ if (!event || typeof event !== "object") {
21545
+ return true;
21546
+ }
21547
+ const parentKey = llmParentKey(message.parent_tool_use_id ?? null);
21548
+ if (event.type === "message_start") {
21549
+ const messageId2 = event.message?.id;
21550
+ const usage = copyUsage(event.message?.usage);
21551
+ if (messageId2) {
21552
+ state.activePartialMessageIdByParentKey.set(parentKey, messageId2);
21553
+ if (usage) {
21554
+ state.usageByMessageId.set(messageId2, usage);
21555
+ }
21556
+ }
21557
+ return true;
21558
+ }
21559
+ const messageId = state.activePartialMessageIdByParentKey.get(parentKey);
21560
+ if (!messageId) {
21561
+ return true;
21562
+ }
21563
+ if (event.type === "message_delta") {
21564
+ const update = copyUsage(event.usage);
21565
+ if (update) {
21566
+ const usage = state.usageByMessageId.get(messageId) ?? {};
21567
+ Object.assign(usage, update);
21568
+ state.usageByMessageId.set(messageId, usage);
21569
+ if (update.output_tokens !== void 0) {
21570
+ state.finalOutputUsageMessageIds.add(messageId);
21571
+ }
21572
+ }
21573
+ } else if (event.type === "message_stop") {
21574
+ state.activePartialMessageIdByParentKey.delete(parentKey);
21575
+ }
21576
+ return true;
21577
+ }
21249
21578
  async function handleStreamMessage(state, message) {
21579
+ if (handlePartialUsageMessage(state, message)) {
21580
+ return;
21581
+ }
21250
21582
  maybeTrackToolUseContext(state, message);
21251
21583
  if (await maybeHandleTaskLifecycleMessage(state, message)) {
21252
21584
  return;
@@ -21293,36 +21625,9 @@ async function handleStreamMessage(state, message) {
21293
21625
  );
21294
21626
  state.currentMessages.push(message);
21295
21627
  }
21296
- if (message.type !== "result" || !message.usage) {
21628
+ if (message.type !== "result") {
21297
21629
  return;
21298
21630
  }
21299
- const finalUsageMetrics = extractUsageFromMessage(message);
21300
- if (state.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
21301
- const lastMessage = state.currentMessages[state.currentMessages.length - 1];
21302
- if (lastMessage?.message?.usage) {
21303
- const adjustedTokens = finalUsageMetrics.completion_tokens - state.accumulatedOutputTokens;
21304
- if (adjustedTokens >= 0) {
21305
- lastMessage.message.usage.output_tokens = adjustedTokens;
21306
- }
21307
- const resultUsage = message.usage;
21308
- if (resultUsage && typeof resultUsage === "object") {
21309
- const cacheReadTokens = getNumberProperty(
21310
- resultUsage,
21311
- "cache_read_input_tokens"
21312
- );
21313
- if (cacheReadTokens !== void 0) {
21314
- lastMessage.message.usage.cache_read_input_tokens = cacheReadTokens;
21315
- }
21316
- const cacheCreationTokens = getNumberProperty(
21317
- resultUsage,
21318
- "cache_creation_input_tokens"
21319
- );
21320
- if (cacheCreationTokens !== void 0) {
21321
- lastMessage.message.usage.cache_creation_input_tokens = cacheCreationTokens;
21322
- }
21323
- }
21324
- }
21325
- }
21326
21631
  const metadata = {};
21327
21632
  if (message.num_turns !== void 0) {
21328
21633
  metadata.num_turns = message.num_turns;
@@ -21330,8 +21635,12 @@ async function handleStreamMessage(state, message) {
21330
21635
  if (message.session_id !== void 0) {
21331
21636
  metadata.session_id = message.session_id;
21332
21637
  }
21333
- if (Object.keys(metadata).length > 0) {
21334
- state.span.log({ metadata });
21638
+ const metrics = state.options.includePartialMessages ? {} : extractUsage(copyUsage(message.usage), true);
21639
+ if (Object.keys(metadata).length > 0 || Object.keys(metrics).length > 0) {
21640
+ state.span.log({
21641
+ ...Object.keys(metadata).length > 0 ? { metadata } : {},
21642
+ ...Object.keys(metrics).length > 0 ? { metrics } : {}
21643
+ });
21335
21644
  }
21336
21645
  }
21337
21646
  async function finalizeQuerySpan(state) {
@@ -21355,6 +21664,9 @@ async function finalizeQuerySpan(state) {
21355
21664
  llmSpan.end();
21356
21665
  }
21357
21666
  state.activeLlmSpansByParentToolUse.clear();
21667
+ state.activePartialMessageIdByParentKey.clear();
21668
+ state.finalOutputUsageMessageIds.clear();
21669
+ state.usageByMessageId.clear();
21358
21670
  for (const toolSpan of state.activeToolSpans.values()) {
21359
21671
  toolSpan.end();
21360
21672
  }
@@ -21479,6 +21791,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21479
21791
  const optionsWithHooks = injectTracingHooks(
21480
21792
  options,
21481
21793
  resolveToolUseParentSpan,
21794
+ taskIdToToolUseId,
21795
+ toolUseToParent,
21482
21796
  activeToolSpans,
21483
21797
  localToolHookNames,
21484
21798
  skipLocalToolHooks,
@@ -21489,8 +21803,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21489
21803
  params.options = optionsWithHooks;
21490
21804
  event.arguments[0] = params;
21491
21805
  spans.set(event, {
21492
- accumulatedOutputTokens: 0,
21493
21806
  activeLlmSpansByParentToolUse,
21807
+ activePartialMessageIdByParentKey: /* @__PURE__ */ new Map(),
21494
21808
  activeToolSpans,
21495
21809
  conversationHistoryByParentKey,
21496
21810
  capturedPromptMessages,
@@ -21498,6 +21812,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21498
21812
  currentMessageStartTime: startTime,
21499
21813
  currentMessages: [],
21500
21814
  endedSubAgentSpans,
21815
+ finalOutputUsageMessageIds: /* @__PURE__ */ new Set(),
21501
21816
  finalResults: [],
21502
21817
  options: optionsWithHooks,
21503
21818
  originalPrompt,
@@ -21513,6 +21828,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21513
21828
  latestLlmParentBySubAgentToolUse,
21514
21829
  latestRootLlmParentRef,
21515
21830
  toolUseToParent,
21831
+ usageByMessageId: /* @__PURE__ */ new Map(),
21516
21832
  localToolContext
21517
21833
  });
21518
21834
  },
@@ -23987,9 +24303,9 @@ function extractEmbedPromptTokenCount(response) {
23987
24303
  let sawAny = false;
23988
24304
  for (const embedding of embeddings) {
23989
24305
  const embeddingStats = tryToDict(tryToDict(embedding)?.statistics);
23990
- const tokenCount = embeddingStats?.tokenCount;
23991
- if (typeof tokenCount === "number" && Number.isFinite(tokenCount)) {
23992
- total += tokenCount;
24306
+ const tokenCount2 = embeddingStats?.tokenCount;
24307
+ if (typeof tokenCount2 === "number" && Number.isFinite(tokenCount2)) {
24308
+ total += tokenCount2;
23993
24309
  sawAny = true;
23994
24310
  }
23995
24311
  }
@@ -30884,7 +31200,7 @@ function getStringProperty2(obj, key) {
30884
31200
  return typeof value === "string" ? value : void 0;
30885
31201
  }
30886
31202
  function extractMetricsFromUsage(usage) {
30887
- const metrics = {
31203
+ const rawMetrics = {
30888
31204
  prompt_tokens: usage.inputTokens,
30889
31205
  completion_tokens: usage.outputTokens,
30890
31206
  ...extractAnthropicCacheTokens(
@@ -30893,10 +31209,10 @@ function extractMetricsFromUsage(usage) {
30893
31209
  )
30894
31210
  };
30895
31211
  if (usage.reasoningTokens !== void 0) {
30896
- metrics.completion_reasoning_tokens = usage.reasoningTokens;
30897
- metrics.reasoning_tokens = usage.reasoningTokens;
31212
+ rawMetrics.completion_reasoning_tokens = usage.reasoningTokens;
31213
+ rawMetrics.reasoning_tokens = usage.reasoningTokens;
30898
31214
  }
30899
- Object.assign(metrics, finalizeAnthropicTokens(metrics));
31215
+ const metrics = finalizeAnthropicTokens(rawMetrics);
30900
31216
  const metadata = {
30901
31217
  model: usage.model
30902
31218
  };
@@ -31835,17 +32151,20 @@ var FlueObserveBridge = class {
31835
32151
  return;
31836
32152
  }
31837
32153
  const metadata = {
32154
+ ...event.runId ? this.runsById.get(event.runId)?.metadata : {},
31838
32155
  ...extractEventMetadata(event),
31839
32156
  "flue.operation": event.operationKind,
31840
32157
  provider: "flue"
31841
32158
  };
31842
32159
  const parent = this.parentSpanForEvent(event);
31843
- const span = startFlueSpan(parent, {
32160
+ const args = {
31844
32161
  name: `flue.${event.operationKind}`,
31845
32162
  spanAttributes: { type: "task" /* TASK */ },
31846
32163
  startTime: eventTime(event.timestamp),
31847
32164
  event: { metadata }
31848
- });
32165
+ };
32166
+ const runSpan = event.runId ? this.runsById.get(event.runId)?.span : void 0;
32167
+ const span = event.operationKind === "prompt" && (!parent || parent === runSpan) ? startFlueRootSpan(args) : startFlueSpan(parent, args);
31849
32168
  this.operationsById.set(event.operationId, { metadata, span });
31850
32169
  }
31851
32170
  handleOperation(event) {
@@ -31860,6 +32179,11 @@ var FlueObserveBridge = class {
31860
32179
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
31861
32180
  ...event.usage ? { "flue.usage": event.usage } : {}
31862
32181
  };
32182
+ const input = flueOperationInput(event);
32183
+ if (!state.loggedInput && input !== void 0) {
32184
+ safeLog3(state.span, { input });
32185
+ state.loggedInput = true;
32186
+ }
31863
32187
  this.finishPendingChildrenForOperation(event, output);
31864
32188
  safeLog3(state.span, {
31865
32189
  ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
@@ -31876,6 +32200,8 @@ var FlueObserveBridge = class {
31876
32200
  return;
31877
32201
  }
31878
32202
  const input = flueTurnRequestInput(event);
32203
+ const operation = event.operationId ? this.operationsById.get(event.operationId) : void 0;
32204
+ const turnInput = prepareFlueTurnInput(event, input, operation);
31879
32205
  const model = flueTurnRequestModel(event);
31880
32206
  const provider = flueTurnRequestProvider(event);
31881
32207
  const api = flueTurnRequestApi(event);
@@ -31888,8 +32214,7 @@ var FlueObserveBridge = class {
31888
32214
  ...provider ? { "flue.provider": provider } : {},
31889
32215
  ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
31890
32216
  ...reasoning ? { reasoning } : {},
31891
- ...input?.systemPrompt ? { "flue.system_prompt": input.systemPrompt } : {},
31892
- ...input?.tools ? { tools: input.tools } : {}
32217
+ ...turnInput.metadata
31893
32218
  };
31894
32219
  const parent = this.parentSpanForTurn(event);
31895
32220
  const span = startFlueSpan(parent, {
@@ -31897,11 +32222,14 @@ var FlueObserveBridge = class {
31897
32222
  spanAttributes: { type: "llm" /* LLM */ },
31898
32223
  startTime: eventTime(event.timestamp),
31899
32224
  event: {
31900
- input: input?.messages,
32225
+ input: turnInput.messages,
31901
32226
  metadata
31902
32227
  }
31903
32228
  });
31904
- this.logOperationInput(event.operationId, input?.messages ?? input);
32229
+ this.logOperationInput(
32230
+ event.operationId,
32231
+ latestUserMessageInput(input?.messages)
32232
+ );
31905
32233
  this.turnsByKey.set(key, { metadata, span });
31906
32234
  }
31907
32235
  handleTurn(event) {
@@ -32148,16 +32476,20 @@ var FlueObserveBridge = class {
32148
32476
  }
32149
32477
  startSyntheticOperation(event) {
32150
32478
  const metadata = {
32479
+ ...event.runId ? this.runsById.get(event.runId)?.metadata : {},
32151
32480
  ...extractEventMetadata(event),
32152
32481
  "flue.operation": event.operationKind,
32153
32482
  provider: "flue"
32154
32483
  };
32155
- const span = startFlueSpan(this.parentSpanForEvent(event), {
32484
+ const args = {
32156
32485
  name: `flue.${event.operationKind}`,
32157
32486
  spanAttributes: { type: "task" /* TASK */ },
32158
32487
  startTime: eventTime(event.timestamp),
32159
32488
  event: { metadata }
32160
- });
32489
+ };
32490
+ const parent = this.parentSpanForEvent(event);
32491
+ const runSpan = event.runId ? this.runsById.get(event.runId)?.span : void 0;
32492
+ const span = event.operationKind === "prompt" && (!parent || parent === runSpan) ? startFlueRootSpan(args) : startFlueSpan(parent, args);
32161
32493
  return { metadata, span };
32162
32494
  }
32163
32495
  startSyntheticTurn(event) {
@@ -32336,6 +32668,71 @@ function flueRunInput(event) {
32336
32668
  function flueTurnRequestInput(event) {
32337
32669
  return event.request?.input ?? event.input;
32338
32670
  }
32671
+ function prepareFlueTurnInput(event, input, operation) {
32672
+ const messages = input?.messages;
32673
+ const tracksUserTurn = event.purpose === "agent" && operation?.metadata["flue.operation"] === "prompt" && Array.isArray(messages);
32674
+ if (!tracksUserTurn) {
32675
+ return {
32676
+ messages,
32677
+ metadata: {
32678
+ ...input?.systemPrompt ? { "flue.system_prompt": input.systemPrompt } : {},
32679
+ ...input?.tools ? { tools: input.tools } : {}
32680
+ }
32681
+ };
32682
+ }
32683
+ const previous = operation.turnInputState;
32684
+ const previousMessageCount = previous?.messageCount ?? 0;
32685
+ const boundaryFingerprint = messages.length > 0 ? fingerprintJsonValue(messages[messages.length - 1]) : void 0;
32686
+ const continuesPreviousInput = previous !== void 0 && messages.length >= previousMessageCount && (previousMessageCount === 0 || previous.boundaryFingerprint !== void 0 && previous.boundaryFingerprint === fingerprintJsonValue(messages[previousMessageCount - 1]));
32687
+ const inputMode = previous === void 0 ? "full" : continuesPreviousInput ? "delta" : "reset";
32688
+ const systemPromptFingerprint = fingerprintJsonValue(input?.systemPrompt);
32689
+ const toolsFingerprint = fingerprintJsonValue(input?.tools);
32690
+ operation.turnInputState = {
32691
+ ...boundaryFingerprint !== void 0 ? { boundaryFingerprint } : {},
32692
+ messageCount: messages.length,
32693
+ ...systemPromptFingerprint !== void 0 ? { systemPromptFingerprint } : {},
32694
+ ...toolsFingerprint !== void 0 ? { toolsFingerprint } : {}
32695
+ };
32696
+ return {
32697
+ messages: continuesPreviousInput ? messages.slice(previousMessageCount) : messages,
32698
+ metadata: {
32699
+ "flue.input_mode": inputMode,
32700
+ ...continuesPreviousInput ? { "flue.input_message_offset": previousMessageCount } : {},
32701
+ ...input?.systemPrompt && (!continuesPreviousInput || systemPromptFingerprint !== previous?.systemPromptFingerprint) ? { "flue.system_prompt": input.systemPrompt } : {},
32702
+ ...input?.tools && (!continuesPreviousInput || toolsFingerprint !== previous?.toolsFingerprint) ? { tools: input.tools } : {}
32703
+ }
32704
+ };
32705
+ }
32706
+ function fingerprintJsonValue(value) {
32707
+ try {
32708
+ const serialized = JSON.stringify(value);
32709
+ if (serialized === void 0) {
32710
+ return void 0;
32711
+ }
32712
+ let hash = 2166136261;
32713
+ for (let i = 0; i < serialized.length; i++) {
32714
+ hash = Math.imul(hash ^ serialized.charCodeAt(i), 16777619);
32715
+ }
32716
+ return `${serialized.length}:${hash >>> 0}`;
32717
+ } catch {
32718
+ return void 0;
32719
+ }
32720
+ }
32721
+ function latestUserMessageInput(messages) {
32722
+ if (!messages) {
32723
+ return void 0;
32724
+ }
32725
+ for (let i = messages.length - 1; i >= 0; i--) {
32726
+ const message = messages[i];
32727
+ if (isObjectLike(message) && Reflect.get(message, "role") === "user") {
32728
+ return [message];
32729
+ }
32730
+ }
32731
+ return void 0;
32732
+ }
32733
+ function flueOperationInput(event) {
32734
+ return typeof event.agentInput?.text === "string" ? [{ content: event.agentInput.text, role: "user" }] : void 0;
32735
+ }
32339
32736
  function flueTurnRequestModel(event) {
32340
32737
  return event.request?.requestedModel ?? event.request?.model ?? event.model;
32341
32738
  }
@@ -32493,6 +32890,21 @@ function startFlueSpan(parent, args) {
32493
32890
  withSpanInstrumentationName(args, INSTRUMENTATION_NAMES.FLUE)
32494
32891
  );
32495
32892
  }
32893
+ function startFlueRootSpan(args) {
32894
+ const state = _internalGetGlobalState();
32895
+ const spanId = state.idGenerator.getSpanId();
32896
+ const rootSpanId = state.idGenerator.shareRootSpanId() ? spanId : state.idGenerator.getTraceId();
32897
+ return withCurrent(
32898
+ NOOP_SPAN,
32899
+ () => startSpan({
32900
+ ...withSpanInstrumentationName(args, INSTRUMENTATION_NAMES.FLUE),
32901
+ parentSpanIds: { parentSpanIds: [], rootSpanId },
32902
+ spanId,
32903
+ state
32904
+ }),
32905
+ state
32906
+ );
32907
+ }
32496
32908
  function runWithCurrentSpanStore(span, next) {
32497
32909
  const state = _internalGetGlobalState();
32498
32910
  const contextManager = state?.contextManager;
@@ -34392,8 +34804,7 @@ function startAgentStream(event, activeChildParents) {
34392
34804
  ...extractAgentMetadata2(agent),
34393
34805
  ...extractModelMetadata3(model),
34394
34806
  "strands.operation": "Agent.stream",
34395
- provider: extractProvider(model),
34396
- ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
34807
+ provider: extractProvider(model)
34397
34808
  };
34398
34809
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
34399
34810
  const attachmentCache = createStrandsAttachmentCache();
@@ -34443,8 +34854,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
34443
34854
  const metadata = {
34444
34855
  "strands.operation": operation,
34445
34856
  provider: "strands",
34446
- ...orchestrator?.id ? { "strands.orchestrator.id": orchestrator.id } : {},
34447
- ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
34857
+ ...orchestrator?.id ? { "strands.orchestrator.id": orchestrator.id } : {}
34448
34858
  };
34449
34859
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
34450
34860
  const input = processStrandsInputAttachments(event.arguments[0]);
@@ -35230,6 +35640,320 @@ function logInstrumentationError5(context, error) {
35230
35640
  debugLogger.debug(`${context}:`, error);
35231
35641
  }
35232
35642
 
35643
+ // src/instrumentation/plugins/voyageai-channels.ts
35644
+ var voyageAIChannels = defineChannels(
35645
+ "voyageai",
35646
+ {
35647
+ embed: channel({
35648
+ channelName: "embed",
35649
+ kind: "async"
35650
+ }),
35651
+ multimodalEmbed: channel({
35652
+ channelName: "multimodalEmbed",
35653
+ kind: "async"
35654
+ }),
35655
+ rerank: channel({
35656
+ channelName: "rerank",
35657
+ kind: "async"
35658
+ }),
35659
+ contextualizedEmbed: channel({
35660
+ channelName: "contextualizedEmbed",
35661
+ kind: "async"
35662
+ })
35663
+ },
35664
+ { instrumentationName: INSTRUMENTATION_NAMES.VOYAGEAI }
35665
+ );
35666
+
35667
+ // src/instrumentation/plugins/voyageai-plugin.ts
35668
+ var RERANK_METADATA_ALLOWLIST = /* @__PURE__ */ new Set([
35669
+ "model",
35670
+ "returnDocuments",
35671
+ "topK",
35672
+ "truncation"
35673
+ ]);
35674
+ var VoyageAIPlugin = class extends BasePlugin {
35675
+ onEnable() {
35676
+ this.unsubscribers.push(
35677
+ interceptVoyageAICall(
35678
+ voyageAIChannels.embed,
35679
+ "voyageai.embed",
35680
+ extractTextEmbeddingInput,
35681
+ summarizeEmbeddingOutput,
35682
+ extractEmbeddingUsageMetrics
35683
+ ),
35684
+ interceptVoyageAICall(
35685
+ voyageAIChannels.multimodalEmbed,
35686
+ "voyageai.multimodalEmbed",
35687
+ extractMultimodalEmbeddingInput,
35688
+ summarizeEmbeddingOutput,
35689
+ extractEmbeddingUsageMetrics
35690
+ ),
35691
+ interceptVoyageAICall(
35692
+ voyageAIChannels.rerank,
35693
+ "voyageai.rerank",
35694
+ extractRerankInput,
35695
+ summarizeRerankOutput
35696
+ ),
35697
+ interceptVoyageAICall(
35698
+ voyageAIChannels.contextualizedEmbed,
35699
+ "voyageai.contextualizedEmbed",
35700
+ extractContextualizedEmbeddingInput,
35701
+ summarizeContextualizedEmbeddingOutput,
35702
+ extractEmbeddingUsageMetrics
35703
+ )
35704
+ );
35705
+ }
35706
+ onDisable() {
35707
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
35708
+ }
35709
+ };
35710
+ function interceptVoyageAICall(channel2, name, extractInput3, extractOutput2, extractMetrics2 = extractUsageMetrics3) {
35711
+ return channel2.intercept((target, thisArg, args) => {
35712
+ const invokeTarget = () => Reflect.apply(target, thisArg, args);
35713
+ if (isAutoInstrumentationSuppressed()) {
35714
+ return invokeTarget();
35715
+ }
35716
+ let span;
35717
+ try {
35718
+ const { input, metadata } = extractInput3(args);
35719
+ span = startSpan(
35720
+ withSpanInstrumentationName(
35721
+ {
35722
+ event: { input, metadata },
35723
+ name,
35724
+ spanAttributes: { type: "llm" /* LLM */ }
35725
+ },
35726
+ INSTRUMENTATION_NAMES.VOYAGEAI
35727
+ )
35728
+ );
35729
+ } catch (error) {
35730
+ debugLogger.error(`Error starting span for ${name}:`, error);
35731
+ return invokeTarget();
35732
+ }
35733
+ let result;
35734
+ try {
35735
+ result = withCurrent(
35736
+ span,
35737
+ () => runWithAutoInstrumentationSuppressed(invokeTarget)
35738
+ );
35739
+ } catch (error) {
35740
+ finishVoyageAISpan(span, name, () => span.log({ error }));
35741
+ throw error;
35742
+ }
35743
+ void Promise.resolve(result).then(
35744
+ (value) => finishVoyageAISpan(span, name, () => {
35745
+ const metadata = extractResponseMetadata3(value);
35746
+ span.log({
35747
+ output: extractOutput2(value),
35748
+ ...metadata ? { metadata } : {},
35749
+ metrics: extractMetrics2(value)
35750
+ });
35751
+ }),
35752
+ (error) => finishVoyageAISpan(span, name, () => span.log({ error }))
35753
+ );
35754
+ return result;
35755
+ });
35756
+ }
35757
+ function finishVoyageAISpan(span, name, log3) {
35758
+ try {
35759
+ log3();
35760
+ } catch (error) {
35761
+ debugLogger.error(`Error logging span for ${name}:`, error);
35762
+ }
35763
+ try {
35764
+ span.end();
35765
+ } catch (error) {
35766
+ debugLogger.error(`Error ending span for ${name}:`, error);
35767
+ }
35768
+ }
35769
+ function getRequestArg2(args) {
35770
+ if (Array.isArray(args)) {
35771
+ return isObject(args[0]) ? args[0] : void 0;
35772
+ }
35773
+ if (!isObject(args)) {
35774
+ return void 0;
35775
+ }
35776
+ const firstArg = Reflect.get(args, "0");
35777
+ return isObject(firstArg) ? firstArg : void 0;
35778
+ }
35779
+ function pickMetadata(request, allowlist) {
35780
+ const metadata = {};
35781
+ if (request) {
35782
+ for (const key of allowlist) {
35783
+ if (!Object.hasOwn(request, key)) {
35784
+ continue;
35785
+ }
35786
+ const value = request[key];
35787
+ if (value !== void 0) {
35788
+ metadata[key] = value;
35789
+ }
35790
+ }
35791
+ }
35792
+ return {
35793
+ ...metadata,
35794
+ provider: "voyage"
35795
+ };
35796
+ }
35797
+ function buildEmbeddingInput(inputs, request) {
35798
+ const outputDimensions = request?.outputDimension;
35799
+ return {
35800
+ inputs,
35801
+ ...typeof outputDimensions === "number" && Number.isFinite(outputDimensions) ? { output_dimensions: outputDimensions } : {}
35802
+ };
35803
+ }
35804
+ function embeddingMetadata(request) {
35805
+ return {
35806
+ ...typeof request?.model === "string" ? { model: request.model } : {},
35807
+ provider: "voyage"
35808
+ };
35809
+ }
35810
+ function extractTextEmbeddingInput(args) {
35811
+ const request = getRequestArg2(args);
35812
+ const rawInput = request?.input;
35813
+ const values = Array.isArray(rawInput) ? rawInput : [rawInput];
35814
+ return {
35815
+ input: buildEmbeddingInput(
35816
+ values.flatMap(
35817
+ (value) => typeof value === "string" ? [{ content: value }] : []
35818
+ ),
35819
+ request
35820
+ ),
35821
+ metadata: embeddingMetadata(request)
35822
+ };
35823
+ }
35824
+ function extractContextualizedEmbeddingInput(args) {
35825
+ const request = getRequestArg2(args);
35826
+ const rawInputs = request?.inputs;
35827
+ const values = Array.isArray(rawInputs) ? rawInputs.flatMap((value) => Array.isArray(value) ? value : [value]) : [];
35828
+ return {
35829
+ input: buildEmbeddingInput(
35830
+ values.flatMap(
35831
+ (value) => typeof value === "string" ? [{ content: value }] : []
35832
+ ),
35833
+ request
35834
+ ),
35835
+ metadata: embeddingMetadata(request)
35836
+ };
35837
+ }
35838
+ function extractMultimodalEmbeddingInput(args) {
35839
+ const request = getRequestArg2(args);
35840
+ const rawInputs = request?.inputs;
35841
+ const inputs = Array.isArray(rawInputs) ? rawInputs.map((rawInput) => {
35842
+ const rawContent = isObject(rawInput) ? rawInput.content : void 0;
35843
+ return {
35844
+ content: Array.isArray(rawContent) ? rawContent.flatMap(normalizeMultimodalContentPart) : []
35845
+ };
35846
+ }) : [];
35847
+ const input = buildEmbeddingInput(inputs, request);
35848
+ const processedInput = processInputAttachments(input);
35849
+ return {
35850
+ input: hasInlineEmbeddingMedia(processedInput) ? input : processedInput,
35851
+ metadata: embeddingMetadata(request)
35852
+ };
35853
+ }
35854
+ function normalizeMultimodalContentPart(part) {
35855
+ if (!isObject(part) || typeof part.type !== "string") {
35856
+ return [];
35857
+ }
35858
+ if (part.type === "text") {
35859
+ return typeof part.text === "string" ? [{ type: "text", text: part.text }] : [];
35860
+ }
35861
+ const camelCaseField = {
35862
+ image_base64: "imageBase64",
35863
+ image_url: "imageUrl",
35864
+ video_base64: "videoBase64",
35865
+ video_url: "videoUrl"
35866
+ };
35867
+ const field = camelCaseField[part.type];
35868
+ if (!field) {
35869
+ return [];
35870
+ }
35871
+ const data = typeof part[field] === "string" ? part[field] : part[part.type];
35872
+ if (typeof data !== "string") {
35873
+ return [];
35874
+ }
35875
+ return part.type.startsWith("image_") ? [{ type: "image_url", image_url: { url: data } }] : [{ type: "file", file: { file_data: data } }];
35876
+ }
35877
+ function hasInlineEmbeddingMedia(input) {
35878
+ return input.inputs.some(
35879
+ ({ content }) => Array.isArray(content) ? content.some((part) => {
35880
+ const value = part.type === "image_url" ? part.image_url.url : part.type === "file" ? part.file.file_data : void 0;
35881
+ return typeof value === "string" && value.startsWith("data:");
35882
+ }) : false
35883
+ );
35884
+ }
35885
+ function extractRerankInput(args) {
35886
+ const request = getRequestArg2(args);
35887
+ const documents = request?.documents;
35888
+ return {
35889
+ input: {
35890
+ documents,
35891
+ query: request?.query
35892
+ },
35893
+ metadata: {
35894
+ ...pickMetadata(request, RERANK_METADATA_ALLOWLIST),
35895
+ ...Array.isArray(documents) ? { document_count: documents.length } : {}
35896
+ }
35897
+ };
35898
+ }
35899
+ function extractResponseMetadata3(result) {
35900
+ if (!isObject(result)) {
35901
+ return void 0;
35902
+ }
35903
+ const rawResponse = isObject(result.rawResponse) ? result.rawResponse : void 0;
35904
+ const model = typeof result.model === "string" ? result.model : typeof rawResponse?.model === "string" ? rawResponse.model : void 0;
35905
+ return model ? { model } : void 0;
35906
+ }
35907
+ function summarizeEmbeddingOutput(result) {
35908
+ return {
35909
+ count: isObject(result) && Array.isArray(result.data) ? result.data.length : 0
35910
+ };
35911
+ }
35912
+ function summarizeRerankOutput(result) {
35913
+ if (!isObject(result) || !Array.isArray(result.data)) {
35914
+ return void 0;
35915
+ }
35916
+ return result.data.slice(0, 100).map((item) => ({
35917
+ index: isObject(item) ? item.index : void 0,
35918
+ relevance_score: isObject(item) ? (typeof item.relevanceScore === "number" ? item.relevanceScore : item.relevance_score) ?? null : null
35919
+ }));
35920
+ }
35921
+ function summarizeContextualizedEmbeddingOutput(result) {
35922
+ if (!isObject(result)) {
35923
+ return { count: 0 };
35924
+ }
35925
+ if (Array.isArray(result.results)) {
35926
+ return {
35927
+ count: result.results.reduce(
35928
+ (count, item) => count + (isObject(item) && Array.isArray(item.embeddings) ? item.embeddings.length : 0),
35929
+ 0
35930
+ )
35931
+ };
35932
+ }
35933
+ if (!Array.isArray(result.data)) {
35934
+ return { count: 0 };
35935
+ }
35936
+ return {
35937
+ count: result.data.reduce(
35938
+ (count, item) => count + (isObject(item) && Array.isArray(item.data) ? item.data.length : 0),
35939
+ 0
35940
+ )
35941
+ };
35942
+ }
35943
+ function extractEmbeddingUsageMetrics(result) {
35944
+ const metrics = extractUsageMetrics3(result);
35945
+ return typeof metrics.tokens === "number" ? { prompt_tokens: metrics.tokens, tokens: metrics.tokens } : {};
35946
+ }
35947
+ function extractUsageMetrics3(result) {
35948
+ if (!isObject(result)) {
35949
+ return {};
35950
+ }
35951
+ const rawResponse = isObject(result.rawResponse) ? result.rawResponse : void 0;
35952
+ const usage = isObject(result.usage) ? result.usage : isObject(rawResponse?.usage) ? rawResponse.usage : void 0;
35953
+ const tokens = typeof result.totalTokens === "number" ? result.totalTokens : usage?.totalTokens ?? usage?.total_tokens;
35954
+ return typeof tokens === "number" && Number.isFinite(tokens) && tokens >= 0 ? { tokens } : {};
35955
+ }
35956
+
35233
35957
  // src/instrumentation/plugins/cloudflare-ai-chat-channels.ts
35234
35958
  var cloudflareAIChatChannels = defineChannels(
35235
35959
  "@cloudflare/ai-chat",
@@ -35808,6 +36532,7 @@ var BraintrustPlugin = class extends BasePlugin {
35808
36532
  langSmithPlugin = null;
35809
36533
  piCodingAgentPlugin = null;
35810
36534
  strandsAgentSDKPlugin = null;
36535
+ voyageAIPlugin = null;
35811
36536
  cloudflareAIChatPlugin = null;
35812
36537
  cloudflareAgentsPlugin = null;
35813
36538
  constructor(config = {}) {
@@ -35882,6 +36607,10 @@ var BraintrustPlugin = class extends BasePlugin {
35882
36607
  this.coherePlugin = new CoherePlugin();
35883
36608
  this.coherePlugin.enable();
35884
36609
  }
36610
+ if (integrations.voyageai !== false) {
36611
+ this.voyageAIPlugin = new VoyageAIPlugin();
36612
+ this.voyageAIPlugin.enable();
36613
+ }
35885
36614
  if (integrations.groq !== false) {
35886
36615
  this.groqPlugin = new GroqPlugin();
35887
36616
  this.groqPlugin.enable();
@@ -35998,6 +36727,10 @@ var BraintrustPlugin = class extends BasePlugin {
35998
36727
  this.coherePlugin.disable();
35999
36728
  this.coherePlugin = null;
36000
36729
  }
36730
+ if (this.voyageAIPlugin) {
36731
+ this.voyageAIPlugin.disable();
36732
+ this.voyageAIPlugin = null;
36733
+ }
36001
36734
  if (this.groqPlugin) {
36002
36735
  this.groqPlugin.disable();
36003
36736
  this.groqPlugin = null;
@@ -36117,7 +36850,10 @@ var envIntegrationAliases = {
36117
36850
  "langchain-js": "langchain",
36118
36851
  "@langchain": "langchain",
36119
36852
  langgraph: "langgraph",
36120
- langsmith: "langsmith"
36853
+ langsmith: "langsmith",
36854
+ voyage: "voyageai",
36855
+ "voyage-ai": "voyageai",
36856
+ voyageai: "voyageai"
36121
36857
  };
36122
36858
  function getDefaultInstrumentationIntegrations() {
36123
36859
  return {
@@ -36152,6 +36888,7 @@ function getDefaultInstrumentationIntegrations() {
36152
36888
  langchain: true,
36153
36889
  langgraph: true,
36154
36890
  langsmith: true,
36891
+ voyageai: true,
36155
36892
  piCodingAgent: true,
36156
36893
  strandsAgentSDK: true,
36157
36894
  cloudflareAgents: true
@@ -36315,6 +37052,8 @@ __export(exports_exports, {
36315
37052
  BRAINTRUST_PARENT_KEY: () => BRAINTRUST_PARENT_KEY,
36316
37053
  BaseAttachment: () => BaseAttachment,
36317
37054
  BaseExperiment: () => BaseExperiment,
37055
+ BatchScorer: () => BatchScorer,
37056
+ BatchTask: () => BatchTask,
36318
37057
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
36319
37058
  BraintrustMiddleware: () => BraintrustMiddleware,
36320
37059
  BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
@@ -36328,6 +37067,8 @@ __export(exports_exports, {
36328
37067
  DEFAULT_MAX_REQUEST_SIZE: () => DEFAULT_MAX_REQUEST_SIZE,
36329
37068
  Dataset: () => Dataset2,
36330
37069
  DatasetPipeline: () => DatasetPipeline,
37070
+ DurableEvalMemoryStore: () => DurableEvalMemoryStore,
37071
+ DurableEvalRedisStore: () => DurableEvalRedisStore,
36331
37072
  ERR_PERMALINK: () => ERR_PERMALINK,
36332
37073
  Eval: () => Eval,
36333
37074
  EvalResultWithSummary: () => EvalResultWithSummary,
@@ -36385,6 +37126,7 @@ __export(exports_exports, {
36385
37126
  currentSpan: () => currentSpan,
36386
37127
  deepCopyEvent: () => deepCopyEvent,
36387
37128
  defaultErrorScoreHandler: () => defaultErrorScoreHandler,
37129
+ defineDurableEval: () => defineDurableEval,
36388
37130
  deserializePlainStringAsJSON: () => deserializePlainStringAsJSON,
36389
37131
  devNullWritableStream: () => devNullWritableStream,
36390
37132
  evaluatorDefinitionSchema: () => evaluatorDefinitionSchema,
@@ -36480,7 +37222,8 @@ __export(exports_exports, {
36480
37222
  wrapPiCodingAgentSDK: () => wrapPiCodingAgentSDK,
36481
37223
  wrapStrandsAgentSDK: () => wrapStrandsAgentSDK,
36482
37224
  wrapTraced: () => wrapTraced,
36483
- wrapVitest: () => wrapVitest
37225
+ wrapVitest: () => wrapVitest,
37226
+ wrapVoyageAI: () => wrapVoyageAI
36484
37227
  });
36485
37228
 
36486
37229
  // src/sandbox.ts
@@ -37741,7 +38484,7 @@ function normalizeUsageMetrics(usage, provider, providerMetadata) {
37741
38484
  cacheCreationTokens
37742
38485
  );
37743
38486
  Object.assign(metrics, cacheTokens);
37744
- Object.assign(metrics, finalizeAnthropicTokens(metrics));
38487
+ return toNumericMetrics(finalizeAnthropicTokens(metrics));
37745
38488
  }
37746
38489
  }
37747
38490
  return metrics;
@@ -42074,6 +42817,84 @@ function wrapRerank3(rerank) {
42074
42817
  });
42075
42818
  }
42076
42819
 
42820
+ // src/wrappers/voyageai.ts
42821
+ function wrapVoyageAI(client) {
42822
+ if (!isSupportedVoyageAIClient(client)) {
42823
+ console.warn("Unsupported Voyage AI library. Not wrapping.");
42824
+ return client;
42825
+ }
42826
+ return voyageAIProxy(client);
42827
+ }
42828
+ var voyageAIProxyCache = /* @__PURE__ */ new WeakMap();
42829
+ function isObject3(value) {
42830
+ return typeof value === "object" && value !== null;
42831
+ }
42832
+ function hasFunction5(value, name) {
42833
+ return typeof Reflect.get(value, name) === "function";
42834
+ }
42835
+ function isSupportedVoyageAIClient(value) {
42836
+ if (!isObject3(value)) {
42837
+ return false;
42838
+ }
42839
+ return hasFunction5(value, "embed") || hasFunction5(value, "multimodalEmbed") || hasFunction5(value, "rerank") || hasFunction5(value, "contextualizedEmbed");
42840
+ }
42841
+ function voyageAIProxy(client) {
42842
+ const cached = voyageAIProxyCache.get(client);
42843
+ if (cached) {
42844
+ return cached;
42845
+ }
42846
+ const proxy = new Proxy(client, {
42847
+ get(target, prop, receiver) {
42848
+ switch (prop) {
42849
+ case "embed":
42850
+ if (typeof target.embed !== "function") {
42851
+ return target.embed;
42852
+ }
42853
+ return (request, options) => voyageAIChannels.embed.invoke(
42854
+ target.embed,
42855
+ target,
42856
+ [request, options],
42857
+ {}
42858
+ );
42859
+ case "multimodalEmbed":
42860
+ if (typeof target.multimodalEmbed !== "function") {
42861
+ return target.multimodalEmbed;
42862
+ }
42863
+ return (request, options) => voyageAIChannels.multimodalEmbed.invoke(
42864
+ target.multimodalEmbed,
42865
+ target,
42866
+ [request, options],
42867
+ {}
42868
+ );
42869
+ case "rerank":
42870
+ if (typeof target.rerank !== "function") {
42871
+ return target.rerank;
42872
+ }
42873
+ return (request, options) => voyageAIChannels.rerank.invoke(
42874
+ target.rerank,
42875
+ target,
42876
+ [request, options],
42877
+ {}
42878
+ );
42879
+ case "contextualizedEmbed":
42880
+ if (typeof target.contextualizedEmbed !== "function") {
42881
+ return target.contextualizedEmbed;
42882
+ }
42883
+ return (request, options) => voyageAIChannels.contextualizedEmbed.invoke(
42884
+ target.contextualizedEmbed,
42885
+ target,
42886
+ [request, options],
42887
+ {}
42888
+ );
42889
+ default:
42890
+ return Reflect.get(target, prop, receiver);
42891
+ }
42892
+ }
42893
+ });
42894
+ voyageAIProxyCache.set(client, proxy);
42895
+ return proxy;
42896
+ }
42897
+
42077
42898
  // src/wrappers/groq.ts
42078
42899
  function wrapGroq(groq) {
42079
42900
  if (isSupportedGroqClient(groq)) {
@@ -42085,14 +42906,14 @@ function wrapGroq(groq) {
42085
42906
  function isRecord6(value) {
42086
42907
  return typeof value === "object" && value !== null;
42087
42908
  }
42088
- function hasFunction5(value, methodName) {
42909
+ function hasFunction6(value, methodName) {
42089
42910
  return isRecord6(value) && methodName in value && typeof value[methodName] === "function";
42090
42911
  }
42091
42912
  function hasChat2(value) {
42092
- return isRecord6(value) && isRecord6(value.completions) && hasFunction5(value.completions, "create");
42913
+ return isRecord6(value) && isRecord6(value.completions) && hasFunction6(value.completions, "create");
42093
42914
  }
42094
42915
  function hasEmbeddings2(value) {
42095
- return hasFunction5(value, "create");
42916
+ return hasFunction6(value, "create");
42096
42917
  }
42097
42918
  function isSupportedGroqClient(value) {
42098
42919
  return isRecord6(value) && (value.chat !== void 0 && hasChat2(value.chat) || value.embeddings !== void 0 && hasEmbeddings2(value.embeddings));
@@ -42675,7 +43496,7 @@ async function flushExperimentWithSync(context, config) {
42675
43496
  // src/wrappers/shared/scorers.ts
42676
43497
  async function runScorers(args) {
42677
43498
  const { scorers, output, expected, input, metadata, span } = args;
42678
- const scorerArgs = {
43499
+ const scorerArgs2 = {
42679
43500
  output,
42680
43501
  expected,
42681
43502
  input,
@@ -42684,7 +43505,7 @@ async function runScorers(args) {
42684
43505
  await Promise.all(
42685
43506
  scorers.map(async (scorer) => {
42686
43507
  try {
42687
- const result = await scorer(scorerArgs);
43508
+ const result = await scorer(scorerArgs2);
42688
43509
  const scores = normalizeScores(result);
42689
43510
  if (scores.length > 0) {
42690
43511
  const accScores = {};
@@ -45043,6 +45864,26 @@ async function getExperimentParametersRef(parameters) {
45043
45864
  version: resolvedParameters.version
45044
45865
  };
45045
45866
  }
45867
+ async function _internalInitEvaluatorExperiment(projectName, evaluator, data, options = {}) {
45868
+ if (options.disabled) return null;
45869
+ const { baseExperiment } = callEvaluatorData(data);
45870
+ const parameters = await getExperimentParametersRef(evaluator.parameters);
45871
+ return initExperiment2(evaluator.state, {
45872
+ ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: projectName },
45873
+ experiment: options.experimentName ?? evaluator.experimentName,
45874
+ description: evaluator.description,
45875
+ metadata: evaluator.metadata,
45876
+ tags: evaluator.tags,
45877
+ isPublic: evaluator.isPublic,
45878
+ update: options.update ?? evaluator.update,
45879
+ baseExperiment: evaluator.baseExperimentName ?? baseExperiment,
45880
+ baseExperimentId: evaluator.baseExperimentId,
45881
+ gitMetadataSettings: evaluator.gitMetadataSettings,
45882
+ repoInfo: evaluator.repoInfo,
45883
+ dataset: Dataset2.isDataset(data) ? data : void 0,
45884
+ parameters
45885
+ });
45886
+ }
45046
45887
  function callEvaluatorData(data) {
45047
45888
  const dataResult = typeof data === "function" ? data() : data;
45048
45889
  let baseExperiment = void 0;
@@ -45060,6 +45901,48 @@ function isAsyncIterable7(value) {
45060
45901
  function isIterable(value) {
45061
45902
  return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
45062
45903
  }
45904
+ async function _internalResolveEvaluatorData(evaluator, experiment) {
45905
+ if (typeof evaluator.data === "string") {
45906
+ throw new Error("Unimplemented: string data paths");
45907
+ }
45908
+ let dataResult = typeof evaluator.data === "function" ? evaluator.data() : evaluator.data;
45909
+ if ("_type" in dataResult) {
45910
+ if (dataResult._type !== "BaseExperiment") {
45911
+ throw new Error("Invalid _type");
45912
+ }
45913
+ if (!experiment) {
45914
+ throw new Error(
45915
+ "Cannot use BaseExperiment() without connecting to Braintrust (you most likely set --no-send-logs)"
45916
+ );
45917
+ }
45918
+ let name = dataResult.name;
45919
+ if (isEmpty2(name)) {
45920
+ const baseExperiment = await experiment.fetchBaseExperiment();
45921
+ if (!baseExperiment) {
45922
+ throw new Error("BaseExperiment() failed to fetch base experiment");
45923
+ }
45924
+ name = baseExperiment.name;
45925
+ }
45926
+ dataResult = initExperiment2(evaluator.state, {
45927
+ ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: evaluator.projectName },
45928
+ experiment: name,
45929
+ open: true
45930
+ }).asDataset();
45931
+ }
45932
+ const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
45933
+ if (isAsyncIterable7(resolvedDataResult)) {
45934
+ return resolvedDataResult;
45935
+ }
45936
+ if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
45937
+ const iterable = resolvedDataResult;
45938
+ return (async function* () {
45939
+ for (const datum of iterable) yield datum;
45940
+ })();
45941
+ }
45942
+ throw new Error(
45943
+ "Evaluator data must be an array, iterable, or async iterable"
45944
+ );
45945
+ }
45063
45946
  globalThis._evals = {
45064
45947
  functions: [],
45065
45948
  prompts: [],
@@ -45106,25 +45989,13 @@ async function Eval(name, evaluator, reporterOrOpts) {
45106
45989
  }
45107
45990
  const resolvedReporter = options.reporter || defaultReporter;
45108
45991
  try {
45109
- const { data, baseExperiment: defaultBaseExperiment } = callEvaluatorData(
45110
- evaluator.data
45111
- );
45112
- const parameters = await getExperimentParametersRef(evaluator.parameters);
45113
- const experiment = options.parent || options.noSendLogs ? null : initExperiment2(evaluator.state, {
45114
- ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: name },
45115
- experiment: evaluator.experimentName,
45116
- description: evaluator.description,
45117
- metadata: evaluator.metadata,
45118
- tags: evaluator.tags,
45119
- isPublic: evaluator.isPublic,
45120
- update: evaluator.update,
45121
- baseExperiment: evaluator.baseExperimentName ?? defaultBaseExperiment,
45122
- baseExperimentId: evaluator.baseExperimentId,
45123
- gitMetadataSettings: evaluator.gitMetadataSettings,
45124
- repoInfo: evaluator.repoInfo,
45125
- dataset: Dataset2.isDataset(data) ? data : void 0,
45126
- parameters
45127
- });
45992
+ const { data } = callEvaluatorData(evaluator.data);
45993
+ const experiment = await _internalInitEvaluatorExperiment(
45994
+ name,
45995
+ evaluator,
45996
+ data,
45997
+ { disabled: Boolean(options.parent || options.noSendLogs) }
45998
+ );
45128
45999
  if (experiment && typeof process !== "undefined" && globalThis.BRAINTRUST_CONTEXT_MANAGER !== void 0) {
45129
46000
  await experiment._waitForId();
45130
46001
  }
@@ -45222,19 +46093,74 @@ function scorerName(scorer, scorer_idx) {
45222
46093
  function classifierName(classifier, classifier_idx) {
45223
46094
  return classifier.name || `classifier_${classifier_idx}`;
45224
46095
  }
46096
+ async function _internalRunEvaluatorTask(task, datum, trialIndex, parameters, span, reportProgress = () => void 0) {
46097
+ const metadata = {
46098
+ ..."metadata" in datum ? datum.metadata : {}
46099
+ };
46100
+ const hooks = {
46101
+ meta(value) {
46102
+ Object.assign(metadata, value);
46103
+ },
46104
+ metadata,
46105
+ expected: "expected" in datum ? datum.expected : void 0,
46106
+ span,
46107
+ parameters,
46108
+ reportProgress,
46109
+ trialIndex,
46110
+ tags: [...datum.tags ?? []]
46111
+ };
46112
+ const output = await task(datum.input, hooks);
46113
+ span.log({ output });
46114
+ return {
46115
+ output,
46116
+ metadata: hooks.metadata,
46117
+ tags: hooks.tags ?? []
46118
+ };
46119
+ }
45225
46120
  function buildSpanMetadata(results) {
45226
- return results.length === 1 ? results[0].metadata : results.reduce(
45227
- (prev, s) => mergeDicts(prev, { [s.name]: s.metadata }),
45228
- {}
46121
+ return results.length === 1 ? results[0].metadata : Object.fromEntries(
46122
+ results.map((result) => [result.name, result.metadata])
45229
46123
  );
45230
46124
  }
45231
46125
  function buildSpanScores(results) {
45232
- const scoresRecord = results.reduce(
45233
- (prev, s) => mergeDicts(prev, { [s.name]: s.score }),
45234
- {}
46126
+ const scoresRecord = Object.fromEntries(
46127
+ results.map((result) => [result.name, result.score])
45235
46128
  );
45236
46129
  return { resultMetadata: buildSpanMetadata(results), scoresRecord };
45237
46130
  }
46131
+ function _internalPrepareEvaluatorScore(scoreValue, name) {
46132
+ if (scoreValue === null) return { results: null };
46133
+ if (Array.isArray(scoreValue)) {
46134
+ for (const score of scoreValue) {
46135
+ if (!(typeof score === "object" && !isEmpty2(score))) {
46136
+ throw new Error(
46137
+ `When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(score)}`
46138
+ );
46139
+ }
46140
+ }
46141
+ }
46142
+ let results;
46143
+ if (Array.isArray(scoreValue)) {
46144
+ results = scoreValue;
46145
+ } else if (typeof scoreValue === "object" && !isEmpty2(scoreValue)) {
46146
+ results = [scoreValue];
46147
+ } else {
46148
+ results = [{ name, score: scoreValue }];
46149
+ }
46150
+ const { resultMetadata, scoresRecord } = buildSpanScores(results);
46151
+ const fields = (score) => {
46152
+ const { metadata: _metadata, name: _name, ...rest } = score;
46153
+ return rest;
46154
+ };
46155
+ return {
46156
+ results,
46157
+ output: results.length === 1 ? fields(results[0]) : Object.fromEntries(
46158
+ results.map((score) => [score.name ?? name, fields(score)])
46159
+ ),
46160
+ metadata: resultMetadata,
46161
+ scores: scoresRecord
46162
+ };
46163
+ }
45238
46164
  async function runInScorerSpan(rootSpan, spanName, spanType, propagatedEvent, eventInput, fn) {
45239
46165
  try {
45240
46166
  const value = await rootSpan.traced(fn, {
@@ -45279,6 +46205,27 @@ function toClassificationItem(c) {
45279
46205
  ...c.metadata !== void 0 ? { metadata: c.metadata } : {}
45280
46206
  };
45281
46207
  }
46208
+ function _internalPrepareEvaluatorClassification(value, name) {
46209
+ if (value === null) return { results: null };
46210
+ const results = (Array.isArray(value) ? value : [value]).map(
46211
+ (result) => validateClassificationResult(result, name)
46212
+ );
46213
+ const classifications = /* @__PURE__ */ Object.create(null);
46214
+ for (const result of results) {
46215
+ (classifications[result.name] ??= []).push(toClassificationItem(result));
46216
+ }
46217
+ return {
46218
+ results,
46219
+ output: results.length === 1 ? toClassificationItem(results[0]) : Object.fromEntries(
46220
+ results.map((result) => [
46221
+ result.name,
46222
+ toClassificationItem(result)
46223
+ ])
46224
+ ),
46225
+ metadata: buildSpanMetadata(results),
46226
+ classifications
46227
+ };
46228
+ }
45282
46229
  function logScoringFailures(kind, failures, metadata, rootSpan, state) {
45283
46230
  if (!failures.length) return [];
45284
46231
  const errorMap = Object.fromEntries(
@@ -45326,54 +46273,14 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45326
46273
  (evaluator.state ?? _internalGetGlobalState())?.spanCache?.start();
45327
46274
  }
45328
46275
  try {
45329
- if (typeof evaluator.data === "string") {
45330
- throw new Error("Unimplemented: string data paths");
45331
- }
45332
- let dataResult = typeof evaluator.data === "function" ? evaluator.data() : evaluator.data;
45333
46276
  parameters = await validateParameters(
45334
46277
  parameters ?? {},
45335
46278
  evaluator.parameters
45336
46279
  );
45337
- if ("_type" in dataResult) {
45338
- if (dataResult._type !== "BaseExperiment") {
45339
- throw new Error("Invalid _type");
45340
- }
45341
- if (!experiment) {
45342
- throw new Error(
45343
- "Cannot use BaseExperiment() without connecting to Braintrust (you most likely set --no-send-logs)"
45344
- );
45345
- }
45346
- let name = dataResult.name;
45347
- if (isEmpty2(name)) {
45348
- const baseExperiment = await experiment.fetchBaseExperiment();
45349
- if (!baseExperiment) {
45350
- throw new Error("BaseExperiment() failed to fetch base experiment");
45351
- }
45352
- name = baseExperiment.name;
45353
- }
45354
- dataResult = initExperiment2(evaluator.state, {
45355
- ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: evaluator.projectName },
45356
- experiment: name,
45357
- open: true
45358
- }).asDataset();
45359
- }
45360
- const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
45361
- const dataIterable = (() => {
45362
- if (isAsyncIterable7(resolvedDataResult)) {
45363
- return resolvedDataResult;
45364
- }
45365
- if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
45366
- const iterable = resolvedDataResult;
45367
- return (async function* () {
45368
- for (const datum of iterable) {
45369
- yield datum;
45370
- }
45371
- })();
45372
- }
45373
- throw new Error(
45374
- "Evaluator data must be an array, iterable, or async iterable"
45375
- );
45376
- })();
46280
+ const dataIterable = await _internalResolveEvaluatorData(
46281
+ evaluator,
46282
+ experiment
46283
+ );
45377
46284
  progressReporter.start(evaluator.evalName, 0);
45378
46285
  const experimentIdPromise = experiment ? (async () => {
45379
46286
  try {
@@ -45442,57 +46349,45 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45442
46349
  ensureSpansFlushed,
45443
46350
  state
45444
46351
  }) : void 0;
45445
- let metadata = {
45446
- ..."metadata" in datum ? datum.metadata : {}
45447
- };
46352
+ let metadata = {};
45448
46353
  const expected = "expected" in datum ? datum.expected : void 0;
45449
46354
  let output = void 0;
45450
46355
  let error = void 0;
45451
- let tags = [...datum.tags ?? []];
45452
- const scores = {};
45453
- const classifications = {};
46356
+ let tags = [];
46357
+ const scores = /* @__PURE__ */ Object.create(null);
46358
+ const classifications = /* @__PURE__ */ Object.create(null);
45454
46359
  const scorerNames = (evaluator.scores ?? []).map(scorerName);
45455
46360
  const classifierNames = (evaluator.classifiers ?? []).map(
45456
46361
  classifierName
45457
46362
  );
45458
46363
  let unhandledScores = scorerNames;
45459
46364
  try {
45460
- const meta = (o) => metadata = { ...metadata, ...o };
45461
- await rootSpan.traced(
45462
- async (span) => {
45463
- const hooksForTask = {
45464
- meta,
45465
- metadata,
45466
- expected,
45467
- span,
45468
- parameters: parameters ?? {},
45469
- reportProgress: (event) => {
45470
- stream?.({
45471
- ...event,
45472
- id: rootSpan.id,
45473
- origin: baseEvent.event?.origin,
45474
- name: evaluator.evalName,
45475
- object_type: "task"
45476
- });
45477
- },
45478
- trialIndex,
45479
- tags
45480
- };
45481
- const outputResult = evaluator.task(datum.input, hooksForTask);
45482
- if (outputResult instanceof Promise) {
45483
- output = await outputResult;
45484
- } else {
45485
- output = outputResult;
46365
+ const taskResult = await rootSpan.traced(
46366
+ (span) => _internalRunEvaluatorTask(
46367
+ evaluator.task,
46368
+ datum,
46369
+ trialIndex,
46370
+ parameters ?? {},
46371
+ span,
46372
+ (event) => {
46373
+ stream?.({
46374
+ ...event,
46375
+ id: rootSpan.id,
46376
+ origin: baseEvent.event?.origin,
46377
+ name: evaluator.evalName,
46378
+ object_type: "task"
46379
+ });
45486
46380
  }
45487
- tags = hooksForTask.tags ?? [];
45488
- span.log({ output });
45489
- },
46381
+ ),
45490
46382
  {
45491
46383
  name: "task",
45492
46384
  spanAttributes: { type: "task" /* TASK */ },
45493
46385
  event: { input: datum.input }
45494
46386
  }
45495
46387
  );
46388
+ output = taskResult.output;
46389
+ metadata = taskResult.metadata;
46390
+ tags = taskResult.tags;
45496
46391
  if (tags.length) {
45497
46392
  rootSpan.log({ output, metadata, expected, tags });
45498
46393
  } else {
@@ -45502,20 +46397,18 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45502
46397
  await rootSpan.flush();
45503
46398
  }
45504
46399
  const scoringArgs = {
46400
+ id: datum.id,
45505
46401
  input: datum.input,
45506
46402
  expected: "expected" in datum ? datum.expected : void 0,
45507
46403
  metadata,
45508
46404
  output,
46405
+ tags,
45509
46406
  trace
45510
46407
  };
45511
46408
  const { trace: _trace, ...scoringArgsForLogging } = scoringArgs;
45512
46409
  const propagatedEvent = makeScorerPropagatedEvent(
45513
46410
  await rootSpan.export()
45514
46411
  );
45515
- const getOtherFields = (s) => {
45516
- const { metadata: _metadata, name: _name, ...rest } = s;
45517
- return rest;
45518
- };
45519
46412
  const [scoreResults, classificationResults] = await Promise.all([
45520
46413
  Promise.all(
45521
46414
  (evaluator.scores ?? []).map(
@@ -45529,35 +46422,17 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45529
46422
  const scoreValue = await Promise.resolve(
45530
46423
  score(scoringArgs)
45531
46424
  );
45532
- if (scoreValue === null) return null;
45533
- if (Array.isArray(scoreValue)) {
45534
- for (const s of scoreValue) {
45535
- if (!(typeof s === "object" && !isEmpty2(s))) {
45536
- throw new Error(
45537
- `When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(s)}`
45538
- );
45539
- }
45540
- }
45541
- }
45542
- const results = Array.isArray(scoreValue) ? scoreValue : typeof scoreValue === "object" && !isEmpty2(scoreValue) ? [scoreValue] : [
45543
- {
45544
- name: scorerNames[score_idx],
45545
- score: scoreValue
45546
- }
45547
- ];
45548
- const { resultMetadata, scoresRecord } = buildSpanScores(results);
45549
- const resultOutput = results.length === 1 ? getOtherFields(results[0]) : results.reduce(
45550
- (prev, s) => mergeDicts(prev, {
45551
- [s.name]: getOtherFields(s)
45552
- }),
45553
- {}
46425
+ const prepared = _internalPrepareEvaluatorScore(
46426
+ scoreValue,
46427
+ scorerNames[score_idx]
45554
46428
  );
46429
+ if (prepared.results === null) return null;
45555
46430
  span.log({
45556
- output: resultOutput,
45557
- metadata: resultMetadata,
45558
- scores: scoresRecord
46431
+ output: prepared.output,
46432
+ metadata: prepared.metadata,
46433
+ scores: prepared.scores
45559
46434
  });
45560
- return results;
46435
+ return prepared.results;
45561
46436
  }
45562
46437
  )
45563
46438
  )
@@ -45574,24 +46449,16 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45574
46449
  const classifierValue = await Promise.resolve(
45575
46450
  classifier(scoringArgs)
45576
46451
  );
45577
- if (classifierValue === null) return null;
45578
- const rawResults = (Array.isArray(classifierValue) ? classifierValue : [classifierValue]).map(
45579
- (result) => validateClassificationResult(
45580
- result,
45581
- classifierNames[idx]
45582
- )
45583
- );
45584
- const resultOutput = rawResults.length === 1 ? toClassificationItem(rawResults[0]) : rawResults.reduce(
45585
- (prev, r) => mergeDicts(prev, {
45586
- [r.name]: toClassificationItem(r)
45587
- }),
45588
- {}
46452
+ const prepared = _internalPrepareEvaluatorClassification(
46453
+ classifierValue,
46454
+ classifierNames[idx]
45589
46455
  );
46456
+ if (prepared.results === null) return null;
45590
46457
  span.log({
45591
- output: resultOutput,
45592
- metadata: buildSpanMetadata(rawResults)
46458
+ output: prepared.output,
46459
+ metadata: prepared.metadata
45593
46460
  });
45594
- return rawResults;
46461
+ return prepared.results;
45595
46462
  }
45596
46463
  )
45597
46464
  )
@@ -45820,7 +46687,7 @@ function accumulateScores(accumulator, scores) {
45820
46687
  }
45821
46688
  }
45822
46689
  function ensureScoreAccumulator(results) {
45823
- const accumulator = {};
46690
+ const accumulator = /* @__PURE__ */ Object.create(null);
45824
46691
  for (const result of results) {
45825
46692
  accumulateScores(accumulator, result.scores);
45826
46693
  }
@@ -45963,6 +46830,1156 @@ var defaultReporter = {
45963
46830
  }
45964
46831
  };
45965
46832
 
46833
+ // src/durable-eval.ts
46834
+ var encoder = new TextEncoder();
46835
+ var decoder = new TextDecoder();
46836
+ var BATCH_TASK_KIND = "braintrust.durable.batch-task";
46837
+ var BATCH_SCORER_KIND = "braintrust.durable.batch-scorer";
46838
+ var DEFAULT_BATCH_SIZE = 1e3;
46839
+ var DurableEvalMemoryStore = class {
46840
+ values = /* @__PURE__ */ new Map();
46841
+ async read(key) {
46842
+ return this.values.get(key)?.slice();
46843
+ }
46844
+ async write(key, value) {
46845
+ this.values.set(key, value.slice());
46846
+ }
46847
+ async getOrSet(key, value) {
46848
+ const existing = this.values.get(key);
46849
+ if (existing) return { value: existing.slice(), created: false };
46850
+ this.values.set(key, value.slice());
46851
+ return { value: value.slice(), created: true };
46852
+ }
46853
+ };
46854
+ var DurableEvalRedisStore = class {
46855
+ client;
46856
+ keyPrefix;
46857
+ ttlMs;
46858
+ constructor(options) {
46859
+ this.client = options.client;
46860
+ this.keyPrefix = options.keyPrefix ?? "braintrust-eval:";
46861
+ this.ttlMs = options.ttlMs ?? 1e3 * 60 * 60 * 24 * 7;
46862
+ if (!Number.isInteger(this.ttlMs) || this.ttlMs < 1) {
46863
+ throw new Error("DurableEvalRedisStore ttlMs must be a positive integer");
46864
+ }
46865
+ }
46866
+ async read(key) {
46867
+ const value = await this.client.get(`${this.keyPrefix}${key}`);
46868
+ if (value == null) return void 0;
46869
+ if (typeof value !== "string") {
46870
+ throw new Error("DurableEvalRedisStore expected GET to return a string");
46871
+ }
46872
+ return base64ToUint8Array(value);
46873
+ }
46874
+ async write(key, value) {
46875
+ const client = this.client;
46876
+ const set = client.set;
46877
+ const redisKey = `${this.keyPrefix}${key}`;
46878
+ const encoded = uint8ArrayToBase64(value);
46879
+ if (typeof client.defineCommand === "function") {
46880
+ await set.call(client, redisKey, encoded, "PX", this.ttlMs);
46881
+ } else if (typeof client.sendCommand === "function") {
46882
+ await set.call(client, redisKey, encoded, { PX: this.ttlMs });
46883
+ } else if (typeof client.createScript === "function") {
46884
+ await set.call(client, redisKey, encoded, { px: this.ttlMs });
46885
+ } else {
46886
+ throw new Error(
46887
+ "DurableEvalRedisStore requires a node-redis, ioredis, or @upstash/redis client"
46888
+ );
46889
+ }
46890
+ }
46891
+ async getOrSet(key, value) {
46892
+ const redisKey = `${this.keyPrefix}${key}`;
46893
+ const encoded = uint8ArrayToBase64(value);
46894
+ const client = this.client;
46895
+ const set = client.set;
46896
+ let setOptions;
46897
+ if (typeof client.defineCommand === "function") {
46898
+ setOptions = ["PX", this.ttlMs, "NX", "GET"];
46899
+ } else if (typeof client.sendCommand === "function") {
46900
+ setOptions = [{ PX: this.ttlMs, NX: true, GET: true }];
46901
+ } else if (typeof client.createScript === "function") {
46902
+ setOptions = [{ px: this.ttlMs, nx: true, get: true }];
46903
+ } else {
46904
+ throw new Error(
46905
+ "DurableEvalRedisStore getOrSet requires a node-redis, ioredis, or @upstash/redis client"
46906
+ );
46907
+ }
46908
+ const existing = await set.call(client, redisKey, encoded, ...setOptions);
46909
+ if (existing === null) return { value: value.slice(), created: true };
46910
+ if (typeof existing !== "string") {
46911
+ throw new Error(
46912
+ "DurableEvalRedisStore expected atomic SET to return a string or null"
46913
+ );
46914
+ }
46915
+ return { value: base64ToUint8Array(existing), created: false };
46916
+ }
46917
+ };
46918
+ var BatchTask = class {
46919
+ constructor(processor) {
46920
+ this.processor = processor;
46921
+ }
46922
+ processor;
46923
+ kind = BATCH_TASK_KIND;
46924
+ };
46925
+ var BatchScorer = class {
46926
+ constructor(processor) {
46927
+ this.processor = processor;
46928
+ this.name = processor.name;
46929
+ }
46930
+ processor;
46931
+ kind = BATCH_SCORER_KIND;
46932
+ name;
46933
+ };
46934
+ function defineDurableEval(projectName, evaluator) {
46935
+ const definition = {
46936
+ projectName,
46937
+ evalName: evaluator.experimentName ?? projectName,
46938
+ evaluator
46939
+ };
46940
+ return {
46941
+ start: (options = {}) => startDurableEval(definition, options),
46942
+ status: (options) => getDurableEvalStatus(definition, options),
46943
+ poll: (options) => pollDurableEval(definition, options),
46944
+ processBatchResult: (result) => processDurableBatchResult(definition, result)
46945
+ };
46946
+ }
46947
+ async function startDurableEval(definition, options) {
46948
+ const store = definition.evaluator.store;
46949
+ const runId = newId();
46950
+ const key = runKey(definition.projectName, definition.evalName, runId);
46951
+ const { data } = callEvaluatorData(definition.evaluator.data);
46952
+ const parameters = await validateParameters(
46953
+ options.parameters ?? {},
46954
+ definition.evaluator.parameters
46955
+ );
46956
+ const experimentName = definition.evaluator.experimentName ?? `${definition.evalName}-${runId}`;
46957
+ const experiment = await _internalInitEvaluatorExperiment(
46958
+ definition.projectName,
46959
+ { ...definition.evaluator, data },
46960
+ data,
46961
+ {
46962
+ disabled: options.noSendLogs ?? false,
46963
+ experimentName,
46964
+ update: true
46965
+ }
46966
+ );
46967
+ if (!isBatchTask(definition.evaluator.task) && !(definition.evaluator.scores ?? []).some(isBatchScorer)) {
46968
+ const result = await runEvaluator(
46969
+ experiment,
46970
+ {
46971
+ ...definition.evaluator,
46972
+ projectName: definition.projectName,
46973
+ evalName: definition.evalName,
46974
+ data
46975
+ },
46976
+ {
46977
+ start: () => void 0,
46978
+ stop: () => void 0,
46979
+ increment: () => void 0
46980
+ },
46981
+ [],
46982
+ void 0,
46983
+ parameters,
46984
+ true,
46985
+ true
46986
+ );
46987
+ const state2 = {
46988
+ runId,
46989
+ experimentName,
46990
+ noSendLogs: options.noSendLogs ?? false,
46991
+ parameters: assertJsonValue(parameters, "eval parameters"),
46992
+ status: "completed",
46993
+ summary: result.summary,
46994
+ cases: [],
46995
+ batches: []
46996
+ };
46997
+ await experiment?.flush();
46998
+ await writeRunRecord(store, key, state2);
46999
+ return currentStatus(definition, state2);
47000
+ }
47001
+ const state = {
47002
+ runId,
47003
+ experimentName,
47004
+ noSendLogs: options.noSendLogs ?? false,
47005
+ parameters: assertJsonValue(parameters, "eval parameters"),
47006
+ status: "running",
47007
+ cases: await materializeCases(definition, data, experiment),
47008
+ batches: []
47009
+ };
47010
+ await writeCaseBaseRecords(store, key, state.cases);
47011
+ await writeRunRecord(store, key, state);
47012
+ return advanceDurableEval(definition, state, store, key, experiment);
47013
+ }
47014
+ async function getDurableEvalStatus(definition, options) {
47015
+ const store = definition.evaluator.store;
47016
+ const state = await readRunState(
47017
+ definition,
47018
+ store,
47019
+ runKey(definition.projectName, definition.evalName, options.runId)
47020
+ );
47021
+ if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
47022
+ return currentStatus(definition, state);
47023
+ }
47024
+ async function processDurableBatchResult(definition, result) {
47025
+ if (!result.batchId && !result.externalId) {
47026
+ throw new Error("Batch results require batchId or externalId");
47027
+ }
47028
+ const store = definition.evaluator.store;
47029
+ const key = runKey(definition.projectName, definition.evalName, result.runId);
47030
+ const state = await readRunState(definition, store, key);
47031
+ if (!state) throw new Error(`Durable eval run ${result.runId} is missing`);
47032
+ const byBatch = result.batchId ? state.batches.find((candidate) => candidate.id === result.batchId) : void 0;
47033
+ const byExternal = result.externalId ? state.batches.find(
47034
+ (candidate) => candidate.externalId === result.externalId
47035
+ ) : void 0;
47036
+ if (byBatch && byExternal && byBatch.id !== byExternal.id) {
47037
+ throw new Error("batchId and externalId identify different batches");
47038
+ }
47039
+ const batch = byBatch ?? byExternal;
47040
+ if (!batch) throw new Error("No submitted batch matches this result");
47041
+ if (batch.status !== "complete") {
47042
+ const records = await collectBatch(definition, state, batch);
47043
+ batch.status = "complete";
47044
+ await writeCaseRecords(store, key, records);
47045
+ await writeBatchRecords(store, key, [batch]);
47046
+ }
47047
+ return advanceDurableEval(
47048
+ definition,
47049
+ await readRunState(definition, store, key),
47050
+ store,
47051
+ key
47052
+ );
47053
+ }
47054
+ async function pollDurableEval(definition, options) {
47055
+ const store = definition.evaluator.store;
47056
+ const key = runKey(
47057
+ definition.projectName,
47058
+ definition.evalName,
47059
+ options.runId
47060
+ );
47061
+ const state = await readRunState(definition, store, key);
47062
+ if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
47063
+ const batches = state.batches.filter((batch) => {
47064
+ if (batch.status === "complete") return false;
47065
+ return processorForStage(definition, batch.kind, batch.scorerName).completion.mode === "poll";
47066
+ });
47067
+ const results = await Promise.all(
47068
+ batches.map(async (batch) => ({
47069
+ batch,
47070
+ result: await processorForStage(definition, batch.kind, batch.scorerName).completion.poll(batch.submissionData, {
47071
+ runId: state.runId,
47072
+ batchId: batch.id
47073
+ })
47074
+ }))
47075
+ );
47076
+ const changedCases = /* @__PURE__ */ new Map();
47077
+ const changedBatches = [];
47078
+ for (const { batch, result } of results) {
47079
+ if (result.status === "failed") throw asError(result.error);
47080
+ if (result.status !== "complete") continue;
47081
+ for (const record of await collectBatch(definition, state, batch)) {
47082
+ changedCases.set(record.id, record);
47083
+ }
47084
+ batch.status = "complete";
47085
+ changedBatches.push(batch);
47086
+ }
47087
+ if (changedBatches.length > 0) {
47088
+ await writeCaseRecords(store, key, [...changedCases.values()]);
47089
+ await writeBatchRecords(store, key, changedBatches);
47090
+ }
47091
+ const currentState = changedBatches.length > 0 ? await readRunState(definition, store, key) : state;
47092
+ return advanceDurableEval(definition, currentState, store, key);
47093
+ }
47094
+ async function openDurableExperiment(definition, state) {
47095
+ const data = [];
47096
+ return await _internalInitEvaluatorExperiment(
47097
+ definition.projectName,
47098
+ { ...definition.evaluator, data },
47099
+ data,
47100
+ {
47101
+ disabled: state.noSendLogs,
47102
+ experimentName: state.experimentName,
47103
+ update: true
47104
+ }
47105
+ );
47106
+ }
47107
+ async function advanceDurableEval(definition, state, store, key, existingExperiment) {
47108
+ if (state.status === "completed") return currentStatus(definition, state);
47109
+ const experiment = existingExperiment === void 0 ? await openDurableExperiment(definition, state) : existingExperiment;
47110
+ await runTaskStage(definition, state, store, key, experiment);
47111
+ await logCompletedTasks(definition, state, store, key, experiment);
47112
+ state = await readRunState(definition, store, key) ?? state;
47113
+ if (state.cases.some((record) => !record.taskComplete || !record.taskLogged)) {
47114
+ return currentStatus(definition, state);
47115
+ }
47116
+ await runScoreStages(definition, state, store, key, experiment);
47117
+ state = await readRunState(definition, store, key) ?? state;
47118
+ const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
47119
+ ({ name }) => name
47120
+ );
47121
+ const classifierNames = (definition.evaluator.classifiers ?? []).map(
47122
+ classifierName
47123
+ );
47124
+ if (state.cases.some(
47125
+ (record) => scorerNames.some(
47126
+ (name) => !Object.hasOwn(record.scores, name) || !Object.hasOwn(record.loggedScores, name)
47127
+ ) || classifierNames.some(
47128
+ (name) => !Object.hasOwn(record.loggedClassifications, name)
47129
+ )
47130
+ )) {
47131
+ return currentStatus(definition, state);
47132
+ }
47133
+ if (!await claimAction(store, key, "finish")) {
47134
+ const latest = await readRunState(definition, store, key);
47135
+ return currentStatus(definition, latest ?? state);
47136
+ }
47137
+ state.summary = await finishExperiment(definition, state, experiment);
47138
+ state.status = "completed";
47139
+ await writeRunRecord(store, key, state);
47140
+ return currentStatus(definition, state);
47141
+ }
47142
+ function currentStatus(definition, state) {
47143
+ if (state.status === "completed") {
47144
+ if (!state.summary) {
47145
+ throw new Error(`Durable eval run ${state.runId} has no saved summary`);
47146
+ }
47147
+ return {
47148
+ status: "completed",
47149
+ runId: state.runId,
47150
+ pending: { poll: 0, webhook: 0 },
47151
+ summary: state.summary
47152
+ };
47153
+ }
47154
+ const pending = { poll: 0, webhook: 0 };
47155
+ for (const batch of state.batches) {
47156
+ if (batch.status === "complete") continue;
47157
+ pending[processorForStage(definition, batch.kind, batch.scorerName).completion.mode]++;
47158
+ }
47159
+ return { status: "waiting", runId: state.runId, pending };
47160
+ }
47161
+ async function startCaseRoot(definition, state, record, experiment) {
47162
+ if (!experiment) return NOOP_SPAN;
47163
+ const datum = record.datum;
47164
+ return _internalStartSpanWithInitialMerge({
47165
+ ...definition.evaluator.state ? { state: definition.evaluator.state } : {},
47166
+ parent: await experiment.export(),
47167
+ name: "eval",
47168
+ spanId: deterministicId(`${state.runId}:${record.id}:span`),
47169
+ spanAttributes: { type: "eval" /* EVAL */ },
47170
+ event: {
47171
+ id: deterministicId(`${state.runId}:${record.id}:row`),
47172
+ input: datum.input,
47173
+ expected: "expected" in datum ? datum.expected : void 0,
47174
+ tags: datum.tags,
47175
+ origin: datum.origin
47176
+ }
47177
+ });
47178
+ }
47179
+ async function logTaskResult(definition, state, record, experiment, task) {
47180
+ const datum = record.datum;
47181
+ const root = await startCaseRoot(definition, state, record, experiment);
47182
+ try {
47183
+ if (task) {
47184
+ const result = await root.traced(
47185
+ (span) => _internalRunEvaluatorTask(
47186
+ task,
47187
+ datum,
47188
+ record.trialIndex,
47189
+ state.parameters,
47190
+ span
47191
+ ),
47192
+ {
47193
+ name: "task",
47194
+ spanId: deterministicId(`${state.runId}:${record.id}:task`),
47195
+ spanAttributes: { type: "task" /* TASK */ },
47196
+ event: { input: datum.input }
47197
+ }
47198
+ );
47199
+ record.output = assertJsonValue(
47200
+ result.output,
47201
+ `task output for ${record.caseId}`
47202
+ );
47203
+ record.metadata = assertJsonValue(result.metadata, "task metadata");
47204
+ record.tags = result.tags;
47205
+ record.taskComplete = true;
47206
+ } else {
47207
+ await root.traced((span) => span.log({ output: record.output }), {
47208
+ name: "task",
47209
+ spanId: deterministicId(`${state.runId}:${record.id}:task`),
47210
+ spanAttributes: { type: "task" /* TASK */ },
47211
+ event: { input: datum.input }
47212
+ });
47213
+ }
47214
+ root.log({
47215
+ output: record.output,
47216
+ expected: "expected" in datum ? datum.expected : void 0,
47217
+ metadata: {
47218
+ ...record.metadata,
47219
+ durable_eval: {
47220
+ run_id: state.runId,
47221
+ case_id: record.caseId,
47222
+ trial_index: record.trialIndex
47223
+ }
47224
+ },
47225
+ tags: record.tags
47226
+ });
47227
+ record.rootSpan = await root.export();
47228
+ record.taskLogged = true;
47229
+ } catch (error) {
47230
+ logError(root, error);
47231
+ throw error;
47232
+ } finally {
47233
+ root.end();
47234
+ }
47235
+ }
47236
+ async function logCompletedTasks(definition, state, store, key, experiment) {
47237
+ const changed = [];
47238
+ for (const record of state.cases) {
47239
+ if (!record.taskComplete || record.taskLogged) continue;
47240
+ if (!await claimAction(store, key, "task-log", record.id)) continue;
47241
+ await logTaskResult(definition, state, record, experiment);
47242
+ changed.push(record);
47243
+ }
47244
+ if (changed.length > 0) {
47245
+ await experiment?.flush();
47246
+ await writeCaseRecords(store, key, changed);
47247
+ }
47248
+ }
47249
+ async function runTaskStage(definition, state, store, key, experiment) {
47250
+ if (isBatchTask(definition.evaluator.task)) {
47251
+ await ensureBatches(definition, state, store, key, "task");
47252
+ return;
47253
+ }
47254
+ const task = definition.evaluator.task;
47255
+ const changed = [];
47256
+ for (const record of state.cases) {
47257
+ if (record.taskComplete) continue;
47258
+ if (!await claimAction(store, key, "task", record.id)) continue;
47259
+ await logTaskResult(definition, state, record, experiment, task);
47260
+ changed.push(record);
47261
+ }
47262
+ if (changed.length > 0) {
47263
+ await experiment?.flush();
47264
+ await writeCaseRecords(store, key, changed);
47265
+ }
47266
+ }
47267
+ async function runScoreStages(definition, state, store, key, experiment) {
47268
+ const scorers = resolveScorers(definition.evaluator.scores ?? []);
47269
+ const changed = /* @__PURE__ */ new Map();
47270
+ const persistChangedCases = async () => {
47271
+ if (changed.size === 0) return;
47272
+ await experiment?.flush();
47273
+ await writeCaseRecords(store, key, [...changed.values()]);
47274
+ changed.clear();
47275
+ };
47276
+ for (const { name, scorer } of scorers) {
47277
+ if (isBatchScorer(scorer)) {
47278
+ for (const record of state.cases) {
47279
+ if (Object.hasOwn(record.scores, name) && !Object.hasOwn(record.loggedScores, name)) {
47280
+ if (!await claimAction(store, key, "score-log", record.id, name)) {
47281
+ continue;
47282
+ }
47283
+ await evaluateAndLogScore(
47284
+ definition,
47285
+ state,
47286
+ record,
47287
+ name,
47288
+ experiment
47289
+ );
47290
+ changed.set(record.id, record);
47291
+ }
47292
+ }
47293
+ await persistChangedCases();
47294
+ await ensureBatches(definition, state, store, key, "score", name);
47295
+ continue;
47296
+ }
47297
+ for (const record of state.cases) {
47298
+ if (Object.hasOwn(record.loggedScores, name)) continue;
47299
+ if (!await claimAction(store, key, "score", record.id, name)) continue;
47300
+ await evaluateAndLogScore(
47301
+ definition,
47302
+ state,
47303
+ record,
47304
+ name,
47305
+ experiment,
47306
+ scorer
47307
+ );
47308
+ changed.set(record.id, record);
47309
+ }
47310
+ }
47311
+ for (const [index, classifier] of (definition.evaluator.classifiers ?? []).entries()) {
47312
+ const name = classifierName(classifier, index);
47313
+ for (const record of state.cases) {
47314
+ if (Object.hasOwn(record.loggedClassifications, name)) continue;
47315
+ if (!await claimAction(store, key, "classification", record.id, name)) {
47316
+ continue;
47317
+ }
47318
+ await evaluateAndLogClassification(
47319
+ definition,
47320
+ state,
47321
+ record,
47322
+ name,
47323
+ classifier,
47324
+ experiment
47325
+ );
47326
+ changed.set(record.id, record);
47327
+ }
47328
+ }
47329
+ await persistChangedCases();
47330
+ }
47331
+ function scorerArgs(record) {
47332
+ const datum = record.datum;
47333
+ return {
47334
+ ...datum,
47335
+ metadata: record.metadata,
47336
+ output: record.output
47337
+ };
47338
+ }
47339
+ function resumeCaseRoot(definition, record, experiment) {
47340
+ if (!experiment) return NOOP_SPAN;
47341
+ if (!record.rootSpan) {
47342
+ throw new Error(`Durable eval case ${record.caseId} has no root span`);
47343
+ }
47344
+ return _internalResumeSpan({
47345
+ exported: record.rootSpan,
47346
+ state: definition.evaluator.state
47347
+ });
47348
+ }
47349
+ async function evaluateAndLogScore(definition, state, record, name, experiment, scorer) {
47350
+ const root = resumeCaseRoot(definition, record, experiment);
47351
+ try {
47352
+ const rootExport = await root.export();
47353
+ const prepared = await root.traced(
47354
+ async (span) => {
47355
+ const value = scorer ? await scorer(scorerArgs(record)) : record.scores[name];
47356
+ if (scorer) {
47357
+ record.scores[name] = assertJsonValue(value, `scorer ${name} output`);
47358
+ }
47359
+ const result = _internalPrepareEvaluatorScore(value, name);
47360
+ if (result.results !== null) {
47361
+ span.log({
47362
+ output: result.output,
47363
+ metadata: result.metadata,
47364
+ scores: result.scores
47365
+ });
47366
+ }
47367
+ return result;
47368
+ },
47369
+ {
47370
+ name,
47371
+ spanId: deterministicId(`${state.runId}:${record.id}:score:${name}`),
47372
+ spanAttributes: {
47373
+ type: "score" /* SCORE */,
47374
+ purpose: "scorer"
47375
+ },
47376
+ propagatedEvent: makeScorerPropagatedEvent(rootExport || void 0),
47377
+ event: { input: scorerArgs(record) }
47378
+ }
47379
+ );
47380
+ if (prepared.scores) root.log({ scores: prepared.scores });
47381
+ record.loggedScores[name] = true;
47382
+ } catch (error) {
47383
+ logError(root, error);
47384
+ throw error;
47385
+ } finally {
47386
+ root.end();
47387
+ }
47388
+ }
47389
+ async function evaluateAndLogClassification(definition, state, record, name, classifier, experiment) {
47390
+ const root = resumeCaseRoot(definition, record, experiment);
47391
+ try {
47392
+ const rootExport = await root.export();
47393
+ const prepared = await root.traced(
47394
+ async (span) => {
47395
+ const value = await classifier(scorerArgs(record));
47396
+ record.classifications[name] = assertJsonValue(
47397
+ value,
47398
+ `classifier ${name} output`
47399
+ );
47400
+ const result = _internalPrepareEvaluatorClassification(value, name);
47401
+ if (result.results !== null) {
47402
+ span.log({ output: result.output, metadata: result.metadata });
47403
+ }
47404
+ return result;
47405
+ },
47406
+ {
47407
+ name,
47408
+ spanId: deterministicId(
47409
+ `${state.runId}:${record.id}:classification:${name}`
47410
+ ),
47411
+ spanAttributes: {
47412
+ type: "classifier" /* CLASSIFIER */,
47413
+ purpose: "scorer"
47414
+ },
47415
+ propagatedEvent: makeScorerPropagatedEvent(rootExport || void 0),
47416
+ event: { input: scorerArgs(record) }
47417
+ }
47418
+ );
47419
+ if (prepared.classifications) {
47420
+ root.log({ classifications: prepared.classifications });
47421
+ }
47422
+ record.loggedClassifications[name] = true;
47423
+ } catch (error) {
47424
+ logError(root, error);
47425
+ throw error;
47426
+ } finally {
47427
+ root.end();
47428
+ }
47429
+ }
47430
+ async function ensureBatches(definition, state, store, key, kind, scorerName2) {
47431
+ const processor = processorForStage(definition, kind, scorerName2);
47432
+ const plans = plannedBatches(
47433
+ definition,
47434
+ state.runId,
47435
+ state.cases.map(({ id }) => id)
47436
+ );
47437
+ const casesById = new Map(state.cases.map((record) => [record.id, record]));
47438
+ for (const plan of plans) {
47439
+ if (plan.kind !== kind || plan.scorerName !== scorerName2) continue;
47440
+ if (state.batches.some(({ id }) => id === plan.id)) continue;
47441
+ const records = plan.itemIds.map((id) => casesById.get(id));
47442
+ const ready = records.every(
47443
+ (record) => kind === "task" ? !record.taskComplete : !Object.hasOwn(record.scores, scorerName2)
47444
+ );
47445
+ if (!ready) continue;
47446
+ const batchId = plan.id;
47447
+ const claim = await store.getOrSet(
47448
+ claimRecordKey(key, "batch", batchId),
47449
+ encoder.encode(batchId)
47450
+ );
47451
+ if (!claim.created) continue;
47452
+ const context = { runId: state.runId, batchId };
47453
+ const items = records.map(
47454
+ (record) => kind === "task" ? taskBatchItem(record, state.parameters) : scorerBatchItem(record)
47455
+ );
47456
+ const submissionData = assertJsonValue(
47457
+ await processor.submit(items, context),
47458
+ `submission data for batch ${batchId}`
47459
+ );
47460
+ const externalId = processor.completion.mode === "webhook" ? processor.completion.getExternalId(submissionData, context) : void 0;
47461
+ if (externalId !== void 0 && !externalId.trim()) {
47462
+ throw new Error(`Batch ${batchId} produced an empty externalId`);
47463
+ }
47464
+ const batch = {
47465
+ id: batchId,
47466
+ kind,
47467
+ scorerName: scorerName2,
47468
+ itemIds: records.map((record) => record.id),
47469
+ submissionData,
47470
+ externalId,
47471
+ status: "submitted"
47472
+ };
47473
+ state.batches.push(batch);
47474
+ await writeBatchRecords(store, key, [batch]);
47475
+ }
47476
+ }
47477
+ async function collectBatch(definition, state, batch) {
47478
+ const processor = processorForStage(definition, batch.kind, batch.scorerName);
47479
+ const context = { runId: state.runId, batchId: batch.id };
47480
+ const results = await processor.collect(batch.submissionData, context);
47481
+ if (!Array.isArray(results)) {
47482
+ throw new Error(`collect for batch ${batch.id} must return an array`);
47483
+ }
47484
+ const expectedIds = new Set(batch.itemIds);
47485
+ const seen = /* @__PURE__ */ new Set();
47486
+ const records = [];
47487
+ for (const result of results) {
47488
+ const id = resultItemId(result);
47489
+ if (!expectedIds.has(id)) {
47490
+ throw new Error(`Batch ${batch.id} returned unknown item ${id}`);
47491
+ }
47492
+ if (seen.has(id)) {
47493
+ throw new Error(`Batch ${batch.id} returned item ${id} more than once`);
47494
+ }
47495
+ seen.add(id);
47496
+ const record = state.cases.find((candidate) => candidate.id === id);
47497
+ records.push(record);
47498
+ if (batch.kind === "task") {
47499
+ record.output = assertJsonValue(
47500
+ result.output,
47501
+ `task output for item ${id}`
47502
+ );
47503
+ if ("metadata" in result && result.metadata !== void 0) {
47504
+ record.metadata = assertJsonValue(
47505
+ {
47506
+ ...record.metadata,
47507
+ ...result.metadata
47508
+ },
47509
+ `metadata for ${id}`
47510
+ );
47511
+ }
47512
+ if ("tags" in result && result.tags !== void 0)
47513
+ record.tags = result.tags;
47514
+ record.taskComplete = true;
47515
+ } else {
47516
+ record.scores[batch.scorerName] = assertJsonValue(
47517
+ result.score,
47518
+ `score output for item ${id}`
47519
+ );
47520
+ }
47521
+ }
47522
+ const missing = batch.itemIds.filter((id) => !seen.has(id));
47523
+ if (missing.length > 0) {
47524
+ throw new Error(
47525
+ `Batch ${batch.id} did not return results for: ${missing.join(", ")}`
47526
+ );
47527
+ }
47528
+ return records;
47529
+ }
47530
+ function processorForStage(definition, kind, scorerName2) {
47531
+ if (kind === "task") {
47532
+ if (!isBatchTask(definition.evaluator.task)) {
47533
+ throw new Error("Definition no longer contains the batch task");
47534
+ }
47535
+ return definition.evaluator.task.processor;
47536
+ }
47537
+ const scorer = resolveScorers(definition.evaluator.scores ?? []).find(
47538
+ ({ name }) => name === scorerName2
47539
+ )?.scorer;
47540
+ if (!isBatchScorer(scorer)) {
47541
+ throw new Error(`Definition no longer contains scorer ${scorerName2}`);
47542
+ }
47543
+ return scorer.processor;
47544
+ }
47545
+ async function materializeCases(definition, data, experiment) {
47546
+ const evaluator = definition.evaluator;
47547
+ const iterable = await _internalResolveEvaluatorData(
47548
+ {
47549
+ data,
47550
+ projectName: definition.projectName,
47551
+ projectId: evaluator.projectId,
47552
+ state: evaluator.state
47553
+ },
47554
+ experiment
47555
+ );
47556
+ const records = [];
47557
+ const seen = /* @__PURE__ */ new Set();
47558
+ for await (const datum of iterable) {
47559
+ const caseId = datum.id ?? datum.upsert_id ?? (evaluator.caseId ? await evaluator.caseId(datum) : void 0);
47560
+ if (!caseId) {
47561
+ throw new Error(
47562
+ "Every durable eval case requires id, upsert_id, or caseId"
47563
+ );
47564
+ }
47565
+ if (seen.has(caseId))
47566
+ throw new Error(`Duplicate durable eval case id: ${caseId}`);
47567
+ seen.add(caseId);
47568
+ const trialCount = datum.trialCount ?? evaluator.trialCount ?? 1;
47569
+ if (!Number.isInteger(trialCount) || trialCount < 1) {
47570
+ throw new Error(`Invalid trialCount for durable eval case ${caseId}`);
47571
+ }
47572
+ for (let trialIndex = 0; trialIndex < trialCount; trialIndex++) {
47573
+ records.push({
47574
+ id: `${caseId}:trial:${trialIndex}`,
47575
+ caseId,
47576
+ trialIndex,
47577
+ datum: assertJsonValue(datum, `case ${caseId}`),
47578
+ metadata: assertJsonValue(
47579
+ "metadata" in datum ? datum.metadata : {},
47580
+ `metadata for ${caseId}`
47581
+ ),
47582
+ tags: datum.tags,
47583
+ taskComplete: false,
47584
+ taskLogged: false,
47585
+ scores: /* @__PURE__ */ Object.create(null),
47586
+ loggedScores: /* @__PURE__ */ Object.create(null),
47587
+ classifications: /* @__PURE__ */ Object.create(null),
47588
+ loggedClassifications: /* @__PURE__ */ Object.create(null)
47589
+ });
47590
+ }
47591
+ }
47592
+ return records;
47593
+ }
47594
+ function taskBatchItem(record, parameters) {
47595
+ const datum = record.datum;
47596
+ return {
47597
+ id: record.id,
47598
+ input: datum.input,
47599
+ expected: "expected" in datum ? datum.expected : void 0,
47600
+ metadata: record.metadata,
47601
+ tags: record.tags,
47602
+ parameters,
47603
+ trialIndex: record.trialIndex
47604
+ };
47605
+ }
47606
+ function scorerBatchItem(record) {
47607
+ const datum = record.datum;
47608
+ return {
47609
+ id: record.id,
47610
+ input: datum.input,
47611
+ output: record.output,
47612
+ expected: "expected" in datum ? datum.expected : void 0,
47613
+ metadata: record.metadata,
47614
+ tags: record.tags,
47615
+ trialIndex: record.trialIndex
47616
+ };
47617
+ }
47618
+ async function finishExperiment(definition, state, experiment) {
47619
+ const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
47620
+ ({ name }) => name
47621
+ );
47622
+ const results = state.cases.map((record) => {
47623
+ const datum = record.datum;
47624
+ const scores = Object.fromEntries(
47625
+ scorerNames.flatMap(
47626
+ (name) => Object.entries(
47627
+ _internalPrepareEvaluatorScore(
47628
+ record.scores[name],
47629
+ name
47630
+ ).scores ?? {}
47631
+ )
47632
+ )
47633
+ );
47634
+ const classifications = Object.fromEntries(
47635
+ Object.entries(record.classifications).flatMap(
47636
+ ([name, value]) => Object.entries(
47637
+ _internalPrepareEvaluatorClassification(value, name).classifications ?? {}
47638
+ )
47639
+ )
47640
+ );
47641
+ return {
47642
+ ...datum,
47643
+ output: record.output,
47644
+ metadata: record.metadata,
47645
+ tags: record.tags,
47646
+ scores,
47647
+ error: void 0,
47648
+ ...Object.keys(classifications).length > 0 ? { classifications } : {}
47649
+ };
47650
+ });
47651
+ if (!experiment) {
47652
+ return buildLocalSummary(
47653
+ {
47654
+ ...definition.evaluator,
47655
+ projectName: definition.projectName,
47656
+ evalName: state.experimentName
47657
+ },
47658
+ results
47659
+ );
47660
+ }
47661
+ await experiment.flush();
47662
+ let comparisonExperimentId = definition.evaluator.baseExperimentId;
47663
+ if (!comparisonExperimentId) {
47664
+ try {
47665
+ comparisonExperimentId = await experiment._getBaseExperimentId();
47666
+ } catch {
47667
+ comparisonExperimentId = void 0;
47668
+ }
47669
+ }
47670
+ return await experiment.summarize({
47671
+ summarizeScores: definition.evaluator.summarizeScores,
47672
+ ...comparisonExperimentId ? { comparisonExperimentId } : {}
47673
+ });
47674
+ }
47675
+ function resolveScorers(scorers) {
47676
+ return scorers.map((scorer, index) => ({
47677
+ name: isBatchScorer(scorer) ? scorer.name : scorer.name || `scorer_${index}`,
47678
+ scorer
47679
+ }));
47680
+ }
47681
+ function runKey(projectName, evalName, runId) {
47682
+ return `durable-eval/v1/runs/${contentVersion(encoder.encode(`${projectName}\0${evalName}\0${runId}`))}`;
47683
+ }
47684
+ function encodedKeyPart(value) {
47685
+ return uint8ArrayToBase64(encoder.encode(value)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
47686
+ }
47687
+ function caseRecordKey(key, caseId, kind, ...names) {
47688
+ const suffix = names.map(encodedKeyPart).join("/");
47689
+ return `${key}/cases/${encodedKeyPart(caseId)}/${kind}${suffix ? `/${suffix}` : ""}`;
47690
+ }
47691
+ function batchRecordKey(key, batchId) {
47692
+ return `${key}/batches/${encodedKeyPart(batchId)}`;
47693
+ }
47694
+ function claimRecordKey(key, kind, ...parts) {
47695
+ const identity = stableStringify([kind, parts]);
47696
+ return `${key}/claims/${contentVersion(encoder.encode(identity))}`;
47697
+ }
47698
+ async function claimAction(store, key, kind, ...parts) {
47699
+ return (await store.getOrSet(
47700
+ claimRecordKey(key, kind, ...parts),
47701
+ encoder.encode("claimed")
47702
+ )).created;
47703
+ }
47704
+ function plannedBatches(definition, runId, caseIds) {
47705
+ const stages = [];
47706
+ if (isBatchTask(definition.evaluator.task)) stages.push({ kind: "task" });
47707
+ for (const { name, scorer } of resolveScorers(
47708
+ definition.evaluator.scores ?? []
47709
+ )) {
47710
+ if (isBatchScorer(scorer)) {
47711
+ stages.push({ kind: "score", scorerName: name });
47712
+ }
47713
+ }
47714
+ const plans = [];
47715
+ for (const { kind, scorerName: scorerName2 } of stages) {
47716
+ const batchSize = processorForStage(definition, kind, scorerName2).batchSize ?? DEFAULT_BATCH_SIZE;
47717
+ if (!Number.isInteger(batchSize) || batchSize < 1) {
47718
+ throw new Error(
47719
+ `Invalid batchSize for ${scorerName2 ?? "task"}: ${batchSize}`
47720
+ );
47721
+ }
47722
+ for (let offset = 0; offset < caseIds.length; offset += batchSize) {
47723
+ const itemIds = caseIds.slice(offset, offset + batchSize);
47724
+ plans.push({
47725
+ id: deterministicId(
47726
+ stableStringify([runId, kind, scorerName2, itemIds])
47727
+ ),
47728
+ kind,
47729
+ scorerName: scorerName2,
47730
+ itemIds
47731
+ });
47732
+ }
47733
+ }
47734
+ return plans;
47735
+ }
47736
+ async function readCaseRecord(definition, store, key, id) {
47737
+ const scorers = resolveScorers(definition.evaluator.scores ?? []);
47738
+ const classifiers = (definition.evaluator.classifiers ?? []).map(
47739
+ classifierName
47740
+ );
47741
+ const [
47742
+ base,
47743
+ task,
47744
+ taskLog,
47745
+ scoreValues,
47746
+ scoreLogValues,
47747
+ classificationValues,
47748
+ classificationLogValues
47749
+ ] = await Promise.all([
47750
+ readJson(store, caseRecordKey(key, id, "base")),
47751
+ readJson(store, caseRecordKey(key, id, "task")),
47752
+ readJson(store, caseRecordKey(key, id, "task-log")),
47753
+ Promise.all(
47754
+ scorers.map(async ({ name }) => ({
47755
+ name,
47756
+ value: await readJson(
47757
+ store,
47758
+ caseRecordKey(key, id, "score", name)
47759
+ )
47760
+ }))
47761
+ ),
47762
+ Promise.all(
47763
+ scorers.map(async ({ name }) => ({
47764
+ name,
47765
+ value: await readJson(
47766
+ store,
47767
+ caseRecordKey(key, id, "score-log", name)
47768
+ )
47769
+ }))
47770
+ ),
47771
+ Promise.all(
47772
+ classifiers.map(async (name) => ({
47773
+ name,
47774
+ value: await readJson(
47775
+ store,
47776
+ caseRecordKey(key, id, "classification", name)
47777
+ )
47778
+ }))
47779
+ ),
47780
+ Promise.all(
47781
+ classifiers.map(async (name) => ({
47782
+ name,
47783
+ value: await readJson(
47784
+ store,
47785
+ caseRecordKey(key, id, "classification-log", name)
47786
+ )
47787
+ }))
47788
+ )
47789
+ ]);
47790
+ if (!base) throw new Error(`Durable eval case ${id} is missing`);
47791
+ const scores = /* @__PURE__ */ Object.create(null);
47792
+ for (const { name, value } of scoreValues) {
47793
+ if (value !== void 0) scores[name] = value;
47794
+ }
47795
+ const loggedScores = /* @__PURE__ */ Object.create(null);
47796
+ for (const { name, value } of scoreLogValues) {
47797
+ if (value) loggedScores[name] = true;
47798
+ }
47799
+ const classifications = /* @__PURE__ */ Object.create(null);
47800
+ for (const { name, value } of classificationValues) {
47801
+ if (value !== void 0) classifications[name] = value;
47802
+ }
47803
+ const loggedClassifications = /* @__PURE__ */ Object.create(null);
47804
+ for (const { name, value } of classificationLogValues) {
47805
+ if (value) loggedClassifications[name] = true;
47806
+ }
47807
+ return {
47808
+ ...base,
47809
+ metadata: task?.metadata ?? base.metadata,
47810
+ tags: task ? task.tags : base.tags,
47811
+ taskComplete: task !== void 0,
47812
+ taskLogged: taskLog !== void 0,
47813
+ output: task?.output,
47814
+ rootSpan: taskLog?.rootSpan,
47815
+ scores,
47816
+ loggedScores,
47817
+ classifications,
47818
+ loggedClassifications
47819
+ };
47820
+ }
47821
+ async function readRunState(definition, store, key) {
47822
+ const record = await readJson(store, key);
47823
+ if (!record) return void 0;
47824
+ const plans = plannedBatches(definition, record.runId, record.caseIds);
47825
+ const [cases, batchRecords] = await Promise.all([
47826
+ Promise.all(
47827
+ record.caseIds.map((id) => readCaseRecord(definition, store, key, id))
47828
+ ),
47829
+ Promise.all(
47830
+ plans.map(async ({ id }) => {
47831
+ return readJson(store, batchRecordKey(key, id));
47832
+ })
47833
+ )
47834
+ ]);
47835
+ const batches = batchRecords.filter(
47836
+ (value) => value !== void 0
47837
+ );
47838
+ const { caseIds: _caseIds, ...state } = record;
47839
+ return { ...state, cases, batches };
47840
+ }
47841
+ async function writeRunRecord(store, key, state) {
47842
+ const { cases, batches: _batches, ...record } = state;
47843
+ await writeJson(store, key, {
47844
+ ...record,
47845
+ caseIds: cases.map(({ id }) => id)
47846
+ });
47847
+ }
47848
+ async function writeCaseBaseRecords(store, key, records) {
47849
+ await Promise.all(
47850
+ records.map(
47851
+ ({ id, caseId, trialIndex, datum, metadata, tags }) => writeJson(store, caseRecordKey(key, id, "base"), {
47852
+ id,
47853
+ caseId,
47854
+ trialIndex,
47855
+ datum,
47856
+ metadata,
47857
+ tags
47858
+ })
47859
+ )
47860
+ );
47861
+ }
47862
+ async function writeCaseRecords(store, key, records) {
47863
+ const writes = [];
47864
+ for (const record of records) {
47865
+ if (record.taskComplete) {
47866
+ writes.push(
47867
+ writeJson(store, caseRecordKey(key, record.id, "task"), {
47868
+ output: record.output,
47869
+ metadata: record.metadata,
47870
+ tags: record.tags,
47871
+ taskComplete: true
47872
+ })
47873
+ );
47874
+ }
47875
+ if (record.taskLogged) {
47876
+ writes.push(
47877
+ writeJson(store, caseRecordKey(key, record.id, "task-log"), {
47878
+ rootSpan: record.rootSpan,
47879
+ taskLogged: true
47880
+ })
47881
+ );
47882
+ }
47883
+ for (const [name, value] of Object.entries(record.scores)) {
47884
+ writes.push(
47885
+ writeJson(store, caseRecordKey(key, record.id, "score", name), value)
47886
+ );
47887
+ }
47888
+ for (const name of Object.keys(record.loggedScores)) {
47889
+ writes.push(
47890
+ writeJson(
47891
+ store,
47892
+ caseRecordKey(key, record.id, "score-log", name),
47893
+ true
47894
+ )
47895
+ );
47896
+ }
47897
+ for (const [name, value] of Object.entries(record.classifications)) {
47898
+ writes.push(
47899
+ writeJson(
47900
+ store,
47901
+ caseRecordKey(key, record.id, "classification", name),
47902
+ value
47903
+ )
47904
+ );
47905
+ }
47906
+ for (const name of Object.keys(record.loggedClassifications)) {
47907
+ writes.push(
47908
+ writeJson(
47909
+ store,
47910
+ caseRecordKey(key, record.id, "classification-log", name),
47911
+ true
47912
+ )
47913
+ );
47914
+ }
47915
+ }
47916
+ await Promise.all(writes);
47917
+ }
47918
+ async function writeBatchRecords(store, key, records) {
47919
+ await Promise.all(
47920
+ records.map(
47921
+ (record) => writeJson(store, batchRecordKey(key, record.id), record)
47922
+ )
47923
+ );
47924
+ }
47925
+ function deterministicId(value) {
47926
+ const hex = contentVersion(encoder.encode(value)).padEnd(32, "0").slice(0, 32);
47927
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-5${hex.slice(13, 16)}-a${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
47928
+ }
47929
+ function contentVersion(value) {
47930
+ if (isomorph_default.hash) return isomorph_default.hash(decoder.decode(value));
47931
+ let hash = 2166136261;
47932
+ for (const byte of value) {
47933
+ hash ^= byte;
47934
+ hash = Math.imul(hash, 16777619);
47935
+ }
47936
+ return (hash >>> 0).toString(16).padStart(8, "0");
47937
+ }
47938
+ async function readJson(store, key) {
47939
+ const value = await store.read(key);
47940
+ return value ? JSON.parse(decoder.decode(value)) : void 0;
47941
+ }
47942
+ async function writeJson(store, key, value) {
47943
+ await store.write(key, encoder.encode(stableStringify(value)));
47944
+ }
47945
+ function stableStringify(value) {
47946
+ return JSON.stringify(value, (_key, nested) => {
47947
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
47948
+ return Object.fromEntries(
47949
+ Object.entries(nested).sort(
47950
+ ([left], [right]) => left.localeCompare(right)
47951
+ )
47952
+ );
47953
+ }
47954
+ return nested;
47955
+ });
47956
+ }
47957
+ function assertJsonValue(value, label) {
47958
+ try {
47959
+ const serialized = JSON.stringify(value);
47960
+ if (serialized === void 0)
47961
+ throw new Error("value serializes to undefined");
47962
+ return JSON.parse(serialized);
47963
+ } catch (error) {
47964
+ throw new Error(`${label} must be JSON serializable`, { cause: error });
47965
+ }
47966
+ }
47967
+ function resultItemId(value) {
47968
+ if (typeof value !== "object" || value === null || !("id" in value) || typeof value.id !== "string") {
47969
+ throw new Error("Batch results must contain a string id");
47970
+ }
47971
+ return value.id;
47972
+ }
47973
+ function isBatchTask(value) {
47974
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_TASK_KIND;
47975
+ }
47976
+ function isBatchScorer(value) {
47977
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_SCORER_KIND;
47978
+ }
47979
+ function asError(error) {
47980
+ return error instanceof Error ? error : new Error(String(error));
47981
+ }
47982
+
45966
47983
  // src/agent-assertions.ts
45967
47984
  function agentAssertionScorer(callback, options = {}) {
45968
47985
  return async (args) => {