braintrust 3.11.0 → 3.13.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 (77) hide show
  1. package/README.md +8 -8
  2. package/dev/dist/index.d.mts +26 -7
  3. package/dev/dist/index.d.ts +26 -7
  4. package/dev/dist/index.js +2717 -335
  5. package/dev/dist/index.mjs +2499 -117
  6. package/dist/apply-auto-instrumentation.browser.d.mts +2 -0
  7. package/dist/apply-auto-instrumentation.browser.d.ts +2 -0
  8. package/dist/apply-auto-instrumentation.browser.js +18 -0
  9. package/dist/apply-auto-instrumentation.browser.mjs +0 -0
  10. package/dist/apply-auto-instrumentation.d.mts +2 -0
  11. package/dist/apply-auto-instrumentation.d.ts +2 -0
  12. package/dist/apply-auto-instrumentation.js +2534 -0
  13. package/dist/apply-auto-instrumentation.mjs +2534 -0
  14. package/dist/auto-instrumentations/bundler/esbuild.cjs +1803 -1283
  15. package/dist/auto-instrumentations/bundler/esbuild.d.mts +9 -5
  16. package/dist/auto-instrumentations/bundler/esbuild.d.ts +9 -5
  17. package/dist/auto-instrumentations/bundler/esbuild.mjs +10 -2
  18. package/dist/auto-instrumentations/bundler/next.cjs +3269 -0
  19. package/dist/auto-instrumentations/bundler/next.d.mts +3 -0
  20. package/dist/auto-instrumentations/bundler/next.d.ts +3 -0
  21. package/dist/auto-instrumentations/bundler/next.mjs +189 -0
  22. package/dist/auto-instrumentations/bundler/rollup.cjs +1803 -1283
  23. package/dist/auto-instrumentations/bundler/rollup.d.mts +9 -5
  24. package/dist/auto-instrumentations/bundler/rollup.d.ts +9 -5
  25. package/dist/auto-instrumentations/bundler/rollup.mjs +10 -2
  26. package/dist/auto-instrumentations/bundler/vite.cjs +1803 -1283
  27. package/dist/auto-instrumentations/bundler/vite.d.mts +9 -5
  28. package/dist/auto-instrumentations/bundler/vite.d.ts +9 -5
  29. package/dist/auto-instrumentations/bundler/vite.mjs +10 -2
  30. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +1861 -1308
  31. package/dist/auto-instrumentations/bundler/webpack-loader.d.ts +3 -3
  32. package/dist/auto-instrumentations/bundler/webpack.cjs +1803 -1283
  33. package/dist/auto-instrumentations/bundler/webpack.d.mts +9 -5
  34. package/dist/auto-instrumentations/bundler/webpack.d.ts +9 -5
  35. package/dist/auto-instrumentations/bundler/webpack.mjs +6 -6
  36. package/dist/auto-instrumentations/{chunk-DIV5TO4S.mjs → chunk-E5DUYJWK.mjs} +338 -1
  37. package/dist/auto-instrumentations/chunk-GJOO4ESL.mjs +300 -0
  38. package/dist/auto-instrumentations/chunk-WFEUJACP.mjs +18 -0
  39. package/dist/auto-instrumentations/hook.mjs +1713 -1460
  40. package/dist/auto-instrumentations/index.cjs +94 -0
  41. package/dist/auto-instrumentations/index.d.mts +5 -1
  42. package/dist/auto-instrumentations/index.d.ts +5 -1
  43. package/dist/auto-instrumentations/index.mjs +6 -247
  44. package/dist/auto-instrumentations/loader/esm-hook.mjs +19 -2
  45. package/dist/auto-instrumentations/plugin-D7nDswtC.d.mts +44 -0
  46. package/dist/auto-instrumentations/plugin-D7nDswtC.d.ts +44 -0
  47. package/dist/browser.d.mts +264 -47
  48. package/dist/browser.d.ts +264 -47
  49. package/dist/browser.js +2521 -159
  50. package/dist/browser.mjs +2521 -159
  51. package/dist/chunk-26JGOELH.js +817 -0
  52. package/dist/chunk-75IQCUB2.mjs +817 -0
  53. package/dist/cli.js +2510 -122
  54. package/dist/edge-light.d.mts +1 -1
  55. package/dist/edge-light.d.ts +1 -1
  56. package/dist/edge-light.js +2521 -159
  57. package/dist/edge-light.mjs +2521 -159
  58. package/dist/index.d.mts +264 -47
  59. package/dist/index.d.ts +264 -47
  60. package/dist/index.js +3498 -1850
  61. package/dist/index.mjs +2635 -987
  62. package/dist/instrumentation/index.d.mts +7897 -48
  63. package/dist/instrumentation/index.d.ts +7897 -48
  64. package/dist/instrumentation/index.js +2408 -95
  65. package/dist/instrumentation/index.mjs +2407 -95
  66. package/dist/workerd.d.mts +1 -1
  67. package/dist/workerd.d.ts +1 -1
  68. package/dist/workerd.js +2521 -159
  69. package/dist/workerd.mjs +2521 -159
  70. package/package.json +23 -17
  71. package/util/dist/index.d.mts +3 -1
  72. package/util/dist/index.d.ts +3 -1
  73. package/util/dist/index.js +6 -0
  74. package/util/dist/index.mjs +6 -0
  75. package/dist/auto-instrumentations/chunk-G6ZWXGZB.mjs +0 -116
  76. package/dist/auto-instrumentations/plugin-Df3qKIl2.d.mts +0 -22
  77. package/dist/auto-instrumentations/plugin-Df3qKIl2.d.ts +0 -22
@@ -96,6 +96,7 @@ import * as fsSync from "node:fs";
96
96
  import * as crypto from "node:crypto";
97
97
  import { promisify } from "node:util";
98
98
  import * as zlib from "node:zlib";
99
+ import * as dotenv from "dotenv";
99
100
 
100
101
  // src/isomorph.ts
