braintrust 0.0.109 → 0.0.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/browser.js CHANGED
@@ -95,28 +95,77 @@ function mergeDicts(mergeInto, mergeFrom) {
95
95
  function constructJsonArray(items) {
96
96
  return `[${items.join(",")}]`;
97
97
  }
98
- var batchRecords = function* (allItems, batchSize, maxRequestSize) {
99
- while (true) {
100
- const items = [];
101
- let itemsLen = 0;
102
- while (items.length < batchSize && itemsLen < maxRequestSize / 2) {
103
- let item = null;
104
- if (allItems.length > 0) {
105
- item = allItems.pop();
106
- } else {
107
- break;
98
+ function mapAt(m, k) {
99
+ const ret = m.get(k);
100
+ if (ret === void 0) {
101
+ throw new Error(`Map does not contain key ${k}`);
102
+ }
103
+ return ret;
104
+ }
105
+ function depthFirstSearch(args) {
106
+ var _a;
107
+ const { graph, firstVisitF, lastVisitF } = args;
108
+ for (const vs of graph.values()) {
109
+ for (const v of vs.values()) {
110
+ if (!graph.has(v)) {
111
+ throw new Error(`Outgoing vertex ${v} must be a key in the graph`);
108
112
  }
109
- const itemS = JSON.stringify(item);
110
- items.push(itemS);
111
- itemsLen += itemS.length;
112
113
  }
113
- if (items.length === 0) {
114
- break;
115
- }
116
- yield items;
117
114
  }
118
- };
119
- function generateMergedRowKey(row) {
115
+ const firstVisitedVertices = /* @__PURE__ */ new Set();
116
+ const visitationOrder = (_a = args.visitationOrder) != null ? _a : [...graph.keys()];
117
+ const events = visitationOrder.map((vertex) => ({ eventType: "first", vertex, extras: {} })).reverse();
118
+ while (events.length) {
119
+ const { eventType, vertex, extras } = events.pop();
120
+ if (eventType === "last") {
121
+ lastVisitF == null ? void 0 : lastVisitF(vertex);
122
+ continue;
123
+ }
124
+ if (firstVisitedVertices.has(vertex)) {
125
+ continue;
126
+ }
127
+ firstVisitedVertices.add(vertex);
128
+ firstVisitF == null ? void 0 : firstVisitF(vertex, { parentVertex: extras.parentVertex });
129
+ events.push({ eventType: "last", vertex, extras: {} });
130
+ mapAt(graph, vertex).forEach((child) => {
131
+ events.push({
132
+ eventType: "first",
133
+ vertex: child,
134
+ extras: { parentVertex: vertex }
135
+ });
136
+ });
137
+ }
138
+ }
139
+ function undirectedConnectedComponents(graph) {
140
+ const directedGraph = new Map(
141
+ [...graph.vertices].map((v) => [v, /* @__PURE__ */ new Set()])
142
+ );
143
+ for (const [i, j] of graph.edges) {
144
+ mapAt(directedGraph, i).add(j);
145
+ mapAt(directedGraph, j).add(i);
146
+ }
147
+ let labelCounter = 0;
148
+ const vertexLabels = /* @__PURE__ */ new Map();
149
+ const firstVisitF = (vertex, args) => {
150
+ const label = (args == null ? void 0 : args.parentVertex) !== void 0 ? mapAt(vertexLabels, args == null ? void 0 : args.parentVertex) : labelCounter++;
151
+ vertexLabels.set(vertex, label);
152
+ };
153
+ depthFirstSearch({ graph: directedGraph, firstVisitF });
154
+ const output = Array.from({ length: labelCounter }).map(() => []);
155
+ for (const [vertex, label] of vertexLabels.entries()) {
156
+ output[label].push(vertex);
157
+ }
158
+ return output;
159
+ }
160
+ function topologicalSort(graph, visitationOrder) {
161
+ const reverseOrdering = [];
162
+ const lastVisitF = (vertex) => {
163
+ reverseOrdering.push(vertex);
164
+ };
165
+ depthFirstSearch({ graph, lastVisitF, visitationOrder });
166
+ return reverseOrdering.reverse();
167
+ }
168
+ function generateMergedRowKey(row, useParentIdForId) {
120
169
  return JSON.stringify(
121
170
  [
122
171
  "org_id",
@@ -125,24 +174,22 @@ function generateMergedRowKey(row) {
125
174
  "dataset_id",
126
175
  "prompt_session_id",
127
176
  "log_id",
128
- "id"
177
+ (useParentIdForId != null ? useParentIdForId : false) ? PARENT_ID_FIELD : "id"
129
178
  ].map((k) => row[k])
130
179
  );
131
180
  }
132
181
  function mergeRowBatch(rows) {
133
- const out = [];
134
- const remainingRows = [];
135
182
  for (const row of rows) {
136
183
  if (row.id === void 0) {
137
- out.push(row);
138
- } else {
139
- remainingRows.push(row);
184
+ throw new Error(
185
+ "Logged row is missing an id. This is an internal braintrust error. Please contact us at info@braintrustdata.com for help"
186
+ );
140
187
  }
141
188
  }
142
- const rowGroups = {};
143
- for (const row of remainingRows) {
189
+ const rowGroups = /* @__PURE__ */ new Map();
190
+ for (const row of rows) {
144
191
  const key = generateMergedRowKey(row);
145
- const existingRow = rowGroups[key];
192
+ const existingRow = rowGroups.get(key);
146
193
  if (existingRow !== void 0 && row[IS_MERGE_FIELD]) {
147
194
  const preserveNoMerge = !existingRow[IS_MERGE_FIELD];
148
195
  mergeDicts(existingRow, row);
@@ -150,11 +197,102 @@ function mergeRowBatch(rows) {
150
197
  delete existingRow[IS_MERGE_FIELD];
151
198
  }
152
199
  } else {
153
- rowGroups[key] = row;
200
+ rowGroups.set(key, row);
201
+ }
202
+ }
203
+ const merged = [...rowGroups.values()];
204
+ const rowToLabel = new Map(
205
+ merged.map((r, i) => [generateMergedRowKey(r), i])
206
+ );
207
+ const graph = new Map(
208
+ Array.from({ length: merged.length }).map((_, i) => [i, /* @__PURE__ */ new Set()])
209
+ );
210
+ merged.forEach((r, i) => {
211
+ const parentId = r[PARENT_ID_FIELD];
212
+ if (!parentId) {
213
+ return;
214
+ }
215
+ const parentRowKey = generateMergedRowKey(
216
+ r,
217
+ true
218
+ /* useParentIdForId */
219
+ );
220
+ const parentLabel = rowToLabel.get(parentRowKey);
221
+ if (parentLabel !== void 0) {
222
+ mapAt(graph, parentLabel).add(i);
223
+ }
224
+ });
225
+ const connectedComponents = undirectedConnectedComponents({
226
+ vertices: new Set(graph.keys()),
227
+ edges: new Set(
228
+ [...graph.entries()].flatMap(
229
+ ([k, vs]) => [...vs].map((v) => {
230
+ const ret = [k, v];
231
+ return ret;
232
+ })
233
+ )
234
+ )
235
+ });
236
+ const buckets = connectedComponents.map(
237
+ (cc) => topologicalSort(
238
+ graph,
239
+ cc
240
+ /* visitationOrder */
241
+ )
242
+ );
243
+ return buckets.map((bucket) => bucket.map((i) => merged[i]));
244
+ }
245
+ function batchItems(args) {
246
+ var _a, _b;
247
+ let { items } = args;
248
+ const batchMaxNumItems = (_a = args.batchMaxNumItems) != null ? _a : Number.POSITIVE_INFINITY;
249
+ const batchMaxNumBytes = (_b = args.batchMaxNumBytes) != null ? _b : Number.POSITIVE_INFINITY;
250
+ const output = [];
251
+ let nextItems = [];
252
+ let batchSet = [];
253
+ let batch = [];
254
+ let batchLen = 0;
255
+ function addToBatch(item) {
256
+ batch.push(item);
257
+ batchLen += item.length;
258
+ }
259
+ function flushBatch() {
260
+ batchSet.push(batch);
261
+ batch = [];
262
+ batchLen = 0;
263
+ }
264
+ while (items.length) {
265
+ for (const bucket of items) {
266
+ let i = 0;
267
+ for (const item of bucket) {
268
+ if (batch.length === 0 || item.length + batchLen < batchMaxNumBytes && batch.length < batchMaxNumItems) {
269
+ addToBatch(item);
270
+ } else if (i === 0) {
271
+ flushBatch();
272
+ addToBatch(item);
273
+ } else {
274
+ break;
275
+ }
276
+ ++i;
277
+ }
278
+ if (i < bucket.length) {
279
+ nextItems.push(bucket.slice(i));
280
+ }
281
+ if (batchLen >= batchMaxNumBytes || batch.length > batchMaxNumItems) {
282
+ flushBatch();
283
+ }
284
+ }
285
+ if (batch.length) {
286
+ flushBatch();
154
287
  }
288
+ if (batchSet.length) {
289
+ output.push(batchSet);
290
+ batchSet = [];
291
+ }
292
+ items = nextItems;
293
+ nextItems = [];
155
294
  }
156
- out.push(...Object.values(rowGroups));
157
- return out;
295
+ return output;
158
296
  }
159
297
  var DEFAULT_IS_LEGACY_DATASET = true;
160
298
  function ensureDatasetRecord(r, legacy) {
@@ -277,13 +415,9 @@ var LazyValue = class {
277
415
  // src/logger.ts
278
416
  var NoopSpan = class {
279
417
  id;
280
- span_id;
281
- root_span_id;
282
418
  kind = "span";
283
419
  constructor() {
284
420
  this.id = "";
285
- this.span_id = "";
286
- this.root_span_id = "";
287
421
  }
288
422
  log(_) {
289
423
  }
@@ -761,14 +895,17 @@ var BackgroundLogger = class {
761
895
  if (allItems.length === 0) {
762
896
  return;
763
897
  }
764
- const postPromises = [];
765
- for (const batch of batchRecords(
766
- allItems,
767
- batchSize,
768
- this.maxRequestSize
769
- )) {
770
- postPromises.push(
771
- (async () => {
898
+ const allItemsStr = allItems.map(
899
+ (bucket) => bucket.map((item) => JSON.stringify(item))
900
+ );
901
+ const batchSets = batchItems({
902
+ items: allItemsStr,
903
+ batchMaxNumItems: batchSize,
904
+ batchMaxNumBytes: this.maxRequestSize / 2
905
+ });
906
+ for (const batchSet of batchSets) {
907
+ const postPromises = batchSet.map(
908
+ (batch) => (async () => {
772
909
  try {
773
910
  await this.submitLogsRequest(batch);
774
911
  return { type: "success" };
@@ -777,14 +914,14 @@ var BackgroundLogger = class {
777
914
  }
778
915
  })()
779
916
  );
780
- }
781
- const results = await Promise.all(postPromises);
782
- const failingResultErrors = results.map((r) => r.type === "success" ? void 0 : r.value).filter((r) => r !== void 0);
783
- if (failingResultErrors.length) {
784
- throw new AggregateError(
785
- failingResultErrors,
786
- `Encountered the following errors while logging:`
787
- );
917
+ const results = await Promise.all(postPromises);
918
+ const failingResultErrors = results.map((r) => r.type === "success" ? void 0 : r.value).filter((r) => r !== void 0);
919
+ if (failingResultErrors.length) {
920
+ throw new AggregateError(
921
+ failingResultErrors,
922
+ `Encountered the following errors while logging:`
923
+ );
924
+ }
788
925
  }
789
926
  if (this.items.length > 0) {
790
927
  await this.flushOnce(args);
@@ -1817,16 +1954,24 @@ var SpanImpl = class _SpanImpl {
1817
1954
  this.parentObject = args.parentObject;
1818
1955
  this.parentIds = args.parentIds;
1819
1956
  const id = args.event?.id ?? v4_default();
1820
- const span_id = v4_default();
1821
- this.rowIds = {
1822
- id,
1823
- span_id,
1824
- root_span_id: "parentSpanInfo" in args && args.parentSpanInfo?.root_span_id ? args.parentSpanInfo.root_span_id : span_id
1825
- };
1826
- if ("parentSpanInfo" in args && args.parentSpanInfo?.span_id) {
1827
- this.internalData.span_parents = [args.parentSpanInfo.span_id];
1828
- } else if ("parentId" in args && !isEmpty(args.parentId)) {
1829
- this.rowIds[PARENT_ID_FIELD] = args.parentId;
1957
+ this.rowIds = { id };
1958
+ const parentSpanInfo = "parentSpanInfo" in args ? args.parentSpanInfo : void 0;
1959
+ const parentId = "parentId" in args ? args.parentId : void 0;
1960
+ if (parentSpanInfo && parentId) {
1961
+ throw new Error(
1962
+ "Only one of parentSpanInfo and parentId may be specified"
1963
+ );
1964
+ }
1965
+ if (parentId) {
1966
+ this.rowIds[PARENT_ID_FIELD] = parentId;
1967
+ } else {
1968
+ this.rowIds.span_id = v4_default();
1969
+ if (parentSpanInfo) {
1970
+ this.rowIds.root_span_id = parentSpanInfo.root_span_id;
1971
+ this.internalData.span_parents = [parentSpanInfo.span_id];
1972
+ } else {
1973
+ this.rowIds.root_span_id = this.rowIds.span_id;
1974
+ }
1830
1975
  }
1831
1976
  this.isMerge = false;
1832
1977
  const { id: _id, ...eventRest } = args.event ?? {};
@@ -1836,37 +1981,29 @@ var SpanImpl = class _SpanImpl {
1836
1981
  get id() {
1837
1982
  return this.rowIds.id;
1838
1983
  }
1839
- get span_id() {
1840
- return this.rowIds.span_id;
1841
- }
1842
- get root_span_id() {
1843
- return this.rowIds.root_span_id;
1844
- }
1845
1984
  log(event) {
1846
1985
  const sanitized = validateAndSanitizeExperimentLogPartialArgs(event);
1847
1986
  let sanitizedAndInternalData = { ...this.internalData };
1848
1987
  mergeDicts(sanitizedAndInternalData, sanitized);
1849
1988
  this.internalData = {};
1850
- if (sanitizedAndInternalData.metrics?.end) {
1851
- this.loggedEndTime = sanitizedAndInternalData.metrics?.end;
1852
- }
1853
- const parentIds = new LazyValue(async () => {
1854
- const { kind, ...ids } = await this.parentIds.get();
1855
- return ids;
1856
- });
1857
1989
  if (sanitizedAndInternalData.tags && sanitizedAndInternalData.tags.length > 0 && this.rowIds.span_id !== this.rowIds.root_span_id) {
1858
1990
  throw new Error("Tags can only be logged to the root span");
1859
1991
  }
1860
- const serializedSanitizedAndInternalData = JSON.stringify(
1861
- sanitizedAndInternalData
1862
- );
1863
- sanitizedAndInternalData = JSON.parse(serializedSanitizedAndInternalData);
1992
+ let partialRecord = {
1993
+ ...sanitizedAndInternalData,
1994
+ ...this.rowIds,
1995
+ [IS_MERGE_FIELD]: this.isMerge
1996
+ };
1997
+ if (partialRecord.metrics?.end) {
1998
+ this.loggedEndTime = partialRecord.metrics?.end;
1999
+ }
2000
+ const serializedPartialRecord = JSON.stringify(partialRecord);
2001
+ partialRecord = JSON.parse(serializedPartialRecord);
1864
2002
  const record = new LazyValue(async () => {
2003
+ const { kind, ...parentIds } = await this.parentIds.get();
1865
2004
  return {
1866
- ...sanitizedAndInternalData,
1867
- ...this.rowIds,
1868
- ...await parentIds.get(),
1869
- [IS_MERGE_FIELD]: this.isMerge
2005
+ ...partialRecord,
2006
+ ...parentIds
1870
2007
  };
1871
2008
  });
1872
2009
  this.bgLogger.log([record]);
@@ -1892,14 +2029,24 @@ var SpanImpl = class _SpanImpl {
1892
2029
  );
1893
2030
  }
1894
2031
  startSpan(args) {
2032
+ const parentId = args?.parentId ?? (this.rowIds[PARENT_ID_FIELD] ? this.id : void 0);
2033
+ const parentSpanInfo = (() => {
2034
+ if (parentId)
2035
+ return void 0;
2036
+ if (!(this.rowIds.span_id && this.rowIds.root_span_id)) {
2037
+ throw new Error("Impossible");
2038
+ }
2039
+ return {
2040
+ span_id: this.rowIds.span_id,
2041
+ root_span_id: this.rowIds.root_span_id
2042
+ };
2043
+ })();
1895
2044
  return new _SpanImpl({
1896
2045
  parentObject: this.parentObject,
1897
2046
  parentIds: this.parentIds,
1898
2047
  bgLogger: this.bgLogger,
1899
- parentSpanInfo: {
1900
- span_id: this.rowIds.span_id,
1901
- root_span_id: this.rowIds.root_span_id
1902
- },
2048
+ parentSpanInfo,
2049
+ parentId,
1903
2050
  ...args
1904
2051
  });
1905
2052
  }
@@ -2226,7 +2373,9 @@ function wrapChatCompletion(completion) {
2226
2373
  if (params.stream) {
2227
2374
  const startTime = getCurrentUnixTimestamp();
2228
2375
  const ret = await completion(params, options);
2229
- return new WrapperStream(span, startTime, ret);
2376
+ const wrapperStream = new WrapperStream(span, startTime, ret.iterator());
2377
+ ret.iterator = () => wrapperStream[Symbol.asyncIterator]();
2378
+ return ret;
2230
2379
  } else {
2231
2380
  try {
2232
2381
  const ret = await completion(