diff --git a/.changeset/tame-groups-open.md b/.changeset/tame-groups-open.md new file mode 100644 index 000000000..8be60d1dd --- /dev/null +++ b/.changeset/tame-groups-open.md @@ -0,0 +1,5 @@ +--- +"braintrust": patch +--- + +Preserve dataset filters as arrays of clauses when creating experiments, allowing the experiment to store the original unnormalized filter form. diff --git a/js/src/logger.test.ts b/js/src/logger.test.ts index 21231d8e0..2d1775c61 100644 --- a/js/src/logger.test.ts +++ b/js/src/logger.test.ts @@ -484,7 +484,7 @@ function mockInitGitMetadata() { ).mockResolvedValue([]); } -test("init forwards dataset _internal_btql to experiment register", async () => { +test("init preserves dataset _internal_btql before experiment register", async () => { const state = await _exportsForTestingOnly.simulateLoginForTests(); try { @@ -504,7 +504,6 @@ test("init forwards dataset _internal_btql to experiment register", async () => }, ], }; - let experimentRegisterBody: unknown; vi.spyOn(state.appConn(), "post_json") .mockResolvedValueOnce({ @@ -564,7 +563,7 @@ test("init forwards dataset _internal_btql to experiment register", async () => } }); -test("dataset fetch forwards _internal_btql filter arrays to btql", async () => { +test("dataset fetch normalizes _internal_btql filter arrays before btql", async () => { const state = await _exportsForTestingOnly.simulateLoginForTests(); try { @@ -584,6 +583,13 @@ test("dataset fetch forwards _internal_btql filter arrays to btql", async () => ], limit: 5, }; + const normalizedDatasetFilter = { + filter: { + op: "and", + children: datasetFilter.filter, + }, + limit: 5, + }; vi.spyOn(state.appConn(), "post_json").mockResolvedValue({ project: { @@ -623,7 +629,7 @@ test("dataset fetch forwards _internal_btql filter arrays to btql", async () => expect(btqlBody).toEqual( expect.objectContaining({ query: expect.objectContaining({ - filter: datasetFilter.filter, + filter: normalizedDatasetFilter.filter, limit: 5, }), }), diff --git a/js/src/logger.ts b/js/src/logger.ts index 81da807fe..4903f6c7e 100644 --- a/js/src/logger.ts +++ b/js/src/logger.ts @@ -3510,6 +3510,35 @@ function getExperimentDatasetFilter({ return isObject(datasetFilter) ? datasetFilter : undefined; } +function normalizeInternalBtqlFilter( + internalBtql?: Record, +): Record | undefined { + if (internalBtql === undefined) { + return undefined; + } + + const filter = internalBtql["filter"]; + if (!Array.isArray(filter)) { + return internalBtql; + } + + const { filter: _filter, ...internalBtqlWithoutFilter } = internalBtql; + if (filter.length === 0) { + return internalBtqlWithoutFilter; + } + + return { + ...internalBtqlWithoutFilter, + filter: + filter.length === 1 + ? filter[0] + : { + op: "and", + children: filter, + }, + }; +} + function getInternalBtqlLimit( internalBtql?: Record, ): number | undefined { @@ -6087,8 +6116,11 @@ export class ObjectFetcher implements AsyncIterable< const internalLimit = getInternalBtqlLimit(this._internal_btql); const limit = batchSize !== undefined ? batchSize : (internalLimit ?? batchLimit); + const normalizedInternalBtql = normalizeInternalBtqlFilter( + this._internal_btql, + ); const internalBtqlWithoutReservedQueryKeys = Object.fromEntries( - Object.entries(this._internal_btql ?? {}).filter( + Object.entries(normalizedInternalBtql ?? {}).filter( ([key]) => key !== "cursor" && key !== "limit" &&