braintrust 0.4.1 → 0.4.3

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.
package/dist/browser.js CHANGED
@@ -5124,7 +5124,6 @@ var init_context3 = __esm({
5124
5124
  "src/otel/context.ts"() {
5125
5125
  "use strict";
5126
5126
  init_logger();
5127
- init_util();
5128
5127
  OTEL_NOT_INSTALLED_MESSAGE = "OpenTelemetry packages are not installed. Install them with: npm install @opentelemetry/api @opentelemetry/sdk-trace-base";
5129
5128
  otelTrace = null;
5130
5129
  otelContext = null;
@@ -5187,6 +5186,10 @@ var init_context3 = __esm({
5187
5186
  const currentContext = otelContext.active();
5188
5187
  let newContext = otelTrace.setSpan(currentContext, wrappedContext);
5189
5188
  newContext = newContext.setValue("braintrust_span", span);
5189
+ const parentValue = span._getOtelParent();
5190
+ if (parentValue) {
5191
+ newContext = newContext.setValue("braintrust.parent", parentValue);
5192
+ }
5190
5193
  return otelContext.with(newContext, callback);
5191
5194
  }
5192
5195
  } catch (error) {
@@ -5202,34 +5205,6 @@ var init_context3 = __esm({
5202
5205
  }
5203
5206
  return void 0;
5204
5207
  }
5205
- _getOtelParent(span) {
5206
- if (!span.parentObjectType || !span.parentObjectId) {
5207
- return void 0;
5208
- }
5209
- try {
5210
- const parentType = span.parentObjectType;
5211
- const parentId = span.parentObjectId;
5212
- if (parentType === 2 /* PROJECT_LOGS */) {
5213
- const id = typeof parentId === "object" && parentId !== null && "get" in parentId ? (
5214
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Type guard ensures object has get method
5215
- parentId.get()
5216
- ) : parentId;
5217
- if (typeof id === "string") {
5218
- return `project_id:${id}`;
5219
- }
5220
- } else if (parentType === 1 /* EXPERIMENT */) {
5221
- const id = typeof parentId === "object" && parentId !== null && "get" in parentId ? (
5222
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Type guard ensures object has get method
5223
- parentId.get()
5224
- ) : parentId;
5225
- if (typeof id === "string") {
5226
- return `experiment_id:${id}`;
5227
- }
5228
- }
5229
- } catch (e9) {
5230
- }
5231
- return void 0;
5232
- }
5233
5208
  };
5234
5209
  }
5235
5210
  });
