braintrust 0.0.89 → 0.0.91

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
@@ -73,6 +73,10 @@ var v4_default = v4;
73
73
  // ../core/js/dist/index.mjs
74
74
  var TRANSACTION_ID_FIELD = "_xact_id";
75
75
  var IS_MERGE_FIELD = "_is_merge";
76
+ var AUDIT_SOURCE_FIELD = "_audit_source";
77
+ var AUDIT_METADATA_FIELD = "_audit_metadata";
78
+ var VALID_SOURCES = ["app", "api", "external"];
79
+ var PARENT_ID_FIELD = "_parent_id";
76
80
  function mergeDicts(mergeInto, mergeFrom) {
77
81
  for (const [k, mergeFromV] of Object.entries(mergeFrom)) {
78
82
  const mergeIntoV = mergeInto[k];
@@ -85,6 +89,7 @@ function mergeDicts(mergeInto, mergeFrom) {
85
89
  mergeInto[k] = mergeFromV;
86
90
  }
87
91
  }
92
+ return mergeInto;
88
93
  }
89
94
  function generateMergedRowKey(row) {
90
95
  return JSON.stringify(
@@ -126,6 +131,15 @@ function mergeRowBatch(rows) {
126
131
  out.push(...Object.values(rowGroups));
127
132
  return out;
128
133
  }
134
+ var SpanTypeAttribute = /* @__PURE__ */ ((SpanTypeAttribute2) => {
135
+ SpanTypeAttribute2["LLM"] = "llm";
136
+ SpanTypeAttribute2["SCORE"] = "score";
137
+ SpanTypeAttribute2["FUNCTION"] = "function";
138
+ SpanTypeAttribute2["EVAL"] = "eval";
139
+ SpanTypeAttribute2["TASK"] = "task";
140
+ SpanTypeAttribute2["TOOL"] = "tool";
141
+ return SpanTypeAttribute2;
142
+ })(SpanTypeAttribute || {});
129
143
 
130
144
  // src/util.ts
131
145
  var GLOBAL_PROJECT = "Global";
@@ -148,6 +162,9 @@ function runFinally(f, finallyF) {
148
162
  function getCurrentUnixTimestamp() {
149
163
  return (/* @__PURE__ */ new Date()).getTime() / 1e3;
150
164
  }
165
+ function isEmpty(a) {
166
+ return a === void 0 || a === null;
167
+ }
151
168
 
152
169
  // src/logger.ts
153
170
  var NoopSpan = class {
@@ -159,6 +176,8 @@ var NoopSpan = class {
159
176
  }
160
177
  log(_) {
161
178
  }
179
+ logFeedback(event) {
180
+ }
162
181
  traced(callback, _1) {
163
182
  return callback(this);
164
183
  }
@@ -333,6 +352,70 @@ var HTTPConnection = class _HTTPConnection {
333
352
  return await resp.json();
334
353
  }
335
354
  };
355
+ function logFeedbackImpl(bgLogger, parentIds, {
356
+ id,
357
+ expected,
358
+ scores,
359
+ metadata: inputMetadata,
360
+ comment,
361
+ source: inputSource
362
+ }) {
363
+ const source = inputSource ?? "external";
364
+ if (!VALID_SOURCES.includes(source)) {
365
+ throw new Error(`source must be one of ${VALID_SOURCES}`);
366
+ }
367
+ if (isEmpty(scores) && isEmpty(expected) && isEmpty(comment)) {
368
+ throw new Error(
369
+ "At least one of scores, expected, or comment must be specified"
370
+ );
371
+ }
372
+ const validatedEvent = validateAndSanitizeExperimentLogPartialArgs({
373
+ scores,
374
+ metadata: inputMetadata,
375
+ expected
376
+ });
377
+ let { metadata, ...updateEvent } = validatedEvent;
378
+ updateEvent = Object.fromEntries(
379
+ Object.entries(updateEvent).filter(([_, v]) => !isEmpty(v))
380
+ );
381
+ const trueParentIds = (async () => {
382
+ const { kind, ...ids } = await parentIds;
383
+ return ids;
384
+ })();
385
+ if (Object.keys(updateEvent).length > 0) {
386
+ const record = (async () => {
387
+ return {
388
+ id,
389
+ ...updateEvent,
390
+ ...await trueParentIds,
391
+ [AUDIT_SOURCE_FIELD]: source,
392
+ [AUDIT_METADATA_FIELD]: metadata,
393
+ [IS_MERGE_FIELD]: true
394
+ };
395
+ })();
396
+ bgLogger.log([record]);
397
+ }
398
+ if (!isEmpty(comment)) {
399
+ const record = (async () => {
400
+ return {
401
+ id: v4_default(),
402
+ created: (/* @__PURE__ */ new Date()).toISOString(),
403
+ origin: {
404
+ // NOTE: We do not know (or care?) what the transaction id of the row that
405
+ // we're commenting on is here, so we omit it.
406
+ id
407
+ },
408
+ comment: {
409
+ text: comment
410
+ },
411
+ ...await trueParentIds,
412
+ [AUDIT_SOURCE_FIELD]: source,
413
+ [AUDIT_METADATA_FIELD]: metadata
414
+ };
415
+ })();
416
+ bgLogger.log([record]);
417
+ }
418
+ }
336
419
  var Logger = class {
337
420
  constructor(lazyMetadata, logOptions = {}) {
338
421
  // For type identification.
@@ -411,6 +494,14 @@ var Logger = class {
411
494
  })();
412
495
  }
413
496
  }
497
+ async lazyParentIds() {
498
+ return {
499
+ kind: "project_log",
500
+ org_id: await this.org_id,
501
+ project_id: (await this.project).id,
502
+ log_id: "g"
503
+ };
504
+ }
414
505
  /**
415
506
  * Lower-level alternative to `traced`, which does not automatically end the span or mark it as current.
416
507
  *
@@ -418,19 +509,27 @@ var Logger = class {
418
509
  */
419
510
  startSpan(args) {
420
511
  const { name, ...argsRest } = args ?? {};
421
- const parentIds = (async () => ({
422
- kind: "project_log",
423
- org_id: await this.org_id,
424
- project_id: (await this.project).id,
425
- log_id: "g"
426
- }))();
427
512
  return new SpanImpl({
428
- parentIds,
513
+ parentIds: this.lazyParentIds(),
429
514
  bgLogger: this.bgLogger,
430
515
  name: name ?? "root",
431
516
  ...argsRest
432
517
  });
433
518
  }
519
+ /**
520
+ * Log feedback to an event. Feedback is used to save feedback scores, set an expected value, or add a comment.
521
+ *
522
+ * @param event
523
+ * @param event.id The id of the event to log feedback for. This is the `id` returned by `log` or accessible as the `id` field of a span.
524
+ * @param event.scores (Optional) a dictionary of numeric values (between 0 and 1) to log. These scores will be merged into the existing scores for the event.
525
+ * @param event.expected (Optional) the ground truth value (an arbitrary, JSON serializable object) that you'd compare to `output` to determine if your `output` value is correct or not.
526
+ * @param event.comment (Optional) an optional comment string to log about the event.
527
+ * @param event.metadata (Optional) a dictionary with additional data about the feedback. If you have a `user_id`, you can log it here and access it in the Braintrust UI.
528
+ * @param event.source (Optional) the source of the feedback. Must be one of "external" (default), "app", or "api".
529
+ */
530
+ logFeedback(event) {
531
+ logFeedbackImpl(this.bgLogger, this.lazyParentIds(), event);
532
+ }
434
533
  /*
435
534
  * Flush any pending logs to the server.
436
535
  */
@@ -1022,6 +1121,13 @@ var Experiment = class {
1022
1121
  () => span.end()
1023
1122
  );
1024
1123
  }
1124
+ async lazyParentIds() {
1125
+ return {
1126
+ kind: "experiment",
1127
+ project_id: (await this.project).id,
1128
+ experiment_id: await this.id
1129
+ };
1130
+ }
1025
1131
  /**
1026
1132
  * Lower-level alternative to `traced`, which does not automatically end the span or mark it as current.
1027
1133
  *
@@ -1029,13 +1135,8 @@ var Experiment = class {
1029
1135
  */
1030
1136
  startSpan(args) {
1031
1137
  const { name, ...argsRest } = args ?? {};
1032
- const parentIds = (async () => ({
1033
- kind: "experiment",
1034
- project_id: (await this.project).id,
1035
- experiment_id: await this.id
1036
- }))();
1037
1138
  return new SpanImpl({
1038
- parentIds,
1139
+ parentIds: this.lazyParentIds(),
1039
1140
  bgLogger: this.bgLogger,
1040
1141
  name: name ?? "root",
1041
1142
  ...argsRest
@@ -1097,6 +1198,20 @@ var Experiment = class {
1097
1198
  metrics
1098
1199
  };
1099
1200
  }
1201
+ /**
1202
+ * Log feedback to an event in the experiment. Feedback is used to save feedback scores, set an expected value, or add a comment.
1203
+ *
1204
+ * @param event
1205
+ * @param event.id The id of the event to log feedback for. This is the `id` returned by `log` or accessible as the `id` field of a span.
1206
+ * @param event.scores (Optional) a dictionary of numeric values (between 0 and 1) to log. These scores will be merged into the existing scores for the event.
1207
+ * @param event.expected (Optional) the ground truth value (an arbitrary, JSON serializable object) that you'd compare to `output` to determine if your `output` value is correct or not.
1208
+ * @param event.comment (Optional) an optional comment string to log about the event.
1209
+ * @param event.metadata (Optional) a dictionary with additional data about the feedback. If you have a `user_id`, you can log it here and access it in the Braintrust UI.
1210
+ * @param event.source (Optional) the source of the feedback. Must be one of "external" (default), "app", or "api".
1211
+ */
1212
+ logFeedback(event) {
1213
+ logFeedbackImpl(this.bgLogger, this.lazyParentIds(), event);
1214
+ }
1100
1215
  /**
1101
1216
  * Flush any pending rows to the server.
1102
1217
  */
@@ -1147,10 +1262,12 @@ var SpanImpl = class _SpanImpl {
1147
1262
  this.rowIds = {
1148
1263
  id,
1149
1264
  span_id,
1150
- root_span_id: args.parentSpanInfo?.root_span_id ?? span_id
1265
+ root_span_id: "parentSpanInfo" in args && args.parentSpanInfo?.root_span_id ? args.parentSpanInfo.root_span_id : span_id
1151
1266
  };
1152
- if (args.parentSpanInfo) {
1267
+ if ("parentSpanInfo" in args && args.parentSpanInfo?.span_id) {
1153
1268
  this.internalData.span_parents = [args.parentSpanInfo.span_id];
1269
+ } else if ("parentId" in args && !isEmpty(args.parentId)) {
1270
+ this.rowIds[PARENT_ID_FIELD] = args.parentId;
1154
1271
  }
1155
1272
  this.isMerge = false;
1156
1273
  const { id: _id, ...eventRest } = args.event ?? {};
@@ -1174,16 +1291,26 @@ var SpanImpl = class _SpanImpl {
1174
1291
  if (sanitizedAndInternalData.metrics?.end) {
1175
1292
  this.loggedEndTime = sanitizedAndInternalData.metrics?.end;
1176
1293
  }
1294
+ const parentIds = (async () => {
1295
+ const { kind, ...ids } = await this.parentIds;
1296
+ return ids;
1297
+ })();
1177
1298
  const record = (async () => {
1178
1299
  return {
1179
1300
  ...sanitizedAndInternalData,
1180
1301
  ...this.rowIds,
1181
- ...await this.parentIds,
1302
+ ...await parentIds,
1182
1303
  [IS_MERGE_FIELD]: this.isMerge
1183
1304
  };
1184
1305
  })();
1185
1306
  this.bgLogger.log([record]);
1186
1307
  }
1308
+ logFeedback(event) {
1309
+ logFeedbackImpl(this.bgLogger, this.parentIds, {
1310
+ ...event,
1311
+ id: this.id
1312
+ });
1313
+ }
1187
1314
  traced(callback, args) {
1188
1315
  const { setCurrent, ...argsRest } = args ?? {};
1189
1316
  const span = this.startSpan(argsRest);
@@ -1451,6 +1578,7 @@ function wrapOpenAI(openai) {
1451
1578
  return openai;
1452
1579
  }
1453
1580
  }
1581
+ globalThis.__inherited_braintrust_wrap_openai = wrapOpenAI;
1454
1582
  function wrapOpenAIv4(openai) {
1455
1583
  let completionProxy = new Proxy(openai.chat.completions, {
1456
1584
  get(target, name, receiver) {
@@ -1469,6 +1597,15 @@ function wrapOpenAIv4(openai) {
1469
1597
  return Reflect.get(target, name, receiver);
1470
1598
  }
1471
1599
  });
1600
+ let embeddingProxy = new Proxy(openai.embeddings, {
1601
+ get(target, name, receiver) {
1602
+ const baseVal = Reflect.get(target, name, receiver);
1603
+ if (name === "create") {
1604
+ return wrapEmbeddings(baseVal.bind(target));
1605
+ }
1606
+ return baseVal;
1607
+ }
1608
+ });
1472
1609
  let betaProxy;
1473
1610
  if (openai.beta?.chat?.completions?.stream) {
1474
1611
  let betaChatCompletionProxy = new Proxy(openai?.beta?.chat.completions, {
@@ -1502,6 +1639,9 @@ function wrapOpenAIv4(openai) {
1502
1639
  if (name === "chat") {
1503
1640
  return chatProxy;
1504
1641
  }
1642
+ if (name === "embeddings") {
1643
+ return embeddingProxy;
1644
+ }
1505
1645
  if (name === "beta" && betaProxy) {
1506
1646
  return betaProxy;
1507
1647
  }
@@ -1511,10 +1651,13 @@ function wrapOpenAIv4(openai) {
1511
1651
  return proxy;
1512
1652
  }
1513
1653
  function wrapBetaChatCompletion(completion) {
1514
- return async (params) => {
1654
+ return (params) => {
1515
1655
  const { messages, ...rest } = params;
1516
1656
  const span = startSpan({
1517
1657
  name: "OpenAI Chat Completion",
1658
+ spanAttributes: {
1659
+ type: SpanTypeAttribute.LLM
1660
+ },
1518
1661
  event: {
1519
1662
  input: messages,
1520
1663
  metadata: {
@@ -1523,7 +1666,7 @@ function wrapBetaChatCompletion(completion) {
1523
1666
  }
1524
1667
  });
1525
1668
  const startTime = getCurrentUnixTimestamp();
1526
- const ret = await completion(params);
1669
+ const ret = completion(params);
1527
1670
  let first = true;
1528
1671
  ret.on("chunk", (_chunk) => {
1529
1672
  if (first) {
@@ -1548,10 +1691,13 @@ function wrapBetaChatCompletion(completion) {
1548
1691
  };
1549
1692
  }
1550
1693
  function wrapChatCompletion(completion) {
1551
- return async (params) => {
1694
+ return async (params, options) => {
1552
1695
  const { messages, ...rest } = params;
1553
1696
  const span = startSpan({
1554
1697
  name: "OpenAI Chat Completion",
1698
+ spanAttributes: {
1699
+ type: SpanTypeAttribute.LLM
1700
+ },
1555
1701
  event: {
1556
1702
  input: messages,
1557
1703
  metadata: {
@@ -1561,11 +1707,14 @@ function wrapChatCompletion(completion) {
1561
1707
  });
1562
1708
  if (params.stream) {
1563
1709
  const startTime = getCurrentUnixTimestamp();
1564
- const ret = await completion(params);
1710
+ const ret = await completion(params, options);
1565
1711
  return new WrapperStream(span, startTime, ret);
1566
1712
  } else {
1567
1713
  try {
1568
- const ret = await completion(params);
1714
+ const ret = await completion(
1715
+ params,
1716
+ options
1717
+ );
1569
1718
  const { messages: messages2, ...rest2 } = params;
1570
1719
  span.log({
1571
1720
  input: messages2,
@@ -1586,6 +1735,37 @@ function wrapChatCompletion(completion) {
1586
1735
  }
1587
1736
  };
1588
1737
  }
1738
+ function wrapEmbeddings(create) {
1739
+ return async (params, options) => {
1740
+ const { input, ...rest } = params;
1741
+ return traced(
1742
+ async (span) => {
1743
+ const result = await create(params, options);
1744
+ const output = result.data[0];
1745
+ span.log({
1746
+ output,
1747
+ metrics: {
1748
+ tokens: result.usage?.total_tokens,
1749
+ prompt_tokens: result.usage?.prompt_tokens
1750
+ }
1751
+ });
1752
+ return result;
1753
+ },
1754
+ {
1755
+ name: "OpenAI Embedding",
1756
+ spanAttributes: {
1757
+ type: SpanTypeAttribute.LLM
1758
+ },
1759
+ event: {
1760
+ input,
1761
+ metadata: {
1762
+ ...rest
1763
+ }
1764
+ }
1765
+ }
1766
+ );
1767
+ };
1768
+ }
1589
1769
  var WrapperStream = class {
1590
1770
  constructor(span, startTime, iter) {
1591
1771
  this.span = span;
package/dist/cli.js CHANGED
@@ -9065,7 +9065,7 @@ var require_package = __commonJS({
9065
9065
  "package.json"(exports2, module2) {
9066
9066
  module2.exports = {
9067
9067
  name: "braintrust",
9068
- version: "0.0.89",
9068
+ version: "0.0.91",
9069
9069
  description: "SDK for integrating Braintrust",
9070
9070
  main: "./dist/index.js",
9071
9071
  browser: {
@@ -9108,7 +9108,7 @@ var require_package = __commonJS({
9108
9108
  typescript: "^5.3.3"
9109
9109
  },
9110
9110
  dependencies: {
9111
- "@braintrust/core": "^0.0.8",
9111
+ "@braintrust/core": "^0.0.12",
9112
9112
  argparse: "^2.0.1",
9113
9113
  chalk: "^4.1.2",
9114
9114
  "cli-progress": "^3.12.0",
@@ -10511,6 +10511,10 @@ var import_pluralize2 = __toESM(require_pluralize());
10511
10511
  // ../core/js/dist/index.mjs
10512
10512
  var TRANSACTION_ID_FIELD = "_xact_id";
10513
10513
  var IS_MERGE_FIELD = "_is_merge";
10514
+ var AUDIT_SOURCE_FIELD = "_audit_source";
10515
+ var AUDIT_METADATA_FIELD = "_audit_metadata";
10516
+ var VALID_SOURCES = ["app", "api", "external"];
10517
+ var PARENT_ID_FIELD = "_parent_id";
10514
10518
  function mergeDicts(mergeInto, mergeFrom) {
10515
10519
  for (const [k, mergeFromV] of Object.entries(mergeFrom)) {
10516
10520
  const mergeIntoV = mergeInto[k];
@@ -10523,6 +10527,7 @@ function mergeDicts(mergeInto, mergeFrom) {
10523
10527
  mergeInto[k] = mergeFromV;
10524
10528
  }
10525
10529
  }
10530
+ return mergeInto;
10526
10531
  }
10527
10532
  function generateMergedRowKey(row) {
10528
10533
  return JSON.stringify(
@@ -10564,6 +10569,15 @@ function mergeRowBatch(rows) {
10564
10569
  out.push(...Object.values(rowGroups));
10565
10570
  return out;
10566
10571
  }
10572
+ var SpanTypeAttribute = /* @__PURE__ */ ((SpanTypeAttribute2) => {
10573
+ SpanTypeAttribute2["LLM"] = "llm";
10574
+ SpanTypeAttribute2["SCORE"] = "score";
10575
+ SpanTypeAttribute2["FUNCTION"] = "function";
10576
+ SpanTypeAttribute2["EVAL"] = "eval";
10577
+ SpanTypeAttribute2["TASK"] = "task";
10578
+ SpanTypeAttribute2["TOOL"] = "tool";
10579
+ return SpanTypeAttribute2;
10580
+ })(SpanTypeAttribute || {});
10567
10581
 
10568
10582
  // src/isomorph.ts
10569
10583
  var DefaultAsyncLocalStorage = class {
@@ -10609,6 +10623,9 @@ function runFinally(f, finallyF) {
10609
10623
  function getCurrentUnixTimestamp() {
10610
10624
  return (/* @__PURE__ */ new Date()).getTime() / 1e3;
10611
10625
  }
10626
+ function isEmpty(a) {
10627
+ return a === void 0 || a === null;
10628
+ }
10612
10629
 
10613
10630
  // src/logger.ts
10614
10631
  var NoopSpan = class {
@@ -10620,6 +10637,8 @@ var NoopSpan = class {
10620
10637
  }
10621
10638
  log(_) {
10622
10639
  }
10640
+ logFeedback(event) {
10641
+ }
10623
10642
  traced(callback, _1) {
10624
10643
  return callback(this);
10625
10644
  }
@@ -10794,6 +10813,70 @@ var HTTPConnection = class _HTTPConnection {
10794
10813
  return await resp.json();
10795
10814
  }
10796
10815
  };
10816
+ function logFeedbackImpl(bgLogger, parentIds, {
10817
+ id,
10818
+ expected,
10819
+ scores,
10820
+ metadata: inputMetadata,
10821
+ comment,
10822
+ source: inputSource
10823
+ }) {
10824
+ const source = inputSource ?? "external";
10825
+ if (!VALID_SOURCES.includes(source)) {
10826
+ throw new Error(`source must be one of ${VALID_SOURCES}`);
10827
+ }
10828
+ if (isEmpty(scores) && isEmpty(expected) && isEmpty(comment)) {
10829
+ throw new Error(
10830
+ "At least one of scores, expected, or comment must be specified"
10831
+ );
10832
+ }
10833
+ const validatedEvent = validateAndSanitizeExperimentLogPartialArgs({
10834
+ scores,
10835
+ metadata: inputMetadata,
10836
+ expected
10837
+ });
10838
+ let { metadata, ...updateEvent } = validatedEvent;
10839
+ updateEvent = Object.fromEntries(
10840
+ Object.entries(updateEvent).filter(([_, v]) => !isEmpty(v))
10841
+ );
10842
+ const trueParentIds = (async () => {
10843
+ const { kind, ...ids } = await parentIds;
10844
+ return ids;
10845
+ })();
10846
+ if (Object.keys(updateEvent).length > 0) {
10847
+ const record = (async () => {
10848
+ return {
10849
+ id,
10850
+ ...updateEvent,
10851
+ ...await trueParentIds,
10852
+ [AUDIT_SOURCE_FIELD]: source,
10853
+ [AUDIT_METADATA_FIELD]: metadata,
10854
+ [IS_MERGE_FIELD]: true
10855
+ };
10856
+ })();
10857
+ bgLogger.log([record]);
10858
+ }
10859
+ if (!isEmpty(comment)) {
10860
+ const record = (async () => {
10861
+ return {
10862
+ id: v4_default(),
10863
+ created: (/* @__PURE__ */ new Date()).toISOString(),
10864
+ origin: {
10865
+ // NOTE: We do not know (or care?) what the transaction id of the row that
10866
+ // we're commenting on is here, so we omit it.
10867
+ id
10868
+ },
10869
+ comment: {
10870
+ text: comment
10871
+ },
10872
+ ...await trueParentIds,
10873
+ [AUDIT_SOURCE_FIELD]: source,
10874
+ [AUDIT_METADATA_FIELD]: metadata
10875
+ };
10876
+ })();
10877
+ bgLogger.log([record]);
10878
+ }
10879
+ }
10797
10880
  var MaxRequestSize = 6 * 1024 * 1024;
10798
10881
  function constructJsonArray(items) {
10799
10882
  return `[${items.join(",")}]`;
@@ -11177,6 +11260,13 @@ var Experiment = class {
11177
11260
  () => span.end()
11178
11261
  );
11179
11262
  }
11263
+ async lazyParentIds() {
11264
+ return {
11265
+ kind: "experiment",
11266
+ project_id: (await this.project).id,
11267
+ experiment_id: await this.id
11268
+ };
11269
+ }
11180
11270
  /**
11181
11271
  * Lower-level alternative to `traced`, which does not automatically end the span or mark it as current.
11182
11272
  *
@@ -11184,13 +11274,8 @@ var Experiment = class {
11184
11274
  */
11185
11275
  startSpan(args) {
11186
11276
  const { name, ...argsRest } = args ?? {};
11187
- const parentIds = (async () => ({
11188
- kind: "experiment",
11189
- project_id: (await this.project).id,
11190
- experiment_id: await this.id
11191
- }))();
11192
11277
  return new SpanImpl({
11193
- parentIds,
11278
+ parentIds: this.lazyParentIds(),
11194
11279
  bgLogger: this.bgLogger,
11195
11280
  name: name ?? "root",
11196
11281
  ...argsRest
@@ -11252,6 +11337,20 @@ var Experiment = class {
11252
11337
  metrics
11253
11338
  };
11254
11339
  }
11340
+ /**
11341
+ * Log feedback to an event in the experiment. Feedback is used to save feedback scores, set an expected value, or add a comment.
11342
+ *
11343
+ * @param event
11344
+ * @param event.id The id of the event to log feedback for. This is the `id` returned by `log` or accessible as the `id` field of a span.
11345
+ * @param event.scores (Optional) a dictionary of numeric values (between 0 and 1) to log. These scores will be merged into the existing scores for the event.
11346
+ * @param event.expected (Optional) the ground truth value (an arbitrary, JSON serializable object) that you'd compare to `output` to determine if your `output` value is correct or not.
11347
+ * @param event.comment (Optional) an optional comment string to log about the event.
11348
+ * @param event.metadata (Optional) a dictionary with additional data about the feedback. If you have a `user_id`, you can log it here and access it in the Braintrust UI.
11349
+ * @param event.source (Optional) the source of the feedback. Must be one of "external" (default), "app", or "api".
11350
+ */
11351
+ logFeedback(event) {
11352
+ logFeedbackImpl(this.bgLogger, this.lazyParentIds(), event);
11353
+ }
11255
11354
  /**
11256
11355
  * Flush any pending rows to the server.
11257
11356
  */
@@ -11302,10 +11401,12 @@ var SpanImpl = class _SpanImpl {
11302
11401
  this.rowIds = {
11303
11402
  id,
11304
11403
  span_id,
11305
- root_span_id: args.parentSpanInfo?.root_span_id ?? span_id
11404
+ root_span_id: "parentSpanInfo" in args && args.parentSpanInfo?.root_span_id ? args.parentSpanInfo.root_span_id : span_id
11306
11405
  };
11307
- if (args.parentSpanInfo) {
11406
+ if ("parentSpanInfo" in args && args.parentSpanInfo?.span_id) {
11308
11407
  this.internalData.span_parents = [args.parentSpanInfo.span_id];
11408
+ } else if ("parentId" in args && !isEmpty(args.parentId)) {
11409
+ this.rowIds[PARENT_ID_FIELD] = args.parentId;
11309
11410
  }
11310
11411
  this.isMerge = false;
11311
11412
  const { id: _id, ...eventRest } = args.event ?? {};
@@ -11329,16 +11430,26 @@ var SpanImpl = class _SpanImpl {
11329
11430
  if (sanitizedAndInternalData.metrics?.end) {
11330
11431
  this.loggedEndTime = sanitizedAndInternalData.metrics?.end;
11331
11432
  }
11433
+ const parentIds = (async () => {
11434
+ const { kind, ...ids } = await this.parentIds;
11435
+ return ids;
11436
+ })();
11332
11437
  const record = (async () => {
11333
11438
  return {
11334
11439
  ...sanitizedAndInternalData,
11335
11440
  ...this.rowIds,
11336
- ...await this.parentIds,
11441
+ ...await parentIds,
11337
11442
  [IS_MERGE_FIELD]: this.isMerge
11338
11443
  };
11339
11444
  })();
11340
11445
  this.bgLogger.log([record]);
11341
11446
  }
11447
+ logFeedback(event) {
11448
+ logFeedbackImpl(this.bgLogger, this.parentIds, {
11449
+ ...event,
11450
+ id: this.id
11451
+ });
11452
+ }
11342
11453
  traced(callback, args) {
11343
11454
  const { setCurrent, ...argsRest } = args ?? {};
11344
11455
  const span = this.startSpan(argsRest);
@@ -11771,7 +11882,7 @@ async function runEvaluator(experiment, evaluator, progressReporter, filters) {
11771
11882
  }
11772
11883
  span.log({ input: datum.input, output });
11773
11884
  },
11774
- { name: "task" }
11885
+ { name: "task", spanAttributes: { type: SpanTypeAttribute.TASK } }
11775
11886
  );
11776
11887
  rootSpan.log({ output });
11777
11888
  const scoringArgs = { ...datum, metadata, output };
@@ -11794,6 +11905,9 @@ async function runEvaluator(experiment, evaluator, progressReporter, filters) {
11794
11905
  },
11795
11906
  {
11796
11907
  name: score.name || `scorer_${score_idx}`,
11908
+ spanAttributes: {
11909
+ type: SpanTypeAttribute.SCORE
11910
+ },
11797
11911
  event: { input: scoringArgs }
11798
11912
  }
11799
11913
  );
@@ -11833,6 +11947,9 @@ async function runEvaluator(experiment, evaluator, progressReporter, filters) {
11833
11947
  } else {
11834
11948
  return await experiment.traced(callback, {
11835
11949
  name: "eval",
11950
+ spanAttributes: {
11951
+ type: SpanTypeAttribute.EVAL
11952
+ },
11836
11953
  event: {
11837
11954
  input: datum.input,
11838
11955
  expected: datum.expected
@@ -15985,7 +16102,9 @@ async function getRepoStatus() {
15985
16102
  async () => (await git.raw(["rev-parse", "--abbrev-ref", "HEAD"])).trim()
15986
16103
  );
15987
16104
  if (dirty) {
15988
- git_diff = await attempt(async () => truncateToByteLimit(await git.raw(["diff", "HEAD"])));
16105
+ git_diff = await attempt(
16106
+ async () => truncateToByteLimit(await git.raw(["diff", "HEAD"]))
16107
+ );
15989
16108
  }
15990
16109
  return {
15991
16110
  commit,