braintrust 3.22.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 +43 -0
  2. package/dev/dist/index.d.mts +1 -1
  3. package/dev/dist/index.d.ts +1 -1
  4. package/dev/dist/index.js +1354 -652
  5. package/dev/dist/index.mjs +736 -34
  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 +149 -20
  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 +11 -13
  28. package/dist/browser.d.ts +11 -13
  29. package/dist/browser.js +1098 -203
  30. package/dist/browser.mjs +1098 -203
  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-MWVVR5LR.js → chunk-FZWPFCVE.js} +1506 -820
  34. package/dist/{chunk-ZG2O3XVF.mjs → chunk-IXL4PMY4.mjs} +719 -33
  35. package/dist/cli.js +737 -35
  36. package/dist/edge-light.d.mts +1 -1
  37. package/dist/edge-light.d.ts +1 -1
  38. package/dist/edge-light.js +1098 -203
  39. package/dist/edge-light.mjs +1098 -203
  40. package/dist/index.d.mts +11 -13
  41. package/dist/index.d.ts +11 -13
  42. package/dist/index.js +786 -593
  43. package/dist/index.mjs +346 -153
  44. package/dist/instrumentation/index.d.mts +3 -11
  45. package/dist/instrumentation/index.d.ts +3 -11
  46. package/dist/instrumentation/index.js +788 -181
  47. package/dist/instrumentation/index.mjs +788 -181
  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 +1098 -203
  53. package/dist/workerd.mjs +1098 -203
  54. package/package.json +1 -1
@@ -18,6 +18,7 @@ import {
18
18
  isInstrumentationIntegrationDisabled,
19
19
  isomorph_default,
20
20
  langChainChannels,
21
+ langSmithChannels,
21
22
  mistralChannels,
22
23
  openAIAgentsCoreChannels,
23
24
  openAIChannels,
@@ -30,7 +31,7 @@ import {
30
31
  smithyClientChannels,
31
32
  smithyCoreChannels,
32
33
  strandsAgentSDKChannels
33
- } from "./chunk-KMGUTPB7.mjs";
34
+ } from "./chunk-36IPYKMG.mjs";
34
35
 
35
36
  // src/id-gen.ts
36
37
  import { v4 as uuidv4 } from "uuid";
@@ -4194,7 +4195,7 @@ var DiskCache = class {
4194
4195
  }
4195
4196
  };
4196
4197
 
4197
- // src/prompt-cache/lru-cache.ts
4198
+ // src/lru-cache.ts
4198
4199
  var LRUCache = class {
4199
4200
  cache;
4200
4201
  maxSize;
@@ -5436,25 +5437,46 @@ var HTTPConnection = class _HTTPConnection {
5436
5437
  })
5437
5438
  );
5438
5439
  }