101
102
  var DefaultAsyncLocalStorage = class {
@@ -182,6 +183,7 @@ var iso = {
182
183
  getRepoInfo: async (_settings) => void 0,
183
184
  getPastNAncestors: async () => [],
184
185
  getEnv: (_name) => void 0,
186
+ getBraintrustApiKey: async () => void 0,
185
187
  getCallerLocation: () => void 0,
186
188
  newAsyncLocalStorage: () => new DefaultAsyncLocalStorage(),
187
189
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -406,11 +408,11 @@ function truncateToByteLimit(s, byteLimit = 65536) {
406
408
  return new TextDecoder().decode(truncated);
407
409
  }
408
410
  async function getRepoInfo(settings) {
409
- if (settings && settings.collect === "none") {
411
+ if (!settings || settings.collect === "none") {
410
412
  return void 0;
411
413
  }
412
414
  const repo = await repoInfo();
413
- if (!repo || !settings || settings.collect === "all") {
415
+ if (!repo || settings.collect === "all") {
414
416
  return repo;
415
417
  }
416
418
  let sanitized = {};
@@ -1228,6 +1230,11 @@ function isPromiseLike(value) {
1228
1230
 
1229
1231
  // util/object_util.ts
1230
1232
  var SET_UNION_FIELDS = /* @__PURE__ */ new Set(["tags"]);
1233
+ var FORBIDDEN_MERGE_KEYS = /* @__PURE__ */ new Set([
1234
+ "__proto__",
1235
+ "constructor",
1236
+ "prototype"
1237
+ ]);
1231
1238
  function mergeDictsWithPaths({
1232
1239
  mergeInto,
1233
1240
  mergeFrom,
@@ -1250,6 +1257,7 @@ function mergeDictsWithPathsHelper({
1250
1257
  mergePaths
1251
1258
  }) {
1252
1259
  Object.entries(mergeFrom).forEach(([k, mergeFromV]) => {
1260
+ if (FORBIDDEN_MERGE_KEYS.has(k)) return;
1253
1261
  const fullPath = path2.concat([k]);
1254
1262
  const fullPathSerialized = JSON.stringify(fullPath);
1255
1263
  const mergeIntoV = recordFind(mergeInto, k);
@@ -5306,6 +5314,13 @@ var HTTPConnection = class _HTTPConnection {
5306
5314
  debugLogger.debug(
5307
5315
  `Retrying API request ${object_type} ${JSON.stringify(args)} ${e.status} ${e.text}`
5308
5316
  );
5317
+ const sleepTimeS = HTTP_RETRY_BASE_SLEEP_TIME_S * 2 ** i;
5318
+ debugLogger.info(
5319
+ `Sleeping for ${sleepTimeS}s before retrying API request`
5320
+ );
5321
+ await new Promise(
5322
+ (resolve) => setTimeout(resolve, sleepTimeS * 1e3)
5323
+ );
5309
5324
  continue;
5310
5325
  }
5311
5326
  throw e;
@@ -5828,20 +5843,7 @@ function startSpanParentArgs(args) {
5828
5843
  `Mismatch between expected span parent object type ${args.parentObjectType} and provided type ${parentComponents.data.object_type}`
5829
5844
  );
5830
5845
  }
5831
- const parentComponentsObjectIdLambda = spanComponentsToObjectIdLambda(
5832
- args.state,
5833
- parentComponents
5834
- );
5835
- const computeParentObjectId = async () => {
5836
- const parentComponentsObjectId = await parentComponentsObjectIdLambda();
5837
- if (await args.parentObjectId.get() !== parentComponentsObjectId) {
5838
- throw new Error(
5839
- `Mismatch between expected span parent object id ${await args.parentObjectId.get()} and provided id ${parentComponentsObjectId}`
5840
- );
5841
- }
5842
- return await args.parentObjectId.get();
5843
- };
5844
- argParentObjectId = new LazyValue(computeParentObjectId);
5846
+ argParentObjectId = args.parentObjectId;
5845
5847
  if (parentComponents.data.row_id) {
5846
5848
  argParentSpanIds = {
5847
5849
  spanId: parentComponents.data.span_id,
@@ -6170,6 +6172,7 @@ function now() {
6170
6172
  }
6171
6173
  var DEFAULT_FLUSH_BACKPRESSURE_BYTES = 10 * 1024 * 1024;
6172
6174
  var BACKGROUND_LOGGER_BASE_SLEEP_TIME_S = 1;
6175
+ var HTTP_RETRY_BASE_SLEEP_TIME_S = 1;
6173
6176
  var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
6174
6177
  apiConn;
6175
6178
  queue;
@@ -6800,17 +6803,10 @@ function init(projectOrOptions, optionalOptions) {
6800
6803
  if (repoInfo2) {
6801
6804
  return repoInfo2;
6802
6805
  }
6803
- let mergedGitMetadataSettings = {
6804
- ...state.gitMetadataSettings || {
6805
- collect: "all"
6806
- }
6807
- };
6808
- if (gitMetadataSettings) {
6809
- mergedGitMetadataSettings = mergeGitMetadataSettings(
6810
- mergedGitMetadataSettings,
6811
- gitMetadataSettings
6812
- );
6813
- }
6806
+ const mergedGitMetadataSettings = state.gitMetadataSettings == null ? gitMetadataSettings ?? { collect: "none" } : mergeGitMetadataSettings(
6807
+ state.gitMetadataSettings,
6808
+ gitMetadataSettings ?? { collect: "all" }
6809
+ );
6814
6810
  return await isomorph_default.getRepoInfo(mergedGitMetadataSettings);
6815
6811
  })();
6816
6812
  if (repoInfoArg) {
@@ -7192,6 +7188,55 @@ async function computeLoggerMetadata(state, {
7192
7188
  };
7193
7189
  }
7194
7190
  }
7191
+ function initLogger(options = {}) {
7192
+ const {
7193
+ projectName,
7194
+ projectId,
7195
+ asyncFlush: asyncFlushArg,
7196
+ appUrl,
7197
+ apiKey,
7198
+ orgName,
7199
+ forceLogin,
7200
+ debugLogLevel,
7201
+ fetch: fetch2,
7202
+ state: stateArg
7203
+ } = options || {};
7204
+ const asyncFlush = asyncFlushArg === void 0 ? true : asyncFlushArg;
7205
+ const computeMetadataArgs = {
7206
+ project_name: projectName,
7207
+ project_id: projectId
7208
+ };
7209
+ const linkArgs = {
7210
+ org_name: orgName,
7211
+ app_url: appUrl,
7212
+ project_name: projectName,
7213
+ project_id: projectId
7214
+ };
7215
+ const state = stateArg ?? _globalState;
7216
+ state.setDebugLogLevel(debugLogLevel);
7217
+ state.enforceQueueSizeLimit(true);
7218
+ const lazyMetadata = new LazyValue(
7219
+ async () => {
7220
+ await state.login({
7221
+ orgName,
7222
+ apiKey,
7223
+ appUrl,
7224
+ forceLogin,
7225
+ fetch: fetch2
7226
+ });
7227
+ return computeLoggerMetadata(state, computeMetadataArgs);
7228
+ }
7229
+ );
7230
+ const ret = new Logger(state, lazyMetadata, {
7231
+ asyncFlush,
7232
+ computeMetadataArgs,
7233
+ linkArgs
7234
+ });
7235
+ if (options.setCurrent ?? true) {
7236
+ state.currentLogger = ret;
7237
+ }
7238
+ return ret;
7239
+ }
7195
7240
  async function login(options = {}) {
7196
7241
  const { forceLogin = false } = options || {};
7197
7242
  if (!_internalGetGlobalState()) {
@@ -7223,10 +7268,11 @@ async function login(options = {}) {
7223
7268
  async function loginToState(options = {}) {
7224
7269
  const {
7225
7270
  appUrl = isomorph_default.getEnv("BRAINTRUST_APP_URL") || "https://www.braintrust.dev",
7226
- apiKey = isomorph_default.getEnv("BRAINTRUST_API_KEY"),
7271
+ apiKey: apiKeyArg,
7227
7272
  orgName = isomorph_default.getEnv("BRAINTRUST_ORG_NAME"),
7228
7273
  fetch: fetch2 = globalThis.fetch
7229
7274
  } = options || {};
7275
+ const apiKey = apiKeyArg !== void 0 ? apiKeyArg : await isomorph_default.getBraintrustApiKey();
7230
7276
  const appPublicUrl = isomorph_default.getEnv("BRAINTRUST_APP_PUBLIC_URL") || appUrl;
7231
7277
  const state = new BraintrustState(options);
7232
7278
  state.resetLoginInfo();
@@ -8245,9 +8291,15 @@ var SpanImpl = class _SpanImpl {
8245
8291
  const cachedSpan = {
8246
8292
  input: partialRecord.input,
8247
8293
  output: partialRecord.output,
8294
+ expected: partialRecord.expected,
8295
+ error: partialRecord.error,
8296
+ scores: partialRecord.scores,
8297
+ metrics: partialRecord.metrics,
8248
8298
  metadata: partialRecord.metadata,
8299
+ tags: partialRecord.tags,
8249
8300
  span_id: this._spanId,
8250
8301
  span_parents: this._spanParents,
8302
+ is_root: this._spanId === this._rootSpanId,
8251
8303
  span_attributes: partialRecord.span_attributes
8252
8304
  };
8253
8305
  this._state.spanCache.queueWrite(
@@ -8583,6 +8635,7 @@ var Dataset2 = class extends ObjectFetcher {
8583
8635
  metadata,
8584
8636
  tags,
8585
8637
  output,
8638
+ origin,
8586
8639
  isMerge
8587
8640
  }) {
8588
8641
  return new LazyValue(async () => {
@@ -8597,6 +8650,7 @@ var Dataset2 = class extends ObjectFetcher {
8597
8650
  created: !isMerge ? (/* @__PURE__ */ new Date()).toISOString() : void 0,
8598
8651
  //if we're merging/updating an event we will not add this ts
8599
8652
  metadata,
8653
+ origin,
8600
8654
  ...!!isMerge ? {
8601
8655
  [IS_MERGE_FIELD]: true
8602
8656
  } : {}
@@ -8616,6 +8670,7 @@ var Dataset2 = class extends ObjectFetcher {
8616
8670
  * about anything else that's relevant, that you can use to help find and analyze examples later. For example, you could log the
8617
8671
  * `prompt`, example's `id`, or anything else that would be useful to slice/dice later. The values in `metadata` can be any
8618
8672
  * JSON-serializable type, but its keys must be strings.
8673
+ * @param event.origin (Optional) a reference to the source object this dataset record was derived from.
8619
8674
  * @param event.id (Optional) a unique identifier for the event. If you don't provide one, Braintrust will generate one for you.
8620
8675
  * @param event.output: (Deprecated) The output of your application. Use `expected` instead.
8621
8676
  * @returns The `id` of the logged record.
@@ -8626,7 +8681,8 @@ var Dataset2 = class extends ObjectFetcher {
8626
8681
  metadata,
8627
8682
  tags,
8628
8683
  id,
8629
- output
8684
+ output,
8685
+ origin
8630
8686
  }) {
8631
8687
  this.validateEvent({ metadata, expected, output, tags });
8632
8688
  const rowId = id || uuidv42();
@@ -8638,6 +8694,7 @@ var Dataset2 = class extends ObjectFetcher {
8638
8694
  metadata,
8639
8695
  tags,
8640
8696
  output,
8697
+ origin,
8641
8698
  isMerge: false
8642
8699
  })
8643
8700
  );
@@ -12877,11 +12934,11 @@ function resolveDenyOutputPaths(event, defaultDenyOutputPaths) {
12877
12934
  if (Array.isArray(event?.denyOutputPaths)) {
12878
12935
  return event.denyOutputPaths;
12879
12936
  }
12880
- const firstArgument = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
12881
- if (!firstArgument || typeof firstArgument !== "object") {
12937
+ const firstArgument2 = event?.arguments && event.arguments.length > 0 ? event.arguments[0] : void 0;
12938
+ if (!firstArgument2 || typeof firstArgument2 !== "object") {
12882
12939
  return defaultDenyOutputPaths;
12883
12940
  }
12884
- const runtimeDenyOutputPaths = firstArgument[RUNTIME_DENY_OUTPUT_PATHS];
12941
+ const runtimeDenyOutputPaths = firstArgument2[RUNTIME_DENY_OUTPUT_PATHS];
12885
12942
  if (Array.isArray(runtimeDenyOutputPaths) && runtimeDenyOutputPaths.every((path2) => typeof path2 === "string")) {
12886
12943
  return runtimeDenyOutputPaths;
12887
12944
  }
@@ -16540,6 +16597,467 @@ function cleanMetrics2(metrics) {
16540
16597
  return cleaned;
16541
16598
  }
16542
16599
 
16600
+ // src/instrumentation/plugins/openai-agents-channels.ts
16601
+ var openAIAgentsCoreChannels = defineChannels("@openai/agents-core", {
16602
+ onTraceStart: channel({
16603
+ channelName: "tracing.processor.onTraceStart",
16604
+ kind: "async"
16605
+ }),
16606
+ onTraceEnd: channel({
16607
+ channelName: "tracing.processor.onTraceEnd",
16608
+ kind: "async"
16609
+ }),
16610
+ onSpanStart: channel({
16611
+ channelName: "tracing.processor.onSpanStart",
16612
+ kind: "async"
16613
+ }),
16614
+ onSpanEnd: channel({
16615
+ channelName: "tracing.processor.onSpanEnd",
16616
+ kind: "async"
16617
+ })
16618
+ });
16619
+
16620
+ // src/instrumentation/plugins/openai-agents-trace-processor.ts
16621
+ function isSpanData(spanData, type) {
16622
+ return spanData.type === type;
16623
+ }
16624
+ function spanTypeFromAgents(span) {
16625
+ const spanType = span.spanData.type;
16626
+ if (spanType === "function" || spanType === "guardrail" || spanType === "mcp_tools") {
16627
+ return "tool" /* TOOL */;
16628
+ }
16629
+ if (spanType === "generation" || spanType === "response" || spanType === "transcription" || spanType === "speech") {
16630
+ return "llm" /* LLM */;
16631
+ }
16632
+ return "task" /* TASK */;
16633
+ }
16634
+ function spanNameFromAgents(span) {
16635
+ const spanData = span.spanData;
16636
+ if ("name" in spanData && spanData.name) {
16637
+ return spanData.name;
16638
+ }
16639
+ switch (spanData.type) {
16640
+ case "generation":
16641
+ return "Generation";
16642
+ case "response":
16643
+ return "Response";
16644
+ case "handoff":
16645
+ return "Handoff";
16646
+ case "mcp_tools":
16647
+ return isSpanData(spanData, "mcp_tools") && spanData.server ? `List Tools (${spanData.server})` : "MCP List Tools";
16648
+ case "transcription":
16649
+ return "Transcription";
16650
+ case "speech":
16651
+ return "Speech";
16652
+ case "speech_group":
16653
+ return "Speech Group";
16654
+ default:
16655
+ return "Unknown";
16656
+ }
16657
+ }
16658
+ function getTimeElapsed(end, start) {
16659
+ if (!start || !end) {
16660
+ return void 0;
16661
+ }
16662
+ const startTime = new Date(start).getTime();
16663
+ const endTime = new Date(end).getTime();
16664
+ if (Number.isNaN(startTime) || Number.isNaN(endTime)) {
16665
+ return void 0;
16666
+ }
16667
+ return (endTime - startTime) / 1e3;
16668
+ }
16669
+ function getNumberProperty2(obj, key) {
16670
+ if (!isObject(obj) || !(key in obj)) {
16671
+ return void 0;
16672
+ }
16673
+ const value = obj[key];
16674
+ return typeof value === "number" ? value : void 0;
16675
+ }
16676
+ function parseUsageMetrics(usage) {
16677
+ const metrics = {};
16678
+ if (!isObject(usage)) {
16679
+ return metrics;
16680
+ }
16681
+ const promptTokens = getNumberProperty2(usage, "prompt_tokens") ?? getNumberProperty2(usage, "input_tokens") ?? getNumberProperty2(usage, "promptTokens") ?? getNumberProperty2(usage, "inputTokens");
16682
+ const completionTokens = getNumberProperty2(usage, "completion_tokens") ?? getNumberProperty2(usage, "output_tokens") ?? getNumberProperty2(usage, "completionTokens") ?? getNumberProperty2(usage, "outputTokens");
16683
+ const totalTokens = getNumberProperty2(usage, "total_tokens") ?? getNumberProperty2(usage, "totalTokens");
16684
+ if (promptTokens !== void 0) {
16685
+ metrics.prompt_tokens = promptTokens;
16686
+ }
16687
+ if (completionTokens !== void 0) {
16688
+ metrics.completion_tokens = completionTokens;
16689
+ }
16690
+ if (totalTokens !== void 0) {
16691
+ metrics.tokens = totalTokens;
16692
+ } else if (promptTokens !== void 0 && completionTokens !== void 0) {
16693
+ metrics.tokens = promptTokens + completionTokens;
16694
+ }
16695
+ const inputDetails = usage.input_tokens_details;
16696
+ const cachedTokens = getNumberProperty2(inputDetails, "cached_tokens");
16697
+ const cacheWriteTokens = getNumberProperty2(
16698
+ inputDetails,
16699
+ "cache_write_tokens"
16700
+ );
16701
+ if (cachedTokens !== void 0) {
16702
+ metrics.prompt_cached_tokens = cachedTokens;
16703
+ }
16704
+ if (cacheWriteTokens !== void 0) {
16705
+ metrics.prompt_cache_creation_tokens = cacheWriteTokens;
16706
+ }
16707
+ return metrics;
16708
+ }
16709
+ var OpenAIAgentsTraceProcessor = class _OpenAIAgentsTraceProcessor {
16710
+ static DEFAULT_MAX_TRACES = 1e4;
16711
+ logger;
16712
+ maxTraces;
16713
+ traceSpans = /* @__PURE__ */ new Map();
16714
+ traceOrder = [];
16715
+ _traceSpans = this.traceSpans;
16716
+ constructor(options = {}) {
16717
+ this.logger = options.logger;
16718
+ this.maxTraces = options.maxTraces ?? _OpenAIAgentsTraceProcessor.DEFAULT_MAX_TRACES;
16719
+ }
16720
+ evictOldestTrace() {
16721
+ const oldestTraceId = this.traceOrder.shift();
16722
+ if (oldestTraceId) {
16723
+ this.traceSpans.delete(oldestTraceId);
16724
+ }
16725
+ }
16726
+ onTraceStart(trace) {
16727
+ if (!trace?.traceId) {
16728
+ return Promise.resolve();
16729
+ }
16730
+ if (this.traceOrder.length >= this.maxTraces) {
16731
+ this.evictOldestTrace();
16732
+ }
16733
+ const current = currentSpan();
16734
+ const span = current && current !== NOOP_SPAN ? current.startSpan({
16735
+ name: trace.name,
16736
+ type: "task" /* TASK */
16737
+ }) : this.logger ? this.logger.startSpan({
16738
+ name: trace.name,
16739
+ type: "task" /* TASK */
16740
+ }) : startSpan({
16741
+ name: trace.name,
16742
+ type: "task" /* TASK */
16743
+ });
16744
+ span.log({
16745
+ input: "Agent workflow started",
16746
+ metadata: {
16747
+ group_id: trace.groupId,
16748
+ ...trace.metadata || {}
16749
+ }
16750
+ });
16751
+ this.traceSpans.set(trace.traceId, {
16752
+ rootSpan: span,
16753
+ childSpans: /* @__PURE__ */ new Map(),
16754
+ metadata: {
16755
+ firstInput: null,
16756
+ lastOutput: null
16757
+ }
16758
+ });
16759
+ this.traceOrder.push(trace.traceId);
16760
+ return Promise.resolve();
16761
+ }
16762
+ async onTraceEnd(trace) {
16763
+ const traceData = this.traceSpans.get(trace?.traceId);
16764
+ if (!traceData) {
16765
+ return;
16766
+ }
16767
+ try {
16768
+ traceData.rootSpan.log({
16769
+ input: traceData.metadata.firstInput,
16770
+ output: traceData.metadata.lastOutput
16771
+ });
16772
+ traceData.rootSpan.end();
16773
+ await traceData.rootSpan.flush();
16774
+ } finally {
16775
+ this.traceSpans.delete(trace.traceId);
16776
+ const orderIndex = this.traceOrder.indexOf(trace.traceId);
16777
+ if (orderIndex > -1) {
16778
+ this.traceOrder.splice(orderIndex, 1);
16779
+ }
16780
+ }
16781
+ }
16782
+ onSpanStart(span) {
16783
+ if (!span?.spanId || !span.traceId) {
16784
+ return Promise.resolve();
16785
+ }
16786
+ const traceData = this.traceSpans.get(span.traceId);
16787
+ if (!traceData) {
16788
+ return Promise.resolve();
16789
+ }
16790
+ const parentSpan = span.parentId ? traceData.childSpans.get(span.parentId) : traceData.rootSpan;
16791
+ if (!parentSpan) {
16792
+ return Promise.resolve();
16793
+ }
16794
+ const childSpan = parentSpan.startSpan({
16795
+ name: spanNameFromAgents(span),
16796
+ type: spanTypeFromAgents(span)
16797
+ });
16798
+ traceData.childSpans.set(span.spanId, childSpan);
16799
+ return Promise.resolve();
16800
+ }
16801
+ onSpanEnd(span) {
16802
+ if (!span?.spanId || !span.traceId) {
16803
+ return Promise.resolve();
16804
+ }
16805
+ const traceData = this.traceSpans.get(span.traceId);
16806
+ if (!traceData) {
16807
+ return Promise.resolve();
16808
+ }
16809
+ const braintrustSpan = traceData.childSpans.get(span.spanId);
16810
+ if (!braintrustSpan) {
16811
+ return Promise.resolve();
16812
+ }
16813
+ const logData = this.extractLogData(span);
16814
+ braintrustSpan.log({
16815
+ error: span.error,
16816
+ ...logData
16817
+ });
16818
+ braintrustSpan.end();
16819
+ traceData.childSpans.delete(span.spanId);
16820
+ const input = logData.input;
16821
+ const output = logData.output;
16822
+ if (traceData.metadata.firstInput === null && input != null) {
16823
+ traceData.metadata.firstInput = input;
16824
+ }
16825
+ if (output != null) {
16826
+ traceData.metadata.lastOutput = output;
16827
+ }
16828
+ return Promise.resolve();
16829
+ }
16830
+ async shutdown() {
16831
+ if (this.logger && typeof this.logger.flush === "function") {
16832
+ await this.logger.flush();
16833
+ }
16834
+ }
16835
+ async forceFlush() {
16836
+ if (this.logger && typeof this.logger.flush === "function") {
16837
+ await this.logger.flush();
16838
+ }
16839
+ }
16840
+ extractLogData(span) {
16841
+ const spanData = span.spanData;
16842
+ switch (spanData.type) {
16843
+ case "agent":
16844
+ return this.extractAgentLogData(spanData);
16845
+ case "response":
16846
+ return this.extractResponseLogData(spanData, span);
16847
+ case "function":
16848
+ return this.extractFunctionLogData(spanData);
16849
+ case "handoff":
16850
+ return this.extractHandoffLogData(spanData);
16851
+ case "guardrail":
16852
+ return this.extractGuardrailLogData(spanData);
16853
+ case "generation":
16854
+ return this.extractGenerationLogData(spanData, span);
16855
+ case "custom":
16856
+ return this.extractCustomLogData(spanData);
16857
+ case "mcp_tools":
16858
+ return this.extractMCPListToolsLogData(spanData);
16859
+ case "transcription":
16860
+ return this.extractTranscriptionLogData(spanData);
16861
+ case "speech":
16862
+ return this.extractSpeechLogData(spanData);
16863
+ case "speech_group":
16864
+ return this.extractSpeechGroupLogData(spanData);
16865
+ default:
16866
+ return {};
16867
+ }
16868
+ }
16869
+ extractAgentLogData(spanData) {
16870
+ return {
16871
+ metadata: {
16872
+ tools: spanData.tools,
16873
+ handoffs: spanData.handoffs,
16874
+ output_type: spanData.output_type
16875
+ }
16876
+ };
16877
+ }
16878
+ extractResponseLogData(spanData, span) {
16879
+ const response = spanData._response;
16880
+ const output = isObject(response) ? response.output : void 0;
16881
+ const usage = isObject(response) ? response.usage : void 0;
16882
+ const metrics = {
16883
+ ...this.extractTimingMetrics(span),
16884
+ ...parseUsageMetrics(usage)
16885
+ };
16886
+ return {
16887
+ input: spanData._input,
16888
+ output,
16889
+ metadata: isObject(response) ? this.omitKeys(response, ["output", "usage"]) : {},
16890
+ metrics
16891
+ };
16892
+ }
16893
+ extractFunctionLogData(spanData) {
16894
+ return {
16895
+ input: spanData.input,
16896
+ output: spanData.output
16897
+ };
16898
+ }
16899
+ extractHandoffLogData(spanData) {
16900
+ return {
16901
+ metadata: {
16902
+ from_agent: spanData.from_agent,
16903
+ to_agent: spanData.to_agent
16904
+ }
16905
+ };
16906
+ }
16907
+ extractGuardrailLogData(spanData) {
16908
+ return {
16909
+ metadata: {
16910
+ triggered: spanData.triggered
16911
+ }
16912
+ };
16913
+ }
16914
+ extractGenerationLogData(spanData, span) {
16915
+ return {
16916
+ input: spanData.input,
16917
+ output: spanData.output,
16918
+ metadata: {
16919
+ model: spanData.model,
16920
+ model_config: spanData.model_config
16921
+ },
16922
+ metrics: {
16923
+ ...this.extractTimingMetrics(span),
16924
+ ...parseUsageMetrics(spanData.usage)
16925
+ }
16926
+ };
16927
+ }
16928
+ extractCustomLogData(spanData) {
16929
+ return spanData.data || {};
16930
+ }
16931
+ extractMCPListToolsLogData(spanData) {
16932
+ return {
16933
+ output: spanData.result,
16934
+ metadata: {
16935
+ server: spanData.server
16936
+ }
16937
+ };
16938
+ }
16939
+ extractTranscriptionLogData(spanData) {
16940
+ return {
16941
+ input: spanData.input,
16942
+ output: spanData.output,
16943
+ metadata: {
16944
+ model: spanData.model,
16945
+ model_config: spanData.model_config
16946
+ }
16947
+ };
16948
+ }
16949
+ extractSpeechLogData(spanData) {
16950
+ return {
16951
+ input: spanData.input,
16952
+ output: spanData.output,
16953
+ metadata: {
16954
+ model: spanData.model,
16955
+ model_config: spanData.model_config
16956
+ }
16957
+ };
16958
+ }
16959
+ extractSpeechGroupLogData(spanData) {
16960
+ return {
16961
+ input: spanData.input
16962
+ };
16963
+ }
16964
+ extractTimingMetrics(span) {
16965
+ const timeToFirstToken = getTimeElapsed(
16966
+ span.endedAt ?? void 0,
16967
+ span.startedAt ?? void 0
16968
+ );
16969
+ return timeToFirstToken === void 0 ? {} : { time_to_first_token: timeToFirstToken };
16970
+ }
16971
+ omitKeys(value, keys) {
16972
+ const result = {};
16973
+ for (const [key, fieldValue] of Object.entries(value)) {
16974
+ if (!keys.includes(key)) {
16975
+ result[key] = fieldValue;
16976
+ }
16977
+ }
16978
+ return result;
16979
+ }
16980
+ };
16981
+
16982
+ // src/instrumentation/plugins/openai-agents-plugin.ts
16983
+ function firstArgument(args) {
16984
+ if (Array.isArray(args)) {
16985
+ return args[0];
16986
+ }
16987
+ if (isObject(args) && "length" in args && typeof args.length === "number" && Number.isInteger(args.length) && args.length >= 0) {
16988
+ return Array.from(args)[0];
16989
+ }
16990
+ return void 0;
16991
+ }
16992
+ function isOpenAIAgentsTrace(value) {
16993
+ return isObject(value) && value.type === "trace" && typeof value.traceId === "string";
16994
+ }
16995
+ function isOpenAIAgentsSpan(value) {
16996
+ return isObject(value) && value.type === "trace.span" && typeof value.traceId === "string" && typeof value.spanId === "string";
16997
+ }
16998
+ var OpenAIAgentsPlugin = class extends BasePlugin {
16999
+ processor = new OpenAIAgentsTraceProcessor();
17000
+ onEnable() {
17001
+ this.subscribeToTraceLifecycle();
17002
+ }
17003
+ onDisable() {
17004
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
17005
+ void this.processor.shutdown();
17006
+ }
17007
+ subscribeToTraceLifecycle() {
17008
+ const traceStartChannel = openAIAgentsCoreChannels.onTraceStart.tracingChannel();
17009
+ const traceStartHandlers = {
17010
+ start: (event) => {
17011
+ const trace = firstArgument(event.arguments);
17012
+ if (isOpenAIAgentsTrace(trace)) {
17013
+ void this.processor.onTraceStart(trace);
17014
+ }
17015
+ }
17016
+ };
17017
+ traceStartChannel.subscribe(traceStartHandlers);
17018
+ this.unsubscribers.push(
17019
+ () => traceStartChannel.unsubscribe(traceStartHandlers)
17020
+ );
17021
+ const traceEndChannel = openAIAgentsCoreChannels.onTraceEnd.tracingChannel();
17022
+ const traceEndHandlers = {
17023
+ start: (event) => {
17024
+ const trace = firstArgument(event.arguments);
17025
+ if (isOpenAIAgentsTrace(trace)) {
17026
+ void this.processor.onTraceEnd(trace);
17027
+ }
17028
+ }
17029
+ };
17030
+ traceEndChannel.subscribe(traceEndHandlers);
17031
+ this.unsubscribers.push(
17032
+ () => traceEndChannel.unsubscribe(traceEndHandlers)
17033
+ );
17034
+ const spanStartChannel = openAIAgentsCoreChannels.onSpanStart.tracingChannel();
17035
+ const spanStartHandlers = {
17036
+ start: (event) => {
17037
+ const span = firstArgument(event.arguments);
17038
+ if (isOpenAIAgentsSpan(span)) {
17039
+ void this.processor.onSpanStart(span);
17040
+ }
17041
+ }
17042
+ };
17043
+ spanStartChannel.subscribe(spanStartHandlers);
17044
+ this.unsubscribers.push(
17045
+ () => spanStartChannel.unsubscribe(spanStartHandlers)
17046
+ );
17047
+ const spanEndChannel = openAIAgentsCoreChannels.onSpanEnd.tracingChannel();
17048
+ const spanEndHandlers = {
17049
+ start: (event) => {
17050
+ const span = firstArgument(event.arguments);
17051
+ if (isOpenAIAgentsSpan(span)) {
17052
+ void this.processor.onSpanEnd(span);
17053
+ }
17054
+ }
17055
+ };
17056
+ spanEndChannel.subscribe(spanEndHandlers);
17057
+ this.unsubscribers.push(() => spanEndChannel.unsubscribe(spanEndHandlers));
17058
+ }
17059
+ };
17060
+
16543
17061
  // src/instrumentation/plugins/google-genai-channels.ts
16544
17062
  var googleGenAIChannels = defineChannels("@google/genai", {
16545
17063
  generateContent: channel({
@@ -22832,35 +23350,1745 @@ var GitHubCopilotPlugin = class extends BasePlugin {
22832
23350
  }
22833
23351
  };
22834
23352
 
22835
- // src/instrumentation/braintrust-plugin.ts
22836
- function getIntegrationConfig(integrations, key) {
22837
- return integrations[key];
23353
+ // src/instrumentation/plugins/flue-channels.ts
23354
+ var flueChannels = defineChannels("@flue/runtime", {
23355
+ createContext: channel({
23356
+ channelName: "createFlueContext",
23357
+ kind: "sync-stream"
23358
+ }),
23359
+ openSession: channel({
23360
+ channelName: "Harness.openSession",
23361
+ kind: "async"
23362
+ }),
23363
+ contextEvent: channel({
23364
+ channelName: "context.event",
23365
+ kind: "sync-stream"
23366
+ }),
23367
+ prompt: channel({
23368
+ channelName: "session.prompt",
23369
+ kind: "async"
23370
+ }),
23371
+ skill: channel({
23372
+ channelName: "session.skill",
23373
+ kind: "async"
23374
+ }),
23375
+ task: channel({
23376
+ channelName: "session.task",
23377
+ kind: "async"
23378
+ }),
23379
+ compact: channel({
23380
+ channelName: "session.compact",
23381
+ kind: "async"
23382
+ })
23383
+ });
23384
+
23385
+ // src/wrappers/flue.ts
23386
+ var WRAPPED_FLUE_CONTEXT = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-context");
23387
+ var WRAPPED_FLUE_HARNESS = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-harness");
23388
+ var WRAPPED_FLUE_SESSION = /* @__PURE__ */ Symbol.for("braintrust.flue.wrapped-session");
23389
+ var SUBSCRIBED_FLUE_CONTEXT_EVENTS = /* @__PURE__ */ Symbol.for(
23390
+ "braintrust.flue.subscribed-context-events"
23391
+ );
23392
+ function patchFlueContextInPlace(ctx) {
23393
+ const context = ctx;
23394
+ if (context[WRAPPED_FLUE_CONTEXT]) {
23395
+ return ctx;
23396
+ }
23397
+ const originalInit = context.init.bind(context);
23398
+ try {
23399
+ Object.defineProperty(context, WRAPPED_FLUE_CONTEXT, {
23400
+ configurable: false,
23401
+ enumerable: false,
23402
+ value: true
23403
+ });
23404
+ Object.defineProperty(context, "init", {
23405
+ configurable: true,
23406
+ value: async function wrappedFlueInit(options) {
23407
+ const harness = await originalInit(options);
23408
+ return wrapFlueHarness(harness);
23409
+ },
23410
+ writable: true
23411
+ });
23412
+ } catch {
23413
+ }
23414
+ return ctx;
22838
23415
  }
22839
- var BraintrustPlugin = class extends BasePlugin {
22840
- config;
22841
- openaiPlugin = null;
22842
- openAICodexPlugin = null;
22843
- anthropicPlugin = null;
22844
- aiSDKPlugin = null;
22845
- claudeAgentSDKPlugin = null;
22846
- cursorSDKPlugin = null;
22847
- googleGenAIPlugin = null;
22848
- huggingFacePlugin = null;
22849
- openRouterPlugin = null;
22850
- openRouterAgentPlugin = null;
22851
- mistralPlugin = null;
22852
- googleADKPlugin = null;
22853
- coherePlugin = null;
22854
- groqPlugin = null;
22855
- genkitPlugin = null;
22856
- gitHubCopilotPlugin = null;
22857
- constructor(config = {}) {
22858
- super();
22859
- this.config = config;
23416
+ function subscribeFlueContextEvents(ctx, options = {}) {
23417
+ if (!ctx || typeof ctx !== "object" || typeof ctx.subscribeEvent !== "function") {
23418
+ return void 0;
22860
23419
  }
22861
- onEnable() {
22862
- const integrations = this.config.integrations || {};
22863
- if (integrations.openai !== false) {
23420
+ const context = ctx;
23421
+ const captureTurnSpans = options.captureTurnSpans ?? true;
23422
+ const existingSubscription = context[SUBSCRIBED_FLUE_CONTEXT_EVENTS];
23423
+ if (existingSubscription) {
23424
+ if (existingSubscription.captureTurnSpans || !captureTurnSpans) {
23425
+ return void 0;
23426
+ }
23427
+ try {
23428
+ existingSubscription.unsubscribe();
23429
+ } catch {
23430
+ }
23431
+ }
23432
+ try {
23433
+ const unsubscribe = ctx.subscribeEvent((event) => {
23434
+ flueChannels.contextEvent.traceSync(() => void 0, {
23435
+ arguments: [event],
23436
+ captureTurnSpans,
23437
+ context: ctx
23438
+ });
23439
+ });
23440
+ if (existingSubscription) {
23441
+ existingSubscription.captureTurnSpans = captureTurnSpans;
23442
+ existingSubscription.unsubscribe = unsubscribe;
23443
+ } else {
23444
+ Object.defineProperty(context, SUBSCRIBED_FLUE_CONTEXT_EVENTS, {
23445
+ configurable: false,
23446
+ enumerable: false,
23447
+ value: {
23448
+ captureTurnSpans,
23449
+ unsubscribe
23450
+ }
23451
+ });
23452
+ }
23453
+ return unsubscribe;
23454
+ } catch {
23455
+ return void 0;
23456
+ }
23457
+ }
23458
+ function wrapFlueHarness(harness) {
23459
+ if (!isPlausibleFlueHarness(harness)) {
23460
+ return harness;
23461
+ }
23462
+ const target = harness;
23463
+ if (target[WRAPPED_FLUE_HARNESS]) {
23464
+ return harness;
23465
+ }
23466
+ const originalSession = target.session.bind(target);
23467
+ try {
23468
+ Object.defineProperty(target, WRAPPED_FLUE_HARNESS, {
23469
+ configurable: false,
23470
+ enumerable: false,
23471
+ value: true
23472
+ });
23473
+ Object.defineProperty(target, "session", {
23474
+ configurable: true,
23475
+ value: async function wrappedFlueHarnessSession(name, options) {
23476
+ const session = await originalSession(name, options);
23477
+ return patchFlueSessionInPlace(session);
23478
+ },
23479
+ writable: true
23480
+ });
23481
+ const sessions = target.sessions;
23482
+ if (sessions && typeof sessions === "object") {
23483
+ patchFlueSessionFactory(sessions, "get");
23484
+ patchFlueSessionFactory(sessions, "create");
23485
+ }
23486
+ } catch {
23487
+ }
23488
+ return harness;
23489
+ }
23490
+ function patchFlueSessionInPlace(session) {
23491
+ if (session[WRAPPED_FLUE_SESSION]) {
23492
+ return session;
23493
+ }
23494
+ try {
23495
+ Object.defineProperty(session, WRAPPED_FLUE_SESSION, {
23496
+ configurable: false,
23497
+ enumerable: false,
23498
+ value: true
23499
+ });
23500
+ patchCallHandleMethod(session, "prompt", flueChannels.prompt);
23501
+ patchCallHandleMethod(session, "skill", flueChannels.skill);
23502
+ patchCallHandleMethod(session, "task", flueChannels.task);
23503
+ patchCompact(session);
23504
+ } catch {
23505
+ }
23506
+ return session;
23507
+ }
23508
+ function patchFlueSessionFactory(sessions, method) {
23509
+ const original = sessions[method];
23510
+ if (typeof original !== "function") {
23511
+ return;
23512
+ }
23513
+ const bound = original.bind(sessions);
23514
+ Object.defineProperty(sessions, method, {
23515
+ configurable: true,
23516
+ value: async function wrappedFlueSessionFactory(name, options) {
23517
+ const session = await bound(name, options);
23518
+ return patchFlueSessionInPlace(session);
23519
+ },
23520
+ writable: true
23521
+ });
23522
+ }
23523
+ function patchCallHandleMethod(session, method, channel2) {
23524
+ const original = session[method];
23525
+ if (typeof original !== "function") {
23526
+ return;
23527
+ }
23528
+ const bound = original.bind(session);
23529
+ Object.defineProperty(session, method, {
23530
+ configurable: true,
23531
+ value(input, options) {
23532
+ const args = [input, options];
23533
+ const { originalResult, traced: traced2 } = traceFlueOperation(channel2, {
23534
+ context: {
23535
+ arguments: args,
23536
+ operation: method,
23537
+ session
23538
+ },
23539
+ run: () => bound(input, options)
23540
+ });
23541
+ return preserveCallHandle(originalResult, traced2);
23542
+ },
23543
+ writable: true
23544
+ });
23545
+ }
23546
+ function patchCompact(session) {
23547
+ const original = session.compact;
23548
+ if (typeof original !== "function") {
23549
+ return;
23550
+ }
23551
+ const bound = original.bind(session);
23552
+ Object.defineProperty(session, "compact", {
23553
+ configurable: true,
23554
+ value() {
23555
+ const context = {
23556
+ arguments: [],
23557
+ operation: "compact",
23558
+ session
23559
+ };
23560
+ return flueChannels.compact.tracePromise(() => bound(), context);
23561
+ },
23562
+ writable: true
23563
+ });
23564
+ }
23565
+ function traceFlueOperation(channel2, args) {
23566
+ const tracingChannel2 = channel2.tracingChannel();
23567
+ const context = args.context;
23568
+ let originalResult;
23569
+ let traced2;
23570
+ const run = () => {
23571
+ try {
23572
+ originalResult = args.run();
23573
+ tracingChannel2.end?.publish(context);
23574
+ } catch (error) {
23575
+ context.error = normalizeError3(error);
23576
+ tracingChannel2.error?.publish(context);
23577
+ tracingChannel2.end?.publish(context);
23578
+ throw error;
23579
+ }
23580
+ traced2 = Promise.resolve(originalResult).then(
23581
+ (result) => {
23582
+ context.result = result;
23583
+ tracingChannel2.asyncStart?.publish(context);
23584
+ tracingChannel2.asyncEnd?.publish(context);
23585
+ return result;
23586
+ },
23587
+ (error) => {
23588
+ context.error = normalizeError3(error);
23589
+ tracingChannel2.error?.publish(context);
23590
+ tracingChannel2.asyncStart?.publish(context);
23591
+ tracingChannel2.asyncEnd?.publish(context);
23592
+ throw error;
23593
+ }
23594
+ );
23595
+ };
23596
+ if (tracingChannel2.start?.runStores) {
23597
+ tracingChannel2.start.runStores(context, run);
23598
+ } else {
23599
+ tracingChannel2.start?.publish(context);
23600
+ run();
23601
+ }
23602
+ return { originalResult, traced: traced2 };
23603
+ }
23604
+ function normalizeError3(error) {
23605
+ return error instanceof Error ? error : new Error(String(error));
23606
+ }
23607
+ function preserveCallHandle(originalHandle, traced2) {
23608
+ if (!isFlueCallHandle(originalHandle)) {
23609
+ return traced2;
23610
+ }
23611
+ const handle = originalHandle;
23612
+ const wrapped = {
23613
+ get signal() {
23614
+ return handle.signal;
23615
+ },
23616
+ abort(reason) {
23617
+ return handle.abort(reason);
23618
+ },
23619
+ then(onfulfilled, onrejected) {
23620
+ return traced2.then(onfulfilled, onrejected);
23621
+ }
23622
+ };
23623
+ return wrapped;
23624
+ }
23625
+ function isPlausibleFlueHarness(value) {
23626
+ return !!value && typeof value === "object" && typeof value.session === "function";
23627
+ }
23628
+ function isFlueCallHandle(value) {
23629
+ return !!value && typeof value === "object" && typeof value.then === "function" && typeof value.abort === "function" && "signal" in value;
23630
+ }
23631
+
23632
+ // src/instrumentation/plugins/flue-plugin.ts
23633
+ var FluePlugin = class extends BasePlugin {
23634
+ activeOperationsById = /* @__PURE__ */ new Map();
23635
+ activeOperationsByScope = /* @__PURE__ */ new Map();
23636
+ compactionsByScope = /* @__PURE__ */ new Map();
23637
+ pendingOperationsByKey = /* @__PURE__ */ new Map();
23638
+ tasksById = /* @__PURE__ */ new Map();
23639
+ toolsById = /* @__PURE__ */ new Map();
23640
+ turnsByScope = /* @__PURE__ */ new Map();
23641
+ onEnable() {
23642
+ this.subscribeToContextCreation();
23643
+ this.subscribeToSessionCreation();
23644
+ this.subscribeToContextEvents();
23645
+ this.subscribeToSessionOperations();
23646
+ }
23647
+ onDisable() {
23648
+ for (const unsubscribe of this.unsubscribers) {
23649
+ unsubscribe();
23650
+ }
23651
+ this.unsubscribers = [];
23652
+ this.activeOperationsById.clear();
23653
+ this.activeOperationsByScope.clear();
23654
+ this.compactionsByScope.clear();
23655
+ this.pendingOperationsByKey.clear();
23656
+ this.tasksById.clear();
23657
+ this.toolsById.clear();
23658
+ this.turnsByScope.clear();
23659
+ }
23660
+ subscribeToContextCreation() {
23661
+ const channel2 = flueChannels.createContext.tracingChannel();
23662
+ const handlers = {
23663
+ end: (event) => {
23664
+ const ctx = event.result;
23665
+ if (!ctx) {
23666
+ return;
23667
+ }
23668
+ subscribeFlueContextEvents(ctx, { captureTurnSpans: false });
23669
+ patchFlueContextInPlace(ctx);
23670
+ },
23671
+ error: () => {
23672
+ }
23673
+ };
23674
+ channel2.subscribe(handlers);
23675
+ this.unsubscribers.push(() => {
23676
+ channel2.unsubscribe(handlers);
23677
+ });
23678
+ }
23679
+ subscribeToSessionCreation() {
23680
+ const channel2 = flueChannels.openSession.tracingChannel();
23681
+ const handlers = {
23682
+ asyncEnd: (event) => {
23683
+ if (event.result) {
23684
+ patchFlueSessionInPlace(
23685
+ event.result
23686
+ );
23687
+ }
23688
+ if (event.harness) {
23689
+ wrapFlueHarness(event.harness);
23690
+ }
23691
+ },
23692
+ error: () => {
23693
+ }
23694
+ };
23695
+ channel2.subscribe(handlers);
23696
+ this.unsubscribers.push(() => {
23697
+ channel2.unsubscribe(handlers);
23698
+ });
23699
+ }
23700
+ subscribeToSessionOperations() {
23701
+ this.subscribeToSessionOperation(flueChannels.prompt);
23702
+ this.subscribeToSessionOperation(flueChannels.skill);
23703
+ this.subscribeToSessionOperation(flueChannels.task);
23704
+ this.subscribeToCompact();
23705
+ }
23706
+ subscribeToSessionOperation(channel2) {
23707
+ const tracingChannel2 = channel2.tracingChannel();
23708
+ const states = /* @__PURE__ */ new WeakMap();
23709
+ const ensureState2 = (event) => {
23710
+ const existing = states.get(event);
23711
+ if (existing) {
23712
+ return existing;
23713
+ }
23714
+ const state = this.startOperationState({
23715
+ args: event.arguments,
23716
+ moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
23717
+ operation: event.operation,
23718
+ session: event.session
23719
+ });
23720
+ states.set(event, state);
23721
+ return state;
23722
+ };
23723
+ const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
23724
+ tracingChannel2,
23725
+ ensureState2
23726
+ );
23727
+ const handlers = {
23728
+ start: (event) => {
23729
+ ensureState2(event);
23730
+ },
23731
+ asyncEnd: (event) => {
23732
+ this.endOperationState(states.get(event), event.result);
23733
+ states.delete(event);
23734
+ },
23735
+ error: (event) => {
23736
+ const state = states.get(event);
23737
+ if (state && event.error) {
23738
+ safeLog3(state.span, { error: errorToString(event.error) });
23739
+ this.finishOperationState(state);
23740
+ }
23741
+ states.delete(event);
23742
+ }
23743
+ };
23744
+ tracingChannel2.subscribe(handlers);
23745
+ this.unsubscribers.push(() => {
23746
+ unbindCurrentSpanStore?.();
23747
+ tracingChannel2.unsubscribe(handlers);
23748
+ });
23749
+ }
23750
+ subscribeToCompact() {
23751
+ const tracingChannel2 = flueChannels.compact.tracingChannel();
23752
+ const states = /* @__PURE__ */ new WeakMap();
23753
+ const ensureState2 = (event) => {
23754
+ const existing = states.get(event);
23755
+ if (existing) {
23756
+ return existing;
23757
+ }
23758
+ const state = this.startOperationState({
23759
+ args: [],
23760
+ moduleVersion: typeof event.moduleVersion === "string" ? event.moduleVersion : void 0,
23761
+ operation: event.operation,
23762
+ session: event.session
23763
+ });
23764
+ states.set(event, state);
23765
+ return state;
23766
+ };
23767
+ const unbindCurrentSpanStore = this.bindCurrentSpanStoreToOperationStart(
23768
+ tracingChannel2,
23769
+ ensureState2
23770
+ );
23771
+ const handlers = {
23772
+ start: (event) => {
23773
+ ensureState2(event);
23774
+ },
23775
+ asyncEnd: (event) => {
23776
+ this.endOperationState(states.get(event), void 0);
23777
+ states.delete(event);
23778
+ },
23779
+ error: (event) => {
23780
+ const state = states.get(event);
23781
+ if (state && event.error) {
23782
+ safeLog3(state.span, { error: errorToString(event.error) });
23783
+ this.finishOperationState(state);
23784
+ }
23785
+ states.delete(event);
23786
+ }
23787
+ };
23788
+ tracingChannel2.subscribe(handlers);
23789
+ this.unsubscribers.push(() => {
23790
+ unbindCurrentSpanStore?.();
23791
+ tracingChannel2.unsubscribe(handlers);
23792
+ });
23793
+ }
23794
+ subscribeToContextEvents() {
23795
+ const channel2 = flueChannels.contextEvent.tracingChannel();
23796
+ const handlers = {
23797
+ start: (event) => {
23798
+ const flueEvent = event.arguments[0];
23799
+ if (!flueEvent) {
23800
+ return;
23801
+ }
23802
+ try {
23803
+ this.handleFlueEvent(flueEvent, {
23804
+ captureTurnSpans: event.captureTurnSpans !== false
23805
+ });
23806
+ } catch (error) {
23807
+ logInstrumentationError3("Flue event", error);
23808
+ }
23809
+ },
23810
+ error: () => {
23811
+ }
23812
+ };
23813
+ channel2.subscribe(handlers);
23814
+ this.unsubscribers.push(() => {
23815
+ channel2.unsubscribe(handlers);
23816
+ });
23817
+ }
23818
+ bindCurrentSpanStoreToOperationStart(tracingChannel2, ensureState2) {
23819
+ const state = _internalGetGlobalState();
23820
+ const startChannel = tracingChannel2.start;
23821
+ const contextManager = state?.contextManager;
23822
+ const currentSpanStore = contextManager ? contextManager[BRAINTRUST_CURRENT_SPAN_STORE] : void 0;
23823
+ if (!currentSpanStore || !startChannel) {
23824
+ return void 0;
23825
+ }
23826
+ startChannel.bindStore(currentSpanStore, (event) => {
23827
+ const operationState = ensureState2(event);
23828
+ return contextManager.wrapSpanForStore(operationState.span);
23829
+ });
23830
+ return () => {
23831
+ startChannel.unbindStore(currentSpanStore);
23832
+ };
23833
+ }
23834
+ startOperationState(args) {
23835
+ const sessionName = getSessionName(args.session);
23836
+ const metadata = {
23837
+ ...extractOperationInputMetadata(args.operation, args.args),
23838
+ ...extractSessionMetadata(args.session),
23839
+ "flue.operation": args.operation,
23840
+ provider: "flue",
23841
+ ...args.moduleVersion ? { "flue.version": args.moduleVersion } : {}
23842
+ };
23843
+ const span = startSpan({
23844
+ name: `flue.session.${args.operation}`,
23845
+ spanAttributes: { type: "task" /* TASK */ }
23846
+ });
23847
+ const state = {
23848
+ metadata,
23849
+ operation: args.operation,
23850
+ sessionName,
23851
+ span,
23852
+ startTime: getCurrentUnixTimestamp()
23853
+ };
23854
+ safeLog3(span, {
23855
+ input: extractOperationInput(args.operation, args.args),
23856
+ metadata
23857
+ });
23858
+ this.pendingOperationQueue(operationKey(sessionName, args.operation)).push(
23859
+ state
23860
+ );
23861
+ addOperationToScope(
23862
+ this.activeOperationsByScope,
23863
+ sessionName ?? "unknown",
23864
+ state
23865
+ );
23866
+ return state;
23867
+ }
23868
+ endOperationState(state, result) {
23869
+ if (!state) {
23870
+ return;
23871
+ }
23872
+ const metadata = {
23873
+ ...state.metadata,
23874
+ ...extractPromptResponseMetadata(result)
23875
+ };
23876
+ const metrics = {
23877
+ ...buildDurationMetrics3(state.startTime),
23878
+ ...metricsFromUsage(result?.usage)
23879
+ };
23880
+ safeLog3(state.span, {
23881
+ metadata,
23882
+ metrics,
23883
+ output: extractOperationOutput(result)
23884
+ });
23885
+ this.finishCompactionsForOperation(state);
23886
+ this.finishOperationState(state);
23887
+ }
23888
+ finishOperationState(state) {
23889
+ removePendingOperation(this.pendingOperationsByKey, state);
23890
+ if (state.operationId) {
23891
+ this.activeOperationsById.delete(state.operationId);
23892
+ }
23893
+ removeScopedOperation(this.activeOperationsByScope, state);
23894
+ state.span.end();
23895
+ }
23896
+ handleFlueEvent(event, options) {
23897
+ switch (event.type) {
23898
+ case "operation_start":
23899
+ this.handleOperationStart(event);
23900
+ return;
23901
+ case "operation":
23902
+ this.handleOperation(event);
23903
+ return;
23904
+ case "text_delta":
23905
+ if (!options.captureTurnSpans) {
23906
+ return;
23907
+ }
23908
+ this.ensureTurnState(event).text.push(
23909
+ typeof event.text === "string" ? event.text : ""
23910
+ );
23911
+ return;
23912
+ case "thinking_start":
23913
+ if (!options.captureTurnSpans) {
23914
+ return;
23915
+ }
23916
+ this.handleThinkingStart(event);
23917
+ return;
23918
+ case "thinking_delta":
23919
+ if (!options.captureTurnSpans) {
23920
+ return;
23921
+ }
23922
+ this.handleThinkingDelta(event);
23923
+ return;
23924
+ case "thinking_end":
23925
+ if (!options.captureTurnSpans) {
23926
+ return;
23927
+ }
23928
+ this.handleThinkingEnd(event);
23929
+ return;
23930
+ case "turn":
23931
+ if (!options.captureTurnSpans) {
23932
+ return;
23933
+ }
23934
+ this.handleTurn(event);
23935
+ return;
23936
+ case "tool_start":
23937
+ this.handleToolStart(event, options);
23938
+ return;
23939
+ case "tool_call":
23940
+ this.handleToolCall(event);
23941
+ return;
23942
+ case "task_start":
23943
+ this.handleTaskStart(event);
23944
+ return;
23945
+ case "task":
23946
+ this.handleTask(event);
23947
+ return;
23948
+ case "compaction_start":
23949
+ this.handleCompactionStart(event);
23950
+ return;
23951
+ case "compaction":
23952
+ this.handleCompaction(event);
23953
+ return;
23954
+ default:
23955
+ return;
23956
+ }
23957
+ }
23958
+ handleOperationStart(event) {
23959
+ if (!isInstrumentedOperation(event.operationKind)) {
23960
+ return;
23961
+ }
23962
+ const state = this.takePendingOperationForEvent(event);
23963
+ if (!state) {
23964
+ return;
23965
+ }
23966
+ state.operationId = event.operationId;
23967
+ this.activeOperationsById.set(event.operationId, state);
23968
+ addScopedOperation(this.activeOperationsByScope, event, state);
23969
+ state.metadata = {
23970
+ ...state.metadata,
23971
+ ...extractEventMetadata(event),
23972
+ "flue.operation_id": event.operationId
23973
+ };
23974
+ safeLog3(state.span, { metadata: state.metadata });
23975
+ }
23976
+ handleOperation(event) {
23977
+ const state = event.operationId ? this.activeOperationsById.get(event.operationId) : void 0;
23978
+ if (!state) {
23979
+ return;
23980
+ }
23981
+ const metadata = {
23982
+ ...state.metadata,
23983
+ ...extractEventMetadata(event),
23984
+ ...typeof event.durationMs === "number" ? { "flue.duration_ms": event.durationMs } : {},
23985
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
23986
+ };
23987
+ const metrics = metricsFromUsage(event.usage);
23988
+ safeLog3(state.span, {
23989
+ ...event.error ? { error: errorToString(event.error) } : {},
23990
+ metadata,
23991
+ ...Object.keys(metrics).length ? { metrics } : {}
23992
+ });
23993
+ }
23994
+ ensureTurnState(event) {
23995
+ const scope = scopeKey(event);
23996
+ const existing = this.turnsByScope.get(scope);
23997
+ if (existing) {
23998
+ return existing;
23999
+ }
24000
+ const parent = this.parentSpanForEvent(event);
24001
+ const metadata = {
24002
+ ...extractEventMetadata(event),
24003
+ provider: "flue"
24004
+ };
24005
+ const span = startFlueSpan(parent, {
24006
+ name: "flue.turn",
24007
+ spanAttributes: { type: "llm" /* LLM */ }
24008
+ });
24009
+ const state = {
24010
+ metadata,
24011
+ span,
24012
+ hasThinking: false,
24013
+ startTime: getCurrentUnixTimestamp(),
24014
+ text: [],
24015
+ thinking: [],
24016
+ toolCalls: []
24017
+ };
24018
+ safeLog3(span, { metadata });
24019
+ this.turnsByScope.set(scope, state);
24020
+ return state;
24021
+ }
24022
+ handleTurn(event) {
24023
+ const scope = scopeKey(event);
24024
+ const state = this.ensureTurnState(event);
24025
+ const text = state.text.join("");
24026
+ const reasoning = state.finalThinking ?? state.thinking.join("");
24027
+ const outputReasoning = reasoning || (state.hasThinking ? "[reasoning stream present; content unavailable]" : void 0);
24028
+ const metadata = {
24029
+ ...state.metadata,
24030
+ ...extractEventMetadata(event),
24031
+ ...event.model ? { model: event.model, "flue.model": event.model } : {},
24032
+ ...event.stopReason ? { "flue.stop_reason": event.stopReason } : {},
24033
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {},
24034
+ provider: "flue"
24035
+ };
24036
+ safeLog3(state.span, {
24037
+ ...event.error ? { error: errorToString(event.error) } : {},
24038
+ metadata,
24039
+ metrics: {
24040
+ ...durationMsMetrics(event.durationMs),
24041
+ ...metricsFromUsage(event.usage)
24042
+ },
24043
+ output: toAssistantOutput(
24044
+ text,
24045
+ event.stopReason,
24046
+ outputReasoning,
24047
+ state.toolCalls
24048
+ )
24049
+ });
24050
+ state.span.end();
24051
+ this.turnsByScope.delete(scope);
24052
+ }
24053
+ handleThinkingDelta(event) {
24054
+ const delta = event.delta;
24055
+ if (typeof delta !== "string" || !delta) {
24056
+ return;
24057
+ }
24058
+ const state = this.ensureTurnState(event);
24059
+ state.hasThinking = true;
24060
+ state.metadata["flue.thinking"] = true;
24061
+ state.thinking.push(delta);
24062
+ }
24063
+ handleThinkingStart(event) {
24064
+ const state = this.ensureTurnState(event);
24065
+ state.hasThinking = true;
24066
+ state.metadata["flue.thinking"] = true;
24067
+ }
24068
+ handleThinkingEnd(event) {
24069
+ const state = this.ensureTurnState(event);
24070
+ state.hasThinking = true;
24071
+ state.metadata["flue.thinking"] = true;
24072
+ if (typeof event.content === "string" && event.content) {
24073
+ state.finalThinking = event.content;
24074
+ }
24075
+ }
24076
+ handleToolStart(event, options) {
24077
+ const toolCallId = event.toolCallId;
24078
+ if (!toolCallId) {
24079
+ return;
24080
+ }
24081
+ const parent = this.parentSpanForEvent(event);
24082
+ const scope = scopeKey(event);
24083
+ let turnState = this.turnsByScope.get(scope);
24084
+ if (!turnState && parent && options.captureTurnSpans) {
24085
+ turnState = this.ensureTurnState(event);
24086
+ }
24087
+ const metadata = {
24088
+ ...extractEventMetadata(event),
24089
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24090
+ "flue.tool_call_id": toolCallId,
24091
+ provider: "flue"
24092
+ };
24093
+ const span = startFlueSpan(parent, {
24094
+ name: `tool: ${event.toolName ?? "unknown"}`,
24095
+ spanAttributes: { type: "tool" /* TOOL */ }
24096
+ });
24097
+ if (turnState) {
24098
+ turnState.toolCalls.push({
24099
+ args: event.args,
24100
+ toolCallId,
24101
+ toolName: event.toolName
24102
+ });
24103
+ }
24104
+ safeLog3(span, {
24105
+ input: event.args,
24106
+ metadata
24107
+ });
24108
+ this.toolsById.set(toolKey(event), {
24109
+ metadata,
24110
+ span,
24111
+ startTime: getCurrentUnixTimestamp()
24112
+ });
24113
+ }
24114
+ handleToolCall(event) {
24115
+ const key = toolKey(event);
24116
+ const state = this.toolsById.get(key) ?? this.startSyntheticToolState(event, event.toolName ?? "unknown");
24117
+ const metadata = {
24118
+ ...state.metadata,
24119
+ ...extractEventMetadata(event),
24120
+ ...event.toolName ? { "flue.tool_name": event.toolName } : {},
24121
+ ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
24122
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24123
+ };
24124
+ safeLog3(state.span, {
24125
+ ...event.isError ? { error: errorToString(event.result) } : {},
24126
+ metadata,
24127
+ metrics: durationMsMetrics(event.durationMs),
24128
+ output: event.result
24129
+ });
24130
+ state.span.end();
24131
+ this.toolsById.delete(key);
24132
+ }
24133
+ handleTaskStart(event) {
24134
+ const parent = this.parentSpanForEvent(event);
24135
+ const metadata = {
24136
+ ...extractEventMetadata(event),
24137
+ ...event.role ? { "flue.role": event.role } : {},
24138
+ ...event.cwd ? { "flue.cwd": event.cwd } : {},
24139
+ "flue.task_id": event.taskId,
24140
+ provider: "flue"
24141
+ };
24142
+ const span = startFlueSpan(parent, {
24143
+ name: "flue.task",
24144
+ spanAttributes: { type: "task" /* TASK */ }
24145
+ });
24146
+ safeLog3(span, {
24147
+ input: event.prompt,
24148
+ metadata
24149
+ });
24150
+ this.tasksById.set(event.taskId, {
24151
+ metadata,
24152
+ span,
24153
+ startTime: getCurrentUnixTimestamp()
24154
+ });
24155
+ }
24156
+ handleTask(event) {
24157
+ const state = this.tasksById.get(event.taskId);
24158
+ if (!state) {
24159
+ return;
24160
+ }
24161
+ safeLog3(state.span, {
24162
+ ...event.isError ? { error: errorToString(event.result) } : {},
24163
+ metadata: {
24164
+ ...state.metadata,
24165
+ ...extractEventMetadata(event),
24166
+ ...event.isError !== void 0 ? { "flue.is_error": event.isError } : {}
24167
+ },
24168
+ metrics: durationMsMetrics(event.durationMs),
24169
+ output: event.result
24170
+ });
24171
+ state.span.end();
24172
+ this.tasksById.delete(event.taskId);
24173
+ }
24174
+ handleCompactionStart(event) {
24175
+ const operationState = this.operationStateForEvent(event);
24176
+ const parent = operationState?.span ?? this.parentSpanForEvent(event);
24177
+ const metadata = {
24178
+ ...extractEventMetadata(event),
24179
+ ...event.reason ? { "flue.compaction_reason": event.reason } : {},
24180
+ provider: "flue"
24181
+ };
24182
+ const input = {
24183
+ ...typeof event.estimatedTokens === "number" ? { estimatedTokens: event.estimatedTokens } : {},
24184
+ ...event.reason ? { reason: event.reason } : {}
24185
+ };
24186
+ const span = startFlueSpan(parent, {
24187
+ name: "flue.compaction",
24188
+ spanAttributes: { type: "task" /* TASK */ }
24189
+ });
24190
+ safeLog3(span, {
24191
+ input,
24192
+ metadata
24193
+ });
24194
+ this.compactionsByScope.set(scopeKey(event), {
24195
+ input,
24196
+ metadata,
24197
+ operationState,
24198
+ span,
24199
+ startTime: getCurrentUnixTimestamp()
24200
+ });
24201
+ }
24202
+ handleCompaction(event) {
24203
+ const key = scopeKey(event);
24204
+ const state = this.compactionsByScope.get(key) ?? this.findCompactionState(event);
24205
+ if (!state) {
24206
+ return;
24207
+ }
24208
+ safeLog3(state.span, {
24209
+ metadata: {
24210
+ ...state.metadata,
24211
+ ...extractEventMetadata(event),
24212
+ ...typeof event.messagesBefore === "number" ? { "flue.messages_before": event.messagesBefore } : {},
24213
+ ...typeof event.messagesAfter === "number" ? { "flue.messages_after": event.messagesAfter } : {}
24214
+ },
24215
+ metrics: {
24216
+ ...durationMsMetrics(event.durationMs),
24217
+ ...metricsFromUsage(event.usage)
24218
+ },
24219
+ output: {
24220
+ messagesAfter: event.messagesAfter,
24221
+ messagesBefore: event.messagesBefore
24222
+ }
24223
+ });
24224
+ state.span.end();
24225
+ this.deleteCompactionState(state);
24226
+ }
24227
+ findCompactionState(event) {
24228
+ const operationState = this.operationStateForEvent(event);
24229
+ for (const state of this.compactionsByScope.values()) {
24230
+ if (operationState && state.operationState === operationState) {
24231
+ return state;
24232
+ }
24233
+ }
24234
+ return void 0;
24235
+ }
24236
+ finishCompactionsForOperation(operationState) {
24237
+ for (const state of [...this.compactionsByScope.values()]) {
24238
+ if (state.operationState !== operationState) {
24239
+ continue;
24240
+ }
24241
+ safeLog3(state.span, {
24242
+ input: state.input,
24243
+ metadata: state.metadata,
24244
+ metrics: {
24245
+ ...buildDurationMetrics3(state.startTime)
24246
+ },
24247
+ output: { completed: true }
24248
+ });
24249
+ state.span.end();
24250
+ this.deleteCompactionState(state);
24251
+ }
24252
+ }
24253
+ deleteCompactionState(state) {
24254
+ for (const [key, candidate] of this.compactionsByScope) {
24255
+ if (candidate !== state) {
24256
+ continue;
24257
+ }
24258
+ this.compactionsByScope.delete(key);
24259
+ return;
24260
+ }
24261
+ }
24262
+ startSyntheticToolState(event, toolName) {
24263
+ const parent = this.parentSpanForEvent(event);
24264
+ const metadata = {
24265
+ ...extractEventMetadata(event),
24266
+ ...event.toolCallId ? { "flue.tool_call_id": event.toolCallId } : {},
24267
+ "flue.tool_name": toolName,
24268
+ provider: "flue"
24269
+ };
24270
+ const span = startFlueSpan(parent, {
24271
+ name: `tool: ${toolName}`,
24272
+ spanAttributes: { type: "tool" /* TOOL */ }
24273
+ });
24274
+ safeLog3(span, { metadata });
24275
+ return { metadata, span, startTime: getCurrentUnixTimestamp() };
24276
+ }
24277
+ operationStateForEvent(event) {
24278
+ if (event.operationId) {
24279
+ const operation = this.activeOperationsById.get(event.operationId) ?? this.promotePendingOperationForEvent(event);
24280
+ if (operation) {
24281
+ return operation;
24282
+ }
24283
+ }
24284
+ return this.activeOperationForEventScope(event) ?? this.pendingOperationForEventScope(event);
24285
+ }
24286
+ parentSpanForEvent(event) {
24287
+ if (event.operationId) {
24288
+ const operation = this.operationStateForEvent(event);
24289
+ if (operation) {
24290
+ return operation.span;
24291
+ }
24292
+ }
24293
+ if (event.taskId) {
24294
+ return this.tasksById.get(event.taskId)?.span;
24295
+ }
24296
+ return this.operationStateForEvent(event)?.span;
24297
+ }
24298
+ promotePendingOperationForEvent(event) {
24299
+ if (!event.operationId) {
24300
+ return void 0;
24301
+ }
24302
+ const scopePrefixes = operationScopePrefixes(event);
24303
+ for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24304
+ if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24305
+ continue;
24306
+ }
24307
+ const state = candidateQueue.shift();
24308
+ if (!state) {
24309
+ return void 0;
24310
+ }
24311
+ state.operationId = event.operationId;
24312
+ this.activeOperationsById.set(event.operationId, state);
24313
+ addScopedOperation(this.activeOperationsByScope, event, state);
24314
+ state.metadata = {
24315
+ ...state.metadata,
24316
+ ...extractEventMetadata(event),
24317
+ "flue.operation_id": event.operationId
24318
+ };
24319
+ safeLog3(state.span, { metadata: state.metadata });
24320
+ return state;
24321
+ }
24322
+ return void 0;
24323
+ }
24324
+ activeOperationForEventScope(event) {
24325
+ for (const scope of operationScopeNames(event)) {
24326
+ const operations = this.activeOperationsByScope.get(scope);
24327
+ if (operations?.length) {
24328
+ return operations[operations.length - 1];
24329
+ }
24330
+ }
24331
+ return void 0;
24332
+ }
24333
+ pendingOperationForEventScope(event) {
24334
+ const scopePrefixes = operationScopePrefixes(event);
24335
+ for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24336
+ if (!candidateQueue.length || !operationKeyMatchesScopes(candidateKey, scopePrefixes)) {
24337
+ continue;
24338
+ }
24339
+ return candidateQueue[0];
24340
+ }
24341
+ return void 0;
24342
+ }
24343
+ takePendingOperationForEvent(event) {
24344
+ const key = operationKey(event.session, event.operationKind);
24345
+ const queue2 = this.pendingOperationsByKey.get(key);
24346
+ if (queue2?.length) {
24347
+ return queue2.shift();
24348
+ }
24349
+ for (const [candidateKey, candidateQueue] of this.pendingOperationsByKey) {
24350
+ if (candidateKey.endsWith(`::${event.operationKind}`) && candidateQueue.length) {
24351
+ return candidateQueue.shift();
24352
+ }
24353
+ }
24354
+ return void 0;
24355
+ }
24356
+ pendingOperationQueue(key) {
24357
+ const existing = this.pendingOperationsByKey.get(key);
24358
+ if (existing) {
24359
+ return existing;
24360
+ }
24361
+ const queue2 = [];
24362
+ this.pendingOperationsByKey.set(key, queue2);
24363
+ return queue2;
24364
+ }
24365
+ };
24366
+ function isInstrumentedOperation(operation) {
24367
+ return operation === "prompt" || operation === "skill" || operation === "task" || operation === "compact";
24368
+ }
24369
+ function getSessionName(session) {
24370
+ return typeof session?.name === "string" ? session.name : void 0;
24371
+ }
24372
+ function operationKey(sessionName, operation) {
24373
+ return `${sessionName ?? "unknown"}::${operation}`;
24374
+ }
24375
+ function operationScopePrefixes(event) {
24376
+ const scopes = /* @__PURE__ */ new Set();
24377
+ for (const scope of operationScopeNames(event)) {
24378
+ scopes.add(`${scope}::`);
24379
+ }
24380
+ return scopes;
24381
+ }
24382
+ function operationKeyMatchesScopes(key, scopes) {
24383
+ for (const scope of scopes) {
24384
+ if (key.startsWith(scope)) {
24385
+ return true;
24386
+ }
24387
+ }
24388
+ return false;
24389
+ }
24390
+ function operationScopeNames(event) {
24391
+ const scopes = /* @__PURE__ */ new Set();
24392
+ if (event.session) {
24393
+ scopes.add(event.session);
24394
+ }
24395
+ if (event.parentSession) {
24396
+ scopes.add(event.parentSession);
24397
+ }
24398
+ if (!scopes.size) {
24399
+ scopes.add("unknown");
24400
+ }
24401
+ return scopes;
24402
+ }
24403
+ function addScopedOperation(operationsByScope, event, state) {
24404
+ for (const scope of operationScopeNames(event)) {
24405
+ addOperationToScope(operationsByScope, scope, state);
24406
+ }
24407
+ }
24408
+ function addOperationToScope(operationsByScope, scope, state) {
24409
+ const operations = operationsByScope.get(scope);
24410
+ if (operations) {
24411
+ if (!operations.includes(state)) {
24412
+ operations.push(state);
24413
+ }
24414
+ } else {
24415
+ operationsByScope.set(scope, [state]);
24416
+ }
24417
+ }
24418
+ function removeScopedOperation(operationsByScope, state) {
24419
+ for (const [scope, operations] of operationsByScope) {
24420
+ const index = operations.indexOf(state);
24421
+ if (index === -1) {
24422
+ continue;
24423
+ }
24424
+ operations.splice(index, 1);
24425
+ if (operations.length === 0) {
24426
+ operationsByScope.delete(scope);
24427
+ }
24428
+ }
24429
+ }
24430
+ function removePendingOperation(pendingOperationsByKey, state) {
24431
+ for (const [key, queue2] of pendingOperationsByKey) {
24432
+ const index = queue2.indexOf(state);
24433
+ if (index === -1) {
24434
+ continue;
24435
+ }
24436
+ queue2.splice(index, 1);
24437
+ if (queue2.length === 0) {
24438
+ pendingOperationsByKey.delete(key);
24439
+ }
24440
+ return;
24441
+ }
24442
+ }
24443
+ function extractSessionMetadata(session) {
24444
+ const sessionName = getSessionName(session);
24445
+ return sessionName ? { "flue.session": sessionName } : {};
24446
+ }
24447
+ function extractEventMetadata(event) {
24448
+ return {
24449
+ ...event.runId ? { "flue.run_id": event.runId } : {},
24450
+ ...typeof event.eventIndex === "number" ? { "flue.event_index": event.eventIndex } : {},
24451
+ ...event.session ? { "flue.session": event.session } : {},
24452
+ ...event.parentSession ? { "flue.parent_session": event.parentSession } : {},
24453
+ ...event.harness ? { "flue.harness": event.harness } : {},
24454
+ ...event.taskId ? { "flue.task_id": event.taskId } : {},
24455
+ ...event.operationId ? { "flue.operation_id": event.operationId } : {}
24456
+ };
24457
+ }
24458
+ function extractOperationInput(operation, args) {
24459
+ switch (operation) {
24460
+ case "prompt":
24461
+ case "task":
24462
+ return args[0];
24463
+ case "skill":
24464
+ return {
24465
+ args: getOptionObject(args[1])?.args,
24466
+ name: args[0]
24467
+ };
24468
+ case "compact":
24469
+ return void 0;
24470
+ }
24471
+ }
24472
+ function extractOperationInputMetadata(operation, args) {
24473
+ const options = getOptionObject(args[1]);
24474
+ return {
24475
+ ...operation === "skill" && typeof args[0] === "string" ? { "flue.skill_name": args[0] } : {},
24476
+ ...options?.model ? { model: options.model, "flue.model": options.model } : {},
24477
+ ...options?.role ? { "flue.role": options.role } : {},
24478
+ ...options?.thinkingLevel ? { "flue.thinking_level": options.thinkingLevel } : {},
24479
+ ...typeof options?.cwd === "string" ? { "flue.cwd": options.cwd } : {},
24480
+ ...Array.isArray(options?.tools) ? {
24481
+ "flue.tools_count": options.tools.length,
24482
+ tools: summarizeTools(options.tools)
24483
+ } : {},
24484
+ ...Array.isArray(options?.images) ? { "flue.images_count": options.images.length } : {},
24485
+ ...options?.result || options?.schema ? { "flue.result_schema": true } : {}
24486
+ };
24487
+ }
24488
+ function getOptionObject(value) {
24489
+ return isObject(value) ? value : void 0;
24490
+ }
24491
+ function summarizeTools(tools) {
24492
+ return tools.flatMap((tool) => {
24493
+ if (!isObject(tool)) {
24494
+ return [];
24495
+ }
24496
+ const name = typeof tool.name === "string" ? tool.name : void 0;
24497
+ if (!name) {
24498
+ return [];
24499
+ }
24500
+ return [
24501
+ {
24502
+ function: {
24503
+ description: typeof tool.description === "string" ? tool.description : void 0,
24504
+ name,
24505
+ parameters: tool.parameters
24506
+ },
24507
+ type: "function"
24508
+ }
24509
+ ];
24510
+ });
24511
+ }
24512
+ function extractPromptResponseMetadata(result) {
24513
+ const modelId = result?.model && typeof result.model.id === "string" ? result.model.id : void 0;
24514
+ return modelId ? {
24515
+ model: modelId,
24516
+ "flue.model": modelId
24517
+ } : {};
24518
+ }
24519
+ function extractOperationOutput(result) {
24520
+ if (!result) {
24521
+ return void 0;
24522
+ }
24523
+ if ("data" in result) {
24524
+ return result.data;
24525
+ }
24526
+ if ("text" in result) {
24527
+ return result.text;
24528
+ }
24529
+ return result;
24530
+ }
24531
+ function metricsFromUsage(usage) {
24532
+ return {
24533
+ ...typeof usage?.input === "number" ? { prompt_tokens: usage.input } : {},
24534
+ ...typeof usage?.output === "number" ? { completion_tokens: usage.output } : {},
24535
+ ...typeof usage?.cacheRead === "number" ? { prompt_cached_tokens: usage.cacheRead } : {},
24536
+ ...typeof usage?.cacheWrite === "number" ? { prompt_cache_creation_tokens: usage.cacheWrite } : {},
24537
+ ...typeof usage?.totalTokens === "number" ? { tokens: usage.totalTokens } : {},
24538
+ ...typeof usage?.cost?.total === "number" ? { estimated_cost: usage.cost.total } : {}
24539
+ };
24540
+ }
24541
+ function buildDurationMetrics3(startTime) {
24542
+ return {
24543
+ duration_ms: Math.max(0, (getCurrentUnixTimestamp() - startTime) * 1e3)
24544
+ };
24545
+ }
24546
+ function durationMsMetrics(durationMs) {
24547
+ return typeof durationMs === "number" ? { duration_ms: durationMs } : {};
24548
+ }
24549
+ function scopeKey(event) {
24550
+ if (event.operationId) {
24551
+ return `operation:${event.operationId}`;
24552
+ }
24553
+ if (event.taskId) {
24554
+ return `task:${event.taskId}`;
24555
+ }
24556
+ if (event.session) {
24557
+ return `session:${event.session}`;
24558
+ }
24559
+ return "flue:unknown";
24560
+ }
24561
+ function toolKey(event) {
24562
+ return `${scopeKey(event)}::tool:${event.toolCallId ?? "unknown"}`;
24563
+ }
24564
+ function toAssistantOutput(text, finishReason, reasoning, toolCalls) {
24565
+ return [
24566
+ {
24567
+ finish_reason: finishReason ?? "stop",
24568
+ index: 0,
24569
+ message: {
24570
+ content: text,
24571
+ ...reasoning ? { reasoning } : {},
24572
+ role: "assistant",
24573
+ ...toolCalls?.length ? {
24574
+ tool_calls: toolCalls.map((toolCall) => ({
24575
+ function: {
24576
+ arguments: toolCall.args === void 0 ? "{}" : JSON.stringify(toolCall.args),
24577
+ name: toolCall.toolName ?? "unknown"
24578
+ },
24579
+ ...toolCall.toolCallId ? { id: toolCall.toolCallId } : {},
24580
+ type: "function"
24581
+ }))
24582
+ } : {}
24583
+ }
24584
+ }
24585
+ ];
24586
+ }
24587
+ function startFlueSpan(parent, args) {
24588
+ return parent ? withCurrent(parent, () => startSpan(args)) : startSpan(args);
24589
+ }
24590
+ function safeLog3(span, event) {
24591
+ try {
24592
+ span.log(event);
24593
+ } catch (error) {
24594
+ logInstrumentationError3("Flue span log", error);
24595
+ }
24596
+ }
24597
+ function errorToString(error) {
24598
+ if (error instanceof Error) {
24599
+ return error.message;
24600
+ }
24601
+ if (typeof error === "string") {
24602
+ return error;
24603
+ }
24604
+ try {
24605
+ return JSON.stringify(error);
24606
+ } catch {
24607
+ return String(error);
24608
+ }
24609
+ }
24610
+ function logInstrumentationError3(label, error) {
24611
+ console.error(`Error in ${label} instrumentation:`, error);
24612
+ }
24613
+
24614
+ // src/wrappers/langchain/callback-handler.ts
24615
+ var BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME = "BraintrustCallbackHandler";
24616
+ var BraintrustLangChainCallbackHandler = class {
24617
+ name = BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
24618
+ spans = /* @__PURE__ */ new Map();
24619
+ skippedRuns = /* @__PURE__ */ new Set();
24620
+ parent;
24621
+ rootRunId;
24622
+ options;
24623
+ startTimes = /* @__PURE__ */ new Map();
24624
+ firstTokenTimes = /* @__PURE__ */ new Map();
24625
+ ttftMs = /* @__PURE__ */ new Map();
24626
+ constructor(options) {
24627
+ this.parent = options?.parent;
24628
+ this.options = {
24629
+ debug: options?.debug ?? false,
24630
+ excludeMetadataProps: options?.excludeMetadataProps ?? /^(l[sc]_|langgraph_|__pregel_|checkpoint_ns)/,
24631
+ logger: options?.logger
24632
+ };
24633
+ }
24634
+ startSpan({
24635
+ runId,
24636
+ parentRunId,
24637
+ ...args
24638
+ }) {
24639
+ if (this.spans.has(runId)) {
24640
+ return;
24641
+ }
24642
+ if (!parentRunId) {
24643
+ this.rootRunId = runId;
24644
+ }
24645
+ const tags = args.event?.tags;
24646
+ const spanAttributes = args.spanAttributes || {};
24647
+ spanAttributes.type = args.type || spanAttributes.type || "task";
24648
+ args.type = spanAttributes.type;
24649
+ const currentParent = (typeof this.parent === "function" ? this.parent() : this.parent) ?? currentSpan();
24650
+ let parentSpan;
24651
+ if (parentRunId && this.spans.has(parentRunId)) {
24652
+ parentSpan = this.spans.get(parentRunId);
24653
+ } else if (!Object.is(currentParent, NOOP_SPAN)) {
24654
+ parentSpan = currentParent;
24655
+ } else if (this.options.logger) {
24656
+ parentSpan = this.options.logger;
24657
+ } else {
24658
+ parentSpan = { startSpan };
24659
+ }
24660
+ args.event = {
24661
+ ...args.event,
24662
+ tags: void 0,
24663
+ metadata: {
24664
+ ...tags ? { tags } : {},
24665
+ ...args.event?.metadata,
24666
+ braintrust: {
24667
+ integration_name: "langchain-js",
24668
+ sdk_language: "javascript"
24669
+ },
24670
+ run_id: runId,
24671
+ parent_run_id: parentRunId,
24672
+ ...this.options.debug ? { runId, parentRunId } : {}
24673
+ }
24674
+ };
24675
+ let span = parentSpan.startSpan(args);
24676
+ if (!Object.is(this.options.logger, NOOP_SPAN) && Object.is(span, NOOP_SPAN)) {
24677
+ span = initLogger().startSpan(args);
24678
+ }
24679
+ this.spans.set(runId, span);
24680
+ }
24681
+ endSpan({
24682
+ runId,
24683
+ parentRunId,
24684
+ tags,
24685
+ metadata,
24686
+ ...args
24687
+ }) {
24688
+ if (!this.spans.has(runId)) {
24689
+ return;
24690
+ }
24691
+ if (this.skippedRuns.has(runId)) {
24692
+ this.skippedRuns.delete(runId);
24693
+ return;
24694
+ }
24695
+ const span = this.spans.get(runId);
24696
+ this.spans.delete(runId);
24697
+ if (runId === this.rootRunId) {
24698
+ this.rootRunId = void 0;
24699
+ }
24700
+ span.log({ ...args, metadata: { tags, ...metadata } });
24701
+ span.end();
24702
+ }
24703
+ async handleLLMStart(llm, prompts, runId, parentRunId, extraParams, tags, metadata, runName) {
24704
+ this.startSpan({
24705
+ runId,
24706
+ parentRunId,
24707
+ name: runName ?? getSerializedName(llm) ?? "LLM",
24708
+ type: "llm",
24709
+ event: {
24710
+ input: prompts,
24711
+ tags,
24712
+ metadata: {
24713
+ serialized: llm,
24714
+ name: runName,
24715
+ metadata,
24716
+ ...extraParams
24717
+ }
24718
+ }
24719
+ });
24720
+ }
24721
+ async handleLLMError(err, runId, parentRunId, tags) {
24722
+ this.endSpan({ runId, parentRunId, error: err, tags });
24723
+ }
24724
+ async handleLLMEnd(output, runId, parentRunId, tags) {
24725
+ const metrics = getMetricsFromResponse(output);
24726
+ const modelName2 = getModelNameFromResponse(output);
24727
+ const ttft = this.ttftMs.get(runId);
24728
+ if (ttft !== void 0) {
24729
+ metrics.time_to_first_token = ttft;
24730
+ }
24731
+ this.startTimes.delete(runId);
24732
+ this.firstTokenTimes.delete(runId);
24733
+ this.ttftMs.delete(runId);
24734
+ this.endSpan({
24735
+ runId,
24736
+ parentRunId,
24737
+ output,
24738
+ metrics,
24739
+ tags,
24740
+ metadata: {
24741
+ model: modelName2
24742
+ }
24743
+ });
24744
+ }
24745
+ async handleChatModelStart(llm, messages, runId, parentRunId, extraParams, tags, metadata, runName) {
24746
+ this.startTimes.set(runId, Date.now());
24747
+ this.firstTokenTimes.delete(runId);
24748
+ this.ttftMs.delete(runId);
24749
+ this.startSpan({
24750
+ runId,
24751
+ parentRunId,
24752
+ name: runName ?? getSerializedName(llm) ?? "Chat Model",
24753
+ type: "llm",
24754
+ event: {
24755
+ input: messages,
24756
+ tags,
24757
+ metadata: {
24758
+ serialized: llm,
24759
+ name: runName,
24760
+ metadata,
24761
+ ...extraParams
24762
+ }
24763
+ }
24764
+ });
24765
+ }
24766
+ async handleChainStart(chain, inputs, runId, parentRunId, tags, metadata, runType, runName) {
24767
+ if (tags?.includes("langsmith:hidden")) {
24768
+ this.skippedRuns.add(runId);
24769
+ return;
24770
+ }
24771
+ this.startSpan({
24772
+ runId,
24773
+ parentRunId,
24774
+ name: runName ?? getSerializedName(chain) ?? "Chain",
24775
+ event: {
24776
+ input: inputs,
24777
+ tags,
24778
+ metadata: {
24779
+ serialized: chain,
24780
+ name: runName,
24781
+ metadata,
24782
+ run_type: runType
24783
+ }
24784
+ }
24785
+ });
24786
+ }
24787
+ async handleChainError(err, runId, parentRunId, tags, kwargs) {
24788
+ this.endSpan({ runId, parentRunId, error: err, tags, metadata: kwargs });
24789
+ }
24790
+ async handleChainEnd(outputs, runId, parentRunId, tags, kwargs) {
24791
+ this.endSpan({
24792
+ runId,
24793
+ parentRunId,
24794
+ tags,
24795
+ output: outputs,
24796
+ metadata: { ...kwargs }
24797
+ });
24798
+ }
24799
+ async handleToolStart(tool, input, runId, parentRunId, tags, metadata, runName) {
24800
+ this.startSpan({
24801
+ runId,
24802
+ parentRunId,
24803
+ name: runName ?? getSerializedName(tool) ?? "Tool",
24804
+ type: "llm",
24805
+ event: {
24806
+ input: safeJsonParse(input),
24807
+ tags,
24808
+ metadata: {
24809
+ metadata,
24810
+ serialized: tool,
24811
+ input_str: input,
24812
+ input: safeJsonParse(input),
24813
+ name: runName
24814
+ }
24815
+ }
24816
+ });
24817
+ }
24818
+ async handleToolError(err, runId, parentRunId, tags) {
24819
+ this.endSpan({ runId, parentRunId, error: err, tags });
24820
+ }
24821
+ async handleToolEnd(output, runId, parentRunId, tags) {
24822
+ this.endSpan({ runId, parentRunId, output, tags });
24823
+ }
24824
+ async handleAgentAction(action, runId, parentRunId, tags) {
24825
+ this.startSpan({
24826
+ runId,
24827
+ parentRunId,
24828
+ type: "llm",
24829
+ name: typeof action.tool === "string" ? action.tool : "Agent",
24830
+ event: {
24831
+ input: action,
24832
+ tags
24833
+ }
24834
+ });
24835
+ }
24836
+ async handleAgentEnd(action, runId, parentRunId, tags) {
24837
+ this.endSpan({ runId, parentRunId, output: action, tags });
24838
+ }
24839
+ async handleRetrieverStart(retriever, query, runId, parentRunId, tags, metadata, name) {
24840
+ this.startSpan({
24841
+ runId,
24842
+ parentRunId,
24843
+ name: name ?? getSerializedName(retriever) ?? "Retriever",
24844
+ type: "function",
24845
+ event: {
24846
+ input: query,
24847
+ tags,
24848
+ metadata: {
24849
+ serialized: retriever,
24850
+ metadata,
24851
+ name
24852
+ }
24853
+ }
24854
+ });
24855
+ }
24856
+ async handleRetrieverEnd(documents, runId, parentRunId, tags) {
24857
+ this.endSpan({ runId, parentRunId, output: documents, tags });
24858
+ }
24859
+ async handleRetrieverError(err, runId, parentRunId, tags) {
24860
+ this.endSpan({ runId, parentRunId, error: err, tags });
24861
+ }
24862
+ async handleLLMNewToken(_token, _idx, runId, _parentRunId, _tags) {
24863
+ if (!this.firstTokenTimes.has(runId)) {
24864
+ const now2 = Date.now();
24865
+ this.firstTokenTimes.set(runId, now2);
24866
+ const start = this.startTimes.get(runId);
24867
+ if (start !== void 0) {
24868
+ this.ttftMs.set(runId, (now2 - start) / 1e3);
24869
+ }
24870
+ }
24871
+ }
24872
+ };
24873
+ function getSerializedName(serialized) {
24874
+ if (typeof serialized.name === "string") {
24875
+ return serialized.name;
24876
+ }
24877
+ const lastIdPart = serialized.id?.at(-1);
24878
+ return typeof lastIdPart === "string" ? lastIdPart : void 0;
24879
+ }
24880
+ function cleanObject(obj) {
24881
+ return Object.fromEntries(
24882
+ Object.entries(obj).filter(([, value]) => {
24883
+ if (typeof value !== "number") {
24884
+ return false;
24885
+ }
24886
+ return Number.isFinite(value);
24887
+ })
24888
+ );
24889
+ }
24890
+ function walkGenerations(response) {
24891
+ const result = [];
24892
+ const generations = response.generations || [];
24893
+ for (const batch of generations) {
24894
+ if (Array.isArray(batch)) {
24895
+ for (const generation of batch) {
24896
+ if (isRecord(generation)) {
24897
+ result.push(generation);
24898
+ }
24899
+ }
24900
+ } else if (isRecord(batch)) {
24901
+ result.push(batch);
24902
+ }
24903
+ }
24904
+ return result;
24905
+ }
24906
+ function getModelNameFromResponse(response) {
24907
+ for (const generation of walkGenerations(response)) {
24908
+ const message = generation.message;
24909
+ if (!isRecord(message)) {
24910
+ continue;
24911
+ }
24912
+ const responseMetadata = message.response_metadata;
24913
+ if (!isRecord(responseMetadata)) {
24914
+ continue;
24915
+ }
24916
+ const modelName3 = responseMetadata.model_name ?? responseMetadata.model;
24917
+ if (typeof modelName3 === "string") {
24918
+ return modelName3;
24919
+ }
24920
+ }
24921
+ const llmOutput = response.llmOutput || {};
24922
+ const modelName2 = llmOutput.model_name ?? llmOutput.model;
24923
+ return typeof modelName2 === "string" ? modelName2 : void 0;
24924
+ }
24925
+ function getMetricsFromResponse(response) {
24926
+ for (const generation of walkGenerations(response)) {
24927
+ const message = generation.message;
24928
+ if (!isRecord(message)) {
24929
+ continue;
24930
+ }
24931
+ const usageMetadata = message.usage_metadata;
24932
+ if (!isRecord(usageMetadata)) {
24933
+ continue;
24934
+ }
24935
+ const inputTokenDetails = usageMetadata.input_token_details;
24936
+ return cleanObject({
24937
+ total_tokens: usageMetadata.total_tokens,
24938
+ prompt_tokens: usageMetadata.input_tokens,
24939
+ completion_tokens: usageMetadata.output_tokens,
24940
+ prompt_cache_creation_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_creation : void 0,
24941
+ prompt_cached_tokens: isRecord(inputTokenDetails) ? inputTokenDetails.cache_read : void 0
24942
+ });
24943
+ }
24944
+ const llmOutput = response.llmOutput || {};
24945
+ const tokenUsage = isRecord(llmOutput.tokenUsage) ? llmOutput.tokenUsage : isRecord(llmOutput.estimatedTokens) ? llmOutput.estimatedTokens : {};
24946
+ return cleanObject({
24947
+ total_tokens: tokenUsage.totalTokens,
24948
+ prompt_tokens: tokenUsage.promptTokens,
24949
+ completion_tokens: tokenUsage.completionTokens
24950
+ });
24951
+ }
24952
+ function safeJsonParse(input) {
24953
+ try {
24954
+ return JSON.parse(input);
24955
+ } catch {
24956
+ return input;
24957
+ }
24958
+ }
24959
+ function isRecord(value) {
24960
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24961
+ }
24962
+
24963
+ // src/instrumentation/plugins/langchain-channels.ts
24964
+ var langChainChannels = defineChannels("@langchain/core", {
24965
+ configure: channel({
24966
+ channelName: "CallbackManager.configure",
24967
+ kind: "sync-stream"
24968
+ }),
24969
+ configureSync: channel({
24970
+ channelName: "CallbackManager._configureSync",
24971
+ kind: "sync-stream"
24972
+ })
24973
+ });
24974
+
24975
+ // src/instrumentation/plugins/langchain-plugin.ts
24976
+ var LangChainPlugin = class extends BasePlugin {
24977
+ injectedManagers = /* @__PURE__ */ new WeakSet();
24978
+ onEnable() {
24979
+ this.subscribeToConfigure(langChainChannels.configure);
24980
+ this.subscribeToConfigure(langChainChannels.configureSync);
24981
+ }
24982
+ onDisable() {
24983
+ for (const unsubscribe of this.unsubscribers) {
24984
+ unsubscribe();
24985
+ }
24986
+ this.unsubscribers = [];
24987
+ this.injectedManagers = /* @__PURE__ */ new WeakSet();
24988
+ }
24989
+ subscribeToConfigure(channel2) {
24990
+ const tracingChannel2 = channel2.tracingChannel();
24991
+ const handlers = {
24992
+ start: (event) => {
24993
+ injectHandlerIntoArguments(event.arguments);
24994
+ },
24995
+ end: (event) => {
24996
+ this.injectHandler(event.result);
24997
+ }
24998
+ };
24999
+ tracingChannel2.subscribe(handlers);
25000
+ this.unsubscribers.push(() => {
25001
+ tracingChannel2.unsubscribe(handlers);
25002
+ });
25003
+ }
25004
+ injectHandler(result) {
25005
+ if (!isCallbackManager(result)) {
25006
+ return;
25007
+ }
25008
+ if (this.injectedManagers.has(result) || hasBraintrustHandler(result)) {
25009
+ return;
25010
+ }
25011
+ try {
25012
+ result.addHandler(new BraintrustLangChainCallbackHandler(), true);
25013
+ this.injectedManagers.add(result);
25014
+ } catch {
25015
+ }
25016
+ }
25017
+ };
25018
+ function isCallbackManager(value) {
25019
+ if (typeof value !== "object" || value === null) {
25020
+ return false;
25021
+ }
25022
+ const maybeManager = value;
25023
+ return typeof maybeManager.addHandler === "function";
25024
+ }
25025
+ function hasBraintrustHandler(manager) {
25026
+ return manager.handlers?.some((handler) => {
25027
+ if (typeof handler !== "object" || handler === null) {
25028
+ return false;
25029
+ }
25030
+ const name = Reflect.get(handler, "name");
25031
+ return name === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
25032
+ }) ?? false;
25033
+ }
25034
+ function injectHandlerIntoArguments(args) {
25035
+ if (!isWritableArgumentsObject(args)) {
25036
+ return;
25037
+ }
25038
+ const inheritedHandlers = Reflect.get(args, "0");
25039
+ const handler = new BraintrustLangChainCallbackHandler();
25040
+ if (inheritedHandlers === void 0 || inheritedHandlers === null) {
25041
+ Reflect.set(args, "0", [handler]);
25042
+ return;
25043
+ }
25044
+ if (Array.isArray(inheritedHandlers)) {
25045
+ if (!inheritedHandlers.some(isBraintrustHandler)) {
25046
+ inheritedHandlers.push(handler);
25047
+ }
25048
+ }
25049
+ }
25050
+ function isWritableArgumentsObject(args) {
25051
+ return typeof args === "object" && args !== null;
25052
+ }
25053
+ function isBraintrustHandler(handler) {
25054
+ if (typeof handler !== "object" || handler === null) {
25055
+ return false;
25056
+ }
25057
+ return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
25058
+ }
25059
+
25060
+ // src/instrumentation/braintrust-plugin.ts
25061
+ function getIntegrationConfig(integrations, key) {
25062
+ return integrations[key];
25063
+ }
25064
+ var BraintrustPlugin = class extends BasePlugin {
25065
+ config;
25066
+ openaiPlugin = null;
25067
+ openAICodexPlugin = null;
25068
+ anthropicPlugin = null;
25069
+ aiSDKPlugin = null;
25070
+ claudeAgentSDKPlugin = null;
25071
+ cursorSDKPlugin = null;
25072
+ openAIAgentsPlugin = null;
25073
+ googleGenAIPlugin = null;
25074
+ huggingFacePlugin = null;
25075
+ openRouterPlugin = null;
25076
+ openRouterAgentPlugin = null;
25077
+ mistralPlugin = null;
25078
+ googleADKPlugin = null;
25079
+ coherePlugin = null;
25080
+ groqPlugin = null;
25081
+ genkitPlugin = null;
25082
+ gitHubCopilotPlugin = null;
25083
+ fluePlugin = null;
25084
+ langChainPlugin = null;
25085
+ constructor(config = {}) {
25086
+ super();
25087
+ this.config = config;
25088
+ }
25089
+ onEnable() {
25090
+ const integrations = this.config.integrations || {};
25091
+ if (integrations.openai !== false) {
22864
25092
  this.openaiPlugin = new OpenAIPlugin();
22865
25093
  this.openaiPlugin.enable();
22866
25094
  }
@@ -22884,6 +25112,10 @@ var BraintrustPlugin = class extends BasePlugin {
22884
25112
  this.cursorSDKPlugin = new CursorSDKPlugin();
22885
25113
  this.cursorSDKPlugin.enable();
22886
25114
  }
25115
+ if (integrations.openAIAgents !== false) {
25116
+ this.openAIAgentsPlugin = new OpenAIAgentsPlugin();
25117
+ this.openAIAgentsPlugin.enable();
25118
+ }
22887
25119
  if (integrations.googleGenAI !== false && integrations.google !== false) {
22888
25120
  this.googleGenAIPlugin = new GoogleGenAIPlugin();
22889
25121
  this.googleGenAIPlugin.enable();
@@ -22924,6 +25156,14 @@ var BraintrustPlugin = class extends BasePlugin {
22924
25156
  this.gitHubCopilotPlugin = new GitHubCopilotPlugin();
22925
25157
  this.gitHubCopilotPlugin.enable();
22926
25158
  }
25159
+ if (getIntegrationConfig(integrations, "flue") !== false) {
25160
+ this.fluePlugin = new FluePlugin();
25161
+ this.fluePlugin.enable();
25162
+ }
25163
+ if (integrations.langchain !== false && integrations.langgraph !== false) {
25164
+ this.langChainPlugin = new LangChainPlugin();
25165
+ this.langChainPlugin.enable();
25166
+ }
22927
25167
  }
22928
25168
  onDisable() {
22929
25169
  if (this.openaiPlugin) {
@@ -22950,6 +25190,10 @@ var BraintrustPlugin = class extends BasePlugin {
22950
25190
  this.cursorSDKPlugin.disable();
22951
25191
  this.cursorSDKPlugin = null;
22952
25192
  }
25193
+ if (this.openAIAgentsPlugin) {
25194
+ this.openAIAgentsPlugin.disable();
25195
+ this.openAIAgentsPlugin = null;
25196
+ }
22953
25197
  if (this.googleGenAIPlugin) {
22954
25198
  this.googleGenAIPlugin.disable();
22955
25199
  this.googleGenAIPlugin = null;
@@ -22990,9 +25234,104 @@ var BraintrustPlugin = class extends BasePlugin {
22990
25234
  this.gitHubCopilotPlugin.disable();
22991
25235
  this.gitHubCopilotPlugin = null;
22992
25236
  }
25237
+ if (this.fluePlugin) {
25238
+ this.fluePlugin.disable();
25239
+ this.fluePlugin = null;
25240
+ }
25241
+ if (this.langChainPlugin) {
25242
+ this.langChainPlugin.disable();
25243
+ this.langChainPlugin = null;
25244
+ }
22993
25245
  }
22994
25246
  };
22995
25247
 
25248
+ // src/instrumentation/config.ts
25249
+ var envIntegrationAliases = {
25250
+ openai: "openai",
25251
+ "openai-codex": "openaiCodexSDK",
25252
+ "openai-codex-sdk": "openaiCodexSDK",
25253
+ openaicodexsdk: "openaiCodexSDK",
25254
+ codex: "openaiCodexSDK",
25255
+ "codex-sdk": "openaiCodexSDK",
25256
+ anthropic: "anthropic",
25257
+ aisdk: "aisdk",
25258
+ "ai-sdk": "aisdk",
25259
+ "vercel-ai": "aisdk",
25260
+ vercel: "vercel",
25261
+ claudeagentsdk: "claudeAgentSDK",
25262
+ "claude-agent-sdk": "claudeAgentSDK",
25263
+ cursor: "cursor",
25264
+ "cursor-sdk": "cursorSDK",
25265
+ cursorsdk: "cursorSDK",
25266
+ flue: "flue",
25267
+ "flue-runtime": "flue",
25268
+ "openai-agents": "openAIAgents",
25269
+ openaiagents: "openAIAgents",
25270
+ "openai-agents-core": "openAIAgents",
25271
+ openaiagentscore: "openAIAgents",
25272
+ google: "google",
25273
+ "google-genai": "googleGenAI",
25274
+ googlegenai: "googleGenAI",
25275
+ huggingface: "huggingface",
25276
+ openrouter: "openrouter",
25277
+ openrouteragent: "openrouterAgent",
25278
+ "openrouter-agent": "openrouterAgent",
25279
+ mistral: "mistral",
25280
+ googleadk: "googleADK",
25281
+ "google-adk": "googleADK",
25282
+ cohere: "cohere",
25283
+ groq: "groq",
25284
+ "groq-sdk": "groq",
25285
+ genkit: "genkit",
25286
+ "firebase-genkit": "genkit",
25287
+ githubcopilot: "gitHubCopilot",
25288
+ "github-copilot": "gitHubCopilot",
25289
+ "copilot-sdk": "gitHubCopilot",
25290
+ langchain: "langchain",
25291
+ "langchain-js": "langchain",
25292
+ "@langchain": "langchain",
25293
+ langgraph: "langgraph"
25294
+ };
25295
+ function getDefaultInstrumentationIntegrations() {
25296
+ return {
25297
+ openai: true,
25298
+ openaiCodexSDK: true,
25299
+ anthropic: true,
25300
+ vercel: true,
25301
+ aisdk: true,
25302
+ google: true,
25303
+ googleGenAI: true,
25304
+ googleADK: true,
25305
+ huggingface: true,
25306
+ claudeAgentSDK: true,
25307
+ cursor: true,
25308
+ cursorSDK: true,
25309
+ flue: true,
25310
+ openAIAgents: true,
25311
+ openrouter: true,
25312
+ openrouterAgent: true,
25313
+ mistral: true,
25314
+ cohere: true,
25315
+ groq: true,
25316
+ genkit: true,
25317
+ gitHubCopilot: true,
25318
+ langchain: true,
25319
+ langgraph: true
25320
+ };
25321
+ }
25322
+ function readDisabledInstrumentationEnvConfig(disabledList) {
25323
+ const integrations = {};
25324
+ if (disabledList) {
25325
+ for (const value of disabledList.split(",")) {
25326
+ const sdk = value.trim().toLowerCase();
25327
+ if (sdk.length > 0) {
25328
+ integrations[envIntegrationAliases[sdk] ?? sdk] = false;
25329
+ }
25330
+ }
25331
+ }
25332
+ return { integrations };
25333
+ }
25334
+
22996
25335
  // src/instrumentation/registry.ts
22997
25336
  var REGISTRY_STATE_KEY = /* @__PURE__ */ Symbol.for("braintrust.registry");
22998
25337
  function getSharedState() {
@@ -23071,60 +25410,75 @@ var PluginRegistry = class {
23071
25410
  * Get default configuration (all integrations enabled).
23072
25411
  */
23073
25412
  getDefaultConfig() {
23074
- return {
23075
- openai: true,
23076
- openaiCodexSDK: true,
23077
- anthropic: true,
23078
- vercel: true,
23079
- aisdk: true,
23080
- google: true,
23081
- googleGenAI: true,
23082
- googleADK: true,
23083
- huggingface: true,
23084
- claudeAgentSDK: true,
23085
- cursor: true,
23086
- cursorSDK: true,
23087
- openrouter: true,
23088
- openrouterAgent: true,
23089
- mistral: true,
23090
- cohere: true,
23091
- groq: true,
23092
- genkit: true,
23093
- gitHubCopilot: true
23094
- };
25413
+ return getDefaultInstrumentationIntegrations();
23095
25414
  }
23096
25415
  /**
23097
25416
  * Read configuration from environment variables.
23098
25417
  * Supports: BRAINTRUST_DISABLE_INSTRUMENTATION=openai,anthropic,...
23099
25418
  */
23100
25419
  readEnvConfig() {
23101
- const integrations = {};
23102
- const disabledList = isomorph_default.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION");
23103
- if (disabledList) {
23104
- const disabled = disabledList.split(",").map((s) => s.trim().toLowerCase()).filter((s) => s.length > 0);
23105
- for (const sdk of disabled) {
23106
- if (sdk === "cursor-sdk") {
23107
- integrations.cursorSDK = false;
23108
- } else if (sdk === "githubcopilot" || sdk === "github-copilot" || sdk === "copilot-sdk") {
23109
- integrations.gitHubCopilot = false;
23110
- } else if (sdk === "openai-codex-sdk") {
23111
- integrations.openaiCodexSDK = false;
23112
- } else {
23113
- integrations[sdk] = false;
23114
- }
23115
- }
23116
- }
23117
- return { integrations };
25420
+ return readDisabledInstrumentationEnvConfig(
25421
+ isomorph_default.getEnv("BRAINTRUST_DISABLE_INSTRUMENTATION")
25422
+ );
23118
25423
  }
23119
25424
  };
23120
25425
  var registry = new PluginRegistry();
23121
25426
 
23122
25427
  // src/node/config.ts
25428
+ var BRAINTRUST_ENV_SEARCH_PARENT_LIMIT = 64;
23123
25429
  function configureNode() {
23124
25430
  isomorph_default.buildType = "node";
23125
25431
  isomorph_default.getRepoInfo = getRepoInfo;
23126
25432
  isomorph_default.getPastNAncestors = getPastNAncestors;
23127
- isomorph_default.getEnv = (name) => process.env[name];
25433
+ isomorph_default.getEnv = (name) => {
25434
+ const value = process.env[name];
25435
+ return name === "BRAINTRUST_API_KEY" && !value?.trim() ? void 0 : value;
25436
+ };
25437
+ isomorph_default.getBraintrustApiKey = async () => {
25438
+ const value = process.env.BRAINTRUST_API_KEY;
25439
+ if (value?.trim()) {
25440
+ return value;
25441
+ }
25442
+ const envPaths = [];
25443
+ for (let dir2 = process.cwd(), depth = 0; depth <= BRAINTRUST_ENV_SEARCH_PARENT_LIMIT; dir2 = path.dirname(dir2), depth++) {
25444
+ envPaths.push(path.join(dir2, ".env.braintrust"));
25445
+ if (path.dirname(dir2) === dir2) {
25446
+ break;
25447
+ }
25448
+ }
25449
+ const pending = /* @__PURE__ */ new Map();
25450
+ envPaths.forEach((envPath, index) => {
25451
+ pending.set(
25452
+ index,
25453
+ fs.readFile(envPath, "utf8").then(
25454
+ (contents) => ({ contents, envPath, index }),
25455
+ (error) => ({ error, envPath, index })
25456
+ )
25457
+ );
25458
+ });
25459
+ const results = [];
25460
+ let nearestUnresolvedIndex = 0;
25461
+ while (pending.size > 0) {
25462
+ const result = await Promise.race(pending.values());
25463
+ pending.delete(result.index);
25464
+ results[result.index] = result;
25465
+ while (results[nearestUnresolvedIndex]) {
25466
+ const nearestResult = results[nearestUnresolvedIndex];
25467
+ if ("contents" in nearestResult) {
25468
+ const parsed = dotenv.parse(nearestResult.contents);
25469
+ const apiKey = parsed.BRAINTRUST_API_KEY;
25470
+ return apiKey?.trim() ? apiKey : void 0;
25471
+ }
25472
+ const e = nearestResult.error;
25473
+ if (typeof e === "object" && e !== null && "code" in e && e.code === "ENOENT") {
25474
+ nearestUnresolvedIndex++;
25475
+ continue;
25476
+ }
25477
+ return void 0;
25478
+ }
25479
+ }
25480
+ return void 0;
25481
+ };
23128
25482
  isomorph_default.getCallerLocation = getCallerLocation;
23129
25483
  isomorph_default.newAsyncLocalStorage = () => new AsyncLocalStorage();
23130
25484
  isomorph_default.newTracingChannel = (nameOrChannels) => diagnostics_channel.tracingChannel(nameOrChannels);
@@ -24279,8 +26633,12 @@ async function invoke(args) {
24279
26633
 
24280
26634
  // src/trace.ts
24281
26635
  var SpanFetcher = class _SpanFetcher extends ObjectFetcher {
24282
- constructor(objectType, _objectId, rootSpanId, _state, spanTypeFilter) {
24283
- const filterExpr = _SpanFetcher.buildFilter(rootSpanId, spanTypeFilter);
26636
+ constructor(objectType, _objectId, rootSpanId, _state, spanTypeFilter, includeScorers = false) {
26637
+ const filterExpr = _SpanFetcher.buildFilter(
26638
+ rootSpanId,
26639
+ spanTypeFilter,
26640
+ includeScorers
26641
+ );
24284
26642
  super(objectType, void 0, void 0, {
24285
26643
  filter: filterExpr
24286
26644
  });
@@ -24289,16 +26647,17 @@ var SpanFetcher = class _SpanFetcher extends ObjectFetcher {
24289
26647
  this._state = _state;
24290
26648
  this.spanTypeFilter = spanTypeFilter;
24291
26649
  }
24292
- static buildFilter(rootSpanId, spanTypeFilter) {
26650
+ static buildFilter(rootSpanId, spanTypeFilter, includeScorers = false) {
24293
26651
  const children = [
24294
26652
  // Base filter: root_span_id = 'value'
24295
26653
  {
24296
26654
  op: "eq",
24297
26655
  left: { op: "ident", name: ["root_span_id"] },
24298
26656
  right: { op: "literal", value: rootSpanId }
24299
- },
24300
- // Exclude span_attributes.purpose = 'score'
24301
- {
26657
+ }
26658
+ ];
26659
+ if (!includeScorers) {
26660
+ children.push({
24302
26661
  op: "or",
24303
26662
  children: [
24304
26663
  {
@@ -24311,8 +26670,8 @@ var SpanFetcher = class _SpanFetcher extends ObjectFetcher {
24311
26670
  right: { op: "literal", value: "scorer" }
24312
26671
  }
24313
26672
  ]
24314
- }
24315
- ];
26673
+ });
26674
+ }
24316
26675
  if (spanTypeFilter && spanTypeFilter.length > 0) {
24317
26676
  children.push({
24318
26677
  op: "in",
@@ -24338,35 +26697,49 @@ var CachedSpanFetcher = class {
24338
26697
  fetchFn;
24339
26698
  constructor(objectTypeOrFetchFn, objectId, rootSpanId, getState) {
24340
26699
  if (typeof objectTypeOrFetchFn === "function") {
24341
- this.fetchFn = objectTypeOrFetchFn;
26700
+ this.fetchFn = (spanType) => objectTypeOrFetchFn(spanType);
24342
26701
  } else {
24343
26702
  const objectType = objectTypeOrFetchFn;
24344
- this.fetchFn = async (spanType) => {
26703
+ this.fetchFn = async (spanType, includeScorers) => {
24345
26704
  const state = await getState();
24346
26705
  const fetcher = new SpanFetcher(
24347
26706
  objectType,
24348
26707
  objectId,
24349
26708
  rootSpanId,
24350
26709
  state,
24351
- spanType
26710
+ spanType,
26711
+ includeScorers
24352
26712
  );
24353
26713
  const rows = await fetcher.fetchedData();
24354
- return rows.filter((row) => row.span_attributes?.purpose !== "scorer").map((row) => ({
26714
+ return rows.map((row) => ({
24355
26715
  input: row.input,
24356
26716
  output: row.output,
26717
+ expected: row.expected,
26718
+ error: row.error,
26719
+ scores: row.scores,
26720
+ metrics: row.metrics,
24357
26721
  metadata: row.metadata,
24358
26722
  span_id: row.span_id,
24359
26723
  span_parents: row.span_parents,
26724
+ is_root: row.is_root,
24360
26725
  span_attributes: row.span_attributes,
24361
26726
  id: row.id,
24362
26727
  _xact_id: row._xact_id,
24363
26728
  _pagination_key: row._pagination_key,
24364
- root_span_id: row.root_span_id
26729
+ root_span_id: row.root_span_id,
26730
+ created: row.created,
26731
+ tags: row.tags
24365
26732
  }));
24366
26733
  };
24367
26734
  }
24368
26735
  }
24369
- async getSpans({ spanType } = {}) {
26736
+ async getSpans({
26737
+ spanType,
26738
+ includeScorers = false
26739
+ } = {}) {
26740
+ if (includeScorers) {
26741
+ return this.fetchFn(spanType, true);
26742
+ }
24370
26743
  if (this.allFetched) {
24371
26744
  return this.getFromCache(spanType);
24372
26745
  }
@@ -24383,7 +26756,7 @@ var CachedSpanFetcher = class {
24383
26756
  return this.getFromCache(spanType);
24384
26757
  }
24385
26758
  async fetchSpans(spanType) {
24386
- const spans = await this.fetchFn(spanType);
26759
+ const spans = await this.fetchFn(spanType, false);
24387
26760
  for (const span of spans) {
24388
26761
  const type = span.span_attributes?.type ?? "";
24389
26762
  const existing = this.spanCache.get(type) ?? [];
@@ -24461,10 +26834,13 @@ var LocalTrace = class {
24461
26834
  * First checks the local span cache for recently logged spans, then falls
24462
26835
  * back to CachedSpanFetcher which handles BTQL fetching and caching.
24463
26836
  */
24464
- async getSpans({ spanType } = {}) {
26837
+ async getSpans({
26838
+ spanType,
26839
+ includeScorers = false
26840
+ } = {}) {
24465
26841
  const cachedSpans = this.state.spanCache.getByRootSpanId(this.rootSpanId);
24466
26842
  if (cachedSpans && cachedSpans.length > 0) {
24467
- let spans = cachedSpans.filter(
26843
+ let spans = includeScorers ? cachedSpans : cachedSpans.filter(
24468
26844
  (span) => span.span_attributes?.purpose !== "scorer"
24469
26845
  );
24470
26846
  if (spanType && spanType.length > 0) {
@@ -24475,13 +26851,19 @@ var LocalTrace = class {
24475
26851
  return spans.map((span) => ({
24476
26852
  input: span.input,
24477
26853
  output: span.output,
26854
+ expected: span.expected,
26855
+ error: span.error,
26856
+ scores: span.scores,
26857
+ metrics: span.metrics,
24478
26858
  metadata: span.metadata,
24479
26859
  span_id: span.span_id,
24480
26860
  span_parents: span.span_parents,
24481
- span_attributes: span.span_attributes
26861
+ is_root: span.is_root,
26862
+ span_attributes: span.span_attributes,
26863
+ tags: span.tags
24482
26864
  }));
24483
26865
  }
24484
- return this.cachedFetcher.getSpans({ spanType });
26866
+ return this.cachedFetcher.getSpans({ spanType, includeScorers });
24485
26867
  }
24486
26868
  /**
24487
26869
  * Get the thread (preprocessed messages) for this trace.