braintrust 0.0.140 → 0.0.141-dev

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.mjs CHANGED
@@ -97,6 +97,125 @@ var LazyValue = class {
97
97
  // src/logger.ts
98
98
  import Mustache from "mustache";
99
99
  import { z } from "zod";
100
+
101
+ // src/stream.ts
102
+ import { callEventSchema } from "@braintrust/core/typespecs";
103
+ import {
104
+ createParser
105
+ } from "eventsource-parser";
106
+ var BraintrustStream = class _BraintrustStream {
107
+ stream;
108
+ constructor(baseStream) {
109
+ this.stream = baseStream.pipeThrough(btStreamParser());
110
+ }
111
+ copy() {
112
+ const [newStream, copyStream] = this.stream.tee();
113
+ this.stream = copyStream;
114
+ return new _BraintrustStream(newStream);
115
+ }
116
+ toReadableStream() {
117
+ return this.stream;
118
+ }
119
+ };
120
+ function btStreamParser() {
121
+ const decoder = new TextDecoder();
122
+ let parser;
123
+ return new TransformStream({
124
+ async start(controller) {
125
+ parser = createParser((event) => {
126
+ if (event.type === "reconnect-interval") {
127
+ return;
128
+ }
129
+ const parsed = callEventSchema.safeParse(event);
130
+ if (!parsed.success) {
131
+ throw new Error(`Failed to parse event: ${parsed.error}`);
132
+ }
133
+ switch (event.event) {
134
+ case "text_delta":
135
+ controller.enqueue({
136
+ type: "text_delta",
137
+ data: JSON.parse(event.data)
138
+ });
139
+ break;
140
+ case "json_delta":
141
+ controller.enqueue({
142
+ type: "json_delta",
143
+ data: event.data
144
+ });
145
+ break;
146
+ case "done":
147
+ break;
148
+ }
149
+ });
150
+ },
151
+ async transform(chunk, controller) {
152
+ if (chunk instanceof Uint8Array) {
153
+ parser.feed(decoder.decode(chunk));
154
+ } else if (typeof chunk === "string") {
155
+ parser.feed(chunk);
156
+ } else {
157
+ controller.enqueue(chunk);
158
+ }
159
+ },
160
+ async flush(controller) {
161
+ controller.terminate();
162
+ }
163
+ });
164
+ }
165
+ function createFinalValuePassThroughStream(onFinal) {
166
+ const decoder = new TextDecoder();
167
+ const textChunks = [];
168
+ const jsonChunks = [];
169
+ const transformStream = new TransformStream({
170
+ transform(chunk, controller) {
171
+ if (typeof chunk === "string") {
172
+ textChunks.push(chunk);
173
+ } else if (chunk instanceof Uint8Array) {
174
+ textChunks.push(decoder.decode(chunk));
175
+ } else {
176
+ const chunkType = chunk.type;
177
+ switch (chunkType) {
178
+ case "text_delta":
179
+ textChunks.push(chunk.data);
180
+ break;
181
+ case "json_delta":
182
+ jsonChunks.push(chunk.data);
183
+ break;
184
+ default:
185
+ const _type = chunkType;
186
+ throw new Error(`Unknown chunk type ${_type}`);
187
+ }
188
+ controller.enqueue(chunk);
189
+ }
190
+ },
191
+ flush(controller) {
192
+ if (jsonChunks.length > 0) {
193
+ onFinal(JSON.parse(jsonChunks.join("")));
194
+ } else if (textChunks.length > 0) {
195
+ onFinal(textChunks.join(""));
196
+ } else {
197
+ onFinal(void 0);
198
+ }
199
+ controller.terminate();
200
+ }
201
+ });
202
+ return transformStream;
203
+ }
204
+ function devNullWritableStream() {
205
+ return new WritableStream({
206
+ write(chunk) {
207
+ },
208
+ close() {
209
+ },
210
+ abort(reason) {
211
+ },
212
+ start(controller) {
213
+ }
214
+ });
215
+ }
216
+
217
+ // src/logger.ts
218
+ import { waitUntil } from "@vercel/functions";
100
219
  var NoopSpan = class {
101
220
  id;
102
221
  kind = "span";
@@ -133,6 +252,7 @@ var loginSchema = z.strictObject({
133
252
  appPublicUrl: z.string(),
134
253
  orgName: z.string(),
135
254
  logUrl: z.string(),
255
+ proxyUrl: z.string(),
136
256
  loginToken: z.string(),
137
257
  orgId: z.string().nullish(),
138
258
  gitMetadataSettings: gitMetadataSettingsSchema.nullish()
@@ -171,11 +291,13 @@ var BraintrustState = class _BraintrustState {
171
291
  orgId = null;
172
292
  orgName = null;
173
293
  logUrl = null;
294
+ proxyUrl = null;
174
295
  loggedIn = false;
175
296
  gitMetadataSettings;
176
297
  fetch = globalThis.fetch;
177
298
  _apiConn = null;
178
299
  _logConn = null;
300
+ _proxyConn = null;
179
301
  resetLoginInfo() {
180
302
  this.appUrl = null;
181
303
  this.appPublicUrl = null;
@@ -183,10 +305,12 @@ var BraintrustState = class _BraintrustState {
183
305
  this.orgId = null;
184
306
  this.orgName = null;
185
307
  this.logUrl = null;
308
+ this.proxyUrl = null;
186
309
  this.loggedIn = false;
187
310
  this.gitMetadataSettings = void 0;
188
311
  this._apiConn = null;
189
312
  this._logConn = null;
313
+ this._proxyConn = null;
190
314
  }
191
315
  copyLoginInfo(other) {
192
316
  this.appUrl = other.appUrl;
@@ -195,10 +319,12 @@ var BraintrustState = class _BraintrustState {
195
319
  this.orgId = other.orgId;
196
320
  this.orgName = other.orgName;
197
321
  this.logUrl = other.logUrl;
322
+ this.proxyUrl = other.proxyUrl;
198
323
  this.loggedIn = other.loggedIn;
199
324
  this.gitMetadataSettings = other.gitMetadataSettings;
200
325
  this._apiConn = other._apiConn;
201
326
  this._logConn = other._logConn;
327
+ this._proxyConn = other._proxyConn;
202
328
  }
203
329
  serialize() {
204
330
  if (!this.loggedIn) {
@@ -206,7 +332,7 @@ var BraintrustState = class _BraintrustState {
206
332
  "Cannot serialize BraintrustState without being logged in"
207
333
  );
208
334
  }
209
- if (!this.appUrl || !this.appPublicUrl || !this.logUrl || !this.orgName || !this.loginToken || !this.loggedIn) {
335
+ if (!this.appUrl || !this.appPublicUrl || !this.logUrl || !this.proxyUrl || !this.orgName || !this.loginToken || !this.loggedIn) {
210
336
  throw new Error(
211
337
  "Cannot serialize BraintrustState without all login attributes"
212
338
  );
@@ -218,6 +344,7 @@ var BraintrustState = class _BraintrustState {
218
344
  orgId: this.orgId,
219
345
  orgName: this.orgName,
220
346
  logUrl: this.logUrl,
347
+ proxyUrl: this.proxyUrl,
221
348
  gitMetadataSettings: this.gitMetadataSettings
222
349
  };
223
350
  }
@@ -240,6 +367,7 @@ var BraintrustState = class _BraintrustState {
240
367
  state.logConn().set_token(state.loginToken);
241
368
  state.logConn().make_long_lived();
242
369
  state.apiConn().set_token(state.loginToken);
370
+ state.proxyConn().set_token(state.loginToken);
243
371
  state.loggedIn = true;
244
372
  state.loginReplaceLogConn(state.logConn());
245
373
  return state;
@@ -278,6 +406,15 @@ var BraintrustState = class _BraintrustState {
278
406
  }
279
407
  return this._logConn;
280
408
  }
409
+ proxyConn() {
410
+ if (!this._proxyConn) {
411
+ if (!this.proxyUrl) {
412
+ throw new Error("Must initialize proxyUrl before requesting proxyConn");
413
+ }
414
+ this._proxyConn = new HTTPConnection(this.proxyUrl, this.fetch);
415
+ }
416
+ return this._proxyConn;
417
+ }
281
418
  bgLogger() {
282
419
  return this._bgLogger;
283
420
  }
@@ -590,6 +727,11 @@ var Logger = class {
590
727
  parentObjectType() {
591
728
  return SpanObjectTypeV2.PROJECT_LOGS;
592
729
  }
730
+ triggerWaitUntilFlush() {
731
+ if (!this.state.bgLogger().syncFlush) {
732
+ return waitUntil(this.state.bgLogger().flush());
733
+ }
734
+ }
593
735
  /**
594
736
  * Log a single event. The event will be batched and uploaded behind the scenes if `logOptions.asyncFlush` is true.
595
737
  *
@@ -615,6 +757,7 @@ var Logger = class {
615
757
  this.lastStartTime = span.end();
616
758
  const ret = span.id;
617
759
  if (this.asyncFlush === true) {
760
+ this.triggerWaitUntilFlush();
618
761
  return ret;
619
762
  } else {
620
763
  return (async () => {
@@ -642,6 +785,7 @@ var Logger = class {
642
785
  () => span.end()
643
786
  );
644
787
  if (this.asyncFlush) {
788
+ this.triggerWaitUntilFlush();
645
789
  return ret;
646
790
  } else {
647
791
  return (async () => {
@@ -1073,6 +1217,8 @@ function init(projectOrOptions, optionalOptions) {
1073
1217
  appUrl,
1074
1218
  apiKey,
1075
1219
  orgName,
1220
+ forceLogin,
1221
+ fetch,
1076
1222
  metadata,
1077
1223
  gitMetadataSettings,
1078
1224
  projectId,
@@ -1090,7 +1236,7 @@ function init(projectOrOptions, optionalOptions) {
1090
1236
  }
1091
1237
  const lazyMetadata2 = new LazyValue(
1092
1238
  async () => {
1093
- await state.login({ apiKey, appUrl, orgName });
1239
+ await state.login({ apiKey, appUrl, orgName, fetch, forceLogin });
1094
1240
  const args = {
1095
1241
  project_name: project,
1096
1242
  project_id: projectId,
@@ -1261,6 +1407,8 @@ function initDataset(projectOrOptions, optionalOptions) {
1261
1407
  appUrl,
1262
1408
  apiKey,
1263
1409
  orgName,
1410
+ fetch,
1411
+ forceLogin,
1264
1412
  projectId,
1265
1413
  useOutput: legacy,
1266
1414
  state: stateArg
@@ -1271,7 +1419,9 @@ function initDataset(projectOrOptions, optionalOptions) {
1271
1419
  await state.login({
1272
1420
  orgName,
1273
1421
  apiKey,
1274
- appUrl
1422
+ appUrl,
1423
+ fetch,
1424
+ forceLogin
1275
1425
  });
1276
1426
  const args = {
1277
1427
  org_id: state.orgId,
@@ -1350,6 +1500,7 @@ function initLogger(options = {}) {
1350
1500
  apiKey,
1351
1501
  orgName,
1352
1502
  forceLogin,
1503
+ fetch,
1353
1504
  state: stateArg
1354
1505
  } = options || {};
1355
1506
  const computeMetadataArgs = {
@@ -1363,7 +1514,8 @@ function initLogger(options = {}) {
1363
1514
  orgName,
1364
1515
  apiKey,
1365
1516
  appUrl,
1366
- forceLogin
1517
+ forceLogin,
1518
+ fetch
1367
1519
  });
1368
1520
  return computeLoggerMetadata(state, computeMetadataArgs);
1369
1521
  }
@@ -1387,6 +1539,8 @@ async function loadPrompt({
1387
1539
  appUrl,
1388
1540
  apiKey,
1389
1541
  orgName,
1542
+ fetch,
1543
+ forceLogin,
1390
1544
  state: stateArg
1391
1545
  }) {
1392
1546
  if (isEmpty(projectName) && isEmpty(projectId)) {
@@ -1399,7 +1553,9 @@ async function loadPrompt({
1399
1553
  state.login({
1400
1554
  orgName,
1401
1555
  apiKey,
1402
- appUrl
1556
+ appUrl,
1557
+ fetch,
1558
+ forceLogin
1403
1559
  });
1404
1560
  const args = {
1405
1561
  project_name: projectName,
@@ -1442,6 +1598,7 @@ async function login(options = {}) {
1442
1598
  }
1443
1599
  await _globalState.login(options);
1444
1600
  globalThis.__inherited_braintrust_state = _globalState;
1601
+ return _globalState;
1445
1602
  }
1446
1603
  async function loginToState(options = {}) {
1447
1604
  const {
@@ -1480,6 +1637,7 @@ async function loginToState(options = {}) {
1480
1637
  }
1481
1638
  conn.make_long_lived();
1482
1639
  state.apiConn().set_token(apiKey);
1640
+ state.proxyConn().set_token(apiKey);
1483
1641
  state.loginToken = conn.token;
1484
1642
  state.loggedIn = true;
1485
1643
  state.loginReplaceLogConn(conn);
@@ -1651,6 +1809,12 @@ function _check_org_info(state, org_info, org_name) {
1651
1809
  state.orgId = org.id;
1652
1810
  state.orgName = org.name;
1653
1811
  state.logUrl = isomorph_default.getEnv("BRAINTRUST_API_URL") ?? org.api_url;
1812
+ state.proxyUrl = isomorph_default.getEnv("BRAINTRUST_PROXY_URL") ?? org.proxy_url;
1813
+ if (state.proxyUrl) {
1814
+ const url = new URL(state.proxyUrl);
1815
+ url.pathname = "";
1816
+ state.proxyUrl = url.toString();
1817
+ }
1654
1818
  state.gitMetadataSettings = org.git_metadata || void 0;
1655
1819
  break;
1656
1820
  }
@@ -1662,7 +1826,9 @@ function _check_org_info(state, org_info, org_name) {
1662
1826
  }
1663
1827
  }
1664
1828
  function _urljoin(...parts) {
1665
- return parts.map((x) => x.replace(/^\//, "")).join("/");
1829
+ return parts.map(
1830
+ (x, i) => x.replace(/^\//, "").replace(i < parts.length - 1 ? /\/$/ : "", "")
1831
+ ).join("/");
1666
1832
  }
1667
1833
  function validateTags(tags) {
1668
1834
  const seen = /* @__PURE__ */ new Set();
@@ -2161,12 +2327,32 @@ var SpanImpl = class _SpanImpl {
2161
2327
  const sanitized = validateAndSanitizeExperimentLogPartialArgs(event ?? {});
2162
2328
  let sanitizedAndInternalData = { ...internalData };
2163
2329
  mergeDicts(sanitizedAndInternalData, sanitized);
2330
+ const serializableInternalData = {};
2331
+ const lazyInternalData = {};
2332
+ for (const [key, value] of Object.entries(sanitizedAndInternalData)) {
2333
+ if (value instanceof BraintrustStream) {
2334
+ const streamCopy = value.copy();
2335
+ lazyInternalData[key] = new LazyValue(async () => {
2336
+ return await new Promise((resolve) => {
2337
+ streamCopy.toReadableStream().pipeThrough(createFinalValuePassThroughStream(resolve)).pipeTo(devNullWritableStream());
2338
+ });
2339
+ });
2340
+ } else if (value instanceof ReadableStream) {
2341
+ lazyInternalData[key] = new LazyValue(async () => {
2342
+ return await new Promise((resolve) => {
2343
+ value.pipeThrough(createFinalValuePassThroughStream(resolve)).pipeTo(devNullWritableStream());
2344
+ });
2345
+ });
2346
+ } else {
2347
+ serializableInternalData[key] = value;
2348
+ }
2349
+ }
2164
2350
  let partialRecord = {
2165
2351
  id: this.id,
2166
2352
  span_id: this.spanId,
2167
2353
  root_span_id: this.rootSpanId,
2168
2354
  span_parents: this.spanParents,
2169
- ...sanitizedAndInternalData,
2355
+ ...serializableInternalData,
2170
2356
  [IS_MERGE_FIELD]: this.isMerge
2171
2357
  };
2172
2358
  const serializedPartialRecord = JSON.stringify(partialRecord, (k, v) => {
@@ -2190,6 +2376,14 @@ var SpanImpl = class _SpanImpl {
2190
2376
  }
2191
2377
  const computeRecord = async () => ({
2192
2378
  ...partialRecord,
2379
+ ...Object.fromEntries(
2380
+ await Promise.all(
2381
+ Object.entries(lazyInternalData).map(async ([key, value]) => [
2382
+ key,
2383
+ await value.get()
2384
+ ])
2385
+ )
2386
+ ),
2193
2387
  ...new SpanComponentsV2({
2194
2388
  objectType: this.parentObjectType,
2195
2389
  objectId: await this.parentObjectId.get()
@@ -2559,6 +2753,55 @@ function configureBrowser() {
2559
2753
  browserConfigured = true;
2560
2754
  }
2561
2755
 
2756
+ // src/functions/invoke.ts
2757
+ import {
2758
+ INVOKE_API_VERSION
2759
+ } from "@braintrust/core/typespecs";
2760
+ async function invoke(args) {
2761
+ const {
2762
+ orgName,
2763
+ apiKey,
2764
+ appUrl,
2765
+ forceLogin,
2766
+ fetch,
2767
+ arg,
2768
+ parent: parentArg,
2769
+ state: stateArg,
2770
+ stream,
2771
+ schema,
2772
+ ...functionId
2773
+ } = args;
2774
+ const state = stateArg ?? _internalGetGlobalState();
2775
+ await state.login({
2776
+ orgName,
2777
+ apiKey,
2778
+ appUrl,
2779
+ forceLogin
2780
+ });
2781
+ const parent = parentArg ? typeof parentArg === "string" ? parentArg : await parentArg.export() : await currentSpan().export();
2782
+ const request = {
2783
+ ...functionId,
2784
+ arg,
2785
+ parent,
2786
+ stream,
2787
+ api_version: INVOKE_API_VERSION
2788
+ };
2789
+ const resp = await state.proxyConn().post(`function/invoke`, request, {
2790
+ headers: {
2791
+ Accept: stream ? "text/event-stream" : "application/json"
2792
+ }
2793
+ });
2794
+ if (stream) {
2795
+ if (!resp.body) {
2796
+ throw new Error("Received empty stream body");
2797
+ }
2798
+ return new BraintrustStream(resp.body);
2799
+ } else {
2800
+ const data = await resp.json();
2801
+ return schema ? schema.parse(data) : data;
2802
+ }
2803
+ }
2804
+
2562
2805
  // src/wrappers/oai.ts
2563
2806
  import { SpanTypeAttribute as SpanTypeAttribute2 } from "@braintrust/core";
2564
2807
  import { mergeDicts as mergeDicts2 } from "@braintrust/core";
@@ -2915,6 +3158,7 @@ var WrapperStream = class {
2915
3158
  configureBrowser();
2916
3159
  export {
2917
3160
  BraintrustState,
3161
+ BraintrustStream,
2918
3162
  Dataset,
2919
3163
  Experiment,
2920
3164
  LEGACY_CACHED_HEADER,
@@ -2927,15 +3171,18 @@ export {
2927
3171
  X_CACHED_HEADER,
2928
3172
  _internalGetGlobalState,
2929
3173
  _internalSetInitialState,
3174
+ createFinalValuePassThroughStream,
2930
3175
  currentExperiment,
2931
3176
  currentLogger,
2932
3177
  currentSpan,
3178
+ devNullWritableStream,
2933
3179
  flush,
2934
3180
  getSpanParentObject,
2935
3181
  init,
2936
3182
  initDataset,
2937
3183
  initExperiment,
2938
3184
  initLogger,
3185
+ invoke,
2939
3186
  loadPrompt,
2940
3187
  log,
2941
3188
  login,