5439
- async post(path2, params, config) {
5440
+ async post(path2, params, config, retries = 0) {
5440
5441
  const { headers, ...rest } = config || {};
5441
5442
  const this_fetch = this.fetch;
5442
5443
  const this_base_url = this.base_url;
5443
5444
  const this_headers = this.headers;
5444
- return await checkResponse(
5445
- await this_fetch(_urljoin(this_base_url, path2), {
5446
- method: "POST",
5447
- headers: {
5448
- Accept: "application/json",
5449
- "Content-Type": "application/json",
5450
- ...this_headers,
5451
- ...headers
5452
- },
5453
- body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
5454
- keepalive: true,
5455
- ...rest
5456
- })
5457
- );
5445
+ const tries = retries + 1;
5446
+ for (let i = 0; i < tries; i++) {
5447
+ try {
5448
+ return await checkResponse(
5449
+ await this_fetch(_urljoin(this_base_url, path2), {
5450
+ method: "POST",
5451
+ headers: {
5452
+ Accept: "application/json",
5453
+ "Content-Type": "application/json",
5454
+ ...this_headers,
5455
+ ...headers
5456
+ },
5457
+ body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
5458
+ keepalive: true,
5459
+ ...rest
5460
+ })
5461
+ );
5462
+ } catch (error) {
5463
+ if (config?.signal?.aborted) {
5464
+ throw getAbortReason(config.signal);
5465
+ }
5466
+ if (i === tries - 1 || !isRetryableHTTPError(error)) {
5467
+ throw error;
5468
+ }
5469
+ debugLogger.debug(
5470
+ `Retrying API request ${path2} after ${formatHTTPError(error)}`
5471
+ );
5472
+ const sleepTimeMs = HTTP_RETRY_BASE_SLEEP_TIME_S * 1e3 * 2 ** i + Math.random() * HTTP_RETRY_JITTER_MS;
5473
+ debugLogger.info(
5474
+ `Sleeping for ${sleepTimeMs}ms before retrying API request`
5475
+ );
5476
+ await waitForRetry(sleepTimeMs, config?.signal);
5477
+ }
5478
+ }
5479
+ throw new Error("Unexpected retry state");
5458
5480
  }
5459
5481
  async get_json(object_type, args = void 0, retries = 0) {
5460
5482
  const tries = retries + 1;
@@ -6551,6 +6573,45 @@ var TestBackgroundLogger = class {
6551
6573
  };
6552
6574
  var BACKGROUND_LOGGER_BASE_SLEEP_TIME_S = 1;
6553
6575
  var HTTP_RETRY_BASE_SLEEP_TIME_S = 1;
6576
+ var HTTP_RETRY_JITTER_MS = 200;
6577
+ var BTQL_HTTP_RETRIES = 3;
6578
+ var RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
6579
+ function isRetryableHTTPError(error) {
6580
+ return !(error instanceof FailedHTTPResponse) || RETRYABLE_HTTP_STATUS_CODES.has(error.status);
6581
+ }
6582
+ function formatHTTPError(error) {
6583
+ if (error instanceof FailedHTTPResponse) {
6584
+ return `${error.status} ${error.text}`;
6585
+ }
6586
+ return error instanceof Error ? error.message : String(error);
6587
+ }
6588
+ function getAbortReason(signal) {
6589
+ return signal.reason ?? new Error("Request aborted");
6590
+ }
6591
+ async function waitForRetry(delayMs, signal) {
6592
+ if (!signal) {
6593
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
6594
+ return;
6595
+ }
6596
+ if (signal.aborted) {
6597
+ throw getAbortReason(signal);
6598
+ }
6599
+ await new Promise((resolve, reject) => {
6600
+ const onAbort = () => {
6601
+ clearTimeout(timeout);
6602
+ signal.removeEventListener("abort", onAbort);
6603
+ reject(getAbortReason(signal));
6604
+ };
6605
+ const timeout = setTimeout(() => {
6606
+ signal.removeEventListener("abort", onAbort);
6607
+ resolve();
6608
+ }, delayMs);
6609
+ signal.addEventListener("abort", onAbort, { once: true });
6610
+ if (signal.aborted) {
6611
+ onAbort();
6612
+ }
6613
+ });
6614
+ }
6554
6615
  var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
6555
6616
  apiConn;
6556
6617
  queue;
@@ -8289,15 +8350,15 @@ function parentSpanIdsUsable(spanId, rootSpanId) {
8289
8350
  return Boolean(spanId) && Boolean(rootSpanId);
8290
8351
  }
8291
8352
  function logError(span, error) {
8292
- let errorMessage = "<error>";
8353
+ let errorMessage2 = "<error>";
8293
8354
  let stackTrace = "";
8294
8355
  if (error instanceof Error) {
8295
- errorMessage = error.message;
8356
+ errorMessage2 = error.message;
8296
8357
  stackTrace = error.stack || "";
8297
8358
  } else {
8298
- errorMessage = String(error);
8359
+ errorMessage2 = String(error);
8299
8360
  }
8300
- span.log({ error: `${errorMessage}
8361
+ span.log({ error: `${errorMessage2}
8301
8362
 
8302
8363
  ${stackTrace}` });
8303
8364
  }
@@ -8884,7 +8945,8 @@ var ObjectFetcher = class {
8884
8945
  version: this.pinnedVersion
8885
8946
  } : {}
8886
8947
  },
8887
- { headers: { "Accept-Encoding": "gzip" } }
8948
+ { headers: { "Accept-Encoding": "gzip" } },
8949
+ BTQL_HTTP_RETRIES
8888
8950
  );
8889
8951
  const respJson = await resp.json();
8890
8952
  const mutate = this.mutateRecord;
@@ -10608,7 +10670,8 @@ async function getPromptVersions(projectId, promptId) {
10608
10670
  use_columnstore: false,
10609
10671
  brainstore_realtime: true
10610
10672
  },
10611
- { headers: { "Accept-Encoding": "gzip" } }
10673
+ { headers: { "Accept-Encoding": "gzip" } },
10674
+ BTQL_HTTP_RETRIES
10612
10675
  );
10613
10676
  if (!response.ok) {
10614
10677
  throw new Error(
@@ -28557,6 +28620,430 @@ function isBraintrustHandler(handler) {
28557
28620
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
28558
28621
  }
28559
28622
 
28623
+ // src/instrumentation/plugins/langsmith-plugin.ts
28624
+ var MAX_COMPLETED_RUNS = 1e4;
28625
+ var BLOCKED_METADATA_KEYS = /* @__PURE__ */ new Set([
28626
+ "__proto__",
28627
+ "constructor",
28628
+ "prototype",
28629
+ "usage_metadata"
28630
+ ]);
28631
+ var BLOCKED_METADATA_PREFIXES = ["__pregel_", "langgraph_", "lc_"];
28632
+ var LLM_SETTING_KEYS = [
28633
+ "temperature",
28634
+ "top_p",
28635
+ "max_tokens",
28636
+ "frequency_penalty",
28637
+ "presence_penalty",
28638
+ "stop",
28639
+ "response_format"
28640
+ ];
28641
+ var LangSmithPlugin = class extends BasePlugin {
28642
+ activeRuns = /* @__PURE__ */ new Map();
28643
+ completedRuns = new LRUCache({
28644
+ max: MAX_COMPLETED_RUNS
28645
+ });
28646
+ skipLangChainRuns;
28647
+ constructor(options = {}) {
28648
+ super();
28649
+ this.skipLangChainRuns = options.skipLangChainRuns ?? true;
28650
+ }
28651
+ onEnable() {
28652
+ const createChannel = langSmithChannels.createRun.tracingChannel();
28653
+ const createHandlers = {
28654
+ start: (event) => {
28655
+ this.containLifecycleFailure("createRun", () => {
28656
+ this.processCreate(event.arguments[0]);
28657
+ });
28658
+ }
28659
+ };
28660
+ createChannel.subscribe(createHandlers);
28661
+ this.unsubscribers.push(() => createChannel.unsubscribe(createHandlers));
28662
+ const updateChannel = langSmithChannels.updateRun.tracingChannel();
28663
+ const updateHandlers = {
28664
+ start: (event) => {
28665
+ this.containLifecycleFailure("updateRun", () => {
28666
+ this.processUpdate(event.arguments[0], event.arguments[1]);
28667
+ });
28668
+ }
28669
+ };
28670
+ updateChannel.subscribe(updateHandlers);
28671
+ this.unsubscribers.push(() => updateChannel.unsubscribe(updateHandlers));
28672
+ const batchChannel = langSmithChannels.batchIngestRuns.tracingChannel();
28673
+ const batchHandlers = {
28674
+ start: (event) => {
28675
+ this.containLifecycleFailure("batchIngestRuns", () => {
28676
+ this.processBatch(event.arguments[0]);
28677
+ });
28678
+ }
28679
+ };
28680
+ batchChannel.subscribe(batchHandlers);
28681
+ this.unsubscribers.push(() => batchChannel.unsubscribe(batchHandlers));
28682
+ }
28683
+ onDisable() {
28684
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
28685
+ for (const { span } of this.activeRuns.values()) {
28686
+ span.end();
28687
+ }
28688
+ this.activeRuns.clear();
28689
+ this.completedRuns.clear();
28690
+ }
28691
+ processBatch(batch) {
28692
+ if (!isRecord2(batch)) {
28693
+ return;
28694
+ }
28695
+ const creates = ownValue(batch, "runCreates");
28696
+ if (Array.isArray(creates)) {
28697
+ const parentFirst = [...creates].sort((left, right) => {
28698
+ const leftId = stringValue2(ownValue(left, "id"));
28699
+ const rightId = stringValue2(ownValue(right, "id"));
28700
+ const leftParent = stringValue2(ownValue(left, "parent_run_id"));
28701
+ const rightParent = stringValue2(ownValue(right, "parent_run_id"));
28702
+ if (leftParent && leftParent === rightId) {
28703
+ return 1;
28704
+ }
28705
+ if (rightParent && rightParent === leftId) {
28706
+ return -1;
28707
+ }
28708
+ return dottedOrderDepth(left) - dottedOrderDepth(right);
28709
+ });
28710
+ for (const run of parentFirst) {
28711
+ this.containLifecycleFailure("batchIngestRuns create", () => {
28712
+ this.processCreate(run);
28713
+ });
28714
+ }
28715
+ }
28716
+ const updates = ownValue(batch, "runUpdates");
28717
+ if (Array.isArray(updates)) {
28718
+ for (const run of updates) {
28719
+ this.containLifecycleFailure("batchIngestRuns update", () => {
28720
+ this.processUpdate(stringValue2(ownValue(run, "id")) ?? "", run);
28721
+ });
28722
+ }
28723
+ }
28724
+ }
28725
+ processCreate(run) {
28726
+ const id = stringValue2(ownValue(run, "id"));
28727
+ if (!id || this.completedRuns.get(id)) {
28728
+ return;
28729
+ }
28730
+ if (this.shouldSkipLangChainRun(run)) {
28731
+ this.completedRuns.set(id, true);
28732
+ return;
28733
+ }
28734
+ const active = this.activeRuns.get(id);
28735
+ if (active) {
28736
+ const previous = active.run;
28737
+ active.run = mergeRuns(previous, run);
28738
+ this.logRun(
28739
+ active.span,
28740
+ active.run,
28741
+ previous,
28742
+ timestampSeconds(ownValue(active.run, "end_time")) !== void 0 || errorMessage(ownValue(active.run, "error")) !== void 0
28743
+ );
28744
+ this.endIfComplete(id, active, active.run);
28745
+ return;
28746
+ }
28747
+ const span = this.startRunSpan(id, run);
28748
+ const activeRun = { run, span };
28749
+ this.activeRuns.set(id, activeRun);
28750
+ this.logRun(
28751
+ span,
28752
+ run,
28753
+ void 0,
28754
+ timestampSeconds(ownValue(run, "end_time")) !== void 0 || errorMessage(ownValue(run, "error")) !== void 0
28755
+ );
28756
+ this.endIfComplete(id, activeRun, run);
28757
+ }
28758
+ processUpdate(explicitId, run) {
28759
+ const id = stringValue2(explicitId) ?? stringValue2(ownValue(run, "id"));
28760
+ if (!id || this.completedRuns.get(id)) {
28761
+ return;
28762
+ }
28763
+ if (this.shouldSkipLangChainRun(run)) {
28764
+ const active2 = this.activeRuns.get(id);
28765
+ active2?.span.end();
28766
+ this.activeRuns.delete(id);
28767
+ this.completedRuns.set(id, true);
28768
+ return;
28769
+ }
28770
+ let active = this.activeRuns.get(id);
28771
+ if (!active) {
28772
+ const span = this.startRunSpan(id, run);
28773
+ active = { run, span };
28774
+ this.activeRuns.set(id, active);
28775
+ }
28776
+ const previous = active.run;
28777
+ active.run = mergeRuns(previous, run);
28778
+ this.logRun(active.span, active.run, previous, true);
28779
+ this.endIfComplete(id, active, active.run);
28780
+ }
28781
+ startRunSpan(id, run) {
28782
+ const traceId = stringValue2(ownValue(run, "trace_id")) ?? id;
28783
+ const parentId = stringValue2(ownValue(run, "parent_run_id")) ?? stringValue2(ownValue(ownValue(run, "parent_run"), "id"));
28784
+ const startTime = timestampSeconds(ownValue(run, "start_time"));
28785
+ return startSpan({
28786
+ name: stringValue2(ownValue(run, "name")) ?? "LangSmith run",
28787
+ spanId: id,
28788
+ parentSpanIds: {
28789
+ parentSpanIds: parentId ? [parentId] : [],
28790
+ rootSpanId: traceId
28791
+ },
28792
+ spanAttributes: {
28793
+ type: mapRunType(ownValue(run, "run_type"))
28794
+ },
28795
+ ...startTime === void 0 ? {} : { startTime },
28796
+ event: { id }
28797
+ });
28798
+ }
28799
+ logRun(span, run, previous, includeOutput) {
28800
+ const inputs = preferOwnValue(run, previous, "inputs");
28801
+ const outputs = preferOwnValue(run, previous, "outputs");
28802
+ const error = errorMessage(preferOwnValue(run, previous, "error"));
28803
+ const metadata = extractMetadata(run, previous);
28804
+ const tags = extractTags(preferOwnValue(run, previous, "tags"));
28805
+ const metrics = extractMetrics(run, previous);
28806
+ span.log({
28807
+ ...inputs === void 0 ? {} : { input: sanitizeLoggedValue(inputs) },
28808
+ ...includeOutput && outputs !== void 0 ? { output: sanitizeLoggedValue(outputs) } : {},
28809
+ ...error === void 0 ? {} : { error },
28810
+ ...metadata === void 0 ? {} : { metadata },
28811
+ ...tags === void 0 ? {} : { tags },
28812
+ ...Object.keys(metrics).length === 0 ? {} : { metrics }
28813
+ });
28814
+ }
28815
+ endIfComplete(id, active, run) {
28816
+ const endTime = timestampSeconds(ownValue(run, "end_time"));
28817
+ if (endTime === void 0 && errorMessage(ownValue(run, "error")) === void 0) {
28818
+ return;
28819
+ }
28820
+ active.span.end(endTime === void 0 ? void 0 : { endTime });
28821
+ this.activeRuns.delete(id);
28822
+ this.completedRuns.set(id, true);
28823
+ }
28824
+ shouldSkipLangChainRun(run) {
28825
+ if (!this.skipLangChainRuns) {
28826
+ return false;
28827
+ }
28828
+ const serialized = ownValue(run, "serialized");
28829
+ return isRecord2(serialized) && ownValue(serialized, "lc") === 1;
28830
+ }
28831
+ containLifecycleFailure(operation, fn) {
28832
+ try {
28833
+ fn();
28834
+ } catch (error) {
28835
+ debugLogger.error(
28836
+ `Failed to process LangSmith ${operation} instrumentation:`,
28837
+ error
28838
+ );
28839
+ }
28840
+ }
28841
+ };
28842
+ function ownValue(value, key) {
28843
+ if (!isRecord2(value)) {
28844
+ return void 0;
28845
+ }
28846
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
28847
+ return descriptor && "value" in descriptor ? descriptor.value : void 0;
28848
+ }
28849
+ function preferOwnValue(current, previous, key) {
28850
+ const currentDescriptor = isRecord2(current) ? Object.getOwnPropertyDescriptor(current, key) : void 0;
28851
+ if (currentDescriptor && "value" in currentDescriptor) {
28852
+ return currentDescriptor.value;
28853
+ }
28854
+ return ownValue(previous, key);
28855
+ }
28856
+ function mergeRuns(previous, current) {
28857
+ const entries = /* @__PURE__ */ new Map();
28858
+ for (const value of [previous, current]) {
28859
+ if (!isRecord2(value)) {
28860
+ continue;
28861
+ }
28862
+ for (const [key, descriptor] of Object.entries(
28863
+ Object.getOwnPropertyDescriptors(value)
28864
+ )) {
28865
+ if (descriptor.enumerable && "value" in descriptor) {
28866
+ entries.set(key, descriptor.value);
28867
+ }
28868
+ }
28869
+ }
28870
+ return Object.fromEntries(entries);
28871
+ }
28872
+ function isRecord2(value) {
28873
+ return typeof value === "object" && value !== null && !Array.isArray(value);
28874
+ }
28875
+ function stringValue2(value) {
28876
+ return typeof value === "string" && value.length > 0 ? value : void 0;
28877
+ }
28878
+ function timestampSeconds(value) {
28879
+ if (value instanceof Date) {
28880
+ const timestamp = value.getTime();
28881
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
28882
+ }
28883
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
28884
+ return value > 1e10 ? value / 1e3 : value;
28885
+ }
28886
+ if (typeof value === "string") {
28887
+ const timestamp = Date.parse(value);
28888
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
28889
+ }
28890
+ return void 0;
28891
+ }
28892
+ function dottedOrderDepth(run) {
28893
+ const dottedOrder = stringValue2(ownValue(run, "dotted_order"));
28894
+ return dottedOrder ? dottedOrder.split(".").length : Number.MAX_SAFE_INTEGER;
28895
+ }
28896
+ function mapRunType(runType) {
28897
+ switch (runType) {
28898
+ case "llm":
28899
+ case "embedding":
28900
+ return "llm" /* LLM */;
28901
+ case "tool":
28902
+ case "retriever":
28903
+ return "tool" /* TOOL */;
28904
+ default:
28905
+ return "task" /* TASK */;
28906
+ }
28907
+ }
28908
+ function extractMetadata(run, previous) {
28909
+ const extra = preferOwnValue(run, previous, "extra");
28910
+ const rawMetadata = ownValue(extra, "metadata");
28911
+ if (!isRecord2(rawMetadata)) {
28912
+ return void 0;
28913
+ }
28914
+ const metadata = {};
28915
+ for (const [key, descriptor] of Object.entries(
28916
+ Object.getOwnPropertyDescriptors(rawMetadata)
28917
+ )) {
28918
+ 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}`)) {
28919
+ continue;
28920
+ }
28921
+ const normalizedKey = key === "ls_provider" ? "provider" : key === "ls_model_name" ? "model" : key.startsWith("ls_") ? key.slice(3) : key;
28922
+ const sanitized = sanitizeLoggedValue(descriptor.value);
28923
+ if (sanitized !== void 0) {
28924
+ metadata[normalizedKey] = sanitized;
28925
+ }
28926
+ }
28927
+ return Object.keys(metadata).length === 0 ? void 0 : metadata;
28928
+ }
28929
+ function extractTags(value) {
28930
+ if (!Array.isArray(value)) {
28931
+ return void 0;
28932
+ }
28933
+ const tags = value.filter(
28934
+ (tag) => typeof tag === "string" && tag.length > 0
28935
+ );
28936
+ return tags.length > 0 ? tags : void 0;
28937
+ }
28938
+ function extractMetrics(run, previous) {
28939
+ const outputs = preferOwnValue(run, previous, "outputs");
28940
+ const extra = preferOwnValue(run, previous, "extra");
28941
+ const metadata = ownValue(extra, "metadata");
28942
+ const usage = ownValue(outputs, "usage_metadata") ?? ownValue(metadata, "usage_metadata");
28943
+ const metrics = {};
28944
+ assignFirstMetric(metrics, "prompt_tokens", usage, [
28945
+ "input_tokens",
28946
+ "prompt_tokens"
28947
+ ]);
28948
+ assignFirstMetric(metrics, "completion_tokens", usage, [
28949
+ "output_tokens",
28950
+ "completion_tokens"
28951
+ ]);
28952
+ assignFirstMetric(metrics, "tokens", usage, ["total_tokens", "tokens"]);
28953
+ const inputTokenDetails = ownValue(usage, "input_token_details");
28954
+ assignFirstMetric(metrics, "prompt_cached_tokens", inputTokenDetails, [
28955
+ "cache_read",
28956
+ "cached_tokens"
28957
+ ]);
28958
+ if (metrics.prompt_cached_tokens === void 0) {
28959
+ assignFirstMetric(metrics, "prompt_cached_tokens", usage, [
28960
+ "cache_read_input_tokens",
28961
+ "prompt_cached_tokens"
28962
+ ]);
28963
+ }
28964
+ assignFirstMetric(
28965
+ metrics,
28966
+ "prompt_cache_creation_tokens",
28967
+ inputTokenDetails,
28968
+ ["cache_creation"]
28969
+ );
28970
+ if (metrics.prompt_cache_creation_tokens === void 0) {
28971
+ assignFirstMetric(metrics, "prompt_cache_creation_tokens", usage, [
28972
+ "cache_creation_input_tokens",
28973
+ "prompt_cache_creation_tokens"
28974
+ ]);
28975
+ }
28976
+ if (metrics.tokens === void 0 && (metrics.prompt_tokens !== void 0 || metrics.completion_tokens !== void 0)) {
28977
+ metrics.tokens = (metrics.prompt_tokens ?? 0) + (metrics.completion_tokens ?? 0);
28978
+ }
28979
+ const startTime = timestampSeconds(
28980
+ preferOwnValue(run, previous, "start_time")
28981
+ );
28982
+ const events = preferOwnValue(run, previous, "events");
28983
+ if (startTime !== void 0 && Array.isArray(events)) {
28984
+ for (const event of events) {
28985
+ if (ownValue(event, "name") !== "new_token") {
28986
+ continue;
28987
+ }
28988
+ const eventTime2 = timestampSeconds(ownValue(event, "time"));
28989
+ if (eventTime2 !== void 0 && eventTime2 >= startTime) {
28990
+ metrics.time_to_first_token = eventTime2 - startTime;
28991
+ }
28992
+ break;
28993
+ }
28994
+ }
28995
+ return metrics;
28996
+ }
28997
+ function assignFirstMetric(metrics, target, source, keys) {
28998
+ for (const key of keys) {
28999
+ const value = ownValue(source, key);
29000
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
29001
+ metrics[target] = value;
29002
+ return;
29003
+ }
29004
+ }
29005
+ }
29006
+ function errorMessage(value) {
29007
+ if (typeof value === "string") {
29008
+ return value;
29009
+ }
29010
+ if (value instanceof Error) {
29011
+ return value.message;
29012
+ }
29013
+ return void 0;
29014
+ }
29015
+ function sanitizeLoggedValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
29016
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
29017
+ return value;
29018
+ }
29019
+ if (typeof value === "number") {
29020
+ return Number.isFinite(value) ? value : void 0;
29021
+ }
29022
+ if (value instanceof Date) {
29023
+ return Number.isFinite(value.getTime()) ? value.toISOString() : void 0;
29024
+ }
29025
+ if (typeof value !== "object" || depth >= 20) {
29026
+ return void 0;
29027
+ }
29028
+ if (seen.has(value)) {
29029
+ return "[Circular]";
29030
+ }
29031
+ seen.add(value);
29032
+ if (Array.isArray(value)) {
29033
+ return value.map((item) => sanitizeLoggedValue(item, seen, depth + 1));
29034
+ }
29035
+ const entries = [];
29036
+ for (const [key, descriptor] of Object.entries(
29037
+ Object.getOwnPropertyDescriptors(value)
29038
+ )) {
29039
+ if (!descriptor.enumerable || !("value" in descriptor) || BLOCKED_METADATA_KEYS.has(key)) {
29040
+ continue;
29041
+ }
29042
+ entries.push([key, sanitizeLoggedValue(descriptor.value, seen, depth + 1)]);
29043
+ }
29044
+ return Object.fromEntries(entries);
29045
+ }
29046
+
28560
29047
  // src/instrumentation/plugins/pi-coding-agent-plugin.ts
