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
@@ -4583,7 +4583,7 @@ var DiskCache = class {
4583
4583
  }
4584
4584
  };
4585
4585
 
4586
- // src/prompt-cache/lru-cache.ts
4586
+ // src/lru-cache.ts
4587
4587
  var LRUCache = class {
4588
4588
  cache;
4589
4589
  maxSize;
@@ -5793,25 +5793,46 @@ var HTTPConnection = class _HTTPConnection {
5793
5793
  })
5794
5794
  );
5795
5795
  }
5796
- async post(path2, params, config) {
5796
+ async post(path2, params, config, retries = 0) {
5797
5797
  const { headers, ...rest } = config || {};
5798
5798
  const this_fetch = this.fetch;
5799
5799
  const this_base_url = this.base_url;
5800
5800
  const this_headers = this.headers;
5801
- return await checkResponse(
5802
- await this_fetch(_urljoin(this_base_url, path2), {
5803
- method: "POST",
5804
- headers: {
5805
- Accept: "application/json",
5806
- "Content-Type": "application/json",
5807
- ...this_headers,
5808
- ...headers
5809
- },
5810
- body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
5811
- keepalive: true,
5812
- ...rest
5813
- })
5814
- );
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");
5815
5836
  }
5816
5837
  async get_json(object_type, args = void 0, retries = 0) {
5817
5838
  const tries = retries + 1;
@@ -6711,6 +6732,45 @@ function now() {
6711
6732
  var DEFAULT_FLUSH_BACKPRESSURE_BYTES = 10 * 1024 * 1024;
6712
6733
  var BACKGROUND_LOGGER_BASE_SLEEP_TIME_S = 1;
6713
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
+ }
6714
6774
  var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
6715
6775
  apiConn;
6716
6776
  queue;
@@ -8138,15 +8198,15 @@ function parentSpanIdsUsable(spanId, rootSpanId) {
8138
8198
  return Boolean(spanId) && Boolean(rootSpanId);
8139
8199
  }
8140
8200
  function logError(span, error) {
8141
- let errorMessage = "<error>";
8201
+ let errorMessage2 = "<error>";
8142
8202
  let stackTrace = "";
8143
8203
  if (error instanceof Error) {
8144
- errorMessage = error.message;
8204
+ errorMessage2 = error.message;
8145
8205
  stackTrace = error.stack || "";
8146
8206
  } else {
8147
- errorMessage = String(error);
8207
+ errorMessage2 = String(error);
8148
8208
  }
8149
- span.log({ error: `${errorMessage}
8209
+ span.log({ error: `${errorMessage2}
8150
8210
 
8151
8211
  ${stackTrace}` });
8152
8212
  }
@@ -8525,7 +8585,8 @@ var ObjectFetcher = class {
8525
8585
  version: this.pinnedVersion
8526
8586
  } : {}
8527
8587
  },
8528
- { headers: { "Accept-Encoding": "gzip" } }
8588
+ { headers: { "Accept-Encoding": "gzip" } },
8589
+ BTQL_HTTP_RETRIES
8529
8590
  );
8530
8591
  const respJson = await resp.json();
8531
8592
  const mutate = this.mutateRecord;
@@ -28613,6 +28674,446 @@ function isBraintrustHandler(handler) {
28613
28674
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
28614
28675
  }
28615
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
+
28616
29117
  // src/instrumentation/plugins/pi-coding-agent-channels.ts
28617
29118
  var piCodingAgentChannels = defineChannels(
28618
29119
  "@earendil-works/pi-coding-agent",
@@ -28992,11 +29493,11 @@ function patchAssistantMessageStream(stream, promptState, llmState) {
28992
29493
  function recordPiAssistantMessageEvent(promptState, llmState, event) {
28993
29494
  recordFirstTokenMetric(llmState, event);
28994
29495
  const message = "message" in event ? event.message : void 0;
28995
- const errorMessage = "error" in event ? event.error : void 0;
29496
+ const errorMessage2 = "error" in event ? event.error : void 0;
28996
29497
  if (event.type === "done" && isPiAssistantMessage(message)) {
28997
29498
  finishPiLlmSpan(promptState, llmState, message);
28998
- } else if (event.type === "error" && isPiAssistantMessage(errorMessage)) {
28999
- finishPiLlmSpan(promptState, llmState, errorMessage);
29499
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
29500
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
29000
29501
  }
29001
29502
  }
29002
29503
  function recordFirstTokenMetric(state, event) {
@@ -29514,6 +30015,7 @@ var strandsAgentSDKChannels = defineChannels("@strands-agents/sdk", {
29514
30015
  });
29515
30016
 
29516
30017
  // src/instrumentation/plugins/strands-agent-sdk-plugin.ts
30018
+ var MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES = 32;
29517
30019
  var StrandsAgentSDKPlugin = class extends BasePlugin {
29518
30020
  activeChildParents = /* @__PURE__ */ new WeakMap();
29519
30021
  onEnable() {
@@ -29658,11 +30160,16 @@ function startAgentStream(event, activeChildParents) {
29658
30160
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
29659
30161
  };
29660
30162
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
30163
+ const attachmentCache = createStrandsAttachmentCache();
30164
+ const input = processStrandsInputAttachments(
30165
+ event.arguments[0],
30166
+ attachmentCache
30167
+ );
29661
30168
  const span = parentSpan ? withCurrent(
29662
30169
  parentSpan,
29663
30170
  () => startSpan({
29664
30171
  event: {
29665
- input: event.arguments[0],
30172
+ input,
29666
30173
  metadata
29667
30174
  },
29668
30175
  name: formatAgentSpanName(agent),
@@ -29670,7 +30177,7 @@ function startAgentStream(event, activeChildParents) {
29670
30177
  })
29671
30178
  ) : startSpan({
29672
30179
  event: {
29673
- input: event.arguments[0],
30180
+ input,
29674
30181
  metadata
29675
30182
  },
29676
30183
  name: formatAgentSpanName(agent),
@@ -29678,6 +30185,7 @@ function startAgentStream(event, activeChildParents) {
29678
30185
  });
29679
30186
  return {
29680
30187
  activeTools: /* @__PURE__ */ new Map(),
30188
+ attachmentCache,
29681
30189
  finalized: false,
29682
30190
  metadata,
29683
30191
  span,
@@ -29693,11 +30201,12 @@ function startMultiAgentStream(event, operation, activeChildParents) {
29693
30201
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
29694
30202
  };
29695
30203
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
30204
+ const input = processStrandsInputAttachments(event.arguments[0]);
29696
30205
  const span = parentSpan ? withCurrent(
29697
30206
  parentSpan,
29698
30207
  () => startSpan({
29699
30208
  event: {
29700
- input: event.arguments[0],
30209
+ input,
29701
30210
  metadata
29702
30211
  },
29703
30212
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -29705,7 +30214,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
29705
30214
  })
29706
30215
  ) : startSpan({
29707
30216
  event: {
29708
- input: event.arguments[0],
30217
+ input,
29709
30218
  metadata
29710
30219
  },
29711
30220
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -29814,7 +30323,10 @@ function startModelSpan(state, event) {
29814
30323
  state.span,
29815
30324
  () => startSpan({
29816
30325
  event: {
29817
- 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,
29818
30330
  metadata
29819
30331
  },
29820
30332
  name: formatModelSpanName(model),
@@ -30028,6 +30540,7 @@ function finalizeAgentStream(state, error, output) {
30028
30540
  ...output !== void 0 ? { output } : {}
30029
30541
  });
30030
30542
  state.span.end();
30543
+ state.attachmentCache.strings.clear();
30031
30544
  }
30032
30545
  function finalizeMultiAgentStream(state, activeChildParents, error, output) {
30033
30546
  if (state.finalized) {
@@ -30174,6 +30687,166 @@ function extractNodeResultOutput(result) {
30174
30687
  }
30175
30688
  return result;
30176
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
+ }
30177
30850
  function normalizeContentBlocks(blocks) {
30178
30851
  const text = blocks.map((block) => typeof block.text === "string" ? block.text : void 0).filter((part) => Boolean(part)).join("");
30179
30852
  return text.length > 0 ? text : blocks;
@@ -30303,6 +30976,7 @@ var BraintrustPlugin = class extends BasePlugin {
30303
30976
  gitHubCopilotPlugin = null;
30304
30977
  fluePlugin = null;
30305
30978
  langChainPlugin = null;
30979
+ langSmithPlugin = null;
30306
30980
  piCodingAgentPlugin = null;
30307
30981
  strandsAgentSDKPlugin = null;
30308
30982
  constructor(config = {}) {
@@ -30399,6 +31073,12 @@ var BraintrustPlugin = class extends BasePlugin {
30399
31073
  this.langChainPlugin = new LangChainPlugin();
30400
31074
  this.langChainPlugin.enable();
30401
31075
  }
31076
+ if (integrations.langsmith !== false) {
31077
+ this.langSmithPlugin = new LangSmithPlugin({
31078
+ skipLangChainRuns: integrations.langchain !== false
31079
+ });
31080
+ this.langSmithPlugin.enable();
31081
+ }
30402
31082
  }
30403
31083
  onDisable() {
30404
31084
  if (this.openaiPlugin) {
@@ -30489,6 +31169,10 @@ var BraintrustPlugin = class extends BasePlugin {
30489
31169
  this.langChainPlugin.disable();
30490
31170
  this.langChainPlugin = null;
30491
31171
  }
31172
+ if (this.langSmithPlugin) {
31173
+ this.langSmithPlugin.disable();
31174
+ this.langSmithPlugin = null;
31175
+ }
30492
31176
  }
30493
31177
  };
30494
31178
 
@@ -30553,7 +31237,8 @@ var envIntegrationAliases = {
30553
31237
  langchain: "langchain",
30554
31238
  "langchain-js": "langchain",
30555
31239
  "@langchain": "langchain",
30556
- langgraph: "langgraph"
31240
+ langgraph: "langgraph",
31241
+ langsmith: "langsmith"
30557
31242
  };
30558
31243
  function getDefaultInstrumentationIntegrations() {
30559
31244
  return {
@@ -30584,6 +31269,7 @@ function getDefaultInstrumentationIntegrations() {
30584
31269
  gitHubCopilot: true,
30585
31270
  langchain: true,
30586
31271
  langgraph: true,
31272
+ langsmith: true,
30587
31273
  piCodingAgent: true,
30588
31274
  strandsAgentSDK: true
30589
31275
  };
@@ -30824,6 +31510,15 @@ function modelMetrics(attributes) {
30824
31510
  }
30825
31511
  return Object.keys(out).length > 0 ? out : void 0;
30826
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
+ }
30827
31522
  function buildMetadata(exported) {
30828
31523
  const out = {};
30829
31524
  if (exported.entityId !== void 0) out.entity_id = exported.entityId;
@@ -30948,8 +31643,15 @@ var BraintrustObservabilityExporter = class {
30948
31643
  event.metadata = metadata;
30949
31644
  }
30950
31645
  const metrics = modelMetrics(exported.attributes);
30951
- if (metrics) {
30952
- 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
+ };
30953
31655
  }
30954
31656
  if (Object.keys(event).length > 0) {
30955
31657
  record.span.log(event);
@@ -33671,8 +34373,8 @@ function makeCheckAuthorized(allowedOrgName) {
33671
34373
  return next(createError(400, "Missing x-bt-org-name header"));
33672
34374
  }
33673
34375
  if (allowedOrgName && allowedOrgName !== orgName) {
33674
- const errorMessage = `Org '${orgName}' is not allowed. Only org '${allowedOrgName}' is allowed.`;
33675
- return next(createError(403, errorMessage));
34376
+ const errorMessage2 = `Org '${orgName}' is not allowed. Only org '${allowedOrgName}' is allowed.`;
34377
+ return next(createError(403, errorMessage2));
33676
34378
  }
33677
34379
  const state = await cachedLogin({
33678
34380
  apiKey: req.ctx?.token,