braintrust 3.22.0 → 3.23.1

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 -654
  5. package/dev/dist/index.mjs +736 -36
  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 -205
  30. package/dist/browser.mjs +1098 -205
  31. package/dist/{chunk-KMGUTPB7.mjs → chunk-36IPYKMG.mjs} +20 -1
  32. package/dist/{chunk-MWVVR5LR.js → chunk-B6ZQIAK3.js} +1506 -822
  33. package/dist/{chunk-BFGIH2ZJ.js → chunk-CDIKAHDZ.js} +21 -2
  34. package/dist/{chunk-ZG2O3XVF.mjs → chunk-RBXD2KYN.mjs} +719 -35
  35. package/dist/cli.js +737 -37
  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 -205
  39. package/dist/edge-light.mjs +1098 -205
  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 -183
  47. package/dist/instrumentation/index.mjs +788 -183
  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 -205
  53. package/dist/workerd.mjs +1098 -205
  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.1",
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;
@@ -21717,8 +21778,6 @@ function filterSerializableOptions(options) {
21717
21778
  "additionalDirectories",
21718
21779
  "permissionMode",
21719
21780
  "debug",
21720
- "apiKey",
21721
- "apiKeySource",
21722
21781
  "agentName",
21723
21782
  "instructions"
21724
21783
  ];
@@ -32916,6 +32975,446 @@ function isBraintrustHandler(handler) {
32916
32975
  return Reflect.get(handler, "name") === BRAINTRUST_LANGCHAIN_CALLBACK_HANDLER_NAME;
32917
32976
  }
32918
32977
 
