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/browser.js CHANGED
@@ -38,6 +38,8 @@ __export(browser_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(browser_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(browser_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(browser_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(browser_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() {
@@ -13220,6 +13378,22 @@ function processInputAttachments(input) {
13220
13378
  };
13221
13379
  }
13222
13380
  }
13381
+ 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;
13382
+ const voyageBase64Value = voyageBase64Key ? node[voyageBase64Key] : void 0;
13383
+ if (voyageBase64Key && typeof voyageBase64Value === "string" && voyageBase64Value.startsWith("data:")) {
13384
+ const mediaType = inferMediaTypeFromDataUrl(
13385
+ voyageBase64Value,
13386
+ node.type === "video_base64" ? "video/mp4" : "image/png"
13387
+ );
13388
+ const filename = `${node.type === "video_base64" ? "video" : "image"}.${getExtensionFromMediaType(mediaType)}`;
13389
+ const attachment = toAttachment(voyageBase64Value, mediaType, filename);
13390
+ if (attachment) {
13391
+ return {
13392
+ ...node,
13393
+ [voyageBase64Key]: attachment
13394
+ };
13395
+ }
13396
+ }
13223
13397
  if (node.type === "file" && node.file && typeof node.file === "object" && typeof node.file.file_data === "string" && node.file.file_data.startsWith("data:")) {
13224
13398
  const mediaType = inferMediaTypeFromDataUrl(
13225
13399
  node.file.file_data,
@@ -14567,12 +14741,30 @@ function logInstrumentationError(context, error) {
14567
14741
 
14568
14742
  // src/wrappers/anthropic-tokens-util.ts
14569
14743
  function finalizeAnthropicTokens(metrics) {
14570
- const prompt_tokens = (metrics.prompt_tokens || 0) + (metrics.prompt_cached_tokens || 0) + (metrics.prompt_cache_creation_tokens || 0);
14571
- return {
14744
+ const hasSplitCacheCreationTokens = metrics.prompt_cache_creation_5m_tokens !== void 0 || metrics.prompt_cache_creation_1h_tokens !== void 0;
14745
+ const splitCacheCreationTokens = (metrics.prompt_cache_creation_5m_tokens || 0) + (metrics.prompt_cache_creation_1h_tokens || 0);
14746
+ const aggregateCacheCreationTokens = metrics.prompt_cache_creation_tokens || 0;
14747
+ const effectiveCacheCreationTokens = Math.max(
14748
+ aggregateCacheCreationTokens,
14749
+ splitCacheCreationTokens
14750
+ );
14751
+ const prompt_tokens = (metrics.prompt_tokens || 0) + (metrics.prompt_cached_tokens || 0) + effectiveCacheCreationTokens;
14752
+ const finalized = {
14572
14753
  ...metrics,
14573
14754
  prompt_tokens,
14574
14755
  tokens: prompt_tokens + (metrics.completion_tokens || 0)
14575
14756
  };
14757
+ if (hasSplitCacheCreationTokens && splitCacheCreationTokens >= aggregateCacheCreationTokens) {
14758
+ delete finalized.prompt_cache_creation_tokens;
14759
+ }
14760
+ return finalized;
14761
+ }
14762
+ function toNumericMetrics(metrics) {
14763
+ return Object.fromEntries(
14764
+ Object.entries(metrics).filter(
14765
+ (entry) => entry[1] !== void 0
14766
+ )
14767
+ );
14576
14768
  }
14577
14769
  function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens = 0) {
14578
14770
  const cacheTokens = {};
@@ -15178,6 +15370,18 @@ function parseMetricsFromUsage2(usage) {
15178
15370
  saveIfExistsTo("output_tokens", "completion_tokens");
15179
15371
  saveIfExistsTo("cache_read_input_tokens", "prompt_cached_tokens");
15180
15372
  saveIfExistsTo("cache_creation_input_tokens", "prompt_cache_creation_tokens");
15373
+ if (isObject(usage.cache_creation)) {
15374
+ const cacheCreation = usage.cache_creation;
15375
+ for (const [source, target] of [
15376
+ ["ephemeral_5m_input_tokens", "prompt_cache_creation_5m_tokens"],
15377
+ ["ephemeral_1h_input_tokens", "prompt_cache_creation_1h_tokens"]
15378
+ ]) {
15379
+ const value = cacheCreation[source];
15380
+ if (typeof value === "number") {
15381
+ metrics[target] = value;
15382
+ }
15383
+ }
15384
+ }
15181
15385
  if (isObject(usage.server_tool_use)) {
15182
15386
  for (const [name, value] of Object.entries(usage.server_tool_use)) {
15183
15387
  if (typeof value === "number") {
@@ -20476,37 +20680,97 @@ function seedTaskToolUseIdMapping(taskIdToToolUseId, message) {
20476
20680
  taskIdToToolUseId.set(message.task_id, message.tool_use_id);
20477
20681
  }
20478
20682
  }
20479
- function extractUsageFromMessage(message) {
20480
- const metrics = {};
20481
- let usage;
20482
- if (message.type === "assistant") {
20483
- usage = message.message?.usage;
20484
- } else if (message.type === "result") {
20485
- usage = message.usage;
20486
- }
20683
+ function tokenCount(value) {
20684
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0 ? value : void 0;
20685
+ }
20686
+ function copyUsage(usage) {
20487
20687
  if (!usage || typeof usage !== "object") {
20488
- return metrics;
20688
+ return void 0;
20689
+ }
20690
+ const copy = {};
20691
+ for (const key of [
20692
+ "input_tokens",
20693
+ "output_tokens",
20694
+ "cache_read_input_tokens",
20695
+ "cache_creation_input_tokens"
20696
+ ]) {
20697
+ const value = tokenCount(Reflect.get(usage, key));
20698
+ if (value !== void 0) {
20699
+ copy[key] = value;
20700
+ }
20701
+ }
20702
+ const cacheCreation = Reflect.get(usage, "cache_creation");
20703
+ if (cacheCreation && typeof cacheCreation === "object") {
20704
+ const cacheCreationCopy = {};
20705
+ for (const key of [
20706
+ "ephemeral_5m_input_tokens",
20707
+ "ephemeral_1h_input_tokens"
20708
+ ]) {
20709
+ const value = tokenCount(Reflect.get(cacheCreation, key));
20710
+ if (value !== void 0) {
20711
+ cacheCreationCopy[key] = value;
20712
+ }
20713
+ }
20714
+ if (Object.keys(cacheCreationCopy).length > 0) {
20715
+ copy.cache_creation = cacheCreationCopy;
20716
+ }
20717
+ }
20718
+ return Object.keys(copy).length > 0 ? copy : void 0;
20719
+ }
20720
+ function mergeUsage(base, override) {
20721
+ if (!base || !override) {
20722
+ return override ?? base;
20723
+ }
20724
+ const cacheCreation = base.cache_creation || override.cache_creation ? { ...base.cache_creation, ...override.cache_creation } : void 0;
20725
+ return {
20726
+ ...base,
20727
+ ...override,
20728
+ ...cacheCreation && { cache_creation: cacheCreation }
20729
+ };
20730
+ }
20731
+ function extractUsage(usage, includeOutput) {
20732
+ const metrics = {};
20733
+ if (!usage) {
20734
+ return {};
20489
20735
  }
20490
20736
  const inputTokens = getNumberProperty(usage, "input_tokens");
20491
20737
  if (inputTokens !== void 0) {
20492
20738
  metrics.prompt_tokens = inputTokens;
20493
20739
  }
20494
- const outputTokens = getNumberProperty(usage, "output_tokens");
20495
- if (outputTokens !== void 0) {
20496
- metrics.completion_tokens = outputTokens;
20740
+ if (includeOutput) {
20741
+ const outputTokens = getNumberProperty(usage, "output_tokens");
20742
+ if (outputTokens !== void 0) {
20743
+ metrics.completion_tokens = outputTokens;
20744
+ }
20497
20745
  }
20498
20746
  const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
20499
20747
  const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
20500
- if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
20501
- Object.assign(
20502
- metrics,
20503
- extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
20504
- );
20748
+ Object.assign(
20749
+ metrics,
20750
+ extractAnthropicCacheTokens(cacheReadTokens, cacheCreationTokens)
20751
+ );
20752
+ const cacheCreation5mTokens = getNumberProperty(
20753
+ usage.cache_creation,
20754
+ "ephemeral_5m_input_tokens"
20755
+ );
20756
+ const cacheCreation1hTokens = getNumberProperty(
20757
+ usage.cache_creation,
20758
+ "ephemeral_1h_input_tokens"
20759
+ );
20760
+ if (cacheCreation5mTokens !== void 0) {
20761
+ metrics.prompt_cache_creation_5m_tokens = cacheCreation5mTokens;
20505
20762
  }
20506
- if (Object.keys(metrics).length > 0) {
20507
- Object.assign(metrics, finalizeAnthropicTokens(metrics));
20763
+ if (cacheCreation1hTokens !== void 0) {
20764
+ metrics.prompt_cache_creation_1h_tokens = cacheCreation1hTokens;
20508
20765
  }
20509
- return metrics;
20766
+ if (Object.keys(metrics).length === 0) {
20767
+ return {};
20768
+ }
20769
+ const finalized = finalizeAnthropicTokens(metrics);
20770
+ if (metrics.completion_tokens === void 0) {
20771
+ delete finalized.tokens;
20772
+ }
20773
+ return toNumericMetrics(finalized);
20510
20774
  }
20511
20775
  function buildLLMInput(promptMessages, conversationHistory) {
20512
20776
  const inputParts = [...promptMessages, ...conversationHistory];
@@ -20540,16 +20804,16 @@ function buildRootPromptMessages(prompt, capturedPromptMessages) {
20540
20804
  function formatCapturedMessages(messages) {
20541
20805
  return messages.length > 0 ? messages : [];
20542
20806
  }
20543
- async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, existingSpan) {
20807
+ async function createLLMSpanForMessages(messages, promptMessages, conversationHistory, options, startTime, parentSpan, usage, hasFinalOutputUsage, existingSpan) {
20544
20808
  if (messages.length === 0) {
20545
20809
  return void 0;
20546
20810
  }
20547
20811
  const lastMessage = messages[messages.length - 1];
20548
- if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
20812
+ if (lastMessage.type !== "assistant") {
20549
20813
  return void 0;
20550
20814
  }
20551
- const model = lastMessage.message.model || options.model;
20552
- const usage = extractUsageFromMessage(lastMessage);
20815
+ const model = lastMessage.message?.model || options.model;
20816
+ const metrics = options.includePartialMessages ? extractUsage(usage, hasFinalOutputUsage) : {};
20553
20817
  const input = buildLLMInput(promptMessages, conversationHistory);
20554
20818
  const outputs = messages.map(
20555
20819
  (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
@@ -20571,8 +20835,8 @@ async function createLLMSpanForMessages(messages, promptMessages, conversationHi
20571
20835
  );
20572
20836
  span.log({
20573
20837
  input,
20574
- metadata: model ? { model } : void 0,
20575
- metrics: usage,
20838
+ metadata: { ...model && { model }, provider: "anthropic" },
20839
+ ...Object.keys(metrics).length > 0 ? { metrics } : {},
20576
20840
  output: outputs
20577
20841
  });
20578
20842
  const spanExport = await span.export();
@@ -20662,11 +20926,17 @@ function prepareLocalToolHandlersInMcpServers(mcpServers) {
20662
20926
  }
20663
20927
  return { hasLocalToolHandlers, localToolHookNames };
20664
20928
  }
20665
- function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
20929
+ function createToolTracingHooks(resolveParentSpan, taskIdToToolUseId, toolUseToParent, activeToolSpans, mcpServers, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
20666
20930
  const preToolUse = async (input, toolUseID) => {
20667
20931
  if (input.hook_event_name !== "PreToolUse" || !toolUseID) {
20668
20932
  return {};
20669
20933
  }
20934
+ if (!toolUseToParent.has(toolUseID) && input.agent_id) {
20935
+ const parentToolUseId = taskIdToToolUseId.get(input.agent_id);
20936
+ if (parentToolUseId) {
20937
+ toolUseToParent.set(toolUseID, parentToolUseId);
20938
+ }
20939
+ }
20670
20940
  if (skipLocalToolHooks && (isLocalToolUse(input.tool_name, mcpServers) || localToolHookNames.has(input.tool_name))) {
20671
20941
  return {};
20672
20942
  }
@@ -20862,9 +21132,6 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
20862
21132
  }
20863
21133
  const metadata = {
20864
21134
  ...subAgentDetailsToMetadata(details),
20865
- ...input.agent_transcript_path && {
20866
- "claude_agent_sdk.agent_transcript_path": input.agent_transcript_path
20867
- },
20868
21135
  "claude_agent_sdk.stop_hook_active": input.stop_hook_active
20869
21136
  };
20870
21137
  try {
@@ -20886,7 +21153,7 @@ function createToolTracingHooks(resolveParentSpan, activeToolSpans, mcpServers,
20886
21153
  subagentStop
20887
21154
  };
20888
21155
  }
20889
- function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
21156
+ function injectTracingHooks(options, resolveParentSpan, taskIdToToolUseId, toolUseToParent, activeToolSpans, localToolHookNames, skipLocalToolHooks, subAgentDetailsByToolUseId, subAgentSpans, endedSubAgentSpans) {
20890
21157
  const {
20891
21158
  preToolUse,
20892
21159
  postToolUse,
@@ -20895,6 +21162,8 @@ function injectTracingHooks(options, resolveParentSpan, activeToolSpans, localTo
20895
21162
  subagentStop
20896
21163
  } = createToolTracingHooks(
20897
21164
  resolveParentSpan,
21165
+ taskIdToToolUseId,
21166
+ toolUseToParent,
20898
21167
  activeToolSpans,
20899
21168
  options.mcpServers,
20900
21169
  localToolHookNames,
@@ -20976,6 +21245,13 @@ async function finalizeCurrentMessageGroup(state) {
20976
21245
  }
20977
21246
  }
20978
21247
  const existingLlmSpan = state.activeLlmSpansByParentToolUse.get(parentKey);
21248
+ const lastMessage = state.currentMessages[state.currentMessages.length - 1];
21249
+ const messageId = lastMessage?.message?.id;
21250
+ const usage = state.options.includePartialMessages ? mergeUsage(
21251
+ copyUsage(lastMessage?.message?.usage),
21252
+ messageId ? state.usageByMessageId.get(messageId) : void 0
21253
+ ) : void 0;
21254
+ const hasFinalOutputUsage = messageId !== void 0 && state.finalOutputUsageMessageIds.has(messageId);
20979
21255
  const llmSpanResult = await createLLMSpanForMessages(
20980
21256
  state.currentMessages,
20981
21257
  promptMessages,
@@ -20983,6 +21259,8 @@ async function finalizeCurrentMessageGroup(state) {
20983
21259
  state.options,
20984
21260
  state.currentMessageStartTime,
20985
21261
  parentSpan,
21262
+ usage,
21263
+ hasFinalOutputUsage,
20986
21264
  existingLlmSpan
20987
21265
  );
20988
21266
  if (llmSpanResult) {
@@ -21000,9 +21278,17 @@ async function finalizeCurrentMessageGroup(state) {
21000
21278
  }
21001
21279
  }
21002
21280
  state.activeLlmSpansByParentToolUse.delete(parentKey);
21003
- const lastMessage = state.currentMessages[state.currentMessages.length - 1];
21004
- if (lastMessage?.message?.usage) {
21005
- state.accumulatedOutputTokens += getNumberProperty(lastMessage.message.usage, "output_tokens") || 0;
21281
+ if (messageId) {
21282
+ state.usageByMessageId.delete(messageId);
21283
+ state.finalOutputUsageMessageIds.delete(messageId);
21284
+ for (const [
21285
+ parent,
21286
+ activeMessageId
21287
+ ] of state.activePartialMessageIdByParentKey) {
21288
+ if (activeMessageId === messageId) {
21289
+ state.activePartialMessageIdByParentKey.delete(parent);
21290
+ }
21291
+ }
21006
21292
  }
21007
21293
  state.currentMessages.length = 0;
21008
21294
  }
@@ -21093,6 +21379,10 @@ async function ensureActiveLlmSpanForParentToolUse(rootSpan, activeLlmSpansByPar
21093
21379
  );
21094
21380
  llmParentSpan = await subAgentSpan.export();
21095
21381
  }
21382
+ const racedLlmSpan = activeLlmSpansByParentToolUse.get(parentKey);
21383
+ if (racedLlmSpan) {
21384
+ return racedLlmSpan;
21385
+ }
21096
21386
  const llmSpan = startSpan(
21097
21387
  withSpanInstrumentationName(
21098
21388
  {
@@ -21212,7 +21502,49 @@ async function maybeHandleTaskLifecycleMessage(state, message) {
21212
21502
  }
21213
21503
  return true;
21214
21504
  }
21505
+ function handlePartialUsageMessage(state, message) {
21506
+ if (message.type !== "stream_event") {
21507
+ return false;
21508
+ }
21509
+ const event = message.event;
21510
+ if (!event || typeof event !== "object") {
21511
+ return true;
21512
+ }
21513
+ const parentKey = llmParentKey(message.parent_tool_use_id ?? null);
21514
+ if (event.type === "message_start") {
21515
+ const messageId2 = event.message?.id;
21516
+ const usage = copyUsage(event.message?.usage);
21517
+ if (messageId2) {
21518
+ state.activePartialMessageIdByParentKey.set(parentKey, messageId2);
21519
+ if (usage) {
21520
+ state.usageByMessageId.set(messageId2, usage);
21521
+ }
21522
+ }
21523
+ return true;
21524
+ }
21525
+ const messageId = state.activePartialMessageIdByParentKey.get(parentKey);
21526
+ if (!messageId) {
21527
+ return true;
21528
+ }
21529
+ if (event.type === "message_delta") {
21530
+ const update = copyUsage(event.usage);
21531
+ if (update) {
21532
+ const usage = state.usageByMessageId.get(messageId) ?? {};
21533
+ Object.assign(usage, update);
21534
+ state.usageByMessageId.set(messageId, usage);
21535
+ if (update.output_tokens !== void 0) {
21536
+ state.finalOutputUsageMessageIds.add(messageId);
21537
+ }
21538
+ }
21539
+ } else if (event.type === "message_stop") {
21540
+ state.activePartialMessageIdByParentKey.delete(parentKey);
21541
+ }
21542
+ return true;
21543
+ }
21215
21544
  async function handleStreamMessage(state, message) {
21545
+ if (handlePartialUsageMessage(state, message)) {
21546
+ return;
21547
+ }
21216
21548
  maybeTrackToolUseContext(state, message);
21217
21549
  if (await maybeHandleTaskLifecycleMessage(state, message)) {
21218
21550
  return;
@@ -21259,36 +21591,9 @@ async function handleStreamMessage(state, message) {
21259
21591
  );
21260
21592
  state.currentMessages.push(message);
21261
21593
  }
21262
- if (message.type !== "result" || !message.usage) {
21594
+ if (message.type !== "result") {
21263
21595
  return;
21264
21596
  }
21265
- const finalUsageMetrics = extractUsageFromMessage(message);
21266
- if (state.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
21267
- const lastMessage = state.currentMessages[state.currentMessages.length - 1];
21268
- if (lastMessage?.message?.usage) {
21269
- const adjustedTokens = finalUsageMetrics.completion_tokens - state.accumulatedOutputTokens;
21270
- if (adjustedTokens >= 0) {
21271
- lastMessage.message.usage.output_tokens = adjustedTokens;
21272
- }
21273
- const resultUsage = message.usage;
21274
- if (resultUsage && typeof resultUsage === "object") {
21275
- const cacheReadTokens = getNumberProperty(
21276
- resultUsage,
21277
- "cache_read_input_tokens"
21278
- );
21279
- if (cacheReadTokens !== void 0) {
21280
- lastMessage.message.usage.cache_read_input_tokens = cacheReadTokens;
21281
- }
21282
- const cacheCreationTokens = getNumberProperty(
21283
- resultUsage,
21284
- "cache_creation_input_tokens"
21285
- );
21286
- if (cacheCreationTokens !== void 0) {
21287
- lastMessage.message.usage.cache_creation_input_tokens = cacheCreationTokens;
21288
- }
21289
- }
21290
- }
21291
- }
21292
21597
  const metadata = {};
21293
21598
  if (message.num_turns !== void 0) {
21294
21599
  metadata.num_turns = message.num_turns;
@@ -21296,8 +21601,12 @@ async function handleStreamMessage(state, message) {
21296
21601
  if (message.session_id !== void 0) {
21297
21602
  metadata.session_id = message.session_id;
21298
21603
  }
21299
- if (Object.keys(metadata).length > 0) {
21300
- state.span.log({ metadata });
21604
+ const metrics = state.options.includePartialMessages ? {} : extractUsage(copyUsage(message.usage), true);
21605
+ if (Object.keys(metadata).length > 0 || Object.keys(metrics).length > 0) {
21606
+ state.span.log({
21607
+ ...Object.keys(metadata).length > 0 ? { metadata } : {},
21608
+ ...Object.keys(metrics).length > 0 ? { metrics } : {}
21609
+ });
21301
21610
  }
21302
21611
  }
21303
21612
  async function finalizeQuerySpan(state) {
@@ -21321,6 +21630,9 @@ async function finalizeQuerySpan(state) {
21321
21630
  llmSpan.end();
21322
21631
  }
21323
21632
  state.activeLlmSpansByParentToolUse.clear();
21633
+ state.activePartialMessageIdByParentKey.clear();
21634
+ state.finalOutputUsageMessageIds.clear();
21635
+ state.usageByMessageId.clear();
21324
21636
  for (const toolSpan of state.activeToolSpans.values()) {
21325
21637
  toolSpan.end();
21326
21638
  }
@@ -21445,6 +21757,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21445
21757
  const optionsWithHooks = injectTracingHooks(
21446
21758
  options,
21447
21759
  resolveToolUseParentSpan,
21760
+ taskIdToToolUseId,
21761
+ toolUseToParent,
21448
21762
  activeToolSpans,
21449
21763
  localToolHookNames,
21450
21764
  skipLocalToolHooks,
@@ -21455,8 +21769,8 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21455
21769
  params.options = optionsWithHooks;
21456
21770
  event.arguments[0] = params;
21457
21771
  spans.set(event, {
21458
- accumulatedOutputTokens: 0,
21459
21772
  activeLlmSpansByParentToolUse,
21773
+ activePartialMessageIdByParentKey: /* @__PURE__ */ new Map(),
21460
21774
  activeToolSpans,
21461
21775
  conversationHistoryByParentKey,
21462
21776
  capturedPromptMessages,
@@ -21464,6 +21778,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21464
21778
  currentMessageStartTime: startTime,
21465
21779
  currentMessages: [],
21466
21780
  endedSubAgentSpans,
21781
+ finalOutputUsageMessageIds: /* @__PURE__ */ new Set(),
21467
21782
  finalResults: [],
21468
21783
  options: optionsWithHooks,
21469
21784
  originalPrompt,
@@ -21479,6 +21794,7 @@ var ClaudeAgentSDKPlugin = class extends BasePlugin {
21479
21794
  latestLlmParentBySubAgentToolUse,
21480
21795
  latestRootLlmParentRef,
21481
21796
  toolUseToParent,
21797
+ usageByMessageId: /* @__PURE__ */ new Map(),
21482
21798
  localToolContext
21483
21799
  });
21484
21800
  },
@@ -23953,9 +24269,9 @@ function extractEmbedPromptTokenCount(response) {
23953
24269
  let sawAny = false;
23954
24270
  for (const embedding of embeddings) {
23955
24271
  const embeddingStats = tryToDict(tryToDict(embedding)?.statistics);
23956
- const tokenCount = embeddingStats?.tokenCount;
23957
- if (typeof tokenCount === "number" && Number.isFinite(tokenCount)) {
23958
- total += tokenCount;
24272
+ const tokenCount2 = embeddingStats?.tokenCount;
24273
+ if (typeof tokenCount2 === "number" && Number.isFinite(tokenCount2)) {
24274
+ total += tokenCount2;
23959
24275
  sawAny = true;
23960
24276
  }
23961
24277
  }
@@ -30850,7 +31166,7 @@ function getStringProperty2(obj, key) {
30850
31166
  return typeof value === "string" ? value : void 0;
30851
31167
  }
30852
31168
  function extractMetricsFromUsage(usage) {
30853
- const metrics = {
31169
+ const rawMetrics = {
30854
31170
  prompt_tokens: usage.inputTokens,
30855
31171
  completion_tokens: usage.outputTokens,
30856
31172
  ...extractAnthropicCacheTokens(
@@ -30859,10 +31175,10 @@ function extractMetricsFromUsage(usage) {
30859
31175
  )
30860
31176
  };
30861
31177
  if (usage.reasoningTokens !== void 0) {
30862
- metrics.completion_reasoning_tokens = usage.reasoningTokens;
30863
- metrics.reasoning_tokens = usage.reasoningTokens;
31178
+ rawMetrics.completion_reasoning_tokens = usage.reasoningTokens;
31179
+ rawMetrics.reasoning_tokens = usage.reasoningTokens;
30864
31180
  }
30865
- Object.assign(metrics, finalizeAnthropicTokens(metrics));
31181
+ const metrics = finalizeAnthropicTokens(rawMetrics);
30866
31182
  const metadata = {
30867
31183
  model: usage.model
30868
31184
  };
@@ -31801,17 +32117,20 @@ var FlueObserveBridge = class {
31801
32117
  return;
31802
32118
  }
31803
32119
  const metadata = {
32120
+ ...event.runId ? this.runsById.get(event.runId)?.metadata : {},
31804
32121
  ...extractEventMetadata(event),
31805
32122
  "flue.operation": event.operationKind,
31806
32123
  provider: "flue"
31807
32124
  };
31808
32125
  const parent = this.parentSpanForEvent(event);
31809
- const span = startFlueSpan(parent, {
32126
+ const args = {
31810
32127
  name: `flue.${event.operationKind}`,
31811
32128
  spanAttributes: { type: "task" /* TASK */ },
31812
32129
  startTime: eventTime(event.timestamp),
31813
32130
  event: { metadata }
31814
- });
32131
+ };
32132
+ const runSpan = event.runId ? this.runsById.get(event.runId)?.span : void 0;
32133
+ const span = event.operationKind === "prompt" && (!parent || parent === runSpan) ? startFlueRootSpan(args) : startFlueSpan(parent, args);
31815
32134
  this.operationsById.set(event.operationId, { metadata, span });
31816
32135
  }
31817
32136
  handleOperation(event) {
@@ -31826,6 +32145,11 @@ var FlueObserveBridge = class {
31826
32145
  ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
31827
32146
  ...event.usage ? { "flue.usage": event.usage } : {}
31828
32147
  };
32148
+ const input = flueOperationInput(event);
32149
+ if (!state.loggedInput && input !== void 0) {
32150
+ safeLog3(state.span, { input });
32151
+ state.loggedInput = true;
32152
+ }
31829
32153
  this.finishPendingChildrenForOperation(event, output);
31830
32154
  safeLog3(state.span, {
31831
32155
  ...event.isError ? { error: toLoggedError(event.errorInfo ?? event.error) } : {},
@@ -31842,6 +32166,8 @@ var FlueObserveBridge = class {
31842
32166
  return;
31843
32167
  }
31844
32168
  const input = flueTurnRequestInput(event);
32169
+ const operation = event.operationId ? this.operationsById.get(event.operationId) : void 0;
32170
+ const turnInput = prepareFlueTurnInput(event, input, operation);
31845
32171
  const model = flueTurnRequestModel(event);
31846
32172
  const provider = flueTurnRequestProvider(event);
31847
32173
  const api = flueTurnRequestApi(event);
@@ -31854,8 +32180,7 @@ var FlueObserveBridge = class {
31854
32180
  ...provider ? { "flue.provider": provider } : {},
31855
32181
  ...event.purpose ? { "flue.turn_purpose": event.purpose } : {},
31856
32182
  ...reasoning ? { reasoning } : {},
31857
- ...input?.systemPrompt ? { "flue.system_prompt": input.systemPrompt } : {},
31858
- ...input?.tools ? { tools: input.tools } : {}
32183
+ ...turnInput.metadata
31859
32184
  };
31860
32185
  const parent = this.parentSpanForTurn(event);
31861
32186
  const span = startFlueSpan(parent, {
@@ -31863,11 +32188,14 @@ var FlueObserveBridge = class {
31863
32188
  spanAttributes: { type: "llm" /* LLM */ },
31864
32189
  startTime: eventTime(event.timestamp),
31865
32190
  event: {
31866
- input: input?.messages,
32191
+ input: turnInput.messages,
31867
32192
  metadata
31868
32193
  }
31869
32194
  });
31870
- this.logOperationInput(event.operationId, input?.messages ?? input);
32195
+ this.logOperationInput(
32196
+ event.operationId,
32197
+ latestUserMessageInput(input?.messages)
32198
+ );
31871
32199
  this.turnsByKey.set(key, { metadata, span });
31872
32200
  }
31873
32201
  handleTurn(event) {
@@ -32114,16 +32442,20 @@ var FlueObserveBridge = class {
32114
32442
  }
32115
32443
  startSyntheticOperation(event) {
32116
32444
  const metadata = {
32445
+ ...event.runId ? this.runsById.get(event.runId)?.metadata : {},
32117
32446
  ...extractEventMetadata(event),
32118
32447
  "flue.operation": event.operationKind,
32119
32448
  provider: "flue"
32120
32449
  };
32121
- const span = startFlueSpan(this.parentSpanForEvent(event), {
32450
+ const args = {
32122
32451
  name: `flue.${event.operationKind}`,
32123
32452
  spanAttributes: { type: "task" /* TASK */ },
32124
32453
  startTime: eventTime(event.timestamp),
32125
32454
  event: { metadata }
32126
- });
32455
+ };
32456
+ const parent = this.parentSpanForEvent(event);
32457
+ const runSpan = event.runId ? this.runsById.get(event.runId)?.span : void 0;
32458
+ const span = event.operationKind === "prompt" && (!parent || parent === runSpan) ? startFlueRootSpan(args) : startFlueSpan(parent, args);
32127
32459
  return { metadata, span };
32128
32460
  }
32129
32461
  startSyntheticTurn(event) {
@@ -32302,6 +32634,71 @@ function flueRunInput(event) {
32302
32634
  function flueTurnRequestInput(event) {
32303
32635
  return event.request?.input ?? event.input;
32304
32636
  }
32637
+ function prepareFlueTurnInput(event, input, operation) {
32638
+ const messages = input?.messages;
32639
+ const tracksUserTurn = event.purpose === "agent" && operation?.metadata["flue.operation"] === "prompt" && Array.isArray(messages);
32640
+ if (!tracksUserTurn) {
32641
+ return {
32642
+ messages,
32643
+ metadata: {
32644
+ ...input?.systemPrompt ? { "flue.system_prompt": input.systemPrompt } : {},
32645
+ ...input?.tools ? { tools: input.tools } : {}
32646
+ }
32647
+ };
32648
+ }
32649
+ const previous = operation.turnInputState;
32650
+ const previousMessageCount = previous?.messageCount ?? 0;
32651
+ const boundaryFingerprint = messages.length > 0 ? fingerprintJsonValue(messages[messages.length - 1]) : void 0;
32652
+ const continuesPreviousInput = previous !== void 0 && messages.length >= previousMessageCount && (previousMessageCount === 0 || previous.boundaryFingerprint !== void 0 && previous.boundaryFingerprint === fingerprintJsonValue(messages[previousMessageCount - 1]));
32653
+ const inputMode = previous === void 0 ? "full" : continuesPreviousInput ? "delta" : "reset";
32654
+ const systemPromptFingerprint = fingerprintJsonValue(input?.systemPrompt);
32655
+ const toolsFingerprint = fingerprintJsonValue(input?.tools);
32656
+ operation.turnInputState = {
32657
+ ...boundaryFingerprint !== void 0 ? { boundaryFingerprint } : {},
32658
+ messageCount: messages.length,
32659
+ ...systemPromptFingerprint !== void 0 ? { systemPromptFingerprint } : {},
32660
+ ...toolsFingerprint !== void 0 ? { toolsFingerprint } : {}
32661
+ };
32662
+ return {
32663
+ messages: continuesPreviousInput ? messages.slice(previousMessageCount) : messages,
32664
+ metadata: {
32665
+ "flue.input_mode": inputMode,
32666
+ ...continuesPreviousInput ? { "flue.input_message_offset": previousMessageCount } : {},
32667
+ ...input?.systemPrompt && (!continuesPreviousInput || systemPromptFingerprint !== previous?.systemPromptFingerprint) ? { "flue.system_prompt": input.systemPrompt } : {},
32668
+ ...input?.tools && (!continuesPreviousInput || toolsFingerprint !== previous?.toolsFingerprint) ? { tools: input.tools } : {}
32669
+ }
32670
+ };
32671
+ }
32672
+ function fingerprintJsonValue(value) {
32673
+ try {
32674
+ const serialized = JSON.stringify(value);
32675
+ if (serialized === void 0) {
32676
+ return void 0;
32677
+ }
32678
+ let hash = 2166136261;
32679
+ for (let i = 0; i < serialized.length; i++) {
32680
+ hash = Math.imul(hash ^ serialized.charCodeAt(i), 16777619);
32681
+ }
32682
+ return `${serialized.length}:${hash >>> 0}`;
32683
+ } catch {
32684
+ return void 0;
32685
+ }
32686
+ }
32687
+ function latestUserMessageInput(messages) {
32688
+ if (!messages) {
32689
+ return void 0;
32690
+ }
32691
+ for (let i = messages.length - 1; i >= 0; i--) {
32692
+ const message = messages[i];
32693
+ if (isObjectLike(message) && Reflect.get(message, "role") === "user") {
32694
+ return [message];
32695
+ }
32696
+ }
32697
+ return void 0;
32698
+ }
32699
+ function flueOperationInput(event) {
32700
+ return typeof event.agentInput?.text === "string" ? [{ content: event.agentInput.text, role: "user" }] : void 0;
32701
+ }
32305
32702
  function flueTurnRequestModel(event) {
32306
32703
  return event.request?.requestedModel ?? event.request?.model ?? event.model;
32307
32704
  }
@@ -32459,6 +32856,21 @@ function startFlueSpan(parent, args) {
32459
32856
  withSpanInstrumentationName(args, INSTRUMENTATION_NAMES.FLUE)
32460
32857
  );
32461
32858
  }
32859
+ function startFlueRootSpan(args) {
32860
+ const state = _internalGetGlobalState();
32861
+ const spanId = state.idGenerator.getSpanId();
32862
+ const rootSpanId = state.idGenerator.shareRootSpanId() ? spanId : state.idGenerator.getTraceId();
32863
+ return withCurrent(
32864
+ NOOP_SPAN,
32865
+ () => startSpan({
32866
+ ...withSpanInstrumentationName(args, INSTRUMENTATION_NAMES.FLUE),
32867
+ parentSpanIds: { parentSpanIds: [], rootSpanId },
32868
+ spanId,
32869
+ state
32870
+ }),
32871
+ state
32872
+ );
32873
+ }
32462
32874
  function runWithCurrentSpanStore(span, next) {
32463
32875
  const state = _internalGetGlobalState();
32464
32876
  const contextManager = state?.contextManager;
@@ -34358,8 +34770,7 @@ function startAgentStream(event, activeChildParents) {
34358
34770
  ...extractAgentMetadata2(agent),
34359
34771
  ...extractModelMetadata3(model),
34360
34772
  "strands.operation": "Agent.stream",
34361
- provider: extractProvider(model),
34362
- ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
34773
+ provider: extractProvider(model)
34363
34774
  };
34364
34775
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
34365
34776
  const attachmentCache = createStrandsAttachmentCache();
@@ -34409,8 +34820,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
34409
34820
  const metadata = {
34410
34821
  "strands.operation": operation,
34411
34822
  provider: "strands",
34412
- ...orchestrator?.id ? { "strands.orchestrator.id": orchestrator.id } : {},
34413
- ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
34823
+ ...orchestrator?.id ? { "strands.orchestrator.id": orchestrator.id } : {}
34414
34824
  };
34415
34825
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
34416
34826
  const input = processStrandsInputAttachments(event.arguments[0]);
@@ -35196,6 +35606,320 @@ function logInstrumentationError5(context, error) {
35196
35606
  debugLogger.debug(`${context}:`, error);
35197
35607
  }
35198
35608
 
35609
+ // src/instrumentation/plugins/voyageai-channels.ts
35610
+ var voyageAIChannels = defineChannels(
35611
+ "voyageai",
35612
+ {
35613
+ embed: channel({
35614
+ channelName: "embed",
35615
+ kind: "async"
35616
+ }),
35617
+ multimodalEmbed: channel({
35618
+ channelName: "multimodalEmbed",
35619
+ kind: "async"
35620
+ }),
35621
+ rerank: channel({
35622
+ channelName: "rerank",
35623
+ kind: "async"
35624
+ }),
35625
+ contextualizedEmbed: channel({
35626
+ channelName: "contextualizedEmbed",
35627
+ kind: "async"
35628
+ })
35629
+ },
35630
+ { instrumentationName: INSTRUMENTATION_NAMES.VOYAGEAI }
35631
+ );
35632
+
35633
+ // src/instrumentation/plugins/voyageai-plugin.ts
35634
+ var RERANK_METADATA_ALLOWLIST = /* @__PURE__ */ new Set([
35635
+ "model",
35636
+ "returnDocuments",
35637
+ "topK",
35638
+ "truncation"
35639
+ ]);
35640
+ var VoyageAIPlugin = class extends BasePlugin {
35641
+ onEnable() {
35642
+ this.unsubscribers.push(
35643
+ interceptVoyageAICall(
35644
+ voyageAIChannels.embed,
35645
+ "voyageai.embed",
35646
+ extractTextEmbeddingInput,
35647
+ summarizeEmbeddingOutput,
35648
+ extractEmbeddingUsageMetrics
35649
+ ),
35650
+ interceptVoyageAICall(
35651
+ voyageAIChannels.multimodalEmbed,
35652
+ "voyageai.multimodalEmbed",
35653
+ extractMultimodalEmbeddingInput,
35654
+ summarizeEmbeddingOutput,
35655
+ extractEmbeddingUsageMetrics
35656
+ ),
35657
+ interceptVoyageAICall(
35658
+ voyageAIChannels.rerank,
35659
+ "voyageai.rerank",
35660
+ extractRerankInput,
35661
+ summarizeRerankOutput
35662
+ ),
35663
+ interceptVoyageAICall(
35664
+ voyageAIChannels.contextualizedEmbed,
35665
+ "voyageai.contextualizedEmbed",
35666
+ extractContextualizedEmbeddingInput,
35667
+ summarizeContextualizedEmbeddingOutput,
35668
+ extractEmbeddingUsageMetrics
35669
+ )
35670
+ );
35671
+ }
35672
+ onDisable() {
35673
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
35674
+ }
35675
+ };
35676
+ function interceptVoyageAICall(channel2, name, extractInput3, extractOutput2, extractMetrics2 = extractUsageMetrics3) {
35677
+ return channel2.intercept((target, thisArg, args) => {
35678
+ const invokeTarget = () => Reflect.apply(target, thisArg, args);
35679
+ if (isAutoInstrumentationSuppressed()) {
35680
+ return invokeTarget();
35681
+ }
35682
+ let span;
35683
+ try {
35684
+ const { input, metadata } = extractInput3(args);
35685
+ span = startSpan(
35686
+ withSpanInstrumentationName(
35687
+ {
35688
+ event: { input, metadata },
35689
+ name,
35690
+ spanAttributes: { type: "llm" /* LLM */ }
35691
+ },
35692
+ INSTRUMENTATION_NAMES.VOYAGEAI
35693
+ )
35694
+ );
35695
+ } catch (error) {
35696
+ debugLogger.error(`Error starting span for ${name}:`, error);
35697
+ return invokeTarget();
35698
+ }
35699
+ let result;
35700
+ try {
35701
+ result = withCurrent(
35702
+ span,
35703
+ () => runWithAutoInstrumentationSuppressed(invokeTarget)
35704
+ );
35705
+ } catch (error) {
35706
+ finishVoyageAISpan(span, name, () => span.log({ error }));
35707
+ throw error;
35708
+ }
35709
+ void Promise.resolve(result).then(
35710
+ (value) => finishVoyageAISpan(span, name, () => {
35711
+ const metadata = extractResponseMetadata3(value);
35712
+ span.log({
35713
+ output: extractOutput2(value),
35714
+ ...metadata ? { metadata } : {},
35715
+ metrics: extractMetrics2(value)
35716
+ });
35717
+ }),
35718
+ (error) => finishVoyageAISpan(span, name, () => span.log({ error }))
35719
+ );
35720
+ return result;
35721
+ });
35722
+ }
35723
+ function finishVoyageAISpan(span, name, log3) {
35724
+ try {
35725
+ log3();
35726
+ } catch (error) {
35727
+ debugLogger.error(`Error logging span for ${name}:`, error);
35728
+ }
35729
+ try {
35730
+ span.end();
35731
+ } catch (error) {
35732
+ debugLogger.error(`Error ending span for ${name}:`, error);
35733
+ }
35734
+ }
35735
+ function getRequestArg2(args) {
35736
+ if (Array.isArray(args)) {
35737
+ return isObject(args[0]) ? args[0] : void 0;
35738
+ }
35739
+ if (!isObject(args)) {
35740
+ return void 0;
35741
+ }
35742
+ const firstArg = Reflect.get(args, "0");
35743
+ return isObject(firstArg) ? firstArg : void 0;
35744
+ }
35745
+ function pickMetadata(request, allowlist) {
35746
+ const metadata = {};
35747
+ if (request) {
35748
+ for (const key of allowlist) {
35749
+ if (!Object.hasOwn(request, key)) {
35750
+ continue;
35751
+ }
35752
+ const value = request[key];
35753
+ if (value !== void 0) {
35754
+ metadata[key] = value;
35755
+ }
35756
+ }
35757
+ }
35758
+ return {
35759
+ ...metadata,
35760
+ provider: "voyage"
35761
+ };
35762
+ }
35763
+ function buildEmbeddingInput(inputs, request) {
35764
+ const outputDimensions = request?.outputDimension;
35765
+ return {
35766
+ inputs,
35767
+ ...typeof outputDimensions === "number" && Number.isFinite(outputDimensions) ? { output_dimensions: outputDimensions } : {}
35768
+ };
35769
+ }
35770
+ function embeddingMetadata(request) {
35771
+ return {
35772
+ ...typeof request?.model === "string" ? { model: request.model } : {},
35773
+ provider: "voyage"
35774
+ };
35775
+ }
35776
+ function extractTextEmbeddingInput(args) {
35777
+ const request = getRequestArg2(args);
35778
+ const rawInput = request?.input;
35779
+ const values = Array.isArray(rawInput) ? rawInput : [rawInput];
35780
+ return {
35781
+ input: buildEmbeddingInput(
35782
+ values.flatMap(
35783
+ (value) => typeof value === "string" ? [{ content: value }] : []
35784
+ ),
35785
+ request
35786
+ ),
35787
+ metadata: embeddingMetadata(request)
35788
+ };
35789
+ }
35790
+ function extractContextualizedEmbeddingInput(args) {
35791
+ const request = getRequestArg2(args);
35792
+ const rawInputs = request?.inputs;
35793
+ const values = Array.isArray(rawInputs) ? rawInputs.flatMap((value) => Array.isArray(value) ? value : [value]) : [];
35794
+ return {
35795
+ input: buildEmbeddingInput(
35796
+ values.flatMap(
35797
+ (value) => typeof value === "string" ? [{ content: value }] : []
35798
+ ),
35799
+ request
35800
+ ),
35801
+ metadata: embeddingMetadata(request)
35802
+ };
35803
+ }
35804
+ function extractMultimodalEmbeddingInput(args) {
35805
+ const request = getRequestArg2(args);
35806
+ const rawInputs = request?.inputs;
35807
+ const inputs = Array.isArray(rawInputs) ? rawInputs.map((rawInput) => {
35808
+ const rawContent = isObject(rawInput) ? rawInput.content : void 0;
35809
+ return {
35810
+ content: Array.isArray(rawContent) ? rawContent.flatMap(normalizeMultimodalContentPart) : []
35811
+ };
35812
+ }) : [];
35813
+ const input = buildEmbeddingInput(inputs, request);
35814
+ const processedInput = processInputAttachments(input);
35815
+ return {
35816
+ input: hasInlineEmbeddingMedia(processedInput) ? input : processedInput,
35817
+ metadata: embeddingMetadata(request)
35818
+ };
35819
+ }
35820
+ function normalizeMultimodalContentPart(part) {
35821
+ if (!isObject(part) || typeof part.type !== "string") {
35822
+ return [];
35823
+ }
35824
+ if (part.type === "text") {
35825
+ return typeof part.text === "string" ? [{ type: "text", text: part.text }] : [];
35826
+ }
35827
+ const camelCaseField = {
35828
+ image_base64: "imageBase64",
35829
+ image_url: "imageUrl",
35830
+ video_base64: "videoBase64",
35831
+ video_url: "videoUrl"
35832
+ };
35833
+ const field = camelCaseField[part.type];
35834
+ if (!field) {
35835
+ return [];
35836
+ }
35837
+ const data = typeof part[field] === "string" ? part[field] : part[part.type];
35838
+ if (typeof data !== "string") {
35839
+ return [];
35840
+ }
35841
+ return part.type.startsWith("image_") ? [{ type: "image_url", image_url: { url: data } }] : [{ type: "file", file: { file_data: data } }];
35842
+ }
35843
+ function hasInlineEmbeddingMedia(input) {
35844
+ return input.inputs.some(
35845
+ ({ content }) => Array.isArray(content) ? content.some((part) => {
35846
+ const value = part.type === "image_url" ? part.image_url.url : part.type === "file" ? part.file.file_data : void 0;
35847
+ return typeof value === "string" && value.startsWith("data:");
35848
+ }) : false
35849
+ );
35850
+ }
35851
+ function extractRerankInput(args) {
35852
+ const request = getRequestArg2(args);
35853
+ const documents = request?.documents;
35854
+ return {
35855
+ input: {
35856
+ documents,
35857
+ query: request?.query
35858
+ },
35859
+ metadata: {
35860
+ ...pickMetadata(request, RERANK_METADATA_ALLOWLIST),
35861
+ ...Array.isArray(documents) ? { document_count: documents.length } : {}
35862
+ }
35863
+ };
35864
+ }
35865
+ function extractResponseMetadata3(result) {
35866
+ if (!isObject(result)) {
35867
+ return void 0;
35868
+ }
35869
+ const rawResponse = isObject(result.rawResponse) ? result.rawResponse : void 0;
35870
+ const model = typeof result.model === "string" ? result.model : typeof rawResponse?.model === "string" ? rawResponse.model : void 0;
35871
+ return model ? { model } : void 0;
35872
+ }
35873
+ function summarizeEmbeddingOutput(result) {
35874
+ return {
35875
+ count: isObject(result) && Array.isArray(result.data) ? result.data.length : 0
35876
+ };
35877
+ }
35878
+ function summarizeRerankOutput(result) {
35879
+ if (!isObject(result) || !Array.isArray(result.data)) {
35880
+ return void 0;
35881
+ }
35882
+ return result.data.slice(0, 100).map((item) => ({
35883
+ index: isObject(item) ? item.index : void 0,
35884
+ relevance_score: isObject(item) ? (typeof item.relevanceScore === "number" ? item.relevanceScore : item.relevance_score) ?? null : null
35885
+ }));
35886
+ }
35887
+ function summarizeContextualizedEmbeddingOutput(result) {
35888
+ if (!isObject(result)) {
35889
+ return { count: 0 };
35890
+ }
35891
+ if (Array.isArray(result.results)) {
35892
+ return {
35893
+ count: result.results.reduce(
35894
+ (count, item) => count + (isObject(item) && Array.isArray(item.embeddings) ? item.embeddings.length : 0),
35895
+ 0
35896
+ )
35897
+ };
35898
+ }
35899
+ if (!Array.isArray(result.data)) {
35900
+ return { count: 0 };
35901
+ }
35902
+ return {
35903
+ count: result.data.reduce(
35904
+ (count, item) => count + (isObject(item) && Array.isArray(item.data) ? item.data.length : 0),
35905
+ 0
35906
+ )
35907
+ };
35908
+ }
35909
+ function extractEmbeddingUsageMetrics(result) {
35910
+ const metrics = extractUsageMetrics3(result);
35911
+ return typeof metrics.tokens === "number" ? { prompt_tokens: metrics.tokens, tokens: metrics.tokens } : {};
35912
+ }
35913
+ function extractUsageMetrics3(result) {
35914
+ if (!isObject(result)) {
35915
+ return {};
35916
+ }
35917
+ const rawResponse = isObject(result.rawResponse) ? result.rawResponse : void 0;
35918
+ const usage = isObject(result.usage) ? result.usage : isObject(rawResponse?.usage) ? rawResponse.usage : void 0;
35919
+ const tokens = typeof result.totalTokens === "number" ? result.totalTokens : usage?.totalTokens ?? usage?.total_tokens;
35920
+ return typeof tokens === "number" && Number.isFinite(tokens) && tokens >= 0 ? { tokens } : {};
35921
+ }
35922
+
35199
35923
  // src/instrumentation/plugins/cloudflare-ai-chat-channels.ts
35200
35924
  var cloudflareAIChatChannels = defineChannels(
35201
35925
  "@cloudflare/ai-chat",
@@ -35774,6 +36498,7 @@ var BraintrustPlugin = class extends BasePlugin {
35774
36498
  langSmithPlugin = null;
35775
36499
  piCodingAgentPlugin = null;
35776
36500
  strandsAgentSDKPlugin = null;
36501
+ voyageAIPlugin = null;
35777
36502
  cloudflareAIChatPlugin = null;
35778
36503
  cloudflareAgentsPlugin = null;
35779
36504
  constructor(config = {}) {
@@ -35848,6 +36573,10 @@ var BraintrustPlugin = class extends BasePlugin {
35848
36573
  this.coherePlugin = new CoherePlugin();
35849
36574
  this.coherePlugin.enable();
35850
36575
  }
36576
+ if (integrations.voyageai !== false) {
36577
+ this.voyageAIPlugin = new VoyageAIPlugin();
36578
+ this.voyageAIPlugin.enable();
36579
+ }
35851
36580
  if (integrations.groq !== false) {
35852
36581
  this.groqPlugin = new GroqPlugin();
35853
36582
  this.groqPlugin.enable();
@@ -35964,6 +36693,10 @@ var BraintrustPlugin = class extends BasePlugin {
35964
36693
  this.coherePlugin.disable();
35965
36694
  this.coherePlugin = null;
35966
36695
  }
36696
+ if (this.voyageAIPlugin) {
36697
+ this.voyageAIPlugin.disable();
36698
+ this.voyageAIPlugin = null;
36699
+ }
35967
36700
  if (this.groqPlugin) {
35968
36701
  this.groqPlugin.disable();
35969
36702
  this.groqPlugin = null;
@@ -36083,7 +36816,10 @@ var envIntegrationAliases = {
36083
36816
  "langchain-js": "langchain",
36084
36817
  "@langchain": "langchain",
36085
36818
  langgraph: "langgraph",
36086
- langsmith: "langsmith"
36819
+ langsmith: "langsmith",
36820
+ voyage: "voyageai",
36821
+ "voyage-ai": "voyageai",
36822
+ voyageai: "voyageai"
36087
36823
  };
36088
36824
  function getDefaultInstrumentationIntegrations() {
36089
36825
  return {
@@ -36118,6 +36854,7 @@ function getDefaultInstrumentationIntegrations() {
36118
36854
  langchain: true,
36119
36855
  langgraph: true,
36120
36856
  langsmith: true,
36857
+ voyageai: true,
36121
36858
  piCodingAgent: true,
36122
36859
  strandsAgentSDK: true,
36123
36860
  cloudflareAgents: true
@@ -36283,6 +37020,8 @@ __export(exports_exports, {
36283
37020
  BRAINTRUST_PARENT_KEY: () => BRAINTRUST_PARENT_KEY,
36284
37021
  BaseAttachment: () => BaseAttachment,
36285
37022
  BaseExperiment: () => BaseExperiment,
37023
+ BatchScorer: () => BatchScorer,
37024
+ BatchTask: () => BatchTask,
36286
37025
  BraintrustLangChainCallbackHandler: () => BraintrustLangChainCallbackHandler,
36287
37026
  BraintrustMiddleware: () => BraintrustMiddleware,
36288
37027
  BraintrustObservabilityExporter: () => BraintrustObservabilityExporter,
@@ -36296,6 +37035,8 @@ __export(exports_exports, {
36296
37035
  DEFAULT_MAX_REQUEST_SIZE: () => DEFAULT_MAX_REQUEST_SIZE,
36297
37036
  Dataset: () => Dataset2,
36298
37037
  DatasetPipeline: () => DatasetPipeline,
37038
+ DurableEvalMemoryStore: () => DurableEvalMemoryStore,
37039
+ DurableEvalRedisStore: () => DurableEvalRedisStore,
36299
37040
  ERR_PERMALINK: () => ERR_PERMALINK,
36300
37041
  Eval: () => Eval,
36301
37042
  EvalResultWithSummary: () => EvalResultWithSummary,
@@ -36353,6 +37094,7 @@ __export(exports_exports, {
36353
37094
  currentSpan: () => currentSpan,
36354
37095
  deepCopyEvent: () => deepCopyEvent,
36355
37096
  defaultErrorScoreHandler: () => defaultErrorScoreHandler,
37097
+ defineDurableEval: () => defineDurableEval,
36356
37098
  deserializePlainStringAsJSON: () => deserializePlainStringAsJSON,
36357
37099
  devNullWritableStream: () => devNullWritableStream,
36358
37100
  evaluatorDefinitionSchema: () => evaluatorDefinitionSchema,
@@ -36448,7 +37190,8 @@ __export(exports_exports, {
36448
37190
  wrapPiCodingAgentSDK: () => wrapPiCodingAgentSDK,
36449
37191
  wrapStrandsAgentSDK: () => wrapStrandsAgentSDK,
36450
37192
  wrapTraced: () => wrapTraced,
36451
- wrapVitest: () => wrapVitest
37193
+ wrapVitest: () => wrapVitest,
37194
+ wrapVoyageAI: () => wrapVoyageAI
36452
37195
  });
36453
37196
 
36454
37197
  // src/sandbox.ts
@@ -37709,7 +38452,7 @@ function normalizeUsageMetrics(usage, provider, providerMetadata) {
37709
38452
  cacheCreationTokens
37710
38453
  );
37711
38454
  Object.assign(metrics, cacheTokens);
37712
- Object.assign(metrics, finalizeAnthropicTokens(metrics));
38455
+ return toNumericMetrics(finalizeAnthropicTokens(metrics));
37713
38456
  }
37714
38457
  }
37715
38458
  return metrics;
@@ -42042,6 +42785,84 @@ function wrapRerank3(rerank) {
42042
42785
  });
42043
42786
  }
42044
42787
 
42788
+ // src/wrappers/voyageai.ts
42789
+ function wrapVoyageAI(client) {
42790
+ if (!isSupportedVoyageAIClient(client)) {
42791
+ console.warn("Unsupported Voyage AI library. Not wrapping.");
42792
+ return client;
42793
+ }
42794
+ return voyageAIProxy(client);
42795
+ }
42796
+ var voyageAIProxyCache = /* @__PURE__ */ new WeakMap();
42797
+ function isObject3(value) {
42798
+ return typeof value === "object" && value !== null;
42799
+ }
42800
+ function hasFunction5(value, name) {
42801
+ return typeof Reflect.get(value, name) === "function";
42802
+ }
42803
+ function isSupportedVoyageAIClient(value) {
42804
+ if (!isObject3(value)) {
42805
+ return false;
42806
+ }
42807
+ return hasFunction5(value, "embed") || hasFunction5(value, "multimodalEmbed") || hasFunction5(value, "rerank") || hasFunction5(value, "contextualizedEmbed");
42808
+ }
42809
+ function voyageAIProxy(client) {
42810
+ const cached = voyageAIProxyCache.get(client);
42811
+ if (cached) {
42812
+ return cached;
42813
+ }
42814
+ const proxy = new Proxy(client, {
42815
+ get(target, prop, receiver) {
42816
+ switch (prop) {
42817
+ case "embed":
42818
+ if (typeof target.embed !== "function") {
42819
+ return target.embed;
42820
+ }
42821
+ return (request, options) => voyageAIChannels.embed.invoke(
42822
+ target.embed,
42823
+ target,
42824
+ [request, options],
42825
+ {}
42826
+ );
42827
+ case "multimodalEmbed":
42828
+ if (typeof target.multimodalEmbed !== "function") {
42829
+ return target.multimodalEmbed;
42830
+ }
42831
+ return (request, options) => voyageAIChannels.multimodalEmbed.invoke(
42832
+ target.multimodalEmbed,
42833
+ target,
42834
+ [request, options],
42835
+ {}
42836
+ );
42837
+ case "rerank":
42838
+ if (typeof target.rerank !== "function") {
42839
+ return target.rerank;
42840
+ }
42841
+ return (request, options) => voyageAIChannels.rerank.invoke(
42842
+ target.rerank,
42843
+ target,
42844
+ [request, options],
42845
+ {}
42846
+ );
42847
+ case "contextualizedEmbed":
42848
+ if (typeof target.contextualizedEmbed !== "function") {
42849
+ return target.contextualizedEmbed;
42850
+ }
42851
+ return (request, options) => voyageAIChannels.contextualizedEmbed.invoke(
42852
+ target.contextualizedEmbed,
42853
+ target,
42854
+ [request, options],
42855
+ {}
42856
+ );
42857
+ default:
42858
+ return Reflect.get(target, prop, receiver);
42859
+ }
42860
+ }
42861
+ });
42862
+ voyageAIProxyCache.set(client, proxy);
42863
+ return proxy;
42864
+ }
42865
+
42045
42866
  // src/wrappers/groq.ts
42046
42867
  function wrapGroq(groq) {
42047
42868
  if (isSupportedGroqClient(groq)) {
@@ -42053,14 +42874,14 @@ function wrapGroq(groq) {
42053
42874
  function isRecord6(value) {
42054
42875
  return typeof value === "object" && value !== null;
42055
42876
  }
42056
- function hasFunction5(value, methodName) {
42877
+ function hasFunction6(value, methodName) {
42057
42878
  return isRecord6(value) && methodName in value && typeof value[methodName] === "function";
42058
42879
  }
42059
42880
  function hasChat2(value) {
42060
- return isRecord6(value) && isRecord6(value.completions) && hasFunction5(value.completions, "create");
42881
+ return isRecord6(value) && isRecord6(value.completions) && hasFunction6(value.completions, "create");
42061
42882
  }
42062
42883
  function hasEmbeddings2(value) {
42063
- return hasFunction5(value, "create");
42884
+ return hasFunction6(value, "create");
42064
42885
  }
42065
42886
  function isSupportedGroqClient(value) {
42066
42887
  return isRecord6(value) && (value.chat !== void 0 && hasChat2(value.chat) || value.embeddings !== void 0 && hasEmbeddings2(value.embeddings));
@@ -42643,7 +43464,7 @@ async function flushExperimentWithSync(context, config) {
42643
43464
  // src/wrappers/shared/scorers.ts
42644
43465
  async function runScorers(args) {
42645
43466
  const { scorers, output, expected, input, metadata, span } = args;
42646
- const scorerArgs = {
43467
+ const scorerArgs2 = {
42647
43468
  output,
42648
43469
  expected,
42649
43470
  input,
@@ -42652,7 +43473,7 @@ async function runScorers(args) {
42652
43473
  await Promise.all(
42653
43474
  scorers.map(async (scorer) => {
42654
43475
  try {
42655
- const result = await scorer(scorerArgs);
43476
+ const result = await scorer(scorerArgs2);
42656
43477
  const scores = normalizeScores(result);
42657
43478
  if (scores.length > 0) {
42658
43479
  const accScores = {};
@@ -45011,6 +45832,26 @@ async function getExperimentParametersRef(parameters) {
45011
45832
  version: resolvedParameters.version
45012
45833
  };
45013
45834
  }
45835
+ async function _internalInitEvaluatorExperiment(projectName, evaluator, data, options = {}) {
45836
+ if (options.disabled) return null;
45837
+ const { baseExperiment } = callEvaluatorData(data);
45838
+ const parameters = await getExperimentParametersRef(evaluator.parameters);
45839
+ return initExperiment2(evaluator.state, {
45840
+ ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: projectName },
45841
+ experiment: options.experimentName ?? evaluator.experimentName,
45842
+ description: evaluator.description,
45843
+ metadata: evaluator.metadata,
45844
+ tags: evaluator.tags,
45845
+ isPublic: evaluator.isPublic,
45846
+ update: options.update ?? evaluator.update,
45847
+ baseExperiment: evaluator.baseExperimentName ?? baseExperiment,
45848
+ baseExperimentId: evaluator.baseExperimentId,
45849
+ gitMetadataSettings: evaluator.gitMetadataSettings,
45850
+ repoInfo: evaluator.repoInfo,
45851
+ dataset: Dataset2.isDataset(data) ? data : void 0,
45852
+ parameters
45853
+ });
45854
+ }
45014
45855
  function callEvaluatorData(data) {
45015
45856
  const dataResult = typeof data === "function" ? data() : data;
45016
45857
  let baseExperiment = void 0;
@@ -45028,6 +45869,48 @@ function isAsyncIterable7(value) {
45028
45869
  function isIterable(value) {
45029
45870
  return typeof value === "object" && value !== null && Symbol.iterator in value && typeof value[Symbol.iterator] === "function";
45030
45871
  }
45872
+ async function _internalResolveEvaluatorData(evaluator, experiment) {
45873
+ if (typeof evaluator.data === "string") {
45874
+ throw new Error("Unimplemented: string data paths");
45875
+ }
45876
+ let dataResult = typeof evaluator.data === "function" ? evaluator.data() : evaluator.data;
45877
+ if ("_type" in dataResult) {
45878
+ if (dataResult._type !== "BaseExperiment") {
45879
+ throw new Error("Invalid _type");
45880
+ }
45881
+ if (!experiment) {
45882
+ throw new Error(
45883
+ "Cannot use BaseExperiment() without connecting to Braintrust (you most likely set --no-send-logs)"
45884
+ );
45885
+ }
45886
+ let name = dataResult.name;
45887
+ if (isEmpty2(name)) {
45888
+ const baseExperiment = await experiment.fetchBaseExperiment();
45889
+ if (!baseExperiment) {
45890
+ throw new Error("BaseExperiment() failed to fetch base experiment");
45891
+ }
45892
+ name = baseExperiment.name;
45893
+ }
45894
+ dataResult = initExperiment2(evaluator.state, {
45895
+ ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: evaluator.projectName },
45896
+ experiment: name,
45897
+ open: true
45898
+ }).asDataset();
45899
+ }
45900
+ const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
45901
+ if (isAsyncIterable7(resolvedDataResult)) {
45902
+ return resolvedDataResult;
45903
+ }
45904
+ if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
45905
+ const iterable = resolvedDataResult;
45906
+ return (async function* () {
45907
+ for (const datum of iterable) yield datum;
45908
+ })();
45909
+ }
45910
+ throw new Error(
45911
+ "Evaluator data must be an array, iterable, or async iterable"
45912
+ );
45913
+ }
45031
45914
  globalThis._evals = {
45032
45915
  functions: [],
45033
45916
  prompts: [],
@@ -45074,25 +45957,13 @@ async function Eval(name, evaluator, reporterOrOpts) {
45074
45957
  }
45075
45958
  const resolvedReporter = options.reporter || defaultReporter;
45076
45959
  try {
45077
- const { data, baseExperiment: defaultBaseExperiment } = callEvaluatorData(
45078
- evaluator.data
45079
- );
45080
- const parameters = await getExperimentParametersRef(evaluator.parameters);
45081
- const experiment = options.parent || options.noSendLogs ? null : initExperiment2(evaluator.state, {
45082
- ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: name },
45083
- experiment: evaluator.experimentName,
45084
- description: evaluator.description,
45085
- metadata: evaluator.metadata,
45086
- tags: evaluator.tags,
45087
- isPublic: evaluator.isPublic,
45088
- update: evaluator.update,
45089
- baseExperiment: evaluator.baseExperimentName ?? defaultBaseExperiment,
45090
- baseExperimentId: evaluator.baseExperimentId,
45091
- gitMetadataSettings: evaluator.gitMetadataSettings,
45092
- repoInfo: evaluator.repoInfo,
45093
- dataset: Dataset2.isDataset(data) ? data : void 0,
45094
- parameters
45095
- });
45960
+ const { data } = callEvaluatorData(evaluator.data);
45961
+ const experiment = await _internalInitEvaluatorExperiment(
45962
+ name,
45963
+ evaluator,
45964
+ data,
45965
+ { disabled: Boolean(options.parent || options.noSendLogs) }
45966
+ );
45096
45967
  if (experiment && typeof process !== "undefined" && globalThis.BRAINTRUST_CONTEXT_MANAGER !== void 0) {
45097
45968
  await experiment._waitForId();
45098
45969
  }
@@ -45190,19 +46061,74 @@ function scorerName(scorer, scorer_idx) {
45190
46061
  function classifierName(classifier, classifier_idx) {
45191
46062
  return classifier.name || `classifier_${classifier_idx}`;
45192
46063
  }
46064
+ async function _internalRunEvaluatorTask(task, datum, trialIndex, parameters, span, reportProgress = () => void 0) {
46065
+ const metadata = {
46066
+ ..."metadata" in datum ? datum.metadata : {}
46067
+ };
46068
+ const hooks = {
46069
+ meta(value) {
46070
+ Object.assign(metadata, value);
46071
+ },
46072
+ metadata,
46073
+ expected: "expected" in datum ? datum.expected : void 0,
46074
+ span,
46075
+ parameters,
46076
+ reportProgress,
46077
+ trialIndex,
46078
+ tags: [...datum.tags ?? []]
46079
+ };
46080
+ const output = await task(datum.input, hooks);
46081
+ span.log({ output });
46082
+ return {
46083
+ output,
46084
+ metadata: hooks.metadata,
46085
+ tags: hooks.tags ?? []
46086
+ };
46087
+ }
45193
46088
  function buildSpanMetadata(results) {
45194
- return results.length === 1 ? results[0].metadata : results.reduce(
45195
- (prev, s) => mergeDicts(prev, { [s.name]: s.metadata }),
45196
- {}
46089
+ return results.length === 1 ? results[0].metadata : Object.fromEntries(
46090
+ results.map((result) => [result.name, result.metadata])
45197
46091
  );
45198
46092
  }
45199
46093
  function buildSpanScores(results) {
45200
- const scoresRecord = results.reduce(
45201
- (prev, s) => mergeDicts(prev, { [s.name]: s.score }),
45202
- {}
46094
+ const scoresRecord = Object.fromEntries(
46095
+ results.map((result) => [result.name, result.score])
45203
46096
  );
45204
46097
  return { resultMetadata: buildSpanMetadata(results), scoresRecord };
45205
46098
  }
46099
+ function _internalPrepareEvaluatorScore(scoreValue, name) {
46100
+ if (scoreValue === null) return { results: null };
46101
+ if (Array.isArray(scoreValue)) {
46102
+ for (const score of scoreValue) {
46103
+ if (!(typeof score === "object" && !isEmpty2(score))) {
46104
+ throw new Error(
46105
+ `When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(score)}`
46106
+ );
46107
+ }
46108
+ }
46109
+ }
46110
+ let results;
46111
+ if (Array.isArray(scoreValue)) {
46112
+ results = scoreValue;
46113
+ } else if (typeof scoreValue === "object" && !isEmpty2(scoreValue)) {
46114
+ results = [scoreValue];
46115
+ } else {
46116
+ results = [{ name, score: scoreValue }];
46117
+ }
46118
+ const { resultMetadata, scoresRecord } = buildSpanScores(results);
46119
+ const fields = (score) => {
46120
+ const { metadata: _metadata, name: _name, ...rest } = score;
46121
+ return rest;
46122
+ };
46123
+ return {
46124
+ results,
46125
+ output: results.length === 1 ? fields(results[0]) : Object.fromEntries(
46126
+ results.map((score) => [score.name ?? name, fields(score)])
46127
+ ),
46128
+ metadata: resultMetadata,
46129
+ scores: scoresRecord
46130
+ };
46131
+ }
45206
46132
  async function runInScorerSpan(rootSpan, spanName, spanType, propagatedEvent, eventInput, fn) {
45207
46133
  try {
45208
46134
  const value = await rootSpan.traced(fn, {
@@ -45247,6 +46173,27 @@ function toClassificationItem(c) {
45247
46173
  ...c.metadata !== void 0 ? { metadata: c.metadata } : {}
45248
46174
  };
45249
46175
  }
46176
+ function _internalPrepareEvaluatorClassification(value, name) {
46177
+ if (value === null) return { results: null };
46178
+ const results = (Array.isArray(value) ? value : [value]).map(
46179
+ (result) => validateClassificationResult(result, name)
46180
+ );
46181
+ const classifications = /* @__PURE__ */ Object.create(null);
46182
+ for (const result of results) {
46183
+ (classifications[result.name] ??= []).push(toClassificationItem(result));
46184
+ }
46185
+ return {
46186
+ results,
46187
+ output: results.length === 1 ? toClassificationItem(results[0]) : Object.fromEntries(
46188
+ results.map((result) => [
46189
+ result.name,
46190
+ toClassificationItem(result)
46191
+ ])
46192
+ ),
46193
+ metadata: buildSpanMetadata(results),
46194
+ classifications
46195
+ };
46196
+ }
45250
46197
  function logScoringFailures(kind, failures, metadata, rootSpan, state) {
45251
46198
  if (!failures.length) return [];
45252
46199
  const errorMap = Object.fromEntries(
@@ -45294,54 +46241,14 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45294
46241
  (evaluator.state ?? _internalGetGlobalState())?.spanCache?.start();
45295
46242
  }
45296
46243
  try {
45297
- if (typeof evaluator.data === "string") {
45298
- throw new Error("Unimplemented: string data paths");
45299
- }
45300
- let dataResult = typeof evaluator.data === "function" ? evaluator.data() : evaluator.data;
45301
46244
  parameters = await validateParameters(
45302
46245
  parameters ?? {},
45303
46246
  evaluator.parameters
45304
46247
  );
45305
- if ("_type" in dataResult) {
45306
- if (dataResult._type !== "BaseExperiment") {
45307
- throw new Error("Invalid _type");
45308
- }
45309
- if (!experiment) {
45310
- throw new Error(
45311
- "Cannot use BaseExperiment() without connecting to Braintrust (you most likely set --no-send-logs)"
45312
- );
45313
- }
45314
- let name = dataResult.name;
45315
- if (isEmpty2(name)) {
45316
- const baseExperiment = await experiment.fetchBaseExperiment();
45317
- if (!baseExperiment) {
45318
- throw new Error("BaseExperiment() failed to fetch base experiment");
45319
- }
45320
- name = baseExperiment.name;
45321
- }
45322
- dataResult = initExperiment2(evaluator.state, {
45323
- ...evaluator.projectId ? { projectId: evaluator.projectId } : { project: evaluator.projectName },
45324
- experiment: name,
45325
- open: true
45326
- }).asDataset();
45327
- }
45328
- const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
45329
- const dataIterable = (() => {
45330
- if (isAsyncIterable7(resolvedDataResult)) {
45331
- return resolvedDataResult;
45332
- }
45333
- if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {
45334
- const iterable = resolvedDataResult;
45335
- return (async function* () {
45336
- for (const datum of iterable) {
45337
- yield datum;
45338
- }
45339
- })();
45340
- }
45341
- throw new Error(
45342
- "Evaluator data must be an array, iterable, or async iterable"
45343
- );
45344
- })();
46248
+ const dataIterable = await _internalResolveEvaluatorData(
46249
+ evaluator,
46250
+ experiment
46251
+ );
45345
46252
  progressReporter.start(evaluator.evalName, 0);
45346
46253
  const experimentIdPromise = experiment ? (async () => {
45347
46254
  try {
@@ -45410,57 +46317,45 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45410
46317
  ensureSpansFlushed,
45411
46318
  state
45412
46319
  }) : void 0;
45413
- let metadata = {
45414
- ..."metadata" in datum ? datum.metadata : {}
45415
- };
46320
+ let metadata = {};
45416
46321
  const expected = "expected" in datum ? datum.expected : void 0;
45417
46322
  let output = void 0;
45418
46323
  let error = void 0;
45419
- let tags = [...datum.tags ?? []];
45420
- const scores = {};
45421
- const classifications = {};
46324
+ let tags = [];
46325
+ const scores = /* @__PURE__ */ Object.create(null);
46326
+ const classifications = /* @__PURE__ */ Object.create(null);
45422
46327
  const scorerNames = (evaluator.scores ?? []).map(scorerName);
45423
46328
  const classifierNames = (evaluator.classifiers ?? []).map(
45424
46329
  classifierName
45425
46330
  );
45426
46331
  let unhandledScores = scorerNames;
45427
46332
  try {
45428
- const meta = (o) => metadata = { ...metadata, ...o };
45429
- await rootSpan.traced(
45430
- async (span) => {
45431
- const hooksForTask = {
45432
- meta,
45433
- metadata,
45434
- expected,
45435
- span,
45436
- parameters: parameters ?? {},
45437
- reportProgress: (event) => {
45438
- stream?.({
45439
- ...event,
45440
- id: rootSpan.id,
45441
- origin: baseEvent.event?.origin,
45442
- name: evaluator.evalName,
45443
- object_type: "task"
45444
- });
45445
- },
45446
- trialIndex,
45447
- tags
45448
- };
45449
- const outputResult = evaluator.task(datum.input, hooksForTask);
45450
- if (outputResult instanceof Promise) {
45451
- output = await outputResult;
45452
- } else {
45453
- output = outputResult;
46333
+ const taskResult = await rootSpan.traced(
46334
+ (span) => _internalRunEvaluatorTask(
46335
+ evaluator.task,
46336
+ datum,
46337
+ trialIndex,
46338
+ parameters ?? {},
46339
+ span,
46340
+ (event) => {
46341
+ stream?.({
46342
+ ...event,
46343
+ id: rootSpan.id,
46344
+ origin: baseEvent.event?.origin,
46345
+ name: evaluator.evalName,
46346
+ object_type: "task"
46347
+ });
45454
46348
  }
45455
- tags = hooksForTask.tags ?? [];
45456
- span.log({ output });
45457
- },
46349
+ ),
45458
46350
  {
45459
46351
  name: "task",
45460
46352
  spanAttributes: { type: "task" /* TASK */ },
45461
46353
  event: { input: datum.input }
45462
46354
  }
45463
46355
  );
46356
+ output = taskResult.output;
46357
+ metadata = taskResult.metadata;
46358
+ tags = taskResult.tags;
45464
46359
  if (tags.length) {
45465
46360
  rootSpan.log({ output, metadata, expected, tags });
45466
46361
  } else {
@@ -45470,20 +46365,18 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45470
46365
  await rootSpan.flush();
45471
46366
  }
45472
46367
  const scoringArgs = {
46368
+ id: datum.id,
45473
46369
  input: datum.input,
45474
46370
  expected: "expected" in datum ? datum.expected : void 0,
45475
46371
  metadata,
45476
46372
  output,
46373
+ tags,
45477
46374
  trace
45478
46375
  };
45479
46376
  const { trace: _trace, ...scoringArgsForLogging } = scoringArgs;
45480
46377
  const propagatedEvent = makeScorerPropagatedEvent(
45481
46378
  await rootSpan.export()
45482
46379
  );
45483
- const getOtherFields = (s) => {
45484
- const { metadata: _metadata, name: _name, ...rest } = s;
45485
- return rest;
45486
- };
45487
46380
  const [scoreResults, classificationResults] = await Promise.all([
45488
46381
  Promise.all(
45489
46382
  (evaluator.scores ?? []).map(
@@ -45497,35 +46390,17 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45497
46390
  const scoreValue = await Promise.resolve(
45498
46391
  score(scoringArgs)
45499
46392
  );
45500
- if (scoreValue === null) return null;
45501
- if (Array.isArray(scoreValue)) {
45502
- for (const s of scoreValue) {
45503
- if (!(typeof s === "object" && !isEmpty2(s))) {
45504
- throw new Error(
45505
- `When returning an array of scores, each score must be a non-empty object. Got: ${JSON.stringify(s)}`
45506
- );
45507
- }
45508
- }
45509
- }
45510
- const results = Array.isArray(scoreValue) ? scoreValue : typeof scoreValue === "object" && !isEmpty2(scoreValue) ? [scoreValue] : [
45511
- {
45512
- name: scorerNames[score_idx],
45513
- score: scoreValue
45514
- }
45515
- ];
45516
- const { resultMetadata, scoresRecord } = buildSpanScores(results);
45517
- const resultOutput = results.length === 1 ? getOtherFields(results[0]) : results.reduce(
45518
- (prev, s) => mergeDicts(prev, {
45519
- [s.name]: getOtherFields(s)
45520
- }),
45521
- {}
46393
+ const prepared = _internalPrepareEvaluatorScore(
46394
+ scoreValue,
46395
+ scorerNames[score_idx]
45522
46396
  );
46397
+ if (prepared.results === null) return null;
45523
46398
  span.log({
45524
- output: resultOutput,
45525
- metadata: resultMetadata,
45526
- scores: scoresRecord
46399
+ output: prepared.output,
46400
+ metadata: prepared.metadata,
46401
+ scores: prepared.scores
45527
46402
  });
45528
- return results;
46403
+ return prepared.results;
45529
46404
  }
45530
46405
  )
45531
46406
  )
@@ -45542,24 +46417,16 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
45542
46417
  const classifierValue = await Promise.resolve(
45543
46418
  classifier(scoringArgs)
45544
46419
  );
45545
- if (classifierValue === null) return null;
45546
- const rawResults = (Array.isArray(classifierValue) ? classifierValue : [classifierValue]).map(
45547
- (result) => validateClassificationResult(
45548
- result,
45549
- classifierNames[idx]
45550
- )
45551
- );
45552
- const resultOutput = rawResults.length === 1 ? toClassificationItem(rawResults[0]) : rawResults.reduce(
45553
- (prev, r) => mergeDicts(prev, {
45554
- [r.name]: toClassificationItem(r)
45555
- }),
45556
- {}
46420
+ const prepared = _internalPrepareEvaluatorClassification(
46421
+ classifierValue,
46422
+ classifierNames[idx]
45557
46423
  );
46424
+ if (prepared.results === null) return null;
45558
46425
  span.log({
45559
- output: resultOutput,
45560
- metadata: buildSpanMetadata(rawResults)
46426
+ output: prepared.output,
46427
+ metadata: prepared.metadata
45561
46428
  });
45562
- return rawResults;
46429
+ return prepared.results;
45563
46430
  }
45564
46431
  )
45565
46432
  )
@@ -45788,7 +46655,7 @@ function accumulateScores(accumulator, scores) {
45788
46655
  }
45789
46656
  }
45790
46657
  function ensureScoreAccumulator(results) {
45791
- const accumulator = {};
46658
+ const accumulator = /* @__PURE__ */ Object.create(null);
45792
46659
  for (const result of results) {
45793
46660
  accumulateScores(accumulator, result.scores);
45794
46661
  }
@@ -45931,6 +46798,1156 @@ var defaultReporter = {
45931
46798
  }
45932
46799
  };
45933
46800
 
46801
+ // src/durable-eval.ts
46802
+ var encoder = new TextEncoder();
46803
+ var decoder = new TextDecoder();
46804
+ var BATCH_TASK_KIND = "braintrust.durable.batch-task";
46805
+ var BATCH_SCORER_KIND = "braintrust.durable.batch-scorer";
46806
+ var DEFAULT_BATCH_SIZE = 1e3;
46807
+ var DurableEvalMemoryStore = class {
46808
+ values = /* @__PURE__ */ new Map();
46809
+ async read(key) {
46810
+ return this.values.get(key)?.slice();
46811
+ }
46812
+ async write(key, value) {
46813
+ this.values.set(key, value.slice());
46814
+ }
46815
+ async getOrSet(key, value) {
46816
+ const existing = this.values.get(key);
46817
+ if (existing) return { value: existing.slice(), created: false };
46818
+ this.values.set(key, value.slice());
46819
+ return { value: value.slice(), created: true };
46820
+ }
46821
+ };
46822
+ var DurableEvalRedisStore = class {
46823
+ client;
46824
+ keyPrefix;
46825
+ ttlMs;
46826
+ constructor(options) {
46827
+ this.client = options.client;
46828
+ this.keyPrefix = options.keyPrefix ?? "braintrust-eval:";
46829
+ this.ttlMs = options.ttlMs ?? 1e3 * 60 * 60 * 24 * 7;
46830
+ if (!Number.isInteger(this.ttlMs) || this.ttlMs < 1) {
46831
+ throw new Error("DurableEvalRedisStore ttlMs must be a positive integer");
46832
+ }
46833
+ }
46834
+ async read(key) {
46835
+ const value = await this.client.get(`${this.keyPrefix}${key}`);
46836
+ if (value == null) return void 0;
46837
+ if (typeof value !== "string") {
46838
+ throw new Error("DurableEvalRedisStore expected GET to return a string");
46839
+ }
46840
+ return base64ToUint8Array(value);
46841
+ }
46842
+ async write(key, value) {
46843
+ const client = this.client;
46844
+ const set = client.set;
46845
+ const redisKey = `${this.keyPrefix}${key}`;
46846
+ const encoded = uint8ArrayToBase64(value);
46847
+ if (typeof client.defineCommand === "function") {
46848
+ await set.call(client, redisKey, encoded, "PX", this.ttlMs);
46849
+ } else if (typeof client.sendCommand === "function") {
46850
+ await set.call(client, redisKey, encoded, { PX: this.ttlMs });
46851
+ } else if (typeof client.createScript === "function") {
46852
+ await set.call(client, redisKey, encoded, { px: this.ttlMs });
46853
+ } else {
46854
+ throw new Error(
46855
+ "DurableEvalRedisStore requires a node-redis, ioredis, or @upstash/redis client"
46856
+ );
46857
+ }
46858
+ }
46859
+ async getOrSet(key, value) {
46860
+ const redisKey = `${this.keyPrefix}${key}`;
46861
+ const encoded = uint8ArrayToBase64(value);
46862
+ const client = this.client;
46863
+ const set = client.set;
46864
+ let setOptions;
46865
+ if (typeof client.defineCommand === "function") {
46866
+ setOptions = ["PX", this.ttlMs, "NX", "GET"];
46867
+ } else if (typeof client.sendCommand === "function") {
46868
+ setOptions = [{ PX: this.ttlMs, NX: true, GET: true }];
46869
+ } else if (typeof client.createScript === "function") {
46870
+ setOptions = [{ px: this.ttlMs, nx: true, get: true }];
46871
+ } else {
46872
+ throw new Error(
46873
+ "DurableEvalRedisStore getOrSet requires a node-redis, ioredis, or @upstash/redis client"
46874
+ );
46875
+ }
46876
+ const existing = await set.call(client, redisKey, encoded, ...setOptions);
46877
+ if (existing === null) return { value: value.slice(), created: true };
46878
+ if (typeof existing !== "string") {
46879
+ throw new Error(
46880
+ "DurableEvalRedisStore expected atomic SET to return a string or null"
46881
+ );
46882
+ }
46883
+ return { value: base64ToUint8Array(existing), created: false };
46884
+ }
46885
+ };
46886
+ var BatchTask = class {
46887
+ constructor(processor) {
46888
+ this.processor = processor;
46889
+ }
46890
+ processor;
46891
+ kind = BATCH_TASK_KIND;
46892
+ };
46893
+ var BatchScorer = class {
46894
+ constructor(processor) {
46895
+ this.processor = processor;
46896
+ this.name = processor.name;
46897
+ }
46898
+ processor;
46899
+ kind = BATCH_SCORER_KIND;
46900
+ name;
46901
+ };
46902
+ function defineDurableEval(projectName, evaluator) {
46903
+ const definition = {
46904
+ projectName,
46905
+ evalName: evaluator.experimentName ?? projectName,
46906
+ evaluator
46907
+ };
46908
+ return {
46909
+ start: (options = {}) => startDurableEval(definition, options),
46910
+ status: (options) => getDurableEvalStatus(definition, options),
46911
+ poll: (options) => pollDurableEval(definition, options),
46912
+ processBatchResult: (result) => processDurableBatchResult(definition, result)
46913
+ };
46914
+ }
46915
+ async function startDurableEval(definition, options) {
46916
+ const store = definition.evaluator.store;
46917
+ const runId = newId();
46918
+ const key = runKey(definition.projectName, definition.evalName, runId);
46919
+ const { data } = callEvaluatorData(definition.evaluator.data);
46920
+ const parameters = await validateParameters(
46921
+ options.parameters ?? {},
46922
+ definition.evaluator.parameters
46923
+ );
46924
+ const experimentName = definition.evaluator.experimentName ?? `${definition.evalName}-${runId}`;
46925
+ const experiment = await _internalInitEvaluatorExperiment(
46926
+ definition.projectName,
46927
+ { ...definition.evaluator, data },
46928
+ data,
46929
+ {
46930
+ disabled: options.noSendLogs ?? false,
46931
+ experimentName,
46932
+ update: true
46933
+ }
46934
+ );
46935
+ if (!isBatchTask(definition.evaluator.task) && !(definition.evaluator.scores ?? []).some(isBatchScorer)) {
46936
+ const result = await runEvaluator(
46937
+ experiment,
46938
+ {
46939
+ ...definition.evaluator,
46940
+ projectName: definition.projectName,
46941
+ evalName: definition.evalName,
46942
+ data
46943
+ },
46944
+ {
46945
+ start: () => void 0,
46946
+ stop: () => void 0,
46947
+ increment: () => void 0
46948
+ },
46949
+ [],
46950
+ void 0,
46951
+ parameters,
46952
+ true,
46953
+ true
46954
+ );
46955
+ const state2 = {
46956
+ runId,
46957
+ experimentName,
46958
+ noSendLogs: options.noSendLogs ?? false,
46959
+ parameters: assertJsonValue(parameters, "eval parameters"),
46960
+ status: "completed",
46961
+ summary: result.summary,
46962
+ cases: [],
46963
+ batches: []
46964
+ };
46965
+ await experiment?.flush();
46966
+ await writeRunRecord(store, key, state2);
46967
+ return currentStatus(definition, state2);
46968
+ }
46969
+ const state = {
46970
+ runId,
46971
+ experimentName,
46972
+ noSendLogs: options.noSendLogs ?? false,
46973
+ parameters: assertJsonValue(parameters, "eval parameters"),
46974
+ status: "running",
46975
+ cases: await materializeCases(definition, data, experiment),
46976
+ batches: []
46977
+ };
46978
+ await writeCaseBaseRecords(store, key, state.cases);
46979
+ await writeRunRecord(store, key, state);
46980
+ return advanceDurableEval(definition, state, store, key, experiment);
46981
+ }
46982
+ async function getDurableEvalStatus(definition, options) {
46983
+ const store = definition.evaluator.store;
46984
+ const state = await readRunState(
46985
+ definition,
46986
+ store,
46987
+ runKey(definition.projectName, definition.evalName, options.runId)
46988
+ );
46989
+ if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
46990
+ return currentStatus(definition, state);
46991
+ }
46992
+ async function processDurableBatchResult(definition, result) {
46993
+ if (!result.batchId && !result.externalId) {
46994
+ throw new Error("Batch results require batchId or externalId");
46995
+ }
46996
+ const store = definition.evaluator.store;
46997
+ const key = runKey(definition.projectName, definition.evalName, result.runId);
46998
+ const state = await readRunState(definition, store, key);
46999
+ if (!state) throw new Error(`Durable eval run ${result.runId} is missing`);
47000
+ const byBatch = result.batchId ? state.batches.find((candidate) => candidate.id === result.batchId) : void 0;
47001
+ const byExternal = result.externalId ? state.batches.find(
47002
+ (candidate) => candidate.externalId === result.externalId
47003
+ ) : void 0;
47004
+ if (byBatch && byExternal && byBatch.id !== byExternal.id) {
47005
+ throw new Error("batchId and externalId identify different batches");
47006
+ }
47007
+ const batch = byBatch ?? byExternal;
47008
+ if (!batch) throw new Error("No submitted batch matches this result");
47009
+ if (batch.status !== "complete") {
47010
+ const records = await collectBatch(definition, state, batch);
47011
+ batch.status = "complete";
47012
+ await writeCaseRecords(store, key, records);
47013
+ await writeBatchRecords(store, key, [batch]);
47014
+ }
47015
+ return advanceDurableEval(
47016
+ definition,
47017
+ await readRunState(definition, store, key),
47018
+ store,
47019
+ key
47020
+ );
47021
+ }
47022
+ async function pollDurableEval(definition, options) {
47023
+ const store = definition.evaluator.store;
47024
+ const key = runKey(
47025
+ definition.projectName,
47026
+ definition.evalName,
47027
+ options.runId
47028
+ );
47029
+ const state = await readRunState(definition, store, key);
47030
+ if (!state) throw new Error(`Durable eval run ${options.runId} is missing`);
47031
+ const batches = state.batches.filter((batch) => {
47032
+ if (batch.status === "complete") return false;
47033
+ return processorForStage(definition, batch.kind, batch.scorerName).completion.mode === "poll";
47034
+ });
47035
+ const results = await Promise.all(
47036
+ batches.map(async (batch) => ({
47037
+ batch,
47038
+ result: await processorForStage(definition, batch.kind, batch.scorerName).completion.poll(batch.submissionData, {
47039
+ runId: state.runId,
47040
+ batchId: batch.id
47041
+ })
47042
+ }))
47043
+ );
47044
+ const changedCases = /* @__PURE__ */ new Map();
47045
+ const changedBatches = [];
47046
+ for (const { batch, result } of results) {
47047
+ if (result.status === "failed") throw asError(result.error);
47048
+ if (result.status !== "complete") continue;
47049
+ for (const record of await collectBatch(definition, state, batch)) {
47050
+ changedCases.set(record.id, record);
47051
+ }
47052
+ batch.status = "complete";
47053
+ changedBatches.push(batch);
47054
+ }
47055
+ if (changedBatches.length > 0) {
47056
+ await writeCaseRecords(store, key, [...changedCases.values()]);
47057
+ await writeBatchRecords(store, key, changedBatches);
47058
+ }
47059
+ const currentState = changedBatches.length > 0 ? await readRunState(definition, store, key) : state;
47060
+ return advanceDurableEval(definition, currentState, store, key);
47061
+ }
47062
+ async function openDurableExperiment(definition, state) {
47063
+ const data = [];
47064
+ return await _internalInitEvaluatorExperiment(
47065
+ definition.projectName,
47066
+ { ...definition.evaluator, data },
47067
+ data,
47068
+ {
47069
+ disabled: state.noSendLogs,
47070
+ experimentName: state.experimentName,
47071
+ update: true
47072
+ }
47073
+ );
47074
+ }
47075
+ async function advanceDurableEval(definition, state, store, key, existingExperiment) {
47076
+ if (state.status === "completed") return currentStatus(definition, state);
47077
+ const experiment = existingExperiment === void 0 ? await openDurableExperiment(definition, state) : existingExperiment;
47078
+ await runTaskStage(definition, state, store, key, experiment);
47079
+ await logCompletedTasks(definition, state, store, key, experiment);
47080
+ state = await readRunState(definition, store, key) ?? state;
47081
+ if (state.cases.some((record) => !record.taskComplete || !record.taskLogged)) {
47082
+ return currentStatus(definition, state);
47083
+ }
47084
+ await runScoreStages(definition, state, store, key, experiment);
47085
+ state = await readRunState(definition, store, key) ?? state;
47086
+ const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
47087
+ ({ name }) => name
47088
+ );
47089
+ const classifierNames = (definition.evaluator.classifiers ?? []).map(
47090
+ classifierName
47091
+ );
47092
+ if (state.cases.some(
47093
+ (record) => scorerNames.some(
47094
+ (name) => !Object.hasOwn(record.scores, name) || !Object.hasOwn(record.loggedScores, name)
47095
+ ) || classifierNames.some(
47096
+ (name) => !Object.hasOwn(record.loggedClassifications, name)
47097
+ )
47098
+ )) {
47099
+ return currentStatus(definition, state);
47100
+ }
47101
+ if (!await claimAction(store, key, "finish")) {
47102
+ const latest = await readRunState(definition, store, key);
47103
+ return currentStatus(definition, latest ?? state);
47104
+ }
47105
+ state.summary = await finishExperiment(definition, state, experiment);
47106
+ state.status = "completed";
47107
+ await writeRunRecord(store, key, state);
47108
+ return currentStatus(definition, state);
47109
+ }
47110
+ function currentStatus(definition, state) {
47111
+ if (state.status === "completed") {
47112
+ if (!state.summary) {
47113
+ throw new Error(`Durable eval run ${state.runId} has no saved summary`);
47114
+ }
47115
+ return {
47116
+ status: "completed",
47117
+ runId: state.runId,
47118
+ pending: { poll: 0, webhook: 0 },
47119
+ summary: state.summary
47120
+ };
47121
+ }
47122
+ const pending = { poll: 0, webhook: 0 };
47123
+ for (const batch of state.batches) {
47124
+ if (batch.status === "complete") continue;
47125
+ pending[processorForStage(definition, batch.kind, batch.scorerName).completion.mode]++;
47126
+ }
47127
+ return { status: "waiting", runId: state.runId, pending };
47128
+ }
47129
+ async function startCaseRoot(definition, state, record, experiment) {
47130
+ if (!experiment) return NOOP_SPAN;
47131
+ const datum = record.datum;
47132
+ return _internalStartSpanWithInitialMerge({
47133
+ ...definition.evaluator.state ? { state: definition.evaluator.state } : {},
47134
+ parent: await experiment.export(),
47135
+ name: "eval",
47136
+ spanId: deterministicId(`${state.runId}:${record.id}:span`),
47137
+ spanAttributes: { type: "eval" /* EVAL */ },
47138
+ event: {
47139
+ id: deterministicId(`${state.runId}:${record.id}:row`),
47140
+ input: datum.input,
47141
+ expected: "expected" in datum ? datum.expected : void 0,
47142
+ tags: datum.tags,
47143
+ origin: datum.origin
47144
+ }
47145
+ });
47146
+ }
47147
+ async function logTaskResult(definition, state, record, experiment, task) {
47148
+ const datum = record.datum;
47149
+ const root = await startCaseRoot(definition, state, record, experiment);
47150
+ try {
47151
+ if (task) {
47152
+ const result = await root.traced(
47153
+ (span) => _internalRunEvaluatorTask(
47154
+ task,
47155
+ datum,
47156
+ record.trialIndex,
47157
+ state.parameters,
47158
+ span
47159
+ ),
47160
+ {
47161
+ name: "task",
47162
+ spanId: deterministicId(`${state.runId}:${record.id}:task`),
47163
+ spanAttributes: { type: "task" /* TASK */ },
47164
+ event: { input: datum.input }
47165
+ }
47166
+ );
47167
+ record.output = assertJsonValue(
47168
+ result.output,
47169
+ `task output for ${record.caseId}`
47170
+ );
47171
+ record.metadata = assertJsonValue(result.metadata, "task metadata");
47172
+ record.tags = result.tags;
47173
+ record.taskComplete = true;
47174
+ } else {
47175
+ await root.traced((span) => span.log({ output: record.output }), {
47176
+ name: "task",
47177
+ spanId: deterministicId(`${state.runId}:${record.id}:task`),
47178
+ spanAttributes: { type: "task" /* TASK */ },
47179
+ event: { input: datum.input }
47180
+ });
47181
+ }
47182
+ root.log({
47183
+ output: record.output,
47184
+ expected: "expected" in datum ? datum.expected : void 0,
47185
+ metadata: {
47186
+ ...record.metadata,
47187
+ durable_eval: {
47188
+ run_id: state.runId,
47189
+ case_id: record.caseId,
47190
+ trial_index: record.trialIndex
47191
+ }
47192
+ },
47193
+ tags: record.tags
47194
+ });
47195
+ record.rootSpan = await root.export();
47196
+ record.taskLogged = true;
47197
+ } catch (error) {
47198
+ logError(root, error);
47199
+ throw error;
47200
+ } finally {
47201
+ root.end();
47202
+ }
47203
+ }
47204
+ async function logCompletedTasks(definition, state, store, key, experiment) {
47205
+ const changed = [];
47206
+ for (const record of state.cases) {
47207
+ if (!record.taskComplete || record.taskLogged) continue;
47208
+ if (!await claimAction(store, key, "task-log", record.id)) continue;
47209
+ await logTaskResult(definition, state, record, experiment);
47210
+ changed.push(record);
47211
+ }
47212
+ if (changed.length > 0) {
47213
+ await experiment?.flush();
47214
+ await writeCaseRecords(store, key, changed);
47215
+ }
47216
+ }
47217
+ async function runTaskStage(definition, state, store, key, experiment) {
47218
+ if (isBatchTask(definition.evaluator.task)) {
47219
+ await ensureBatches(definition, state, store, key, "task");
47220
+ return;
47221
+ }
47222
+ const task = definition.evaluator.task;
47223
+ const changed = [];
47224
+ for (const record of state.cases) {
47225
+ if (record.taskComplete) continue;
47226
+ if (!await claimAction(store, key, "task", record.id)) continue;
47227
+ await logTaskResult(definition, state, record, experiment, task);
47228
+ changed.push(record);
47229
+ }
47230
+ if (changed.length > 0) {
47231
+ await experiment?.flush();
47232
+ await writeCaseRecords(store, key, changed);
47233
+ }
47234
+ }
47235
+ async function runScoreStages(definition, state, store, key, experiment) {
47236
+ const scorers = resolveScorers(definition.evaluator.scores ?? []);
47237
+ const changed = /* @__PURE__ */ new Map();
47238
+ const persistChangedCases = async () => {
47239
+ if (changed.size === 0) return;
47240
+ await experiment?.flush();
47241
+ await writeCaseRecords(store, key, [...changed.values()]);
47242
+ changed.clear();
47243
+ };
47244
+ for (const { name, scorer } of scorers) {
47245
+ if (isBatchScorer(scorer)) {
47246
+ for (const record of state.cases) {
47247
+ if (Object.hasOwn(record.scores, name) && !Object.hasOwn(record.loggedScores, name)) {
47248
+ if (!await claimAction(store, key, "score-log", record.id, name)) {
47249
+ continue;
47250
+ }
47251
+ await evaluateAndLogScore(
47252
+ definition,
47253
+ state,
47254
+ record,
47255
+ name,
47256
+ experiment
47257
+ );
47258
+ changed.set(record.id, record);
47259
+ }
47260
+ }
47261
+ await persistChangedCases();
47262
+ await ensureBatches(definition, state, store, key, "score", name);
47263
+ continue;
47264
+ }
47265
+ for (const record of state.cases) {
47266
+ if (Object.hasOwn(record.loggedScores, name)) continue;
47267
+ if (!await claimAction(store, key, "score", record.id, name)) continue;
47268
+ await evaluateAndLogScore(
47269
+ definition,
47270
+ state,
47271
+ record,
47272
+ name,
47273
+ experiment,
47274
+ scorer
47275
+ );
47276
+ changed.set(record.id, record);
47277
+ }
47278
+ }
47279
+ for (const [index, classifier] of (definition.evaluator.classifiers ?? []).entries()) {
47280
+ const name = classifierName(classifier, index);
47281
+ for (const record of state.cases) {
47282
+ if (Object.hasOwn(record.loggedClassifications, name)) continue;
47283
+ if (!await claimAction(store, key, "classification", record.id, name)) {
47284
+ continue;
47285
+ }
47286
+ await evaluateAndLogClassification(
47287
+ definition,
47288
+ state,
47289
+ record,
47290
+ name,
47291
+ classifier,
47292
+ experiment
47293
+ );
47294
+ changed.set(record.id, record);
47295
+ }
47296
+ }
47297
+ await persistChangedCases();
47298
+ }
47299
+ function scorerArgs(record) {
47300
+ const datum = record.datum;
47301
+ return {
47302
+ ...datum,
47303
+ metadata: record.metadata,
47304
+ output: record.output
47305
+ };
47306
+ }
47307
+ function resumeCaseRoot(definition, record, experiment) {
47308
+ if (!experiment) return NOOP_SPAN;
47309
+ if (!record.rootSpan) {
47310
+ throw new Error(`Durable eval case ${record.caseId} has no root span`);
47311
+ }
47312
+ return _internalResumeSpan({
47313
+ exported: record.rootSpan,
47314
+ state: definition.evaluator.state
47315
+ });
47316
+ }
47317
+ async function evaluateAndLogScore(definition, state, record, name, experiment, scorer) {
47318
+ const root = resumeCaseRoot(definition, record, experiment);
47319
+ try {
47320
+ const rootExport = await root.export();
47321
+ const prepared = await root.traced(
47322
+ async (span) => {
47323
+ const value = scorer ? await scorer(scorerArgs(record)) : record.scores[name];
47324
+ if (scorer) {
47325
+ record.scores[name] = assertJsonValue(value, `scorer ${name} output`);
47326
+ }
47327
+ const result = _internalPrepareEvaluatorScore(value, name);
47328
+ if (result.results !== null) {
47329
+ span.log({
47330
+ output: result.output,
47331
+ metadata: result.metadata,
47332
+ scores: result.scores
47333
+ });
47334
+ }
47335
+ return result;
47336
+ },
47337
+ {
47338
+ name,
47339
+ spanId: deterministicId(`${state.runId}:${record.id}:score:${name}`),
47340
+ spanAttributes: {
47341
+ type: "score" /* SCORE */,
47342
+ purpose: "scorer"
47343
+ },
47344
+ propagatedEvent: makeScorerPropagatedEvent(rootExport || void 0),
47345
+ event: { input: scorerArgs(record) }
47346
+ }
47347
+ );
47348
+ if (prepared.scores) root.log({ scores: prepared.scores });
47349
+ record.loggedScores[name] = true;
47350
+ } catch (error) {
47351
+ logError(root, error);
47352
+ throw error;
47353
+ } finally {
47354
+ root.end();
47355
+ }
47356
+ }
47357
+ async function evaluateAndLogClassification(definition, state, record, name, classifier, experiment) {
47358
+ const root = resumeCaseRoot(definition, record, experiment);
47359
+ try {
47360
+ const rootExport = await root.export();
47361
+ const prepared = await root.traced(
47362
+ async (span) => {
47363
+ const value = await classifier(scorerArgs(record));
47364
+ record.classifications[name] = assertJsonValue(
47365
+ value,
47366
+ `classifier ${name} output`
47367
+ );
47368
+ const result = _internalPrepareEvaluatorClassification(value, name);
47369
+ if (result.results !== null) {
47370
+ span.log({ output: result.output, metadata: result.metadata });
47371
+ }
47372
+ return result;
47373
+ },
47374
+ {
47375
+ name,
47376
+ spanId: deterministicId(
47377
+ `${state.runId}:${record.id}:classification:${name}`
47378
+ ),
47379
+ spanAttributes: {
47380
+ type: "classifier" /* CLASSIFIER */,
47381
+ purpose: "scorer"
47382
+ },
47383
+ propagatedEvent: makeScorerPropagatedEvent(rootExport || void 0),
47384
+ event: { input: scorerArgs(record) }
47385
+ }
47386
+ );
47387
+ if (prepared.classifications) {
47388
+ root.log({ classifications: prepared.classifications });
47389
+ }
47390
+ record.loggedClassifications[name] = true;
47391
+ } catch (error) {
47392
+ logError(root, error);
47393
+ throw error;
47394
+ } finally {
47395
+ root.end();
47396
+ }
47397
+ }
47398
+ async function ensureBatches(definition, state, store, key, kind, scorerName2) {
47399
+ const processor = processorForStage(definition, kind, scorerName2);
47400
+ const plans = plannedBatches(
47401
+ definition,
47402
+ state.runId,
47403
+ state.cases.map(({ id }) => id)
47404
+ );
47405
+ const casesById = new Map(state.cases.map((record) => [record.id, record]));
47406
+ for (const plan of plans) {
47407
+ if (plan.kind !== kind || plan.scorerName !== scorerName2) continue;
47408
+ if (state.batches.some(({ id }) => id === plan.id)) continue;
47409
+ const records = plan.itemIds.map((id) => casesById.get(id));
47410
+ const ready = records.every(
47411
+ (record) => kind === "task" ? !record.taskComplete : !Object.hasOwn(record.scores, scorerName2)
47412
+ );
47413
+ if (!ready) continue;
47414
+ const batchId = plan.id;
47415
+ const claim = await store.getOrSet(
47416
+ claimRecordKey(key, "batch", batchId),
47417
+ encoder.encode(batchId)
47418
+ );
47419
+ if (!claim.created) continue;
47420
+ const context = { runId: state.runId, batchId };
47421
+ const items = records.map(
47422
+ (record) => kind === "task" ? taskBatchItem(record, state.parameters) : scorerBatchItem(record)
47423
+ );
47424
+ const submissionData = assertJsonValue(
47425
+ await processor.submit(items, context),
47426
+ `submission data for batch ${batchId}`
47427
+ );
47428
+ const externalId = processor.completion.mode === "webhook" ? processor.completion.getExternalId(submissionData, context) : void 0;
47429
+ if (externalId !== void 0 && !externalId.trim()) {
47430
+ throw new Error(`Batch ${batchId} produced an empty externalId`);
47431
+ }
47432
+ const batch = {
47433
+ id: batchId,
47434
+ kind,
47435
+ scorerName: scorerName2,
47436
+ itemIds: records.map((record) => record.id),
47437
+ submissionData,
47438
+ externalId,
47439
+ status: "submitted"
47440
+ };
47441
+ state.batches.push(batch);
47442
+ await writeBatchRecords(store, key, [batch]);
47443
+ }
47444
+ }
47445
+ async function collectBatch(definition, state, batch) {
47446
+ const processor = processorForStage(definition, batch.kind, batch.scorerName);
47447
+ const context = { runId: state.runId, batchId: batch.id };
47448
+ const results = await processor.collect(batch.submissionData, context);
47449
+ if (!Array.isArray(results)) {
47450
+ throw new Error(`collect for batch ${batch.id} must return an array`);
47451
+ }
47452
+ const expectedIds = new Set(batch.itemIds);
47453
+ const seen = /* @__PURE__ */ new Set();
47454
+ const records = [];
47455
+ for (const result of results) {
47456
+ const id = resultItemId(result);
47457
+ if (!expectedIds.has(id)) {
47458
+ throw new Error(`Batch ${batch.id} returned unknown item ${id}`);
47459
+ }
47460
+ if (seen.has(id)) {
47461
+ throw new Error(`Batch ${batch.id} returned item ${id} more than once`);
47462
+ }
47463
+ seen.add(id);
47464
+ const record = state.cases.find((candidate) => candidate.id === id);
47465
+ records.push(record);
47466
+ if (batch.kind === "task") {
47467
+ record.output = assertJsonValue(
47468
+ result.output,
47469
+ `task output for item ${id}`
47470
+ );
47471
+ if ("metadata" in result && result.metadata !== void 0) {
47472
+ record.metadata = assertJsonValue(
47473
+ {
47474
+ ...record.metadata,
47475
+ ...result.metadata
47476
+ },
47477
+ `metadata for ${id}`
47478
+ );
47479
+ }
47480
+ if ("tags" in result && result.tags !== void 0)
47481
+ record.tags = result.tags;
47482
+ record.taskComplete = true;
47483
+ } else {
47484
+ record.scores[batch.scorerName] = assertJsonValue(
47485
+ result.score,
47486
+ `score output for item ${id}`
47487
+ );
47488
+ }
47489
+ }
47490
+ const missing = batch.itemIds.filter((id) => !seen.has(id));
47491
+ if (missing.length > 0) {
47492
+ throw new Error(
47493
+ `Batch ${batch.id} did not return results for: ${missing.join(", ")}`
47494
+ );
47495
+ }
47496
+ return records;
47497
+ }
47498
+ function processorForStage(definition, kind, scorerName2) {
47499
+ if (kind === "task") {
47500
+ if (!isBatchTask(definition.evaluator.task)) {
47501
+ throw new Error("Definition no longer contains the batch task");
47502
+ }
47503
+ return definition.evaluator.task.processor;
47504
+ }
47505
+ const scorer = resolveScorers(definition.evaluator.scores ?? []).find(
47506
+ ({ name }) => name === scorerName2
47507
+ )?.scorer;
47508
+ if (!isBatchScorer(scorer)) {
47509
+ throw new Error(`Definition no longer contains scorer ${scorerName2}`);
47510
+ }
47511
+ return scorer.processor;
47512
+ }
47513
+ async function materializeCases(definition, data, experiment) {
47514
+ const evaluator = definition.evaluator;
47515
+ const iterable = await _internalResolveEvaluatorData(
47516
+ {
47517
+ data,
47518
+ projectName: definition.projectName,
47519
+ projectId: evaluator.projectId,
47520
+ state: evaluator.state
47521
+ },
47522
+ experiment
47523
+ );
47524
+ const records = [];
47525
+ const seen = /* @__PURE__ */ new Set();
47526
+ for await (const datum of iterable) {
47527
+ const caseId = datum.id ?? datum.upsert_id ?? (evaluator.caseId ? await evaluator.caseId(datum) : void 0);
47528
+ if (!caseId) {
47529
+ throw new Error(
47530
+ "Every durable eval case requires id, upsert_id, or caseId"
47531
+ );
47532
+ }
47533
+ if (seen.has(caseId))
47534
+ throw new Error(`Duplicate durable eval case id: ${caseId}`);
47535
+ seen.add(caseId);
47536
+ const trialCount = datum.trialCount ?? evaluator.trialCount ?? 1;
47537
+ if (!Number.isInteger(trialCount) || trialCount < 1) {
47538
+ throw new Error(`Invalid trialCount for durable eval case ${caseId}`);
47539
+ }
47540
+ for (let trialIndex = 0; trialIndex < trialCount; trialIndex++) {
47541
+ records.push({
47542
+ id: `${caseId}:trial:${trialIndex}`,
47543
+ caseId,
47544
+ trialIndex,
47545
+ datum: assertJsonValue(datum, `case ${caseId}`),
47546
+ metadata: assertJsonValue(
47547
+ "metadata" in datum ? datum.metadata : {},
47548
+ `metadata for ${caseId}`
47549
+ ),
47550
+ tags: datum.tags,
47551
+ taskComplete: false,
47552
+ taskLogged: false,
47553
+ scores: /* @__PURE__ */ Object.create(null),
47554
+ loggedScores: /* @__PURE__ */ Object.create(null),
47555
+ classifications: /* @__PURE__ */ Object.create(null),
47556
+ loggedClassifications: /* @__PURE__ */ Object.create(null)
47557
+ });
47558
+ }
47559
+ }
47560
+ return records;
47561
+ }
47562
+ function taskBatchItem(record, parameters) {
47563
+ const datum = record.datum;
47564
+ return {
47565
+ id: record.id,
47566
+ input: datum.input,
47567
+ expected: "expected" in datum ? datum.expected : void 0,
47568
+ metadata: record.metadata,
47569
+ tags: record.tags,
47570
+ parameters,
47571
+ trialIndex: record.trialIndex
47572
+ };
47573
+ }
47574
+ function scorerBatchItem(record) {
47575
+ const datum = record.datum;
47576
+ return {
47577
+ id: record.id,
47578
+ input: datum.input,
47579
+ output: record.output,
47580
+ expected: "expected" in datum ? datum.expected : void 0,
47581
+ metadata: record.metadata,
47582
+ tags: record.tags,
47583
+ trialIndex: record.trialIndex
47584
+ };
47585
+ }
47586
+ async function finishExperiment(definition, state, experiment) {
47587
+ const scorerNames = resolveScorers(definition.evaluator.scores ?? []).map(
47588
+ ({ name }) => name
47589
+ );
47590
+ const results = state.cases.map((record) => {
47591
+ const datum = record.datum;
47592
+ const scores = Object.fromEntries(
47593
+ scorerNames.flatMap(
47594
+ (name) => Object.entries(
47595
+ _internalPrepareEvaluatorScore(
47596
+ record.scores[name],
47597
+ name
47598
+ ).scores ?? {}
47599
+ )
47600
+ )
47601
+ );
47602
+ const classifications = Object.fromEntries(
47603
+ Object.entries(record.classifications).flatMap(
47604
+ ([name, value]) => Object.entries(
47605
+ _internalPrepareEvaluatorClassification(value, name).classifications ?? {}
47606
+ )
47607
+ )
47608
+ );
47609
+ return {
47610
+ ...datum,
47611
+ output: record.output,
47612
+ metadata: record.metadata,
47613
+ tags: record.tags,
47614
+ scores,
47615
+ error: void 0,
47616
+ ...Object.keys(classifications).length > 0 ? { classifications } : {}
47617
+ };
47618
+ });
47619
+ if (!experiment) {
47620
+ return buildLocalSummary(
47621
+ {
47622
+ ...definition.evaluator,
47623
+ projectName: definition.projectName,
47624
+ evalName: state.experimentName
47625
+ },
47626
+ results
47627
+ );
47628
+ }
47629
+ await experiment.flush();
47630
+ let comparisonExperimentId = definition.evaluator.baseExperimentId;
47631
+ if (!comparisonExperimentId) {
47632
+ try {
47633
+ comparisonExperimentId = await experiment._getBaseExperimentId();
47634
+ } catch {
47635
+ comparisonExperimentId = void 0;
47636
+ }
47637
+ }
47638
+ return await experiment.summarize({
47639
+ summarizeScores: definition.evaluator.summarizeScores,
47640
+ ...comparisonExperimentId ? { comparisonExperimentId } : {}
47641
+ });
47642
+ }
47643
+ function resolveScorers(scorers) {
47644
+ return scorers.map((scorer, index) => ({
47645
+ name: isBatchScorer(scorer) ? scorer.name : scorer.name || `scorer_${index}`,
47646
+ scorer
47647
+ }));
47648
+ }
47649
+ function runKey(projectName, evalName, runId) {
47650
+ return `durable-eval/v1/runs/${contentVersion(encoder.encode(`${projectName}\0${evalName}\0${runId}`))}`;
47651
+ }
47652
+ function encodedKeyPart(value) {
47653
+ return uint8ArrayToBase64(encoder.encode(value)).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
47654
+ }
47655
+ function caseRecordKey(key, caseId, kind, ...names) {
47656
+ const suffix = names.map(encodedKeyPart).join("/");
47657
+ return `${key}/cases/${encodedKeyPart(caseId)}/${kind}${suffix ? `/${suffix}` : ""}`;
47658
+ }
47659
+ function batchRecordKey(key, batchId) {
47660
+ return `${key}/batches/${encodedKeyPart(batchId)}`;
47661
+ }
47662
+ function claimRecordKey(key, kind, ...parts) {
47663
+ const identity = stableStringify([kind, parts]);
47664
+ return `${key}/claims/${contentVersion(encoder.encode(identity))}`;
47665
+ }
47666
+ async function claimAction(store, key, kind, ...parts) {
47667
+ return (await store.getOrSet(
47668
+ claimRecordKey(key, kind, ...parts),
47669
+ encoder.encode("claimed")
47670
+ )).created;
47671
+ }
47672
+ function plannedBatches(definition, runId, caseIds) {
47673
+ const stages = [];
47674
+ if (isBatchTask(definition.evaluator.task)) stages.push({ kind: "task" });
47675
+ for (const { name, scorer } of resolveScorers(
47676
+ definition.evaluator.scores ?? []
47677
+ )) {
47678
+ if (isBatchScorer(scorer)) {
47679
+ stages.push({ kind: "score", scorerName: name });
47680
+ }
47681
+ }
47682
+ const plans = [];
47683
+ for (const { kind, scorerName: scorerName2 } of stages) {
47684
+ const batchSize = processorForStage(definition, kind, scorerName2).batchSize ?? DEFAULT_BATCH_SIZE;
47685
+ if (!Number.isInteger(batchSize) || batchSize < 1) {
47686
+ throw new Error(
47687
+ `Invalid batchSize for ${scorerName2 ?? "task"}: ${batchSize}`
47688
+ );
47689
+ }
47690
+ for (let offset = 0; offset < caseIds.length; offset += batchSize) {
47691
+ const itemIds = caseIds.slice(offset, offset + batchSize);
47692
+ plans.push({
47693
+ id: deterministicId(
47694
+ stableStringify([runId, kind, scorerName2, itemIds])
47695
+ ),
47696
+ kind,
47697
+ scorerName: scorerName2,
47698
+ itemIds
47699
+ });
47700
+ }
47701
+ }
47702
+ return plans;
47703
+ }
47704
+ async function readCaseRecord(definition, store, key, id) {
47705
+ const scorers = resolveScorers(definition.evaluator.scores ?? []);
47706
+ const classifiers = (definition.evaluator.classifiers ?? []).map(
47707
+ classifierName
47708
+ );
47709
+ const [
47710
+ base,
47711
+ task,
47712
+ taskLog,
47713
+ scoreValues,
47714
+ scoreLogValues,
47715
+ classificationValues,
47716
+ classificationLogValues
47717
+ ] = await Promise.all([
47718
+ readJson(store, caseRecordKey(key, id, "base")),
47719
+ readJson(store, caseRecordKey(key, id, "task")),
47720
+ readJson(store, caseRecordKey(key, id, "task-log")),
47721
+ Promise.all(
47722
+ scorers.map(async ({ name }) => ({
47723
+ name,
47724
+ value: await readJson(
47725
+ store,
47726
+ caseRecordKey(key, id, "score", name)
47727
+ )
47728
+ }))
47729
+ ),
47730
+ Promise.all(
47731
+ scorers.map(async ({ name }) => ({
47732
+ name,
47733
+ value: await readJson(
47734
+ store,
47735
+ caseRecordKey(key, id, "score-log", name)
47736
+ )
47737
+ }))
47738
+ ),
47739
+ Promise.all(
47740
+ classifiers.map(async (name) => ({
47741
+ name,
47742
+ value: await readJson(
47743
+ store,
47744
+ caseRecordKey(key, id, "classification", name)
47745
+ )
47746
+ }))
47747
+ ),
47748
+ Promise.all(
47749
+ classifiers.map(async (name) => ({
47750
+ name,
47751
+ value: await readJson(
47752
+ store,
47753
+ caseRecordKey(key, id, "classification-log", name)
47754
+ )
47755
+ }))
47756
+ )
47757
+ ]);
47758
+ if (!base) throw new Error(`Durable eval case ${id} is missing`);
47759
+ const scores = /* @__PURE__ */ Object.create(null);
47760
+ for (const { name, value } of scoreValues) {
47761
+ if (value !== void 0) scores[name] = value;
47762
+ }
47763
+ const loggedScores = /* @__PURE__ */ Object.create(null);
47764
+ for (const { name, value } of scoreLogValues) {
47765
+ if (value) loggedScores[name] = true;
47766
+ }
47767
+ const classifications = /* @__PURE__ */ Object.create(null);
47768
+ for (const { name, value } of classificationValues) {
47769
+ if (value !== void 0) classifications[name] = value;
47770
+ }
47771
+ const loggedClassifications = /* @__PURE__ */ Object.create(null);
47772
+ for (const { name, value } of classificationLogValues) {
47773
+ if (value) loggedClassifications[name] = true;
47774
+ }
47775
+ return {
47776
+ ...base,
47777
+ metadata: task?.metadata ?? base.metadata,
47778
+ tags: task ? task.tags : base.tags,
47779
+ taskComplete: task !== void 0,
47780
+ taskLogged: taskLog !== void 0,
47781
+ output: task?.output,
47782
+ rootSpan: taskLog?.rootSpan,
47783
+ scores,
47784
+ loggedScores,
47785
+ classifications,
47786
+ loggedClassifications
47787
+ };
47788
+ }
47789
+ async function readRunState(definition, store, key) {
47790
+ const record = await readJson(store, key);
47791
+ if (!record) return void 0;
47792
+ const plans = plannedBatches(definition, record.runId, record.caseIds);
47793
+ const [cases, batchRecords] = await Promise.all([
47794
+ Promise.all(
47795
+ record.caseIds.map((id) => readCaseRecord(definition, store, key, id))
47796
+ ),
47797
+ Promise.all(
47798
+ plans.map(async ({ id }) => {
47799
+ return readJson(store, batchRecordKey(key, id));
47800
+ })
47801
+ )
47802
+ ]);
47803
+ const batches = batchRecords.filter(
47804
+ (value) => value !== void 0
47805
+ );
47806
+ const { caseIds: _caseIds, ...state } = record;
47807
+ return { ...state, cases, batches };
47808
+ }
47809
+ async function writeRunRecord(store, key, state) {
47810
+ const { cases, batches: _batches, ...record } = state;
47811
+ await writeJson(store, key, {
47812
+ ...record,
47813
+ caseIds: cases.map(({ id }) => id)
47814
+ });
47815
+ }
47816
+ async function writeCaseBaseRecords(store, key, records) {
47817
+ await Promise.all(
47818
+ records.map(
47819
+ ({ id, caseId, trialIndex, datum, metadata, tags }) => writeJson(store, caseRecordKey(key, id, "base"), {
47820
+ id,
47821
+ caseId,
47822
+ trialIndex,
47823
+ datum,
47824
+ metadata,
47825
+ tags
47826
+ })
47827
+ )
47828
+ );
47829
+ }
47830
+ async function writeCaseRecords(store, key, records) {
47831
+ const writes = [];
47832
+ for (const record of records) {
47833
+ if (record.taskComplete) {
47834
+ writes.push(
47835
+ writeJson(store, caseRecordKey(key, record.id, "task"), {
47836
+ output: record.output,
47837
+ metadata: record.metadata,
47838
+ tags: record.tags,
47839
+ taskComplete: true
47840
+ })
47841
+ );
47842
+ }
47843
+ if (record.taskLogged) {
47844
+ writes.push(
47845
+ writeJson(store, caseRecordKey(key, record.id, "task-log"), {
47846
+ rootSpan: record.rootSpan,
47847
+ taskLogged: true
47848
+ })
47849
+ );
47850
+ }
47851
+ for (const [name, value] of Object.entries(record.scores)) {
47852
+ writes.push(
47853
+ writeJson(store, caseRecordKey(key, record.id, "score", name), value)
47854
+ );
47855
+ }
47856
+ for (const name of Object.keys(record.loggedScores)) {
47857
+ writes.push(
47858
+ writeJson(
47859
+ store,
47860
+ caseRecordKey(key, record.id, "score-log", name),
47861
+ true
47862
+ )
47863
+ );
47864
+ }
47865
+ for (const [name, value] of Object.entries(record.classifications)) {
47866
+ writes.push(
47867
+ writeJson(
47868
+ store,
47869
+ caseRecordKey(key, record.id, "classification", name),
47870
+ value
47871
+ )
47872
+ );
47873
+ }
47874
+ for (const name of Object.keys(record.loggedClassifications)) {
47875
+ writes.push(
47876
+ writeJson(
47877
+ store,
47878
+ caseRecordKey(key, record.id, "classification-log", name),
47879
+ true
47880
+ )
47881
+ );
47882
+ }
47883
+ }
47884
+ await Promise.all(writes);
47885
+ }
47886
+ async function writeBatchRecords(store, key, records) {
47887
+ await Promise.all(
47888
+ records.map(
47889
+ (record) => writeJson(store, batchRecordKey(key, record.id), record)
47890
+ )
47891
+ );
47892
+ }
47893
+ function deterministicId(value) {
47894
+ const hex = contentVersion(encoder.encode(value)).padEnd(32, "0").slice(0, 32);
47895
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-5${hex.slice(13, 16)}-a${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
47896
+ }
47897
+ function contentVersion(value) {
47898
+ if (isomorph_default.hash) return isomorph_default.hash(decoder.decode(value));
47899
+ let hash = 2166136261;
47900
+ for (const byte of value) {
47901
+ hash ^= byte;
47902
+ hash = Math.imul(hash, 16777619);
47903
+ }
47904
+ return (hash >>> 0).toString(16).padStart(8, "0");
47905
+ }
47906
+ async function readJson(store, key) {
47907
+ const value = await store.read(key);
47908
+ return value ? JSON.parse(decoder.decode(value)) : void 0;
47909
+ }
47910
+ async function writeJson(store, key, value) {
47911
+ await store.write(key, encoder.encode(stableStringify(value)));
47912
+ }
47913
+ function stableStringify(value) {
47914
+ return JSON.stringify(value, (_key, nested) => {
47915
+ if (nested && typeof nested === "object" && !Array.isArray(nested)) {
47916
+ return Object.fromEntries(
47917
+ Object.entries(nested).sort(
47918
+ ([left], [right]) => left.localeCompare(right)
47919
+ )
47920
+ );
47921
+ }
47922
+ return nested;
47923
+ });
47924
+ }
47925
+ function assertJsonValue(value, label) {
47926
+ try {
47927
+ const serialized = JSON.stringify(value);
47928
+ if (serialized === void 0)
47929
+ throw new Error("value serializes to undefined");
47930
+ return JSON.parse(serialized);
47931
+ } catch (error) {
47932
+ throw new Error(`${label} must be JSON serializable`, { cause: error });
47933
+ }
47934
+ }
47935
+ function resultItemId(value) {
47936
+ if (typeof value !== "object" || value === null || !("id" in value) || typeof value.id !== "string") {
47937
+ throw new Error("Batch results must contain a string id");
47938
+ }
47939
+ return value.id;
47940
+ }
47941
+ function isBatchTask(value) {
47942
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_TASK_KIND;
47943
+ }
47944
+ function isBatchScorer(value) {
47945
+ return typeof value === "object" && value !== null && "kind" in value && value.kind === BATCH_SCORER_KIND;
47946
+ }
47947
+ function asError(error) {
47948
+ return error instanceof Error ? error : new Error(String(error));
47949
+ }
47950
+
45934
47951
  // src/agent-assertions.ts
45935
47952
  function agentAssertionScorer(callback, options = {}) {
45936
47953
  return async (args) => {