braintrust 3.1.0 → 3.2.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 (63) hide show
  1. package/README.md +73 -0
  2. package/dev/dist/index.d.mts +187 -903
  3. package/dev/dist/index.d.ts +187 -903
  4. package/dev/dist/index.js +2942 -323
  5. package/dev/dist/index.mjs +2704 -85
  6. package/dist/auto-instrumentations/bundler/esbuild.cjs +401 -0
  7. package/dist/auto-instrumentations/bundler/esbuild.d.mts +8 -0
  8. package/dist/auto-instrumentations/bundler/esbuild.d.ts +8 -0
  9. package/dist/auto-instrumentations/bundler/esbuild.mjs +10 -0
  10. package/dist/auto-instrumentations/bundler/rollup.cjs +401 -0
  11. package/dist/auto-instrumentations/bundler/rollup.d.mts +8 -0
  12. package/dist/auto-instrumentations/bundler/rollup.d.ts +8 -0
  13. package/dist/auto-instrumentations/bundler/rollup.mjs +10 -0
  14. package/dist/auto-instrumentations/bundler/vite.cjs +401 -0
  15. package/dist/auto-instrumentations/bundler/vite.d.mts +8 -0
  16. package/dist/auto-instrumentations/bundler/vite.d.ts +8 -0
  17. package/dist/auto-instrumentations/bundler/vite.mjs +10 -0
  18. package/dist/auto-instrumentations/bundler/webpack.cjs +401 -0
  19. package/dist/auto-instrumentations/bundler/webpack.d.mts +8 -0
  20. package/dist/auto-instrumentations/bundler/webpack.d.ts +8 -0
  21. package/dist/auto-instrumentations/bundler/webpack.mjs +10 -0
  22. package/dist/auto-instrumentations/chunk-KVX7OFPD.mjs +288 -0
  23. package/dist/auto-instrumentations/chunk-OLOPGWTJ.mjs +89 -0
  24. package/dist/auto-instrumentations/chunk-XDBPUTVE.mjs +22 -0
  25. package/dist/auto-instrumentations/chunk-ZEC7BCL4.mjs +22 -0
  26. package/dist/auto-instrumentations/hook.mjs +378 -0
  27. package/dist/auto-instrumentations/index.cjs +318 -0
  28. package/dist/auto-instrumentations/index.d.mts +69 -0
  29. package/dist/auto-instrumentations/index.d.ts +69 -0
  30. package/dist/auto-instrumentations/index.mjs +14 -0
  31. package/dist/auto-instrumentations/loader/cjs-patch.cjs +116 -0
  32. package/dist/auto-instrumentations/loader/cjs-patch.d.mts +28 -0
  33. package/dist/auto-instrumentations/loader/cjs-patch.d.ts +28 -0
  34. package/dist/auto-instrumentations/loader/cjs-patch.mjs +66 -0
  35. package/dist/auto-instrumentations/loader/esm-hook.mjs +72 -0
  36. package/dist/auto-instrumentations/loader/get-package-version.cjs +46 -0
  37. package/dist/auto-instrumentations/loader/get-package-version.d.mts +7 -0
  38. package/dist/auto-instrumentations/loader/get-package-version.d.ts +7 -0
  39. package/dist/auto-instrumentations/loader/get-package-version.mjs +6 -0
  40. package/dist/auto-instrumentations/plugin-Df3qKIl2.d.mts +22 -0
  41. package/dist/auto-instrumentations/plugin-Df3qKIl2.d.ts +22 -0
  42. package/dist/browser.d.mts +484 -980
  43. package/dist/browser.d.ts +484 -980
  44. package/dist/browser.js +3437 -386
  45. package/dist/browser.mjs +3437 -386
  46. package/dist/cli.js +2890 -228
  47. package/dist/edge-light.d.mts +1 -1
  48. package/dist/edge-light.d.ts +1 -1
  49. package/dist/edge-light.js +3134 -86
  50. package/dist/edge-light.mjs +3134 -86
  51. package/dist/index.d.mts +484 -980
  52. package/dist/index.d.ts +484 -980
  53. package/dist/index.js +3754 -703
  54. package/dist/index.mjs +3615 -564
  55. package/dist/instrumentation/index.d.mts +323 -0
  56. package/dist/instrumentation/index.d.ts +323 -0
  57. package/dist/instrumentation/index.js +9942 -0
  58. package/dist/instrumentation/index.mjs +9902 -0
  59. package/dist/workerd.d.mts +1 -1
  60. package/dist/workerd.d.ts +1 -1
  61. package/dist/workerd.js +3134 -86
  62. package/dist/workerd.mjs +3134 -86
  63. package/package.json +46 -7
@@ -47,7 +47,7 @@ async function currentRepo() {
47
47
  } else {
48
48
  return null;
49
49
  }
50
- } catch (e) {
50
+ } catch {
51
51
  return null;
52
52
  }
53
53
  }
@@ -103,7 +103,7 @@ async function getBaseBranchAncestor(remote = void 0) {
103
103
  `${remoteName}/${baseBranch}`
104
104
  ]);
105
105
  return ancestor.trim();
106
- } catch (e) {
106
+ } catch {
107
107
  return void 0;
108
108
  }
109
109
  }
@@ -130,7 +130,7 @@ async function getPastNAncestors(n = 1e3, remote = void 0) {
130
130
  async function attempt(fn) {
131
131
  try {
132
132
  return await fn();
133
- } catch (e) {
133
+ } catch {
134
134
  return void 0;
135
135
  }
136
136
  }
@@ -1739,6 +1739,7 @@ var ApiKey = z6.object({
1739
1739
  var TriggeredFunctionState = z6.object({
1740
1740
  triggered_xact_id: z6.string(),
1741
1741
  completed_xact_id: z6.union([z6.string(), z6.null()]).optional(),
1742
+ idempotency_key: z6.union([z6.string(), z6.null()]).optional(),
1742
1743
  attempts: z6.number().int().gte(0).optional().default(0),
1743
1744
  scope: z6.union([
1744
1745
  z6.object({ type: z6.literal("span") }),
@@ -1771,7 +1772,8 @@ var AsyncScoringControl = z6.union([
1771
1772
  scope: z6.union([
1772
1773
  z6.object({ type: z6.literal("span") }),
1773
1774
  z6.object({ type: z6.literal("trace") })
1774
- ])
1775
+ ]),
1776
+ idempotency_key: z6.string().optional()
1775
1777
  })
1776
1778
  ).min(1)
1777
1779
  }),
@@ -1816,7 +1818,8 @@ var FunctionTypeEnum = z6.enum([
1816
1818
  "facet",
1817
1819
  "classifier",
1818
1820
  "tag",
1819
- "parameters"
1821
+ "parameters",
1822
+ "sandbox"
1820
1823
  ]);
1821
1824
  var NullableSavedFunctionId = z6.union([
1822
1825
  z6.object({
@@ -1831,66 +1834,14 @@ var NullableSavedFunctionId = z6.union([
1831
1834
  }),
1832
1835
  z6.null()
1833
1836
  ]);
1834
- var TopicMapReport = z6.object({
1835
- version: z6.literal(1),
1836
- created_at: z6.string().optional(),
1837
- settings: z6.object({
1838
- algorithm: z6.enum(["hdbscan", "kmeans", "hierarchical"]),
1839
- dimension_reduction: z6.enum(["umap", "pca", "none"]),
1840
- vector_field: z6.string(),
1841
- embedding_model: z6.string(),
1842
- n_clusters: z6.union([z6.number(), z6.null()]).optional(),
1843
- umap_dimensions: z6.union([z6.number(), z6.null()]).optional(),
1844
- min_cluster_size: z6.union([z6.number(), z6.null()]).optional(),
1845
- min_samples: z6.union([z6.number(), z6.null()]).optional()
1846
- }),
1847
- query_settings: z6.object({
1848
- hierarchy_threshold: z6.union([z6.number(), z6.null()]),
1849
- auto_naming: z6.boolean(),
1850
- skip_cache: z6.boolean(),
1851
- viz_mode: z6.enum(["bar", "scatter"]),
1852
- naming_model: z6.string()
1853
- }).partial(),
1854
- clusters: z6.array(
1855
- z6.object({
1856
- cluster_id: z6.number(),
1857
- parent_cluster_id: z6.union([z6.number(), z6.null()]).optional(),
1858
- topic_id: z6.string(),
1859
- count: z6.number(),
1860
- sample_texts: z6.array(z6.string()),
1861
- samples: z6.array(
1862
- z6.object({
1863
- id: z6.string(),
1864
- text: z6.string(),
1865
- root_span_id: z6.string(),
1866
- span_id: z6.string()
1867
- })
1868
- ),
1869
- name: z6.string().optional(),
1870
- description: z6.string().optional(),
1871
- keywords: z6.array(z6.string()).optional(),
1872
- centroid: z6.array(z6.number()).optional(),
1873
- parent_id: z6.union([z6.number(), z6.null()]).optional(),
1874
- is_leaf: z6.boolean().optional(),
1875
- depth: z6.number().optional()
1876
- })
1877
- ),
1878
- embedding_points: z6.array(
1879
- z6.object({
1880
- x: z6.number(),
1881
- y: z6.number(),
1882
- cluster: z6.number(),
1883
- text: z6.string().optional()
1884
- })
1885
- ).optional()
1886
- });
1887
1837
  var TopicMapData = z6.object({
1888
1838
  type: z6.literal("topic_map"),
1889
1839
  source_facet: z6.string(),
1890
1840
  embedding_model: z6.string(),
1891
- bundle_key: z6.string(),
1892
- distance_threshold: z6.number().optional(),
1893
- report: TopicMapReport.optional()
1841
+ bundle_key: z6.string().optional(),
1842
+ report_key: z6.string().optional(),
1843
+ topic_names: z6.record(z6.string()).optional(),
1844
+ distance_threshold: z6.number().optional()
1894
1845
  });
1895
1846
  var BatchedFacetData = z6.object({
1896
1847
  type: z6.literal("batched_facet"),
@@ -1905,11 +1856,13 @@ var BatchedFacetData = z6.object({
1905
1856
  })
1906
1857
  ),
1907
1858
  topic_maps: z6.record(
1908
- z6.object({
1909
- function_name: z6.string(),
1910
- topic_map_id: z6.string().optional(),
1911
- topic_map_data: TopicMapData
1912
- })
1859
+ z6.array(
1860
+ z6.object({
1861
+ function_name: z6.string(),
1862
+ topic_map_id: z6.string().optional(),
1863
+ topic_map_data: TopicMapData
1864
+ })
1865
+ )
1913
1866
  ).optional()
1914
1867
  });
1915
1868
  var BraintrustModelParams = z6.object({
@@ -2088,9 +2041,20 @@ var CodeBundle = z6.object({
2088
2041
  z6.object({ type: z6.literal("scorer"), index: z6.number().int().gte(0) })
2089
2042
  ])
2090
2043
  }),
2091
- z6.object({ type: z6.literal("function"), index: z6.number().int().gte(0) })
2044
+ z6.object({ type: z6.literal("function"), index: z6.number().int().gte(0) }),
2045
+ z6.object({
2046
+ type: z6.literal("sandbox"),
2047
+ sandbox_spec: z6.union([
2048
+ z6.object({ provider: z6.literal("modal"), snapshot_ref: z6.string() }),
2049
+ z6.object({ provider: z6.literal("lambda") })
2050
+ ]),
2051
+ entrypoints: z6.array(z6.string()).optional(),
2052
+ eval_name: z6.string(),
2053
+ parameters: z6.object({}).partial().passthrough().optional(),
2054
+ evaluator_definition: z6.unknown().optional()
2055
+ })
2092
2056
  ]),
2093
- bundle_id: z6.string(),
2057
+ bundle_id: z6.union([z6.string(), z6.null()]).optional(),
2094
2058
  preview: z6.union([z6.string(), z6.null()]).optional()
2095
2059
  });
2096
2060
  var Dataset = z6.object({
@@ -2192,7 +2156,7 @@ var EnvVar = z6.object({
2192
2156
  used: z6.union([z6.string(), z6.null()]).optional(),
2193
2157
  metadata: z6.union([z6.object({}).partial().passthrough(), z6.null()]).optional(),
2194
2158
  secret_type: z6.union([z6.string(), z6.null()]).optional(),
2195
- secret_category: z6.enum(["env_var", "ai_provider"]).optional().default("env_var")
2159
+ secret_category: z6.enum(["env_var", "ai_provider", "sandbox_provider"]).optional().default("env_var")
2196
2160
  });
2197
2161
  var EvalStatusPageTheme = z6.enum(["light", "dark"]);
2198
2162
  var EvalStatusPageConfig = z6.object({
@@ -2494,7 +2458,8 @@ var FunctionTypeEnumNullish = z6.union([
2494
2458
  "facet",
2495
2459
  "classifier",
2496
2460
  "tag",
2497
- "parameters"
2461
+ "parameters",
2462
+ "sandbox"
2498
2463
  ]),
2499
2464
  z6.null()
2500
2465
  ]);
@@ -2727,7 +2692,8 @@ var FunctionObjectType = z6.enum([
2727
2692
  "preprocessor",
2728
2693
  "facet",
2729
2694
  "classifier",
2730
- "parameters"
2695
+ "parameters",
2696
+ "sandbox"
2731
2697
  ]);
2732
2698
  var FunctionOutputType = z6.enum([
2733
2699
  "completion",
@@ -2863,6 +2829,7 @@ var Organization = z6.object({
2863
2829
  name: z6.string(),
2864
2830
  api_url: z6.union([z6.string(), z6.null()]).optional(),
2865
2831
  is_universal_api: z6.union([z6.boolean(), z6.null()]).optional(),
2832
+ is_dataplane_private: z6.union([z6.boolean(), z6.null()]).optional(),
2866
2833
  proxy_url: z6.union([z6.string(), z6.null()]).optional(),
2867
2834
  realtime_url: z6.union([z6.string(), z6.null()]).optional(),
2868
2835
  created: z6.union([z6.string(), z6.null()]).optional(),
@@ -2887,7 +2854,7 @@ var ProjectSettings = z6.union([
2887
2854
  z6.array(
2888
2855
  z6.object({
2889
2856
  url: z6.string(),
2890
- name: z6.string(),
2857
+ name: z6.union([z6.string(), z6.null()]).optional(),
2891
2858
  description: z6.union([z6.string(), z6.null()]).optional()
2892
2859
  })
2893
2860
  ),
@@ -2913,6 +2880,25 @@ var RetentionObjectType = z6.enum([
2913
2880
  "experiment",
2914
2881
  "dataset"
2915
2882
  ]);
2883
+ var TopicMapFunctionAutomation = z6.object({
2884
+ function: SavedFunctionId.and(z6.unknown()),
2885
+ btql_filter: z6.union([z6.string(), z6.null()]).optional()
2886
+ });
2887
+ var TopicAutomationDataScope = z6.union([
2888
+ z6.object({ type: z6.literal("project_logs") }),
2889
+ z6.object({ type: z6.literal("project_experiments") }),
2890
+ z6.object({ type: z6.literal("experiment"), experiment_id: z6.string() }),
2891
+ z6.null()
2892
+ ]);
2893
+ var TopicAutomationConfig = z6.object({
2894
+ event_type: z6.literal("topic"),
2895
+ sampling_rate: z6.number().gte(0).lte(1),
2896
+ facet_functions: z6.array(SavedFunctionId),
2897
+ topic_map_functions: z6.array(TopicMapFunctionAutomation),
2898
+ scope: z6.union([SpanScope, TraceScope, GroupScope, z6.null()]).optional(),
2899
+ data_scope: TopicAutomationDataScope.optional(),
2900
+ btql_filter: z6.union([z6.string(), z6.null()]).optional()
2901
+ });
2916
2902
  var ProjectAutomation = z6.object({
2917
2903
  id: z6.string().uuid(),
2918
2904
  project_id: z6.string().uuid(),
@@ -2969,7 +2955,8 @@ var ProjectAutomation = z6.object({
2969
2955
  message_template: z6.string().optional()
2970
2956
  })
2971
2957
  ])
2972
- })
2958
+ }),
2959
+ TopicAutomationConfig
2973
2960
  ])
2974
2961
  });
