braintrust 0.0.55 → 0.0.57

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/index.js CHANGED
@@ -8880,11 +8880,11 @@ var require_mime_types = __commonJS({
8880
8880
  }
8881
8881
  return exts[0];
8882
8882
  }
8883
- function lookup(path) {
8884
- if (!path || typeof path !== "string") {
8883
+ function lookup(path2) {
8884
+ if (!path2 || typeof path2 !== "string") {
8885
8885
  return false;
8886
8886
  }
8887
- var extension2 = extname("x." + path).toLowerCase().substr(1);
8887
+ var extension2 = extname("x." + path2).toLowerCase().substr(1);
8888
8888
  if (!extension2) {
8889
8889
  return false;
8890
8890
  }
@@ -9141,7 +9141,7 @@ var require_form_data = __commonJS({
9141
9141
  "node_modules/form-data/lib/form_data.js"(exports, module2) {
9142
9142
  var CombinedStream = require_combined_stream();
9143
9143
  var util2 = require("util");
9144
- var path = require("path");
9144
+ var path2 = require("path");
9145
9145
  var http3 = require("http");
9146
9146
  var https3 = require("https");
9147
9147
  var parseUrl = require("url").parse;
@@ -9268,11 +9268,11 @@ var require_form_data = __commonJS({
9268
9268
  FormData3.prototype._getContentDisposition = function(value, options) {
9269
9269
  var filename, contentDisposition;
9270
9270
  if (typeof options.filepath === "string") {
9271
- filename = path.normalize(options.filepath).replace(/\\/g, "/");
9271
+ filename = path2.normalize(options.filepath).replace(/\\/g, "/");
9272
9272
  } else if (options.filename || value.name || value.path) {
9273
- filename = path.basename(options.filename || value.name || value.path);
9273
+ filename = path2.basename(options.filename || value.name || value.path);
9274
9274
  } else if (value.readable && value.hasOwnProperty("httpVersion")) {
9275
- filename = path.basename(value.client._httpMessage.path || "");
9275
+ filename = path2.basename(value.client._httpMessage.path || "");
9276
9276
  }
9277
9277
  if (filename) {
9278
9278
  contentDisposition = 'filename="' + filename + '"';
@@ -10749,10 +10749,10 @@ var require_src2 = __commonJS({
10749
10749
  var fs_1 = require("fs");
10750
10750
  var debug_1 = __importDefault(require_src());
10751
10751
  var log2 = debug_1.default("@kwsites/file-exists");
10752
- function check(path, isFile2, isDirectory) {
10753
- log2(`checking %s`, path);
10752
+ function check(path2, isFile2, isDirectory) {
10753
+ log2(`checking %s`, path2);
10754
10754
  try {
10755
- const stat = fs_1.statSync(path);
10755
+ const stat = fs_1.statSync(path2);
10756
10756
  if (stat.isFile() && isFile2) {
10757
10757
  log2(`[OK] path represents a file`);
10758
10758
  return true;
@@ -10772,8 +10772,8 @@ var require_src2 = __commonJS({
10772
10772
  throw e;
10773
10773
  }
10774
10774
  }
10775
- function exists2(path, type = exports.READABLE) {
10776
- return check(path, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
10775
+ function exists2(path2, type = exports.READABLE) {
10776
+ return check(path2, (type & exports.FILE) > 0, (type & exports.FOLDER) > 0);
10777
10777
  }
10778
10778
  exports.exists = exists2;
10779
10779
  exports.FILE = 1;
@@ -10844,12 +10844,23 @@ __export(src_exports, {
10844
10844
  Dataset: () => Dataset,
10845
10845
  Eval: () => Eval,
10846
10846
  Experiment: () => Experiment,
10847
+ NoopSpan: () => NoopSpan,
10847
10848
  Project: () => Project,
10849
+ SpanImpl: () => SpanImpl,
10850
+ _internalGetGlobalState: () => _internalGetGlobalState,
10851
+ currentExperiment: () => currentExperiment,
10852
+ currentSpan: () => currentSpan,
10848
10853
  init: () => init,
10849
10854
  initDataset: () => initDataset,
10850
10855
  log: () => log,
10851
10856
  login: () => login,
10852
- summarize: () => summarize
10857
+ noopSpan: () => noopSpan,
10858
+ startSpan: () => startSpan,
10859
+ summarize: () => summarize,
10860
+ traced: () => traced,
10861
+ withCurrent: () => withCurrent,
10862
+ withDataset: () => withDataset,
10863
+ withExperiment: () => withExperiment
10853
10864
  });
10854
10865
  module.exports = __toCommonJS(src_exports);
10855
10866
 
@@ -11305,10 +11316,10 @@ function isVisitable(thing) {
11305
11316
  function removeBrackets(key) {
11306
11317
  return utils_default.endsWith(key, "[]") ? key.slice(0, -2) : key;
11307
11318
  }
11308
- function renderKey(path, key, dots) {
11309
- if (!path)
11319
+ function renderKey(path2, key, dots) {
11320
+ if (!path2)
11310
11321
  return key;
11311
- return path.concat(key).map(function each(token, i) {
11322
+ return path2.concat(key).map(function each(token, i) {
11312
11323
  token = removeBrackets(token);
11313
11324
  return !dots && i ? "[" + token + "]" : token;
11314
11325
  }).join(dots ? "." : "");
@@ -11354,9 +11365,9 @@ function toFormData(obj, formData, options) {
11354
11365
  }
11355
11366
  return value;
11356
11367
  }
11357
- function defaultVisitor(value, key, path) {
11368
+ function defaultVisitor(value, key, path2) {
11358
11369
  let arr = value;
11359
- if (value && !path && typeof value === "object") {
11370
+ if (value && !path2 && typeof value === "object") {
11360
11371
  if (utils_default.endsWith(key, "{}")) {
11361
11372
  key = metaTokens ? key : key.slice(0, -2);
11362
11373
  value = JSON.stringify(value);
@@ -11375,7 +11386,7 @@ function toFormData(obj, formData, options) {
11375
11386
  if (isVisitable(value)) {
11376
11387
  return true;
11377
11388
  }
11378
- formData.append(renderKey(path, key, dots), convertValue(value));
11389
+ formData.append(renderKey(path2, key, dots), convertValue(value));
11379
11390
  return false;
11380
11391
  }
11381
11392
  const stack = [];
@@ -11384,11 +11395,11 @@ function toFormData(obj, formData, options) {
11384
11395
  convertValue,
11385
11396
  isVisitable
11386
11397
  });
11387
- function build(value, path) {
11398
+ function build(value, path2) {
11388
11399
  if (utils_default.isUndefined(value))
11389
11400
  return;
11390
11401
  if (stack.indexOf(value) !== -1) {
11391
- throw Error("Circular reference detected in " + path.join("."));
11402
+ throw Error("Circular reference detected in " + path2.join("."));
11392
11403
  }
11393
11404
  stack.push(value);
11394
11405
  utils_default.forEach(value, function each(el, key) {
@@ -11396,11 +11407,11 @@ function toFormData(obj, formData, options) {
11396
11407
  formData,
11397
11408
  el,
11398
11409
  utils_default.isString(key) ? key.trim() : key,
11399
- path,
11410
+ path2,
11400
11411
  exposedHelpers
11401
11412
  );
11402
11413
  if (result === true) {
11403
- build(el, path ? path.concat(key) : [key]);
11414
+ build(el, path2 ? path2.concat(key) : [key]);
11404
11415
  }
11405
11416
  });
11406
11417
  stack.pop();
@@ -11561,7 +11572,7 @@ var node_default = {
11561
11572
  // node_modules/axios/lib/helpers/toURLEncodedForm.js
11562
11573
  function toURLEncodedForm(data, options) {
11563
11574
  return toFormData_default(data, new node_default.classes.URLSearchParams(), Object.assign({
11564
- visitor: function(value, key, path, helpers) {
11575
+ visitor: function(value, key, path2, helpers) {
11565
11576
  if (node_default.isNode && utils_default.isBuffer(value)) {
11566
11577
  this.append(key, value.toString("base64"));
11567
11578
  return false;
@@ -11590,10 +11601,10 @@ function arrayToObject(arr) {
11590
11601
  return obj;
11591
11602
  }
11592
11603
  function formDataToJSON(formData) {
11593
- function buildPath(path, value, target, index) {
11594
- let name = path[index++];
11604
+ function buildPath(path2, value, target, index) {
11605
+ let name = path2[index++];
11595
11606
  const isNumericKey = Number.isFinite(+name);
11596
- const isLast = index >= path.length;
11607
+ const isLast = index >= path2.length;
11597
11608
  name = !name && utils_default.isArray(target) ? target.length : name;
11598
11609
  if (isLast) {
11599
11610
  if (utils_default.hasOwnProp(target, name)) {
@@ -11606,7 +11617,7 @@ function formDataToJSON(formData) {
11606
11617
  if (!target[name] || !utils_default.isObject(target[name])) {
11607
11618
  target[name] = [];
11608
11619
  }
11609
- const result = buildPath(path, value, target[name], index);
11620
+ const result = buildPath(path2, value, target[name], index);
11610
11621
  if (result && utils_default.isArray(target[name])) {
11611
11622
  target[name] = arrayToObject(target[name]);
11612
11623
  }
@@ -12705,9 +12716,9 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
12705
12716
  auth = urlUsername + ":" + urlPassword;
12706
12717
  }
12707
12718
  auth && headers.delete("authorization");
12708
- let path;
12719
+ let path2;
12709
12720
  try {
12710
- path = buildURL(
12721
+ path2 = buildURL(
12711
12722
  parsed.pathname + parsed.search,
12712
12723
  config.params,
12713
12724
  config.paramsSerializer
@@ -12725,7 +12736,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
12725
12736
  false
12726
12737
  );
12727
12738
  const options = {
12728
- path,
12739
+ path: path2,
12729
12740
  method,
12730
12741
  headers: headers.toJSON(),
12731
12742
  agents: { http: config.httpAgent, https: config.httpsAgent },
@@ -12948,14 +12959,14 @@ var cookies_default = node_default.isStandardBrowserEnv ? (
12948
12959
  // Standard browser envs support document.cookie
12949
12960
  function standardBrowserEnv() {
12950
12961
  return {
12951
- write: function write(name, value, expires, path, domain, secure) {
12962
+ write: function write(name, value, expires, path2, domain, secure) {
12952
12963
  const cookie = [];
12953
12964
  cookie.push(name + "=" + encodeURIComponent(value));
12954
12965
  if (utils_default.isNumber(expires)) {
12955
12966
  cookie.push("expires=" + new Date(expires).toGMTString());
12956
12967
  }
12957
- if (utils_default.isString(path)) {
12958
- cookie.push("path=" + path);
12968
+ if (utils_default.isString(path2)) {
12969
+ cookie.push("path=" + path2);
12959
12970
  }
12960
12971
  if (utils_default.isString(domain)) {
12961
12972
  cookie.push("domain=" + domain);
@@ -13790,14 +13801,31 @@ var {
13790
13801
  mergeConfig: mergeConfig2
13791
13802
  } = axios_default;
13792
13803
 
13804
+ // src/node.ts
13805
+ var import_node_async_hooks = require("node:async_hooks");
13806
+
13793
13807
  // src/isomorph.ts
13808
+ var DefaultAsyncLocalStorage = class {
13809
+ constructor() {
13810
+ }
13811
+ enterWith(_) {
13812
+ }
13813
+ run(_, callback) {
13814
+ return callback();
13815
+ }
13816
+ getStore() {
13817
+ return void 0;
13818
+ }
13819
+ };
13794
13820
  var iso = {
13795
13821
  makeAxios: (conf) => axios_default.create({
13796
13822
  ...conf
13797
13823
  }),
13798
13824
  getRepoStatus: async () => void 0,
13799
13825
  getPastNAncestors: async () => [],
13800
- getEnv: (_name) => void 0
13826
+ getEnv: (_name) => void 0,
13827
+ getCallerLocation: () => void 0,
13828
+ newAsyncLocalStorage: () => new DefaultAsyncLocalStorage()
13801
13829
  };
13802
13830
  var isomorph_default = iso;
13803
13831
 
@@ -13872,8 +13900,8 @@ var __async = (__this, __arguments, generator) => {
13872
13900
  step((generator = generator.apply(__this, __arguments)).next());
13873
13901
  });
13874
13902
  };
13875
- function isPathSpec(path) {
13876
- return path instanceof String && cache.has(path);
13903
+ function isPathSpec(path2) {
13904
+ return path2 instanceof String && cache.has(path2);
13877
13905
  }
13878
13906
  function toPaths(pathSpec) {
13879
13907
  return cache.get(pathSpec) || [];
@@ -13955,8 +13983,8 @@ function toLinesWithContent(input = "", trimmed2 = true, separator = "\n") {
13955
13983
  function forEachLineWithContent(input, callback) {
13956
13984
  return toLinesWithContent(input, true).map((line) => callback(line));
13957
13985
  }
13958
- function folderExists(path) {
13959
- return (0, import_file_exists.exists)(path, import_file_exists.FOLDER);
13986
+ function folderExists(path2) {
13987
+ return (0, import_file_exists.exists)(path2, import_file_exists.FOLDER);
13960
13988
  }
13961
13989
  function append2(target, item) {
13962
13990
  if (Array.isArray(target)) {
@@ -14319,8 +14347,8 @@ function checkIsRepoRootTask() {
14319
14347
  commands,
14320
14348
  format: "utf-8",
14321
14349
  onError,
14322
- parser(path) {
14323
- return /^\.(git)?$/.test(path.trim());
14350
+ parser(path2) {
14351
+ return /^\.(git)?$/.test(path2.trim());
14324
14352
  }
14325
14353
  };
14326
14354
  }
@@ -14711,11 +14739,11 @@ function parseGrep(grep) {
14711
14739
  const paths = /* @__PURE__ */ new Set();
14712
14740
  const results = {};
14713
14741
  forEachLineWithContent(grep, (input) => {
14714
- const [path, line, preview] = input.split(NULL);
14715
- paths.add(path);
14716
- (results[path] = results[path] || []).push({
14742
+ const [path2, line, preview] = input.split(NULL);
14743
+ paths.add(path2);
14744
+ (results[path2] = results[path2] || []).push({
14717
14745
  line: asNumber(line),
14718
- path,
14746
+ path: path2,
14719
14747
  preview
14720
14748
  });
14721
14749
  });
@@ -15332,14 +15360,14 @@ var init_hash_object = __esm({
15332
15360
  init_task();
15333
15361
  }
15334
15362
  });
15335
- function parseInit(bare, path, text) {
15363
+ function parseInit(bare, path2, text) {
15336
15364
  const response = String(text).trim();
15337
15365
  let result;
15338
15366
  if (result = initResponseRegex.exec(response)) {
15339
- return new InitSummary(bare, path, false, result[1]);
15367
+ return new InitSummary(bare, path2, false, result[1]);
15340
15368
  }
15341
15369
  if (result = reInitResponseRegex.exec(response)) {
15342
- return new InitSummary(bare, path, true, result[1]);
15370
+ return new InitSummary(bare, path2, true, result[1]);
15343
15371
  }
15344
15372
  let gitDir = "";
15345
15373
  const tokens = response.split(" ");
@@ -15350,7 +15378,7 @@ function parseInit(bare, path, text) {
15350
15378
  break;
15351
15379
  }
15352
15380
  }
15353
- return new InitSummary(bare, path, /^re/i.test(response), gitDir);
15381
+ return new InitSummary(bare, path2, /^re/i.test(response), gitDir);
15354
15382
  }
15355
15383
  var InitSummary;
15356
15384
  var initResponseRegex;
@@ -15358,9 +15386,9 @@ var reInitResponseRegex;
15358
15386
  var init_InitSummary = __esm({
15359
15387
  "src/lib/responses/InitSummary.ts"() {
15360
15388
  InitSummary = class {
15361
- constructor(bare, path, existing, gitDir) {
15389
+ constructor(bare, path2, existing, gitDir) {
15362
15390
  this.bare = bare;
15363
- this.path = path;
15391
+ this.path = path2;
15364
15392
  this.existing = existing;
15365
15393
  this.gitDir = gitDir;
15366
15394
  }
@@ -15372,7 +15400,7 @@ var init_InitSummary = __esm({
15372
15400
  function hasBareCommand(command) {
15373
15401
  return command.includes(bareCommand);
15374
15402
  }
15375
- function initTask(bare = false, path, customArgs) {
15403
+ function initTask(bare = false, path2, customArgs) {
15376
15404
  const commands = ["init", ...customArgs];
15377
15405
  if (bare && !hasBareCommand(commands)) {
15378
15406
  commands.splice(1, 0, bareCommand);
@@ -15381,7 +15409,7 @@ function initTask(bare = false, path, customArgs) {
15381
15409
  commands,
15382
15410
  format: "utf-8",
15383
15411
  parser(text) {
15384
- return parseInit(commands.includes("--bare"), path, text);
15412
+ return parseInit(commands.includes("--bare"), path2, text);
15385
15413
  }
15386
15414
  };
15387
15415
  }
@@ -16101,12 +16129,12 @@ var init_FileStatusSummary = __esm({
16101
16129
  "src/lib/responses/FileStatusSummary.ts"() {
16102
16130
  fromPathRegex = /^(.+) -> (.+)$/;
16103
16131
  FileStatusSummary = class {
16104
- constructor(path, index, working_dir) {
16105
- this.path = path;
16132
+ constructor(path2, index, working_dir) {
16133
+ this.path = path2;
16106
16134
  this.index = index;
16107
16135
  this.working_dir = working_dir;
16108
16136
  if (index + working_dir === "R") {
16109
- const detail = fromPathRegex.exec(path) || [null, path, path];
16137
+ const detail = fromPathRegex.exec(path2) || [null, path2, path2];
16110
16138
  this.from = detail[1] || "";
16111
16139
  this.path = detail[2] || "";
16112
16140
  }
@@ -16137,14 +16165,14 @@ function splitLine(result, lineStr) {
16137
16165
  default:
16138
16166
  return;
16139
16167
  }
16140
- function data(index, workingDir, path) {
16168
+ function data(index, workingDir, path2) {
16141
16169
  const raw = `${index}${workingDir}`;
16142
16170
  const handler = parsers6.get(raw);
16143
16171
  if (handler) {
16144
- handler(result, path);
16172
+ handler(result, path2);
16145
16173
  }
16146
16174
  if (raw !== "##" && raw !== "!!") {
16147
- result.files.push(new FileStatusSummary(path.replace(/\0.+$/, ""), index, workingDir));
16175
+ result.files.push(new FileStatusSummary(path2.replace(/\0.+$/, ""), index, workingDir));
16148
16176
  }
16149
16177
  }
16150
16178
  }
@@ -16389,8 +16417,8 @@ var init_simple_git_api = __esm({
16389
16417
  }
16390
16418
  return this._runTask(configurationErrorTask("Git.cwd: workingDirectory must be supplied as a string"), next);
16391
16419
  }
16392
- hashObject(path, write) {
16393
- return this._runTask(hashObjectTask(path, write === true), trailingFunctionArgument(arguments));
16420
+ hashObject(path2, write) {
16421
+ return this._runTask(hashObjectTask(path2, write === true), trailingFunctionArgument(arguments));
16394
16422
  }
16395
16423
  init(bare) {
16396
16424
  return this._runTask(initTask(bare === true, this._executor.cwd, getTrailingOptions(arguments)), trailingFunctionArgument(arguments));
@@ -16967,8 +16995,8 @@ __export2(sub_module_exports, {
16967
16995
  subModuleTask: () => subModuleTask,
16968
16996
  updateSubModuleTask: () => updateSubModuleTask
16969
16997
  });
16970
- function addSubModuleTask(repo, path) {
16971
- return subModuleTask(["add", repo, path]);
16998
+ function addSubModuleTask(repo, path2) {
16999
+ return subModuleTask(["add", repo, path2]);
16972
17000
  }
16973
17001
  function initSubModuleTask(customArgs) {
16974
17002
  return subModuleTask(["init", ...customArgs]);
@@ -17234,8 +17262,8 @@ var require_git = __commonJS2({
17234
17262
  }
17235
17263
  return this._runTask(straightThroughStringTask2(command, this._trimmed), next);
17236
17264
  };
17237
- Git2.prototype.submoduleAdd = function(repo, path, then) {
17238
- return this._runTask(addSubModuleTask2(repo, path), trailingFunctionArgument2(arguments));
17265
+ Git2.prototype.submoduleAdd = function(repo, path2, then) {
17266
+ return this._runTask(addSubModuleTask2(repo, path2), trailingFunctionArgument2(arguments));
17239
17267
  };
17240
17268
  Git2.prototype.submoduleUpdate = function(args, then) {
17241
17269
  return this._runTask(updateSubModuleTask2(getTrailingOptions2(arguments, true)), trailingFunctionArgument2(arguments));
@@ -17824,6 +17852,50 @@ async function getRepoStatus() {
17824
17852
  };
17825
17853
  }
17826
17854
 
17855
+ // src/stackutil.ts
17856
+ var path = __toESM(require("path"));
17857
+ function getStackTrace() {
17858
+ const trace = new Error().stack;
17859
+ if (trace === void 0) {
17860
+ return [];
17861
+ }
17862
+ const traceLines = trace.split("\n");
17863
+ const out = [];
17864
+ const stackFrameRegex = /at(.*)\((.*):(\d+):(\d+)\)/;
17865
+ for (const traceLine of traceLines.slice(1)) {
17866
+ const matches = traceLine.match(stackFrameRegex);
17867
+ if (matches === null || matches.length !== 5) {
17868
+ continue;
17869
+ }
17870
+ const entry = {
17871
+ functionName: matches[1].trim(),
17872
+ fileName: matches[2],
17873
+ lineNo: parseInt(matches[3])
17874
+ };
17875
+ if (!isNaN(entry.lineNo)) {
17876
+ out.push(entry);
17877
+ }
17878
+ }
17879
+ return out;
17880
+ }
17881
+ function getCallerLocation() {
17882
+ let thisDir = void 0;
17883
+ const entries = getStackTrace();
17884
+ for (const frame of entries) {
17885
+ if (thisDir === void 0) {
17886
+ thisDir = path.dirname(frame.fileName);
17887
+ }
17888
+ if (path.dirname(frame.fileName) !== thisDir) {
17889
+ return {
17890
+ caller_functionname: frame.functionName,
17891
+ caller_filename: frame.fileName,
17892
+ caller_lineno: frame.lineNo
17893
+ };
17894
+ }
17895
+ }
17896
+ return void 0;
17897
+ }
17898
+
17827
17899
  // src/node.ts
17828
17900
  function configureNode() {
17829
17901
  isomorph_default.makeAxios = (options) => {
@@ -17838,6 +17910,8 @@ function configureNode() {
17838
17910
  isomorph_default.getRepoStatus = getRepoStatus;
17839
17911
  isomorph_default.getPastNAncestors = getPastNAncestors;
17840
17912
  isomorph_default.getEnv = (name) => process.env[name];
17913
+ isomorph_default.getCallerLocation = getCallerLocation;
17914
+ isomorph_default.newAsyncLocalStorage = () => new import_node_async_hooks.AsyncLocalStorage();
17841
17915
  }
17842
17916
 
17843
17917
  // node_modules/uuid/dist/esm-node/rng.js
@@ -17887,17 +17961,126 @@ function v4(options, buf, offset) {
17887
17961
  }
17888
17962
  var v4_default = v4;
17889
17963
 
17964
+ // src/util.ts
17965
+ function runFinally(f, finallyF) {
17966
+ let runSyncCleanup = true;
17967
+ try {
17968
+ const ret = f();
17969
+ if (ret instanceof Promise) {
17970
+ runSyncCleanup = false;
17971
+ return ret.finally(finallyF);
17972
+ } else {
17973
+ return ret;
17974
+ }
17975
+ } finally {
17976
+ if (runSyncCleanup) {
17977
+ finallyF();
17978
+ }
17979
+ }
17980
+ }
17981
+
17890
17982
  // src/logger.ts
17891
- var _state = {
17892
- current_project: null,
17893
- current_experiment: null
17983
+ var NoopSpan = class {
17984
+ constructor() {
17985
+ this.kind = "span";
17986
+ this.id = "";
17987
+ this.span_id = "";
17988
+ this.root_span_id = "";
17989
+ }
17990
+ log(_) {
17991
+ }
17992
+ startSpan(_0, _1) {
17993
+ return this;
17994
+ }
17995
+ traced(_0, callback, _1) {
17996
+ return callback(this);
17997
+ }
17998
+ end(args) {
17999
+ return args?.endTime ?? getCurrentUnixTimestamp();
18000
+ }
18001
+ close(args) {
18002
+ return this.end(args);
18003
+ }
18004
+ };
18005
+ var noopSpan = new NoopSpan();
18006
+ var BraintrustState = class {
18007
+ constructor() {
18008
+ this.id = v4_default();
18009
+ this.currentExperiment = isomorph_default.newAsyncLocalStorage();
18010
+ this.currentSpan = isomorph_default.newAsyncLocalStorage();
18011
+ this.apiUrl = null;
18012
+ this.loginToken = null;
18013
+ this.orgId = null;
18014
+ this.orgName = null;
18015
+ this.logUrl = null;
18016
+ this.loggedIn = false;
18017
+ this._apiConn = null;
18018
+ this._logConn = null;
18019
+ this._userInfo = null;
18020
+ globalThis.__inherited_braintrust_state = this;
18021
+ }
18022
+ apiConn() {
18023
+ if (!this._apiConn) {
18024
+ if (!this.apiUrl) {
18025
+ throw new Error("Must initialize apiUrl before requesting apiConn");
18026
+ }
18027
+ this._apiConn = new HTTPConnection(this.apiUrl);
18028
+ }
18029
+ return this._apiConn;
18030
+ }
18031
+ logConn() {
18032
+ if (!this._logConn) {
18033
+ if (!this.logUrl) {
18034
+ throw new Error("Must initialize logUrl before requesting logConn");
18035
+ }
18036
+ this._logConn = new HTTPConnection(this.logUrl);
18037
+ }
18038
+ return this._logConn;
18039
+ }
18040
+ async userInfo() {
18041
+ if (!this._userInfo) {
18042
+ this._userInfo = await this.logConn().get_json("ping");
18043
+ }
18044
+ return this._userInfo;
18045
+ }
18046
+ setUserInfoIfNull(info) {
18047
+ if (!this._userInfo) {
18048
+ this._userInfo = info;
18049
+ }
18050
+ }
17894
18051
  };
17895
- var API_URL = null;
17896
- var LOGIN_TOKEN = null;
17897
- var ORG_ID = null;
17898
- var ORG_NAME = null;
17899
- var LOG_URL = null;
17900
- var LOGGED_IN = false;
18052
+ var _state = globalThis.__inherited_braintrust_state || new BraintrustState();
18053
+ var _internalGetGlobalState = () => _state;
18054
+ var UnterminatedObjectsHandler = class {
18055
+ constructor() {
18056
+ this.unterminatedObjects = /* @__PURE__ */ new Map();
18057
+ process.on("exit", () => {
18058
+ this.warnUnterminated();
18059
+ });
18060
+ }
18061
+ addUnterminated(obj, createdLocation) {
18062
+ this.unterminatedObjects.set(obj, createdLocation);
18063
+ }
18064
+ removeUnterminated(obj) {
18065
+ this.unterminatedObjects.delete(obj);
18066
+ }
18067
+ warnUnterminated() {
18068
+ if (this.unterminatedObjects.size === 0) {
18069
+ return;
18070
+ }
18071
+ let warningMessage = "WARNING: Did not close the following braintrust objects. We recommend running `.close` on the listed objects, or by running them inside a callback so they are closed automatically:";
18072
+ this.unterminatedObjects.forEach((createdLocation, obj) => {
18073
+ let msg = `
18074
+ Object of type ${obj?.constructor?.name}`;
18075
+ if (createdLocation) {
18076
+ msg += ` created at ${JSON.stringify(createdLocation)}`;
18077
+ }
18078
+ warningMessage += msg;
18079
+ });
18080
+ console.warn(warningMessage);
18081
+ }
18082
+ };
18083
+ var unterminatedObjects = new UnterminatedObjectsHandler();
17901
18084
  var TRANSACTION_ID_FIELD = "_xact_id";
17902
18085
  var HTTPConnection = class _HTTPConnection {
17903
18086
  constructor(base_url) {
@@ -17909,9 +18092,7 @@ var HTTPConnection = class _HTTPConnection {
17909
18092
  async ping() {
17910
18093
  try {
17911
18094
  const resp = await this.get("ping");
17912
- if (_var_user_info === null) {
17913
- _var_user_info = resp.data;
17914
- }
18095
+ _state.setUserInfoIfNull(resp.data);
17915
18096
  return resp.status === 200;
17916
18097
  } catch (e) {
17917
18098
  return false;
@@ -17936,12 +18117,12 @@ var HTTPConnection = class _HTTPConnection {
17936
18117
  }
17937
18118
  this.session = isomorph_default.makeAxios({ headers });
17938
18119
  }
17939
- async get(path, params = void 0) {
17940
- return await this.session.get(_urljoin(this.base_url, path), { params });
18120
+ async get(path2, params = void 0) {
18121
+ return await this.session.get(_urljoin(this.base_url, path2), { params });
17941
18122
  }
17942
- async post(path, params = void 0, config = void 0) {
18123
+ async post(path2, params = void 0, config = void 0) {
17943
18124
  return await this.session.post(
17944
- _urljoin(this.base_url, path),
18125
+ _urljoin(this.base_url, path2),
17945
18126
  params,
17946
18127
  config
17947
18128
  );
@@ -17971,32 +18152,6 @@ var HTTPConnection = class _HTTPConnection {
17971
18152
  return resp.data;
17972
18153
  }
17973
18154
  };
17974
- var _api_conn = null;
17975
- function api_conn() {
17976
- if (!_api_conn) {
17977
- _api_conn = new HTTPConnection(API_URL);
17978
- }
17979
- return _api_conn;
17980
- }
17981
- var _log_conn = null;
17982
- function log_conn() {
17983
- if (!_log_conn) {
17984
- _log_conn = new HTTPConnection(LOG_URL);
17985
- }
17986
- return _log_conn;
17987
- }
17988
- var _var_user_info = null;
17989
- async function _user_info() {
17990
- if (_var_user_info === null) {
17991
- _var_user_info = await log_conn().get_json("ping");
17992
- }
17993
- return _var_user_info;
17994
- }
17995
- function clear_cached_globals() {
17996
- _api_conn = null;
17997
- _log_conn = null;
17998
- _var_user_info = null;
17999
- }
18000
18155
  var Project = class {
18001
18156
  constructor(name, id, org_id) {
18002
18157
  this.name = name;
@@ -18041,10 +18196,7 @@ var LogThread = class {
18041
18196
  itemsLen += itemS.length;
18042
18197
  }
18043
18198
  if (items.length > 0) {
18044
- const resp = await log_conn().post_json(
18045
- "logs",
18046
- constructJsonArray(items)
18047
- );
18199
+ const resp = await _state.logConn().post_json("logs", constructJsonArray(items));
18048
18200
  ret = resp.data;
18049
18201
  } else {
18050
18202
  break;
@@ -18085,7 +18237,7 @@ async function init(project, options = {}) {
18085
18237
  apiKey,
18086
18238
  apiUrl
18087
18239
  });
18088
- const ret = await _initExperiment(project, {
18240
+ return await _initExperiment(project, {
18089
18241
  experimentName: experiment,
18090
18242
  description,
18091
18243
  dataset,
@@ -18093,8 +18245,19 @@ async function init(project, options = {}) {
18093
18245
  baseExperiment,
18094
18246
  isPublic
18095
18247
  });
18096
- _state.current_experiment = ret;
18097
- return ret;
18248
+ }
18249
+ async function withExperiment(project, callback, options = {}) {
18250
+ const experiment = await init(project, options);
18251
+ return runFinally(
18252
+ () => {
18253
+ if (options.setCurrent ?? true) {
18254
+ return withCurrent(experiment, () => callback(experiment));
18255
+ } else {
18256
+ return callback(experiment);
18257
+ }
18258
+ },
18259
+ () => experiment.close()
18260
+ );
18098
18261
  }
18099
18262
  async function initDataset(project, options = {}) {
18100
18263
  const {
@@ -18118,6 +18281,13 @@ async function initDataset(project, options = {}) {
18118
18281
  version
18119
18282
  });
18120
18283
  }
18284
+ async function withDataset(project, callback, options = {}) {
18285
+ const dataset = await initDataset(project, options);
18286
+ return runFinally(
18287
+ () => callback(dataset),
18288
+ () => dataset.close()
18289
+ );
18290
+ }
18121
18291
  async function login(options = {}) {
18122
18292
  const {
18123
18293
  apiUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") || "https://www.braintrustdata.com",
@@ -18126,24 +18296,27 @@ async function login(options = {}) {
18126
18296
  disableCache = false
18127
18297
  } = options || {};
18128
18298
  let { forceLogin = false } = options || {};
18129
- if (apiUrl != API_URL || apiKey !== void 0 && HTTPConnection.sanitize_token(apiKey) != LOGIN_TOKEN || orgName !== void 0 && orgName != ORG_NAME) {
18299
+ if (apiUrl != _state.apiUrl || apiKey !== void 0 && HTTPConnection.sanitize_token(apiKey) != _state.loginToken || orgName !== void 0 && orgName != _state.orgName) {
18130
18300
  forceLogin = true;
18131
18301
  }
18132
- if (LOGGED_IN && !forceLogin) {
18302
+ if (_state.loggedIn && !forceLogin) {
18133
18303
  return;
18134
18304
  }
18135
- clear_cached_globals();
18136
- API_URL = apiUrl;
18305
+ _state = new BraintrustState();
18306
+ _state.apiUrl = apiUrl;
18137
18307
  let login_key_info = null;
18138
18308
  let ping_ok = false;
18139
18309
  let conn = null;
18140
18310
  if (apiKey !== void 0) {
18141
- const resp = await axios_default.post(_urljoin(API_URL, `/api/apikey/login`), {
18142
- token: apiKey
18143
- });
18311
+ const resp = await axios_default.post(
18312
+ _urljoin(_state.apiUrl, `/api/apikey/login`),
18313
+ {
18314
+ token: apiKey
18315
+ }
18316
+ );
18144
18317
  const info = resp.data;
18145
18318
  _check_org_info(info.org_info, orgName);
18146
- conn = log_conn();
18319
+ conn = _state.logConn();
18147
18320
  conn.set_token(apiKey);
18148
18321
  ping_ok = await conn.ping();
18149
18322
  } else {
@@ -18158,21 +18331,66 @@ async function login(options = {}) {
18158
18331
  await conn.get("ping");
18159
18332
  }
18160
18333
  conn.make_long_lived();
18161
- api_conn().set_token(apiKey);
18162
- LOGIN_TOKEN = conn.token;
18163
- LOGGED_IN = true;
18334
+ _state.apiConn().set_token(apiKey);
18335
+ _state.loginToken = conn.token;
18336
+ _state.loggedIn = true;
18164
18337
  }
18165
- function log(options) {
18166
- if (!_state.current_experiment) {
18338
+ function log(event) {
18339
+ const currentExperiment2 = _state.currentExperiment.getStore();
18340
+ if (!currentExperiment2) {
18167
18341
  throw new Error("Not initialized. Please call init() first");
18168
18342
  }
18169
- return _state.current_experiment.log(options);
18343
+ return currentExperiment2.log(event);
18170
18344
  }
18171
18345
  async function summarize(options = {}) {
18172
- if (!_state.current_experiment) {
18346
+ const currentExperiment2 = _state.currentExperiment.getStore();
18347
+ if (!currentExperiment2) {
18173
18348
  throw new Error("Not initialized. Please call init() first");
18174
18349
  }
18175
- return await _state.current_experiment.summarize(options);
18350
+ return await currentExperiment2.summarize(options);
18351
+ }
18352
+ function currentExperiment() {
18353
+ return _state.currentExperiment.getStore();
18354
+ }
18355
+ function currentSpan() {
18356
+ return _state.currentSpan.getStore();
18357
+ }
18358
+ function startSpan(args) {
18359
+ const { name: nameOpt, ...argsRest } = args ?? {};
18360
+ const name = (nameOpt ?? isomorph_default.getCallerLocation()?.caller_functionname) || "root";
18361
+ const parentSpan = currentSpan();
18362
+ if (!Object.is(parentSpan, noopSpan)) {
18363
+ return parentSpan.startSpan(name, argsRest);
18364
+ }
18365
+ const experiment = currentExperiment();
18366
+ if (experiment) {
18367
+ return experiment.startSpan({ name, ...argsRest });
18368
+ }
18369
+ return noopSpan;
18370
+ }
18371
+ function traced(callback, args) {
18372
+ const span = startSpan(args);
18373
+ return runFinally(
18374
+ () => {
18375
+ if (args?.setCurrent ?? true) {
18376
+ return withCurrent(span, () => callback(span));
18377
+ } else {
18378
+ return callback(span);
18379
+ }
18380
+ },
18381
+ () => span.end()
18382
+ );
18383
+ }
18384
+ function withCurrent(object, callback) {
18385
+ if (object.kind === "experiment") {
18386
+ return _state.currentExperiment.run(object, callback);
18387
+ } else if (object.kind === "span") {
18388
+ return _state.currentSpan.run(object, callback);
18389
+ } else {
18390
+ throw new Error(
18391
+ `Invalid object of type ${object.constructor.name}`
18392
+ );
18393
+ }
18176
18394
  }
18177
18395
  function _check_org_info(org_info, org_name) {
18178
18396
  if (org_info.length === 0) {
@@ -18180,13 +18398,13 @@ function _check_org_info(org_info, org_name) {
18180
18398
  }
18181
18399
  for (const org of org_info) {
18182
18400
  if (org_name === void 0 || org.name === org_name) {
18183
- ORG_ID = org.id;
18184
- ORG_NAME = org.name;
18185
- LOG_URL = org.api_url;
18401
+ _state.orgId = org.id;
18402
+ _state.orgName = org.name;
18403
+ _state.logUrl = org.api_url;
18186
18404
  break;
18187
18405
  }
18188
18406
  }
18189
- if (ORG_ID === void 0) {
18407
+ if (_state.orgId === void 0) {
18190
18408
  throw new Error(
18191
18409
  `Organization ${org_name} not found. Must be one of ${org_info.map((x) => x.name).join(", ")}`
18192
18410
  );
@@ -18195,6 +18413,82 @@ function _check_org_info(org_info, org_name) {
18195
18413
  function _urljoin(...parts) {
18196
18414
  return parts.map((x) => x.replace(/^\//, "")).join("/");
18197
18415
  }
18416
+ function getCurrentUnixTimestamp() {
18417
+ return (/* @__PURE__ */ new Date()).getTime() / 1e3;
18418
+ }
18419
+ function validateAndSanitizeExperimentLogPartialArgs(event) {
18420
+ if (event.scores) {
18421
+ for (let [name, score] of Object.entries(event.scores)) {
18422
+ if (typeof name !== "string") {
18423
+ throw new Error("score names must be strings");
18424
+ }
18425
+ if (typeof score === "boolean") {
18426
+ score = score ? 1 : 0;
18427
+ event.scores[name] = score;
18428
+ }
18429
+ if (typeof score !== "number") {
18430
+ throw new Error("score values must be numbers");
18431
+ }
18432
+ if (score < 0 || score > 1) {
18433
+ throw new Error("score values must be between 0 and 1");
18434
+ }
18435
+ }
18436
+ }
18437
+ if (event.metadata) {
18438
+ for (const key of Object.keys(event.metadata)) {
18439
+ if (typeof key !== "string") {
18440
+ throw new Error("metadata keys must be strings");
18441
+ }
18442
+ }
18443
+ }
18444
+ if (event.metrics) {
18445
+ for (const key of Object.keys(event.metrics)) {
18446
+ if (typeof key !== "string") {
18447
+ throw new Error("metric keys must be strings");
18448
+ }
18449
+ }
18450
+ for (const forbiddenKey of [
18451
+ "start",
18452
+ "end",
18453
+ "caller_functionname",
18454
+ "caller_filename",
18455
+ "caller_lineno"
18456
+ ]) {
18457
+ if (forbiddenKey in event.metrics) {
18458
+ throw new Error(`Key ${forbiddenKey} may not be specified in metrics`);
18459
+ }
18460
+ }
18461
+ }
18462
+ if ("input" in event && event.input && "inputs" in event && event.inputs) {
18463
+ throw new Error(
18464
+ "Only one of input or inputs (deprecated) can be specified. Prefer input."
18465
+ );
18466
+ }
18467
+ if ("inputs" in event) {
18468
+ const { inputs, ...rest } = event;
18469
+ return { input: inputs, ...rest };
18470
+ } else {
18471
+ return { ...event };
18472
+ }
18473
+ }
18474
+ function validateAndSanitizeExperimentLogFullArgs(event, hasDataset) {
18475
+ if ("input" in event && event.input && "inputs" in event && event.inputs || !("input" in event) && !("inputs" in event)) {
18476
+ throw new Error(
18477
+ "Exactly one of input or inputs (deprecated) must be specified. Prefer input."
18478
+ );
18479
+ }
18480
+ if (!event.scores) {
18481
+ throw new Error("scores must be specified");
18482
+ }
18483
+ if (hasDataset && event.datasetRecordId === void 0) {
18484
+ throw new Error("datasetRecordId must be specified when using a dataset");
18485
+ } else if (!hasDataset && event.datasetRecordId !== void 0) {
18486
+ throw new Error(
18487
+ "datasetRecordId cannot be specified when not using a dataset"
18488
+ );
18489
+ }
18490
+ return event;
18491
+ }
18198
18492
  async function _initExperiment(projectName, {
18199
18493
  experimentName,
18200
18494
  description,
@@ -18210,7 +18504,7 @@ async function _initExperiment(projectName, {
18210
18504
  }) {
18211
18505
  const args = {
18212
18506
  project_name: projectName,
18213
- org_id: ORG_ID
18507
+ org_id: _state.orgId
18214
18508
  };
18215
18509
  if (experimentName) {
18216
18510
  args["experiment_name"] = experimentName;
@@ -18237,10 +18531,10 @@ async function _initExperiment(projectName, {
18237
18531
  if (isPublic !== void 0) {
18238
18532
  args["public"] = isPublic;
18239
18533
  }
18240
- const response = await api_conn().post_json("api/experiment/register", args);
18534
+ const response = await _state.apiConn().post_json("api/experiment/register", args);
18241
18535
  const project = response.project;
18242
18536
  const experiment = response.experiment;
18243
- const user_id = (await _user_info())["id"];
18537
+ const user_id = (await _state.userInfo())["id"];
18244
18538
  return new Experiment(
18245
18539
  project,
18246
18540
  experiment.id,
@@ -18251,106 +18545,71 @@ async function _initExperiment(projectName, {
18251
18545
  }
18252
18546
  var Experiment = class {
18253
18547
  constructor(project, id, name, user_id, dataset) {
18548
+ // For type identification.
18549
+ this.kind = "experiment";
18550
+ this.finished = false;
18254
18551
  this.project = project;
18255
18552
  this.id = id;
18256
18553
  this.name = name;
18257
18554
  this.user_id = user_id;
18258
18555
  this.dataset = dataset;
18259
18556
  this.logger = new LogThread();
18557
+ this.lastStartTime = getCurrentUnixTimestamp();
18558
+ unterminatedObjects.addUnterminated(this, isomorph_default.getCallerLocation());
18260
18559
  }
18261
18560
  /**
18262
18561
  * Log a single event to the experiment. The event will be batched and uploaded behind the scenes.
18263
18562
  *
18264
18563
  * @param event The event to log.
18265
- * @param event.input The arguments that uniquely define a test case (an arbitrary, JSON serializable object). Later on,
18266
- * Braintrust will use the `input` to know whether two test cases are the same between experiments, so they should
18267
- * not contain experiment-specific state. A simple rule of thumb is that if you run the same experiment twice, the
18268
- * `input` should be identical.
18269
- * @param event.output The output of your application, including post-processing (an arbitrary, JSON serializable object),
18270
- * that allows you to determine whether the result is correct or not. For example, in an app that generates SQL queries,
18271
- * the `output` should be the _result_ of the SQL query generated by the model, not the query itself, because there may
18272
- * be multiple valid queries that answer a single question.
18273
- * @param event.expected The ground truth value (an arbitrary, JSON serializable object) that you'd compare to `output` to
18274
- * determine if your `output` value is correct or not. Braintrust currently does not compare `output` to `expected` for
18275
- * you, since there are so many different ways to do that correctly. Instead, these values are just used to help you
18276
- * navigate your experiments while digging into analyses. However, we may later use these values to re-score outputs or
18277
- * fine-tune your models.
18278
- * @param event.scores A dictionary of numeric values (between 0 and 1) to log. The scores should give you a variety of signals
18279
- * that help you determine how accurate the outputs are compared to what you expect and diagnose failures. For example, a
18280
- * summarization app might have one score that tells you how accurate the summary is, and another that measures the word similarity
18281
- * between the generated and grouth truth summary. The word similarity score could help you determine whether the summarization was
18282
- * covering similar concepts or not. You can use these scores to help you sort, filter, and compare experiments.
18283
- * @param event.metadata (Optional) a dictionary with additional data about the test example, model outputs, or just
18284
- * about anything else that's relevant, that you can use to help find and analyze examples later. For example, you could log the
18285
- * `prompt`, example's `id`, or anything else that would be useful to slice/dice later. The values in `metadata` can be any
18286
- * JSON-serializable type, but its keys must be strings.
18287
- * @param event.id (Optional) a unique identifier for the event. If you don't provide one, Braintrust will generate one for you.
18288
- * @param event.inputs (Deprecated) the same as `input` (will be removed in a future version)
18289
- * @returns The `id` of the logged event.
18564
+ * @param event.input: The arguments that uniquely define a test case (an arbitrary, JSON serializable object). Later on, Braintrust will use the `input` to know whether two test cases are the same between experiments, so they should not contain experiment-specific state. A simple rule of thumb is that if you run the same experiment twice, the `input` should be identical.
18565
+ * @param event.output: The output of your application, including post-processing (an arbitrary, JSON serializable object), that allows you to determine whether the result is correct or not. For example, in an app that generates SQL queries, the `output` should be the _result_ of the SQL query generated by the model, not the query itself, because there may be multiple valid queries that answer a single question.
18566
+ * @param event.expected: 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. Braintrust currently does not compare `output` to `expected` for you, since there are so many different ways to do that correctly. Instead, these values are just used to help you navigate your experiments while digging into analyses. However, we may later use these values to re-score outputs or fine-tune your models.
18567
+ * @param event.scores: A dictionary of numeric values (between 0 and 1) to log. The scores should give you a variety of signals that help you determine how accurate the outputs are compared to what you expect and diagnose failures. For example, a summarization app might have one score that tells you how accurate the summary is, and another that measures the word similarity between the generated and grouth truth summary. The word similarity score could help you determine whether the summarization was covering similar concepts or not. You can use these scores to help you sort, filter, and compare experiments.
18568
+ * @param event.metadata: (Optional) a dictionary with additional data about the test example, model outputs, or just about anything else that's relevant, that you can use to help find and analyze examples later. For example, you could log the `prompt`, example's `id`, or anything else that would be useful to slice/dice later. The values in `metadata` can be any JSON-serializable type, but its keys must be strings.
18569
+ * @param event.metrics: (Optional) a dictionary of metrics to log. The following keys are populated automatically and should not be specified: "start", "end", "caller_functionname", "caller_filename", "caller_lineno".
18570
+ * @param event.id: (Optional) a unique identifier for the event. If you don't provide one, BrainTrust will generate one for you.
18571
+ * @param event.dataset_record_id: (Optional) the id of the dataset record that this event is associated with. This field is required if and only if the experiment is associated with a dataset.
18572
+ * @param event.inputs: (Deprecated) the same as `input` (will be removed in a future version).
18573
+ * :returns: The `id` of the logged event.
18290
18574
  */
18291
- log({
18292
- input,
18293
- output,
18294
- expected,
18295
- scores,
18296
- metadata,
18297
- id,
18298
- datasetRecordId,
18299
- inputs
18300
- }) {
18301
- if (input === void 0 && inputs === void 0) {
18302
- throw new Error(
18303
- "Either input or inputs (deprecated) must be specified. Prefer input."
18304
- );
18305
- } else if (input !== void 0 && inputs !== void 0) {
18306
- throw new Error(
18307
- "Only one of input or inputs (deprecated) can be specified. Prefer input."
18308
- );
18309
- }
18310
- for (let [name, score] of Object.entries(scores)) {
18311
- if (typeof name !== "string") {
18312
- throw new Error("score names must be strings");
18313
- }
18314
- if (typeof score === "boolean") {
18315
- score = score ? 1 : 0;
18316
- scores[name] = score;
18317
- }
18318
- if (typeof score !== "number") {
18319
- throw new Error("score values must be numbers");
18320
- }
18321
- if (score < 0 || score > 1) {
18322
- throw new Error("score values must be between 0 and 1");
18323
- }
18324
- }
18325
- if (metadata !== void 0) {
18326
- for (const key of Object.keys(metadata)) {
18327
- if (typeof key !== "string") {
18328
- throw new Error("metadata keys must be strings");
18575
+ log(event) {
18576
+ this.checkNotFinished();
18577
+ event = validateAndSanitizeExperimentLogFullArgs(event, !!this.dataset);
18578
+ const span = this.startSpan({ startTime: this.lastStartTime, event });
18579
+ this.lastStartTime = span.end();
18580
+ return span.id;
18581
+ }
18582
+ /**
18583
+ * Create a new toplevel span. The name parameter is optional and defaults to "root".
18584
+ *
18585
+ * See `Span.startSpan` for full details.
18586
+ */
18587
+ startSpan(args) {
18588
+ this.checkNotFinished();
18589
+ const { name, ...argsRest } = args ?? {};
18590
+ return new SpanImpl({
18591
+ experimentLogger: this.logger,
18592
+ name: name ?? "root",
18593
+ ...argsRest,
18594
+ rootExperiment: this
18595
+ });
18596
+ }
18597
+ /**
18598
+ * Wrapper over `Experiment.startSpan`, which passes the initialized `Span` it to the given callback and ends it afterwards. See `Span.traced` for full details.
18599
+ */
18600
+ traced(callback, args) {
18601
+ const { setCurrent, ...argsRest } = args ?? {};
18602
+ const span = this.startSpan(argsRest);
18603
+ return runFinally(
18604
+ () => {
18605
+ if (setCurrent ?? true) {
18606
+ return withCurrent(span, () => callback(span));
18607
+ } else {
18608
+ return callback(span);
18329
18609
  }
18330
- }
18331
- }
18332
- if (this.dataset && datasetRecordId === void 0) {
18333
- throw new Error("datasetRecordId must be specified when using a dataset");
18334
- } else if (!this.dataset && datasetRecordId !== void 0) {
18335
- throw new Error(
18336
- "datasetRecordId cannot be specified when not using a dataset"
18337
- );
18338
- }
18339
- const args = {
18340
- id: id || v4_default(),
18341
- inputs: input ?? inputs,
18342
- output,
18343
- expected,
18344
- scores,
18345
- project_id: this.project.id,
18346
- experiment_id: this.id,
18347
- user_id: this.user_id,
18348
- created: (/* @__PURE__ */ new Date()).toISOString(),
18349
- dataset_record_id: datasetRecordId,
18350
- metadata
18351
- };
18352
- this.logger.log([args]);
18353
- return args.id;
18610
+ },
18611
+ () => span.end()
18612
+ );
18354
18613
  }
18355
18614
  /**
18356
18615
  * Summarize the experiment, including the scores (compared to the closest reference experiment) and metadata.
@@ -18363,15 +18622,15 @@ var Experiment = class {
18363
18622
  async summarize(options = {}) {
18364
18623
  let { summarizeScores = true, comparisonExperimentId = void 0 } = options || {};
18365
18624
  await this.logger.flush();
18366
- const projectUrl = `${API_URL}/app/${encodeURIComponent(
18367
- ORG_NAME
18625
+ const projectUrl = `${_state.apiUrl}/app/${encodeURIComponent(
18626
+ _state.orgName
18368
18627
  )}/p/${encodeURIComponent(this.project.name)}`;
18369
18628
  const experimentUrl = `${projectUrl}/${encodeURIComponent(this.name)}`;
18370
18629
  let scores = void 0;
18371
18630
  let comparisonExperimentName = void 0;
18372
18631
  if (summarizeScores) {
18373
18632
  if (comparisonExperimentId === void 0) {
18374
- const conn = log_conn();
18633
+ const conn = _state.logConn();
18375
18634
  const resp = await conn.get("/crud/base_experiments", {
18376
18635
  id: this.id
18377
18636
  });
@@ -18382,7 +18641,7 @@ var Experiment = class {
18382
18641
  }
18383
18642
  }
18384
18643
  if (comparisonExperimentId !== void 0) {
18385
- scores = await log_conn().get_json(
18644
+ scores = await _state.logConn().get_json(
18386
18645
  "/experiment-comparison",
18387
18646
  {
18388
18647
  experiment_id: this.id,
@@ -18401,6 +18660,122 @@ var Experiment = class {
18401
18660
  scores
18402
18661
  };
18403
18662
  }
18663
+ /**
18664
+ * Finish the experiment and return its id. After calling close, you may not invoke any further methods on the experiment object.
18665
+ *
18666
+ * Will be invoked automatically if the experiment is wrapped in a callback passed to `braintrust.withExperiment`.
18667
+ *
18668
+ * @returns The experiment id.
18669
+ */
18670
+ async close() {
18671
+ this.checkNotFinished();
18672
+ await this.logger.flush();
18673
+ this.finished = true;
18674
+ unterminatedObjects.removeUnterminated(this);
18675
+ return this.id;
18676
+ }
18677
+ checkNotFinished() {
18678
+ if (this.finished) {
18679
+ throw new Error("Cannot invoke method on finished experiment");
18680
+ }
18681
+ }
18682
+ };
18683
+ var SpanImpl = class _SpanImpl {
18684
+ // root_experiment should only be specified for a root span. parent_span
18685
+ // should only be specified for non-root spans.
18686
+ constructor(args) {
18687
+ this.kind = "span";
18688
+ this.finished = false;
18689
+ this.experimentLogger = args.experimentLogger;
18690
+ const callerLocation = isomorph_default.getCallerLocation();
18691
+ this.internalData = {
18692
+ metrics: {
18693
+ start: args.startTime ?? getCurrentUnixTimestamp(),
18694
+ ...callerLocation
18695
+ },
18696
+ span_attributes: { ...args.spanAttributes, name: args.name }
18697
+ };
18698
+ this.id = args.event?.id ?? v4_default();
18699
+ this.span_id = v4_default();
18700
+ if ("rootExperiment" in args) {
18701
+ this.root_span_id = this.span_id;
18702
+ this._project_id = args.rootExperiment.project.id;
18703
+ this._experiment_id = args.rootExperiment.id;
18704
+ this.internalData = Object.assign(this.internalData, {
18705
+ // TODO: Hopefully we can remove this.
18706
+ user_id: args.rootExperiment.user_id,
18707
+ created: (/* @__PURE__ */ new Date()).toISOString()
18708
+ });
18709
+ } else if ("parentSpan" in args) {
18710
+ this.root_span_id = args.parentSpan.root_span_id;
18711
+ this._project_id = args.parentSpan._project_id;
18712
+ this._experiment_id = args.parentSpan._experiment_id;
18713
+ this.internalData.span_parents = [args.parentSpan.span_id];
18714
+ } else {
18715
+ throw new Error("Must provide either 'rootExperiment' or 'parentSpan'");
18716
+ }
18717
+ this.isMerge = false;
18718
+ const { id, ...eventRest } = args.event ?? {};
18719
+ this.log(eventRest);
18720
+ this.isMerge = true;
18721
+ unterminatedObjects.addUnterminated(this, callerLocation);
18722
+ }
18723
+ log(event) {
18724
+ this.checkNotFinished();
18725
+ const sanitized = validateAndSanitizeExperimentLogPartialArgs(event);
18726
+ const record = {
18727
+ ...sanitized,
18728
+ ...this.internalData,
18729
+ id: this.id,
18730
+ span_id: this.span_id,
18731
+ root_span_id: this.root_span_id,
18732
+ project_id: this._project_id,
18733
+ experiment_id: this._experiment_id,
18734
+ _is_merge: this.isMerge
18735
+ };
18736
+ this.internalData = {};
18737
+ this.experimentLogger.log([record]);
18738
+ }
18739
+ startSpan(name, args) {
18740
+ this.checkNotFinished();
18741
+ return new _SpanImpl({
18742
+ experimentLogger: this.experimentLogger,
18743
+ name,
18744
+ ...args,
18745
+ parentSpan: this
18746
+ });
18747
+ }
18748
+ traced(name, callback, args) {
18749
+ const { setCurrent, ...argsRest } = args ?? {};
18750
+ const span = this.startSpan(name, argsRest);
18751
+ return runFinally(
18752
+ () => {
18753
+ if (setCurrent ?? true) {
18754
+ return withCurrent(span, () => callback(span));
18755
+ } else {
18756
+ return callback(span);
18757
+ }
18758
+ },
18759
+ () => span.end()
18760
+ );
18761
+ }
18762
+ end(args) {
18763
+ this.checkNotFinished();
18764
+ const endTime = args?.endTime ?? getCurrentUnixTimestamp();
18765
+ this.internalData = { metrics: { end: endTime } };
18766
+ this.log({});
18767
+ this.finished = true;
18768
+ unterminatedObjects.removeUnterminated(this);
18769
+ return endTime;
18770
+ }
18771
+ close(args) {
18772
+ return this.end(args);
18773
+ }
18774
+ checkNotFinished() {
18775
+ if (this.finished) {
18776
+ throw new Error("Cannot invoke method on finished span");
18777
+ }
18778
+ }
18404
18779
  };
18405
18780
  async function _initDataset(project_name, {
18406
18781
  name,
@@ -18408,26 +18783,28 @@ async function _initDataset(project_name, {
18408
18783
  version
18409
18784
  } = {}) {
18410
18785
  const args = {
18411
- org_id: ORG_ID,
18786
+ org_id: _state.orgId,
18412
18787
  project_name,
18413
18788
  dataset_name: name,
18414
18789
  description
18415
18790
  };
18416
- const response = await api_conn().post_json("api/dataset/register", args);
18791
+ const response = await _state.apiConn().post_json("api/dataset/register", args);
18417
18792
  const project = response.project;
18418
18793
  const dataset = response.dataset;
18419
- const user_id = (await _user_info())["id"];
18794
+ const user_id = (await _state.userInfo())["id"];
18420
18795
  return new Dataset(project, dataset.id, dataset.name, user_id, version);
18421
18796
  }
18422
18797
  var Dataset = class {
18423
18798
  constructor(project, id, name, user_id, pinnedVersion) {
18424
18799
  this._fetchedData = void 0;
18800
+ this.finished = false;
18425
18801
  this.project = project;
18426
18802
  this.id = id;
18427
18803
  this.name = name;
18428
18804
  this.user_id = user_id;
18429
18805
  this.pinnedVersion = pinnedVersion;
18430
18806
  this.logger = new LogThread();
18807
+ unterminatedObjects.addUnterminated(this, isomorph_default.getCallerLocation());
18431
18808
  }
18432
18809
  /**
18433
18810
  * Insert a single record to the dataset. The record will be batched and uploaded behind the scenes. If you pass in an `id`,
@@ -18449,6 +18826,7 @@ var Dataset = class {
18449
18826
  metadata,
18450
18827
  id
18451
18828
  }) {
18829
+ this.checkNotFinished();
18452
18830
  if (metadata !== void 0) {
18453
18831
  for (const key of Object.keys(metadata)) {
18454
18832
  if (typeof key !== "string") {
@@ -18470,6 +18848,7 @@ var Dataset = class {
18470
18848
  return args.id;
18471
18849
  }
18472
18850
  delete(id) {
18851
+ this.checkNotFinished();
18473
18852
  const user_id = this.user_id;
18474
18853
  const args = {
18475
18854
  id,
@@ -18489,15 +18868,16 @@ var Dataset = class {
18489
18868
  * @returns A summary of the dataset.
18490
18869
  */
18491
18870
  async summarize(options = {}) {
18871
+ this.checkNotFinished();
18492
18872
  let { summarizeData = true } = options || {};
18493
18873
  await this.logger.flush();
18494
- const projectUrl = `${API_URL}/app/${encodeURIComponent(
18495
- ORG_NAME
18874
+ const projectUrl = `${_state.apiUrl}/app/${encodeURIComponent(
18875
+ _state.orgName
18496
18876
  )}/p/${encodeURIComponent(this.project.name)}`;
18497
18877
  const datasetUrl = `${projectUrl}/d/${encodeURIComponent(this.name)}`;
18498
18878
  let dataSummary = void 0;
18499
18879
  if (summarizeData) {
18500
- dataSummary = await log_conn().get_json(
18880
+ dataSummary = await _state.logConn().get_json(
18501
18881
  "dataset-summary",
18502
18882
  {
18503
18883
  dataset_id: this.id
@@ -18532,6 +18912,7 @@ var Dataset = class {
18532
18912
  * @returns An iterator over the dataset's records.
18533
18913
  */
18534
18914
  async *fetch() {
18915
+ this.checkNotFinished();
18535
18916
  const records = await this.fetchedData();
18536
18917
  for (const record of records) {
18537
18918
  yield {
@@ -18555,11 +18936,13 @@ var Dataset = class {
18555
18936
  * ```
18556
18937
  */
18557
18938
  [Symbol.asyncIterator]() {
18939
+ this.checkNotFinished();
18558
18940
  return this.fetch();
18559
18941
  }
18560
18942
  async fetchedData() {
18943
+ this.checkNotFinished();
18561
18944
  if (this._fetchedData === void 0) {
18562
- const resp = await log_conn().get("object/dataset", {
18945
+ const resp = await _state.logConn().get("object/dataset", {
18563
18946
  id: this.id,
18564
18947
  fmt: "json",
18565
18948
  version: this.pinnedVersion
@@ -18570,9 +18953,11 @@ var Dataset = class {
18570
18953
  return this._fetchedData || [];
18571
18954
  }
18572
18955
  clearCache() {
18956
+ this.checkNotFinished();
18573
18957
  this._fetchedData = void 0;
18574
18958
  }
18575
18959
  async version() {
18960
+ this.checkNotFinished();
18576
18961
  if (this.pinnedVersion !== void 0) {
18577
18962
  return this.pinnedVersion;
18578
18963
  } else {
@@ -18587,6 +18972,25 @@ var Dataset = class {
18587
18972
  return maxVersion;
18588
18973
  }
18589
18974
  }
18975
+ /**
18976
+ * Terminate connection to the dataset and return its id. After calling close, you may not invoke any further methods on the dataset object.
18977
+ *
18978
+ * Will be invoked automatically if the dataset is bound as a context manager.
18979
+ *
18980
+ * @returns The dataset id.
18981
+ */
18982
+ async close() {
18983
+ this.checkNotFinished();
18984
+ await this.logger.flush();
18985
+ this.finished = true;
18986
+ unterminatedObjects.removeUnterminated(this);
18987
+ return this.id;
18988
+ }
18989
+ checkNotFinished() {
18990
+ if (this.finished) {
18991
+ throw new Error("Cannot invoke method on finished dataset");
18992
+ }
18993
+ }
18590
18994
  };
18591
18995
 
18592
18996
  // src/framework.ts
@@ -18605,12 +19009,23 @@ configureNode();
18605
19009
  Dataset,
18606
19010
  Eval,
18607
19011
  Experiment,
19012
+ NoopSpan,
18608
19013
  Project,
19014
+ SpanImpl,
19015
+ _internalGetGlobalState,
19016
+ currentExperiment,
19017
+ currentSpan,
18609
19018
  init,
18610
19019
  initDataset,
18611
19020
  log,
18612
19021
  login,
18613
- summarize
19022
+ noopSpan,
19023
+ startSpan,
19024
+ summarize,
19025
+ traced,
19026
+ withCurrent,
19027
+ withDataset,
19028
+ withExperiment
18614
19029
  });
18615
19030
  /*! Bundled license information:
18616
19031