diff --git a/.github/release.yml b/.github/release.yml
index bfe7aac..6d198a7 100644
--- a/.github/release.yml
+++ b/.github/release.yml
@@ -1,5 +1,4 @@
changelog:
- include_author: false
exclude:
labels:
- ignore-for-release
diff --git a/src/main/java/dev/braintrust/Braintrust.java b/src/main/java/dev/braintrust/Braintrust.java
index d0bc5b3..4b723f3 100644
--- a/src/main/java/dev/braintrust/Braintrust.java
+++ b/src/main/java/dev/braintrust/Braintrust.java
@@ -2,6 +2,7 @@
import dev.braintrust.api.BraintrustApiClient;
import dev.braintrust.config.BraintrustConfig;
+import dev.braintrust.eval.Dataset;
import dev.braintrust.eval.Eval;
import dev.braintrust.prompt.BraintrustPromptLoader;
import dev.braintrust.trace.BraintrustTracing;
@@ -12,6 +13,7 @@
import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import lombok.Getter;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
@@ -161,4 +163,14 @@ public Eval.Builder evalBuilder() {
return (Eval.Builder)
Eval.builder().config(this.config).apiClient(this.apiClient);
}
+
+ public Dataset fetchDataset(String datasetName) {
+ return fetchDataset(datasetName, null);
+ }
+
+ public Dataset fetchDataset(
+ String datasetName, @Nullable String datasetVersion) {
+ var projectName = apiClient.getOrCreateProjectAndOrgInfo(config).project().name();
+ return Dataset.fetchFromBraintrust(apiClient(), projectName, datasetName, datasetVersion);
+ }
}
diff --git a/src/main/java/dev/braintrust/api/BraintrustApiClient.java b/src/main/java/dev/braintrust/api/BraintrustApiClient.java
index 51561d4..a8f8965 100644
--- a/src/main/java/dev/braintrust/api/BraintrustApiClient.java
+++ b/src/main/java/dev/braintrust/api/BraintrustApiClient.java
@@ -49,6 +49,15 @@ public interface BraintrustApiClient {
Optional getPrompt(
@Nonnull String projectName, @Nonnull String slug, @Nullable String version);
+ /** Fetch dataset events with pagination */
+ DatasetFetchResponse fetchDatasetEvents(String datasetId, DatasetFetchRequest request);
+
+ /** Get dataset metadata by ID */
+ Optional getDataset(String datasetId);
+
+ /** Query datasets by project name and dataset name */
+ List queryDatasets(String projectName, String datasetName);
+
static BraintrustApiClient of(BraintrustConfig config) {
return new HttpImpl(config);
}
@@ -235,6 +244,49 @@ public Optional getPrompt(
}
}
+ @Override
+ public DatasetFetchResponse fetchDatasetEvents(
+ String datasetId, DatasetFetchRequest request) {
+ try {
+ String path = "/v1/dataset/" + datasetId + "/fetch";
+ return postAsync(path, request, DatasetFetchResponse.class).get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new ApiException(e);
+ }
+ }
+
+ @Override
+ public Optional getDataset(String datasetId) {
+ try {
+ return getAsync("/v1/dataset/" + datasetId, Dataset.class)
+ .handle(
+ (dataset, error) -> {
+ if (error != null && isNotFound(error)) {
+ return Optional.empty();
+ }
+ if (error != null) {
+ throw new CompletionException(error);
+ }
+ return Optional.of(dataset);
+ })
+ .get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public List queryDatasets(String projectName, String datasetName) {
+ try {
+ String path =
+ "/v1/dataset?project_name=" + projectName + "&dataset_name=" + datasetName;
+ DatasetList response = getAsync(path, DatasetList.class).get();
+ return response.objects();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
private CompletableFuture getAsync(String path, Class responseType) {
var request =
HttpRequest.newBuilder()
@@ -301,8 +353,14 @@ private T handleResponse(HttpResponse response, Class responseTyp
}
private boolean isNotFound(Throwable error) {
- if (error instanceof ApiException) {
- return ((ApiException) error).getMessage().contains("404");
+ // Unwrap CompletionException if present
+ Throwable cause = error;
+ if (error instanceof CompletionException && error.getCause() != null) {
+ cause = error.getCause();
+ }
+
+ if (cause instanceof ApiException) {
+ return ((ApiException) cause).getMessage().contains("404");
}
return false;
}
@@ -493,6 +551,23 @@ public Optional getPrompt(
return Optional.of(matchingPrompts.get(0));
}
+
+ // Will add dataset support if needed in unit tests (this is unlikely to be needed though)
+ @Override
+ public DatasetFetchResponse fetchDatasetEvents(
+ String datasetId, DatasetFetchRequest request) {
+ return new DatasetFetchResponse(List.of(), null);
+ }
+
+ @Override
+ public Optional getDataset(String datasetId) {
+ return Optional.empty();
+ }
+
+ @Override
+ public List queryDatasets(String projectName, String datasetName) {
+ return List.of();
+ }
}
// Request/Response DTOs
@@ -538,7 +613,7 @@ record Dataset(
String createdAt,
String updatedAt) {}
- record DatasetList(List datasets) {}
+ record DatasetList(List objects) {}
record DatasetEvent(Object input, Optional