32978
+ // src/instrumentation/plugins/langsmith-channels.ts
32979
+ var langSmithChannels = defineChannels("langsmith", {
32980
+ createRun: channel({
32981
+ channelName: "Client.createRun",
32982
+ kind: "async"
32983
+ }),
32984
+ updateRun: channel({
32985
+ channelName: "Client.updateRun",
32986
+ kind: "async"
32987
+ }),
32988
+ batchIngestRuns: channel({
32989
+ channelName: "Client.batchIngestRuns",
32990
+ kind: "async"
32991
+ })
32992
+ });
32993
+
32994
+ // src/instrumentation/plugins/langsmith-plugin.ts
32995
+ var MAX_COMPLETED_RUNS = 1e4;
32996
+ var BLOCKED_METADATA_KEYS = /* @__PURE__ */ new Set([
32997
+ "__proto__",
32998
+ "constructor",
32999
+ "prototype",
33000
+ "usage_metadata"
33001
+ ]);
33002
+ var BLOCKED_METADATA_PREFIXES = ["__pregel_", "langgraph_", "lc_"];
33003
+ var LLM_SETTING_KEYS = [
33004
+ "temperature",
33005
+ "top_p",
33006
+ "max_tokens",
33007
+ "frequency_penalty",
33008
+ "presence_penalty",
33009
+ "stop",
33010
+ "response_format"
33011
+ ];
33012
+ var LangSmithPlugin = class extends BasePlugin {
33013
+ activeRuns = /* @__PURE__ */ new Map();
33014
+ completedRuns = new LRUCache({
33015
+ max: MAX_COMPLETED_RUNS
33016
+ });
33017
+ skipLangChainRuns;
33018
+ constructor(options = {}) {
33019
+ super();
33020
+ this.skipLangChainRuns = options.skipLangChainRuns ?? true;
33021
+ }
33022
+ onEnable() {
33023
+ const createChannel = langSmithChannels.createRun.tracingChannel();
33024
+ const createHandlers = {
33025
+ start: (event) => {
33026
+ this.containLifecycleFailure("createRun", () => {
33027
+ this.processCreate(event.arguments[0]);
33028
+ });
33029
+ }
33030
+ };
33031
+ createChannel.subscribe(createHandlers);
33032
+ this.unsubscribers.push(() => createChannel.unsubscribe(createHandlers));
33033
+ const updateChannel = langSmithChannels.updateRun.tracingChannel();
33034
+ const updateHandlers = {
33035
+ start: (event) => {
33036
+ this.containLifecycleFailure("updateRun", () => {
33037
+ this.processUpdate(event.arguments[0], event.arguments[1]);
33038
+ });
33039
+ }
33040
+ };
33041
+ updateChannel.subscribe(updateHandlers);
33042
+ this.unsubscribers.push(() => updateChannel.unsubscribe(updateHandlers));
33043
+ const batchChannel = langSmithChannels.batchIngestRuns.tracingChannel();
33044
+ const batchHandlers = {
33045
+ start: (event) => {
33046
+ this.containLifecycleFailure("batchIngestRuns", () => {
33047
+ this.processBatch(event.arguments[0]);
33048
+ });
33049
+ }
33050
+ };
33051
+ batchChannel.subscribe(batchHandlers);
33052
+ this.unsubscribers.push(() => batchChannel.unsubscribe(batchHandlers));
33053
+ }
33054
+ onDisable() {
33055
+ this.unsubscribers = unsubscribeAll(this.unsubscribers);
33056
+ for (const { span } of this.activeRuns.values()) {
33057
+ span.end();
33058
+ }
33059
+ this.activeRuns.clear();
33060
+ this.completedRuns.clear();
33061
+ }
33062
+ processBatch(batch) {
33063
+ if (!isRecord2(batch)) {
33064
+ return;
33065
+ }
33066
+ const creates = ownValue(batch, "runCreates");
33067
+ if (Array.isArray(creates)) {
33068
+ const parentFirst = [...creates].sort((left, right) => {
33069
+ const leftId = stringValue2(ownValue(left, "id"));
33070
+ const rightId = stringValue2(ownValue(right, "id"));
33071
+ const leftParent = stringValue2(ownValue(left, "parent_run_id"));
33072
+ const rightParent = stringValue2(ownValue(right, "parent_run_id"));
33073
+ if (leftParent && leftParent === rightId) {
33074
+ return 1;
33075
+ }
33076
+ if (rightParent && rightParent === leftId) {
33077
+ return -1;
33078
+ }
33079
+ return dottedOrderDepth(left) - dottedOrderDepth(right);
33080
+ });
33081
+ for (const run2 of parentFirst) {
33082
+ this.containLifecycleFailure("batchIngestRuns create", () => {
33083
+ this.processCreate(run2);
33084
+ });
33085
+ }
33086
+ }
33087
+ const updates = ownValue(batch, "runUpdates");
33088
+ if (Array.isArray(updates)) {
33089
+ for (const run2 of updates) {
33090
+ this.containLifecycleFailure("batchIngestRuns update", () => {
33091
+ this.processUpdate(stringValue2(ownValue(run2, "id")) ?? "", run2);
33092
+ });
33093
+ }
33094
+ }
33095
+ }
33096
+ processCreate(run2) {
33097
+ const id = stringValue2(ownValue(run2, "id"));
33098
+ if (!id || this.completedRuns.get(id)) {
33099
+ return;
33100
+ }
33101
+ if (this.shouldSkipLangChainRun(run2)) {
33102
+ this.completedRuns.set(id, true);
33103
+ return;
33104
+ }
33105
+ const active = this.activeRuns.get(id);
33106
+ if (active) {
33107
+ const previous = active.run;
33108
+ active.run = mergeRuns(previous, run2);
33109
+ this.logRun(
33110
+ active.span,
33111
+ active.run,
33112
+ previous,
33113
+ timestampSeconds(ownValue(active.run, "end_time")) !== void 0 || errorMessage(ownValue(active.run, "error")) !== void 0
33114
+ );
33115
+ this.endIfComplete(id, active, active.run);
33116
+ return;
33117
+ }
33118
+ const span = this.startRunSpan(id, run2);
33119
+ const activeRun = { run: run2, span };
33120
+ this.activeRuns.set(id, activeRun);
33121
+ this.logRun(
33122
+ span,
33123
+ run2,
33124
+ void 0,
33125
+ timestampSeconds(ownValue(run2, "end_time")) !== void 0 || errorMessage(ownValue(run2, "error")) !== void 0
33126
+ );
33127
+ this.endIfComplete(id, activeRun, run2);
33128
+ }
33129
+ processUpdate(explicitId, run2) {
33130
+ const id = stringValue2(explicitId) ?? stringValue2(ownValue(run2, "id"));
33131
+ if (!id || this.completedRuns.get(id)) {
33132
+ return;
33133
+ }
33134
+ if (this.shouldSkipLangChainRun(run2)) {
33135
+ const active2 = this.activeRuns.get(id);
33136
+ active2?.span.end();
33137
+ this.activeRuns.delete(id);
33138
+ this.completedRuns.set(id, true);
33139
+ return;
33140
+ }
33141
+ let active = this.activeRuns.get(id);
33142
+ if (!active) {
33143
+ const span = this.startRunSpan(id, run2);
33144
+ active = { run: run2, span };
33145
+ this.activeRuns.set(id, active);
33146
+ }
33147
+ const previous = active.run;
33148
+ active.run = mergeRuns(previous, run2);
33149
+ this.logRun(active.span, active.run, previous, true);
33150
+ this.endIfComplete(id, active, active.run);
33151
+ }
33152
+ startRunSpan(id, run2) {
33153
+ const traceId = stringValue2(ownValue(run2, "trace_id")) ?? id;
33154
+ const parentId = stringValue2(ownValue(run2, "parent_run_id")) ?? stringValue2(ownValue(ownValue(run2, "parent_run"), "id"));
33155
+ const startTime = timestampSeconds(ownValue(run2, "start_time"));
33156
+ return startSpan({
33157
+ name: stringValue2(ownValue(run2, "name")) ?? "LangSmith run",
33158
+ spanId: id,
33159
+ parentSpanIds: {
33160
+ parentSpanIds: parentId ? [parentId] : [],
33161
+ rootSpanId: traceId
33162
+ },
33163
+ spanAttributes: {
33164
+ type: mapRunType(ownValue(run2, "run_type"))
33165
+ },
33166
+ ...startTime === void 0 ? {} : { startTime },
33167
+ event: { id }
33168
+ });
33169
+ }
33170
+ logRun(span, run2, previous, includeOutput) {
33171
+ const inputs = preferOwnValue(run2, previous, "inputs");
33172
+ const outputs = preferOwnValue(run2, previous, "outputs");
33173
+ const error2 = errorMessage(preferOwnValue(run2, previous, "error"));
33174
+ const metadata = extractMetadata(run2, previous);
33175
+ const tags = extractTags(preferOwnValue(run2, previous, "tags"));
33176
+ const metrics = extractMetrics(run2, previous);
33177
+ span.log({
33178
+ ...inputs === void 0 ? {} : { input: sanitizeLoggedValue(inputs) },
33179
+ ...includeOutput && outputs !== void 0 ? { output: sanitizeLoggedValue(outputs) } : {},
33180
+ ...error2 === void 0 ? {} : { error: error2 },
33181
+ ...metadata === void 0 ? {} : { metadata },
33182
+ ...tags === void 0 ? {} : { tags },
33183
+ ...Object.keys(metrics).length === 0 ? {} : { metrics }
33184
+ });
33185
+ }
33186
+ endIfComplete(id, active, run2) {
33187
+ const endTime = timestampSeconds(ownValue(run2, "end_time"));
33188
+ if (endTime === void 0 && errorMessage(ownValue(run2, "error")) === void 0) {
33189
+ return;
33190
+ }
33191
+ active.span.end(endTime === void 0 ? void 0 : { endTime });
33192
+ this.activeRuns.delete(id);
33193
+ this.completedRuns.set(id, true);
33194
+ }
33195
+ shouldSkipLangChainRun(run2) {
33196
+ if (!this.skipLangChainRuns) {
33197
+ return false;
33198
+ }
33199
+ const serialized = ownValue(run2, "serialized");
33200
+ return isRecord2(serialized) && ownValue(serialized, "lc") === 1;
33201
+ }
33202
+ containLifecycleFailure(operation, fn) {
33203
+ try {
33204
+ fn();
33205
+ } catch (error2) {
33206
+ debugLogger.error(
33207
+ `Failed to process LangSmith ${operation} instrumentation:`,
33208
+ error2
33209
+ );
33210
+ }
33211
+ }
33212
+ };
33213
+ function ownValue(value, key) {
33214
+ if (!isRecord2(value)) {
33215
+ return void 0;
33216
+ }
33217
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
33218
+ return descriptor && "value" in descriptor ? descriptor.value : void 0;
33219
+ }
33220
+ function preferOwnValue(current, previous, key) {
33221
+ const currentDescriptor = isRecord2(current) ? Object.getOwnPropertyDescriptor(current, key) : void 0;
33222
+ if (currentDescriptor && "value" in currentDescriptor) {
33223
+ return currentDescriptor.value;
33224
+ }
33225
+ return ownValue(previous, key);
33226
+ }
33227
+ function mergeRuns(previous, current) {
33228
+ const entries = /* @__PURE__ */ new Map();
33229
+ for (const value of [previous, current]) {
33230
+ if (!isRecord2(value)) {
33231
+ continue;
33232
+ }
33233
+ for (const [key, descriptor] of Object.entries(
33234
+ Object.getOwnPropertyDescriptors(value)
33235
+ )) {
33236
+ if (descriptor.enumerable && "value" in descriptor) {
33237
+ entries.set(key, descriptor.value);
33238
+ }
33239
+ }
33240
+ }
33241
+ return Object.fromEntries(entries);
33242
+ }
33243
+ function isRecord2(value) {
33244
+ return typeof value === "object" && value !== null && !Array.isArray(value);
33245
+ }
33246
+ function stringValue2(value) {
33247
+ return typeof value === "string" && value.length > 0 ? value : void 0;
33248
+ }
33249
+ function timestampSeconds(value) {
33250
+ if (value instanceof Date) {
33251
+ const timestamp = value.getTime();
33252
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
33253
+ }
33254
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
33255
+ return value > 1e10 ? value / 1e3 : value;
33256
+ }
33257
+ if (typeof value === "string") {
33258
+ const timestamp = Date.parse(value);
33259
+ return Number.isFinite(timestamp) ? timestamp / 1e3 : void 0;
33260
+ }
33261
+ return void 0;
33262
+ }
33263
+ function dottedOrderDepth(run2) {
33264
+ const dottedOrder = stringValue2(ownValue(run2, "dotted_order"));
33265
+ return dottedOrder ? dottedOrder.split(".").length : Number.MAX_SAFE_INTEGER;
33266
+ }
33267
+ function mapRunType(runType) {
33268
+ switch (runType) {
33269
+ case "llm":
33270
+ case "embedding":
33271
+ return "llm" /* LLM */;
33272
+ case "tool":
33273
+ case "retriever":
33274
+ return "tool" /* TOOL */;
33275
+ default:
33276
+ return "task" /* TASK */;
33277
+ }
33278
+ }
33279
+ function extractMetadata(run2, previous) {
33280
+ const extra = preferOwnValue(run2, previous, "extra");
33281
+ const rawMetadata = ownValue(extra, "metadata");
33282
+ if (!isRecord2(rawMetadata)) {
33283
+ return void 0;
33284
+ }
33285
+ const metadata = {};
33286
+ for (const [key, descriptor] of Object.entries(
33287
+ Object.getOwnPropertyDescriptors(rawMetadata)
33288
+ )) {
33289
+ 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}`)) {
33290
+ continue;
33291
+ }
33292
+ const normalizedKey = key === "ls_provider" ? "provider" : key === "ls_model_name" ? "model" : key.startsWith("ls_") ? key.slice(3) : key;
33293
+ const sanitized = sanitizeLoggedValue(descriptor.value);
33294
+ if (sanitized !== void 0) {
33295
+ metadata[normalizedKey] = sanitized;
33296
+ }
33297
+ }
33298
+ return Object.keys(metadata).length === 0 ? void 0 : metadata;
33299
+ }
33300
+ function extractTags(value) {
33301
+ if (!Array.isArray(value)) {
33302
+ return void 0;
33303
+ }
33304
+ const tags = value.filter(
33305
+ (tag) => typeof tag === "string" && tag.length > 0
33306
+ );
33307
+ return tags.length > 0 ? tags : void 0;
33308
+ }
33309
+ function extractMetrics(run2, previous) {
33310
+ const outputs = preferOwnValue(run2, previous, "outputs");
33311
+ const extra = preferOwnValue(run2, previous, "extra");
33312
+ const metadata = ownValue(extra, "metadata");
33313
+ const usage = ownValue(outputs, "usage_metadata") ?? ownValue(metadata, "usage_metadata");
33314
+ const metrics = {};
33315
+ assignFirstMetric(metrics, "prompt_tokens", usage, [
33316
+ "input_tokens",
33317
+ "prompt_tokens"
33318
+ ]);
33319
+ assignFirstMetric(metrics, "completion_tokens", usage, [
33320
+ "output_tokens",
33321
+ "completion_tokens"
33322
+ ]);
33323
+ assignFirstMetric(metrics, "tokens", usage, ["total_tokens", "tokens"]);
33324
+ const inputTokenDetails = ownValue(usage, "input_token_details");
33325
+ assignFirstMetric(metrics, "prompt_cached_tokens", inputTokenDetails, [
33326
+ "cache_read",
33327
+ "cached_tokens"
33328
+ ]);
33329
+ if (metrics.prompt_cached_tokens === void 0) {
33330
+ assignFirstMetric(metrics, "prompt_cached_tokens", usage, [
33331
+ "cache_read_input_tokens",
33332
+ "prompt_cached_tokens"
33333
+ ]);
33334
+ }
33335
+ assignFirstMetric(
33336
+ metrics,
33337
+ "prompt_cache_creation_tokens",
33338
+ inputTokenDetails,
33339
+ ["cache_creation"]
33340
+ );
33341
+ if (metrics.prompt_cache_creation_tokens === void 0) {
33342
+ assignFirstMetric(metrics, "prompt_cache_creation_tokens", usage, [
33343
+ "cache_creation_input_tokens",
33344
+ "prompt_cache_creation_tokens"
33345
+ ]);
33346
+ }
33347
+ if (metrics.tokens === void 0 && (metrics.prompt_tokens !== void 0 || metrics.completion_tokens !== void 0)) {
33348
+ metrics.tokens = (metrics.prompt_tokens ?? 0) + (metrics.completion_tokens ?? 0);
33349
+ }
33350
+ const startTime = timestampSeconds(
33351
+ preferOwnValue(run2, previous, "start_time")
33352
+ );
33353
+ const events = preferOwnValue(run2, previous, "events");
33354
+ if (startTime !== void 0 && Array.isArray(events)) {
33355
+ for (const event of events) {
33356
+ if (ownValue(event, "name") !== "new_token") {
33357
+ continue;
33358
+ }
33359
+ const eventTime2 = timestampSeconds(ownValue(event, "time"));
33360
+ if (eventTime2 !== void 0 && eventTime2 >= startTime) {
33361
+ metrics.time_to_first_token = eventTime2 - startTime;
33362
+ }
33363
+ break;
33364
+ }
33365
+ }
33366
+ return metrics;
33367
+ }
33368
+ function assignFirstMetric(metrics, target, source, keys) {
33369
+ for (const key of keys) {
33370
+ const value = ownValue(source, key);
33371
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
33372
+ metrics[target] = value;
33373
+ return;
33374
+ }
33375
+ }
33376
+ }
33377
+ function errorMessage(value) {
33378
+ if (typeof value === "string") {
33379
+ return value;
33380
+ }
33381
+ if (value instanceof Error) {
33382
+ return value.message;
33383
+ }
33384
+ return void 0;
33385
+ }
33386
+ function sanitizeLoggedValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
33387
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
33388
+ return value;
33389
+ }
33390
+ if (typeof value === "number") {
33391
+ return Number.isFinite(value) ? value : void 0;
33392
+ }
33393
+ if (value instanceof Date) {
33394
+ return Number.isFinite(value.getTime()) ? value.toISOString() : void 0;
33395
+ }
33396
+ if (typeof value !== "object" || depth >= 20) {
33397
+ return void 0;
33398
+ }
33399
+ if (seen.has(value)) {
33400
+ return "[Circular]";
33401
+ }
33402
+ seen.add(value);
33403
+ if (Array.isArray(value)) {
33404
+ return value.map((item) => sanitizeLoggedValue(item, seen, depth + 1));
33405
+ }
33406
+ const entries = [];
33407
+ for (const [key, descriptor] of Object.entries(
33408
+ Object.getOwnPropertyDescriptors(value)
33409
+ )) {
33410
+ if (!descriptor.enumerable || !("value" in descriptor) || BLOCKED_METADATA_KEYS.has(key)) {
33411
+ continue;
33412
+ }
33413
+ entries.push([key, sanitizeLoggedValue(descriptor.value, seen, depth + 1)]);
33414
+ }
33415
+ return Object.fromEntries(entries);
33416
+ }
33417
+
32919
33418
  // src/instrumentation/plugins/pi-coding-agent-channels.ts
32920
33419
  var piCodingAgentChannels = defineChannels(
32921
33420
  "@earendil-works/pi-coding-agent",
@@ -33295,11 +33794,11 @@ function patchAssistantMessageStream(stream, promptState, llmState) {
33295
33794
  function recordPiAssistantMessageEvent(promptState, llmState, event) {
33296
33795
  recordFirstTokenMetric(llmState, event);
33297
33796
  const message = "message" in event ? event.message : void 0;
33298
- const errorMessage = "error" in event ? event.error : void 0;
33797
+ const errorMessage2 = "error" in event ? event.error : void 0;
33299
33798
  if (event.type === "done" && isPiAssistantMessage(message)) {
33300
33799
  finishPiLlmSpan(promptState, llmState, message);
33301
- } else if (event.type === "error" && isPiAssistantMessage(errorMessage)) {
33302
- finishPiLlmSpan(promptState, llmState, errorMessage);
33800
+ } else if (event.type === "error" && isPiAssistantMessage(errorMessage2)) {
33801
+ finishPiLlmSpan(promptState, llmState, errorMessage2);
33303
33802
  }
33304
33803
  }
33305
33804
  function recordFirstTokenMetric(state, event) {
@@ -33817,6 +34316,7 @@ var strandsAgentSDKChannels = defineChannels("@strands-agents/sdk", {
33817
34316
  });
33818
34317
 
33819
34318
  // src/instrumentation/plugins/strands-agent-sdk-plugin.ts
34319
+ var MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES = 32;
33820
34320
  var StrandsAgentSDKPlugin = class extends BasePlugin {
33821
34321
  activeChildParents = /* @__PURE__ */ new WeakMap();
33822
34322
  onEnable() {
@@ -33961,11 +34461,16 @@ function startAgentStream(event, activeChildParents) {
33961
34461
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
33962
34462
  };
33963
34463
  const parentSpan = agent ? getOnlyChildParent(activeChildParents, agent) : void 0;
34464
+ const attachmentCache = createStrandsAttachmentCache();
34465
+ const input = processStrandsInputAttachments(
34466
+ event.arguments[0],
34467
+ attachmentCache
34468
+ );
33964
34469
  const span = parentSpan ? withCurrent(
33965
34470
  parentSpan,
33966
34471
  () => startSpan({
33967
34472
  event: {
33968
- input: event.arguments[0],
34473
+ input,
33969
34474
  metadata
33970
34475
  },
33971
34476
  name: formatAgentSpanName(agent),
@@ -33973,7 +34478,7 @@ function startAgentStream(event, activeChildParents) {
33973
34478
  })
33974
34479
  ) : startSpan({
33975
34480
  event: {
33976
- input: event.arguments[0],
34481
+ input,
33977
34482
  metadata
33978
34483
  },
33979
34484
  name: formatAgentSpanName(agent),
@@ -33981,6 +34486,7 @@ function startAgentStream(event, activeChildParents) {
33981
34486
  });
33982
34487
  return {
33983
34488
  activeTools: /* @__PURE__ */ new Map(),
34489
+ attachmentCache,
33984
34490
  finalized: false,
33985
34491
  metadata,
33986
34492
  span,
@@ -33996,11 +34502,12 @@ function startMultiAgentStream(event, operation, activeChildParents) {
33996
34502
  ...event.moduleVersion ? { "strands_agent_sdk.version": event.moduleVersion } : {}
33997
34503
  };
33998
34504
  const parentSpan = orchestrator ? getOnlyChildParent(activeChildParents, orchestrator) : void 0;
34505
+ const input = processStrandsInputAttachments(event.arguments[0]);
33999
34506
  const span = parentSpan ? withCurrent(
34000
34507
  parentSpan,
34001
34508
  () => startSpan({
34002
34509
  event: {
34003
- input: event.arguments[0],
34510
+ input,
34004
34511
  metadata
34005
34512
  },
34006
34513
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -34008,7 +34515,7 @@ function startMultiAgentStream(event, operation, activeChildParents) {
34008
34515
  })
34009
34516
  ) : startSpan({
34010
34517
  event: {
34011
- input: event.arguments[0],
34518
+ input,
34012
34519
  metadata
34013
34520
  },
34014
34521
  name: operation === "Graph.stream" ? "Strands Graph" : "Strands Swarm",
@@ -34117,7 +34624,10 @@ function startModelSpan(state, event) {
34117
34624
  state.span,
34118
34625
  () => startSpan({
34119
34626
  event: {
34120
- input: Array.isArray(event.agent?.messages) ? event.agent.messages : void 0,
34627
+ input: Array.isArray(event.agent?.messages) ? processStrandsInputAttachments(
34628
+ event.agent.messages,
34629
+ state.attachmentCache
34630
+ ) : void 0,
34121
34631
  metadata
34122
34632
  },
34123
34633
  name: formatModelSpanName(model),
@@ -34331,6 +34841,7 @@ function finalizeAgentStream(state, error2, output) {
34331
34841
  ...output !== void 0 ? { output } : {}
34332
34842
  });
34333
34843
  state.span.end();
34844
+ state.attachmentCache.strings.clear();
34334
34845
  }
34335
34846
  function finalizeMultiAgentStream(state, activeChildParents, error2, output) {
34336
34847
  if (state.finalized) {
@@ -34477,6 +34988,166 @@ function extractNodeResultOutput(result) {
34477
34988
  }
34478
34989
  return result;
34479
34990
  }
34991
+ var STRANDS_MEDIA_TYPES = {
34992
+ png: "image/png",
34993
+ jpg: "image/jpeg",
34994
+ jpeg: "image/jpeg",
34995
+ gif: "image/gif",
34996
+ webp: "image/webp",
34997
+ mkv: "video/x-matroska",
34998
+ mov: "video/quicktime",
34999
+ mp4: "video/mp4",
35000
+ webm: "video/webm",
35001
+ flv: "video/x-flv",
35002
+ mpeg: "video/mpeg",
35003
+ mpg: "video/mpeg",
35004
+ wmv: "video/x-ms-wmv",
35005
+ "3gp": "video/3gpp",
35006
+ pdf: "application/pdf",
35007
+ csv: "text/csv",
35008
+ doc: "application/msword",
35009
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
35010
+ xls: "application/vnd.ms-excel",
35011
+ xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
35012
+ html: "text/html",
35013
+ txt: "text/plain",
35014
+ md: "text/markdown",
35015
+ json: "application/json",
35016
+ xml: "application/xml"
35017
+ };
35018
+ function createStrandsAttachmentCache() {
35019
+ return {
35020
+ objects: /* @__PURE__ */ new WeakMap(),
35021
+ strings: new LRUCache({
35022
+ max: MAX_STRANDS_STRING_ATTACHMENT_CACHE_ENTRIES
35023
+ })
35024
+ };
35025
+ }
35026
+ function processStrandsInputAttachments(input, cache = createStrandsAttachmentCache()) {
35027
+ try {
35028
+ return processStrandsInputNode(input, cache);
35029
+ } catch (error2) {
35030
+ logInstrumentationError5("Strands Agent SDK input attachments", error2);
35031
+ return input;
35032
+ }
35033
+ }
35034
+ function processStrandsInputNode(value, cache) {
35035
+ if (value instanceof BaseAttachment) {
35036
+ return value;
35037
+ }
35038
+ if (Array.isArray(value)) {
35039
+ return value.map((child) => processStrandsInputNode(child, cache));
35040
+ }
35041
+ if (!isObject(value)) {
35042
+ return value;
35043
+ }
35044
+ const directMedia = processDirectStrandsMediaBlock(value, cache);
35045
+ if (directMedia !== void 0) {
35046
+ return directMedia;
35047
+ }
35048
+ const wrappedMedia = processWrappedStrandsMediaBlock(value, cache);
35049
+ if (wrappedMedia !== void 0) {
35050
+ return wrappedMedia;
35051
+ }
35052
+ if (value.type === "message" && Array.isArray(value.content)) {
35053
+ return {
35054
+ role: value.role,
35055
+ content: value.content.map(
35056
+ (child) => processStrandsInputNode(child, cache)
35057
+ ),
35058
+ ...value.metadata !== void 0 ? { metadata: value.metadata } : {}
35059
+ };
35060
+ }
35061
+ if (typeof value.toJSON === "function") {
35062
+ return processStrandsInputNode(value.toJSON(), cache);
35063
+ }
35064
+ return Object.fromEntries(
35065
+ Object.entries(value).map(([key, child]) => [
35066
+ key,
35067
+ processStrandsInputNode(child, cache)
35068
+ ])
35069
+ );
35070
+ }
35071
+ function processDirectStrandsMediaBlock(block, cache) {
35072
+ if (!isStrandsMediaBlock(block)) {
35073
+ return void 0;
35074
+ }
35075
+ const mediaKey = block.type === "imageBlock" ? "image" : block.type === "videoBlock" ? "video" : "document";
35076
+ return createStrandsMediaAttachment(mediaKey, block, cache);
35077
+ }
35078
+ function isStrandsMediaBlock(block) {
35079
+ return (block.type === "imageBlock" || block.type === "videoBlock" || block.type === "documentBlock") && typeof block.format === "string" && isObject(block.source);
35080
+ }
35081
+ function processWrappedStrandsMediaBlock(block, cache) {
35082
+ for (const mediaKey of ["image", "video", "document"]) {
35083
+ const media = block[mediaKey];
35084
+ if (!Object.hasOwn(block, mediaKey) || !isObject(media)) {
35085
+ continue;
35086
+ }
35087
+ const processed = createStrandsMediaAttachment(mediaKey, media, cache);
35088
+ if (processed !== void 0) {
35089
+ return processed;
35090
+ }
35091
+ }
35092
+ return void 0;
35093
+ }
35094
+ function createStrandsMediaAttachment(mediaKey, media, cache) {
35095
+ const format = media.format;
35096
+ const source = media.source;
35097
+ if (typeof format !== "string" || !isObject(source) || !Object.hasOwn(source, "bytes")) {
35098
+ return void 0;
35099
+ }
35100
+ const contentType = STRANDS_MEDIA_TYPES[format.toLowerCase()];
35101
+ if (!contentType) {
35102
+ return void 0;
35103
+ }
35104
+ const filename = mediaKey === "document" && typeof media.name === "string" && media.name.length > 0 ? media.name : `${mediaKey}.${format.toLowerCase()}`;
35105
+ const attachment = getOrCreateStrandsAttachment(
35106
+ source.bytes,
35107
+ filename,
35108
+ contentType,
35109
+ cache
35110
+ );
35111
+ if (!attachment) {
35112
+ return void 0;
35113
+ }
35114
+ const { type: _type, ...serializedMedia } = media;
35115
+ const { type: _sourceType, ...serializedSource } = source;
35116
+ return {
35117
+ [mediaKey]: {
35118
+ ...serializedMedia,
35119
+ source: {
35120
+ ...serializedSource,
35121
+ bytes: attachment
35122
+ }
35123
+ }
35124
+ };
35125
+ }
35126
+ function getOrCreateStrandsAttachment(data, filename, contentType, cache) {
35127
+ const key = `${contentType}\0${filename}`;
35128
+ const attachments = typeof data === "string" ? cache.strings.get(data) : isObject(data) ? cache.objects.get(data) : void 0;
35129
+ const cached = attachments?.get(key);
35130
+ if (cached) {
35131
+ return cached;
35132
+ }
35133
+ const blob = convertDataToBlob(data, contentType);
35134
+ if (!blob) {
35135
+ return void 0;
35136
+ }
35137
+ const attachment = new Attachment({
35138
+ data: blob,
35139
+ filename,
35140
+ contentType
35141
+ });
35142
+ const updatedAttachments = attachments ?? /* @__PURE__ */ new Map();
35143
+ updatedAttachments.set(key, attachment);
35144
+ if (typeof data === "string") {
35145
+ cache.strings.set(data, updatedAttachments);
35146
+ } else if (isObject(data)) {
35147
+ cache.objects.set(data, updatedAttachments);
35148
+ }
35149
+ return attachment;
35150
+ }
34480
35151
  function normalizeContentBlocks(blocks) {
34481
35152
  const text = blocks.map((block) => typeof block.text === "string" ? block.text : void 0).filter((part) => Boolean(part)).join("");
34482
35153
  return text.length > 0 ? text : blocks;
@@ -34606,6 +35277,7 @@ var BraintrustPlugin = class extends BasePlugin {
34606
35277
  gitHubCopilotPlugin = null;
34607
35278
  fluePlugin = null;
34608
35279
  langChainPlugin = null;
35280
+ langSmithPlugin = null;
34609
35281
  piCodingAgentPlugin = null;
34610
35282
  strandsAgentSDKPlugin = null;
34611
35283
  constructor(config3 = {}) {
@@ -34702,6 +35374,12 @@ var BraintrustPlugin = class extends BasePlugin {
34702
35374
  this.langChainPlugin = new LangChainPlugin();
34703
35375
  this.langChainPlugin.enable();
34704
35376
  }
35377
+ if (integrations.langsmith !== false) {
35378
+ this.langSmithPlugin = new LangSmithPlugin({
35379
+ skipLangChainRuns: integrations.langchain !== false
35380
+ });
35381
+ this.langSmithPlugin.enable();
35382
+ }
34705
35383
  }
34706
35384
  onDisable() {
34707
35385
  if (this.openaiPlugin) {
@@ -34792,6 +35470,10 @@ var BraintrustPlugin = class extends BasePlugin {
34792
35470
  this.langChainPlugin.disable();
34793
35471
  this.langChainPlugin = null;
34794
35472
  }
35473
+ if (this.langSmithPlugin) {
35474
+ this.langSmithPlugin.disable();
35475
+ this.langSmithPlugin = null;
35476
+ }
34795
35477
  }
34796
35478
  };
34797
35479
 
@@ -34856,7 +35538,8 @@ var envIntegrationAliases = {
34856
35538
  langchain: "langchain",
34857
35539
  "langchain-js": "langchain",
34858
35540
  "@langchain": "langchain",
34859
- langgraph: "langgraph"
35541
+ langgraph: "langgraph",
35542
+ langsmith: "langsmith"
34860
35543
  };
34861
35544
  function getDefaultInstrumentationIntegrations() {
34862
35545
  return {
@@ -34887,6 +35570,7 @@ function getDefaultInstrumentationIntegrations() {
34887
35570
  gitHubCopilot: true,
34888
35571
  langchain: true,
34889
35572
  langgraph: true,
35573
+ langsmith: true,
34890
35574
  piCodingAgent: true,
34891
35575
  strandsAgentSDK: true
34892
35576
  };
@@ -35127,6 +35811,15 @@ function modelMetrics(attributes) {
35127
35811
  }
35128
35812
  return Object.keys(out).length > 0 ? out : void 0;
35129
35813
  }
35814
+ function timeToFirstTokenSeconds(attributes, spanStartSeconds) {
35815
+ if (!isObject(attributes)) return void 0;
35816
+ if (spanStartSeconds === void 0) return void 0;
35817
+ const raw = attributes.completionStartTime;
35818
+ const completionStart = raw instanceof Date || typeof raw === "string" || typeof raw === "number" ? epochSeconds(raw) : void 0;
35819
+ if (completionStart === void 0) return void 0;
35820
+ const ttft = completionStart - spanStartSeconds;
35821
+ return Number.isFinite(ttft) && ttft >= 0 ? ttft : void 0;
35822
+ }
35130
35823
  function buildMetadata(exported) {
35131
35824
  const out = {};
35132
35825
  if (exported.entityId !== void 0) out.entity_id = exported.entityId;
@@ -35251,8 +35944,15 @@ var BraintrustObservabilityExporter = class {
35251
35944
  event.metadata = metadata;
35252
35945
  }
35253
35946
  const metrics = modelMetrics(exported.attributes);
35254
- if (metrics) {
35255
- event.metrics = metrics;
35947
+ const ttft = timeToFirstTokenSeconds(
35948
+ exported.attributes,
35949
+ epochSeconds(exported.startTime)
35950
+ );
35951
+ if (metrics || ttft !== void 0) {
35952
+ event.metrics = {
35953
+ ...metrics ?? {},
35954
+ ...ttft !== void 0 ? { time_to_first_token: ttft } : {}
35955
+ };
35256
35956
  }
35257
35957
  if (Object.keys(event).length > 0) {
35258
35958
  record.span.log(event);
@@ -36951,8 +37651,8 @@ function makeCheckAuthorized(allowedOrgName) {
36951
37651
  return next((0, import_http_errors.default)(400, "Missing x-bt-org-name header"));
36952
37652
  }
36953
37653
  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));
37654
+ const errorMessage2 = `Org '${orgName}' is not allowed. Only org '${allowedOrgName}' is allowed.`;
37655
+ return next((0, import_http_errors.default)(403, errorMessage2));
36956
37656
  }
36957
37657
  const state = await cachedLogin({
36958
37658
  apiKey: req.ctx?.token,