2975
2962
  var ProjectLogsEvent = z6.object({
@@ -3159,6 +3146,8 @@ var RunEval = z6.object({
3159
3146
  }),
3160
3147
  z6.object({ data: z6.array(z6.unknown()) })
3161
3148
  ]),
3149
+ name: z6.string().optional(),
3150
+ parameters: z6.object({}).partial().passthrough().optional(),
3162
3151
  task: FunctionId.and(z6.unknown()),
3163
3152
  scores: z6.array(FunctionId),
3164
3153
  experiment_name: z6.string().optional(),
@@ -4838,7 +4827,7 @@ var HTTPConnection = class _HTTPConnection {
4838
4827
  try {
4839
4828
  const resp = await this.get("ping");
4840
4829
  return resp.status === 200;
4841
- } catch (e) {
4830
+ } catch {
4842
4831
  return false;
4843
4832
  }
4844
4833
  }
@@ -5242,9 +5231,9 @@ function logFeedbackImpl(state, parentObjectType, parentObjectId, {
5242
5231
  expected,
5243
5232
  tags
5244
5233
  });
5245
- let { metadata, ...updateEvent } = deepCopyEvent(validatedEvent);
5246
- updateEvent = Object.fromEntries(
5247
- Object.entries(updateEvent).filter(([_, v]) => !isEmpty2(v))
5234
+ const { metadata, ...rawUpdateEvent } = deepCopyEvent(validatedEvent);
5235
+ const updateEvent = Object.fromEntries(
5236
+ Object.entries(rawUpdateEvent).filter(([_, v]) => !isEmpty2(v))
5248
5237
  );
5249
5238
  const parentIds = async () => new SpanComponentsV3({
5250
5239
  object_type: parentObjectType,
@@ -6811,7 +6800,8 @@ function validateAndSanitizeExperimentLogPartialArgs(event) {
6811
6800
  if (Array.isArray(event.scores)) {
6812
6801
  throw new Error("scores must be an object, not an array");
6813
6802
  }
6814
- for (let [name, score] of Object.entries(event.scores)) {
6803
+ for (const [name, rawScore] of Object.entries(event.scores)) {
6804
+ let score = rawScore;
6815
6805
  if (typeof name !== "string") {
6816
6806
  throw new Error("score names must be strings");
6817
6807
  }
@@ -7253,7 +7243,11 @@ var Experiment2 = class extends ObjectFetcher {
7253
7243
  * @returns A summary of the experiment, including the scores (compared to the closest reference experiment) and metadata.
7254
7244
  */
7255
7245
  async summarize(options = {}) {
7256
- let { summarizeScores = true, comparisonExperimentId = void 0 } = options || {};
7246
+ const {
7247
+ summarizeScores = true,
7248
+ comparisonExperimentId: comparisonExperimentIdOpt
7249
+ } = options || {};
7250
+ let comparisonExperimentId = comparisonExperimentIdOpt;
7257
7251
  const state = await this.getState();
7258
7252
  const projectUrl = `${state.appPublicUrl}/app/${encodeURIComponent(
7259
7253
  state.orgName
@@ -8437,12 +8431,2636 @@ var RemoteEvalParameters = class {
8437
8431
  return true;
8438
8432
  }
8439
8433
  static isParameters(x) {
8440
- return typeof x === "object" && x !== null && "__braintrust_parameters_marker" in x && // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
8441
- x.__braintrust_parameters_marker === true;
8434
+ return typeof x === "object" && x !== null && "__braintrust_parameters_marker" in x && x.__braintrust_parameters_marker === true;
8442
8435
  }
8443
8436
  };
8444
8437
  var TEST_API_KEY = "___TEST_API_KEY__THIS_IS_NOT_REAL___";
8445
8438
 
8439
+ // src/instrumentation/core/plugin.ts
8440
+ import { tracingChannel } from "dc-browser";
8441
+
8442
+ // src/instrumentation/core/stream-patcher.ts
8443
+ function isAsyncIterable(value) {
8444
+ return value !== null && typeof value === "object" && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
8445
+ }
8446
+ function patchStreamIfNeeded(stream, options) {
8447
+ if (!isAsyncIterable(stream)) {
8448
+ return stream;
8449
+ }
8450
+ if (Object.isFrozen(stream) || Object.isSealed(stream)) {
8451
+ console.warn(
8452
+ "Cannot patch frozen/sealed stream. Stream output will not be collected."
8453
+ );
8454
+ return stream;
8455
+ }
8456
+ const originalIteratorFn = stream[Symbol.asyncIterator];
8457
+ if (originalIteratorFn.__braintrust_patched) {
8458
+ return stream;
8459
+ }
8460
+ try {
8461
+ const patchedIteratorFn = function() {
8462
+ const iterator = originalIteratorFn.call(this);
8463
+ const originalNext = iterator.next.bind(iterator);
8464
+ const chunks = [];
8465
+ let completed = false;
8466
+ iterator.next = async function(...args) {
8467
+ try {
8468
+ const result = await originalNext(...args);
8469
+ if (result.done) {
8470
+ if (!completed) {
8471
+ completed = true;
8472
+ try {
8473
+ options.onComplete(chunks);
8474
+ } catch (error) {
8475
+ console.error("Error in stream onComplete handler:", error);
8476
+ }
8477
+ }
8478
+ } else {
8479
+ const chunk = result.value;
8480
+ const shouldCollect = options.shouldCollect ? options.shouldCollect(chunk) : true;
8481
+ if (shouldCollect) {
8482
+ chunks.push(chunk);
8483
+ if (options.onChunk) {
8484
+ try {
8485
+ options.onChunk(chunk);
8486
+ } catch (error) {
8487
+ console.error("Error in stream onChunk handler:", error);
8488
+ }
8489
+ }
8490
+ }
8491
+ }
8492
+ return result;
8493
+ } catch (error) {
8494
+ if (!completed) {
8495
+ completed = true;
8496
+ if (options.onError) {
8497
+ try {
8498
+ options.onError(error, chunks);
8499
+ } catch (handlerError) {
8500
+ console.error("Error in stream onError handler:", handlerError);
8501
+ }
8502
+ }
8503
+ }
8504
+ throw error;
8505
+ }
8506
+ };
8507
+ if (iterator.return) {
8508
+ const originalReturn = iterator.return.bind(iterator);
8509
+ iterator.return = async function(...args) {
8510
+ if (!completed) {
8511
+ completed = true;
8512
+ try {
8513
+ options.onComplete(chunks);
8514
+ } catch (error) {
8515
+ console.error("Error in stream onComplete handler:", error);
8516
+ }
8517
+ }
8518
+ return originalReturn(...args);
8519
+ };
8520
+ }
8521
+ if (iterator.throw) {
8522
+ const originalThrow = iterator.throw.bind(iterator);
8523
+ iterator.throw = async function(...args) {
8524
+ if (!completed) {
8525
+ completed = true;
8526
+ const error = args[0];
8527
+ if (options.onError) {
8528
+ try {
8529
+ options.onError(error, chunks);
8530
+ } catch (handlerError) {
8531
+ console.error("Error in stream onError handler:", handlerError);
8532
+ }
8533
+ }
8534
+ }
8535
+ return originalThrow(...args);
8536
+ };
8537
+ }
8538
+ return iterator;
8539
+ };
8540
+ patchedIteratorFn.__braintrust_patched = true;
8541
+ stream[Symbol.asyncIterator] = patchedIteratorFn;
8542
+ return stream;
8543
+ } catch (error) {
8544
+ console.warn("Failed to patch stream:", error);
8545
+ return stream;
8546
+ }
8547
+ }
8548
+
8549
+ // src/instrumentation/core/plugin.ts
8550
+ var BasePlugin = class {
8551
+ enabled = false;
8552
+ unsubscribers = [];
8553
+ /**
8554
+ * Enables the plugin. Must be called before the plugin will receive events.
8555
+ */
8556
+ enable() {
8557
+ if (this.enabled) {
8558
+ return;
8559
+ }
8560
+ this.enabled = true;
8561
+ this.onEnable();
8562
+ }
8563
+ /**
8564
+ * Disables the plugin. After this, the plugin will no longer receive events.
8565
+ */
8566
+ disable() {
8567
+ if (!this.enabled) {
8568
+ return;
8569
+ }
8570
+ this.enabled = false;
8571
+ this.onDisable();
8572
+ }
8573
+ /**
8574
+ * Helper to subscribe to a channel with raw handlers.
8575
+ *
8576
+ * @param channelName - The channel name to subscribe to
8577
+ * @param handlers - Event handlers
8578
+ */
8579
+ subscribe(channelName, handlers) {
8580
+ const channel = tracingChannel(channelName);
8581
+ channel.subscribe(handlers);
8582
+ }
8583
+ /**
8584
+ * Subscribe to a channel for async methods (non-streaming).
8585
+ * Creates a span and logs input/output/metrics.
8586
+ */
8587
+ subscribeToChannel(channelName, config) {
8588
+ const channel = tracingChannel(channelName);
8589
+ const spans = /* @__PURE__ */ new WeakMap();
8590
+ const handlers = {
8591
+ start: (event) => {
8592
+ const span = startSpan({
8593
+ name: config.name,
8594
+ spanAttributes: {
8595
+ type: config.type
8596
+ }
8597
+ });
8598
+ const startTime = getCurrentUnixTimestamp();
8599
+ spans.set(event, { span, startTime });
8600
+ try {
8601
+ const { input, metadata } = config.extractInput(event.arguments);
8602
+ span.log({
8603
+ input,
8604
+ metadata
8605
+ });
8606
+ } catch (error) {
8607
+ console.error(`Error extracting input for ${channelName}:`, error);
8608
+ }
8609
+ },
8610
+ asyncEnd: (event) => {
8611
+ const spanData = spans.get(event);
8612
+ if (!spanData) {
8613
+ return;
8614
+ }
8615
+ const { span, startTime } = spanData;
8616
+ try {
8617
+ const output = config.extractOutput(event.result);
8618
+ const metrics = config.extractMetrics(event.result, startTime);
8619
+ span.log({
8620
+ output,
8621
+ metrics
8622
+ });
8623
+ } catch (error) {
8624
+ console.error(`Error extracting output for ${channelName}:`, error);
8625
+ } finally {
8626
+ span.end();
8627
+ spans.delete(event);
8628
+ }
8629
+ },
8630
+ error: (event) => {
8631
+ const spanData = spans.get(event);
8632
+ if (!spanData) {
8633
+ return;
8634
+ }
8635
+ const { span } = spanData;
8636
+ span.log({
8637
+ error: event.error.message
8638
+ });
8639
+ span.end();
8640
+ spans.delete(event);
8641
+ }
8642
+ };
8643
+ channel.subscribe(handlers);
8644
+ this.unsubscribers.push(() => {
8645
+ channel.unsubscribe(handlers);
8646
+ });
8647
+ }
8648
+ /**
8649
+ * Subscribe to a channel for async methods that may return streams.
8650
+ * Handles both streaming and non-streaming responses.
8651
+ */
8652
+ subscribeToStreamingChannel(channelName, config) {
8653
+ const channel = tracingChannel(channelName);
8654
+ const spans = /* @__PURE__ */ new WeakMap();
8655
+ const handlers = {
8656
+ start: (event) => {
8657
+ const span = startSpan({
8658
+ name: config.name,
8659
+ spanAttributes: {
8660
+ type: config.type
8661
+ }
8662
+ });
8663
+ const startTime = getCurrentUnixTimestamp();
8664
+ spans.set(event, { span, startTime });
8665
+ try {
8666
+ const { input, metadata } = config.extractInput(event.arguments);
8667
+ span.log({
8668
+ input,
8669
+ metadata
8670
+ });
8671
+ } catch (error) {
8672
+ console.error(`Error extracting input for ${channelName}:`, error);
8673
+ }
8674
+ },
8675
+ asyncEnd: (event) => {
8676
+ const spanData = spans.get(event);
8677
+ if (!spanData) {
8678
+ return;
8679
+ }
8680
+ const { span, startTime } = spanData;
8681
+ if (isAsyncIterable(event.result)) {
8682
+ patchStreamIfNeeded(event.result, {
8683
+ onComplete: (chunks) => {
8684
+ try {
8685
+ let output;
8686
+ let metrics;
8687
+ if (config.aggregateChunks) {
8688
+ const aggregated = config.aggregateChunks(chunks);
8689
+ output = aggregated.output;
8690
+ metrics = aggregated.metrics;
8691
+ } else {
8692
+ output = config.extractOutput(chunks);
8693
+ metrics = config.extractMetrics(chunks, startTime);
8694
+ }
8695
+ if (!metrics.time_to_first_token && chunks.length > 0) {
8696
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
8697
+ }
8698
+ span.log({
8699
+ output,
8700
+ metrics
8701
+ });
8702
+ } catch (error) {
8703
+ console.error(
8704
+ `Error extracting output for ${channelName}:`,
8705
+ error
8706
+ );
8707
+ } finally {
8708
+ span.end();
8709
+ }
8710
+ },
8711
+ onError: (error) => {
8712
+ span.log({
8713
+ error: error.message
8714
+ });
8715
+ span.end();
8716
+ }
8717
+ });
8718
+ } else {
8719
+ try {
8720
+ const output = config.extractOutput(event.result);
8721
+ const metrics = config.extractMetrics(event.result, startTime);
8722
+ span.log({
8723
+ output,
8724
+ metrics
8725
+ });
8726
+ } catch (error) {
8727
+ console.error(`Error extracting output for ${channelName}:`, error);
8728
+ } finally {
8729
+ span.end();
8730
+ spans.delete(event);
8731
+ }
8732
+ }
8733
+ },
8734
+ error: (event) => {
8735
+ const spanData = spans.get(event);
8736
+ if (!spanData) {
8737
+ return;
8738
+ }
8739
+ const { span } = spanData;
8740
+ span.log({
8741
+ error: event.error.message
8742
+ });
8743
+ span.end();
8744
+ spans.delete(event);
8745
+ }
8746
+ };
8747
+ channel.subscribe(handlers);
8748
+ this.unsubscribers.push(() => {
8749
+ channel.unsubscribe(handlers);
8750
+ });
8751
+ }
8752
+ /**
8753
+ * Subscribe to a channel for sync methods that return event-based streams.
8754
+ * Used for methods like beta.chat.completions.stream() and responses.stream().
8755
+ */
8756
+ subscribeToSyncStreamChannel(channelName, config) {
8757
+ const channel = tracingChannel(channelName);
8758
+ const spans = /* @__PURE__ */ new WeakMap();
8759
+ const handlers = {
8760
+ start: (event) => {
8761
+ const span = startSpan({
8762
+ name: config.name,
8763
+ spanAttributes: {
8764
+ type: config.type
8765
+ }
8766
+ });
8767
+ const startTime = getCurrentUnixTimestamp();
8768
+ spans.set(event, { span, startTime });
8769
+ try {
8770
+ const { input, metadata } = config.extractInput(event.arguments);
8771
+ span.log({
8772
+ input,
8773
+ metadata
8774
+ });
8775
+ } catch (error) {
8776
+ console.error(`Error extracting input for ${channelName}:`, error);
8777
+ }
8778
+ },
8779
+ end: (event) => {
8780
+ const spanData = spans.get(event);
8781
+ if (!spanData) {
8782
+ return;
8783
+ }
8784
+ const { span, startTime } = spanData;
8785
+ const stream = event.result;
8786
+ if (!stream || typeof stream.on !== "function") {
8787
+ span.end();
8788
+ spans.delete(event);
8789
+ return;
8790
+ }
8791
+ let first = true;
8792
+ stream.on("chunk", (chunk) => {
8793
+ if (first) {
8794
+ const now2 = getCurrentUnixTimestamp();
8795
+ span.log({
8796
+ metrics: {
8797
+ time_to_first_token: now2 - startTime
8798
+ }
8799
+ });
8800
+ first = false;
8801
+ }
8802
+ });
8803
+ stream.on("chatCompletion", (completion) => {
8804
+ try {
8805
+ span.log({
8806
+ output: completion.choices
8807
+ });
8808
+ } catch (error) {
8809
+ console.error(
8810
+ `Error extracting chatCompletion for ${channelName}:`,
8811
+ error
8812
+ );
8813
+ }
8814
+ });
8815
+ stream.on("event", (streamEvent) => {
8816
+ if (config.extractFromEvent) {
8817
+ try {
8818
+ if (first) {
8819
+ const now2 = getCurrentUnixTimestamp();
8820
+ span.log({
8821
+ metrics: {
8822
+ time_to_first_token: now2 - startTime
8823
+ }
8824
+ });
8825
+ first = false;
8826
+ }
8827
+ const extracted = config.extractFromEvent(streamEvent);
8828
+ if (extracted && Object.keys(extracted).length > 0) {
8829
+ span.log(extracted);
8830
+ }
8831
+ } catch (error) {
8832
+ console.error(
8833
+ `Error extracting event for ${channelName}:`,
8834
+ error
8835
+ );
8836
+ }
8837
+ }
8838
+ });
8839
+ stream.on("end", () => {
8840
+ span.end();
8841
+ spans.delete(event);
8842
+ });
8843
+ },
8844
+ error: (event) => {
8845
+ const spanData = spans.get(event);
8846
+ if (!spanData) {
8847
+ return;
8848
+ }
8849
+ const { span } = spanData;
8850
+ span.log({
8851
+ error: event.error.message
8852
+ });
8853
+ span.end();
8854
+ spans.delete(event);
8855
+ }
8856
+ };
8857
+ channel.subscribe(handlers);
8858
+ this.unsubscribers.push(() => {
8859
+ channel.unsubscribe(handlers);
8860
+ });
8861
+ }
8862
+ };
8863
+
8864
+ // src/wrappers/attachment-utils.ts
8865
+ function getExtensionFromMediaType(mediaType) {
8866
+ const extensionMap = {
8867
+ "image/png": "png",
8868
+ "image/jpeg": "jpg",
8869
+ "image/gif": "gif",
8870
+ "image/webp": "webp",
8871
+ "image/svg+xml": "svg",
8872
+ "audio/mpeg": "mp3",
8873
+ "audio/wav": "wav",
8874
+ "audio/ogg": "ogg",
8875
+ "video/mp4": "mp4",
8876
+ "video/webm": "webm",
8877
+ "application/pdf": "pdf",
8878
+ "application/json": "json",
8879
+ "text/plain": "txt",
8880
+ "text/html": "html",
8881
+ "text/csv": "csv"
8882
+ };
8883
+ return extensionMap[mediaType] || "bin";
8884
+ }
8885
+ function convertDataToBlob(data, mediaType) {
8886
+ try {
8887
+ if (typeof data === "string") {
8888
+ if (data.startsWith("data:")) {
8889
+ const base64Match = data.match(/^data:[^;]+;base64,(.+)$/);
8890
+ if (base64Match) {
8891
+ const base64 = base64Match[1];
8892
+ const binaryString = atob(base64);
8893
+ const bytes = new Uint8Array(binaryString.length);
8894
+ for (let i = 0; i < binaryString.length; i++) {
8895
+ bytes[i] = binaryString.charCodeAt(i);
8896
+ }
8897
+ return new Blob([bytes], { type: mediaType });
8898
+ }
8899
+ } else if (data.startsWith("http://") || data.startsWith("https://")) {
8900
+ return null;
8901
+ } else {
8902
+ const binaryString = atob(data);
8903
+ const bytes = new Uint8Array(binaryString.length);
8904
+ for (let i = 0; i < binaryString.length; i++) {
8905
+ bytes[i] = binaryString.charCodeAt(i);
8906
+ }
8907
+ return new Blob([bytes], { type: mediaType });
8908
+ }
8909
+ } else if (data instanceof Uint8Array) {
8910
+ return new Blob([data], { type: mediaType });
8911
+ } else if (data instanceof ArrayBuffer) {
8912
+ return new Blob([data], { type: mediaType });
8913
+ } else if (typeof Buffer !== "undefined" && data instanceof Buffer) {
8914
+ return new Blob([data], { type: mediaType });
8915
+ }
8916
+ } catch {
8917
+ return null;
8918
+ }
8919
+ return null;
8920
+ }
8921
+ function processInputAttachments(input) {
8922
+ if (!input) {
8923
+ return input;
8924
+ }
8925
+ let attachmentIndex = 0;
8926
+ const processContentPart = (part) => {
8927
+ if (!part || typeof part !== "object") {
8928
+ return part;
8929
+ }
8930
+ if (part.type === "image" && part.image) {
8931
+ let mediaType = "image/png";
8932
+ if (typeof part.image === "string" && part.image.startsWith("data:")) {
8933
+ const mediaTypeMatch = part.image.match(/^data:([^;]+);/);
8934
+ if (mediaTypeMatch) {
8935
+ mediaType = mediaTypeMatch[1];
8936
+ }
8937
+ } else if (part.mediaType) {
8938
+ mediaType = part.mediaType;
8939
+ }
8940
+ const blob = convertDataToBlob(part.image, mediaType);
8941
+ if (blob) {
8942
+ const filename = `input_image_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8943
+ attachmentIndex++;
8944
+ const attachment = new Attachment({
8945
+ data: blob,
8946
+ filename,
8947
+ contentType: mediaType
8948
+ });
8949
+ return {
8950
+ ...part,
8951
+ image: attachment
8952
+ };
8953
+ }
8954
+ }
8955
+ if (part.type === "file" && part.data) {
8956
+ const mediaType = part.mediaType || "application/octet-stream";
8957
+ const blob = convertDataToBlob(part.data, mediaType);
8958
+ if (blob) {
8959
+ const filename = part.filename || `input_file_${attachmentIndex}.${getExtensionFromMediaType(mediaType)}`;
8960
+ attachmentIndex++;
8961
+ const attachment = new Attachment({
8962
+ data: blob,
8963
+ filename,
8964
+ contentType: mediaType
8965
+ });
8966
+ return {
8967
+ ...part,
8968
+ data: attachment
8969
+ };
8970
+ }
8971
+ }
8972
+ return part;
8973
+ };
8974
+ const processMessage = (message) => {
8975
+ if (!message || typeof message !== "object") {
8976
+ return message;
8977
+ }
8978
+ if (Array.isArray(message.content)) {
8979
+ return {
8980
+ ...message,
8981
+ content: message.content.map(processContentPart)
8982
+ };
8983
+ }
8984
+ return message;
8985
+ };
8986
+ if (Array.isArray(input)) {
8987
+ return input.map(processMessage);
8988
+ } else if (typeof input === "object" && input.content) {
8989
+ return processMessage(input);
8990
+ }
8991
+ return input;
8992
+ }
8993
+
8994
+ // src/instrumentation/plugins/openai-plugin.ts
8995
+ var OpenAIPlugin = class extends BasePlugin {
8996
+ constructor() {
8997
+ super();
8998
+ }
8999
+ onEnable() {
9000
+ this.subscribeToStreamingChannel(
9001
+ "orchestrion:openai:chat.completions.create",
9002
+ {
9003
+ name: "Chat Completion",
9004
+ type: "llm" /* LLM */,
9005
+ extractInput: (args) => {
9006
+ const params = args[0] || {};
9007
+ const { messages, ...metadata } = params;
9008
+ return {
9009
+ input: processInputAttachments(messages),
9010
+ metadata: { ...metadata, provider: "openai" }
9011
+ };
9012
+ },
9013
+ extractOutput: (result) => {
9014
+ return result?.choices;
9015
+ },
9016
+ extractMetrics: (result, startTime) => {
9017
+ const metrics = parseMetricsFromUsage(result?.usage);
9018
+ if (startTime) {
9019
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9020
+ }
9021
+ return metrics;
9022
+ },
9023
+ aggregateChunks: aggregateChatCompletionChunks
9024
+ }
9025
+ );
9026
+ this.subscribeToChannel("orchestrion:openai:embeddings.create", {
9027
+ name: "Embedding",
9028
+ type: "llm" /* LLM */,
9029
+ extractInput: (args) => {
9030
+ const params = args[0] || {};
9031
+ const { input, ...metadata } = params;
9032
+ return {
9033
+ input,
9034
+ metadata: { ...metadata, provider: "openai" }
9035
+ };
9036
+ },
9037
+ extractOutput: (result) => {
9038
+ return result?.data?.map((d) => d.embedding);
9039
+ },
9040
+ extractMetrics: (result) => {
9041
+ return parseMetricsFromUsage(result?.usage);
9042
+ }
9043
+ });
9044
+ this.subscribeToStreamingChannel(
9045
+ "orchestrion:openai:beta.chat.completions.parse",
9046
+ {
9047
+ name: "Chat Completion",
9048
+ type: "llm" /* LLM */,
9049
+ extractInput: (args) => {
9050
+ const params = args[0] || {};
9051
+ const { messages, ...metadata } = params;
9052
+ return {
9053
+ input: processInputAttachments(messages),
9054
+ metadata: { ...metadata, provider: "openai" }
9055
+ };
9056
+ },
9057
+ extractOutput: (result) => {
9058
+ return result?.choices;
9059
+ },
9060
+ extractMetrics: (result, startTime) => {
9061
+ const metrics = parseMetricsFromUsage(result?.usage);
9062
+ if (startTime) {
9063
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9064
+ }
9065
+ return metrics;
9066
+ },
9067
+ aggregateChunks: aggregateChatCompletionChunks
9068
+ }
9069
+ );
9070
+ this.subscribeToSyncStreamChannel(
9071
+ "orchestrion:openai:beta.chat.completions.stream",
9072
+ {
9073
+ name: "Chat Completion",
9074
+ type: "llm" /* LLM */,
9075
+ extractInput: (args) => {
9076
+ const params = args[0] || {};
9077
+ const { messages, ...metadata } = params;
9078
+ return {
9079
+ input: processInputAttachments(messages),
9080
+ metadata: { ...metadata, provider: "openai" }
9081
+ };
9082
+ }
9083
+ }
9084
+ );
9085
+ this.subscribeToChannel("orchestrion:openai:moderations.create", {
9086
+ name: "Moderation",
9087
+ type: "llm" /* LLM */,
9088
+ extractInput: (args) => {
9089
+ const params = args[0] || {};
9090
+ const { input, ...metadata } = params;
9091
+ return {
9092
+ input,
9093
+ metadata: { ...metadata, provider: "openai" }
9094
+ };
9095
+ },
9096
+ extractOutput: (result) => {
9097
+ return result?.results;
9098
+ },
9099
+ extractMetrics: () => {
9100
+ return {};
9101
+ }
9102
+ });
9103
+ this.subscribeToStreamingChannel("orchestrion:openai:responses.create", {
9104
+ name: "openai.responses.create",
9105
+ type: "llm" /* LLM */,
9106
+ extractInput: (args) => {
9107
+ const params = args[0] || {};
9108
+ const { input, ...metadata } = params;
9109
+ return {
9110
+ input: processInputAttachments(input),
9111
+ metadata: { ...metadata, provider: "openai" }
9112
+ };
9113
+ },
9114
+ extractOutput: (result) => {
9115
+ return processImagesInOutput(result?.output);
9116
+ },
9117
+ extractMetrics: (result, startTime) => {
9118
+ const metrics = parseMetricsFromUsage(result?.usage);
9119
+ if (startTime) {
9120
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9121
+ }
9122
+ return metrics;
9123
+ }
9124
+ });
9125
+ this.subscribeToSyncStreamChannel("orchestrion:openai:responses.stream", {
9126
+ name: "openai.responses.stream",
9127
+ type: "llm" /* LLM */,
9128
+ extractInput: (args) => {
9129
+ const params = args[0] || {};
9130
+ const { input, ...metadata } = params;
9131
+ return {
9132
+ input: processInputAttachments(input),
9133
+ metadata: { ...metadata, provider: "openai" }
9134
+ };
9135
+ },
9136
+ extractFromEvent: (event) => {
9137
+ if (!event || !event.type || !event.response) {
9138
+ return {};
9139
+ }
9140
+ const response = event.response;
9141
+ if (event.type === "response.completed") {
9142
+ const data = {};
9143
+ if (response?.output !== void 0) {
9144
+ data.output = processImagesInOutput(response.output);
9145
+ }
9146
+ if (response) {
9147
+ const { usage: _usage, output: _output, ...metadata } = response;
9148
+ if (Object.keys(metadata).length > 0) {
9149
+ data.metadata = metadata;
9150
+ }
9151
+ }
9152
+ data.metrics = parseMetricsFromUsage(response?.usage);
9153
+ return data;
9154
+ }
9155
+ return {};
9156
+ }
9157
+ });
9158
+ this.subscribeToStreamingChannel("orchestrion:openai:responses.parse", {
9159
+ name: "openai.responses.parse",
9160
+ type: "llm" /* LLM */,
9161
+ extractInput: (args) => {
9162
+ const params = args[0] || {};
9163
+ const { input, ...metadata } = params;
9164
+ return {
9165
+ input: processInputAttachments(input),
9166
+ metadata: { ...metadata, provider: "openai" }
9167
+ };
9168
+ },
9169
+ extractOutput: (result) => {
9170
+ return processImagesInOutput(result?.output);
9171
+ },
9172
+ extractMetrics: (result, startTime) => {
9173
+ const metrics = parseMetricsFromUsage(result?.usage);
9174
+ if (startTime) {
9175
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9176
+ }
9177
+ return metrics;
9178
+ }
9179
+ });
9180
+ }
9181
+ onDisable() {
9182
+ }
9183
+ };
9184
+ var TOKEN_NAME_MAP = {
9185
+ input_tokens: "prompt_tokens",
9186
+ output_tokens: "completion_tokens",
9187
+ total_tokens: "tokens"
9188
+ };
9189
+ var TOKEN_PREFIX_MAP = {
9190
+ input: "prompt",
9191
+ output: "completion"
9192
+ };
9193
+ function parseMetricsFromUsage(usage) {
9194
+ if (!usage) {
9195
+ return {};
9196
+ }
9197
+ const metrics = {};
9198
+ for (const [oai_name, value] of Object.entries(usage)) {
9199
+ if (typeof value === "number") {
9200
+ const metricName = TOKEN_NAME_MAP[oai_name] || oai_name;
9201
+ metrics[metricName] = value;
9202
+ } else if (oai_name.endsWith("_tokens_details")) {
9203
+ if (!isObject(value)) {
9204
+ continue;
9205
+ }
9206
+ const rawPrefix = oai_name.slice(0, -"_tokens_details".length);
9207
+ const prefix = TOKEN_PREFIX_MAP[rawPrefix] || rawPrefix;
9208
+ for (const [key, n] of Object.entries(value)) {
9209
+ if (typeof n !== "number") {
9210
+ continue;
9211
+ }
9212
+ const metricName = `${prefix}_${key}`;
9213
+ metrics[metricName] = n;
9214
+ }
9215
+ }
9216
+ }
9217
+ return metrics;
9218
+ }
9219
+ function processImagesInOutput(output) {
9220
+ if (Array.isArray(output)) {
9221
+ return output.map(processImagesInOutput);
9222
+ }
9223
+ if (isObject(output)) {
9224
+ if (output.type === "image_generation_call" && output.result && typeof output.result === "string") {
9225
+ const fileExtension = output.output_format || "png";
9226
+ const contentType = `image/${fileExtension}`;
9227
+ const baseFilename = output.revised_prompt && typeof output.revised_prompt === "string" ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") : "generated_image";
9228
+ const filename = `${baseFilename}.${fileExtension}`;
9229
+ const binaryString = atob(output.result);
9230
+ const bytes = new Uint8Array(binaryString.length);
9231
+ for (let i = 0; i < binaryString.length; i++) {
9232
+ bytes[i] = binaryString.charCodeAt(i);
9233
+ }
9234
+ const blob = new Blob([bytes], { type: contentType });
9235
+ const attachment = new Attachment({
9236
+ data: blob,
9237
+ filename,
9238
+ contentType
9239
+ });
9240
+ return {
9241
+ ...output,
9242
+ result: attachment
9243
+ };
9244
+ }
9245
+ }
9246
+ return output;
9247
+ }
9248
+ function aggregateChatCompletionChunks(chunks) {
9249
+ let role = void 0;
9250
+ let content = void 0;
9251
+ let tool_calls = void 0;
9252
+ let finish_reason = void 0;
9253
+ let metrics = {};
9254
+ for (const chunk of chunks) {
9255
+ if (chunk.usage) {
9256
+ metrics = {
9257
+ ...metrics,
9258
+ ...parseMetricsFromUsage(chunk.usage)
9259
+ };
9260
+ }
9261
+ const delta = chunk.choices?.[0]?.delta;
9262
+ if (!delta) {
9263
+ continue;
9264
+ }
9265
+ if (!role && delta.role) {
9266
+ role = delta.role;
9267
+ }
9268
+ if (delta.finish_reason) {
9269
+ finish_reason = delta.finish_reason;
9270
+ }
9271
+ if (delta.content) {
9272
+ content = (content || "") + delta.content;
9273
+ }
9274
+ if (delta.tool_calls) {
9275
+ const toolDelta = delta.tool_calls[0];
9276
+ if (!tool_calls || toolDelta.id && tool_calls[tool_calls.length - 1].id !== toolDelta.id) {
9277
+ tool_calls = [
9278
+ ...tool_calls || [],
9279
+ {
9280
+ id: toolDelta.id,
9281
+ type: toolDelta.type,
9282
+ function: toolDelta.function
9283
+ }
9284
+ ];
9285
+ } else {
9286
+ tool_calls[tool_calls.length - 1].function.arguments += toolDelta.function.arguments;
9287
+ }
9288
+ }
9289
+ }
9290
+ return {
9291
+ metrics,
9292
+ output: [
9293
+ {
9294
+ index: 0,
9295
+ message: {
9296
+ role,
9297
+ content,
9298
+ tool_calls
9299
+ },
9300
+ logprobs: null,
9301
+ finish_reason
9302
+ }
9303
+ ]
9304
+ };
9305
+ }
9306
+
9307
+ // src/instrumentation/plugins/anthropic-plugin.ts
9308
+ import { tracingChannel as tracingChannel2 } from "dc-browser";
9309
+
9310
+ // src/wrappers/anthropic-tokens-util.ts
9311
+ function finalizeAnthropicTokens(metrics) {
9312
+ const prompt_tokens = (metrics.prompt_tokens || 0) + (metrics.prompt_cached_tokens || 0) + (metrics.prompt_cache_creation_tokens || 0);
9313
+ return {
9314
+ ...metrics,
9315
+ prompt_tokens,
9316
+ tokens: prompt_tokens + (metrics.completion_tokens || 0)
9317
+ };
9318
+ }
9319
+ function extractAnthropicCacheTokens(cacheReadTokens = 0, cacheCreationTokens = 0) {
9320
+ const cacheTokens = {};
9321
+ if (cacheReadTokens > 0) {
9322
+ cacheTokens.prompt_cached_tokens = cacheReadTokens;
9323
+ }
9324
+ if (cacheCreationTokens > 0) {
9325
+ cacheTokens.prompt_cache_creation_tokens = cacheCreationTokens;
9326
+ }
9327
+ return cacheTokens;
9328
+ }
9329
+
9330
+ // src/instrumentation/plugins/anthropic-plugin.ts
9331
+ var AnthropicPlugin = class extends BasePlugin {
9332
+ unsubscribers = [];
9333
+ onEnable() {
9334
+ this.subscribeToAnthropicChannels();
9335
+ }
9336
+ onDisable() {
9337
+ for (const unsubscribe of this.unsubscribers) {
9338
+ unsubscribe();
9339
+ }
9340
+ this.unsubscribers = [];
9341
+ }
9342
+ subscribeToAnthropicChannels() {
9343
+ this.subscribeToStreamingChannel("orchestrion:anthropic:messages.create", {
9344
+ name: "anthropic.messages.create",
9345
+ type: "llm" /* LLM */,
9346
+ extractInput: (args) => {
9347
+ const params = args[0] || {};
9348
+ const input = coalesceInput(params.messages || [], params.system);
9349
+ const metadata = filterFrom(params, ["messages", "system"]);
9350
+ return {
9351
+ input: processAttachmentsInInput(input),
9352
+ metadata: { ...metadata, provider: "anthropic" }
9353
+ };
9354
+ },
9355
+ extractOutput: (result) => {
9356
+ return result ? { role: result.role, content: result.content } : null;
9357
+ },
9358
+ extractMetrics: (result, startTime) => {
9359
+ const metrics = parseMetricsFromUsage2(result?.usage);
9360
+ if (startTime) {
9361
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9362
+ }
9363
+ const finalized = finalizeAnthropicTokens(metrics);
9364
+ return Object.fromEntries(
9365
+ Object.entries(finalized).filter(([, v]) => v !== void 0)
9366
+ );
9367
+ },
9368
+ extractMetadata: (result) => {
9369
+ const metadata = {};
9370
+ const metas = ["stop_reason", "stop_sequence"];
9371
+ for (const m of metas) {
9372
+ if (result?.[m] !== void 0) {
9373
+ metadata[m] = result[m];
9374
+ }
9375
+ }
9376
+ return metadata;
9377
+ },
9378
+ aggregateChunks: aggregateAnthropicStreamChunks,
9379
+ isStreaming: (args) => {
9380
+ return args[0]?.stream === true;
9381
+ }
9382
+ });
9383
+ this.subscribeToStreamingChannel(
9384
+ "orchestrion:anthropic:beta.messages.create",
9385
+ {
9386
+ name: "anthropic.beta.messages.create",
9387
+ type: "llm" /* LLM */,
9388
+ extractInput: (args) => {
9389
+ const params = args[0] || {};
9390
+ const input = coalesceInput(params.messages || [], params.system);
9391
+ const metadata = filterFrom(params, ["messages", "system"]);
9392
+ return {
9393
+ input: processAttachmentsInInput(input),
9394
+ metadata: { ...metadata, provider: "anthropic" }
9395
+ };
9396
+ },
9397
+ extractOutput: (result) => {
9398
+ return result ? { role: result.role, content: result.content } : null;
9399
+ },
9400
+ extractMetrics: (result, startTime) => {
9401
+ const metrics = parseMetricsFromUsage2(result?.usage);
9402
+ if (startTime) {
9403
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9404
+ }
9405
+ const finalized = finalizeAnthropicTokens(metrics);
9406
+ return Object.fromEntries(
9407
+ Object.entries(finalized).filter(([, v]) => v !== void 0)
9408
+ );
9409
+ },
9410
+ extractMetadata: (result) => {
9411
+ const metadata = {};
9412
+ const metas = ["stop_reason", "stop_sequence"];
9413
+ for (const m of metas) {
9414
+ if (result?.[m] !== void 0) {
9415
+ metadata[m] = result[m];
9416
+ }
9417
+ }
9418
+ return metadata;
9419
+ },
9420
+ aggregateChunks: aggregateAnthropicStreamChunks,
9421
+ isStreaming: (args) => {
9422
+ return args[0]?.stream === true;
9423
+ }
9424
+ }
9425
+ );
9426
+ }
9427
+ /**
9428
+ * Subscribe to a channel for async methods that may return streams.
9429
+ * Handles both streaming and non-streaming responses based on the stream parameter.
9430
+ */
9431
+ subscribeToStreamingChannel(channelName, config) {
9432
+ const channel = tracingChannel2(channelName);
9433
+ const spans = /* @__PURE__ */ new WeakMap();
9434
+ const handlers = {
9435
+ start: (event) => {
9436
+ const span = startSpan({
9437
+ name: config.name,
9438
+ spanAttributes: {
9439
+ type: config.type
9440
+ }
9441
+ });
9442
+ const startTime = getCurrentUnixTimestamp();
9443
+ spans.set(event, { span, startTime });
9444
+ try {
9445
+ const { input, metadata } = config.extractInput(event.arguments);
9446
+ span.log({
9447
+ input,
9448
+ metadata
9449
+ });
9450
+ } catch (error) {
9451
+ console.error(`Error extracting input for ${channelName}:`, error);
9452
+ }
9453
+ },
9454
+ asyncEnd: (event) => {
9455
+ const spanData = spans.get(event);
9456
+ if (!spanData) {
9457
+ return;
9458
+ }
9459
+ const { span, startTime } = spanData;
9460
+ const isStreaming = config.isStreaming ? config.isStreaming(event.arguments) : isAsyncIterable(event.result);
9461
+ if (isStreaming && isAsyncIterable(event.result)) {
9462
+ patchStreamIfNeeded(event.result, {
9463
+ onComplete: (chunks) => {
9464
+ try {
9465
+ let output;
9466
+ let metrics;
9467
+ let metadata = {};
9468
+ if (config.aggregateChunks) {
9469
+ const aggregated = config.aggregateChunks(chunks);
9470
+ output = aggregated.output;
9471
+ metrics = aggregated.metrics;
9472
+ metadata = aggregated.metadata || {};
9473
+ } else {
9474
+ output = config.extractOutput(chunks);
9475
+ metrics = config.extractMetrics(chunks, startTime);
9476
+ if (config.extractMetadata) {
9477
+ metadata = config.extractMetadata(chunks);
9478
+ }
9479
+ }
9480
+ if (!metrics.time_to_first_token && chunks.length > 0) {
9481
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9482
+ }
9483
+ span.log({
9484
+ output,
9485
+ metrics,
9486
+ metadata
9487
+ });
9488
+ } catch (error) {
9489
+ console.error(
9490
+ `Error extracting output for ${channelName}:`,
9491
+ error
9492
+ );
9493
+ } finally {
9494
+ span.end();
9495
+ }
9496
+ },
9497
+ onError: (error) => {
9498
+ span.log({
9499
+ error: error.message
9500
+ });
9501
+ span.end();
9502
+ }
9503
+ });
9504
+ } else {
9505
+ try {
9506
+ const output = config.extractOutput(event.result);
9507
+ const metrics = config.extractMetrics(event.result, startTime);
9508
+ const metadata = config.extractMetadata ? config.extractMetadata(event.result) : {};
9509
+ span.log({
9510
+ output,
9511
+ metrics,
9512
+ metadata
9513
+ });
9514
+ } catch (error) {
9515
+ console.error(`Error extracting output for ${channelName}:`, error);
9516
+ } finally {
9517
+ span.end();
9518
+ spans.delete(event);
9519
+ }
9520
+ }
9521
+ },
9522
+ error: (event) => {
9523
+ const spanData = spans.get(event);
9524
+ if (!spanData) {
9525
+ return;
9526
+ }
9527
+ const { span } = spanData;
9528
+ span.log({
9529
+ error: event.error.message
9530
+ });
9531
+ span.end();
9532
+ spans.delete(event);
9533
+ }
9534
+ };
9535
+ channel.subscribe(handlers);
9536
+ this.unsubscribers.push(() => {
9537
+ channel.unsubscribe(handlers);
9538
+ });
9539
+ }
9540
+ };
9541
+ function parseMetricsFromUsage2(usage) {
9542
+ if (!usage) {
9543
+ return {};
9544
+ }
9545
+ const metrics = {};
9546
+ function saveIfExistsTo(source, target) {
9547
+ const value = usage[source];
9548
+ if (value !== void 0 && value !== null && typeof value === "number") {
9549
+ metrics[target] = value;
9550
+ }
9551
+ }
9552
+ saveIfExistsTo("input_tokens", "prompt_tokens");
9553
+ saveIfExistsTo("output_tokens", "completion_tokens");
9554
+ saveIfExistsTo("cache_read_input_tokens", "prompt_cached_tokens");
9555
+ saveIfExistsTo("cache_creation_input_tokens", "prompt_cache_creation_tokens");
9556
+ return metrics;
9557
+ }
9558
+ function aggregateAnthropicStreamChunks(chunks) {
9559
+ const deltas = [];
9560
+ let metrics = {};
9561
+ let metadata = {};
9562
+ for (const chunk of chunks) {
9563
+ switch (chunk?.type) {
9564
+ case "message_start":
9565
+ if (chunk.message?.usage) {
9566
+ const initialMetrics = parseMetricsFromUsage2(chunk.message.usage);
9567
+ metrics = { ...metrics, ...initialMetrics };
9568
+ }
9569
+ break;
9570
+ case "content_block_delta":
9571
+ if (chunk.delta?.type === "text_delta") {
9572
+ const text = chunk.delta?.text;
9573
+ if (text) {
9574
+ deltas.push(text);
9575
+ }
9576
+ }
9577
+ break;
9578
+ case "message_delta":
9579
+ if (chunk.usage) {
9580
+ const finalMetrics = parseMetricsFromUsage2(chunk.usage);
9581
+ metrics = { ...metrics, ...finalMetrics };
9582
+ }
9583
+ if (chunk.delta) {
9584
+ metadata = { ...metadata, ...chunk.delta };
9585
+ }
9586
+ break;
9587
+ }
9588
+ }
9589
+ const output = deltas.join("");
9590
+ const finalized = finalizeAnthropicTokens(metrics);
9591
+ const filteredMetrics = Object.fromEntries(
9592
+ Object.entries(finalized).filter(([, v]) => v !== void 0)
9593
+ );
9594
+ return {
9595
+ output,
9596
+ metrics: filteredMetrics,
9597
+ metadata
9598
+ };
9599
+ }
9600
+ function convertBase64ToAttachment(source, contentType) {
9601
+ const mediaType = typeof source.media_type === "string" ? source.media_type : "image/png";
9602
+ const base64Data = source.data;
9603
+ if (base64Data && typeof base64Data === "string") {
9604
+ const binaryString = atob(base64Data);
9605
+ const bytes = new Uint8Array(binaryString.length);
9606
+ for (let i = 0; i < binaryString.length; i++) {
9607
+ bytes[i] = binaryString.charCodeAt(i);
9608
+ }
9609
+ const blob = new Blob([bytes], { type: mediaType });
9610
+ const extension = mediaType.split("/")[1] || "bin";
9611
+ const prefix = contentType === "document" ? "document" : "image";
9612
+ const filename = `${prefix}.${extension}`;
9613
+ const attachment = new Attachment({
9614
+ data: blob,
9615
+ filename,
9616
+ contentType: mediaType
9617
+ });
9618
+ return {
9619
+ ...source,
9620
+ data: attachment
9621
+ };
9622
+ }
9623
+ return source;
9624
+ }
9625
+ function processAttachmentsInInput(input) {
9626
+ if (Array.isArray(input)) {
9627
+ return input.map(processAttachmentsInInput);
9628
+ }
9629
+ if (isObject(input)) {
9630
+ if ((input.type === "image" || input.type === "document") && isObject(input.source) && input.source.type === "base64") {
9631
+ return {
9632
+ ...input,
9633
+ source: convertBase64ToAttachment(input.source, input.type)
9634
+ };
9635
+ }
9636
+ const processed = {};
9637
+ for (const [key, value] of Object.entries(input)) {
9638
+ processed[key] = processAttachmentsInInput(value);
9639
+ }
9640
+ return processed;
9641
+ }
9642
+ return input;
9643
+ }
9644
+ function coalesceInput(messages, system) {
9645
+ const input = (messages || []).slice();
9646
+ if (system) {
9647
+ input.push({ role: "system", content: system });
9648
+ }
9649
+ return input;
9650
+ }
9651
+ function filterFrom(obj, fieldsToRemove) {
9652
+ const result = {};
9653
+ for (const [key, value] of Object.entries(obj)) {
9654
+ if (!fieldsToRemove.includes(key)) {
9655
+ result[key] = value;
9656
+ }
9657
+ }
9658
+ return result;
9659
+ }
9660
+
9661
+ // src/instrumentation/plugins/ai-sdk-plugin.ts
9662
+ import { tracingChannel as tracingChannel3 } from "dc-browser";
9663
+ var DEFAULT_DENY_OUTPUT_PATHS = [
9664
+ // v3
9665
+ "roundtrips[].request.body",
9666
+ "roundtrips[].response.headers",
9667
+ "rawResponse.headers",
9668
+ "responseMessages",
9669
+ // v5
9670
+ "request.body",
9671
+ "response.body",
9672
+ "response.headers",
9673
+ "steps[].request.body",
9674
+ "steps[].response.body",
9675
+ "steps[].response.headers"
9676
+ ];
9677
+ var AISDKPlugin = class extends BasePlugin {
9678
+ unsubscribers = [];
9679
+ config;
9680
+ constructor(config = {}) {
9681
+ super();
9682
+ this.config = config;
9683
+ }
9684
+ onEnable() {
9685
+ this.subscribeToAISDK();
9686
+ }
9687
+ onDisable() {
9688
+ for (const unsubscribe of this.unsubscribers) {
9689
+ unsubscribe();
9690
+ }
9691
+ this.unsubscribers = [];
9692
+ }
9693
+ subscribeToAISDK() {
9694
+ const denyOutputPaths = this.config.denyOutputPaths || DEFAULT_DENY_OUTPUT_PATHS;
9695
+ this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateText", {
9696
+ name: "generateText",
9697
+ type: "llm" /* LLM */,
9698
+ extractInput: (args) => {
9699
+ const params = args[0] || {};
9700
+ return {
9701
+ input: processAISDKInput(params),
9702
+ metadata: extractMetadataFromParams(params)
9703
+ };
9704
+ },
9705
+ extractOutput: (result) => {
9706
+ return processAISDKOutput(result, denyOutputPaths);
9707
+ },
9708
+ extractMetrics: (result, startTime) => {
9709
+ const metrics = extractTokenMetrics(result);
9710
+ if (startTime) {
9711
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9712
+ }
9713
+ return metrics;
9714
+ },
9715
+ aggregateChunks: aggregateAISDKChunks
9716
+ });
9717
+ this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamText", {
9718
+ name: "streamText",
9719
+ type: "llm" /* LLM */,
9720
+ extractInput: (args) => {
9721
+ const params = args[0] || {};
9722
+ return {
9723
+ input: processAISDKInput(params),
9724
+ metadata: extractMetadataFromParams(params)
9725
+ };
9726
+ },
9727
+ extractOutput: (result) => {
9728
+ return processAISDKOutput(result, denyOutputPaths);
9729
+ },
9730
+ extractMetrics: (result, startTime) => {
9731
+ const metrics = extractTokenMetrics(result);
9732
+ if (startTime) {
9733
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9734
+ }
9735
+ return metrics;
9736
+ },
9737
+ aggregateChunks: aggregateAISDKChunks
9738
+ });
9739
+ this.subscribeToStreamingChannel("orchestrion:ai-sdk:generateObject", {
9740
+ name: "generateObject",
9741
+ type: "llm" /* LLM */,
9742
+ extractInput: (args) => {
9743
+ const params = args[0] || {};
9744
+ return {
9745
+ input: processAISDKInput(params),
9746
+ metadata: extractMetadataFromParams(params)
9747
+ };
9748
+ },
9749
+ extractOutput: (result) => {
9750
+ return processAISDKOutput(result, denyOutputPaths);
9751
+ },
9752
+ extractMetrics: (result, startTime) => {
9753
+ const metrics = extractTokenMetrics(result);
9754
+ if (startTime) {
9755
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9756
+ }
9757
+ return metrics;
9758
+ },
9759
+ aggregateChunks: aggregateAISDKChunks
9760
+ });
9761
+ this.subscribeToStreamingChannel("orchestrion:ai-sdk:streamObject", {
9762
+ name: "streamObject",
9763
+ type: "llm" /* LLM */,
9764
+ extractInput: (args) => {
9765
+ const params = args[0] || {};
9766
+ return {
9767
+ input: processAISDKInput(params),
9768
+ metadata: extractMetadataFromParams(params)
9769
+ };
9770
+ },
9771
+ extractOutput: (result) => {
9772
+ return processAISDKOutput(result, denyOutputPaths);
9773
+ },
9774
+ extractMetrics: (result, startTime) => {
9775
+ const metrics = extractTokenMetrics(result);
9776
+ if (startTime) {
9777
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9778
+ }
9779
+ return metrics;
9780
+ },
9781
+ aggregateChunks: aggregateAISDKChunks
9782
+ });
9783
+ this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.generate", {
9784
+ name: "Agent.generate",
9785
+ type: "llm" /* LLM */,
9786
+ extractInput: (args) => {
9787
+ const params = args[0] || {};
9788
+ return {
9789
+ input: processAISDKInput(params),
9790
+ metadata: extractMetadataFromParams(params)
9791
+ };
9792
+ },
9793
+ extractOutput: (result) => {
9794
+ return processAISDKOutput(result, denyOutputPaths);
9795
+ },
9796
+ extractMetrics: (result, startTime) => {
9797
+ const metrics = extractTokenMetrics(result);
9798
+ if (startTime) {
9799
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9800
+ }
9801
+ return metrics;
9802
+ },
9803
+ aggregateChunks: aggregateAISDKChunks
9804
+ });
9805
+ this.subscribeToStreamingChannel("orchestrion:ai-sdk:Agent.stream", {
9806
+ name: "Agent.stream",
9807
+ type: "llm" /* LLM */,
9808
+ extractInput: (args) => {
9809
+ const params = args[0] || {};
9810
+ return {
9811
+ input: processAISDKInput(params),
9812
+ metadata: extractMetadataFromParams(params)
9813
+ };
9814
+ },
9815
+ extractOutput: (result) => {
9816
+ return processAISDKOutput(result, denyOutputPaths);
9817
+ },
9818
+ extractMetrics: (result, startTime) => {
9819
+ const metrics = extractTokenMetrics(result);
9820
+ if (startTime) {
9821
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9822
+ }
9823
+ return metrics;
9824
+ },
9825
+ aggregateChunks: aggregateAISDKChunks
9826
+ });
9827
+ }
9828
+ /**
9829
+ * Subscribe to a channel for async methods that may return streams.
9830
+ * Handles both streaming and non-streaming responses.
9831
+ */
9832
+ subscribeToStreamingChannel(channelName, config) {
9833
+ const channel = tracingChannel3(channelName);
9834
+ const spans = /* @__PURE__ */ new WeakMap();
9835
+ const handlers = {
9836
+ start: (event) => {
9837
+ const span = startSpan({
9838
+ name: config.name,
9839
+ spanAttributes: {
9840
+ type: config.type
9841
+ }
9842
+ });
9843
+ const startTime = getCurrentUnixTimestamp();
9844
+ spans.set(event, { span, startTime });
9845
+ try {
9846
+ const { input, metadata } = config.extractInput(event.arguments);
9847
+ span.log({
9848
+ input,
9849
+ metadata
9850
+ });
9851
+ } catch (error) {
9852
+ console.error(`Error extracting input for ${channelName}:`, error);
9853
+ }
9854
+ },
9855
+ asyncEnd: (event) => {
9856
+ const spanData = spans.get(event);
9857
+ if (!spanData) {
9858
+ return;
9859
+ }
9860
+ const { span, startTime } = spanData;
9861
+ if (isAsyncIterable(event.result)) {
9862
+ patchStreamIfNeeded(event.result, {
9863
+ onComplete: (chunks) => {
9864
+ try {
9865
+ let output;
9866
+ let metrics;
9867
+ if (config.aggregateChunks) {
9868
+ const aggregated = config.aggregateChunks(chunks);
9869
+ output = aggregated.output;
9870
+ metrics = aggregated.metrics;
9871
+ } else {
9872
+ output = config.extractOutput(chunks);
9873
+ metrics = config.extractMetrics(chunks, startTime);
9874
+ }
9875
+ if (!metrics.time_to_first_token && chunks.length > 0) {
9876
+ metrics.time_to_first_token = getCurrentUnixTimestamp() - startTime;
9877
+ }
9878
+ span.log({
9879
+ output,
9880
+ metrics
9881
+ });
9882
+ } catch (error) {
9883
+ console.error(
9884
+ `Error extracting output for ${channelName}:`,
9885
+ error
9886
+ );
9887
+ } finally {
9888
+ span.end();
9889
+ }
9890
+ },
9891
+ onError: (error) => {
9892
+ span.log({
9893
+ error: error.message
9894
+ });
9895
+ span.end();
9896
+ }
9897
+ });
9898
+ } else {
9899
+ try {
9900
+ const output = config.extractOutput(event.result);
9901
+ const metrics = config.extractMetrics(event.result, startTime);
9902
+ span.log({
9903
+ output,
9904
+ metrics
9905
+ });
9906
+ } catch (error) {
9907
+ console.error(`Error extracting output for ${channelName}:`, error);
9908
+ } finally {
9909
+ span.end();
9910
+ spans.delete(event);
9911
+ }
9912
+ }
9913
+ },
9914
+ error: (event) => {
9915
+ const spanData = spans.get(event);
9916
+ if (!spanData) {
9917
+ return;
9918
+ }
9919
+ const { span } = spanData;
9920
+ span.log({
9921
+ error: event.error.message
9922
+ });
9923
+ span.end();
9924
+ spans.delete(event);
9925
+ }
9926
+ };
9927
+ channel.subscribe(handlers);
9928
+ this.unsubscribers.push(() => {
9929
+ channel.unsubscribe(handlers);
9930
+ });
9931
+ }
9932
+ };
9933
+ function processAISDKInput(params) {
9934
+ if (!params) return params;
9935
+ return processInputAttachments(params);
9936
+ }
9937
+ function extractMetadataFromParams(params) {
9938
+ const metadata = {
9939
+ braintrust: {
9940
+ integration_name: "ai-sdk",
9941
+ sdk_language: "typescript"
9942
+ }
9943
+ };
9944
+ const { model, provider } = serializeModelWithProvider(params.model);
9945
+ if (model) {
9946
+ metadata.model = model;
9947
+ }
9948
+ if (provider) {
9949
+ metadata.provider = provider;
9950
+ }
9951
+ return metadata;
9952
+ }
9953
+ function processAISDKOutput(output, denyOutputPaths) {
9954
+ if (!output) return output;
9955
+ const getterValues = extractGetterValues(output);
9956
+ const merged = { ...output, ...getterValues };
9957
+ return omit(merged, denyOutputPaths);
9958
+ }
9959
+ function extractTokenMetrics(result) {
9960
+ const metrics = {};
9961
+ let usage = result?.totalUsage || result?.usage;
9962
+ if (!usage && result) {
9963
+ try {
9964
+ if ("totalUsage" in result && typeof result.totalUsage !== "function") {
9965
+ usage = result.totalUsage;
9966
+ } else if ("usage" in result && typeof result.usage !== "function") {
9967
+ usage = result.usage;
9968
+ }
9969
+ } catch {
9970
+ }
9971
+ }
9972
+ if (!usage) {
9973
+ return metrics;
9974
+ }
9975
+ const promptTokens = firstNumber(
9976
+ usage.inputTokens?.total,
9977
+ usage.inputTokens,
9978
+ usage.promptTokens,
9979
+ usage.prompt_tokens
9980
+ );
9981
+ if (promptTokens !== void 0) {
9982
+ metrics.prompt_tokens = promptTokens;
9983
+ }
9984
+ const completionTokens = firstNumber(
9985
+ usage.outputTokens?.total,
9986
+ usage.outputTokens,
9987
+ usage.completionTokens,
9988
+ usage.completion_tokens
9989
+ );
9990
+ if (completionTokens !== void 0) {
9991
+ metrics.completion_tokens = completionTokens;
9992
+ }
9993
+ const totalTokens = firstNumber(
9994
+ usage.totalTokens,
9995
+ usage.tokens,
9996
+ usage.total_tokens
9997
+ );
9998
+ if (totalTokens !== void 0) {
9999
+ metrics.tokens = totalTokens;
10000
+ }
10001
+ const cost = extractCostFromResult(result);
10002
+ if (cost !== void 0) {
10003
+ metrics.estimated_cost = cost;
10004
+ }
10005
+ return metrics;
10006
+ }
10007
+ function aggregateAISDKChunks(chunks) {
10008
+ const lastChunk = chunks[chunks.length - 1];
10009
+ const output = {};
10010
+ let metrics = {};
10011
+ if (lastChunk) {
10012
+ metrics = extractTokenMetrics(lastChunk);
10013
+ if (lastChunk.text !== void 0) {
10014
+ output.text = lastChunk.text;
10015
+ }
10016
+ if (lastChunk.object !== void 0) {
10017
+ output.object = lastChunk.object;
10018
+ }
10019
+ if (lastChunk.finishReason !== void 0) {
10020
+ output.finishReason = lastChunk.finishReason;
10021
+ }
10022
+ if (lastChunk.toolCalls !== void 0) {
10023
+ output.toolCalls = lastChunk.toolCalls;
10024
+ }
10025
+ }
10026
+ return { output, metrics };
10027
+ }
10028
+ function extractGetterValues(obj) {
10029
+ const getterValues = {};
10030
+ const getterNames = [
10031
+ "text",
10032
+ "object",
10033
+ "finishReason",
10034
+ "usage",
10035
+ "totalUsage",
10036
+ "toolCalls",
10037
+ "toolResults",
10038
+ "warnings",
10039
+ "experimental_providerMetadata",
10040
+ "providerMetadata",
10041
+ "rawResponse",
10042
+ "response"
10043
+ ];
10044
+ for (const name of getterNames) {
10045
+ try {
10046
+ if (obj && name in obj && typeof obj[name] !== "function") {
10047
+ getterValues[name] = obj[name];
10048
+ }
10049
+ } catch {
10050
+ }
10051
+ }
10052
+ return getterValues;
10053
+ }
10054
+ function serializeModelWithProvider(model) {
10055
+ const modelId = typeof model === "string" ? model : model?.modelId;
10056
+ const explicitProvider = typeof model === "object" ? model?.provider : void 0;
10057
+ if (!modelId) {
10058
+ return { model: modelId, provider: explicitProvider };
10059
+ }
10060
+ const parsed = parseGatewayModelString(modelId);
10061
+ return {
10062
+ model: parsed.model,
10063
+ provider: explicitProvider || parsed.provider
10064
+ };
10065
+ }
10066
+ function parseGatewayModelString(modelString) {
10067
+ if (!modelString || typeof modelString !== "string") {
10068
+ return { model: modelString };
10069
+ }
10070
+ const slashIndex = modelString.indexOf("/");
10071
+ if (slashIndex > 0 && slashIndex < modelString.length - 1) {
10072
+ return {
10073
+ provider: modelString.substring(0, slashIndex),
10074
+ model: modelString.substring(slashIndex + 1)
10075
+ };
10076
+ }
10077
+ return { model: modelString };
10078
+ }
10079
+ function extractCostFromResult(result) {
10080
+ if (result?.steps && Array.isArray(result.steps) && result.steps.length > 0) {
10081
+ let totalCost = 0;
10082
+ let foundCost = false;
10083
+ for (const step of result.steps) {
10084
+ const gateway2 = step?.providerMetadata?.gateway;
10085
+ const stepCost = parseGatewayCost(gateway2?.cost) || parseGatewayCost(gateway2?.marketCost);
10086
+ if (stepCost !== void 0 && stepCost > 0) {
10087
+ totalCost += stepCost;
10088
+ foundCost = true;
10089
+ }
10090
+ }
10091
+ if (foundCost) {
10092
+ return totalCost;
10093
+ }
10094
+ }
10095
+ const gateway = result?.providerMetadata?.gateway;
10096
+ const directCost = parseGatewayCost(gateway?.cost) || parseGatewayCost(gateway?.marketCost);
10097
+ if (directCost !== void 0 && directCost > 0) {
10098
+ return directCost;
10099
+ }
10100
+ return void 0;
10101
+ }
10102
+ function parseGatewayCost(cost) {
10103
+ if (cost === void 0 || cost === null) {
10104
+ return void 0;
10105
+ }
10106
+ if (typeof cost === "number") {
10107
+ return cost;
10108
+ }
10109
+ if (typeof cost === "string") {
10110
+ const parsed = parseFloat(cost);
10111
+ if (!isNaN(parsed)) {
10112
+ return parsed;
10113
+ }
10114
+ }
10115
+ return void 0;
10116
+ }
10117
+ function firstNumber(...values) {
10118
+ for (const v of values) {
10119
+ if (typeof v === "number") {
10120
+ return v;
10121
+ }
10122
+ }
10123
+ return void 0;
10124
+ }
10125
+ function deepCopy(obj) {
10126
+ return JSON.parse(JSON.stringify(obj));
10127
+ }
10128
+ function parsePath(path2) {
10129
+ const keys = [];
10130
+ let current = "";
10131
+ for (let i = 0; i < path2.length; i++) {
10132
+ const char = path2[i];
10133
+ if (char === ".") {
10134
+ if (current) {
10135
+ keys.push(current);
10136
+ current = "";
10137
+ }
10138
+ } else if (char === "[") {
10139
+ if (current) {
10140
+ keys.push(current);
10141
+ current = "";
10142
+ }
10143
+ let bracketContent = "";
10144
+ i++;
10145
+ while (i < path2.length && path2[i] !== "]") {
10146
+ bracketContent += path2[i];
10147
+ i++;
10148
+ }
10149
+ if (bracketContent === "") {
10150
+ keys.push("[]");
10151
+ } else {
10152
+ const index = parseInt(bracketContent, 10);
10153
+ keys.push(isNaN(index) ? bracketContent : index);
10154
+ }
10155
+ } else {
10156
+ current += char;
10157
+ }
10158
+ }
10159
+ if (current) {
10160
+ keys.push(current);
10161
+ }
10162
+ return keys;
10163
+ }
10164
+ function omitAtPath(obj, keys) {
10165
+ if (keys.length === 0) return;
10166
+ const firstKey = keys[0];
10167
+ const remainingKeys = keys.slice(1);
10168
+ if (firstKey === "[]") {
10169
+ if (Array.isArray(obj)) {
10170
+ obj.forEach((item) => {
10171
+ if (remainingKeys.length > 0) {
10172
+ omitAtPath(item, remainingKeys);
10173
+ }
10174
+ });
10175
+ }
10176
+ } else if (remainingKeys.length === 0) {
10177
+ if (obj && typeof obj === "object" && firstKey in obj) {
10178
+ obj[firstKey] = "<omitted>";
10179
+ }
10180
+ } else {
10181
+ if (obj && typeof obj === "object" && firstKey in obj) {
10182
+ omitAtPath(obj[firstKey], remainingKeys);
10183
+ }
10184
+ }
10185
+ }
10186
+ function omit(obj, paths) {
10187
+ const result = deepCopy(obj);
10188
+ for (const path2 of paths) {
10189
+ const keys = parsePath(path2);
10190
+ omitAtPath(result, keys);
10191
+ }
10192
+ return result;
10193
+ }
10194
+
10195
+ // src/instrumentation/plugins/claude-agent-sdk-plugin.ts
10196
+ import { tracingChannel as tracingChannel4 } from "dc-browser";
10197
+ function filterSerializableOptions(options) {
10198
+ const allowedKeys = [
10199
+ "model",
10200
+ "maxTurns",
10201
+ "cwd",
10202
+ "continue",
10203
+ "allowedTools",
10204
+ "disallowedTools",
10205
+ "additionalDirectories",
10206
+ "permissionMode",
10207
+ "debug",
10208
+ "apiKey",
10209
+ "apiKeySource",
10210
+ "agentName",
10211
+ "instructions"
10212
+ ];
10213
+ const filtered = {};
10214
+ for (const key of allowedKeys) {
10215
+ if (options[key] !== void 0) {
10216
+ filtered[key] = options[key];
10217
+ }
10218
+ }
10219
+ return filtered;
10220
+ }
10221
+ function getNumberProperty(obj, key) {
10222
+ if (!obj || typeof obj !== "object" || !(key in obj)) {
10223
+ return void 0;
10224
+ }
10225
+ const value = Reflect.get(obj, key);
10226
+ return typeof value === "number" ? value : void 0;
10227
+ }
10228
+ function extractUsageFromMessage(message) {
10229
+ const metrics = {};
10230
+ let usage;
10231
+ if (message.type === "assistant") {
10232
+ usage = message.message?.usage;
10233
+ } else if (message.type === "result") {
10234
+ usage = message.usage;
10235
+ }
10236
+ if (!usage || typeof usage !== "object") {
10237
+ return metrics;
10238
+ }
10239
+ const inputTokens = getNumberProperty(usage, "input_tokens");
10240
+ if (inputTokens !== void 0) {
10241
+ metrics.prompt_tokens = inputTokens;
10242
+ }
10243
+ const outputTokens = getNumberProperty(usage, "output_tokens");
10244
+ if (outputTokens !== void 0) {
10245
+ metrics.completion_tokens = outputTokens;
10246
+ }
10247
+ const cacheReadTokens = getNumberProperty(usage, "cache_read_input_tokens") || 0;
10248
+ const cacheCreationTokens = getNumberProperty(usage, "cache_creation_input_tokens") || 0;
10249
+ if (cacheReadTokens > 0 || cacheCreationTokens > 0) {
10250
+ const cacheTokens = extractAnthropicCacheTokens(
10251
+ cacheReadTokens,
10252
+ cacheCreationTokens
10253
+ );
10254
+ Object.assign(metrics, cacheTokens);
10255
+ }
10256
+ if (Object.keys(metrics).length > 0) {
10257
+ Object.assign(metrics, finalizeAnthropicTokens(metrics));
10258
+ }
10259
+ return metrics;
10260
+ }
10261
+ function buildLLMInput(prompt, conversationHistory) {
10262
+ const promptMessage = typeof prompt === "string" ? { content: prompt, role: "user" } : void 0;
10263
+ const inputParts = [
10264
+ ...promptMessage ? [promptMessage] : [],
10265
+ ...conversationHistory
10266
+ ];
10267
+ return inputParts.length > 0 ? inputParts : void 0;
10268
+ }
10269
+ async function createLLMSpanForMessages(messages, prompt, conversationHistory, options, startTime, parentSpan) {
10270
+ if (messages.length === 0) return void 0;
10271
+ const lastMessage = messages[messages.length - 1];
10272
+ if (lastMessage.type !== "assistant" || !lastMessage.message?.usage) {
10273
+ return void 0;
10274
+ }
10275
+ const model = lastMessage.message.model || options.model;
10276
+ const usage = extractUsageFromMessage(lastMessage);
10277
+ const input = buildLLMInput(prompt, conversationHistory);
10278
+ const outputs = messages.map(
10279
+ (m) => m.message?.content && m.message?.role ? { content: m.message.content, role: m.message.role } : void 0
10280
+ ).filter((c) => c !== void 0);
10281
+ const span = startSpan({
10282
+ name: "anthropic.messages.create",
10283
+ spanAttributes: {
10284
+ type: "llm" /* LLM */
10285
+ },
10286
+ startTime,
10287
+ parent: parentSpan
10288
+ });
10289
+ span.log({
10290
+ input,
10291
+ output: outputs,
10292
+ metadata: model ? { model } : void 0,
10293
+ metrics: usage
10294
+ });
10295
+ await span.end();
10296
+ return lastMessage.message?.content && lastMessage.message?.role ? { content: lastMessage.message.content, role: lastMessage.message.role } : void 0;
10297
+ }
10298
+ var ClaudeAgentSDKPlugin = class extends BasePlugin {
10299
+ unsubscribers = [];
10300
+ onEnable() {
10301
+ this.subscribeToQuery();
10302
+ }
10303
+ onDisable() {
10304
+ for (const unsubscribe of this.unsubscribers) {
10305
+ unsubscribe();
10306
+ }
10307
+ this.unsubscribers = [];
10308
+ }
10309
+ /**
10310
+ * Subscribe to the query channel for agent interactions.
10311
+ * Handles streaming responses and traces both the top-level agent task
10312
+ * and individual LLM calls.
10313
+ */
10314
+ subscribeToQuery() {
10315
+ const channel = tracingChannel4("orchestrion:claude-agent-sdk:query");
10316
+ const spans = /* @__PURE__ */ new WeakMap();
10317
+ const handlers = {
10318
+ start: (event) => {
10319
+ const params = event.arguments[0] ?? {};
10320
+ const { prompt, options = {} } = params;
10321
+ const span = startSpan({
10322
+ name: "Claude Agent",
10323
+ spanAttributes: {
10324
+ type: "task" /* TASK */
10325
+ }
10326
+ });
10327
+ const startTime = getCurrentUnixTimestamp();
10328
+ try {
10329
+ span.log({
10330
+ input: typeof prompt === "string" ? prompt : {
10331
+ type: "streaming",
10332
+ description: "AsyncIterable<SDKMessage>"
10333
+ },
10334
+ metadata: filterSerializableOptions(options)
10335
+ });
10336
+ } catch (error) {
10337
+ console.error("Error extracting input for Claude Agent SDK:", error);
10338
+ }
10339
+ spans.set(event, {
10340
+ span,
10341
+ startTime,
10342
+ conversationHistory: [],
10343
+ currentMessages: [],
10344
+ currentMessageId: void 0,
10345
+ currentMessageStartTime: startTime,
10346
+ accumulatedOutputTokens: 0
10347
+ });
10348
+ },
10349
+ asyncEnd: (event) => {
10350
+ const spanData = spans.get(event);
10351
+ if (!spanData) {
10352
+ return;
10353
+ }
10354
+ if (isAsyncIterable(event.result)) {
10355
+ patchStreamIfNeeded(event.result, {
10356
+ onChunk: async (message) => {
10357
+ const currentTime = getCurrentUnixTimestamp();
10358
+ const params = event.arguments[0];
10359
+ const { prompt, options = {} } = params;
10360
+ const messageId = message.message?.id;
10361
+ if (messageId && messageId !== spanData.currentMessageId) {
10362
+ if (spanData.currentMessages.length > 0) {
10363
+ const finalMessage = await createLLMSpanForMessages(
10364
+ spanData.currentMessages,
10365
+ prompt,
10366
+ spanData.conversationHistory,
10367
+ options,
10368
+ spanData.currentMessageStartTime,
10369
+ await spanData.span.export()
10370
+ );
10371
+ if (finalMessage) {
10372
+ spanData.conversationHistory.push(finalMessage);
10373
+ }
10374
+ const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
10375
+ if (lastMessage?.message?.usage) {
10376
+ const outputTokens = getNumberProperty(
10377
+ lastMessage.message.usage,
10378
+ "output_tokens"
10379
+ ) || 0;
10380
+ spanData.accumulatedOutputTokens += outputTokens;
10381
+ }
10382
+ spanData.currentMessages = [];
10383
+ }
10384
+ spanData.currentMessageId = messageId;
10385
+ spanData.currentMessageStartTime = currentTime;
10386
+ }
10387
+ if (message.type === "assistant" && message.message?.usage) {
10388
+ spanData.currentMessages.push(message);
10389
+ }
10390
+ if (message.type === "result" && message.usage) {
10391
+ const finalUsageMetrics = extractUsageFromMessage(message);
10392
+ if (spanData.currentMessages.length > 0 && finalUsageMetrics.completion_tokens !== void 0) {
10393
+ const lastMessage = spanData.currentMessages[spanData.currentMessages.length - 1];
10394
+ if (lastMessage?.message?.usage) {
10395
+ const adjustedTokens = finalUsageMetrics.completion_tokens - spanData.accumulatedOutputTokens;
10396
+ if (adjustedTokens >= 0) {
10397
+ lastMessage.message.usage.output_tokens = adjustedTokens;
10398
+ }
10399
+ }
10400
+ }
10401
+ const result_metadata = {};
10402
+ if (message.num_turns !== void 0) {
10403
+ result_metadata.num_turns = message.num_turns;
10404
+ }
10405
+ if (message.session_id !== void 0) {
10406
+ result_metadata.session_id = message.session_id;
10407
+ }
10408
+ if (Object.keys(result_metadata).length > 0) {
10409
+ spanData.span.log({
10410
+ metadata: result_metadata
10411
+ });
10412
+ }
10413
+ }
10414
+ },
10415
+ onComplete: async () => {
10416
+ try {
10417
+ const params = event.arguments[0];
10418
+ const { prompt, options = {} } = params;
10419
+ if (spanData.currentMessages.length > 0) {
10420
+ const finalMessage = await createLLMSpanForMessages(
10421
+ spanData.currentMessages,
10422
+ prompt,
10423
+ spanData.conversationHistory,
10424
+ options,
10425
+ spanData.currentMessageStartTime,
10426
+ await spanData.span.export()
10427
+ );
10428
+ if (finalMessage) {
10429
+ spanData.conversationHistory.push(finalMessage);
10430
+ }
10431
+ }
10432
+ spanData.span.log({
10433
+ output: spanData.conversationHistory.length > 0 ? spanData.conversationHistory[spanData.conversationHistory.length - 1] : void 0
10434
+ });
10435
+ } catch (error) {
10436
+ console.error(
10437
+ "Error extracting output for Claude Agent SDK:",
10438
+ error
10439
+ );
10440
+ } finally {
10441
+ spanData.span.end();
10442
+ spans.delete(event);
10443
+ }
10444
+ },
10445
+ onError: (error) => {
10446
+ spanData.span.log({
10447
+ error: error.message
10448
+ });
10449
+ spanData.span.end();
10450
+ spans.delete(event);
10451
+ }
10452
+ });
10453
+ } else {
10454
+ try {
10455
+ spanData.span.log({
10456
+ output: event.result
10457
+ });
10458
+ } catch (error) {
10459
+ console.error(
10460
+ "Error extracting output for Claude Agent SDK:",
10461
+ error
10462
+ );
10463
+ } finally {
10464
+ spanData.span.end();
10465
+ spans.delete(event);
10466
+ }
10467
+ }
10468
+ },
10469
+ error: (event) => {
10470
+ const spanData = spans.get(event);
10471
+ if (!spanData) {
10472
+ return;
10473
+ }
10474
+ const { span } = spanData;
10475
+ span.log({
10476
+ error: event.error.message
10477
+ });
10478
+ span.end();
10479
+ spans.delete(event);
10480
+ }
10481
+ };
10482
+ channel.subscribe(handlers);
10483
+ this.unsubscribers.push(() => {
10484
+ channel.unsubscribe(handlers);
10485
+ });
10486
+ }
10487
+ };
10488
+
10489
+ // src/instrumentation/plugins/google-genai-plugin.ts
10490
+ import { tracingChannel as tracingChannel5 } from "dc-browser";
10491
+ var GoogleGenAIPlugin = class extends BasePlugin {
10492
+ unsubscribers = [];
10493
+ onEnable() {
10494
+ this.subscribeToGoogleGenAIChannels();
10495
+ }
10496
+ onDisable() {
10497
+ for (const unsubscribe of this.unsubscribers) {
10498
+ unsubscribe();
10499
+ }
10500
+ this.unsubscribers = [];
10501
+ }
10502
+ subscribeToGoogleGenAIChannels() {
10503
+ this.subscribeToChannel("orchestrion:google-genai:models.generateContent", {
10504
+ name: "google-genai.generateContent",
10505
+ type: "llm" /* LLM */,
10506
+ extractInput: (args) => {
10507
+ const params = args[0] || {};
10508
+ const input = serializeInput(params);
10509
+ const metadata = extractMetadata(params);
10510
+ return {
10511
+ input,
10512
+ metadata: { ...metadata, provider: "google-genai" }
10513
+ };
10514
+ },
10515
+ extractOutput: (result) => {
10516
+ return result;
10517
+ },
10518
+ extractMetrics: (result, startTime) => {
10519
+ return extractGenerateContentMetrics(result, startTime);
10520
+ }
10521
+ });
10522
+ this.subscribeToGoogleStreamingChannel(
10523
+ "orchestrion:google-genai:models.generateContentStream",
10524
+ {
10525
+ name: "google-genai.generateContentStream",
10526
+ type: "llm" /* LLM */,
10527
+ extractInput: (args) => {
10528
+ const params = args[0] || {};
10529
+ const input = serializeInput(params);
10530
+ const metadata = extractMetadata(params);
10531
+ return {
10532
+ input,
10533
+ metadata: { ...metadata, provider: "google-genai" }
10534
+ };
10535
+ },
10536
+ aggregateChunks: aggregateGenerateContentChunks
10537
+ }
10538
+ );
10539
+ }
10540
+ subscribeToChannel(channelName, config) {
10541
+ const channel = tracingChannel5(channelName);
10542
+ const spans = /* @__PURE__ */ new WeakMap();
10543
+ const handlers = {
10544
+ start: (event) => {
10545
+ const span = startSpan({
10546
+ name: config.name,
10547
+ spanAttributes: {
10548
+ type: config.type
10549
+ }
10550
+ });
10551
+ const startTime = getCurrentUnixTimestamp();
10552
+ spans.set(event, { span, startTime });
10553
+ try {
10554
+ const { input, metadata } = config.extractInput(event.arguments);
10555
+ span.log({
10556
+ input,
10557
+ metadata
10558
+ });
10559
+ } catch (error) {
10560
+ console.error(`Error extracting input for ${channelName}:`, error);
10561
+ }
10562
+ },
10563
+ asyncEnd: (event) => {
10564
+ const spanData = spans.get(event);
10565
+ if (!spanData) {
10566
+ return;
10567
+ }
10568
+ const { span, startTime } = spanData;
10569
+ try {
10570
+ const output = config.extractOutput(event.result);
10571
+ const metrics = config.extractMetrics(event.result, startTime);
10572
+ span.log({
10573
+ output,
10574
+ metrics
10575
+ });
10576
+ } catch (error) {
10577
+ console.error(`Error extracting output for ${channelName}:`, error);
10578
+ } finally {
10579
+ span.end();
10580
+ spans.delete(event);
10581
+ }
10582
+ },
10583
+ error: (event) => {
10584
+ const spanData = spans.get(event);
10585
+ if (!spanData) {
10586
+ return;
10587
+ }
10588
+ const { span } = spanData;
10589
+ span.log({
10590
+ error: event.error.message
10591
+ });
10592
+ span.end();
10593
+ spans.delete(event);
10594
+ }
10595
+ };
10596
+ channel.subscribe(handlers);
10597
+ this.unsubscribers.push(() => {
10598
+ channel.unsubscribe(handlers);
10599
+ });
10600
+ }
10601
+ subscribeToGoogleStreamingChannel(channelName, config) {
10602
+ const channel = tracingChannel5(channelName);
10603
+ const spans = /* @__PURE__ */ new WeakMap();
10604
+ const handlers = {
10605
+ start: (event) => {
10606
+ const span = startSpan({
10607
+ name: config.name,
10608
+ spanAttributes: {
10609
+ type: config.type
10610
+ }
10611
+ });
10612
+ const startTime = getCurrentUnixTimestamp();
10613
+ spans.set(event, { span, startTime });
10614
+ try {
10615
+ const { input, metadata } = config.extractInput(event.arguments);
10616
+ span.log({
10617
+ input,
10618
+ metadata
10619
+ });
10620
+ } catch (error) {
10621
+ console.error(`Error extracting input for ${channelName}:`, error);
10622
+ }
10623
+ },
10624
+ asyncEnd: (event) => {
10625
+ const spanData = spans.get(event);
10626
+ if (!spanData) {
10627
+ return;
10628
+ }
10629
+ const { span, startTime } = spanData;
10630
+ if (isAsyncIterable(event.result)) {
10631
+ patchStreamIfNeeded(event.result, {
10632
+ onComplete: (chunks) => {
10633
+ try {
10634
+ const { output, metrics } = config.aggregateChunks(
10635
+ chunks,
10636
+ startTime
10637
+ );
10638
+ span.log({
10639
+ output,
10640
+ metrics
10641
+ });
10642
+ } catch (error) {
10643
+ console.error(
10644
+ `Error extracting output for ${channelName}:`,
10645
+ error
10646
+ );
10647
+ } finally {
10648
+ span.end();
10649
+ }
10650
+ },
10651
+ onError: (error) => {
10652
+ span.log({
10653
+ error: error.message
10654
+ });
10655
+ span.end();
10656
+ }
10657
+ });
10658
+ } else {
10659
+ span.end();
10660
+ spans.delete(event);
10661
+ }
10662
+ },
10663
+ error: (event) => {
10664
+ const spanData = spans.get(event);
10665
+ if (!spanData) {
10666
+ return;
10667
+ }
10668
+ const { span } = spanData;
10669
+ span.log({
10670
+ error: event.error.message
10671
+ });
10672
+ span.end();
10673
+ spans.delete(event);
10674
+ }
10675
+ };
10676
+ channel.subscribe(handlers);
10677
+ this.unsubscribers.push(() => {
10678
+ channel.unsubscribe(handlers);
10679
+ });
10680
+ }
10681
+ };
10682
+ function serializeInput(params) {
10683
+ const input = {
10684
+ model: params.model,
10685
+ contents: serializeContents(params.contents)
10686
+ };
10687
+ if (params.config) {
10688
+ const config = tryToDict(params.config);
10689
+ if (config) {
10690
+ const tools = serializeTools(params);
10691
+ if (tools) {
10692
+ config.tools = tools;
10693
+ }
10694
+ input.config = config;
10695
+ }
10696
+ }
10697
+ return input;
10698
+ }
10699
+ function serializeContents(contents) {
10700
+ if (contents === null || contents === void 0) {
10701
+ return null;
10702
+ }
10703
+ if (Array.isArray(contents)) {
10704
+ return contents.map((item) => serializeContentItem(item));
10705
+ }
10706
+ return serializeContentItem(contents);
10707
+ }
10708
+ function serializeContentItem(item) {
10709
+ if (typeof item === "object" && item !== null) {
10710
+ if (item.parts && Array.isArray(item.parts)) {
10711
+ return {
10712
+ ...item,
10713
+ parts: item.parts.map((part) => serializePart(part))
10714
+ };
10715
+ }
10716
+ return item;
10717
+ }
10718
+ if (typeof item === "string") {
10719
+ return { text: item };
10720
+ }
10721
+ return item;
10722
+ }
10723
+ function serializePart(part) {
10724
+ if (!part || typeof part !== "object") {
10725
+ return part;
10726
+ }
10727
+ if (part.inlineData && part.inlineData.data) {
10728
+ const { data, mimeType } = part.inlineData;
10729
+ if (data instanceof Uint8Array || typeof Buffer !== "undefined" && Buffer.isBuffer(data) || typeof data === "string") {
10730
+ const extension = mimeType ? mimeType.split("/")[1] : "bin";
10731
+ const filename = `file.${extension}`;
10732
+ const buffer = typeof data === "string" ? typeof Buffer !== "undefined" ? Buffer.from(data, "base64") : new Uint8Array(
10733
+ atob(data).split("").map((c) => c.charCodeAt(0))
10734
+ ) : typeof Buffer !== "undefined" ? Buffer.from(data) : new Uint8Array(data);
10735
+ const attachment = new Attachment({
10736
+ data: buffer,
10737
+ filename,
10738
+ contentType: mimeType || "application/octet-stream"
10739
+ });
10740
+ return {
10741
+ image_url: { url: attachment }
10742
+ };
10743
+ }
10744
+ }
10745
+ return part;
10746
+ }
10747
+ function serializeTools(params) {
10748
+ if (!params.config?.tools) {
10749
+ return null;
10750
+ }
10751
+ try {
10752
+ return params.config.tools.map((tool) => {
10753
+ if (typeof tool === "object" && tool.functionDeclarations) {
10754
+ return tool;
10755
+ }
10756
+ return tool;
10757
+ });
10758
+ } catch {
10759
+ return null;
10760
+ }
10761
+ }
10762
+ function extractMetadata(params) {
10763
+ const metadata = {};
10764
+ if (params.model) {
10765
+ metadata.model = params.model;
10766
+ }
10767
+ if (params.config) {
10768
+ const config = tryToDict(params.config);
10769
+ if (config) {
10770
+ Object.keys(config).forEach((key) => {
10771
+ if (key !== "tools") {
10772
+ metadata[key] = config[key];
10773
+ }
10774
+ });
10775
+ }
10776
+ }
10777
+ return metadata;
10778
+ }
10779
+ function extractGenerateContentMetrics(response, startTime) {
10780
+ const metrics = {};
10781
+ if (startTime) {
10782
+ const end = getCurrentUnixTimestamp();
10783
+ metrics.duration = end - startTime;
10784
+ }
10785
+ if (response.usageMetadata) {
10786
+ const usage = response.usageMetadata;
10787
+ if (usage.promptTokenCount !== void 0) {
10788
+ metrics.prompt_tokens = usage.promptTokenCount;
10789
+ }
10790
+ if (usage.candidatesTokenCount !== void 0) {
10791
+ metrics.completion_tokens = usage.candidatesTokenCount;
10792
+ }
10793
+ if (usage.totalTokenCount !== void 0) {
10794
+ metrics.tokens = usage.totalTokenCount;
10795
+ }
10796
+ if (usage.cachedContentTokenCount !== void 0) {
10797
+ metrics.prompt_cached_tokens = usage.cachedContentTokenCount;
10798
+ }
10799
+ if (usage.thoughtsTokenCount !== void 0) {
10800
+ metrics.completion_reasoning_tokens = usage.thoughtsTokenCount;
10801
+ }
10802
+ }
10803
+ return metrics;
10804
+ }
10805
+ function aggregateGenerateContentChunks(chunks, startTime) {
10806
+ const end = getCurrentUnixTimestamp();
10807
+ const metrics = {
10808
+ duration: end - startTime
10809
+ };
10810
+ let firstTokenTime = null;
10811
+ if (chunks.length > 0 && firstTokenTime === null) {
10812
+ firstTokenTime = getCurrentUnixTimestamp();
10813
+ metrics.time_to_first_token = firstTokenTime - startTime;
10814
+ }
10815
+ if (chunks.length === 0) {
10816
+ return { output: {}, metrics };
10817
+ }
10818
+ let text = "";
10819
+ let thoughtText = "";
10820
+ const otherParts = [];
10821
+ let usageMetadata = null;
10822
+ let lastResponse = null;
10823
+ for (const chunk of chunks) {
10824
+ lastResponse = chunk;
10825
+ if (chunk.usageMetadata) {
10826
+ usageMetadata = chunk.usageMetadata;
10827
+ }
10828
+ if (chunk.candidates && Array.isArray(chunk.candidates)) {
10829
+ for (const candidate of chunk.candidates) {
10830
+ if (candidate.content?.parts) {
10831
+ for (const part of candidate.content.parts) {
10832
+ if (part.text !== void 0) {
10833
+ if (part.thought) {
10834
+ thoughtText += part.text;
10835
+ } else {
10836
+ text += part.text;
10837
+ }
10838
+ } else if (part.functionCall) {
10839
+ otherParts.push({ functionCall: part.functionCall });
10840
+ } else if (part.codeExecutionResult) {
10841
+ otherParts.push({
10842
+ codeExecutionResult: part.codeExecutionResult
10843
+ });
10844
+ } else if (part.executableCode) {
10845
+ otherParts.push({ executableCode: part.executableCode });
10846
+ }
10847
+ }
10848
+ }
10849
+ }
10850
+ }
10851
+ }
10852
+ const output = {};
10853
+ const parts = [];
10854
+ if (thoughtText) {
10855
+ parts.push({ text: thoughtText, thought: true });
10856
+ }
10857
+ if (text) {
10858
+ parts.push({ text });
10859
+ }
10860
+ parts.push(...otherParts);
10861
+ if (parts.length > 0 && lastResponse?.candidates) {
10862
+ const candidates = [];
10863
+ for (const candidate of lastResponse.candidates) {
10864
+ const candidateDict = {
10865
+ content: {
10866
+ parts,
10867
+ role: "model"
10868
+ }
10869
+ };
10870
+ if (candidate.finishReason !== void 0) {
10871
+ candidateDict.finishReason = candidate.finishReason;
10872
+ }
10873
+ if (candidate.safetyRatings) {
10874
+ candidateDict.safetyRatings = candidate.safetyRatings;
10875
+ }
10876
+ candidates.push(candidateDict);
10877
+ }
10878
+ output.candidates = candidates;
10879
+ }
10880
+ if (usageMetadata) {
10881
+ output.usageMetadata = usageMetadata;
10882
+ if (usageMetadata.promptTokenCount !== void 0) {
10883
+ metrics.prompt_tokens = usageMetadata.promptTokenCount;
10884
+ }
10885
+ if (usageMetadata.candidatesTokenCount !== void 0) {
10886
+ metrics.completion_tokens = usageMetadata.candidatesTokenCount;
10887
+ }
10888
+ if (usageMetadata.totalTokenCount !== void 0) {
10889
+ metrics.tokens = usageMetadata.totalTokenCount;
10890
+ }
10891
+ if (usageMetadata.cachedContentTokenCount !== void 0) {
10892
+ metrics.prompt_cached_tokens = usageMetadata.cachedContentTokenCount;
10893
+ }
10894
+ if (usageMetadata.thoughtsTokenCount !== void 0) {
10895
+ metrics.completion_reasoning_tokens = usageMetadata.thoughtsTokenCount;
10896
+ }
10897
+ }
10898
+ if (text) {
10899
+ output.text = text;
10900
+ }
10901
+ return { output, metrics };
10902
+ }
10903
+ function tryToDict(obj) {
10904
+ if (obj === null || obj === void 0) {
10905
+ return null;
10906
+ }
10907
+ if (typeof obj === "object") {
10908
+ if (typeof obj.toJSON === "function") {
10909
+ return obj.toJSON();
10910
+ }
10911
+ return obj;
10912
+ }
10913
+ return null;
10914
+ }
10915
+
10916
+ // src/instrumentation/braintrust-plugin.ts
10917
+ var BraintrustPlugin = class extends BasePlugin {
10918
+ config;
10919
+ openaiPlugin = null;
10920
+ anthropicPlugin = null;
10921
+ aiSDKPlugin = null;
10922
+ claudeAgentSDKPlugin = null;
10923
+ googleGenAIPlugin = null;
10924
+ constructor(config = {}) {
10925
+ super();
10926
+ this.config = config;
10927
+ }
10928
+ onEnable() {
10929
+ const integrations = this.config.integrations || {};
10930
+ if (integrations.openai !== false) {
10931
+ this.openaiPlugin = new OpenAIPlugin();
10932
+ this.openaiPlugin.enable();
10933
+ }
10934
+ if (integrations.anthropic !== false) {
10935
+ this.anthropicPlugin = new AnthropicPlugin();
10936
+ this.anthropicPlugin.enable();
10937
+ }
10938
+ if (integrations.aisdk !== false && integrations.vercel !== false) {
10939
+ this.aiSDKPlugin = new AISDKPlugin();
10940
+ this.aiSDKPlugin.enable();
10941
+ }
10942
+ if (integrations.claudeAgentSDK !== false) {
10943
+ this.claudeAgentSDKPlugin = new ClaudeAgentSDKPlugin();
10944
+ this.claudeAgentSDKPlugin.enable();
10945
+ }
10946
+ if (integrations.googleGenAI !== false && integrations.google !== false) {
10947
+ this.googleGenAIPlugin = new GoogleGenAIPlugin();
10948
+ this.googleGenAIPlugin.enable();
10949
+ }
10950
+ }
10951
+ onDisable() {
10952
+ if (this.openaiPlugin) {
10953
+ this.openaiPlugin.disable();
10954
+ this.openaiPlugin = null;
10955
+ }
10956
+ if (this.anthropicPlugin) {
10957
+ this.anthropicPlugin.disable();
10958
+ this.anthropicPlugin = null;
10959
+ }
10960
+ if (this.aiSDKPlugin) {
10961
+ this.aiSDKPlugin.disable();
10962
+ this.aiSDKPlugin = null;
10963
+ }
10964
+ if (this.claudeAgentSDKPlugin) {
10965
+ this.claudeAgentSDKPlugin.disable();
10966
+ this.claudeAgentSDKPlugin = null;
10967
+ }
10968
+ if (this.googleGenAIPlugin) {
10969
+ this.googleGenAIPlugin.disable();
10970
+ this.googleGenAIPlugin = null;
10971
+ }
10972
+ }
10973
+ };
10974
+
10975
+ // src/instrumentation/registry.ts
10976
+ var PluginRegistry = class {
10977
+ braintrustPlugin = null;
10978
+ config = {};
10979
+ enabled = false;
10980
+ /**
10981
+ * Configure which integrations should be enabled.
10982
+ * This must be called before any SDK imports to take effect.
10983
+ */
10984
+ configure(config) {
10985
+ if (this.enabled) {
10986
+ console.warn(
10987
+ "Braintrust: Cannot configure instrumentation after it has been enabled. Call configureInstrumentation() before importing any AI SDKs."
10988
+ );
10989
+ return;
10990
+ }
10991
+ this.config = { ...this.config, ...config };
10992
+ }
10993
+ /**
10994
+ * Enable all configured plugins.
10995
+ * Called automatically when the library is loaded.
10996
+ */
10997
+ enable() {
10998
+ if (this.enabled) {
10999
+ return;
11000
+ }
11001
+ this.enabled = true;
11002
+ const envConfig = this.readEnvConfig();
11003
+ const finalConfig = {
11004
+ integrations: {
11005
+ ...this.getDefaultConfig(),
11006
+ ...this.config.integrations,
11007
+ ...envConfig.integrations
11008
+ }
11009
+ };
11010
+ this.braintrustPlugin = new BraintrustPlugin(finalConfig);
11011
+ this.braintrustPlugin.enable();
11012
+ }
11013
+ /**
11014
+ * Disable all plugins.
11015
+ * Primarily used for testing.
11016
+ */
11017
+ disable() {
11018
+ if (!this.enabled) {
11019
+ return;
11020
+ }
11021
+ this.enabled = false;
11022
+ if (this.braintrustPlugin) {
11023
+ this.braintrustPlugin.disable();
11024
+ this.braintrustPlugin = null;
11025
+ }
11026
+ }
11027
+ /**
11028
+ * Check if instrumentation is enabled.
11029
+ */
11030
+ isEnabled() {
11031
+ return this.enabled;
11032
+ }
11033
+ /**
11034
+ * Get default configuration (all integrations enabled).
11035
+ */
11036
+ getDefaultConfig() {
11037
+ return {
11038
+ openai: true,
11039
+ anthropic: true,
11040
+ vercel: true,
11041
+ aisdk: true,
11042
+ google: true,
11043
+ claudeAgentSDK: true
11044
+ };
11045
+ }
11046
+ /**
11047
+ * Read configuration from environment variables.
11048
+ * Supports: BRAINTRUST_DISABLE_INSTRUMENTATION=openai,anthropic,...
11049
+ */
11050
+ readEnvConfig() {
11051
+ const integrations = {};
11052
+ const disabledList = isomorph_default.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION");
11053
+ if (disabledList) {
11054
+ const disabled = disabledList.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0);
11055
+ for (const sdk of disabled) {
11056
+ integrations[sdk] = false;
11057
+ }
11058
+ }
11059
+ return { integrations };
11060
+ }
11061
+ };
11062
+ var registry = new PluginRegistry();
11063
+
8446
11064
  // src/node/config.ts