28561
29048
  var piStreamPatchStates = /* @__PURE__ */ new WeakMap();
28562
29049
  var piPromptContextStore;
@@ -28925,11 +29412,11 @@ function patchAssistantMessageStream(stream, promptState, llmState) {
28925
29412
  function recordPiAssistantMessageEvent(promptState, llmState, event) {
28926
29413
  recordFirstTokenMetric(llmState, event);
28927
29414
  const message = "message" in event ? event.message : void 0;
28928
- const errorMessage = "error" in event ? event.error : void 0;
29415
+ const errorMessage2 = "error" in event ? event.error : void 0;
28929
29416
  if (event.type === "done" && isPiAssistantMessage(message)) {
28930
29417
  finishPiLlmSpan(promptState, llmState, message);
28931
- } else if (event.type === "error" && isPiAssistantMessage(errorMessage)) {
28932
- finishPiLlmSpan(promptState, llmState, errorMessage);
29418
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
29419
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
28933
29420
  }
28934
29421
  }
28935
29422
  function recordFirstTokenMetric(state, event) {
@@ -29431,6 +29918,7 @@ function logInstrumentationError4(context, error) {
29431
29918
  }
29432
29919
 
29433
29920
  // src/instrumentation/plugins/strands-agent-sdk-plugin.ts
29921
+ var MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES = 32;
29434
29922
  var StrandsAgentSDKPlugin = class extends BasePlugin {
29435
29923
  activeChildParents = /* @__PURE__ */ new WeakMap();
29436
29924
  onEnable() {
@@ -29575,11 +30063,16 @@ function startAgentStream(event, activeChildParents) {
29575
30063
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
29576
30064
  };
29577
30065
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
30066
+ const attachmentCache = createStrandsAttachmentCache();
30067
+ const input = processStrandsInputAttachments(
30068
+ event.arguments[0],
30069
+ attachmentCache
30070
+ );
29578
30071
  const span = parentSpan ? withCurrent(
29579
30072
  parentSpan,
29580
30073
  () => startSpan({
29581
30074
  event: {
29582
- input: event.arguments[0],
30075
+ input,
29583
30076
  metadata
29584
30077
  },
29585
30078
  name: formatAgentSpanName(agent),
@@ -29587,7 +30080,7 @@ function startAgentStream(event, activeChildParents) {
29587
30080
  })
29588
30081
  ) : startSpan({
29589
30082
  event: {
29590
- input: event.arguments[0],
30083
+ input,
29591
30084
  metadata
29592
30085
  },
29593
30086
  name: formatAgentSpanName(agent),
@@ -29595,6 +30088,7 @@ function startAgentStream(event, activeChildParents) {
29595
30088
  });
29596
30089
  return {
29597
30090
  activeTools: /* @__PURE__ */ new Map(),
30091
+ attachmentCache,
29598
30092
  finalized: false,
29599
30093
  metadata,
29600
30094
  span,
@@ -29610,11 +30104,12 @@ function startMultiAgentStream(event, operation, activeChildParents) {
29610
30104
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
29611
30105
  };
29612
30106
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
30107
+ const input = processStrandsInputAttachments(event.arguments[0]);
29613
30108
  const span = parentSpan ? withCurrent(
29614
30109
  parentSpan,
29615
30110
  () => startSpan({
29616
30111
  event: {
29617
- input: event.arguments[0],
30112
+ input,
29618
30113
  metadata
29619
30114
  },
29620
30115
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -29622,7 +30117,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
29622
30117
  })
29623
30118
  ) : startSpan({
29624
30119
  event: {
29625
- input: event.arguments[0],
30120
+ input,
29626
30121
  metadata
29627
30122
  },
29628
30123
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -29731,7 +30226,10 @@ function startModelSpan(state, event) {
29731
30226
  state.span,
29732
30227
  () => startSpan({
29733
30228
  event: {
29734
- input: Array.isArray(event.agent?.messages) ? event.agent.messages : void 0,
30229
+ input: Array.isArray(event.agent?.messages) ? processStrandsInputAttachments(
30230
+ event.agent.messages,
30231
+ state.attachmentCache
30232
+ ) : void 0,
29735
30233
  metadata
29736
30234
  },
29737
30235
  name: formatModelSpanName(model),
@@ -29945,6 +30443,7 @@ function finalizeAgentStream(state, error, output) {
29945
30443
  ...output !== void 0 ? { output } : {}
29946
30444
  });
29947
30445
  state.span.end();
30446
+ state.attachmentCache.strings.clear();
29948
30447
  }
29949
30448
  function finalizeMultiAgentStream(state, activeChildParents, error, output) {
29950
30449
  if (state.finalized) {
@@ -30091,6 +30590,166 @@ function extractNodeResultOutput(result) {
30091
30590
  }
30092
30591
  return result;
30093
30592
  }
30593
+ var STRANDS_MEDIA_TYPES = {
30594
+ png: "image/png",
30595
+ jpg: "image/jpeg",
30596
+ jpeg: "image/jpeg",
30597
+ gif: "image/gif",
30598
+ webp: "image/webp",
30599
+ mkv: "video/x-matroska",
30600
+ mov: "video/quicktime",
30601
+ mp4: "video/mp4",
30602
+ webm: "video/webm",
30603
+ flv: "video/x-flv",
30604
+ mpeg: "video/mpeg",
30605
+ mpg: "video/mpeg",
30606
+ wmv: "video/x-ms-wmv",
30607
+ "3gp": "video/3gpp",
30608
+ pdf: "application/pdf",
30609
+ csv: "text/csv",
30610
+ doc: "application/msword",
30611
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
30612
+ xls: "application/vnd.ms-excel",
30613
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
30614
+ html: "text/html",
30615
+ txt: "text/plain",
30616
+ md: "text/markdown",
30617
+ json: "application/json",
30618
+ xml: "application/xml"
30619
+ };
30620
+ function createStrandsAttachmentCache() {
30621
+ return {
30622
+ objects: /* @__PURE__ */ new WeakMap(),
30623
+ strings: new LRUCache({
30624
+ max: MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES
30625
+ })
30626
+ };
30627
+ }
30628
+ function processStrandsInputAttachments(input, cache = createStrandsAttachmentCache()) {
30629
+ try {
30630
+ return processStrandsInputNode(input, cache);
30631
+ } catch (error) {
30632
+ logInstrumentationError5("Strands Agent SDK input attachments", error);
30633
+ return input;
30634
+ }
30635
+ }
30636
+ function processStrandsInputNode(value, cache) {
30637
+ if (value instanceof BaseAttachment) {
30638
+ return value;
30639
+ }
30640
+ if (Array.isArray(value)) {
30641
+ return value.map((child) => processStrandsInputNode(child, cache));
30642
+ }
30643
+ if (!isObject(value)) {
30644
+ return value;
30645
+ }
30646
+ const directMedia = processDirectStrandsMediaBlock(value, cache);
30647
+ if (directMedia !== void 0) {
30648
+ return directMedia;
30649
+ }
30650
+ const wrappedMedia = processWrappedStrandsMediaBlock(value, cache);
30651
+ if (wrappedMedia !== void 0) {
30652
+ return wrappedMedia;
30653
+ }
30654
+ if (value.type === "message" && Array.isArray(value.content)) {
30655
+ return {
30656
+ role: value.role,
30657
+ content: value.content.map(
30658
+ (child) => processStrandsInputNode(child, cache)
30659
+ ),
30660
+ ...value.metadata !== void 0 ? { metadata: value.metadata } : {}
30661
+ };
30662
+ }
30663
+ if (typeof value.toJSON === "function") {
30664
+ return processStrandsInputNode(value.toJSON(), cache);
30665
+ }
30666
+ return Object.fromEntries(
30667
+ Object.entries(value).map(([key, child]) => [
30668
+ key,
30669
+ processStrandsInputNode(child, cache)
30670
+ ])
30671
+ );
30672
+ }
30673
+ function processDirectStrandsMediaBlock(block, cache) {
30674
+ if (!isStrandsMediaBlock(block)) {
30675
+ return void 0;
30676
+ }
30677
+ const mediaKey = block.type === "imageBlock" ? "image" : block.type === "videoBlock" ? "video" : "document";
30678
+ return createStrandsMediaAttachment(mediaKey, block, cache);
30679
+ }
30680
+ function isStrandsMediaBlock(block) {
30681
+ return (block.type === "imageBlock" || block.type === "videoBlock" || block.type === "documentBlock") && typeof block.format === "string" && isObject(block.source);
30682
+ }
30683
+ function processWrappedStrandsMediaBlock(block, cache) {
30684
+ for (const mediaKey of ["image", "video", "document"]) {
30685
+ const media = block[mediaKey];
30686
+ if (!Object.hasOwn(block, mediaKey) || !isObject(media)) {
30687
+ continue;
30688
+ }
30689
+ const processed = createStrandsMediaAttachment(mediaKey, media, cache);
30690
+ if (processed !== void 0) {
30691
+ return processed;
30692
+ }
30693
+ }
30694
+ return void 0;
30695
+ }
30696
+ function createStrandsMediaAttachment(mediaKey, media, cache) {
30697
+ const format = media.format;
30698
+ const source = media.source;
30699
+ if (typeof format !== "string" || !isObject(source) || !Object.hasOwn(source, "bytes")) {
30700
+ return void 0;
30701
+ }
30702
+ const contentType = STRANDS_MEDIA_TYPES[format.toLowerCase()];
30703
+ if (!contentType) {
30704
+ return void 0;
30705
+ }
30706
+ const filename = mediaKey === "document" && typeof media.name === "string" && media.name.length > 0 ? media.name : `${mediaKey}.${format.toLowerCase()}`;
30707
+ const attachment = getOrCreateStrandsAttachment(
30708
+ source.bytes,
30709
+ filename,
30710
+ contentType,
30711
+ cache
30712
+ );
30713
+ if (!attachment) {
30714
+ return void 0;
30715
+ }
30716
+ const { type: _type, ...serializedMedia } = media;
30717
+ const { type: _sourceType, ...serializedSource } = source;
30718
+ return {
30719
+ [mediaKey]: {
30720
+ ...serializedMedia,
30721
+ source: {
30722
+ ...serializedSource,
30723
+ bytes: attachment
30724
+ }
30725
+ }
30726
+ };
30727
+ }
30728
+ function getOrCreateStrandsAttachment(data, filename, contentType, cache) {
30729
+ const key = `${contentType}\0${filename}`;
30730
+ const attachments = typeof data === "string" ? cache.strings.get(data) : isObject(data) ? cache.objects.get(data) : void 0;
30731
+ const cached = attachments?.get(key);
30732
+ if (cached) {
30733
+ return cached;
30734
+ }
30735
+ const blob = convertDataToBlob(data, contentType);
30736
+ if (!blob) {
30737
+ return void 0;
30738
+ }
30739
+ const attachment = new Attachment({
30740
+ data: blob,
30741
+ filename,
30742
+ contentType
30743
+ });
30744
+ const updatedAttachments = attachments ?? /* @__PURE__ */ new Map();
30745
+ updatedAttachments.set(key, attachment);
30746
+ if (typeof data === "string") {
30747
+ cache.strings.set(data, updatedAttachments);
30748
+ } else if (isObject(data)) {
30749
+ cache.objects.set(data, updatedAttachments);
30750
+ }
30751
+ return attachment;
30752
+ }
30094
30753
  function normalizeContentBlocks(blocks) {
30095
30754
  const text = blocks.map((block) => typeof block.text === "string" ? block.text : void 0).filter((part) => Boolean(part)).join("");
30096
30755
  return text.length > 0 ? text : blocks;
@@ -30220,6 +30879,7 @@ var BraintrustPlugin = class extends BasePlugin {
30220
30879
  gitHubCopilotPlugin = null;
30221
30880
  fluePlugin = null;
30222
30881
  langChainPlugin = null;
30882
+ langSmithPlugin = null;
30223
30883
  piCodingAgentPlugin = null;
30224
30884
  strandsAgentSDKPlugin = null;
30225
30885
  constructor(config = {}) {
@@ -30316,6 +30976,12 @@ var BraintrustPlugin = class extends BasePlugin {
30316
30976
  this.langChainPlugin = new LangChainPlugin();
30317
30977
  this.langChainPlugin.enable();
30318
30978
  }
30979
+ if (integrations.langsmith !== false) {
30980
+ this.langSmithPlugin = new LangSmithPlugin({
30981
+ skipLangChainRuns: integrations.langchain !== false
30982
+ });
30983
+ this.langSmithPlugin.enable();
30984
+ }
30319
30985
  }
30320
30986
  onDisable() {
30321
30987
  if (this.openaiPlugin) {
@@ -30406,6 +31072,10 @@ var BraintrustPlugin = class extends BasePlugin {
30406
31072
  this.langChainPlugin.disable();
30407
31073
  this.langChainPlugin = null;
30408
31074
  }
31075
+ if (this.langSmithPlugin) {
31076
+ this.langSmithPlugin.disable();
31077
+ this.langSmithPlugin = null;
31078
+ }
30409
31079
  }
30410
31080
  };
30411
31081
 
@@ -30566,6 +31236,15 @@ function modelMetrics(attributes) {
30566
31236
  }
30567
31237
  return Object.keys(out).length > 0 ? out : void 0;
30568
31238
  }
31239
+ function timeToFirstTokenSeconds(attributes, spanStartSeconds) {
31240
+ if (!isObject(attributes)) return void 0;
31241
+ if (spanStartSeconds === void 0) return void 0;
31242
+ const raw = attributes.completionStartTime;
31243
+ const completionStart = raw instanceof Date || typeof raw === "string" || typeof raw === "number" ? epochSeconds(raw) : void 0;
31244
+ if (completionStart === void 0) return void 0;
31245
+ const ttft = completionStart - spanStartSeconds;
31246
+ return Number.isFinite(ttft) && ttft >= 0 ? ttft : void 0;
31247
+ }
30569
31248
  function buildMetadata(exported) {
30570
31249
  const out = {};
30571
31250
  if (exported.entityId !== void 0) out.entity_id = exported.entityId;
@@ -30690,8 +31369,15 @@ var BraintrustObservabilityExporter = class {
30690
31369
  event.metadata = metadata;
30691
31370
  }
30692
31371
  const metrics = modelMetrics(exported.attributes);
30693
- if (metrics) {
30694
- event.metrics = metrics;
31372
+ const ttft = timeToFirstTokenSeconds(
31373
+ exported.attributes,
31374
+ epochSeconds(exported.startTime)
31375
+ );
31376
+ if (metrics || ttft !== void 0) {
31377
+ event.metrics = {
31378
+ ...metrics ?? {},
31379
+ ...ttft !== void 0 ? { time_to_first_token: ttft } : {}
31380
+ };
30695
31381
  }
30696
31382
  if (Object.keys(event).length > 0) {
30697
31383
  record.span.log(event);