braintrust 0.0.93 → 0.0.95

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/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.93",
9068
+ version: "0.0.95",
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.13",
9111
+ "@braintrust/core": "^0.0.14",
9112
9112
  argparse: "^2.0.1",
9113
9113
  chalk: "^4.1.2",
9114
9114
  "cli-progress": "^3.12.0",
@@ -9128,7 +9128,7 @@ var esbuild = __toESM(require("esbuild"));
9128
9128
  var import_fs = __toESM(require("fs"));
9129
9129
  var import_os = __toESM(require("os"));
9130
9130
  var import_path = __toESM(require("path"));
9131
- var import_util2 = __toESM(require("util"));
9131
+ var import_util3 = __toESM(require("util"));
9132
9132
  var fsWalk = __toESM(require_out3());
9133
9133
 
9134
9134
  // ../../node_modules/.pnpm/minimatch@9.0.3/node_modules/minimatch/dist/mjs/index.js
@@ -10644,6 +10644,21 @@ function getCurrentUnixTimestamp() {
10644
10644
  function isEmpty(a) {
10645
10645
  return a === void 0 || a === null;
10646
10646
  }
10647
+ var LazyValue = class {
10648
+ constructor(callable) {
10649
+ this.value = {
10650
+ hasComputed: false
10651
+ };
10652
+ this.callable = callable;
10653
+ }
10654
+ async get() {
10655
+ if (this.value.hasComputed) {
10656
+ return this.value.val;
10657
+ }
10658
+ this.value = { hasComputed: true, val: await this.callable() };
10659
+ return this.value.val;
10660
+ }
10661
+ };
10647
10662
 
10648
10663
  // src/logger.ts
10649
10664
  var NoopSpan = class {
@@ -10858,25 +10873,25 @@ function logFeedbackImpl(bgLogger, parentIds, {
10858
10873
  updateEvent = Object.fromEntries(
10859
10874
  Object.entries(updateEvent).filter(([_, v]) => !isEmpty(v))
10860
10875
  );
10861
- const trueParentIds = (async () => {
10862
- const { kind, ...ids } = await parentIds;
10876
+ const trueParentIds = new LazyValue(async () => {
10877
+ const { kind, ...ids } = await parentIds.get();
10863
10878
  return ids;
10864
- })();
10879
+ });
10865
10880
  if (Object.keys(updateEvent).length > 0) {
10866
- const record = (async () => {
10881
+ const record = new LazyValue(async () => {
10867
10882
  return {
10868
10883
  id,
10869
10884
  ...updateEvent,
10870
- ...await trueParentIds,
10885
+ ...await trueParentIds.get(),
10871
10886
  [AUDIT_SOURCE_FIELD]: source,
10872
10887
  [AUDIT_METADATA_FIELD]: metadata,
10873
10888
  [IS_MERGE_FIELD]: true
10874
10889
  };
10875
- })();
10890
+ });
10876
10891
  bgLogger.log([record]);
10877
10892
  }
10878
10893
  if (!isEmpty(comment)) {
10879
- const record = (async () => {
10894
+ const record = new LazyValue(async () => {
10880
10895
  return {
10881
10896
  id: v4_default(),
10882
10897
  created: (/* @__PURE__ */ new Date()).toISOString(),
@@ -10888,11 +10903,11 @@ function logFeedbackImpl(bgLogger, parentIds, {
10888
10903
  comment: {
10889
10904
  text: comment
10890
10905
  },
10891
- ...await trueParentIds,
10906
+ ...await trueParentIds.get(),
10892
10907
  [AUDIT_SOURCE_FIELD]: source,
10893
10908
  [AUDIT_METADATA_FIELD]: metadata
10894
10909
  };
10895
- })();
10910
+ });
10896
10911
  bgLogger.log([record]);
10897
10912
  }
10898
10913
  }
@@ -10924,9 +10939,20 @@ var BackgroundLogger = class {
10924
10939
  }
10925
10940
  async flush_once(batchSize = DefaultBatchSize) {
10926
10941
  this.active_flush_resolved = false;
10927
- const itemPromises = this.items;
10942
+ const itemLazyValues = this.items;
10928
10943
  this.items = [];
10929
- const allItems = mergeRowBatch(await Promise.all(itemPromises)).reverse();
10944
+ const allItems = await (async () => {
10945
+ try {
10946
+ const itemPromises = itemLazyValues.map((x) => x.get());
10947
+ return mergeRowBatch(await Promise.all(itemPromises)).reverse();
10948
+ } catch (e) {
10949
+ console.warn(
10950
+ "Encountered error when constructing records to flush:\n",
10951
+ e
10952
+ );
10953
+ return [];
10954
+ }
10955
+ })();
10930
10956
  let postPromises = [];
10931
10957
  while (true) {
10932
10958
  const items = [];
@@ -10951,9 +10977,7 @@ var BackgroundLogger = class {
10951
10977
  for (let i = 0; i < NumRetries; i++) {
10952
10978
  const startTime = now();
10953
10979
  try {
10954
- return (await (await this.logConn).post_json("logs", itemsS)).map(
10955
- (res) => res.id
10956
- );
10980
+ return (await (await this.logConn.get()).post_json("logs", itemsS)).map((res) => res.id);
10957
10981
  } catch (e) {
10958
10982
  const retryingText = i + 1 === NumRetries ? "" : " Retrying";
10959
10983
  const errMsg = (() => {
@@ -10999,6 +11023,7 @@ function init(project, options = {}) {
10999
11023
  dataset,
11000
11024
  baseExperiment,
11001
11025
  isPublic,
11026
+ open,
11002
11027
  update,
11003
11028
  appUrl,
11004
11029
  apiKey,
@@ -11006,82 +11031,121 @@ function init(project, options = {}) {
11006
11031
  metadata,
11007
11032
  gitMetadataSettings
11008
11033
  } = options || {};
11009
- const lazyMetadata = (async () => {
11010
- await login({
11011
- orgName,
11012
- apiKey,
11013
- appUrl
11014
- });
11015
- const args = {
11016
- project_name: project,
11017
- org_id: _state.orgId
11018
- };
11019
- if (experiment) {
11020
- args["experiment_name"] = experiment;
11021
- }
11022
- if (description) {
11023
- args["description"] = description;
11034
+ if (open) {
11035
+ if (isEmpty(experiment)) {
11036
+ throw new Error("Cannot open an experiment without specifying its name");
11024
11037
  }
11025
11038
  if (update) {
11026
- args["update"] = update;
11039
+ throw new Error("Cannot open and update an experiment at the same time");
11027
11040
  }
11028
- let mergedGitMetadataSettings = {
11029
- ..._state.gitMetadataSettings || {
11030
- collect: "all"
11041
+ const lazyMetadata2 = new LazyValue(async () => {
11042
+ await login({
11043
+ orgName,
11044
+ apiKey,
11045
+ appUrl
11046
+ });
11047
+ const args = {
11048
+ project_name: project,
11049
+ org_name: _state.orgName,
11050
+ experiment_name: experiment
11051
+ };
11052
+ const response = await _state.apiConn().post_json("api/experiment/get", args);
11053
+ if (response.length === 0) {
11054
+ throw new Error(
11055
+ `Experiment ${experiment} not found in project ${project}.`
11056
+ );
11031
11057
  }
11032
- };
11033
- if (gitMetadataSettings) {
11034
- mergedGitMetadataSettings = mergeGitMetadataSettings(
11035
- mergedGitMetadataSettings,
11036
- gitMetadataSettings
11037
- );
11038
- }
11039
- const repoStatus2 = await isomorph_default.getRepoStatus(gitMetadataSettings);
11040
- if (repoStatus2) {
11041
- args["repo_info"] = repoStatus2;
11042
- }
11043
- if (baseExperiment) {
11044
- args["base_experiment"] = baseExperiment;
11045
- } else {
11046
- args["ancestor_commits"] = await isomorph_default.getPastNAncestors();
11047
- }
11048
- if (dataset !== void 0) {
11049
- args["dataset_id"] = dataset.id;
11050
- args["dataset_version"] = await dataset.version();
11051
- }
11052
- if (isPublic !== void 0) {
11053
- args["public"] = isPublic;
11054
- }
11055
- if (metadata) {
11056
- args["metadata"] = metadata;
11057
- }
11058
- let response = null;
11059
- while (true) {
11060
- try {
11061
- response = await _state.apiConn().post_json("api/experiment/register", args);
11062
- break;
11063
- } catch (e) {
11064
- if (args["base_experiment"] && `${"data" in e && e.data}`.includes("base experiment")) {
11065
- console.warn(`Base experiment ${args["base_experiment"]} not found.`);
11066
- delete args["base_experiment"];
11067
- } else {
11068
- throw e;
11058
+ const info = response[0];
11059
+ return {
11060
+ id: info.id,
11061
+ name: info.name,
11062
+ fullInfo: info
11063
+ };
11064
+ });
11065
+ return new ReadonlyExperiment(
11066
+ lazyMetadata2
11067
+ );
11068
+ }
11069
+ const lazyMetadata = new LazyValue(
11070
+ async () => {
11071
+ await login({
11072
+ orgName,
11073
+ apiKey,
11074
+ appUrl
11075
+ });
11076
+ const args = {
11077
+ project_name: project,
11078
+ org_id: _state.orgId
11079
+ };
11080
+ if (experiment) {
11081
+ args["experiment_name"] = experiment;
11082
+ }
11083
+ if (description) {
11084
+ args["description"] = description;
11085
+ }
11086
+ if (update) {
11087
+ args["update"] = update;
11088
+ }
11089
+ let mergedGitMetadataSettings = {
11090
+ ..._state.gitMetadataSettings || {
11091
+ collect: "all"
11069
11092
  }
11093
+ };
11094
+ if (gitMetadataSettings) {
11095
+ mergedGitMetadataSettings = mergeGitMetadataSettings(
11096
+ mergedGitMetadataSettings,
11097
+ gitMetadataSettings
11098
+ );
11070
11099
  }
11071
- }
11072
- return {
11073
- project: {
11074
- id: response.project.id,
11075
- name: response.project.name,
11076
- fullInfo: response.project
11077
- },
11078
- experiment: {
11079
- id: response.experiment.id,
11080
- name: response.experiment.name,
11081
- fullInfo: response.experiment
11100
+ const repoStatus2 = await isomorph_default.getRepoStatus(gitMetadataSettings);
11101
+ if (repoStatus2) {
11102
+ args["repo_info"] = repoStatus2;
11082
11103
  }
11083
- };
11084
- })();
11104
+ if (baseExperiment) {
11105
+ args["base_experiment"] = baseExperiment;
11106
+ } else {
11107
+ args["ancestor_commits"] = await isomorph_default.getPastNAncestors();
11108
+ }
11109
+ if (dataset !== void 0) {
11110
+ args["dataset_id"] = await dataset.id;
11111
+ args["dataset_version"] = await dataset.version();
11112
+ }
11113
+ if (isPublic !== void 0) {
11114
+ args["public"] = isPublic;
11115
+ }
11116
+ if (metadata) {
11117
+ args["metadata"] = metadata;
11118
+ }
11119
+ let response = null;
11120
+ while (true) {
11121
+ try {
11122
+ response = await _state.apiConn().post_json("api/experiment/register", args);
11123
+ break;
11124
+ } catch (e) {
11125
+ if (args["base_experiment"] && `${"data" in e && e.data}`.includes("base experiment")) {
11126
+ console.warn(
11127
+ `Base experiment ${args["base_experiment"]} not found.`
11128
+ );
11129
+ delete args["base_experiment"];
11130
+ } else {
11131
+ throw e;
11132
+ }
11133
+ }
11134
+ }
11135
+ return {
11136
+ project: {
11137
+ id: response.project.id,
11138
+ name: response.project.name,
11139
+ fullInfo: response.project
11140
+ },
11141
+ experiment: {
11142
+ id: response.experiment.id,
11143
+ name: response.experiment.name,
11144
+ fullInfo: response.experiment
11145
+ }
11146
+ };
11147
+ }
11148
+ );
11085
11149
  const ret = new Experiment(lazyMetadata, dataset);
11086
11150
  if (options.setCurrent ?? true) {
11087
11151
  _state.currentExperiment = ret;
@@ -11206,15 +11270,15 @@ function validateAndSanitizeExperimentLogPartialArgs(event) {
11206
11270
  }
11207
11271
  }
11208
11272
  function validateAndSanitizeExperimentLogFullArgs(event, hasDataset) {
11209
- if ("input" in event && event.input && "inputs" in event && event.inputs || !("input" in event) && !("inputs" in event)) {
11273
+ if ("input" in event && !isEmpty(event.input) && "inputs" in event && !isEmpty(event.inputs) || !("input" in event) && !("inputs" in event)) {
11210
11274
  throw new Error(
11211
11275
  "Exactly one of input or inputs (deprecated) must be specified. Prefer input."
11212
11276
  );
11213
11277
  }
11214
- if (!event.output) {
11278
+ if (isEmpty(event.output)) {
11215
11279
  throw new Error("output must be specified");
11216
11280
  }
11217
- if (!event.scores) {
11281
+ if (isEmpty(event.scores)) {
11218
11282
  throw new Error("scores must be specified");
11219
11283
  }
11220
11284
  if (hasDataset && event.datasetRecordId === void 0) {
@@ -11226,33 +11290,88 @@ function validateAndSanitizeExperimentLogFullArgs(event, hasDataset) {
11226
11290
  }
11227
11291
  return event;
11228
11292
  }
11229
- var Experiment = class {
11293
+ var ObjectFetcher = class {
11294
+ constructor(objectType, pinnedVersion) {
11295
+ this.objectType = objectType;
11296
+ this.pinnedVersion = pinnedVersion;
11297
+ this._fetchedData = void 0;
11298
+ }
11299
+ get id() {
11300
+ throw new Error("ObjectFetcher subclasses must have an 'id' attribute");
11301
+ }
11302
+ async getState() {
11303
+ throw new Error("ObjectFetcher subclasses must have a 'getState' method");
11304
+ }
11305
+ async *fetch() {
11306
+ const records = await this.fetchedData();
11307
+ for (const record of records) {
11308
+ yield record;
11309
+ }
11310
+ }
11311
+ [Symbol.iterator]() {
11312
+ return this.fetch();
11313
+ }
11314
+ async fetchedData() {
11315
+ if (this._fetchedData === void 0) {
11316
+ const state = await this.getState();
11317
+ const resp = await state.logConn().get(`object/${this.objectType}`, {
11318
+ id: await this.id,
11319
+ fmt: "json2",
11320
+ version: this.pinnedVersion
11321
+ });
11322
+ this._fetchedData = await resp.json();
11323
+ }
11324
+ return this._fetchedData || [];
11325
+ }
11326
+ clearCache() {
11327
+ this._fetchedData = void 0;
11328
+ }
11329
+ async version() {
11330
+ if (this.pinnedVersion !== void 0) {
11331
+ return this.pinnedVersion;
11332
+ } else {
11333
+ const fetchedData = await this.fetchedData();
11334
+ let maxVersion = void 0;
11335
+ for (const record of fetchedData) {
11336
+ const xactId = record[TRANSACTION_ID_FIELD];
11337
+ if (maxVersion === void 0 || (xactId ?? xactId > maxVersion)) {
11338
+ maxVersion = xactId;
11339
+ }
11340
+ }
11341
+ return maxVersion;
11342
+ }
11343
+ }
11344
+ };
11345
+ var Experiment = class extends ObjectFetcher {
11230
11346
  constructor(lazyMetadata, dataset) {
11347
+ super("experiment", void 0);
11231
11348
  // For type identification.
11232
11349
  this.kind = "experiment";
11233
11350
  this.lazyMetadata = lazyMetadata;
11234
11351
  this.dataset = dataset;
11235
- const logConn = this.getState().then((state) => state.logConn());
11352
+ const logConn = new LazyValue(
11353
+ () => this.getState().then((state) => state.logConn())
11354
+ );
11236
11355
  this.bgLogger = new BackgroundLogger(logConn);
11237
11356
  this.lastStartTime = getCurrentUnixTimestamp();
11238
11357
  }
11239
11358
  get id() {
11240
11359
  return (async () => {
11241
- return (await this.lazyMetadata).experiment.id;
11360
+ return (await this.lazyMetadata.get()).experiment.id;
11242
11361
  })();
11243
11362
  }
11244
11363
  get name() {
11245
11364
  return (async () => {
11246
- return (await this.lazyMetadata).experiment.name;
11365
+ return (await this.lazyMetadata.get()).experiment.name;
11247
11366
  })();
11248
11367
  }
11249
11368
  get project() {
11250
11369
  return (async () => {
11251
- return (await this.lazyMetadata).project;
11370
+ return (await this.lazyMetadata.get()).project;
11252
11371
  })();
11253
11372
  }
11254
11373
  async getState() {
11255
- await this.lazyMetadata;
11374
+ await this.lazyMetadata.get();
11256
11375
  return _state;
11257
11376
  }
11258
11377
  /**
@@ -11312,12 +11431,32 @@ var Experiment = class {
11312
11431
  startSpan(args) {
11313
11432
  const { name, ...argsRest } = args ?? {};
11314
11433
  return new SpanImpl({
11315
- parentIds: this.lazyParentIds(),
11434
+ parentIds: new LazyValue(() => this.lazyParentIds()),
11316
11435
  bgLogger: this.bgLogger,
11317
11436
  name: name ?? "root",
11318
11437
  ...argsRest
11319
11438
  });
11320
11439
  }
11440
+ async fetchBaseExperiment() {
11441
+ const state = await this.getState();
11442
+ const conn = state.apiConn();
11443
+ try {
11444
+ const resp = await conn.post("/api/base_experiment/get_id", {
11445
+ id: await this.id
11446
+ });
11447
+ const base = await resp.json();
11448
+ return {
11449
+ id: base["base_exp_id"],
11450
+ name: base["base_exp_name"]
11451
+ };
11452
+ } catch (e) {
11453
+ if (e instanceof FailedHTTPResponse && e.status === 400) {
11454
+ return null;
11455
+ } else {
11456
+ throw e;
11457
+ }
11458
+ }
11459
+ }
11321
11460
  /**
11322
11461
  * Summarize the experiment, including the scores (compared to the closest reference experiment) and metadata.
11323
11462
  *
@@ -11341,14 +11480,10 @@ var Experiment = class {
11341
11480
  let comparisonExperimentName = void 0;
11342
11481
  if (summarizeScores) {
11343
11482
  if (comparisonExperimentId === void 0) {
11344
- const conn = state.logConn();
11345
- const resp = await conn.get("/crud/base_experiments", {
11346
- id: await this.id
11347
- });
11348
- const base_experiments = await resp.json();
11349
- if (base_experiments.length > 0) {
11350
- comparisonExperimentId = base_experiments[0]["base_exp_id"];
11351
- comparisonExperimentName = base_experiments[0]["base_exp_name"];
11483
+ const baseExperiment = await this.fetchBaseExperiment();
11484
+ if (baseExperiment !== null) {
11485
+ comparisonExperimentId = baseExperiment.id;
11486
+ comparisonExperimentName = baseExperiment.name;
11352
11487
  }
11353
11488
  }
11354
11489
  if (comparisonExperimentId !== void 0) {
@@ -11386,7 +11521,11 @@ var Experiment = class {
11386
11521
  * @param event.source (Optional) the source of the feedback. Must be one of "external" (default), "app", or "api".
11387
11522
  */
11388
11523
  logFeedback(event) {
11389
- logFeedbackImpl(this.bgLogger, this.lazyParentIds(), event);
11524
+ logFeedbackImpl(
11525
+ this.bgLogger,
11526
+ new LazyValue(() => this.lazyParentIds()),
11527
+ event
11528
+ );
11390
11529
  }
11391
11530
  /**
11392
11531
  * Flush any pending rows to the server.
@@ -11404,6 +11543,40 @@ var Experiment = class {
11404
11543
  return this.id;
11405
11544
  }
11406
11545
  };
11546
+ var ReadonlyExperiment = class extends ObjectFetcher {
11547
+ constructor(lazyMetadata) {
11548
+ super("experiment", void 0);
11549
+ this.lazyMetadata = lazyMetadata;
11550
+ }
11551
+ get id() {
11552
+ return (async () => {
11553
+ return (await this.lazyMetadata.get()).id;
11554
+ })();
11555
+ }
11556
+ get name() {
11557
+ return (async () => {
11558
+ return (await this.lazyMetadata.get()).name;
11559
+ })();
11560
+ }
11561
+ async getState() {
11562
+ await this.lazyMetadata.get();
11563
+ return _state;
11564
+ }
11565
+ async *asDataset() {
11566
+ const records = this.fetch();
11567
+ for await (const record of records) {
11568
+ if (record.root_span_id !== record.span_id) {
11569
+ continue;
11570
+ }
11571
+ const { output, expected } = record;
11572
+ yield {
11573
+ input: record.input,
11574
+ expected: expected ?? output
11575
+ };
11576
+ }
11577
+ }
11578
+ };
11579
+ var executionCounter = 0;
11407
11580
  var SpanImpl = class _SpanImpl {
11408
11581
  // root_experiment should only be specified for a root span. parent_span
11409
11582
  // should only be specified for non-root spans.
@@ -11429,7 +11602,11 @@ var SpanImpl = class _SpanImpl {
11429
11602
  start: args.startTime ?? getCurrentUnixTimestamp()
11430
11603
  },
11431
11604
  context: { ...callerLocation },
11432
- span_attributes: { ...args.spanAttributes, name },
11605
+ span_attributes: {
11606
+ ...args.spanAttributes,
11607
+ name,
11608
+ exec_counter: executionCounter++
11609
+ },
11433
11610
  created: (/* @__PURE__ */ new Date()).toISOString()
11434
11611
  };
11435
11612
  this.parentIds = args.parentIds;
@@ -11467,18 +11644,18 @@ var SpanImpl = class _SpanImpl {
11467
11644
  if (sanitizedAndInternalData.metrics?.end) {
11468
11645
  this.loggedEndTime = sanitizedAndInternalData.metrics?.end;
11469
11646
  }
11470
- const parentIds = (async () => {
11471
- const { kind, ...ids } = await this.parentIds;
11647
+ const parentIds = new LazyValue(async () => {
11648
+ const { kind, ...ids } = await this.parentIds.get();
11472
11649
  return ids;
11473
- })();
11474
- const record = (async () => {
11650
+ });
11651
+ const record = new LazyValue(async () => {
11475
11652
  return {
11476
11653
  ...sanitizedAndInternalData,
11477
11654
  ...this.rowIds,
11478
- ...await parentIds,
11655
+ ...await parentIds.get(),
11479
11656
  [IS_MERGE_FIELD]: this.isMerge
11480
11657
  };
11481
- })();
11658
+ });
11482
11659
  this.bgLogger.log([record]);
11483
11660
  }
11484
11661
  logFeedback(event) {
@@ -11527,207 +11704,6 @@ var SpanImpl = class _SpanImpl {
11527
11704
  return this.end(args);
11528
11705
  }
11529
11706
  };
11530
- var Dataset = class {
11531
- constructor(lazyMetadata, pinnedVersion) {
11532
- this._fetchedData = void 0;
11533
- this.lazyMetadata = lazyMetadata;
11534
- this.pinnedVersion = pinnedVersion;
11535
- const logConn = this.getState().then((state) => state.logConn());
11536
- this.bgLogger = new BackgroundLogger(logConn);
11537
- }
11538
- get id() {
11539
- return (async () => {
11540
- return (await this.lazyMetadata).dataset.id;
11541
- })();
11542
- }
11543
- get name() {
11544
- return (async () => {
11545
- return (await this.lazyMetadata).dataset.name;
11546
- })();
11547
- }
11548
- get project() {
11549
- return (async () => {
11550
- return (await this.lazyMetadata).project;
11551
- })();
11552
- }
11553
- async getState() {
11554
- await this.lazyMetadata;
11555
- return _state;
11556
- }
11557
- /**
11558
- * Insert a single record to the dataset. The record will be batched and uploaded behind the scenes. If you pass in an `id`,
11559
- * and a record with that `id` already exists, it will be overwritten (upsert).
11560
- *
11561
- * @param event The event to log.
11562
- * @param event.input The argument that uniquely define an input case (an arbitrary, JSON serializable object).
11563
- * @param event.output The output of your application, including post-processing (an arbitrary, JSON serializable object).
11564
- * @param event.metadata (Optional) a dictionary with additional data about the test example, model outputs, or just
11565
- * about anything else that's relevant, that you can use to help find and analyze examples later. For example, you could log the
11566
- * `prompt`, example's `id`, or anything else that would be useful to slice/dice later. The values in `metadata` can be any
11567
- * JSON-serializable type, but its keys must be strings.
11568
- * @param event.id (Optional) a unique identifier for the event. If you don't provide one, Braintrust will generate one for you.
11569
- * @returns The `id` of the logged record.
11570
- */
11571
- insert({
11572
- input,
11573
- output,
11574
- metadata,
11575
- id
11576
- }) {
11577
- if (metadata !== void 0) {
11578
- for (const key of Object.keys(metadata)) {
11579
- if (typeof key !== "string") {
11580
- throw new Error("metadata keys must be strings");
11581
- }
11582
- }
11583
- }
11584
- const rowId = id || v4_default();
11585
- const args = (async () => ({
11586
- id: rowId,
11587
- inputs: input,
11588
- output,
11589
- project_id: (await this.project).id,
11590
- dataset_id: await this.id,
11591
- created: (/* @__PURE__ */ new Date()).toISOString(),
11592
- metadata
11593
- }))();
11594
- this.bgLogger.log([args]);
11595
- return rowId;
11596
- }
11597
- delete(id) {
11598
- const args = (async () => ({
11599
- id,
11600
- project_id: (await this.project).id,
11601
- dataset_id: await this.id,
11602
- created: (/* @__PURE__ */ new Date()).toISOString(),
11603
- _object_delete: true
11604
- }))();
11605
- this.bgLogger.log([args]);
11606
- return id;
11607
- }
11608
- /**
11609
- * Summarize the dataset, including high level metrics about its size and other metadata.
11610
- * @param summarizeData Whether to summarize the data. If false, only the metadata will be returned.
11611
- * @returns `DatasetSummary`
11612
- * @returns A summary of the dataset.
11613
- */
11614
- async summarize(options = {}) {
11615
- let { summarizeData = true } = options || {};
11616
- await this.bgLogger.flush();
11617
- const state = await this.getState();
11618
- const projectUrl = `${state.appUrl}/app/${encodeURIComponent(
11619
- state.orgName
11620
- )}/p/${encodeURIComponent((await this.project).name)}`;
11621
- const datasetUrl = `${projectUrl}/d/${encodeURIComponent(await this.name)}`;
11622
- let dataSummary = void 0;
11623
- if (summarizeData) {
11624
- dataSummary = await state.logConn().get_json(
11625
- "dataset-summary",
11626
- {
11627
- dataset_id: await this.id
11628
- },
11629
- 3
11630
- );
11631
- }
11632
- return {
11633
- projectName: (await this.project).name,
11634
- datasetName: await this.name,
11635
- projectUrl,
11636
- datasetUrl,
11637
- dataSummary
11638
- };
11639
- }
11640
- /**
11641
- * Fetch all records in the dataset.
11642
- *
11643
- * @example
11644
- * ```
11645
- * // Use an async iterator to fetch all records in the dataset.
11646
- * for await (const record of dataset.fetch()) {
11647
- * console.log(record);
11648
- * }
11649
- *
11650
- * // You can also iterate over the dataset directly.
11651
- * for await (const record of dataset) {
11652
- * console.log(record);
11653
- * }
11654
- * ```
11655
- *
11656
- * @returns An iterator over the dataset's records.
11657
- */
11658
- async *fetch() {
11659
- const records = await this.fetchedData();
11660
- for (const record of records) {
11661
- yield {
11662
- id: record.id,
11663
- input: record.input && JSON.parse(record.input),
11664
- output: record.input && JSON.parse(record.output),
11665
- metadata: record.metadata && JSON.parse(record.metadata)
11666
- };
11667
- }
11668
- this.clearCache();
11669
- }
11670
- /**
11671
- * Fetch all records in the dataset.
11672
- *
11673
- * @example
11674
- * ```
11675
- * // Use an async iterator to fetch all records in the dataset.
11676
- * for await (const record of dataset) {
11677
- * console.log(record);
11678
- * }
11679
- * ```
11680
- */
11681
- [Symbol.asyncIterator]() {
11682
- return this.fetch();
11683
- }
11684
- async fetchedData() {
11685
- if (this._fetchedData === void 0) {
11686
- const state = await this.getState();
11687
- const resp = await state.logConn().get("object/dataset", {
11688
- id: await this.id,
11689
- fmt: "json",
11690
- version: this.pinnedVersion
11691
- });
11692
- const text = await resp.text();
11693
- this._fetchedData = text.split("\n").filter((x) => x.trim() !== "").map((x) => JSON.parse(x));
11694
- }
11695
- return this._fetchedData || [];
11696
- }
11697
- clearCache() {
11698
- this._fetchedData = void 0;
11699
- }
11700
- async version() {
11701
- if (this.pinnedVersion !== void 0) {
11702
- return this.pinnedVersion;
11703
- } else {
11704
- const fetchedData = await this.fetchedData();
11705
- let maxVersion = void 0;
11706
- for (const record of fetchedData) {
11707
- const xactId = record[TRANSACTION_ID_FIELD];
11708
- if (maxVersion === void 0 || (xactId ?? xactId > maxVersion)) {
11709
- maxVersion = xactId;
11710
- }
11711
- }
11712
- return maxVersion;
11713
- }
11714
- }
11715
- /**
11716
- * Flush any pending rows to the server.
11717
- */
11718
- async flush() {
11719
- return await this.bgLogger.flush();
11720
- }
11721
- /**
11722
- * This function is deprecated. You can simply remove it from your code.
11723
- */
11724
- async close() {
11725
- console.warn(
11726
- "close is deprecated and will be removed in a future version of braintrust. It is now a no-op and can be removed"
11727
- );
11728
- return this.id;
11729
- }
11730
- };
11731
11707
 
11732
11708
  // src/progress.ts
11733
11709
  var cliProgress = __toESM(require_cli_progress());
@@ -11844,6 +11820,12 @@ var GlobalPaths = findGlobalPaths();
11844
11820
  // src/framework.ts
11845
11821
  var import_chalk = __toESM(require_source());
11846
11822
  var import_pluralize = __toESM(require_pluralize());
11823
+ function initExperiment(projectName, options = {}) {
11824
+ return init(projectName, {
11825
+ ...options,
11826
+ setCurrent: false
11827
+ });
11828
+ }
11847
11829
  globalThis._evals = {};
11848
11830
  function serializeJSONWithPlainString(v) {
11849
11831
  if (typeof v === "string") {
@@ -11890,10 +11872,37 @@ async function runEvaluator(experiment, evaluator, progressReporter, filters) {
11890
11872
  if (typeof evaluator.data === "string") {
11891
11873
  throw new Error("Unimplemented: string data paths");
11892
11874
  }
11893
- const dataResult = evaluator.data();
11894
- let data = null;
11875
+ let dataResult = typeof evaluator.data === "function" ? evaluator.data() : evaluator.data;
11876
+ if ("_type" in dataResult) {
11877
+ if (dataResult._type !== "BaseExperiment") {
11878
+ throw new Error("Invalid _type");
11879
+ }
11880
+ if (!experiment) {
11881
+ throw new Error(
11882
+ "Cannot use BaseExperiment() without connecting to Braintrust (you most likely set --no-send-logs)"
11883
+ );
11884
+ }
11885
+ let name = dataResult.name;
11886
+ if (isEmpty(name)) {
11887
+ const baseExperiment = await experiment.fetchBaseExperiment();
11888
+ if (!baseExperiment) {
11889
+ throw new Error("BaseExperiment() failed to fetch base experiment");
11890
+ }
11891
+ name = baseExperiment.name;
11892
+ }
11893
+ dataResult = initExperiment(evaluator.projectName, {
11894
+ experiment: name,
11895
+ open: true
11896
+ }).asDataset();
11897
+ }
11898
+ let data = [];
11895
11899
  if (dataResult instanceof Promise) {
11896
11900
  data = await dataResult;
11901
+ } else if (Symbol.asyncIterator in dataResult) {
11902
+ data = [];
11903
+ for await (const d of dataResult) {
11904
+ data.push(d);
11905
+ }
11897
11906
  } else {
11898
11907
  data = dataResult;
11899
11908
  }
@@ -16054,7 +16063,9 @@ async function getBaseBranchAncestor(remote = void 0) {
16054
16063
  if (git === null) {
16055
16064
  throw new Error("Not in a git repo");
16056
16065
  }
16057
- const { remote: remoteName, branch: baseBranch } = await getBaseBranch(remote);
16066
+ const { remote: remoteName, branch: baseBranch } = await getBaseBranch(
16067
+ remote
16068
+ );
16058
16069
  const isDirty = (await git.diffSummary()).files.length > 0;
16059
16070
  const head = isDirty ? "HEAD" : "HEAD^";
16060
16071
  try {
@@ -16474,7 +16485,7 @@ async function collectFiles(inputPath) {
16474
16485
  files.push(inputPath);
16475
16486
  }
16476
16487
  } else {
16477
- const walked = await import_util2.default.promisify(fsWalk.walk)(inputPath, {
16488
+ const walked = await import_util3.default.promisify(fsWalk.walk)(inputPath, {
16478
16489
  deepFilter: (entry) => {
16479
16490
  return checkMatch(entry.path, null, EXCLUDE);
16480
16491
  },