braintrust 3.21.0 → 3.23.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 (54) hide show
  1. package/README.md +47 -0
  2. package/dev/dist/index.d.mts +1030 -1
  3. package/dev/dist/index.d.ts +1030 -1
  4. package/dev/dist/index.js +1429 -663
  5. package/dev/dist/index.mjs +811 -45
  6. package/dist/apply-auto-instrumentation.js +222 -186
  7. package/dist/apply-auto-instrumentation.mjs +37 -1
  8. package/dist/auto-instrumentations/bundler/esbuild.cjs +53 -1
  9. package/dist/auto-instrumentations/bundler/esbuild.mjs +2 -2
  10. package/dist/auto-instrumentations/bundler/next.cjs +53 -1
  11. package/dist/auto-instrumentations/bundler/next.mjs +3 -3
  12. package/dist/auto-instrumentations/bundler/rollup.cjs +53 -1
  13. package/dist/auto-instrumentations/bundler/rollup.mjs +2 -2
  14. package/dist/auto-instrumentations/bundler/vite.cjs +53 -1
  15. package/dist/auto-instrumentations/bundler/vite.mjs +2 -2
  16. package/dist/auto-instrumentations/bundler/webpack-loader.cjs +53 -1
  17. package/dist/auto-instrumentations/bundler/webpack.cjs +53 -1
  18. package/dist/auto-instrumentations/bundler/webpack.mjs +3 -3
  19. package/dist/auto-instrumentations/{chunk-TKRPRPGD.mjs → chunk-EXY7QCJD.mjs} +5 -2
  20. package/dist/auto-instrumentations/{chunk-T6J4C7LX.mjs → chunk-KIMLYPRW.mjs} +1 -1
  21. package/dist/auto-instrumentations/{chunk-BRQX23KL.mjs → chunk-YXLNSAMJ.mjs} +51 -0
  22. package/dist/auto-instrumentations/hook.mjs +224 -31
  23. package/dist/auto-instrumentations/index.cjs +52 -0
  24. package/dist/auto-instrumentations/index.d.mts +3 -1
  25. package/dist/auto-instrumentations/index.d.ts +3 -1
  26. package/dist/auto-instrumentations/index.mjs +3 -1
  27. package/dist/browser.d.mts +3273 -93
  28. package/dist/browser.d.ts +3273 -93
  29. package/dist/browser.js +2495 -63
  30. package/dist/browser.mjs +2495 -63
  31. package/dist/{chunk-KMGUTPB7.mjs → chunk-36IPYKMG.mjs} +20 -1
  32. package/dist/{chunk-BFGIH2ZJ.js → chunk-CDIKAHDZ.js} +21 -2
  33. package/dist/{chunk-FY7DAKA5.js → chunk-FZWPFCVE.js} +1590 -832
  34. package/dist/{chunk-O2P765XK.mjs → chunk-IXL4PMY4.mjs} +802 -44
  35. package/dist/cli.js +816 -47
  36. package/dist/edge-light.d.mts +1 -1
  37. package/dist/edge-light.d.ts +1 -1
  38. package/dist/edge-light.js +2495 -63
  39. package/dist/edge-light.mjs +2495 -63
  40. package/dist/index.d.mts +3273 -93
  41. package/dist/index.d.ts +3273 -93
  42. package/dist/index.js +2078 -416
  43. package/dist/index.mjs +1664 -2
  44. package/dist/instrumentation/index.d.mts +912 -2
  45. package/dist/instrumentation/index.d.ts +912 -2
  46. package/dist/instrumentation/index.js +2202 -42
  47. package/dist/instrumentation/index.mjs +2200 -42
  48. package/dist/vitest-evals-reporter.js +16 -16
  49. package/dist/vitest-evals-reporter.mjs +2 -2
  50. package/dist/workerd.d.mts +1 -1
  51. package/dist/workerd.d.ts +1 -1
  52. package/dist/workerd.js +2495 -63
  53. package/dist/workerd.mjs +2495 -63
  54. package/package.json +5 -2
