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
package/dist/cli.js CHANGED
@@ -1232,7 +1232,7 @@ var require_package = __commonJS({
1232
1232
  "package.json"(exports2, module2) {
1233
1233
  module2.exports = {
1234
1234
  name: "braintrust",
1235
- version: "3.22.0",
1235
+ version: "3.23.0",
1236
1236
  description: "SDK for integrating Braintrust",
1237
1237
  repository: {
1238
1238
  type: "git",
@@ -5798,7 +5798,7 @@ var DiskCache = class {
5798
5798
  }
5799
5799
  };
5800
5800
 
5801
- // src/prompt-cache/lru-cache.ts
5801
+ // src/lru-cache.ts
5802
5802
  var LRUCache = class {
5803
5803
  cache;
5804
5804
  maxSize;
@@ -7008,25 +7008,46 @@ var HTTPConnection = class _HTTPConnection {
7008
7008
  })
7009
7009
  );
7010
7010
  }
7011
- async post(path8, params, config3) {
7011
+ async post(path8, params, config3, retries = 0) {
7012
7012
  const { headers, ...rest } = config3 || {};
7013
7013
  const this_fetch = this.fetch;
7014
7014
  const this_base_url = this.base_url;
7015
7015
  const this_headers = this.headers;
7016
- return await checkResponse(
7017
- await this_fetch(_urljoin(this_base_url, path8), {
7018
- method: "POST",
7019
- headers: {
7020
- Accept: "application/json",
7021
- "Content-Type": "application/json",
7022
- ...this_headers,
7023
- ...headers
7024
- },
7025
- body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
7026
- keepalive: true,
7027
- ...rest
7028
- })
7029
- );
7016
+ const tries = retries + 1;
7017
+ for (let i = 0; i < tries; i++) {
7018
+ try {
7019
+ return await checkResponse(
7020
+ await this_fetch(_urljoin(this_base_url, path8), {
7021
+ method: "POST",
7022
+ headers: {
7023
+ Accept: "application/json",
7024
+ "Content-Type": "application/json",
7025
+ ...this_headers,
7026
+ ...headers
7027
+ },
7028
+ body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
7029
+ keepalive: true,
7030
+ ...rest
7031
+ })
7032
+ );
7033
+ } catch (error2) {
7034
+ if (config3?.signal?.aborted) {
7035
+ throw getAbortReason(config3.signal);
7036
+ }
7037
+ if (i === tries - 1 || !isRetryableHTTPError(error2)) {
7038
+ throw error2;
7039
+ }
7040
+ debugLogger.debug(
7041
+ `Retrying API request ${path8} after ${formatHTTPError(error2)}`
7042
+ );
7043
+ const sleepTimeMs = HTTP_RETRY_BASE_SLEEP_TIME_S * 1e3 * 2 ** i + Math.random() * HTTP_RETRY_JITTER_MS;
7044
+ debugLogger.info(
7045
+ `Sleeping for ${sleepTimeMs}ms before retrying API request`
7046
+ );
7047
+ await waitForRetry(sleepTimeMs, config3?.signal);
7048
+ }
7049
+ }
7050
+ throw new Error("Unexpected retry state");
7030
7051
  }
7031
7052
  async get_json(object_type, args = void 0, retries = 0) {
7032
7053
  const tries = retries + 1;
@@ -7926,6 +7947,45 @@ function now() {
7926
7947
  var DEFAULT_FLUSH_BACKPRESSURE_BYTES = 10 * 1024 * 1024;
7927
7948
  var BACKGROUND_LOGGER_BASE_SLEEP_TIME_S = 1;
7928
7949
  var HTTP_RETRY_BASE_SLEEP_TIME_S = 1;
7950
+ var HTTP_RETRY_JITTER_MS = 200;
7951
+ var BTQL_HTTP_RETRIES = 3;
7952
+ var RETRYABLE_HTTP_STATUS_CODES = /* @__PURE__ */ new Set([500, 502, 503, 504]);
7953
+ function isRetryableHTTPError(error2) {
7954
+ return !(error2 instanceof FailedHTTPResponse) || RETRYABLE_HTTP_STATUS_CODES.has(error2.status);
7955
+ }
7956
+ function formatHTTPError(error2) {
7957
+ if (error2 instanceof FailedHTTPResponse) {
7958
+ return `${error2.status} ${error2.text}`;
7959
+ }
7960
+ return error2 instanceof Error ? error2.message : String(error2);
7961
+ }
7962
+ function getAbortReason(signal) {
7963
+ return signal.reason ?? new Error("Request aborted");
7964
+ }
7965
+ async function waitForRetry(delayMs, signal) {
7966
+ if (!signal) {
7967
+ await new Promise((resolve2) => setTimeout(resolve2, delayMs));
7968
+ return;
7969
+ }
7970
+ if (signal.aborted) {
7971
+ throw getAbortReason(signal);
7972
+ }
7973
+ await new Promise((resolve2, reject2) => {
7974
+ const onAbort = () => {
7975
+ clearTimeout(timeout);
7976
+ signal.removeEventListener("abort", onAbort);
7977
+ reject2(getAbortReason(signal));
7978
+ };
7979
+ const timeout = setTimeout(() => {
7980
+ signal.removeEventListener("abort", onAbort);
7981
+ resolve2();
7982
+ }, delayMs);
7983
+ signal.addEventListener("abort", onAbort, { once: true });
7984
+ if (signal.aborted) {
7985
+ onAbort();
7986
+ }
7987
+ });
7988
+ }
7929
7989
  var HTTPBackgroundLogger = class _HTTPBackgroundLogger {
7930
7990
  apiConn;
7931
7991
  queue;
@@ -9353,15 +9413,15 @@ function parentSpanIdsUsable(spanId, rootSpanId) {
9353
9413
  return Boolean(spanId) && Boolean(rootSpanId);
9354
9414
  }
9355
9415
  function logError(span, error2) {
9356
- let errorMessage = "<error>";
9416
+ let errorMessage2 = "<error>";
9357
9417
  let stackTrace = "";
9358
9418
  if (error2 instanceof Error) {
9359
- errorMessage = error2.message;
9419
+ errorMessage2 = error2.message;
9360
9420
  stackTrace = error2.stack || "";
9361
9421
  } else {
9362
- errorMessage = String(error2);
9422
+ errorMessage2 = String(error2);
9363
9423
  }
9364
- span.log({ error: `${errorMessage}
9424
+ span.log({ error: `${errorMessage2}
9365
9425
 
9366
9426
  ${stackTrace}` });
9367
9427
  }
@@ -9740,7 +9800,8 @@ var ObjectFetcher = class {
9740
9800
  version: this.pinnedVersion
9741
9801
  } : {}
9742
9802
  },
9743
- { headers: { "Accept-Encoding": "gzip" } }
9803
+ { headers: { "Accept-Encoding": "gzip" } },
9804
+ BTQL_HTTP_RETRIES
9744
9805
  );
9745
9806
  const respJson = await resp.json();
9746
9807
  const mutate = this.mutateRecord;
@@ -32916,6 +32977,446 @@ function isBraintrustHandler(handler) {
32916
32977
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
32917
32978
  }
32918
32979
 
32980
+ // src/instrumentation/plugins/langsmith-channels.ts
32981
+ var langSmithChannels = defineChannels("langsmith", {
32982
+ createRun: channel({
32983
+ channelName: "Client.createRun",
32984
+ kind: "async"
32985
+ }),
32986
+ updateRun: channel({
32987
+ channelName: "Client.updateRun",
32988
+ kind: "async"
32989
+ }),
32990
+ batchIngestRuns: channel({
32991
+ channelName: "Client.batchIngestRuns",
32992
+ kind: "async"
32993
+ })
32994
+ });
32995
+
32996
+ // src/instrumentation/plugins/langsmith-plugin.ts
32997
+ var MAX_COMPLETED_RUNS = 1e4;
32998
+ var BLOCKED_METADATA_KEYS = /* @__PURE__ */ new Set([
32999
+ "__proto__",
33000
+ "constructor",
33001
+ "prototype",
33002
+ "usage_metadata"
33003
+ ]);
33004
+ var BLOCKED_METADATA_PREFIXES = ["__pregel_", "langgraph_", "lc_"];
33005
+ var LLM_SETTING_KEYS = [
33006
+ "temperature",
33007
+ "top_p",
33008
+ "max_tokens",
33009
+ "frequency_penalty",
33010
+ "presence_penalty",
33011
+ "stop",
33012
+ "response_format"
33013
+ ];
33014
+ var LangSmithPlugin = class extends BasePlugin {
33015
+ activeRuns = /* @__PURE__ */ new Map();
33016
+ completedRuns = new LRUCache({
33017
+ max: MAX_COMPLETED_RUNS
33018
+ });
33019
+ skipLangChainRuns;
33020
+ constructor(options = {}) {
33021
+ super();
33022
+ this.skipLangChainRuns = options.skipLangChainRuns ?? true;
33023
+ }
33024
+ onEnable() {
33025
+ const createChannel = langSmithChannels.createRun.tracingChannel();
33026
+ const createHandlers = {
33027
+ start: (event) => {
33028
+ this.containLifecycleFailure("createRun", () => {
33029
+ this.processCreate(event.arguments[0]);
33030
+ });
33031
+ }
33032
+ };
33033
+ createChannel.subscribe(createHandlers);
33034
+ this.unsubscribers.push(() => createChannel.unsubscribe(createHandlers));
33035
+ const updateChannel = langSmithChannels.updateRun.tracingChannel();
33036
+ const updateHandlers = {
33037
+ start: (event) => {
33038
+ this.containLifecycleFailure("updateRun", () => {
33039
+ this.processUpdate(event.arguments[0], event.arguments[1]);
33040
+ });
33041
+ }
33042
+ };
33043
+ updateChannel.subscribe(updateHandlers);
33044
+ this.unsubscribers.push(() => updateChannel.unsubscribe(updateHandlers));
33045
+ const batchChannel = langSmithChannels.batchIngestRuns.tracingChannel();
33046
+ const batchHandlers = {
33047
+ start: (event) => {
33048
+ this.containLifecycleFailure("batchIngestRuns", () => {
33049
+ this.processBatch(event.arguments[0]);
33050
+ });
33051
+ }
33052
+ };
33053
+ batchChannel.subscribe(batchHandlers);
33054
+ this.unsubscribers.push(() => batchChannel.unsubscribe(batchHandlers));
33055
+ }
33056
+ onDisable() {
33057
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
33058
+ for (const { span } of this.activeRuns.values()) {
33059
+ span.end();
33060
+ }
33061
+ this.activeRuns.clear();
33062
+ this.completedRuns.clear();
33063
+ }
33064
+ processBatch(batch) {
33065
+ if (!isRecord2(batch)) {
33066
+ return;
33067
+ }
33068
+ const creates = ownValue(batch, "runCreates");
33069
+ if (Array.isArray(creates)) {
33070
+ const parentFirst = [...creates].sort((left, right) => {
33071
+ const leftId = stringValue2(ownValue(left, "id"));
33072
+ const rightId = stringValue2(ownValue(right, "id"));
33073
+ const leftParent = stringValue2(ownValue(left, "parent_run_id"));
33074
+ const rightParent = stringValue2(ownValue(right, "parent_run_id"));
33075
+ if (leftParent && leftParent === rightId) {
33076
+ return 1;
33077
+ }
33078
+ if (rightParent && rightParent === leftId) {
33079
+ return -1;
33080
+ }
33081
+ return dottedOrderDepth(left) - dottedOrderDepth(right);
33082
+ });
33083
+ for (const run2 of parentFirst) {
33084
+ this.containLifecycleFailure("batchIngestRuns create", () => {
33085
+ this.processCreate(run2);
33086
+ });
33087
+ }
33088
+ }
33089
+ const updates = ownValue(batch, "runUpdates");
33090
+ if (Array.isArray(updates)) {
33091
+ for (const run2 of updates) {
33092
+ this.containLifecycleFailure("batchIngestRuns update", () => {
33093
+ this.processUpdate(stringValue2(ownValue(run2, "id")) ?? "", run2);
33094
+ });
33095
+ }
33096
+ }
33097
+ }
33098
+ processCreate(run2) {
33099
+ const id = stringValue2(ownValue(run2, "id"));
33100
+ if (!id || this.completedRuns.get(id)) {
33101
+ return;
33102
+ }
33103
+ if (this.shouldSkipLangChainRun(run2)) {
33104
+ this.completedRuns.set(id, true);
33105
+ return;
33106
+ }
33107
+ const active = this.activeRuns.get(id);
33108
+ if (active) {
33109
+ const previous = active.run;
33110
+ active.run = mergeRuns(previous, run2);
33111
+ this.logRun(
33112
+ active.span,
33113
+ active.run,
33114
+ previous,
33115
+ timestampSeconds(ownValue(active.run, "end_time")) !== void 0 || errorMessage(ownValue(active.run, "error")) !== void 0
33116
+ );
33117
+ this.endIfComplete(id, active, active.run);
33118
+ return;
33119
+ }
33120
+ const span = this.startRunSpan(id, run2);
33121
+ const activeRun = { run: run2, span };
33122
+ this.activeRuns.set(id, activeRun);
33123
+ this.logRun(
33124
+ span,
33125
+ run2,
33126
+ void 0,
33127
+ timestampSeconds(ownValue(run2, "end_time")) !== void 0 || errorMessage(ownValue(run2, "error")) !== void 0
33128
+ );
33129
+ this.endIfComplete(id, activeRun, run2);
33130
+ }
33131
+ processUpdate(explicitId, run2) {
33132
+ const id = stringValue2(explicitId) ?? stringValue2(ownValue(run2, "id"));
33133
+ if (!id || this.completedRuns.get(id)) {
33134
+ return;
33135
+ }
33136
+ if (this.shouldSkipLangChainRun(run2)) {
33137
+ const active2 = this.activeRuns.get(id);
33138
+ active2?.span.end();
33139
+ this.activeRuns.delete(id);
33140
+ this.completedRuns.set(id, true);
33141
+ return;
33142
+ }
33143
+ let active = this.activeRuns.get(id);
33144
+ if (!active) {
33145
+ const span = this.startRunSpan(id, run2);
33146
+ active = { run: run2, span };
33147
+ this.activeRuns.set(id, active);
33148
+ }
33149
+ const previous = active.run;
33150
+ active.run = mergeRuns(previous, run2);
33151
+ this.logRun(active.span, active.run, previous, true);
33152
+ this.endIfComplete(id, active, active.run);
33153
+ }
33154
+ startRunSpan(id, run2) {
33155
+ const traceId = stringValue2(ownValue(run2, "trace_id")) ?? id;
33156
+ const parentId = stringValue2(ownValue(run2, "parent_run_id")) ?? stringValue2(ownValue(ownValue(run2, "parent_run"), "id"));
33157
+ const startTime = timestampSeconds(ownValue(run2, "start_time"));
33158
+ return startSpan({
33159
+ name: stringValue2(ownValue(run2, "name")) ?? "LangSmith run",
33160
+ spanId: id,
33161
+ parentSpanIds: {
33162
+ parentSpanIds: parentId ? [parentId] : [],
33163
+ rootSpanId: traceId
33164
+ },
33165
+ spanAttributes: {
33166
+ type: mapRunType(ownValue(run2, "run_type"))
33167
+ },
33168
+ ...startTime === void 0 ? {} : { startTime },
33169
+ event: { id }
33170
+ });
33171
+ }
33172
+ logRun(span, run2, previous, includeOutput) {
33173
+ const inputs = preferOwnValue(run2, previous, "inputs");
33174
+ const outputs = preferOwnValue(run2, previous, "outputs");
33175
+ const error2 = errorMessage(preferOwnValue(run2, previous, "error"));
33176
+ const metadata = extractMetadata(run2, previous);
33177
+ const tags = extractTags(preferOwnValue(run2, previous, "tags"));
33178
+ const metrics = extractMetrics(run2, previous);
33179
+ span.log({
33180
+ ...inputs === void 0 ? {} : { input: sanitizeLoggedValue(inputs) },
33181
+ ...includeOutput && outputs !== void 0 ? { output: sanitizeLoggedValue(outputs) } : {},
33182
+ ...error2 === void 0 ? {} : { error: error2 },
33183
+ ...metadata === void 0 ? {} : { metadata },
33184
+ ...tags === void 0 ? {} : { tags },
33185
+ ...Object.keys(metrics).length === 0 ? {} : { metrics }
33186
+ });
33187
+ }
33188
+ endIfComplete(id, active, run2) {
33189
+ const endTime = timestampSeconds(ownValue(run2, "end_time"));
33190
+ if (endTime === void 0 && errorMessage(ownValue(run2, "error")) === void 0) {
33191
+ return;
33192
+ }
33193
+ active.span.end(endTime === void 0 ? void 0 : { endTime });
33194
+ this.activeRuns.delete(id);
33195
+ this.completedRuns.set(id, true);
33196
+ }
33197
+ shouldSkipLangChainRun(run2) {
33198
+ if (!this.skipLangChainRuns) {
33199
+ return false;
33200
+ }
33201
+ const serialized = ownValue(run2, "serialized");
33202
+ return isRecord2(serialized) && ownValue(serialized, "lc") === 1;
33203
+ }
33204
+ containLifecycleFailure(operation, fn) {
33205
+ try {
33206
+ fn();
33207
+ } catch (error2) {
33208
+ debugLogger.error(
33209
+ `Failed to process LangSmith ${operation} instrumentation:`,
33210
+ error2
33211
+ );
33212
+ }
33213
+ }
33214
+ };
33215
+ function ownValue(value, key) {
33216
+ if (!isRecord2(value)) {
33217
+ return void 0;
33218
+ }
33219
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
33220
+ return descriptor && "value" in descriptor ? descriptor.value : void 0;
33221
+ }
33222
+ function preferOwnValue(current, previous, key) {
33223
+ const currentDescriptor = isRecord2(current) ? Object.getOwnPropertyDescriptor(current, key) : void 0;
33224
+ if (currentDescriptor && "value" in currentDescriptor) {
33225
+ return currentDescriptor.value;
33226
+ }
33227
+ return ownValue(previous, key);
33228
+ }
33229
+ function mergeRuns(previous, current) {
33230
+ const entries = /* @__PURE__ */ new Map();
33231
+ for (const value of [previous, current]) {
33232
+ if (!isRecord2(value)) {
33233
+ continue;
33234
+ }
33235
+ for (const [key, descriptor] of Object.entries(
33236
+ Object.getOwnPropertyDescriptors(value)
33237
+ )) {
33238
+ if (descriptor.enumerable && "value" in descriptor) {
33239
+ entries.set(key, descriptor.value);
33240
+ }
33241
+ }
33242
+ }
33243
+ return Object.fromEntries(entries);
33244
+ }
33245
+ function isRecord2(value) {
33246
+ return typeof value === "object" && value !== null && !Array.isArray(value);
33247
+ }
33248
+ function stringValue2(value) {
33249
+ return typeof value === "string" && value.length > 0 ? value : void 0;
33250
+ }
33251
+ function timestampSeconds(value) {
33252
+ if (value instanceof Date) {
33253
+ const timestamp = value.getTime();
33254
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
33255
+ }
33256
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
33257
+ return value > 1e10 ? value / 1e3 : value;
33258
+ }
33259
+ if (typeof value === "string") {
33260
+ const timestamp = Date.parse(value);
33261
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
33262
+ }
33263
+ return void 0;
33264
+ }
33265
+ function dottedOrderDepth(run2) {
33266
+ const dottedOrder = stringValue2(ownValue(run2, "dotted_order"));
33267
+ return dottedOrder ? dottedOrder.split(".").length : Number.MAX_SAFE_INTEGER;
33268
+ }
33269
+ function mapRunType(runType) {
33270
+ switch (runType) {
33271
+ case "llm":
33272
+ case "embedding":
33273
+ return "llm" /* LLM */;
33274
+ case "tool":
33275
+ case "retriever":
33276
+ return "tool" /* TOOL */;
33277
+ default:
33278
+ return "task" /* TASK */;
33279
+ }
33280
+ }
33281
+ function extractMetadata(run2, previous) {
33282
+ const extra = preferOwnValue(run2, previous, "extra");
33283
+ const rawMetadata = ownValue(extra, "metadata");
33284
+ if (!isRecord2(rawMetadata)) {
33285
+ return void 0;
33286
+ }
33287
+ const metadata = {};
33288
+ for (const [key, descriptor] of Object.entries(
33289
+ Object.getOwnPropertyDescriptors(rawMetadata)
33290
+ )) {
33291
+ 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}`)) {
33292
+ continue;
33293
+ }
33294
+ const normalizedKey = key === "ls_provider" ? "provider" : key === "ls_model_name" ? "model" : key.startsWith("ls_") ? key.slice(3) : key;
33295
+ const sanitized = sanitizeLoggedValue(descriptor.value);
33296
+ if (sanitized !== void 0) {
33297
+ metadata[normalizedKey] = sanitized;
33298
+ }
33299
+ }
33300
+ return Object.keys(metadata).length === 0 ? void 0 : metadata;
33301
+ }
33302
+ function extractTags(value) {
33303
+ if (!Array.isArray(value)) {
33304
+ return void 0;
33305
+ }
33306
+ const tags = value.filter(
33307
+ (tag) => typeof tag === "string" && tag.length > 0
33308
+ );
33309
+ return tags.length > 0 ? tags : void 0;
33310
+ }
33311
+ function extractMetrics(run2, previous) {
33312
+ const outputs = preferOwnValue(run2, previous, "outputs");
33313
+ const extra = preferOwnValue(run2, previous, "extra");
33314
+ const metadata = ownValue(extra, "metadata");
33315
+ const usage = ownValue(outputs, "usage_metadata") ?? ownValue(metadata, "usage_metadata");
33316
+ const metrics = {};
33317
+ assignFirstMetric(metrics, "prompt_tokens", usage, [
33318
+ "input_tokens",
33319
+ "prompt_tokens"
33320
+ ]);
33321
+ assignFirstMetric(metrics, "completion_tokens", usage, [
33322
+ "output_tokens",
33323
+ "completion_tokens"
33324
+ ]);
33325
+ assignFirstMetric(metrics, "tokens", usage, ["total_tokens", "tokens"]);
33326
+ const inputTokenDetails = ownValue(usage, "input_token_details");
33327
+ assignFirstMetric(metrics, "prompt_cached_tokens", inputTokenDetails, [
33328
+ "cache_read",
33329
+ "cached_tokens"
33330
+ ]);
33331
+ if (metrics.prompt_cached_tokens === void 0) {
33332
+ assignFirstMetric(metrics, "prompt_cached_tokens", usage, [
33333
+ "cache_read_input_tokens",
33334
+ "prompt_cached_tokens"
33335
+ ]);
33336
+ }
33337
+ assignFirstMetric(
33338
+ metrics,
33339
+ "prompt_cache_creation_tokens",
33340
+ inputTokenDetails,
33341
+ ["cache_creation"]
33342
+ );
33343
+ if (metrics.prompt_cache_creation_tokens === void 0) {
33344
+ assignFirstMetric(metrics, "prompt_cache_creation_tokens", usage, [
33345
+ "cache_creation_input_tokens",
33346
+ "prompt_cache_creation_tokens"
33347
+ ]);
33348
+ }
33349
+ if (metrics.tokens === void 0 && (metrics.prompt_tokens !== void 0 || metrics.completion_tokens !== void 0)) {
33350
+ metrics.tokens = (metrics.prompt_tokens ?? 0) + (metrics.completion_tokens ?? 0);
33351
+ }
33352
+ const startTime = timestampSeconds(
33353
+ preferOwnValue(run2, previous, "start_time")
33354
+ );
33355
+ const events = preferOwnValue(run2, previous, "events");
33356
+ if (startTime !== void 0 && Array.isArray(events)) {
33357
+ for (const event of events) {
33358
+ if (ownValue(event, "name") !== "new_token") {
33359
+ continue;
33360
+ }
33361
+ const eventTime2 = timestampSeconds(ownValue(event, "time"));
33362
+ if (eventTime2 !== void 0 && eventTime2 >= startTime) {
33363
+ metrics.time_to_first_token = eventTime2 - startTime;
33364
+ }
33365
+ break;
33366
+ }
33367
+ }
33368
+ return metrics;
33369
+ }
33370
+ function assignFirstMetric(metrics, target, source, keys) {
33371
+ for (const key of keys) {
33372
+ const value = ownValue(source, key);
33373
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
33374
+ metrics[target] = value;
33375
+ return;
33376
+ }
33377
+ }
33378
+ }
33379
+ function errorMessage(value) {
33380
+ if (typeof value === "string") {
33381
+ return value;
33382
+ }
33383
+ if (value instanceof Error) {
33384
+ return value.message;
33385
+ }
33386
+ return void 0;
33387
+ }
33388
+ function sanitizeLoggedValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
33389
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
33390
+ return value;
33391
+ }
33392
+ if (typeof value === "number") {
33393
+ return Number.isFinite(value) ? value : void 0;
33394
+ }
33395
+ if (value instanceof Date) {
33396
+ return Number.isFinite(value.getTime()) ? value.toISOString() : void 0;
33397
+ }
33398
+ if (typeof value !== "object" || depth >= 20) {
33399
+ return void 0;
33400
+ }
33401
+ if (seen.has(value)) {
33402
+ return "[Circular]";
33403
+ }
33404
+ seen.add(value);
33405
+ if (Array.isArray(value)) {
33406
+ return value.map((item) => sanitizeLoggedValue(item, seen, depth + 1));
33407
+ }
33408
+ const entries = [];
33409
+ for (const [key, descriptor] of Object.entries(
33410
+ Object.getOwnPropertyDescriptors(value)
33411
+ )) {
33412
+ if (!descriptor.enumerable || !("value" in descriptor) || BLOCKED_METADATA_KEYS.has(key)) {
33413
+ continue;
33414
+ }
33415
+ entries.push([key, sanitizeLoggedValue(descriptor.value, seen, depth + 1)]);
33416
+ }
33417
+ return Object.fromEntries(entries);
33418
+ }
33419
+
32919
33420
  // src/instrumentation/plugins/pi-coding-agent-channels.ts
32920
33421
  var piCodingAgentChannels = defineChannels(
32921
33422
  "@earendil-works/pi-coding-agent",
@@ -33295,11 +33796,11 @@ function patchAssistantMessageStream(stream, promptState, llmState) {
33295
33796
  function recordPiAssistantMessageEvent(promptState, llmState, event) {
33296
33797
  recordFirstTokenMetric(llmState, event);
33297
33798
  const message = "message" in event ? event.message : void 0;
33298
- const errorMessage = "error" in event ? event.error : void 0;
33799
+ const errorMessage2 = "error" in event ? event.error : void 0;
33299
33800
  if (event.type === "done" && isPiAssistantMessage(message)) {
33300
33801
  finishPiLlmSpan(promptState, llmState, message);
33301
- } else if (event.type === "error" && isPiAssistantMessage(errorMessage)) {
33302
- finishPiLlmSpan(promptState, llmState, errorMessage);
33802
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
33803
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
33303
33804
  }
33304
33805
  }
33305
33806
  function recordFirstTokenMetric(state, event) {
@@ -33817,6 +34318,7 @@ var strandsAgentSDKChannels = defineChannels("@strands-agents/sdk", {
33817
34318
  });
33818
34319
 
33819
34320
  // src/instrumentation/plugins/strands-agent-sdk-plugin.ts
34321
+ var MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES = 32;
33820
34322
  var StrandsAgentSDKPlugin = class extends BasePlugin {
33821
34323
  activeChildParents = /* @__PURE__ */ new WeakMap();
33822
34324
  onEnable() {
@@ -33961,11 +34463,16 @@ function startAgentStream(event, activeChildParents) {
33961
34463
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
33962
34464
  };
33963
34465
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
34466
+ const attachmentCache = createStrandsAttachmentCache();
34467
+ const input = processStrandsInputAttachments(
34468
+ event.arguments[0],
34469
+ attachmentCache
34470
+ );
33964
34471
  const span = parentSpan ? withCurrent(
33965
34472
  parentSpan,
33966
34473
  () => startSpan({
33967
34474
  event: {
33968
- input: event.arguments[0],
34475
+ input,
33969
34476
  metadata
33970
34477
  },
33971
34478
  name: formatAgentSpanName(agent),
@@ -33973,7 +34480,7 @@ function startAgentStream(event, activeChildParents) {
33973
34480
  })
33974
34481
  ) : startSpan({
33975
34482
  event: {
33976
- input: event.arguments[0],
34483
+ input,
33977
34484
  metadata
33978
34485
  },
33979
34486
  name: formatAgentSpanName(agent),
@@ -33981,6 +34488,7 @@ function startAgentStream(event, activeChildParents) {
33981
34488
  });
33982
34489
  return {
33983
34490
  activeTools: /* @__PURE__ */ new Map(),
34491
+ attachmentCache,
33984
34492
  finalized: false,
33985
34493
  metadata,
33986
34494
  span,
@@ -33996,11 +34504,12 @@ function startMultiAgentStream(event, operation, activeChildParents) {
33996
34504
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
33997
34505
  };
33998
34506
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
34507
+ const input = processStrandsInputAttachments(event.arguments[0]);
33999
34508
  const span = parentSpan ? withCurrent(
34000
34509
  parentSpan,
34001
34510
  () => startSpan({
34002
34511
  event: {
34003
- input: event.arguments[0],
34512
+ input,
34004
34513
  metadata
34005
34514
  },
34006
34515
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -34008,7 +34517,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
34008
34517
  })
34009
34518
  ) : startSpan({
34010
34519
  event: {
34011
- input: event.arguments[0],
34520
+ input,
34012
34521
  metadata
34013
34522
  },
34014
34523
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -34117,7 +34626,10 @@ function startModelSpan(state, event) {
34117
34626
  state.span,
34118
34627
  () => startSpan({
34119
34628
  event: {
34120
- input: Array.isArray(event.agent?.messages) ? event.agent.messages : void 0,
34629
+ input: Array.isArray(event.agent?.messages) ? processStrandsInputAttachments(
34630
+ event.agent.messages,
34631
+ state.attachmentCache
34632
+ ) : void 0,
34121
34633
  metadata
34122
34634
  },
34123
34635
  name: formatModelSpanName(model),
@@ -34331,6 +34843,7 @@ function finalizeAgentStream(state, error2, output) {
34331
34843
  ...output !== void 0 ? { output } : {}
34332
34844
  });
34333
34845
  state.span.end();
34846
+ state.attachmentCache.strings.clear();
34334
34847
  }
34335
34848
  function finalizeMultiAgentStream(state, activeChildParents, error2, output) {
34336
34849
  if (state.finalized) {
@@ -34477,6 +34990,166 @@ function extractNodeResultOutput(result) {
34477
34990
  }
34478
34991
  return result;
34479
34992
  }
34993
+ var STRANDS_MEDIA_TYPES = {
34994
+ png: "image/png",
34995
+ jpg: "image/jpeg",
34996
+ jpeg: "image/jpeg",
34997
+ gif: "image/gif",
34998
+ webp: "image/webp",
34999
+ mkv: "video/x-matroska",
35000
+ mov: "video/quicktime",
35001
+ mp4: "video/mp4",
35002
+ webm: "video/webm",
35003
+ flv: "video/x-flv",
35004
+ mpeg: "video/mpeg",
35005
+ mpg: "video/mpeg",
35006
+ wmv: "video/x-ms-wmv",
35007
+ "3gp": "video/3gpp",
35008
+ pdf: "application/pdf",
35009
+ csv: "text/csv",
35010
+ doc: "application/msword",
35011
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
35012
+ xls: "application/vnd.ms-excel",
35013
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
35014
+ html: "text/html",
35015
+ txt: "text/plain",
35016
+ md: "text/markdown",
35017
+ json: "application/json",
35018
+ xml: "application/xml"
35019
+ };
35020
+ function createStrandsAttachmentCache() {
35021
+ return {
35022
+ objects: /* @__PURE__ */ new WeakMap(),
35023
+ strings: new LRUCache({
35024
+ max: MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES
35025
+ })
35026
+ };
35027
+ }
35028
+ function processStrandsInputAttachments(input, cache = createStrandsAttachmentCache()) {
35029
+ try {
35030
+ return processStrandsInputNode(input, cache);
35031
+ } catch (error2) {
35032
+ logInstrumentationError5("Strands Agent SDK input attachments", error2);
35033
+ return input;
35034
+ }
35035
+ }
35036
+ function processStrandsInputNode(value, cache) {
35037
+ if (value instanceof BaseAttachment) {
35038
+ return value;
35039
+ }
35040
+ if (Array.isArray(value)) {
35041
+ return value.map((child) => processStrandsInputNode(child, cache));
35042
+ }
35043
+ if (!isObject(value)) {
35044
+ return value;
35045
+ }
35046
+ const directMedia = processDirectStrandsMediaBlock(value, cache);
35047
+ if (directMedia !== void 0) {
35048
+ return directMedia;
35049
+ }
35050
+ const wrappedMedia = processWrappedStrandsMediaBlock(value, cache);
35051
+ if (wrappedMedia !== void 0) {
35052
+ return wrappedMedia;
35053
+ }
35054
+ if (value.type === "message" && Array.isArray(value.content)) {
35055
+ return {
35056
+ role: value.role,
35057
+ content: value.content.map(
35058
+ (child) => processStrandsInputNode(child, cache)
35059
+ ),
35060
+ ...value.metadata !== void 0 ? { metadata: value.metadata } : {}
35061
+ };
35062
+ }
35063
+ if (typeof value.toJSON === "function") {
35064
+ return processStrandsInputNode(value.toJSON(), cache);
35065
+ }
35066
+ return Object.fromEntries(
35067
+ Object.entries(value).map(([key, child]) => [
35068
+ key,
35069
+ processStrandsInputNode(child, cache)
35070
+ ])
35071
+ );
35072
+ }
35073
+ function processDirectStrandsMediaBlock(block, cache) {
35074
+ if (!isStrandsMediaBlock(block)) {
35075
+ return void 0;
35076
+ }
35077
+ const mediaKey = block.type === "imageBlock" ? "image" : block.type === "videoBlock" ? "video" : "document";
35078
+ return createStrandsMediaAttachment(mediaKey, block, cache);
35079
+ }
35080
+ function isStrandsMediaBlock(block) {
35081
+ return (block.type === "imageBlock" || block.type === "videoBlock" || block.type === "documentBlock") && typeof block.format === "string" && isObject(block.source);
35082
+ }
35083
+ function processWrappedStrandsMediaBlock(block, cache) {
35084
+ for (const mediaKey of ["image", "video", "document"]) {
35085
+ const media = block[mediaKey];
35086
+ if (!Object.hasOwn(block, mediaKey) || !isObject(media)) {
35087
+ continue;
35088
+ }
35089
+ const processed = createStrandsMediaAttachment(mediaKey, media, cache);
35090
+ if (processed !== void 0) {
35091
+ return processed;
35092
+ }
35093
+ }
35094
+ return void 0;
35095
+ }
35096
+ function createStrandsMediaAttachment(mediaKey, media, cache) {
35097
+ const format = media.format;
35098
+ const source = media.source;
35099
+ if (typeof format !== "string" || !isObject(source) || !Object.hasOwn(source, "bytes")) {
35100
+ return void 0;
35101
+ }
35102
+ const contentType = STRANDS_MEDIA_TYPES[format.toLowerCase()];
35103
+ if (!contentType) {
35104
+ return void 0;
35105
+ }
35106
+ const filename = mediaKey === "document" && typeof media.name === "string" && media.name.length > 0 ? media.name : `${mediaKey}.${format.toLowerCase()}`;
35107
+ const attachment = getOrCreateStrandsAttachment(
35108
+ source.bytes,
35109
+ filename,
35110
+ contentType,
35111
+ cache
35112
+ );
35113
+ if (!attachment) {
35114
+ return void 0;
35115
+ }
35116
+ const { type: _type, ...serializedMedia } = media;
35117
+ const { type: _sourceType, ...serializedSource } = source;
35118
+ return {
35119
+ [mediaKey]: {
35120
+ ...serializedMedia,
35121
+ source: {
35122
+ ...serializedSource,
35123
+ bytes: attachment
35124
+ }
35125
+ }
35126
+ };
35127
+ }
35128
+ function getOrCreateStrandsAttachment(data, filename, contentType, cache) {
35129
+ const key = `${contentType}\0${filename}`;
35130
+ const attachments = typeof data === "string" ? cache.strings.get(data) : isObject(data) ? cache.objects.get(data) : void 0;
35131
+ const cached = attachments?.get(key);
35132
+ if (cached) {
35133
+ return cached;
35134
+ }
35135
+ const blob = convertDataToBlob(data, contentType);
35136
+ if (!blob) {
35137
+ return void 0;
35138
+ }
35139
+ const attachment = new Attachment({
35140
+ data: blob,
35141
+ filename,
35142
+ contentType
35143
+ });
35144
+ const updatedAttachments = attachments ?? /* @__PURE__ */ new Map();
35145
+ updatedAttachments.set(key, attachment);
35146
+ if (typeof data === "string") {
35147
+ cache.strings.set(data, updatedAttachments);
35148
+ } else if (isObject(data)) {
35149
+ cache.objects.set(data, updatedAttachments);
35150
+ }
35151
+ return attachment;
35152
+ }
34480
35153
  function normalizeContentBlocks(blocks) {
34481
35154
  const text = blocks.map((block) => typeof block.text === "string" ? block.text : void 0).filter((part) => Boolean(part)).join("");
34482
35155
  return text.length > 0 ? text : blocks;
@@ -34606,6 +35279,7 @@ var BraintrustPlugin = class extends BasePlugin {
34606
35279
  gitHubCopilotPlugin = null;
34607
35280
  fluePlugin = null;
34608
35281
  langChainPlugin = null;
35282
+ langSmithPlugin = null;
34609
35283
  piCodingAgentPlugin = null;
34610
35284
  strandsAgentSDKPlugin = null;
34611
35285
  constructor(config3 = {}) {
@@ -34702,6 +35376,12 @@ var BraintrustPlugin = class extends BasePlugin {
34702
35376
  this.langChainPlugin = new LangChainPlugin();
34703
35377
  this.langChainPlugin.enable();
34704
35378
  }
35379
+ if (integrations.langsmith !== false) {
35380
+ this.langSmithPlugin = new LangSmithPlugin({
35381
+ skipLangChainRuns: integrations.langchain !== false
35382
+ });
35383
+ this.langSmithPlugin.enable();
35384
+ }
34705
35385
  }
34706
35386
  onDisable() {
34707
35387
  if (this.openaiPlugin) {
@@ -34792,6 +35472,10 @@ var BraintrustPlugin = class extends BasePlugin {
34792
35472
  this.langChainPlugin.disable();
34793
35473
  this.langChainPlugin = null;
34794
35474
  }
35475
+ if (this.langSmithPlugin) {
35476
+ this.langSmithPlugin.disable();
35477
+ this.langSmithPlugin = null;
35478
+ }
34795
35479
  }
34796
35480
  };
34797
35481
 
@@ -34856,7 +35540,8 @@ var envIntegrationAliases = {
34856
35540
  langchain: "langchain",
34857
35541
  "langchain-js": "langchain",
34858
35542
  "@langchain": "langchain",
34859
- langgraph: "langgraph"
35543
+ langgraph: "langgraph",
35544
+ langsmith: "langsmith"
34860
35545
  };
34861
35546
  function getDefaultInstrumentationIntegrations() {
34862
35547
  return {
@@ -34887,6 +35572,7 @@ function getDefaultInstrumentationIntegrations() {
34887
35572
  gitHubCopilot: true,
34888
35573
  langchain: true,
34889
35574
  langgraph: true,
35575
+ langsmith: true,
34890
35576
  piCodingAgent: true,
34891
35577
  strandsAgentSDK: true
34892
35578
  };
@@ -35127,6 +35813,15 @@ function modelMetrics(attributes) {
35127
35813
  }
35128
35814
  return Object.keys(out).length > 0 ? out : void 0;
35129
35815
  }
35816
+ function timeToFirstTokenSeconds(attributes, spanStartSeconds) {
35817
+ if (!isObject(attributes)) return void 0;
35818
+ if (spanStartSeconds === void 0) return void 0;
35819
+ const raw = attributes.completionStartTime;
35820
+ const completionStart = raw instanceof Date || typeof raw === "string" || typeof raw === "number" ? epochSeconds(raw) : void 0;
35821
+ if (completionStart === void 0) return void 0;
35822
+ const ttft = completionStart - spanStartSeconds;
35823
+ return Number.isFinite(ttft) && ttft >= 0 ? ttft : void 0;
35824
+ }
35130
35825
  function buildMetadata(exported) {
35131
35826
  const out = {};
35132
35827
  if (exported.entityId !== void 0) out.entity_id = exported.entityId;
@@ -35251,8 +35946,15 @@ var BraintrustObservabilityExporter = class {
35251
35946
  event.metadata = metadata;
35252
35947
  }
35253
35948
  const metrics = modelMetrics(exported.attributes);
35254
- if (metrics) {
35255
- event.metrics = metrics;
35949
+ const ttft = timeToFirstTokenSeconds(
35950
+ exported.attributes,
35951
+ epochSeconds(exported.startTime)
35952
+ );
35953
+ if (metrics || ttft !== void 0) {
35954
+ event.metrics = {
35955
+ ...metrics ?? {},
35956
+ ...ttft !== void 0 ? { time_to_first_token: ttft } : {}
35957
+ };
35256
35958
  }
35257
35959
  if (Object.keys(event).length > 0) {
35258
35960
  record.span.log(event);
@@ -36951,8 +37653,8 @@ function makeCheckAuthorized(allowedOrgName) {
36951
37653
  return next((0, import_http_errors.default)(400, "Missing x-bt-org-name header"));
36952
37654
  }
36953
37655
  if (allowedOrgName && allowedOrgName !== orgName) {
36954
- const errorMessage = `Org '${orgName}' is not allowed. Only org '${allowedOrgName}' is allowed.`;
36955
- return next((0, import_http_errors.default)(403, errorMessage));
37656
+ const errorMessage2 = `Org '${orgName}' is not allowed. Only org '${allowedOrgName}' is allowed.`;
37657
+ return next((0, import_http_errors.default)(403, errorMessage2));
36956
37658
  }
36957
37659
  const state = await cachedLogin({
36958
37660
  apiKey: req.ctx?.token,