braintrust 0.0.166 → 0.0.168
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.d.mts +184 -41
- package/dist/browser.d.ts +184 -41
- package/dist/browser.js +348 -101
- package/dist/browser.mjs +349 -103
- package/dist/cli.js +397 -85
- package/dist/index.d.mts +186 -44
- package/dist/index.d.ts +186 -44
- package/dist/index.js +369 -105
- package/dist/index.mjs +370 -107
- package/package.json +7 -2
- package/typedoc.json +1 -0
package/dist/browser.mjs
CHANGED
|
@@ -55,7 +55,8 @@ import {
|
|
|
55
55
|
promptDataSchema,
|
|
56
56
|
promptSchema,
|
|
57
57
|
toolsSchema,
|
|
58
|
-
gitMetadataSettingsSchema
|
|
58
|
+
gitMetadataSettingsSchema,
|
|
59
|
+
BRAINTRUST_ATTACHMENT
|
|
59
60
|
} from "@braintrust/core/typespecs";
|
|
60
61
|
|
|
61
62
|
// src/util.ts
|
|
@@ -106,7 +107,7 @@ var LazyValue = class {
|
|
|
106
107
|
|
|
107
108
|
// src/logger.ts
|
|
108
109
|
import Mustache from "mustache";
|
|
109
|
-
import { z as z2 } from "zod";
|
|
110
|
+
import { z as z2, ZodError } from "zod";
|
|
110
111
|
|
|
111
112
|
// src/functions/stream.ts
|
|
112
113
|
import {
|
|
@@ -396,6 +397,9 @@ var NoopSpan = class {
|
|
|
396
397
|
async export() {
|
|
397
398
|
return "";
|
|
398
399
|
}
|
|
400
|
+
async permalink() {
|
|
401
|
+
return "";
|
|
402
|
+
}
|
|
399
403
|
async flush() {
|
|
400
404
|
}
|
|
401
405
|
close(args) {
|
|
@@ -537,11 +541,11 @@ var BraintrustState = class _BraintrustState {
|
|
|
537
541
|
state.loginReplaceApiConn(state.apiConn());
|
|
538
542
|
return state;
|
|
539
543
|
}
|
|
540
|
-
setFetch(
|
|
541
|
-
this.loginParams.fetch =
|
|
542
|
-
this.fetch =
|
|
543
|
-
this._apiConn?.setFetch(
|
|
544
|
-
this._appConn?.setFetch(
|
|
544
|
+
setFetch(fetch2) {
|
|
545
|
+
this.loginParams.fetch = fetch2;
|
|
546
|
+
this.fetch = fetch2;
|
|
547
|
+
this._apiConn?.setFetch(fetch2);
|
|
548
|
+
this._appConn?.setFetch(fetch2);
|
|
545
549
|
}
|
|
546
550
|
async login(loginParams) {
|
|
547
551
|
if (this.apiUrl && !loginParams.forceLogin) {
|
|
@@ -630,15 +634,15 @@ var HTTPConnection = class _HTTPConnection {
|
|
|
630
634
|
token;
|
|
631
635
|
headers;
|
|
632
636
|
fetch;
|
|
633
|
-
constructor(base_url,
|
|
637
|
+
constructor(base_url, fetch2) {
|
|
634
638
|
this.base_url = base_url;
|
|
635
639
|
this.token = null;
|
|
636
640
|
this.headers = {};
|
|
637
641
|
this._reset();
|
|
638
|
-
this.fetch =
|
|
642
|
+
this.fetch = fetch2;
|
|
639
643
|
}
|
|
640
|
-
setFetch(
|
|
641
|
-
this.fetch =
|
|
644
|
+
setFetch(fetch2) {
|
|
645
|
+
this.fetch = fetch2;
|
|
642
646
|
}
|
|
643
647
|
async ping() {
|
|
644
648
|
try {
|
|
@@ -674,12 +678,14 @@ var HTTPConnection = class _HTTPConnection {
|
|
|
674
678
|
([k, v]) => v !== void 0 ? typeof v === "string" ? [[k, v]] : v.map((x) => [k, x]) : []
|
|
675
679
|
) : []
|
|
676
680
|
).toString();
|
|
681
|
+
const this_fetch = this.fetch;
|
|
682
|
+
const this_headers = this.headers;
|
|
677
683
|
return await checkResponse(
|
|
678
684
|
// Using toString() here makes it work with isomorphic fetch
|
|
679
|
-
await
|
|
685
|
+
await this_fetch(url.toString(), {
|
|
680
686
|
headers: {
|
|
681
687
|
Accept: "application/json",
|
|
682
|
-
...
|
|
688
|
+
...this_headers,
|
|
683
689
|
...headers
|
|
684
690
|
},
|
|
685
691
|
keepalive: true,
|
|
@@ -689,13 +695,16 @@ var HTTPConnection = class _HTTPConnection {
|
|
|
689
695
|
}
|
|
690
696
|
async post(path, params, config) {
|
|
691
697
|
const { headers, ...rest } = config || {};
|
|
698
|
+
const this_fetch = this.fetch;
|
|
699
|
+
const this_base_url = this.base_url;
|
|
700
|
+
const this_headers = this.headers;
|
|
692
701
|
return await checkResponse(
|
|
693
|
-
await
|
|
702
|
+
await this_fetch(_urljoin(this_base_url, path), {
|
|
694
703
|
method: "POST",
|
|
695
704
|
headers: {
|
|
696
705
|
Accept: "application/json",
|
|
697
706
|
"Content-Type": "application/json",
|
|
698
|
-
...
|
|
707
|
+
...this_headers,
|
|
699
708
|
...headers
|
|
700
709
|
},
|
|
701
710
|
body: typeof params === "string" ? params : params ? JSON.stringify(params) : void 0,
|
|
@@ -728,6 +737,167 @@ var HTTPConnection = class _HTTPConnection {
|
|
|
728
737
|
return await resp.json();
|
|
729
738
|
}
|
|
730
739
|
};
|
|
740
|
+
var Attachment = class {
|
|
741
|
+
/**
|
|
742
|
+
* The object that replaces this `Attachment` at upload time.
|
|
743
|
+
*/
|
|
744
|
+
reference;
|
|
745
|
+
uploader;
|
|
746
|
+
data;
|
|
747
|
+
state;
|
|
748
|
+
// For debug logging only.
|
|
749
|
+
dataDebugString;
|
|
750
|
+
/**
|
|
751
|
+
* Construct an attachment.
|
|
752
|
+
*
|
|
753
|
+
* @param data A string representing the path of the file on disk, or a
|
|
754
|
+
* `Blob`/`ArrayBuffer` with the file's contents. The caller is responsible
|
|
755
|
+
* for ensuring the file/blob/buffer is not modified until upload is complete.
|
|
756
|
+
*
|
|
757
|
+
* @param filename The desired name of the file in Braintrust after uploading.
|
|
758
|
+
* This parameter is for visualization purposes only and has no effect on
|
|
759
|
+
* attachment storage.
|
|
760
|
+
*
|
|
761
|
+
* @param contentType The MIME type of the file.
|
|
762
|
+
*
|
|
763
|
+
* @param state (Optional) For internal use.
|
|
764
|
+
*/
|
|
765
|
+
constructor({ data, filename, contentType, state }) {
|
|
766
|
+
this.reference = {
|
|
767
|
+
type: BRAINTRUST_ATTACHMENT,
|
|
768
|
+
filename,
|
|
769
|
+
content_type: contentType,
|
|
770
|
+
key: newId()
|
|
771
|
+
};
|
|
772
|
+
this.state = state;
|
|
773
|
+
this.dataDebugString = typeof data === "string" ? data : "<in-memory data>";
|
|
774
|
+
this.data = this.initData(data);
|
|
775
|
+
this.uploader = this.initUploader();
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* On first access, (1) reads the attachment from disk if needed, (2)
|
|
779
|
+
* authenticates with the data plane to request a signed URL, (3) uploads to
|
|
780
|
+
* object store, and (4) updates the attachment.
|
|
781
|
+
*
|
|
782
|
+
* @returns The attachment status.
|
|
783
|
+
*/
|
|
784
|
+
async upload() {
|
|
785
|
+
return await this.uploader.get();
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* A human-readable description for logging and debugging.
|
|
789
|
+
*
|
|
790
|
+
* @returns The debug object. The return type is not stable and may change in
|
|
791
|
+
* a future release.
|
|
792
|
+
*/
|
|
793
|
+
debugInfo() {
|
|
794
|
+
return {
|
|
795
|
+
inputData: this.dataDebugString,
|
|
796
|
+
reference: this.reference,
|
|
797
|
+
state: this.state
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
initUploader() {
|
|
801
|
+
const doUpload = async (conn, orgId) => {
|
|
802
|
+
const requestParams = {
|
|
803
|
+
key: this.reference.key,
|
|
804
|
+
filename: this.reference.filename,
|
|
805
|
+
content_type: this.reference.content_type,
|
|
806
|
+
org_id: orgId
|
|
807
|
+
};
|
|
808
|
+
const [metadataPromiseResult, dataPromiseResult] = await Promise.allSettled([
|
|
809
|
+
conn.post("/attachment", requestParams),
|
|
810
|
+
this.data.get()
|
|
811
|
+
]);
|
|
812
|
+
if (metadataPromiseResult.status === "rejected") {
|
|
813
|
+
const errorStr = JSON.stringify(metadataPromiseResult.reason);
|
|
814
|
+
throw new Error(
|
|
815
|
+
`Failed to request signed URL from API server: ${errorStr}`
|
|
816
|
+
);
|
|
817
|
+
}
|
|
818
|
+
if (dataPromiseResult.status === "rejected") {
|
|
819
|
+
const errorStr = JSON.stringify(dataPromiseResult.reason);
|
|
820
|
+
throw new Error(`Failed to read file: ${errorStr}`);
|
|
821
|
+
}
|
|
822
|
+
const metadataResponse = metadataPromiseResult.value;
|
|
823
|
+
const data = dataPromiseResult.value;
|
|
824
|
+
let signedUrl;
|
|
825
|
+
let headers;
|
|
826
|
+
try {
|
|
827
|
+
({ signedUrl, headers } = z2.object({
|
|
828
|
+
signedUrl: z2.string().url(),
|
|
829
|
+
headers: z2.record(z2.string())
|
|
830
|
+
}).parse(await metadataResponse.json()));
|
|
831
|
+
} catch (error) {
|
|
832
|
+
if (error instanceof ZodError) {
|
|
833
|
+
const errorStr = JSON.stringify(error.flatten());
|
|
834
|
+
throw new Error(`Invalid response from API server: ${errorStr}`);
|
|
835
|
+
}
|
|
836
|
+
throw error;
|
|
837
|
+
}
|
|
838
|
+
let objectStoreResponse;
|
|
839
|
+
try {
|
|
840
|
+
objectStoreResponse = await checkResponse(
|
|
841
|
+
await fetch(signedUrl, {
|
|
842
|
+
method: "PUT",
|
|
843
|
+
headers,
|
|
844
|
+
body: data
|
|
845
|
+
})
|
|
846
|
+
);
|
|
847
|
+
} catch (error) {
|
|
848
|
+
if (error instanceof FailedHTTPResponse) {
|
|
849
|
+
throw new Error(
|
|
850
|
+
`Failed to upload attachment to object store: ${error.status} ${error.text} ${error.data}`
|
|
851
|
+
);
|
|
852
|
+
}
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
855
|
+
return { signedUrl, metadataResponse, objectStoreResponse };
|
|
856
|
+
};
|
|
857
|
+
const errorWrapper = async () => {
|
|
858
|
+
const status = { upload_status: "done" };
|
|
859
|
+
const state = this.state ?? _globalState;
|
|
860
|
+
await state.login({});
|
|
861
|
+
const conn = state.apiConn();
|
|
862
|
+
const orgId = state.orgId ?? "";
|
|
863
|
+
try {
|
|
864
|
+
await doUpload(conn, orgId);
|
|
865
|
+
} catch (error) {
|
|
866
|
+
status.upload_status = "error";
|
|
867
|
+
status.error_message = error instanceof Error ? error.message : JSON.stringify(error);
|
|
868
|
+
}
|
|
869
|
+
const requestParams = {
|
|
870
|
+
key: this.reference.key,
|
|
871
|
+
org_id: orgId,
|
|
872
|
+
status
|
|
873
|
+
};
|
|
874
|
+
const statusResponse = await conn.post(
|
|
875
|
+
"/attachment/status",
|
|
876
|
+
requestParams
|
|
877
|
+
);
|
|
878
|
+
if (!statusResponse.ok) {
|
|
879
|
+
const errorStr = JSON.stringify(statusResponse);
|
|
880
|
+
throw new Error(`Couldn't log attachment status: ${errorStr}`);
|
|
881
|
+
}
|
|
882
|
+
return status;
|
|
883
|
+
};
|
|
884
|
+
return new LazyValue(errorWrapper);
|
|
885
|
+
}
|
|
886
|
+
initData(data) {
|
|
887
|
+
if (typeof data === "string") {
|
|
888
|
+
const readFile = isomorph_default.readFile;
|
|
889
|
+
if (!readFile) {
|
|
890
|
+
throw new Error(
|
|
891
|
+
`This platform does not support reading the filesystem. Construct the Attachment
|
|
892
|
+
with a Blob/ArrayBuffer, or run the program on Node.js.`
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
return new LazyValue(async () => new Blob([await readFile(data)]));
|
|
896
|
+
} else {
|
|
897
|
+
return new LazyValue(async () => new Blob([data]));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
};
|
|
731
901
|
function logFeedbackImpl(state, parentObjectType, parentObjectId, {
|
|
732
902
|
id,
|
|
733
903
|
expected,
|
|
@@ -752,7 +922,7 @@ function logFeedbackImpl(state, parentObjectType, parentObjectId, {
|
|
|
752
922
|
expected,
|
|
753
923
|
tags
|
|
754
924
|
});
|
|
755
|
-
let { metadata, ...updateEvent } = validatedEvent;
|
|
925
|
+
let { metadata, ...updateEvent } = deepCopyEvent(validatedEvent);
|
|
756
926
|
updateEvent = Object.fromEntries(
|
|
757
927
|
Object.entries(updateEvent).filter(([_, v]) => !isEmpty(v))
|
|
758
928
|
);
|
|
@@ -801,10 +971,12 @@ function updateSpanImpl({
|
|
|
801
971
|
id,
|
|
802
972
|
event
|
|
803
973
|
}) {
|
|
804
|
-
const updateEvent =
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
974
|
+
const updateEvent = deepCopyEvent(
|
|
975
|
+
validateAndSanitizeExperimentLogPartialArgs({
|
|
976
|
+
id,
|
|
977
|
+
...event
|
|
978
|
+
})
|
|
979
|
+
);
|
|
808
980
|
const parentIds = async () => new SpanComponentsV3({
|
|
809
981
|
object_type: parentObjectType,
|
|
810
982
|
object_id: await parentObjectId.get()
|
|
@@ -1005,7 +1177,7 @@ var Logger = class {
|
|
|
1005
1177
|
* @param event.id: (Optional) a unique identifier for the event. If you don't provide one, BrainTrust will generate one for you.
|
|
1006
1178
|
* @param options Additional logging options
|
|
1007
1179
|
* @param options.allowConcurrentWithSpans in rare cases where you need to log at the top level separately from spans on the logger elsewhere, set this to true.
|
|
1008
|
-
*
|
|
1180
|
+
* @returns The `id` of the logged event.
|
|
1009
1181
|
*/
|
|
1010
1182
|
log(event, options) {
|
|
1011
1183
|
if (this.calledStartSpan && !options?.allowConcurrentWithSpans) {
|
|
@@ -1028,7 +1200,7 @@ var Logger = class {
|
|
|
1028
1200
|
/**
|
|
1029
1201
|
* Create a new toplevel span underneath the logger. The name defaults to "root".
|
|
1030
1202
|
*
|
|
1031
|
-
* See
|
|
1203
|
+
* See {@link Span.traced} for full details.
|
|
1032
1204
|
*/
|
|
1033
1205
|
traced(callback, args) {
|
|
1034
1206
|
const { setCurrent, ...argsRest } = args ?? {};
|
|
@@ -1062,7 +1234,7 @@ var Logger = class {
|
|
|
1062
1234
|
* where you cannot use callbacks. However, spans started with `startSpan` will not be marked as the "current span",
|
|
1063
1235
|
* so `currentSpan()` and `traced()` will be no-ops. If you want to mark a span as current, use `traced` instead.
|
|
1064
1236
|
*
|
|
1065
|
-
* See
|
|
1237
|
+
* See {@link traced} for full details.
|
|
1066
1238
|
*/
|
|
1067
1239
|
startSpan(args) {
|
|
1068
1240
|
this.calledStartSpan = true;
|
|
@@ -1102,7 +1274,7 @@ var Logger = class {
|
|
|
1102
1274
|
* Update a span in the experiment using its id. It is important that you only update a span once the original span has been fully written and flushed,
|
|
1103
1275
|
* since otherwise updates to the span may conflict with the original span.
|
|
1104
1276
|
*
|
|
1105
|
-
* @param event The event data to update the span with. Must include `id`. See
|
|
1277
|
+
* @param event The event data to update the span with. Must include `id`. See {@link Experiment.log} for a full list of valid fields.
|
|
1106
1278
|
*/
|
|
1107
1279
|
updateSpan(event) {
|
|
1108
1280
|
const { id, ...eventRest } = event;
|
|
@@ -1118,7 +1290,9 @@ var Logger = class {
|
|
|
1118
1290
|
});
|
|
1119
1291
|
}
|
|
1120
1292
|
/**
|
|
1121
|
-
* Return a serialized representation of the logger that can be used to start subspans in other places.
|
|
1293
|
+
* Return a serialized representation of the logger that can be used to start subspans in other places.
|
|
1294
|
+
*
|
|
1295
|
+
* See {@link Span.startSpan} for more details.
|
|
1122
1296
|
*/
|
|
1123
1297
|
async export() {
|
|
1124
1298
|
return new SpanComponentsV3({
|
|
@@ -1158,6 +1332,7 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1158
1332
|
activeFlush = Promise.resolve();
|
|
1159
1333
|
activeFlushResolved = true;
|
|
1160
1334
|
activeFlushError = void 0;
|
|
1335
|
+
onFlushError;
|
|
1161
1336
|
syncFlush = false;
|
|
1162
1337
|
// 6 MB for the AWS lambda gateway (from our own testing).
|
|
1163
1338
|
maxRequestSize = 6 * 1024 * 1024;
|
|
@@ -1221,6 +1396,7 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1221
1396
|
await this.flush();
|
|
1222
1397
|
});
|
|
1223
1398
|
}
|
|
1399
|
+
this.onFlushError = opts.onFlushError;
|
|
1224
1400
|
}
|
|
1225
1401
|
log(items) {
|
|
1226
1402
|
const [addedItems, droppedItems] = (() => {
|
|
@@ -1252,14 +1428,16 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1252
1428
|
if (this.activeFlushError) {
|
|
1253
1429
|
const err = this.activeFlushError;
|
|
1254
1430
|
this.activeFlushError = void 0;
|
|
1255
|
-
|
|
1431
|
+
if (this.syncFlush) {
|
|
1432
|
+
throw err;
|
|
1433
|
+
}
|
|
1256
1434
|
}
|
|
1257
1435
|
}
|
|
1258
1436
|
async flushOnce(args) {
|
|
1259
1437
|
const batchSize = args?.batchSize ?? this.defaultBatchSize;
|
|
1260
1438
|
const wrappedItems = this.items;
|
|
1261
1439
|
this.items = [];
|
|
1262
|
-
const allItems = await this.unwrapLazyValues(wrappedItems);
|
|
1440
|
+
const [allItems, attachments] = await this.unwrapLazyValues(wrappedItems);
|
|
1263
1441
|
if (allItems.length === 0) {
|
|
1264
1442
|
return;
|
|
1265
1443
|
}
|
|
@@ -1291,6 +1469,23 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1291
1469
|
);
|
|
1292
1470
|
}
|
|
1293
1471
|
}
|
|
1472
|
+
const attachmentErrors = [];
|
|
1473
|
+
for (const attachment of attachments) {
|
|
1474
|
+
try {
|
|
1475
|
+
const result = await attachment.upload();
|
|
1476
|
+
if (result.upload_status === "error" && result.error_message) {
|
|
1477
|
+
attachmentErrors.push(new Error(result.error_message));
|
|
1478
|
+
}
|
|
1479
|
+
} catch (error) {
|
|
1480
|
+
attachmentErrors.push(error);
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
if (attachmentErrors.length > 0) {
|
|
1484
|
+
throw new AggregateError(
|
|
1485
|
+
attachmentErrors,
|
|
1486
|
+
`Encountered the following errors while uploading attachments:`
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1294
1489
|
if (this.items.length > 0) {
|
|
1295
1490
|
await this.flushOnce(args);
|
|
1296
1491
|
}
|
|
@@ -1298,8 +1493,10 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1298
1493
|
async unwrapLazyValues(wrappedItems) {
|
|
1299
1494
|
for (let i = 0; i < this.numTries; ++i) {
|
|
1300
1495
|
try {
|
|
1301
|
-
const
|
|
1302
|
-
|
|
1496
|
+
const items = await Promise.all(wrappedItems.map((x) => x.get()));
|
|
1497
|
+
const attachments = [];
|
|
1498
|
+
items.forEach((item) => extractAttachments(item, attachments));
|
|
1499
|
+
return [mergeRowBatch(items), attachments];
|
|
1303
1500
|
} catch (e) {
|
|
1304
1501
|
let errmsg = "Encountered error when constructing records to flush";
|
|
1305
1502
|
const isRetrying = i + 1 < this.numTries;
|
|
@@ -1307,7 +1504,10 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1307
1504
|
errmsg += ". Retrying";
|
|
1308
1505
|
}
|
|
1309
1506
|
console.warn(errmsg);
|
|
1310
|
-
if (!isRetrying
|
|
1507
|
+
if (!isRetrying) {
|
|
1508
|
+
console.warn(
|
|
1509
|
+
`Failed to construct log records to flush after ${this.numTries} attempts. Dropping batch`
|
|
1510
|
+
);
|
|
1311
1511
|
throw e;
|
|
1312
1512
|
} else {
|
|
1313
1513
|
console.warn(e);
|
|
@@ -1315,10 +1515,7 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1315
1515
|
}
|
|
1316
1516
|
}
|
|
1317
1517
|
}
|
|
1318
|
-
|
|
1319
|
-
`Failed to construct log records to flush after ${this.numTries} attempts. Dropping batch`
|
|
1320
|
-
);
|
|
1321
|
-
return [];
|
|
1518
|
+
throw new Error("Impossible");
|
|
1322
1519
|
}
|
|
1323
1520
|
async submitLogsRequest(items) {
|
|
1324
1521
|
const conn = await this.apiConn.get();
|
|
@@ -1334,16 +1531,14 @@ var BackgroundLogger = class _BackgroundLogger {
|
|
|
1334
1531
|
let error = void 0;
|
|
1335
1532
|
try {
|
|
1336
1533
|
await conn.post_json("logs3", dataStr);
|
|
1337
|
-
} catch
|
|
1534
|
+
} catch {
|
|
1338
1535
|
try {
|
|
1339
1536
|
const legacyDataS = constructJsonArray(
|
|
1340
|
-
items.map(
|
|
1341
|
-
(r) => JSON.stringify(makeLegacyEvent(JSON.parse(r)))
|
|
1342
|
-
)
|
|
1537
|
+
items.map((r) => JSON.stringify(makeLegacyEvent(JSON.parse(r))))
|
|
1343
1538
|
);
|
|
1344
1539
|
await conn.post_json("logs", legacyDataS);
|
|
1345
|
-
} catch (
|
|
1346
|
-
error =
|
|
1540
|
+
} catch (e) {
|
|
1541
|
+
error = e;
|
|
1347
1542
|
}
|
|
1348
1543
|
}
|
|
1349
1544
|
if (error === void 0) {
|
|
@@ -1367,7 +1562,10 @@ Error: ${errorText}`;
|
|
|
1367
1562
|
});
|
|
1368
1563
|
this.logFailedPayloadsDir();
|
|
1369
1564
|
}
|
|
1370
|
-
if (!isRetrying
|
|
1565
|
+
if (!isRetrying) {
|
|
1566
|
+
console.warn(
|
|
1567
|
+
`log request failed after ${this.numTries} retries. Dropping batch`
|
|
1568
|
+
);
|
|
1371
1569
|
throw new Error(errMsg);
|
|
1372
1570
|
} else {
|
|
1373
1571
|
console.warn(errMsg);
|
|
@@ -1376,10 +1574,6 @@ Error: ${errorText}`;
|
|
|
1376
1574
|
}
|
|
1377
1575
|
}
|
|
1378
1576
|
}
|
|
1379
|
-
console.warn(
|
|
1380
|
-
`log request failed after ${this.numTries} retries. Dropping batch`
|
|
1381
|
-
);
|
|
1382
|
-
return;
|
|
1383
1577
|
}
|
|
1384
1578
|
registerDroppedItemCount(numItems) {
|
|
1385
1579
|
if (numItems <= 0) {
|
|
@@ -1407,15 +1601,17 @@ Error: ${errorText}`;
|
|
|
1407
1601
|
return;
|
|
1408
1602
|
}
|
|
1409
1603
|
try {
|
|
1410
|
-
const allItems = await this.unwrapLazyValues(wrappedItems);
|
|
1604
|
+
const [allItems, allAttachments] = await this.unwrapLazyValues(wrappedItems);
|
|
1411
1605
|
const dataStr = constructLogs3Data(
|
|
1412
1606
|
allItems.map((x) => JSON.stringify(x))
|
|
1413
1607
|
);
|
|
1608
|
+
const attachmentStr = JSON.stringify(
|
|
1609
|
+
allAttachments.map((a) => a.debugInfo())
|
|
1610
|
+
);
|
|
1611
|
+
const payload = `{"data": ${dataStr}, "attachments": ${attachmentStr}}
|
|
1612
|
+
`;
|
|
1414
1613
|
for (const payloadDir of publishPayloadsDir) {
|
|
1415
|
-
await _BackgroundLogger.writePayloadToDir({
|
|
1416
|
-
payloadDir,
|
|
1417
|
-
payload: dataStr
|
|
1418
|
-
});
|
|
1614
|
+
await _BackgroundLogger.writePayloadToDir({ payloadDir, payload });
|
|
1419
1615
|
}
|
|
1420
1616
|
} catch (e) {
|
|
1421
1617
|
console.error(e);
|
|
@@ -1454,6 +1650,13 @@ Error: ${errorText}`;
|
|
|
1454
1650
|
try {
|
|
1455
1651
|
await this.flushOnce();
|
|
1456
1652
|
} catch (err) {
|
|
1653
|
+
if (err instanceof AggregateError) {
|
|
1654
|
+
for (const e of err.errors) {
|
|
1655
|
+
this.onFlushError?.(e);
|
|
1656
|
+
}
|
|
1657
|
+
} else {
|
|
1658
|
+
this.onFlushError?.(err);
|
|
1659
|
+
}
|
|
1457
1660
|
this.activeFlushError = err;
|
|
1458
1661
|
} finally {
|
|
1459
1662
|
this.activeFlushResolved = true;
|
|
@@ -1496,7 +1699,7 @@ function init(projectOrOptions, optionalOptions) {
|
|
|
1496
1699
|
apiKey,
|
|
1497
1700
|
orgName,
|
|
1498
1701
|
forceLogin,
|
|
1499
|
-
fetch,
|
|
1702
|
+
fetch: fetch2,
|
|
1500
1703
|
metadata,
|
|
1501
1704
|
gitMetadataSettings,
|
|
1502
1705
|
projectId,
|
|
@@ -1514,7 +1717,7 @@ function init(projectOrOptions, optionalOptions) {
|
|
|
1514
1717
|
}
|
|
1515
1718
|
const lazyMetadata2 = new LazyValue(
|
|
1516
1719
|
async () => {
|
|
1517
|
-
await state.login({ apiKey, appUrl, orgName, fetch, forceLogin });
|
|
1720
|
+
await state.login({ apiKey, appUrl, orgName, fetch: fetch2, forceLogin });
|
|
1518
1721
|
const args = {
|
|
1519
1722
|
project_name: project,
|
|
1520
1723
|
project_id: projectId,
|
|
@@ -1685,7 +1888,7 @@ function initDataset(projectOrOptions, optionalOptions) {
|
|
|
1685
1888
|
appUrl,
|
|
1686
1889
|
apiKey,
|
|
1687
1890
|
orgName,
|
|
1688
|
-
fetch,
|
|
1891
|
+
fetch: fetch2,
|
|
1689
1892
|
forceLogin,
|
|
1690
1893
|
projectId,
|
|
1691
1894
|
metadata,
|
|
@@ -1699,7 +1902,7 @@ function initDataset(projectOrOptions, optionalOptions) {
|
|
|
1699
1902
|
orgName,
|
|
1700
1903
|
apiKey,
|
|
1701
1904
|
appUrl,
|
|
1702
|
-
fetch,
|
|
1905
|
+
fetch: fetch2,
|
|
1703
1906
|
forceLogin
|
|
1704
1907
|
});
|
|
1705
1908
|
const args = {
|
|
@@ -1781,7 +1984,7 @@ function initLogger(options = {}) {
|
|
|
1781
1984
|
apiKey,
|
|
1782
1985
|
orgName,
|
|
1783
1986
|
forceLogin,
|
|
1784
|
-
fetch,
|
|
1987
|
+
fetch: fetch2,
|
|
1785
1988
|
state: stateArg
|
|
1786
1989
|
} = options || {};
|
|
1787
1990
|
const computeMetadataArgs = {
|
|
@@ -1796,7 +1999,7 @@ function initLogger(options = {}) {
|
|
|
1796
1999
|
apiKey,
|
|
1797
2000
|
appUrl,
|
|
1798
2001
|
forceLogin,
|
|
1799
|
-
fetch
|
|
2002
|
+
fetch: fetch2
|
|
1800
2003
|
});
|
|
1801
2004
|
return computeLoggerMetadata(state, computeMetadataArgs);
|
|
1802
2005
|
}
|
|
@@ -1820,7 +2023,7 @@ async function loadPrompt({
|
|
|
1820
2023
|
appUrl,
|
|
1821
2024
|
apiKey,
|
|
1822
2025
|
orgName,
|
|
1823
|
-
fetch,
|
|
2026
|
+
fetch: fetch2,
|
|
1824
2027
|
forceLogin,
|
|
1825
2028
|
state: stateArg
|
|
1826
2029
|
}) {
|
|
@@ -1835,7 +2038,7 @@ async function loadPrompt({
|
|
|
1835
2038
|
orgName,
|
|
1836
2039
|
apiKey,
|
|
1837
2040
|
appUrl,
|
|
1838
|
-
fetch,
|
|
2041
|
+
fetch: fetch2,
|
|
1839
2042
|
forceLogin
|
|
1840
2043
|
});
|
|
1841
2044
|
const args = {
|
|
@@ -1886,7 +2089,7 @@ async function loginToState(options = {}) {
|
|
|
1886
2089
|
appUrl = isomorph_default.getEnv("BRAINTRUST_APP_URL") || "https://www.braintrust.dev",
|
|
1887
2090
|
apiKey = isomorph_default.getEnv("BRAINTRUST_API_KEY"),
|
|
1888
2091
|
orgName = isomorph_default.getEnv("BRAINTRUST_ORG_NAME"),
|
|
1889
|
-
fetch = globalThis.fetch
|
|
2092
|
+
fetch: fetch2 = globalThis.fetch
|
|
1890
2093
|
} = options || {};
|
|
1891
2094
|
const appPublicUrl = isomorph_default.getEnv("BRAINTRUST_APP_PUBLIC_URL") || appUrl;
|
|
1892
2095
|
const state = new BraintrustState(options);
|
|
@@ -1896,7 +2099,7 @@ async function loginToState(options = {}) {
|
|
|
1896
2099
|
let conn = null;
|
|
1897
2100
|
if (apiKey !== void 0) {
|
|
1898
2101
|
const resp = await checkResponse(
|
|
1899
|
-
await
|
|
2102
|
+
await fetch2(_urljoin(state.appUrl, `/api/apikey/login`), {
|
|
1900
2103
|
method: "POST",
|
|
1901
2104
|
headers: {
|
|
1902
2105
|
"Content-Type": "application/json",
|
|
@@ -2064,8 +2267,8 @@ async function flush(options) {
|
|
|
2064
2267
|
const state = options?.state ?? _globalState;
|
|
2065
2268
|
return await state.bgLogger().flush();
|
|
2066
2269
|
}
|
|
2067
|
-
function setFetch(
|
|
2068
|
-
_globalState.setFetch(
|
|
2270
|
+
function setFetch(fetch2) {
|
|
2271
|
+
_globalState.setFetch(fetch2);
|
|
2069
2272
|
}
|
|
2070
2273
|
function startSpanAndIsLogger(args) {
|
|
2071
2274
|
const state = args?.state ?? _globalState;
|
|
@@ -2193,6 +2396,47 @@ function validateAndSanitizeExperimentLogPartialArgs(event) {
|
|
|
2193
2396
|
return { ...event };
|
|
2194
2397
|
}
|
|
2195
2398
|
}
|
|
2399
|
+
function deepCopyEvent(event) {
|
|
2400
|
+
const attachments = [];
|
|
2401
|
+
const IDENTIFIER = "_bt_internal_saved_attachment";
|
|
2402
|
+
const savedAttachmentSchema = z2.strictObject({ [IDENTIFIER]: z2.number() });
|
|
2403
|
+
const serialized = JSON.stringify(event, (_k, v) => {
|
|
2404
|
+
if (v instanceof SpanImpl || v instanceof NoopSpan) {
|
|
2405
|
+
return `<span>`;
|
|
2406
|
+
} else if (v instanceof Experiment) {
|
|
2407
|
+
return `<experiment>`;
|
|
2408
|
+
} else if (v instanceof Dataset) {
|
|
2409
|
+
return `<dataset>`;
|
|
2410
|
+
} else if (v instanceof Logger) {
|
|
2411
|
+
return `<logger>`;
|
|
2412
|
+
} else if (v instanceof Attachment) {
|
|
2413
|
+
const idx = attachments.push(v);
|
|
2414
|
+
return { [IDENTIFIER]: idx - 1 };
|
|
2415
|
+
}
|
|
2416
|
+
return v;
|
|
2417
|
+
});
|
|
2418
|
+
const x = JSON.parse(serialized, (_k, v) => {
|
|
2419
|
+
const parsedAttachment = savedAttachmentSchema.safeParse(v);
|
|
2420
|
+
if (parsedAttachment.success) {
|
|
2421
|
+
return attachments[parsedAttachment.data[IDENTIFIER]];
|
|
2422
|
+
}
|
|
2423
|
+
return v;
|
|
2424
|
+
});
|
|
2425
|
+
return x;
|
|
2426
|
+
}
|
|
2427
|
+
function extractAttachments(event, attachments) {
|
|
2428
|
+
for (const [key, value] of Object.entries(event)) {
|
|
2429
|
+
if (value instanceof Attachment) {
|
|
2430
|
+
attachments.push(value);
|
|
2431
|
+
event[key] = value.reference;
|
|
2432
|
+
continue;
|
|
2433
|
+
}
|
|
2434
|
+
if (!(value instanceof Object)) {
|
|
2435
|
+
continue;
|
|
2436
|
+
}
|
|
2437
|
+
extractAttachments(value, attachments);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2196
2440
|
function validateAndSanitizeExperimentLogFullArgs(event, hasDataset) {
|
|
2197
2441
|
if ("input" in event && !isEmpty(event.input) && "inputs" in event && !isEmpty(event.inputs) || !("input" in event) && !("inputs" in event)) {
|
|
2198
2442
|
throw new Error(
|
|
@@ -2323,10 +2567,9 @@ var Experiment = class extends ObjectFetcher {
|
|
|
2323
2567
|
* @param event.metrics: (Optional) a dictionary of metrics to log. The following keys are populated automatically: "start", "end".
|
|
2324
2568
|
* @param event.id: (Optional) a unique identifier for the event. If you don't provide one, BrainTrust will generate one for you.
|
|
2325
2569
|
* @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.
|
|
2326
|
-
* @param event.inputs: (Deprecated) the same as `input` (will be removed in a future version).
|
|
2327
2570
|
* @param options Additional logging options
|
|
2328
2571
|
* @param options.allowConcurrentWithSpans in rare cases where you need to log at the top level separately from spans on the experiment elsewhere, set this to true.
|
|
2329
|
-
*
|
|
2572
|
+
* @returns The `id` of the logged event.
|
|
2330
2573
|
*/
|
|
2331
2574
|
log(event, options) {
|
|
2332
2575
|
if (this.calledStartSpan && !options?.allowConcurrentWithSpans) {
|
|
@@ -2342,7 +2585,7 @@ var Experiment = class extends ObjectFetcher {
|
|
|
2342
2585
|
/**
|
|
2343
2586
|
* Create a new toplevel span underneath the experiment. The name defaults to "root".
|
|
2344
2587
|
*
|
|
2345
|
-
* See
|
|
2588
|
+
* See {@link Span.traced} for full details.
|
|
2346
2589
|
*/
|
|
2347
2590
|
traced(callback, args) {
|
|
2348
2591
|
const { setCurrent, ...argsRest } = args ?? {};
|
|
@@ -2368,7 +2611,7 @@ var Experiment = class extends ObjectFetcher {
|
|
|
2368
2611
|
* where you cannot use callbacks. However, spans started with `startSpan` will not be marked as the "current span",
|
|
2369
2612
|
* so `currentSpan()` and `traced()` will be no-ops. If you want to mark a span as current, use `traced` instead.
|
|
2370
2613
|
*
|
|
2371
|
-
* See
|
|
2614
|
+
* See {@link traced} for full details.
|
|
2372
2615
|
*/
|
|
2373
2616
|
startSpan(args) {
|
|
2374
2617
|
this.calledStartSpan = true;
|
|
@@ -2480,7 +2723,7 @@ var Experiment = class extends ObjectFetcher {
|
|
|
2480
2723
|
* Update a span in the experiment using its id. It is important that you only update a span once the original span has been fully written and flushed,
|
|
2481
2724
|
* since otherwise updates to the span may conflict with the original span.
|
|
2482
2725
|
*
|
|
2483
|
-
* @param event The event data to update the span with. Must include `id`. See
|
|
2726
|
+
* @param event The event data to update the span with. Must include `id`. See {@link Experiment.log} for a full list of valid fields.
|
|
2484
2727
|
*/
|
|
2485
2728
|
updateSpan(event) {
|
|
2486
2729
|
const { id, ...eventRest } = event;
|
|
@@ -2496,7 +2739,9 @@ var Experiment = class extends ObjectFetcher {
|
|
|
2496
2739
|
});
|
|
2497
2740
|
}
|
|
2498
2741
|
/**
|
|
2499
|
-
* Return a serialized representation of the experiment that can be used to start subspans in other places.
|
|
2742
|
+
* Return a serialized representation of the experiment that can be used to start subspans in other places.
|
|
2743
|
+
*
|
|
2744
|
+
* See {@link Span.startSpan} for more details.
|
|
2500
2745
|
*/
|
|
2501
2746
|
async export() {
|
|
2502
2747
|
return new SpanComponentsV3({
|
|
@@ -2511,7 +2756,7 @@ var Experiment = class extends ObjectFetcher {
|
|
|
2511
2756
|
return await this.state.bgLogger().flush();
|
|
2512
2757
|
}
|
|
2513
2758
|
/**
|
|
2514
|
-
* This function is deprecated. You can simply remove it from your code.
|
|
2759
|
+
* @deprecated This function is deprecated. You can simply remove it from your code.
|
|
2515
2760
|
*/
|
|
2516
2761
|
async close() {
|
|
2517
2762
|
console.warn(
|
|
@@ -2653,27 +2898,14 @@ var SpanImpl = class _SpanImpl {
|
|
|
2653
2898
|
event,
|
|
2654
2899
|
internalData
|
|
2655
2900
|
});
|
|
2656
|
-
|
|
2901
|
+
const partialRecord = deepCopyEvent({
|
|
2657
2902
|
id: this.id,
|
|
2658
2903
|
span_id: this.spanId,
|
|
2659
2904
|
root_span_id: this.rootSpanId,
|
|
2660
2905
|
span_parents: this.spanParents,
|
|
2661
2906
|
...serializableInternalData,
|
|
2662
2907
|
[IS_MERGE_FIELD]: this.isMerge
|
|
2663
|
-
};
|
|
2664
|
-
const serializedPartialRecord = JSON.stringify(partialRecord, (_k, v) => {
|
|
2665
|
-
if (v instanceof _SpanImpl) {
|
|
2666
|
-
return `<span>`;
|
|
2667
|
-
} else if (v instanceof Experiment) {
|
|
2668
|
-
return `<experiment>`;
|
|
2669
|
-
} else if (v instanceof Dataset) {
|
|
2670
|
-
return `<dataset>`;
|
|
2671
|
-
} else if (v instanceof Logger) {
|
|
2672
|
-
return `<logger>`;
|
|
2673
|
-
}
|
|
2674
|
-
return v;
|
|
2675
2908
|
});
|
|
2676
|
-
partialRecord = JSON.parse(serializedPartialRecord);
|
|
2677
2909
|
if (partialRecord.metrics?.end) {
|
|
2678
2910
|
this.loggedEndTime = partialRecord.metrics?.end;
|
|
2679
2911
|
}
|
|
@@ -2759,6 +2991,11 @@ var SpanImpl = class _SpanImpl {
|
|
|
2759
2991
|
propagated_event: this.propagatedEvent
|
|
2760
2992
|
}).toStr();
|
|
2761
2993
|
}
|
|
2994
|
+
async permalink() {
|
|
2995
|
+
return await permalink(await this.export(), {
|
|
2996
|
+
state: this.state
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2762
2999
|
async flush() {
|
|
2763
3000
|
return await this.state.bgLogger().flush();
|
|
2764
3001
|
}
|
|
@@ -2908,15 +3145,17 @@ var Dataset = class extends ObjectFetcher {
|
|
|
2908
3145
|
}) {
|
|
2909
3146
|
this.validateEvent({ metadata, expected, output, tags });
|
|
2910
3147
|
const rowId = id || uuidv4();
|
|
2911
|
-
const args = this.createArgs(
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3148
|
+
const args = this.createArgs(
|
|
3149
|
+
deepCopyEvent({
|
|
3150
|
+
id: rowId,
|
|
3151
|
+
input,
|
|
3152
|
+
expected,
|
|
3153
|
+
metadata,
|
|
3154
|
+
tags,
|
|
3155
|
+
output,
|
|
3156
|
+
isMerge: false
|
|
3157
|
+
})
|
|
3158
|
+
);
|
|
2920
3159
|
this.state.bgLogger().log([args]);
|
|
2921
3160
|
return rowId;
|
|
2922
3161
|
}
|
|
@@ -2941,14 +3180,16 @@ var Dataset = class extends ObjectFetcher {
|
|
|
2941
3180
|
id
|
|
2942
3181
|
}) {
|
|
2943
3182
|
this.validateEvent({ metadata, expected, tags });
|
|
2944
|
-
const args = this.createArgs(
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
3183
|
+
const args = this.createArgs(
|
|
3184
|
+
deepCopyEvent({
|
|
3185
|
+
id,
|
|
3186
|
+
input,
|
|
3187
|
+
expected,
|
|
3188
|
+
metadata,
|
|
3189
|
+
tags,
|
|
3190
|
+
isMerge: true
|
|
3191
|
+
})
|
|
3192
|
+
);
|
|
2952
3193
|
this.state.bgLogger().log([args]);
|
|
2953
3194
|
return id;
|
|
2954
3195
|
}
|
|
@@ -3003,7 +3244,7 @@ var Dataset = class extends ObjectFetcher {
|
|
|
3003
3244
|
return await this.state.bgLogger().flush();
|
|
3004
3245
|
}
|
|
3005
3246
|
/**
|
|
3006
|
-
* This function is deprecated. You can simply remove it from your code.
|
|
3247
|
+
* @deprecated This function is deprecated. You can simply remove it from your code.
|
|
3007
3248
|
*/
|
|
3008
3249
|
async close() {
|
|
3009
3250
|
console.warn(
|
|
@@ -3167,6 +3408,7 @@ var Prompt = class {
|
|
|
3167
3408
|
return this.parsedPromptData;
|
|
3168
3409
|
}
|
|
3169
3410
|
};
|
|
3411
|
+
var _exportsForTestingOnly = { extractAttachments, deepCopyEvent };
|
|
3170
3412
|
|
|
3171
3413
|
// src/browser-config.ts
|
|
3172
3414
|
var browserConfigured = false;
|
|
@@ -3193,6 +3435,7 @@ function configureBrowser() {
|
|
|
3193
3435
|
// src/exports-browser.ts
|
|
3194
3436
|
var exports_browser_exports = {};
|
|
3195
3437
|
__export(exports_browser_exports, {
|
|
3438
|
+
Attachment: () => Attachment,
|
|
3196
3439
|
BraintrustState: () => BraintrustState,
|
|
3197
3440
|
BraintrustStream: () => BraintrustStream,
|
|
3198
3441
|
Dataset: () => Dataset,
|
|
@@ -3206,6 +3449,7 @@ __export(exports_browser_exports, {
|
|
|
3206
3449
|
ReadonlyExperiment: () => ReadonlyExperiment,
|
|
3207
3450
|
SpanImpl: () => SpanImpl,
|
|
3208
3451
|
X_CACHED_HEADER: () => X_CACHED_HEADER,
|
|
3452
|
+
_exportsForTestingOnly: () => _exportsForTestingOnly,
|
|
3209
3453
|
_internalGetGlobalState: () => _internalGetGlobalState,
|
|
3210
3454
|
_internalSetInitialState: () => _internalSetInitialState,
|
|
3211
3455
|
braintrustStreamChunkSchema: () => braintrustStreamChunkSchema,
|
|
@@ -3256,7 +3500,7 @@ async function invoke(args) {
|
|
|
3256
3500
|
apiKey,
|
|
3257
3501
|
appUrl,
|
|
3258
3502
|
forceLogin,
|
|
3259
|
-
fetch,
|
|
3503
|
+
fetch: fetch2,
|
|
3260
3504
|
input,
|
|
3261
3505
|
messages,
|
|
3262
3506
|
parent: parentArg,
|
|
@@ -3272,7 +3516,7 @@ async function invoke(args) {
|
|
|
3272
3516
|
apiKey,
|
|
3273
3517
|
appUrl,
|
|
3274
3518
|
forceLogin,
|
|
3275
|
-
fetch
|
|
3519
|
+
fetch: fetch2
|
|
3276
3520
|
});
|
|
3277
3521
|
const parent = parentArg ? typeof parentArg === "string" ? parentArg : await parentArg.export() : await getSpanParentObject().export();
|
|
3278
3522
|
const functionId = functionIdSchema.safeParse({
|
|
@@ -3699,6 +3943,7 @@ var WrapperStream = class {
|
|
|
3699
3943
|
configureBrowser();
|
|
3700
3944
|
var browser_default = exports_browser_exports;
|
|
3701
3945
|
export {
|
|
3946
|
+
Attachment,
|
|
3702
3947
|
BraintrustState,
|
|
3703
3948
|
BraintrustStream,
|
|
3704
3949
|
Dataset,
|
|
@@ -3712,6 +3957,7 @@ export {
|
|
|
3712
3957
|
ReadonlyExperiment,
|
|
3713
3958
|
SpanImpl,
|
|
3714
3959
|
X_CACHED_HEADER,
|
|
3960
|
+
_exportsForTestingOnly,
|
|
3715
3961
|
_internalGetGlobalState,
|
|
3716
3962
|
_internalSetInitialState,
|
|
3717
3963
|
braintrustStreamChunkSchema,
|