@@ -5265,7 +5240,7 @@ function getContextManager() {
5265
5240
  try {
5266
5241
  const { OtelContextManager: OtelContextManager2 } = (init_context3(), __toCommonJS(context_exports));
5267
5242
  return new OtelContextManager2();
5268
- } catch (e10) {
5243
+ } catch (e9) {
5269
5244
  console.warn(
5270
5245
  "OTEL not available, falling back to Braintrust-only context manager"
5271
5246
  );
@@ -6931,7 +6906,7 @@ function resetIdGenStateForTests() {
6931
6906
  state.resetIdGenState();
6932
6907
  }
6933
6908
  }
6934
- var BRAINTRUST_ATTACHMENT, EXTERNAL_ATTACHMENT, BRAINTRUST_PARAMS, REDACTION_FIELDS, MaskingError, ContextManager, BraintrustContextManager, NoopSpan, NOOP_SPAN, NOOP_SPAN_PERMALINK, loginSchema, stateNonce, BraintrustState, _globalState, _internalGetGlobalState, FailedHTTPResponse, HTTPConnection, BaseAttachment, Attachment, ExternalAttachment, attachmentMetadataSchema, ReadonlyAttachment, ERR_PERMALINK, Logger, TestBackgroundLogger, BACKGROUND_LOGGER_BASE_SLEEP_TIME_S, HTTPBackgroundLogger, traceable, INTERNAL_BTQL_LIMIT, MAX_BTQL_ITERATIONS, ObjectFetcher, Experiment2, ReadonlyExperiment, executionCounter, SpanImpl, Dataset2, Prompt2, TEST_API_KEY, _exportsForTestingOnly;
6909
+ var BRAINTRUST_ATTACHMENT, EXTERNAL_ATTACHMENT, BRAINTRUST_PARAMS, REDACTION_FIELDS, MaskingError, ContextManager, BraintrustContextManager, NoopSpan, NOOP_SPAN, NOOP_SPAN_PERMALINK, loginSchema, stateNonce, BraintrustState, _globalState, _internalGetGlobalState, FailedHTTPResponse, HTTPConnection, BaseAttachment, Attachment, ExternalAttachment, attachmentMetadataSchema, ReadonlyAttachment, JSONAttachment, ERR_PERMALINK, Logger, TestBackgroundLogger, BACKGROUND_LOGGER_BASE_SLEEP_TIME_S, HTTPBackgroundLogger, traceable, INTERNAL_BTQL_LIMIT, MAX_BTQL_ITERATIONS, ObjectFetcher, Experiment2, ReadonlyExperiment, executionCounter, SpanImpl, Dataset2, Prompt2, TEST_API_KEY, _exportsForTestingOnly;
6935
6910
  var init_logger = __esm({
6936
6911
  "src/logger.ts"() {
6937
6912
  "use strict";
@@ -7040,6 +7015,9 @@ var init_logger = __esm({
7040
7015
  state() {
7041
7016
  return _internalGetGlobalState();
7042
7017
  }
7018
+ _getOtelParent() {
7019
+ return void 0;
7020
+ }
7043
7021
  // Custom inspect for Node.js console.log
7044
7022
  [Symbol.for("nodejs.util.inspect.custom")]() {
7045
7023
  return `NoopSpan {
@@ -7801,6 +7779,44 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
7801
7779
  return new LazyValue(download);
7802
7780
  }
7803
7781
  };
7782
+ JSONAttachment = exports.JSONAttachment = class extends Attachment {
7783
+ /**
7784
+ * Construct a JSONAttachment from a JSON-serializable object.
7785
+ *
7786
+ * @param data The JSON object to attach. Must be JSON-serializable.
7787
+ * @param options Additional options:
7788
+ * - `filename`: The filename for the attachment (defaults to "data.json")
7789
+ * - `pretty`: Whether to pretty-print the JSON (defaults to false)
7790
+ * - `state`: (Optional) For internal use.
7791
+ *
7792
+ * @example
7793
+ * ```typescript
7794
+ * const largeTranscript = [
7795
+ * { role: "user", content: "..." },
7796
+ * { role: "assistant", content: "..." },
7797
+ * // ... many more messages
7798
+ * ];
7799
+ *
7800
+ * logger.log({
7801
+ * input: {
7802
+ * type: "chat",
7803
+ * transcript: new JSONAttachment(largeTranscript, { filename: "transcript.json" })
7804
+ * }
7805
+ * });
7806
+ * ```
7807
+ */
7808
+ constructor(data, options) {
7809
+ const { filename = "data.json", pretty = false, state } = _nullishCoalesce(options, () => ( {}));
7810
+ const jsonString = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
7811
+ const blob = new Blob([jsonString], { type: "application/json" });
7812
+ super({
7813
+ data: blob,
7814
+ filename,
7815
+ contentType: "application/json",
7816
+ state
7817
+ });
7818
+ }
7819
+ };
7804
7820
  ERR_PERMALINK = exports.ERR_PERMALINK = "https://braintrust.dev/error-generating-link";
7805
7821
  Logger = exports.Logger = (_class6 = class {
7806
7822
 
@@ -7834,6 +7850,9 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
7834
7850
  get id() {
7835
7851
  return (async () => (await this.project).id)();
7836
7852
  }
7853
+ get loggingState() {
7854
+ return this.state;
7855
+ }
7837
7856
  parentObjectType() {
7838
7857
  return 2 /* PROJECT_LOGS */;
7839
7858
  }
@@ -8547,6 +8566,18 @@ Error: ${errorText}`;
8547
8566
  return (await this.lazyMetadata.get()).experiment.id;
8548
8567
  })();
8549
8568
  }
8569
+ get loggingState() {
8570
+ return this.state;
8571
+ }
8572
+ /**
8573
+ * Wait for the experiment ID to be resolved. This is useful for ensuring the ID
8574
+ * is available synchronously in child spans (for OTEL parent attributes).
8575
+ * @internal
8576
+ */
8577
+ async _waitForId() {
8578
+ await this.lazyId.get().catch(() => {
8579
+ });
8580
+ }
8550
8581
  get name() {
8551
8582
  return (async () => {
8552
8583
  return (await this.lazyMetadata.get()).experiment.name;
@@ -8803,6 +8834,9 @@ View complete results in Braintrust or run experiment.summarize() again.`
8803
8834
  return (await this.lazyMetadata.get()).experiment.name;
8804
8835
  })();
8805
8836
  }
8837
+ get loggingState() {
8838
+ return this.state;
8839
+ }
8806
8840
  async getState() {
8807
8841
  await this.lazyMetadata.get();
8808
8842
  return this.state;
@@ -9099,6 +9133,42 @@ View complete results in Braintrust or run experiment.summarize() again.`
9099
9133
  state() {
9100
9134
  return this._state;
9101
9135
  }
9136
+ /**
9137
+ * Internal method to get the OTEL parent string for this span.
9138
+ * This is used by OtelContextManager to set the braintrust.parent attribute.
9139
+ * @returns A string like "project_id:X" or "experiment_id:X", or undefined if no parent
9140
+ */
9141
+ _getOtelParent() {
9142
+ if (!this.parentObjectType) {
9143
+ return void 0;
9144
+ }
9145
+ try {
9146
+ if (this.parentObjectType === 2 /* PROJECT_LOGS */) {
9147
+ const syncResult = this.parentObjectId.getSync();
9148
+ const id = syncResult.value;
9149
+ const args = this.parentComputeObjectMetadataArgs;
9150
+ if (id) {
9151
+ return `project_id:${id}`;
9152
+ }
9153
+ const projectName = _optionalChain([args, 'optionalAccess', _106 => _106.project_name]);
9154
+ if (projectName) {
9155
+ return `project_name:${projectName}`;
9156
+ }
9157
+ } else if (this.parentObjectType === 1 /* EXPERIMENT */) {
9158
+ const syncResult = this.parentObjectId.getSync();
9159
+ const id = syncResult.value;
9160
+ if (!syncResult.resolved) {
9161
+ this.parentObjectId.get().catch(() => {
9162
+ });
9163
+ }
9164
+ if (id) {
9165
+ return `experiment_id:${id}`;
9166
+ }
9167
+ }
9168
+ } catch (e) {
9169
+ }
9170
+ return void 0;
9171
+ }
9102
9172
  // Custom inspect for Node.js console.log
9103
9173
  [Symbol.for("nodejs.util.inspect.custom")]() {
9104
9174
  return `SpanImpl {
@@ -9155,6 +9225,9 @@ View complete results in Braintrust or run experiment.summarize() again.`
9155
9225
  return (await this.lazyMetadata.get()).project;
9156
9226
  })();
9157
9227
  }
9228
+ get loggingState() {
9229
+ return this.state;
9230
+ }
9158
9231
  async getState() {
9159
9232
  await this.lazyMetadata.get();
9160
9233
  return this.state;
@@ -9377,13 +9450,13 @@ View complete results in Braintrust or run experiment.summarize() again.`
9377
9450
  return "slug" in this.metadata ? this.metadata.slug : this.metadata.id;
9378
9451
  }
9379
9452
  get prompt() {
9380
- return _optionalChain([this, 'access', _106 => _106.getParsedPromptData, 'call', _107 => _107(), 'optionalAccess', _108 => _108.prompt]);
9453
+ return _optionalChain([this, 'access', _107 => _107.getParsedPromptData, 'call', _108 => _108(), 'optionalAccess', _109 => _109.prompt]);
9381
9454
  }
9382
9455
  get version() {
9383
9456
  return this.metadata[TRANSACTION_ID_FIELD];
9384
9457
  }
9385
9458
  get options() {
9386
- return _optionalChain([this, 'access', _109 => _109.getParsedPromptData, 'call', _110 => _110(), 'optionalAccess', _111 => _111.options]) || {};
9459
+ return _optionalChain([this, 'access', _110 => _110.getParsedPromptData, 'call', _111 => _111(), 'optionalAccess', _112 => _112.options]) || {};
9387
9460
  }
9388
9461
  get promptData() {
9389
9462
  return this.getParsedPromptData();
@@ -9534,7 +9607,7 @@ View complete results in Braintrust or run experiment.summarize() again.`
9534
9607
  return {
9535
9608
  type: "chat",
9536
9609
  messages,
9537
- ..._optionalChain([prompt, 'access', _112 => _112.tools, 'optionalAccess', _113 => _113.trim, 'call', _114 => _114()]) ? {
9610
+ ..._optionalChain([prompt, 'access', _113 => _113.tools, 'optionalAccess', _114 => _114.trim, 'call', _115 => _115()]) ? {
9538
9611
  tools: render(prompt.tools)
9539
9612
  } : void 0
9540
9613
  };
@@ -9609,7 +9682,7 @@ function configureBrowser() {
9609
9682
  if (typeof AsyncLocalStorage !== "undefined") {
9610
9683
  isomorph_default.newAsyncLocalStorage = () => new AsyncLocalStorage();
9611
9684
  }
9612
- } catch (e11) {
9685
+ } catch (e10) {
9613
9686
  }
9614
9687
  isomorph_default.getEnv = (name) => {
9615
9688
  if (typeof process === "undefined" || typeof process.env === "undefined") {
@@ -9645,6 +9718,7 @@ __export(exports_browser_exports, {
9645
9718
  ExternalAttachment: () => ExternalAttachment,
9646
9719
  FailedHTTPResponse: () => FailedHTTPResponse,
9647
9720
  INTERNAL_BTQL_LIMIT: () => INTERNAL_BTQL_LIMIT,
9721
+ JSONAttachment: () => JSONAttachment,
9648
9722
  LEGACY_CACHED_HEADER: () => LEGACY_CACHED_HEADER,
9649
9723
  LazyValue: () => LazyValue,
9650
9724
  Logger: () => Logger,
@@ -9862,7 +9936,7 @@ function parseSpanFromResponseCreateParams(params) {
9862
9936
  }
9863
9937
  function parseEventFromResponseCreateResult(result) {
9864
9938
  const data = {};
9865
- if (_optionalChain([result, 'optionalAccess', _115 => _115.output]) !== void 0) {
9939
+ if (_optionalChain([result, 'optionalAccess', _116 => _116.output]) !== void 0) {
9866
9940
  data.output = processImagesInOutput(result.output);
9867
9941
  }
9868
9942
  if (result) {
@@ -9871,7 +9945,7 @@ function parseEventFromResponseCreateResult(result) {
9871
9945
  data.metadata = metadata;
9872
9946
  }
9873
9947
  }
9874
- data.metrics = parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _116 => _116.usage]));
9948
+ data.metrics = parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _117 => _117.usage]));
9875
9949
  return data;
9876
9950
  }
9877
9951
  function processImagesInOutput(output) {
@@ -9925,7 +9999,7 @@ function parseSpanFromResponseParseParams(params) {
9925
9999
  }
9926
10000
  function parseEventFromResponseParseResult(result) {
9927
10001
  const data = {};
9928
- if (_optionalChain([result, 'optionalAccess', _117 => _117.output]) !== void 0) {
10002
+ if (_optionalChain([result, 'optionalAccess', _118 => _118.output]) !== void 0) {
9929
10003
  data.output = processImagesInOutput(result.output);
9930
10004
  }
9931
10005
  if (result) {
@@ -9934,7 +10008,7 @@ function parseEventFromResponseParseResult(result) {
9934
10008
  data.metadata = metadata;
9935
10009
  }
9936
10010
  }
9937
- data.metrics = parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _118 => _118.usage]));
10011
+ data.metrics = parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _119 => _119.usage]));
9938
10012
  return data;
9939
10013
  }
9940
10014
  function traceResponseCreateStream(stream, timedSpan) {
@@ -9951,7 +10025,7 @@ function traceResponseCreateStream(stream, timedSpan) {
9951
10025
  return result;
9952
10026
  }
9953
10027
  const item = result.value;
9954
- if (!item || !_optionalChain([item, 'optionalAccess', _119 => _119.type]) || !_optionalChain([item, 'optionalAccess', _120 => _120.response])) {
10028
+ if (!item || !_optionalChain([item, 'optionalAccess', _120 => _120.type]) || !_optionalChain([item, 'optionalAccess', _121 => _121.response])) {
9955
10029
  return result;
9956
10030
  }
9957
10031
  const event = parseLogFromItem(item);
@@ -9962,14 +10036,14 @@ function traceResponseCreateStream(stream, timedSpan) {
9962
10036
  };
9963
10037
  }
9964
10038
  function parseLogFromItem(item) {
9965
- if (!item || !_optionalChain([item, 'optionalAccess', _121 => _121.type]) || !_optionalChain([item, 'optionalAccess', _122 => _122.response])) {
10039
+ if (!item || !_optionalChain([item, 'optionalAccess', _122 => _122.type]) || !_optionalChain([item, 'optionalAccess', _123 => _123.response])) {
9966
10040
  return {};
9967
10041
  }
9968
10042
  const response = item.response;
9969
10043
  switch (item.type) {
9970
10044
  case "response.completed":
9971
10045
  const data = {};
9972
- if (_optionalChain([response, 'optionalAccess', _123 => _123.output]) !== void 0) {
10046
+ if (_optionalChain([response, 'optionalAccess', _124 => _124.output]) !== void 0) {
9973
10047
  data.output = processImagesInOutput(response.output);
9974
10048
  }
9975
10049
  if (response) {
@@ -9978,7 +10052,7 @@ function parseLogFromItem(item) {
9978
10052
  data.metadata = metadata;
9979
10053
  }
9980
10054
  }
9981
- data.metrics = parseMetricsFromUsage(_optionalChain([response, 'optionalAccess', _124 => _124.usage]));
10055
+ data.metrics = parseMetricsFromUsage(_optionalChain([response, 'optionalAccess', _125 => _125.usage]));
9982
10056
  return data;
9983
10057
  default:
9984
10058
  return {};
@@ -10162,8 +10236,8 @@ function wrapOpenAIv4(openai) {
10162
10236
  const embeddingProxy = createEndpointProxy(openai.embeddings, wrapEmbeddings);
10163
10237
  const moderationProxy = createEndpointProxy(openai.moderations, wrapModerations);
10164
10238
  let betaProxy;
10165
- if (_optionalChain([openai, 'access', _125 => _125.beta, 'optionalAccess', _126 => _126.chat, 'optionalAccess', _127 => _127.completions, 'optionalAccess', _128 => _128.stream])) {
10166
- const betaChatCompletionProxy = new Proxy(_optionalChain([openai, 'optionalAccess', _129 => _129.beta, 'optionalAccess', _130 => _130.chat, 'access', _131 => _131.completions]), {
10239
+ if (_optionalChain([openai, 'access', _126 => _126.beta, 'optionalAccess', _127 => _127.chat, 'optionalAccess', _128 => _128.completions, 'optionalAccess', _129 => _129.stream])) {
10240
+ const betaChatCompletionProxy = new Proxy(_optionalChain([openai, 'optionalAccess', _130 => _130.beta, 'optionalAccess', _131 => _131.chat, 'access', _132 => _132.completions]), {
10167
10241
  get(target, name, receiver) {
10168
10242
  const baseVal = Reflect.get(target, name, receiver);
10169
10243
  if (name === "parse") {
@@ -10211,7 +10285,7 @@ function wrapOpenAIv4(openai) {
10211
10285
  });
10212
10286
  }
10213
10287
  function logCompletionResponse(startTime, response, span) {
10214
- const metrics2 = parseMetricsFromUsage(_optionalChain([response, 'optionalAccess', _132 => _132.usage]));
10288
+ const metrics2 = parseMetricsFromUsage(_optionalChain([response, 'optionalAccess', _133 => _133.usage]));
10215
10289
  metrics2.time_to_first_token = getCurrentUnixTimestamp() - startTime;
10216
10290
  span.log({
10217
10291
  output: response.choices,
@@ -10441,7 +10515,7 @@ function parseChatCompletionParams(params) {
10441
10515
  function processEmbeddingResponse(result, span) {
10442
10516
  span.log({
10443
10517
  output: { embedding_length: result.data[0].embedding.length },
10444
- metrics: parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _133 => _133.usage]))
10518
+ metrics: parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _134 => _134.usage]))
10445
10519
  });
10446
10520
  }
10447
10521
  function processModerationResponse(result, span) {
@@ -10471,10 +10545,10 @@ function postprocessStreamingResults(allResults) {
10471
10545
  if (result.usage) {
10472
10546
  metrics2 = {
10473
10547
  ...metrics2,
10474
- ...parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _134 => _134.usage]))
10548
+ ...parseMetricsFromUsage(_optionalChain([result, 'optionalAccess', _135 => _135.usage]))
10475
10549
  };
10476
10550
  }
10477
- const delta = _optionalChain([result, 'access', _135 => _135.choices, 'optionalAccess', _136 => _136[0], 'optionalAccess', _137 => _137.delta]);
10551
+ const delta = _optionalChain([result, 'access', _136 => _136.choices, 'optionalAccess', _137 => _137[0], 'optionalAccess', _138 => _138.delta]);
10478
10552
  if (!delta) {
10479
10553
  continue;
10480
10554
  }
@@ -10674,4 +10748,5 @@ var browser_default = exports_browser_exports;
10674
10748
 
10675
10749
 
10676
10750
 
10677
- exports.Attachment = Attachment; exports.BaseAttachment = BaseAttachment; exports.BraintrustState = BraintrustState; exports.BraintrustStream = BraintrustStream; exports.ContextManager = ContextManager; exports.Dataset = Dataset2; exports.ERR_PERMALINK = ERR_PERMALINK; exports.Experiment = Experiment2; exports.ExternalAttachment = ExternalAttachment; exports.FailedHTTPResponse = FailedHTTPResponse; exports.INTERNAL_BTQL_LIMIT = INTERNAL_BTQL_LIMIT; exports.LEGACY_CACHED_HEADER = LEGACY_CACHED_HEADER; exports.LazyValue = LazyValue; exports.Logger = Logger; exports.NOOP_SPAN = NOOP_SPAN; exports.NOOP_SPAN_PERMALINK = NOOP_SPAN_PERMALINK; exports.NoopSpan = NoopSpan; exports.Prompt = Prompt2; exports.ReadonlyAttachment = ReadonlyAttachment; exports.ReadonlyExperiment = ReadonlyExperiment; exports.SpanImpl = SpanImpl; exports.TestBackgroundLogger = TestBackgroundLogger; exports.X_CACHED_HEADER = X_CACHED_HEADER; exports._exportsForTestingOnly = _exportsForTestingOnly; exports._internalGetGlobalState = _internalGetGlobalState; exports._internalSetInitialState = _internalSetInitialState; exports.braintrustStreamChunkSchema = braintrustStreamChunkSchema; exports.createFinalValuePassThroughStream = createFinalValuePassThroughStream; exports.currentExperiment = currentExperiment; exports.currentLogger = currentLogger; exports.currentSpan = currentSpan; exports.deepCopyEvent = deepCopyEvent; exports.default = browser_default; exports.deserializePlainStringAsJSON = deserializePlainStringAsJSON; exports.devNullWritableStream = devNullWritableStream; exports.evaluatorDefinitionSchema = evaluatorDefinitionSchema; exports.evaluatorDefinitionsSchema = evaluatorDefinitionsSchema; exports.flush = flush; exports.getContextManager = getContextManager; exports.getPromptVersions = getPromptVersions; exports.getSpanParentObject = getSpanParentObject; exports.init = init; exports.initDataset = initDataset; exports.initExperiment = initExperiment; exports.initFunction = initFunction; exports.initLogger = initLogger; exports.invoke = invoke; exports.loadPrompt = loadPrompt; exports.log = log; exports.logError = logError; exports.login = login; exports.loginToState = loginToState; exports.newId = newId; exports.parseCachedHeader = parseCachedHeader; exports.permalink = permalink; exports.renderMessage = renderMessage; exports.renderPromptParams = renderPromptParams; exports.setFetch = setFetch; exports.setMaskingFunction = setMaskingFunction; exports.spanComponentsToObjectId = spanComponentsToObjectId; exports.startSpan = startSpan; exports.summarize = summarize; exports.traceable = traceable; exports.traced = traced; exports.updateSpan = updateSpan; exports.withCurrent = withCurrent; exports.withDataset = withDataset; exports.withExperiment = withExperiment; exports.withLogger = withLogger; exports.withParent = withParent; exports.wrapOpenAI = wrapOpenAI; exports.wrapOpenAIv4 = wrapOpenAIv4; exports.wrapTraced = wrapTraced;
10751
+
10752
+ exports.Attachment = Attachment; exports.BaseAttachment = BaseAttachment; exports.BraintrustState = BraintrustState; exports.BraintrustStream = BraintrustStream; exports.ContextManager = ContextManager; exports.Dataset = Dataset2; exports.ERR_PERMALINK = ERR_PERMALINK; exports.Experiment = Experiment2; exports.ExternalAttachment = ExternalAttachment; exports.FailedHTTPResponse = FailedHTTPResponse; exports.INTERNAL_BTQL_LIMIT = INTERNAL_BTQL_LIMIT; exports.JSONAttachment = JSONAttachment; exports.LEGACY_CACHED_HEADER = LEGACY_CACHED_HEADER; exports.LazyValue = LazyValue; exports.Logger = Logger; exports.NOOP_SPAN = NOOP_SPAN; exports.NOOP_SPAN_PERMALINK = NOOP_SPAN_PERMALINK; exports.NoopSpan = NoopSpan; exports.Prompt = Prompt2; exports.ReadonlyAttachment = ReadonlyAttachment; exports.ReadonlyExperiment = ReadonlyExperiment; exports.SpanImpl = SpanImpl; exports.TestBackgroundLogger = TestBackgroundLogger; exports.X_CACHED_HEADER = X_CACHED_HEADER; exports._exportsForTestingOnly = _exportsForTestingOnly; exports._internalGetGlobalState = _internalGetGlobalState; exports._internalSetInitialState = _internalSetInitialState; exports.braintrustStreamChunkSchema = braintrustStreamChunkSchema; exports.createFinalValuePassThroughStream = createFinalValuePassThroughStream; exports.currentExperiment = currentExperiment; exports.currentLogger = currentLogger; exports.currentSpan = currentSpan; exports.deepCopyEvent = deepCopyEvent; exports.default = browser_default; exports.deserializePlainStringAsJSON = deserializePlainStringAsJSON; exports.devNullWritableStream = devNullWritableStream; exports.evaluatorDefinitionSchema = evaluatorDefinitionSchema; exports.evaluatorDefinitionsSchema = evaluatorDefinitionsSchema; exports.flush = flush; exports.getContextManager = getContextManager; exports.getPromptVersions = getPromptVersions; exports.getSpanParentObject = getSpanParentObject; exports.init = init; exports.initDataset = initDataset; exports.initExperiment = initExperiment; exports.initFunction = initFunction; exports.initLogger = initLogger; exports.invoke = invoke; exports.loadPrompt = loadPrompt; exports.log = log; exports.logError = logError; exports.login = login; exports.loginToState = loginToState; exports.newId = newId; exports.parseCachedHeader = parseCachedHeader; exports.permalink = permalink; exports.renderMessage = renderMessage; exports.renderPromptParams = renderPromptParams; exports.setFetch = setFetch; exports.setMaskingFunction = setMaskingFunction; exports.spanComponentsToObjectId = spanComponentsToObjectId; exports.startSpan = startSpan; exports.summarize = summarize; exports.traceable = traceable; exports.traced = traced; exports.updateSpan = updateSpan; exports.withCurrent = withCurrent; exports.withDataset = withDataset; exports.withExperiment = withExperiment; exports.withLogger = withLogger; exports.withParent = withParent; exports.wrapOpenAI = wrapOpenAI; exports.wrapOpenAIv4 = wrapOpenAIv4; exports.wrapTraced = wrapTraced;
package/dist/browser.mjs CHANGED
@@ -5124,7 +5124,6 @@ var init_context3 = __esm({
5124
5124
  "src/otel/context.ts"() {
5125
5125
  "use strict";
5126
5126
  init_logger();
5127
- init_util();
5128
5127
  OTEL_NOT_INSTALLED_MESSAGE = "OpenTelemetry packages are not installed. Install them with: npm install @opentelemetry/api @opentelemetry/sdk-trace-base";
5129
5128
  otelTrace = null;
5130
5129
  otelContext = null;
@@ -5187,6 +5186,10 @@ var init_context3 = __esm({
5187
5186
  const currentContext = otelContext.active();
5188
5187
  let newContext = otelTrace.setSpan(currentContext, wrappedContext);
5189
5188
  newContext = newContext.setValue("braintrust_span", span);
5189
+ const parentValue = span._getOtelParent();
5190
+ if (parentValue) {
5191
+ newContext = newContext.setValue("braintrust.parent", parentValue);
5192
+ }
5190
5193
  return otelContext.with(newContext, callback);
5191
5194
  }
5192
5195
  } catch (error) {
@@ -5202,34 +5205,6 @@ var init_context3 = __esm({
5202
5205
  }
5203
5206
  return void 0;
5204
5207
  }
5205
- _getOtelParent(span) {
5206
- if (!span.parentObjectType || !span.parentObjectId) {
5207
- return void 0;
5208
- }
5209
- try {
5210
- const parentType = span.parentObjectType;
5211
- const parentId = span.parentObjectId;
5212
- if (parentType === 2 /* PROJECT_LOGS */) {
5213
- const id = typeof parentId === "object" && parentId !== null && "get" in parentId ? (
5214
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Type guard ensures object has get method
5215
- parentId.get()
5216
- ) : parentId;
5217
- if (typeof id === "string") {
5218
- return `project_id:${id}`;
5219
- }
5220
- } else if (parentType === 1 /* EXPERIMENT */) {
5221
- const id = typeof parentId === "object" && parentId !== null && "get" in parentId ? (
5222
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Type guard ensures object has get method
5223
- parentId.get()
5224
- ) : parentId;
5225
- if (typeof id === "string") {
5226
- return `experiment_id:${id}`;
5227
- }
5228
- }
5229
- } catch {
5230
- }
5231
- return void 0;
5232
- }
5233
5208
  };
5234
5209
  }
5235
5210
  });
@@ -6931,7 +6906,7 @@ function resetIdGenStateForTests() {
6931
6906
  state.resetIdGenState();
6932
6907
  }
6933
6908
  }
6934
- var BRAINTRUST_ATTACHMENT, EXTERNAL_ATTACHMENT, BRAINTRUST_PARAMS, REDACTION_FIELDS, MaskingError, ContextManager, BraintrustContextManager, NoopSpan, NOOP_SPAN, NOOP_SPAN_PERMALINK, loginSchema, stateNonce, BraintrustState, _globalState, _internalGetGlobalState, FailedHTTPResponse, HTTPConnection, BaseAttachment, Attachment, ExternalAttachment, attachmentMetadataSchema, ReadonlyAttachment, ERR_PERMALINK, Logger, TestBackgroundLogger, BACKGROUND_LOGGER_BASE_SLEEP_TIME_S, HTTPBackgroundLogger, traceable, INTERNAL_BTQL_LIMIT, MAX_BTQL_ITERATIONS, ObjectFetcher, Experiment2, ReadonlyExperiment, executionCounter, SpanImpl, Dataset2, Prompt2, TEST_API_KEY, _exportsForTestingOnly;
6909
+ var BRAINTRUST_ATTACHMENT, EXTERNAL_ATTACHMENT, BRAINTRUST_PARAMS, REDACTION_FIELDS, MaskingError, ContextManager, BraintrustContextManager, NoopSpan, NOOP_SPAN, NOOP_SPAN_PERMALINK, loginSchema, stateNonce, BraintrustState, _globalState, _internalGetGlobalState, FailedHTTPResponse, HTTPConnection, BaseAttachment, Attachment, ExternalAttachment, attachmentMetadataSchema, ReadonlyAttachment, JSONAttachment, ERR_PERMALINK, Logger, TestBackgroundLogger, BACKGROUND_LOGGER_BASE_SLEEP_TIME_S, HTTPBackgroundLogger, traceable, INTERNAL_BTQL_LIMIT, MAX_BTQL_ITERATIONS, ObjectFetcher, Experiment2, ReadonlyExperiment, executionCounter, SpanImpl, Dataset2, Prompt2, TEST_API_KEY, _exportsForTestingOnly;
6935
6910
  var init_logger = __esm({
6936
6911
  "src/logger.ts"() {
6937
6912
  "use strict";
@@ -7040,6 +7015,9 @@ var init_logger = __esm({
7040
7015
  state() {
7041
7016
  return _internalGetGlobalState();
7042
7017
  }
7018
+ _getOtelParent() {
7019
+ return void 0;
7020
+ }
7043
7021
  // Custom inspect for Node.js console.log
7044
7022
  [Symbol.for("nodejs.util.inspect.custom")]() {
7045
7023
  return `NoopSpan {
@@ -7801,6 +7779,44 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
7801
7779
  return new LazyValue(download);
7802
7780
  }
7803
7781
  };
7782
+ JSONAttachment = class extends Attachment {
7783
+ /**
7784
+ * Construct a JSONAttachment from a JSON-serializable object.
7785
+ *
7786
+ * @param data The JSON object to attach. Must be JSON-serializable.
7787
+ * @param options Additional options:
7788
+ * - `filename`: The filename for the attachment (defaults to "data.json")
7789
+ * - `pretty`: Whether to pretty-print the JSON (defaults to false)
7790
+ * - `state`: (Optional) For internal use.
7791
+ *
7792
+ * @example
7793
+ * ```typescript
7794
+ * const largeTranscript = [
7795
+ * { role: "user", content: "..." },
7796
+ * { role: "assistant", content: "..." },
7797
+ * // ... many more messages
7798
+ * ];
7799
+ *
7800
+ * logger.log({
7801
+ * input: {
7802
+ * type: "chat",
7803
+ * transcript: new JSONAttachment(largeTranscript, { filename: "transcript.json" })
7804
+ * }
7805
+ * });
7806
+ * ```
7807
+ */
7808
+ constructor(data, options) {
7809
+ const { filename = "data.json", pretty = false, state } = options ?? {};
7810
+ const jsonString = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
7811
+ const blob = new Blob([jsonString], { type: "application/json" });
7812
+ super({
7813
+ data: blob,
7814
+ filename,
7815
+ contentType: "application/json",
7816
+ state
7817
+ });
7818
+ }
7819
+ };
7804
7820
  ERR_PERMALINK = "https://braintrust.dev/error-generating-link";
7805
7821
  Logger = class {
7806
7822
  state;
@@ -7834,6 +7850,9 @@ with a Blob/ArrayBuffer, or run the program on Node.js.`
7834
7850
  get id() {
7835
7851
  return (async () => (await this.project).id)();
7836
7852
  }
7853
+ get loggingState() {
7854
+ return this.state;
7855
+ }
7837
7856
  parentObjectType() {
7838
7857
  return 2 /* PROJECT_LOGS */;
7839
7858
  }
@@ -8547,6 +8566,18 @@ Error: ${errorText}`;
8547
8566
  return (await this.lazyMetadata.get()).experiment.id;
8548
8567
  })();
8549
8568
  }
8569
+ get loggingState() {
8570
+ return this.state;
8571
+ }
8572
+ /**
8573
+ * Wait for the experiment ID to be resolved. This is useful for ensuring the ID
8574
+ * is available synchronously in child spans (for OTEL parent attributes).
8575
+ * @internal
8576
+ */
8577
+ async _waitForId() {
8578
+ await this.lazyId.get().catch(() => {
8579
+ });
8580
+ }
8550
8581
  get name() {
8551
8582
  return (async () => {
8552
8583
  return (await this.lazyMetadata.get()).experiment.name;
@@ -8803,6 +8834,9 @@ View complete results in Braintrust or run experiment.summarize() again.`
8803
8834
  return (await this.lazyMetadata.get()).experiment.name;
8804
8835
  })();
8805
8836
  }
8837
+ get loggingState() {
8838
+ return this.state;
8839
+ }
8806
8840
  async getState() {
8807
8841
  await this.lazyMetadata.get();
8808
8842
  return this.state;
@@ -9099,6 +9133,42 @@ View complete results in Braintrust or run experiment.summarize() again.`
9099
9133
  state() {
9100
9134
  return this._state;
9101
9135
  }
9136
+ /**
9137
+ * Internal method to get the OTEL parent string for this span.
9138
+ * This is used by OtelContextManager to set the braintrust.parent attribute.
9139
+ * @returns A string like "project_id:X" or "experiment_id:X", or undefined if no parent
9140
+ */
9141
+ _getOtelParent() {
9142
+ if (!this.parentObjectType) {
9143
+ return void 0;
9144
+ }
9145
+ try {
9146
+ if (this.parentObjectType === 2 /* PROJECT_LOGS */) {
9147
+ const syncResult = this.parentObjectId.getSync();
9148
+ const id = syncResult.value;
9149
+ const args = this.parentComputeObjectMetadataArgs;
9150
+ if (id) {
9151
+ return `project_id:${id}`;
9152
+ }
9153
+ const projectName = args?.project_name;
9154
+ if (projectName) {
9155
+ return `project_name:${projectName}`;
9156
+ }
9157
+ } else if (this.parentObjectType === 1 /* EXPERIMENT */) {
9158
+ const syncResult = this.parentObjectId.getSync();
9159
+ const id = syncResult.value;
9160
+ if (!syncResult.resolved) {
9161
+ this.parentObjectId.get().catch(() => {
9162
+ });
9163
+ }
9164
+ if (id) {
9165
+ return `experiment_id:${id}`;
9166
+ }
9167
+ }
9168
+ } catch (e) {
9169
+ }
9170
+ return void 0;
9171
+ }
9102
9172
  // Custom inspect for Node.js console.log
9103
9173
  [Symbol.for("nodejs.util.inspect.custom")]() {
9104
9174
  return `SpanImpl {
@@ -9155,6 +9225,9 @@ View complete results in Braintrust or run experiment.summarize() again.`
9155
9225
  return (await this.lazyMetadata.get()).project;
9156
9226
  })();
9157
9227
  }
9228
+ get loggingState() {
9229
+ return this.state;
9230
+ }
9158
9231
  async getState() {
9159
9232
  await this.lazyMetadata.get();
9160
9233
  return this.state;
@@ -9645,6 +9718,7 @@ __export(exports_browser_exports, {
9645
9718
  ExternalAttachment: () => ExternalAttachment,
9646
9719
  FailedHTTPResponse: () => FailedHTTPResponse,
9647
9720
  INTERNAL_BTQL_LIMIT: () => INTERNAL_BTQL_LIMIT,
9721
+ JSONAttachment: () => JSONAttachment,
9648
9722
  LEGACY_CACHED_HEADER: () => LEGACY_CACHED_HEADER,
9649
9723
  LazyValue: () => LazyValue,
9650
9724
  Logger: () => Logger,
@@ -10612,6 +10686,7 @@ export {
10612
10686
  ExternalAttachment,
10613
10687
  FailedHTTPResponse,
10614
10688
  INTERNAL_BTQL_LIMIT,
10689
+ JSONAttachment,
10615
10690
  LEGACY_CACHED_HEADER,
10616
10691
  LazyValue,
10617
10692
  Logger,