8447
11065
  function configureNode() {
8448
11066
  isomorph_default.buildType = "node";
@@ -8477,6 +11095,7 @@ function configureNode() {
8477
11095
  isomorph_default.gunzip = promisify(zlib.gunzip);
8478
11096
  isomorph_default.hash = (data) => crypto.createHash("sha256").update(data).digest("hex");
8479
11097
  _internalSetInitialState();
11098
+ registry.enable();
8480
11099
  }
8481
11100
 
8482
11101
  // dev/server.ts
@@ -8554,7 +11173,7 @@ function isAsync(fn) {
8554
11173
  function isAsyncGenerator(fn) {
8555
11174
  return fn[Symbol.toStringTag] === "AsyncGenerator";
8556
11175
  }
8557
- function isAsyncIterable(obj) {
11176
+ function isAsyncIterable2(obj) {
8558
11177
  return typeof obj[Symbol.asyncIterator] === "function";
8559
11178
  }
8560
11179
  function wrapAsync(asyncFn) {
@@ -8725,7 +11344,7 @@ var eachOfLimit$2 = (limit) => {
8725
11344
  if (isAsyncGenerator(obj)) {
8726
11345
  return asyncEachOfLimit(obj, limit, iteratee, callback);
8727
11346
  }
8728
- if (isAsyncIterable(obj)) {
11347
+ if (isAsyncIterable2(obj)) {
8729
11348
  return asyncEachOfLimit(obj[Symbol.asyncIterator](), limit, iteratee, callback);
8730
11349
  }
8731
11350
  var nextElem = createIterator(obj);
@@ -10035,7 +12654,7 @@ function callEvaluatorData(data) {
10035
12654
  baseExperiment
10036
12655
  };
10037
12656
  }
10038
- function isAsyncIterable2(value) {
12657
+ function isAsyncIterable3(value) {
10039
12658
  return typeof value === "object" && value !== null && typeof value[Symbol.asyncIterator] === "function";
10040
12659
  }
10041
12660
  function isIterable(value) {
@@ -10238,7 +12857,7 @@ async function runEvaluatorInternal(experiment, evaluator, progressReporter, fil
10238
12857
  }
10239
12858
  const resolvedDataResult = dataResult instanceof Promise ? await dataResult : dataResult;
10240
12859
  const dataIterable = (() => {
10241
- if (isAsyncIterable2(resolvedDataResult)) {
12860
+ if (isAsyncIterable3(resolvedDataResult)) {
10242
12861
  return resolvedDataResult;
10243
12862
  }
10244
12863
  if (Array.isArray(resolvedDataResult) || isIterable(resolvedDataResult)) {