@@ -2510,19 +2510,30 @@ var CallEvent = z6.union([
2510
2510
  var ChatCompletionContentPartTextWithTitle = z6.object({
2511
2511
  text: z6.string().default(""),
2512
2512
  type: z6.literal("text"),
2513
- cache_control: z6.object({ type: z6.literal("ephemeral") }).optional()
2513
+ cache_control: z6.object({
2514
+ type: z6.literal("ephemeral"),
2515
+ ttl: z6.enum(["5m", "1h"]).optional()
2516
+ }).optional()
2514
2517
  });
2515
2518
  var ChatCompletionContentPartImageWithTitle = z6.object({
2516
2519
  image_url: z6.object({
2517
2520
  url: z6.string(),
2518
2521
  detail: z6.union([z6.literal("auto"), z6.literal("low"), z6.literal("high")]).optional()
2519
2522
  }),
2520
- type: z6.literal("image_url")
2523
+ type: z6.literal("image_url"),
2524
+ cache_control: z6.object({
2525
+ type: z6.literal("ephemeral"),
2526
+ ttl: z6.enum(["5m", "1h"]).optional()
2527
+ }).optional()
2521
2528
  });
2522
2529
  var ChatCompletionContentPartFileFile = z6.object({ file_data: z6.string(), filename: z6.string(), file_id: z6.string() }).partial();
2523
2530
  var ChatCompletionContentPartFileWithTitle = z6.object({
2524
2531
  file: ChatCompletionContentPartFileFile,
2525
- type: z6.literal("file")
2532
+ type: z6.literal("file"),
2533
+ cache_control: z6.object({
2534
+ type: z6.literal("ephemeral"),
2535
+ ttl: z6.enum(["5m", "1h"]).optional()
2536
+ }).optional()
2526
2537
  });
2527
2538
  var ChatCompletionContentPart = z6.union([
2528
2539
  ChatCompletionContentPartTextWithTitle,
@@ -2532,7 +2543,10 @@ var ChatCompletionContentPart = z6.union([
2532
2543
  var ChatCompletionContentPartText = z6.object({
2533
2544
  text: z6.string().default(""),
2534
2545
  type: z6.literal("text"),
2535
- cache_control: z6.object({ type: z6.literal("ephemeral") }).optional()
2546
+ cache_control: z6.object({
2547
+ type: z6.literal("ephemeral"),
2548
+ ttl: z6.enum(["5m", "1h"]).optional()
2549
+ }).optional()
2536
2550
  });
2537
2551
  var ChatCompletionMessageToolCall = z6.object({
2538
2552
  id: z6.string(),
@@ -4569,7 +4583,7 @@ var DiskCache = class {
4569
4583
  }
4570
4584
  };
4571
4585
 
4572
- // src/prompt-cache/lru-cache.ts
4586
+ // src/lru-cache.ts
4573
4587
  var LRUCache = class {
4574
4588
  cache;
4575
4589
  maxSize;
@@ -4585,13 +4599,23 @@ var LRUCache = class {
4585
4599
  * @returns The cached value if found, undefined otherwise.
4586
4600
  */
4587
4601
  get(key) {
4588
- const value = this.cache.get(key);
4589
- if (value === void 0) {
4602
+ const entry = this.cache.get(key);
4603
+ if (entry === void 0) {
4590
4604
  return void 0;
4591
4605
  }
4592
4606
  this.cache.delete(key);
4593
- this.cache.set(key, value);
4594
- return value;
4607
+ this.cache.set(key, entry);
4608
+ return entry.value;
4609
+ }
4610
+ /**
4611
+ * Checks whether a key exists and marks it as most recently used.
4612
+ */
4613
+ has(key) {
4614
+ if (!this.cache.has(key)) {
4615
+ return false;
4616
+ }
4617
+ this.get(key);
4618
+ return true;
4595
4619
  }
4596
4620
  /**
4597
4621
  * Stores a value in the cache.
@@ -4608,7 +4632,38 @@ var LRUCache = class {
4608
4632
  const first = this.cache.keys().next().value;
4609
4633
  this.cache.delete(first);
4610
4634
  }
4611
- this.cache.set(key, value);
4635
+ this.cache.set(key, { value });
4636
+ }
4637
+ /**
4638
+ * Removes an item from the cache.
4639
+ */
4640
+ delete(key) {
4641
+ return this.cache.delete(key);
4642
+ }
4643
+ /**
4644
+ * Iterates over cache entries from least to most recently used.
4645
+ */
4646
+ *entries() {
4647
+ for (const [key, entry] of this.cache) {
4648
+ yield [key, entry.value];
4649
+ }
4650
+ }
4651
+ /**
4652
+ * Iterates over cache keys from least to most recently used.
4653
+ */
4654
+ keys() {
4655
+ return this.cache.keys();
4656
+ }
4657
+ /**
4658
+ * Iterates over cache values from least to most recently used.
4659
+ */
4660
+ *values() {
4661
+ for (const entry of this.cache.values()) {
4662
+ yield entry.value;
4663
+ }
4664
+ }
4665
+ [Symbol.iterator]() {
4666
+ return this.entries();
4612
4667
  }
4613
4668
  /**
4614
4669
  * Removes all items from the cache.
@@ -5166,6 +5221,9 @@ function applyMaskingToField(maskingFunction, data, fieldName) {
5166
5221
  return `ERROR: Failed to mask field '${fieldName}' - ${errorType}`;
5167
5222
  }
5168
5223
  }
5224
+ var INITIAL_SPAN_WRITE_AS_MERGE = /* @__PURE__ */ Symbol(
5225
+ "braintrust.initial-span-write-as-merge"
5226
+ );
5169
5227
  var BRAINTRUST_CURRENT_SPAN_STORE = /* @__PURE__ */ Symbol.for(
5170
5228
  "braintrust.currentSpanStore"
5171
5229
  );
@@ -5735,25 +5793,46 @@ var HTTPConnection = class _HTTPConnection {
5735
5793
  })
5736
5794
  );
5737
5795
  }
5738
- async post(path2, params, config) {
5796
+ async post(path2, params, config, retries = 0) {
5739
5797
  const { headers, ...rest } = config || {};
5740
5798
  const this_fetch = this.fetch;
5741
5799
  const this_base_url = this.base_url;
5742
5800
  const this_headers = this.headers;
5743
- return await checkResponse(
5744
- await this_fetch(_urljoin(this_base_url, path2), {
5745
- method: "POST",
5746
- headers: {
5747
- Accept: "application/json",
5748
- "Content-Type": "application/json",
5749
- ...this_headers,
5750
- ...headers
5751
- },
5752
- body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
5753
- keepalive: true,
5754
- ...rest
5755
- })
5756
- );
5801
+ const tries = retries + 1;
5802
+ for (let i = 0; i < tries; i++) {
5803
+ try {
5804
+ return await checkResponse(
5805
+ await this_fetch(_urljoin(this_base_url, path2), {
5806
+ method: "POST",
5807
+ headers: {
5808
+ Accept: "application/json",
5809
+ "Content-Type": "application/json",
5810
+ ...this_headers,
5811
+ ...headers
5812
+ },
5813
+ body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
5814
+ keepalive: true,
5815
+ ...rest
5816
+ })
5817
+ );
5818
+ } catch (error) {
5819
+ if (config?.signal?.aborted) {
5820
+ throw getAbortReason(config.signal);
5821
+ }
5822
+ if (i === tries - 1 || !isRetryableHTTPError(error)) {
5823
+ throw error;
5824
+ }
5825
+ debugLogger.debug(
5826
+ `Retrying API request ${path2} after ${formatHTTPError(error)}`
5827
+ );
5828
+ const sleepTimeMs = HTTP_RETRY_BASE_SLEEP_TIME_S * 1e3 * 2 ** i + Math.random() * HTTP_RETRY_JITTER_MS;
5829
+ debugLogger.info(
5830
+ `Sleeping for ${sleepTimeMs}ms before retrying API request`
5831
+ );
5832
+ await waitForRetry(sleepTimeMs, config?.signal);
5833
+ }
5834
+ }
5835
+ throw new Error("Unexpected retry state");
5757
5836
  }
5758
5837
  async get_json(object_type, args = void 0, retries = 0) {
5759
5838
  const tries = retries + 1;
@@ -6653,6 +6732,45 @@ function now() {
6653
6732
  var DEFAULT_FLUSH_BACKPRESSURE_BYTES = 10 * 1024 * 1024;
6654
6733
  var BACKGROUND_LOGGER_BASE_SLEEP_TIME_S = 1;
6655
6734
  var HTTP_RETRY_BASE_SLEEP_TIME_S = 1;
6735
+ var HTTP_RETRY_JITTER_MS = 200;
6736
+ var BTQL_HTTP_RETRIES = 3;
6737
+ var RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
6738
+ function isRetryableHTTPError(error) {
6739
+ return !(error instanceof FailedHTTPResponse) || RETRYABLE_HTTP_STATUS_CODES.has(error.status);
6740
+ }
6741
+ function formatHTTPError(error) {
6742
+ if (error instanceof FailedHTTPResponse) {
6743
+ return `${error.status} ${error.text}`;
6744
+ }
6745
+ return error instanceof Error ? error.message : String(error);
6746
+ }
6747
+ function getAbortReason(signal) {
6748
+ return signal.reason ?? new Error("Request aborted");
6749
+ }
6750
+ async function waitForRetry(delayMs, signal) {
6751
+ if (!signal) {
6752
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
6753
+ return;
6754
+ }
6755
+ if (signal.aborted) {
6756
+ throw getAbortReason(signal);
6757
+ }
6758
+ await new Promise((resolve, reject2) => {
6759
+ const onAbort = () => {
6760
+ clearTimeout(timeout);
6761
+ signal.removeEventListener("abort", onAbort);
6762
+ reject2(getAbortReason(signal));
6763
+ };
6764
+ const timeout = setTimeout(() => {
6765
+ signal.removeEventListener("abort", onAbort);
6766
+ resolve();
6767
+ }, delayMs);
6768
+ signal.addEventListener("abort", onAbort, { once: true });
6769
+ if (signal.aborted) {
6770
+ onAbort();
6771
+ }
6772
+ });
6773
+ }
6656
6774
  var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
6657
6775
  apiConn;
6658
6776
  queue;
@@ -8080,15 +8198,15 @@ function parentSpanIdsUsable(spanId, rootSpanId) {
8080
8198
  return Boolean(spanId) && Boolean(rootSpanId);
8081
8199
  }
8082
8200
  function logError(span, error) {
8083
- let errorMessage = "<error>";
8201
+ let errorMessage2 = "<error>";
8084
8202
  let stackTrace = "";
8085
8203
  if (error instanceof Error) {
8086
- errorMessage = error.message;
8204
+ errorMessage2 = error.message;
8087
8205
  stackTrace = error.stack || "";
8088
8206
  } else {
8089
- errorMessage = String(error);
8207
+ errorMessage2 = String(error);
8090
8208
  }
8091
- span.log({ error: `${errorMessage}
8209
+ span.log({ error: `${errorMessage2}
8092
8210
 
8093
8211
  ${stackTrace}` });
8094
8212
  }
@@ -8467,7 +8585,8 @@ var ObjectFetcher = class {
8467
8585
  version: this.pinnedVersion
8468
8586
  } : {}
8469
8587
  },
8470
- { headers: { "Accept-Encoding": "gzip" } }
8588
+ { headers: { "Accept-Encoding": "gzip" } },
8589
+ BTQL_HTTP_RETRIES
8471
8590
  );
8472
8591
  const respJson = await resp.json();
8473
8592
  const mutate = this.mutateRecord;
@@ -8984,7 +9103,7 @@ var SpanImpl = class _SpanImpl {
8984
9103
  this._spanId = resolvedIds.spanId;
8985
9104
  this._rootSpanId = resolvedIds.rootSpanId;
8986
9105
  this._spanParents = resolvedIds.spanParents;
8987
- this.isMerge = false;
9106
+ this.isMerge = args[INITIAL_SPAN_WRITE_AS_MERGE] === true;
8988
9107
  this.logInternal({ event, internalData });
8989
9108
  this.isMerge = true;
8990
9109
  }
@@ -28555,6 +28674,446 @@ function isBraintrustHandler(handler) {
28555
28674
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
28556
28675
  }
28557
28676
 
28677
+ // src/instrumentation/plugins/langsmith-channels.ts
28678
+ var langSmithChannels = defineChannels("langsmith", {
28679
+ createRun: channel({
28680
+ channelName: "Client.createRun",
28681
+ kind: "async"
28682
+ }),
28683
+ updateRun: channel({
28684
+ channelName: "Client.updateRun",
28685
+ kind: "async"
28686
+ }),
28687
+ batchIngestRuns: channel({
28688
+ channelName: "Client.batchIngestRuns",
28689
+ kind: "async"
28690
+ })
28691
+ });
28692
+
28693
+ // src/instrumentation/plugins/langsmith-plugin.ts
28694
+ var MAX_COMPLETED_RUNS = 1e4;
28695
+ var BLOCKED_METADATA_KEYS = /* @__PURE__ */ new Set([
28696
+ "__proto__",
28697
+ "constructor",
28698
+ "prototype",
28699
+ "usage_metadata"
28700
+ ]);
28701
+ var BLOCKED_METADATA_PREFIXES = ["__pregel_", "langgraph_", "lc_"];
28702
+ var LLM_SETTING_KEYS = [
28703
+ "temperature",
28704
+ "top_p",
28705
+ "max_tokens",
28706
+ "frequency_penalty",
28707
+ "presence_penalty",
28708
+ "stop",
28709
+ "response_format"
28710
+ ];
28711
+ var LangSmithPlugin = class extends BasePlugin {
28712
+ activeRuns = /* @__PURE__ */ new Map();
28713
+ completedRuns = new LRUCache({
28714
+ max: MAX_COMPLETED_RUNS
28715
+ });
28716
+ skipLangChainRuns;
28717
+ constructor(options = {}) {
28718
+ super();
28719
+ this.skipLangChainRuns = options.skipLangChainRuns ?? true;
28720
+ }
28721
+ onEnable() {
28722
+ const createChannel = langSmithChannels.createRun.tracingChannel();
28723
+ const createHandlers = {
28724
+ start: (event) => {
28725
+ this.containLifecycleFailure("createRun", () => {
28726
+ this.processCreate(event.arguments[0]);
28727
+ });
28728
+ }
28729
+ };
28730
+ createChannel.subscribe(createHandlers);
28731
+ this.unsubscribers.push(() => createChannel.unsubscribe(createHandlers));
28732
+ const updateChannel = langSmithChannels.updateRun.tracingChannel();
28733
+ const updateHandlers = {
28734
+ start: (event) => {
28735
+ this.containLifecycleFailure("updateRun", () => {
28736
+ this.processUpdate(event.arguments[0], event.arguments[1]);
28737
+ });
28738
+ }
28739
+ };
28740
+ updateChannel.subscribe(updateHandlers);
28741
+ this.unsubscribers.push(() => updateChannel.unsubscribe(updateHandlers));
28742
+ const batchChannel = langSmithChannels.batchIngestRuns.tracingChannel();
28743
+ const batchHandlers = {
28744
+ start: (event) => {
28745
+ this.containLifecycleFailure("batchIngestRuns", () => {
28746
+ this.processBatch(event.arguments[0]);
28747
+ });
28748
+ }
28749
+ };
28750
+ batchChannel.subscribe(batchHandlers);
28751
+ this.unsubscribers.push(() => batchChannel.unsubscribe(batchHandlers));
28752
+ }
28753
+ onDisable() {
28754
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
28755
+ for (const { span } of this.activeRuns.values()) {
28756
+ span.end();
28757
+ }
28758
+ this.activeRuns.clear();
28759
+ this.completedRuns.clear();
28760
+ }
28761
+ processBatch(batch) {
28762
+ if (!isRecord2(batch)) {
28763
+ return;
28764
+ }
28765
+ const creates = ownValue(batch, "runCreates");
28766
+ if (Array.isArray(creates)) {
28767
+ const parentFirst = [...creates].sort((left, right) => {
28768
+ const leftId = stringValue2(ownValue(left, "id"));
28769
+ const rightId = stringValue2(ownValue(right, "id"));
28770
+ const leftParent = stringValue2(ownValue(left, "parent_run_id"));
28771
+ const rightParent = stringValue2(ownValue(right, "parent_run_id"));
28772
+ if (leftParent && leftParent === rightId) {
28773
+ return 1;
28774
+ }
28775
+ if (rightParent && rightParent === leftId) {
28776
+ return -1;
28777
+ }
28778
+ return dottedOrderDepth(left) - dottedOrderDepth(right);
28779
+ });
28780
+ for (const run of parentFirst) {
28781
+ this.containLifecycleFailure("batchIngestRuns create", () => {
28782
+ this.processCreate(run);
28783
+ });
28784
+ }
28785
+ }
28786
+ const updates = ownValue(batch, "runUpdates");
28787
+ if (Array.isArray(updates)) {
28788
+ for (const run of updates) {
28789
+ this.containLifecycleFailure("batchIngestRuns update", () => {
28790
+ this.processUpdate(stringValue2(ownValue(run, "id")) ?? "", run);
28791
+ });
28792
+ }
28793
+ }
28794
+ }
28795
+ processCreate(run) {
28796
+ const id = stringValue2(ownValue(run, "id"));
28797
+ if (!id || this.completedRuns.get(id)) {
28798
+ return;
28799
+ }
28800
+ if (this.shouldSkipLangChainRun(run)) {
28801
+ this.completedRuns.set(id, true);
28802
+ return;
28803
+ }
28804
+ const active = this.activeRuns.get(id);
28805
+ if (active) {
28806
+ const previous = active.run;
28807
+ active.run = mergeRuns(previous, run);
28808
+ this.logRun(
28809
+ active.span,
28810
+ active.run,
28811
+ previous,
28812
+ timestampSeconds(ownValue(active.run, "end_time")) !== void 0 || errorMessage(ownValue(active.run, "error")) !== void 0
28813
+ );
28814
+ this.endIfComplete(id, active, active.run);
28815
+ return;
28816
+ }
28817
+ const span = this.startRunSpan(id, run);
28818
+ const activeRun = { run, span };
28819
+ this.activeRuns.set(id, activeRun);
28820
+ this.logRun(
28821
+ span,
28822
+ run,
28823
+ void 0,
28824
+ timestampSeconds(ownValue(run, "end_time")) !== void 0 || errorMessage(ownValue(run, "error")) !== void 0
28825
+ );
28826
+ this.endIfComplete(id, activeRun, run);
28827
+ }
28828
+ processUpdate(explicitId, run) {
28829
+ const id = stringValue2(explicitId) ?? stringValue2(ownValue(run, "id"));
28830
+ if (!id || this.completedRuns.get(id)) {
28831
+ return;
28832
+ }
28833
+ if (this.shouldSkipLangChainRun(run)) {
28834
+ const active2 = this.activeRuns.get(id);
28835
+ active2?.span.end();
28836
+ this.activeRuns.delete(id);
28837
+ this.completedRuns.set(id, true);
28838
+ return;
28839
+ }
28840
+ let active = this.activeRuns.get(id);
28841
+ if (!active) {
28842
+ const span = this.startRunSpan(id, run);
28843
+ active = { run, span };
28844
+ this.activeRuns.set(id, active);
28845
+ }
28846
+ const previous = active.run;
28847
+ active.run = mergeRuns(previous, run);
28848
+ this.logRun(active.span, active.run, previous, true);
28849
+ this.endIfComplete(id, active, active.run);
28850
+ }
28851
+ startRunSpan(id, run) {
28852
+ const traceId = stringValue2(ownValue(run, "trace_id")) ?? id;
28853
+ const parentId = stringValue2(ownValue(run, "parent_run_id")) ?? stringValue2(ownValue(ownValue(run, "parent_run"), "id"));
28854
+ const startTime = timestampSeconds(ownValue(run, "start_time"));
28855
+ return startSpan({
28856
+ name: stringValue2(ownValue(run, "name")) ?? "LangSmith run",
28857
+ spanId: id,
28858
+ parentSpanIds: {
28859
+ parentSpanIds: parentId ? [parentId] : [],
28860
+ rootSpanId: traceId
28861
+ },
28862
+ spanAttributes: {
28863
+ type: mapRunType(ownValue(run, "run_type"))
28864
+ },
28865
+ ...startTime === void 0 ? {} : { startTime },
28866
+ event: { id }
28867
+ });
28868
+ }
28869
+ logRun(span, run, previous, includeOutput) {
28870
+ const inputs = preferOwnValue(run, previous, "inputs");
28871
+ const outputs = preferOwnValue(run, previous, "outputs");
28872
+ const error = errorMessage(preferOwnValue(run, previous, "error"));
28873
+ const metadata = extractMetadata(run, previous);
28874
+ const tags = extractTags(preferOwnValue(run, previous, "tags"));
28875
+ const metrics = extractMetrics(run, previous);
28876
+ span.log({
28877
+ ...inputs === void 0 ? {} : { input: sanitizeLoggedValue(inputs) },
28878
+ ...includeOutput && outputs !== void 0 ? { output: sanitizeLoggedValue(outputs) } : {},
28879
+ ...error === void 0 ? {} : { error },
28880
+ ...metadata === void 0 ? {} : { metadata },
28881
+ ...tags === void 0 ? {} : { tags },
28882
+ ...Object.keys(metrics).length === 0 ? {} : { metrics }
28883
+ });
28884
+ }
28885
+ endIfComplete(id, active, run) {
28886
+ const endTime = timestampSeconds(ownValue(run, "end_time"));
28887
+ if (endTime === void 0 && errorMessage(ownValue(run, "error")) === void 0) {
28888
+ return;
28889
+ }
28890
+ active.span.end(endTime === void 0 ? void 0 : { endTime });
28891
+ this.activeRuns.delete(id);
28892
+ this.completedRuns.set(id, true);
28893
+ }
28894
+ shouldSkipLangChainRun(run) {
28895
+ if (!this.skipLangChainRuns) {
28896
+ return false;
28897
+ }
28898
+ const serialized = ownValue(run, "serialized");
28899
+ return isRecord2(serialized) && ownValue(serialized, "lc") === 1;
28900
+ }
28901
+ containLifecycleFailure(operation, fn) {
28902
+ try {
28903
+ fn();
28904
+ } catch (error) {
28905
+ debugLogger.error(
28906
+ `Failed to process LangSmith ${operation} instrumentation:`,
28907
+ error
28908
+ );
28909
+ }
28910
+ }
28911
+ };
28912
+ function ownValue(value, key) {
28913
+ if (!isRecord2(value)) {
28914
+ return void 0;
28915
+ }
28916
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
28917
+ return descriptor && "value" in descriptor ? descriptor.value : void 0;
28918
+ }
28919
+ function preferOwnValue(current, previous, key) {
28920
+ const currentDescriptor = isRecord2(current) ? Object.getOwnPropertyDescriptor(current, key) : void 0;
28921
+ if (currentDescriptor && "value" in currentDescriptor) {
28922
+ return currentDescriptor.value;
28923
+ }
28924
+ return ownValue(previous, key);
28925
+ }
28926
+ function mergeRuns(previous, current) {
28927
+ const entries = /* @__PURE__ */ new Map();
28928
+ for (const value of [previous, current]) {
28929
+ if (!isRecord2(value)) {
28930
+ continue;
28931
+ }
28932
+ for (const [key, descriptor] of Object.entries(
28933
+ Object.getOwnPropertyDescriptors(value)
28934
+ )) {
28935
+ if (descriptor.enumerable && "value" in descriptor) {
28936
+ entries.set(key, descriptor.value);
28937
+ }
28938
+ }
28939
+ }
28940
+ return Object.fromEntries(entries);
28941
+ }
28942
+ function isRecord2(value) {
28943
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28944
+ }
28945
+ function stringValue2(value) {
28946
+ return typeof value === "string" && value.length > 0 ? value : void 0;
28947
+ }
28948
+ function timestampSeconds(value) {
28949
+ if (value instanceof Date) {
28950
+ const timestamp = value.getTime();
28951
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
28952
+ }
28953
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
28954
+ return value > 1e10 ? value / 1e3 : value;
28955
+ }
28956
+ if (typeof value === "string") {
28957
+ const timestamp = Date.parse(value);
28958
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
28959
+ }
28960
+ return void 0;
28961
+ }
28962
+ function dottedOrderDepth(run) {
28963
+ const dottedOrder = stringValue2(ownValue(run, "dotted_order"));
28964
+ return dottedOrder ? dottedOrder.split(".").length : Number.MAX_SAFE_INTEGER;
28965
+ }
28966
+ function mapRunType(runType) {
28967
+ switch (runType) {
28968
+ case "llm":
28969
+ case "embedding":
28970
+ return "llm" /* LLM */;
28971
+ case "tool":
28972
+ case "retriever":
28973
+ return "tool" /* TOOL */;
28974
+ default:
28975
+ return "task" /* TASK */;
28976
+ }
28977
+ }
28978
+ function extractMetadata(run, previous) {
28979
+ const extra = preferOwnValue(run, previous, "extra");
28980
+ const rawMetadata = ownValue(extra, "metadata");
28981
+ if (!isRecord2(rawMetadata)) {
28982
+ return void 0;
28983
+ }
28984
+ const metadata = {};
28985
+ for (const [key, descriptor] of Object.entries(
28986
+ Object.getOwnPropertyDescriptors(rawMetadata)
28987
+ )) {
28988
+ if (!("value" in descriptor) || !descriptor.enumerable || BLOCKED_METADATA_KEYS.has(key) || BLOCKED_METADATA_PREFIXES.some((prefix) => key.startsWith(prefix)) || key.startsWith("ls_") && key !== "ls_provider" && key !== "ls_model_name" && !LLM_SETTING_KEYS.some((setting) => key === `ls_${setting}`)) {
28989
+ continue;
28990
+ }
28991
+ const normalizedKey = key === "ls_provider" ? "provider" : key === "ls_model_name" ? "model" : key.startsWith("ls_") ? key.slice(3) : key;
28992
+ const sanitized = sanitizeLoggedValue(descriptor.value);
28993
+ if (sanitized !== void 0) {
28994
+ metadata[normalizedKey] = sanitized;
28995
+ }
28996
+ }
28997
+ return Object.keys(metadata).length === 0 ? void 0 : metadata;
28998
+ }
28999
+ function extractTags(value) {
29000
+ if (!Array.isArray(value)) {
29001
+ return void 0;
29002
+ }
29003
+ const tags = value.filter(
29004
+ (tag) => typeof tag === "string" && tag.length > 0
29005
+ );
29006
+ return tags.length > 0 ? tags : void 0;
29007
+ }
29008
+ function extractMetrics(run, previous) {
29009
+ const outputs = preferOwnValue(run, previous, "outputs");
29010
+ const extra = preferOwnValue(run, previous, "extra");
29011
+ const metadata = ownValue(extra, "metadata");
29012
+ const usage = ownValue(outputs, "usage_metadata") ?? ownValue(metadata, "usage_metadata");
29013
+ const metrics = {};
29014
+ assignFirstMetric(metrics, "prompt_tokens", usage, [
29015
+ "input_tokens",
29016
+ "prompt_tokens"
29017
+ ]);
29018
+ assignFirstMetric(metrics, "completion_tokens", usage, [
29019
+ "output_tokens",
29020
+ "completion_tokens"
29021
+ ]);
29022
+ assignFirstMetric(metrics, "tokens", usage, ["total_tokens", "tokens"]);
29023
+ const inputTokenDetails = ownValue(usage, "input_token_details");
29024
+ assignFirstMetric(metrics, "prompt_cached_tokens", inputTokenDetails, [
29025
+ "cache_read",
29026
+ "cached_tokens"
29027
+ ]);
29028
+ if (metrics.prompt_cached_tokens === void 0) {
29029
+ assignFirstMetric(metrics, "prompt_cached_tokens", usage, [
29030
+ "cache_read_input_tokens",
29031
+ "prompt_cached_tokens"
29032
+ ]);
29033
+ }
29034
+ assignFirstMetric(
29035
+ metrics,
29036
+ "prompt_cache_creation_tokens",
29037
+ inputTokenDetails,
29038
+ ["cache_creation"]
29039
+ );
29040
+ if (metrics.prompt_cache_creation_tokens === void 0) {
29041
+ assignFirstMetric(metrics, "prompt_cache_creation_tokens", usage, [
29042
+ "cache_creation_input_tokens",
29043
+ "prompt_cache_creation_tokens"
29044
+ ]);
29045
+ }
29046
+ if (metrics.tokens === void 0 && (metrics.prompt_tokens !== void 0 || metrics.completion_tokens !== void 0)) {
29047
+ metrics.tokens = (metrics.prompt_tokens ?? 0) + (metrics.completion_tokens ?? 0);
29048
+ }
29049
+ const startTime = timestampSeconds(
29050
+ preferOwnValue(run, previous, "start_time")
29051
+ );
29052
+ const events = preferOwnValue(run, previous, "events");
29053
+ if (startTime !== void 0 && Array.isArray(events)) {
29054
+ for (const event of events) {
29055
+ if (ownValue(event, "name") !== "new_token") {
29056
+ continue;
29057
+ }
29058
+ const eventTime2 = timestampSeconds(ownValue(event, "time"));
29059
+ if (eventTime2 !== void 0 && eventTime2 >= startTime) {
29060
+ metrics.time_to_first_token = eventTime2 - startTime;
29061
+ }
29062
+ break;
29063
+ }
29064
+ }
29065
+ return metrics;
29066
+ }
29067
+ function assignFirstMetric(metrics, target, source, keys) {
29068
+ for (const key of keys) {
29069
+ const value = ownValue(source, key);
29070
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
29071
+ metrics[target] = value;
29072
+ return;
29073
+ }
29074
+ }
29075
+ }
29076
+ function errorMessage(value) {
29077
+ if (typeof value === "string") {
29078
+ return value;
29079
+ }
29080
+ if (value instanceof Error) {
29081
+ return value.message;
29082
+ }
29083
+ return void 0;
29084
+ }
29085
+ function sanitizeLoggedValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
29086
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
29087
+ return value;
29088
+ }
29089
+ if (typeof value === "number") {
29090
+ return Number.isFinite(value) ? value : void 0;
29091
+ }
29092
+ if (value instanceof Date) {
29093
+ return Number.isFinite(value.getTime()) ? value.toISOString() : void 0;
29094
+ }
29095
+ if (typeof value !== "object" || depth >= 20) {
29096
+ return void 0;
29097
+ }
29098
+ if (seen.has(value)) {
29099
+ return "[Circular]";
29100
+ }
29101
+ seen.add(value);
29102
+ if (Array.isArray(value)) {
29103
+ return value.map((item) => sanitizeLoggedValue(item, seen, depth + 1));
29104
+ }
29105
+ const entries = [];
29106
+ for (const [key, descriptor] of Object.entries(
29107
+ Object.getOwnPropertyDescriptors(value)
29108
+ )) {
29109
+ if (!descriptor.enumerable || !("value" in descriptor) || BLOCKED_METADATA_KEYS.has(key)) {
29110
+ continue;
29111
+ }
29112
+ entries.push([key, sanitizeLoggedValue(descriptor.value, seen, depth + 1)]);
29113
+ }
29114
+ return Object.fromEntries(entries);
29115
+ }
29116
+
28558
29117
  // src/instrumentation/plugins/pi-coding-agent-channels.ts
28559
29118
  var piCodingAgentChannels = defineChannels(
28560
29119
  "@earendil-works/pi-coding-agent",
@@ -28934,11 +29493,11 @@ function patchAssistantMessageStream(stream, promptState, llmState) {
28934
29493
  function recordPiAssistantMessageEvent(promptState, llmState, event) {
28935
29494
  recordFirstTokenMetric(llmState, event);
28936
29495
  const message = "message" in event ? event.message : void 0;
28937
- const errorMessage = "error" in event ? event.error : void 0;
29496
+ const errorMessage2 = "error" in event ? event.error : void 0;
28938
29497
  if (event.type === "done" && isPiAssistantMessage(message)) {
28939
29498
  finishPiLlmSpan(promptState, llmState, message);
28940
- } else if (event.type === "error" && isPiAssistantMessage(errorMessage)) {
28941
- finishPiLlmSpan(promptState, llmState, errorMessage);
29499
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
29500
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
28942
29501
  }
28943
29502
  }
28944
29503
  function recordFirstTokenMetric(state, event) {
@@ -29456,6 +30015,7 @@ var strandsAgentSDKChannels = defineChannels("@strands-agents/sdk", {
29456
30015
  });
29457
30016
 
29458
30017
  // src/instrumentation/plugins/strands-agent-sdk-plugin.ts
30018
+ var MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES = 32;
29459
30019
  var StrandsAgentSDKPlugin = class extends BasePlugin {
29460
30020
  activeChildParents = /* @__PURE__ */ new WeakMap();
29461
30021
  onEnable() {
@@ -29600,11 +30160,16 @@ function startAgentStream(event, activeChildParents) {
29600
30160
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
29601
30161
  };
29602
30162
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
30163
+ const attachmentCache = createStrandsAttachmentCache();
30164
+ const input = processStrandsInputAttachments(
30165
+ event.arguments[0],
30166
+ attachmentCache
30167
+ );
29603
30168
  const span = parentSpan ? withCurrent(
29604
30169
  parentSpan,
29605
30170
  () => startSpan({
29606
30171
  event: {
29607
- input: event.arguments[0],
30172
+ input,
29608
30173
  metadata
29609
30174
  },
29610
30175
  name: formatAgentSpanName(agent),
@@ -29612,7 +30177,7 @@ function startAgentStream(event, activeChildParents) {
29612
30177
  })
29613
30178
  ) : startSpan({
29614
30179
  event: {
29615
- input: event.arguments[0],
30180
+ input,
29616
30181
  metadata
29617
30182
  },
29618
30183
  name: formatAgentSpanName(agent),
@@ -29620,6 +30185,7 @@ function startAgentStream(event, activeChildParents) {
29620
30185
  });
29621
30186
  return {
29622
30187
  activeTools: /* @__PURE__ */ new Map(),
30188
+ attachmentCache,
29623
30189
  finalized: false,
29624
30190
  metadata,
29625
30191
  span,
@@ -29635,11 +30201,12 @@ function startMultiAgentStream(event, operation, activeChildParents) {
29635
30201
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
29636
30202
  };
29637
30203
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
30204
+ const input = processStrandsInputAttachments(event.arguments[0]);
29638
30205
  const span = parentSpan ? withCurrent(
29639
30206
  parentSpan,
29640
30207
  () => startSpan({
29641
30208
  event: {
29642
- input: event.arguments[0],
30209
+ input,
29643
30210
  metadata
29644
30211
  },
29645
30212
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -29647,7 +30214,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
29647
30214
  })
29648
30215
  ) : startSpan({
29649
30216
  event: {
29650
- input: event.arguments[0],
30217
+ input,
29651
30218
  metadata
29652
30219
  },
29653
30220
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -29756,7 +30323,10 @@ function startModelSpan(state, event) {
29756
30323
  state.span,
29757
30324
  () => startSpan({
29758
30325
  event: {
29759
- input: Array.isArray(event.agent?.messages) ? event.agent.messages : void 0,
30326
+ input: Array.isArray(event.agent?.messages) ? processStrandsInputAttachments(
30327
+ event.agent.messages,
30328
+ state.attachmentCache
30329
+ ) : void 0,
29760
30330
  metadata
29761
30331
  },
29762
30332
  name: formatModelSpanName(model),
@@ -29970,6 +30540,7 @@ function finalizeAgentStream(state, error, output) {
29970
30540
  ...output !== void 0 ? { output } : {}
29971
30541
  });
29972
30542
  state.span.end();
30543
+ state.attachmentCache.strings.clear();
29973
30544
  }
29974
30545
  function finalizeMultiAgentStream(state, activeChildParents, error, output) {
29975
30546
  if (state.finalized) {
@@ -30116,6 +30687,166 @@ function extractNodeResultOutput(result) {
30116
30687
  }
30117
30688
  return result;
30118
30689
  }
30690
+ var STRANDS_MEDIA_TYPES = {
30691
+ png: "image/png",
30692
+ jpg: "image/jpeg",
30693
+ jpeg: "image/jpeg",
30694
+ gif: "image/gif",
30695
+ webp: "image/webp",
30696
+ mkv: "video/x-matroska",
30697
+ mov: "video/quicktime",
30698
+ mp4: "video/mp4",
30699
+ webm: "video/webm",
30700
+ flv: "video/x-flv",
30701
+ mpeg: "video/mpeg",
30702
+ mpg: "video/mpeg",
30703
+ wmv: "video/x-ms-wmv",
30704
+ "3gp": "video/3gpp",
30705
+ pdf: "application/pdf",
30706
+ csv: "text/csv",
30707
+ doc: "application/msword",
30708
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
30709
+ xls: "application/vnd.ms-excel",
30710
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
30711
+ html: "text/html",
30712
+ txt: "text/plain",
30713
+ md: "text/markdown",
30714
+ json: "application/json",
30715
+ xml: "application/xml"
30716
+ };
30717
+ function createStrandsAttachmentCache() {
30718
+ return {
30719
+ objects: /* @__PURE__ */ new WeakMap(),
30720
+ strings: new LRUCache({
30721
+ max: MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES
30722
+ })
30723
+ };
30724
+ }
30725
+ function processStrandsInputAttachments(input, cache = createStrandsAttachmentCache()) {
30726
+ try {
30727
+ return processStrandsInputNode(input, cache);
30728
+ } catch (error) {
30729
+ logInstrumentationError5("Strands Agent SDK input attachments", error);
30730
+ return input;
30731
+ }
30732
+ }
30733
+ function processStrandsInputNode(value, cache) {
30734
+ if (value instanceof BaseAttachment) {
30735
+ return value;
30736
+ }
30737
+ if (Array.isArray(value)) {
30738
+ return value.map((child) => processStrandsInputNode(child, cache));
30739
+ }
30740
+ if (!isObject(value)) {
30741
+ return value;
30742
+ }
30743
+ const directMedia = processDirectStrandsMediaBlock(value, cache);
30744
+ if (directMedia !== void 0) {
30745
+ return directMedia;
30746
+ }
30747
+ const wrappedMedia = processWrappedStrandsMediaBlock(value, cache);
30748
+ if (wrappedMedia !== void 0) {
30749
+ return wrappedMedia;
30750
+ }
30751
+ if (value.type === "message" && Array.isArray(value.content)) {
30752
+ return {
30753
+ role: value.role,
30754
+ content: value.content.map(
30755
+ (child) => processStrandsInputNode(child, cache)
30756
+ ),
30757
+ ...value.metadata !== void 0 ? { metadata: value.metadata } : {}
30758
+ };
30759
+ }
30760
+ if (typeof value.toJSON === "function") {
30761
+ return processStrandsInputNode(value.toJSON(), cache);
30762
+ }
30763
+ return Object.fromEntries(
30764
+ Object.entries(value).map(([key, child]) => [
30765
+ key,
30766
+ processStrandsInputNode(child, cache)
30767
+ ])
30768
+ );
30769
+ }
30770
+ function processDirectStrandsMediaBlock(block, cache) {
30771
+ if (!isStrandsMediaBlock(block)) {
30772
+ return void 0;
30773
+ }
30774
+ const mediaKey = block.type === "imageBlock" ? "image" : block.type === "videoBlock" ? "video" : "document";
30775
+ return createStrandsMediaAttachment(mediaKey, block, cache);
30776
+ }
30777
+ function isStrandsMediaBlock(block) {
30778
+ return (block.type === "imageBlock" || block.type === "videoBlock" || block.type === "documentBlock") && typeof block.format === "string" && isObject(block.source);
30779
+ }
30780
+ function processWrappedStrandsMediaBlock(block, cache) {
30781
+ for (const mediaKey of ["image", "video", "document"]) {
30782
+ const media = block[mediaKey];
30783
+ if (!Object.hasOwn(block, mediaKey) || !isObject(media)) {
30784
+ continue;
30785
+ }
30786
+ const processed = createStrandsMediaAttachment(mediaKey, media, cache);
30787
+ if (processed !== void 0) {
30788
+ return processed;
30789
+ }
30790
+ }
30791
+ return void 0;
30792
+ }
30793
+ function createStrandsMediaAttachment(mediaKey, media, cache) {
30794
+ const format = media.format;
30795
+ const source = media.source;
30796
+ if (typeof format !== "string" || !isObject(source) || !Object.hasOwn(source, "bytes")) {
30797
+ return void 0;
30798
+ }
30799
+ const contentType = STRANDS_MEDIA_TYPES[format.toLowerCase()];
30800
+ if (!contentType) {
30801
+ return void 0;
30802
+ }
30803
+ const filename = mediaKey === "document" && typeof media.name === "string" && media.name.length > 0 ? media.name : `${mediaKey}.${format.toLowerCase()}`;
30804
+ const attachment = getOrCreateStrandsAttachment(
30805
+ source.bytes,
30806
+ filename,
30807
+ contentType,
30808
+ cache
30809
+ );
30810
+ if (!attachment) {
30811
+ return void 0;
30812
+ }
30813
+ const { type: _type, ...serializedMedia } = media;
30814
+ const { type: _sourceType, ...serializedSource } = source;
30815
+ return {
30816
+ [mediaKey]: {
30817
+ ...serializedMedia,
30818
+ source: {
30819
+ ...serializedSource,
30820
+ bytes: attachment
30821
+ }
30822
+ }
30823
+ };
30824
+ }
30825
+ function getOrCreateStrandsAttachment(data, filename, contentType, cache) {
30826
+ const key = `${contentType}\0${filename}`;
30827
+ const attachments = typeof data === "string" ? cache.strings.get(data) : isObject(data) ? cache.objects.get(data) : void 0;
30828
+ const cached = attachments?.get(key);
30829
+ if (cached) {
30830
+ return cached;
30831
+ }
30832
+ const blob = convertDataToBlob(data, contentType);
30833
+ if (!blob) {
30834
+ return void 0;
30835
+ }
30836
+ const attachment = new Attachment({
30837
+ data: blob,
30838
+ filename,
30839
+ contentType
30840
+ });
30841
+ const updatedAttachments = attachments ?? /* @__PURE__ */ new Map();
30842
+ updatedAttachments.set(key, attachment);
30843
+ if (typeof data === "string") {
30844
+ cache.strings.set(data, updatedAttachments);
30845
+ } else if (isObject(data)) {
30846
+ cache.objects.set(data, updatedAttachments);
30847
+ }
30848
+ return attachment;
30849
+ }
30119
30850
  function normalizeContentBlocks(blocks) {
30120
30851
  const text = blocks.map((block) => typeof block.text === "string" ? block.text : void 0).filter((part) => Boolean(part)).join("");
30121
30852
  return text.length > 0 ? text : blocks;
@@ -30245,6 +30976,7 @@ var BraintrustPlugin = class extends BasePlugin {
30245
30976
  gitHubCopilotPlugin = null;
30246
30977
  fluePlugin = null;
30247
30978
  langChainPlugin = null;
30979
+ langSmithPlugin = null;
30248
30980
  piCodingAgentPlugin = null;
30249
30981
  strandsAgentSDKPlugin = null;
30250
30982
  constructor(config = {}) {
@@ -30341,6 +31073,12 @@ var BraintrustPlugin = class extends BasePlugin {
30341
31073
  this.langChainPlugin = new LangChainPlugin();
30342
31074
  this.langChainPlugin.enable();
30343
31075
  }
31076
+ if (integrations.langsmith !== false) {
31077
+ this.langSmithPlugin = new LangSmithPlugin({
31078
+ skipLangChainRuns: integrations.langchain !== false
31079
+ });
31080
+ this.langSmithPlugin.enable();
31081
+ }
30344
31082
  }
30345
31083
  onDisable() {
30346
31084
  if (this.openaiPlugin) {
@@ -30431,6 +31169,10 @@ var BraintrustPlugin = class extends BasePlugin {
30431
31169
  this.langChainPlugin.disable();
30432
31170
  this.langChainPlugin = null;
30433
31171
  }
31172
+ if (this.langSmithPlugin) {
31173
+ this.langSmithPlugin.disable();
31174
+ this.langSmithPlugin = null;
31175
+ }
30434
31176
  }
30435
31177
  };
30436
31178
 
@@ -30495,7 +31237,8 @@ var envIntegrationAliases = {
30495
31237
  langchain: "langchain",
30496
31238
  "langchain-js": "langchain",
30497
31239
  "@langchain": "langchain",
30498
- langgraph: "langgraph"
31240
+ langgraph: "langgraph",
31241
+ langsmith: "langsmith"
30499
31242
  };
30500
31243
  function getDefaultInstrumentationIntegrations() {
30501
31244
  return {
@@ -30526,6 +31269,7 @@ function getDefaultInstrumentationIntegrations() {
30526
31269
  gitHubCopilot: true,
30527
31270
  langchain: true,
30528
31271
  langgraph: true,
31272
+ langsmith: true,
30529
31273
  piCodingAgent: true,
30530
31274
  strandsAgentSDK: true
30531
31275
  };
@@ -30766,6 +31510,15 @@ function modelMetrics(attributes) {
30766
31510
  }
30767
31511
  return Object.keys(out).length > 0 ? out : void 0;
30768
31512
  }
31513
+ function timeToFirstTokenSeconds(attributes, spanStartSeconds) {
31514
+ if (!isObject(attributes)) return void 0;
31515
+ if (spanStartSeconds === void 0) return void 0;
31516
+ const raw = attributes.completionStartTime;
31517
+ const completionStart = raw instanceof Date || typeof raw === "string" || typeof raw === "number" ? epochSeconds(raw) : void 0;
31518
+ if (completionStart === void 0) return void 0;
31519
+ const ttft = completionStart - spanStartSeconds;
31520
+ return Number.isFinite(ttft) && ttft >= 0 ? ttft : void 0;
31521
+ }
30769
31522
  function buildMetadata(exported) {
30770
31523
  const out = {};
30771
31524
  if (exported.entityId !== void 0) out.entity_id = exported.entityId;
@@ -30835,7 +31588,13 @@ var BraintrustObservabilityExporter = class {
30835
31588
  const args = {
30836
31589
  name: exported.name,
30837
31590
  spanAttributes: { type: spanTypeFor(exported.type) },
30838
- startTime: epochSeconds(exported.startTime)
31591
+ startTime: epochSeconds(exported.startTime),
31592
+ // Use the Mastra span id as the Braintrust row id so that
31593
+ // `logFeedback({ id: <mastra span id> })` (and Mastra's score events)
31594
+ // attach to the right row. Without this, `SpanImpl` auto-generates a
31595
+ // row id (`this._id = eventId ?? idGenerator.getSpanId()`) that no
31596
+ // external caller could know.
31597
+ event: { id: exported.id }
30839
31598
  };
30840
31599
  const parentRecord = exported.parentSpanId ? this.spans.get(exported.parentSpanId) : void 0;
30841
31600
  if (!this.capturedParent) {
@@ -30884,8 +31643,15 @@ var BraintrustObservabilityExporter = class {
30884
31643
  event.metadata = metadata;
30885
31644
  }
30886
31645
  const metrics = modelMetrics(exported.attributes);
30887
- if (metrics) {
30888
- event.metrics = metrics;
31646
+ const ttft = timeToFirstTokenSeconds(
31647
+ exported.attributes,
31648
+ epochSeconds(exported.startTime)
31649
+ );
31650
+ if (metrics || ttft !== void 0) {
31651
+ event.metrics = {
31652
+ ...metrics ?? {},
31653
+ ...ttft !== void 0 ? { time_to_first_token: ttft } : {}
31654
+ };
30889
31655
  }
30890
31656
  if (Object.keys(event).length > 0) {
30891
31657
  record.span.log(event);
@@ -33607,8 +34373,8 @@ function makeCheckAuthorized(allowedOrgName) {
33607
34373
  return next(createError(400, "Missing x-bt-org-name header"));
33608
34374
  }
33609
34375
  if (allowedOrgName && allowedOrgName !== orgName) {
33610
- const errorMessage = `Org '${orgName}' is not allowed. Only org '${allowedOrgName}' is allowed.`;
33611
- return next(createError(403, errorMessage));
34376
+ const errorMessage2 = `Org '${orgName}' is not allowed. Only org '${allowedOrgName}' is allowed.`;
34377
+ return next(createError(403, errorMessage2));
33612
34378
  }
33613
34379
  const state = await cachedLogin({
33614
34380
  apiKey: req.ctx?.token,