diff --git a/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java b/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java index d3690db..faf075a 100644 --- a/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java +++ b/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java @@ -14,7 +14,7 @@ public class ExperimentExample { public static void main(String[] args) throws Exception { var config = BraintrustConfig.fromEnvironment(); - var openTelemetry = BraintrustTracing.quickstart(config, true); + var openTelemetry = BraintrustTracing.of(config, true); var openAIClient = BraintrustOpenAI.wrapOpenAI(openTelemetry, OpenAIOkHttpClient.fromEnv()); Function getFoodType = diff --git a/examples/src/main/java/dev/braintrust/examples/OpenAIInstrumentationExample.java b/examples/src/main/java/dev/braintrust/examples/OpenAIInstrumentationExample.java index 1c89b90..aaddfbd 100644 --- a/examples/src/main/java/dev/braintrust/examples/OpenAIInstrumentationExample.java +++ b/examples/src/main/java/dev/braintrust/examples/OpenAIInstrumentationExample.java @@ -16,7 +16,7 @@ public static void main(String[] args) throws Exception { "\nWARNING envar OPEN_AI_API_KEY not found. This example will likely fail.\n"); } var braintrustConfig = BraintrustConfig.fromEnvironment(); - var openTelemetry = BraintrustTracing.quickstart(braintrustConfig, true); + var openTelemetry = BraintrustTracing.of(braintrustConfig, true); var tracer = BraintrustTracing.getTracer(openTelemetry); OpenAIClient openAIClient = BraintrustOpenAI.wrapOpenAI(openTelemetry, OpenAIOkHttpClient.fromEnv()); diff --git a/examples/src/main/java/dev/braintrust/examples/SimpleOpenTelemetryExample.java b/examples/src/main/java/dev/braintrust/examples/SimpleOpenTelemetryExample.java index ec2398b..50f275e 100644 --- a/examples/src/main/java/dev/braintrust/examples/SimpleOpenTelemetryExample.java +++ b/examples/src/main/java/dev/braintrust/examples/SimpleOpenTelemetryExample.java @@ -6,7 +6,7 @@ public class SimpleOpenTelemetryExample { public static void main(String[] args) throws Exception { var braintrustConfig = BraintrustConfig.fromEnvironment(); - var openTelemetry = BraintrustTracing.quickstart(braintrustConfig, true); + var openTelemetry = BraintrustTracing.of(braintrustConfig, true); var tracer = BraintrustTracing.getTracer(openTelemetry); var span = tracer.spanBuilder("hello-java").startSpan(); diff --git a/src/main/java/dev/braintrust/api/ApiException.java b/src/main/java/dev/braintrust/api/ApiException.java index 291d44c..cfe9c28 100644 --- a/src/main/java/dev/braintrust/api/ApiException.java +++ b/src/main/java/dev/braintrust/api/ApiException.java @@ -1,7 +1,6 @@ package dev.braintrust.api; -/** Exception thrown when API operations fail. */ -public class ApiException extends RuntimeException { +class ApiException extends RuntimeException { public ApiException(String message) { super(message); diff --git a/src/main/java/dev/braintrust/api/BraintrustApiClient.java b/src/main/java/dev/braintrust/api/BraintrustApiClient.java index 0effd5b..2322bd6 100644 --- a/src/main/java/dev/braintrust/api/BraintrustApiClient.java +++ b/src/main/java/dev/braintrust/api/BraintrustApiClient.java @@ -19,204 +19,222 @@ import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; -public class BraintrustApiClient { - private final BraintrustConfig config; - private final HttpClient httpClient; - private final ObjectMapper objectMapper; - - public BraintrustApiClient(BraintrustConfig config) { - this(config, createDefaultHttpClient(config)); - } - - BraintrustApiClient(BraintrustConfig config, HttpClient httpClient) { - this.config = config; - this.httpClient = httpClient; - this.objectMapper = createObjectMapper(); - } - +public interface BraintrustApiClient { /** Creates or gets a project by name. */ - public Project getOrCreateProject(String projectName) { - try { - var request = new CreateProjectRequest(projectName); - return postAsync("/v1/project", request, Project.class).get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } + Project getOrCreateProject(String projectName); /** Gets a project by ID. */ - public Optional getProject(String projectId) { - try { - return getAsync("/v1/project/" + projectId, Project.class) - .handle( - (project, error) -> { - if (error != null && isNotFound(error)) { - return Optional.empty(); - } - if (error != null) { - throw new CompletionException(error); - } - return Optional.of(project); - }) - .get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } - } + Optional getProject(String projectId); /** Creates an experiment. */ - public Experiment getOrCreateExperiment(CreateExperimentRequest request) { - try { - return postAsync("/v1/experiment", request, Experiment.class).get(); - } catch (InterruptedException | ExecutionException e) { - throw new ApiException(e); - } + Experiment getOrCreateExperiment(CreateExperimentRequest request); + + /** Get project and org info for the default project ID */ + Optional getProjectAndOrgInfo(); + + /** Get project and org info for the given project ID */ + Optional getProjectAndOrgInfo(String projectId); + + static BraintrustApiClient of(BraintrustConfig config) { + return new HttpImpl(config); } - /** Login to get user information including organization details. */ - public LoginResponse login() { - try { - return postAsync( - "/api/apikey/login", - new LoginRequest(config.apiKey()), - LoginResponse.class) - .get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); + class HttpImpl implements BraintrustApiClient { + private final BraintrustConfig config; + private final HttpClient httpClient; + private final ObjectMapper objectMapper; + + HttpImpl(BraintrustConfig config) { + this(config, createDefaultHttpClient(config)); } - } - public Optional getProjectAndOrgInfo() { - var projectId = config.defaultProjectId().orElse(null); - if (null == projectId) { - projectId = getOrCreateProject(config.defaultProjectName().orElseThrow()).id(); + private HttpImpl(BraintrustConfig config, HttpClient httpClient) { + this.config = config; + this.httpClient = httpClient; + this.objectMapper = createObjectMapper(); } - return getProjectAndOrgInfo(projectId); - } - public Optional getOrCreateProjectAndOrgInfo(String projectName) { - return getProjectAndOrgInfo(getOrCreateProject(projectName).id()); - } + @Override + public Project getOrCreateProject(String projectName) { + try { + var request = new CreateProjectRequest(projectName); + return postAsync("/v1/project", request, Project.class).get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } - public Optional getProjectAndOrgInfo(String projectId) { - var project = getProject(projectId).orElse(null); - if (null == project) { - return Optional.empty(); + @Override + public Optional getProject(String projectId) { + try { + return getAsync("/v1/project/" + projectId, Project.class) + .handle( + (project, error) -> { + if (error != null && isNotFound(error)) { + return Optional.empty(); + } + if (error != null) { + throw new CompletionException(error); + } + return Optional.of(project); + }) + .get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } } - OrganizationInfo orgInfo = null; - for (var org : login().orgInfo()) { - if (project.orgId().equalsIgnoreCase(org.id())) { - orgInfo = org; - break; + + @Override + public Experiment getOrCreateExperiment(CreateExperimentRequest request) { + try { + return postAsync("/v1/experiment", request, Experiment.class).get(); + } catch (InterruptedException | ExecutionException e) { + throw new ApiException(e); } } - if (null == orgInfo) { - throw new ApiException( - "Should not happen. Unable to find project's org: " + project.orgId()); + + private LoginResponse login() { + try { + return postAsync( + "/api/apikey/login", + new LoginRequest(config.apiKey()), + LoginResponse.class) + .get(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } } - return Optional.of(new OrganizationAndProjectInfo(orgInfo, project)); - } - private CompletableFuture getAsync(String path, Class responseType) { - var request = - HttpRequest.newBuilder() - .uri(URI.create(config.apiUrl() + path)) - .header("Authorization", "Bearer " + config.apiKey()) - .header("Accept", "application/json") - .timeout(config.requestTimeout()) - .GET() - .build(); - - return sendAsync(request, responseType); - } + @Override + public Optional getProjectAndOrgInfo() { + var projectId = config.defaultProjectId().orElse(null); + if (null == projectId) { + projectId = getOrCreateProject(config.defaultProjectName().orElseThrow()).id(); + } + return getProjectAndOrgInfo(projectId); + } - private CompletableFuture postAsync(String path, Object body, Class responseType) { - try { - var jsonBody = objectMapper.writeValueAsString(body); + @Override + public Optional getProjectAndOrgInfo(String projectId) { + var project = getProject(projectId).orElse(null); + if (null == project) { + return Optional.empty(); + } + OrganizationInfo orgInfo = null; + for (var org : login().orgInfo()) { + if (project.orgId().equalsIgnoreCase(org.id())) { + orgInfo = org; + break; + } + } + if (null == orgInfo) { + throw new ApiException( + "Should not happen. Unable to find project's org: " + project.orgId()); + } + return Optional.of(new OrganizationAndProjectInfo(orgInfo, project)); + } + private CompletableFuture getAsync(String path, Class responseType) { var request = HttpRequest.newBuilder() .uri(URI.create(config.apiUrl() + path)) .header("Authorization", "Bearer " + config.apiKey()) - .header("Content-Type", "application/json") .header("Accept", "application/json") .timeout(config.requestTimeout()) - .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) + .GET() .build(); return sendAsync(request, responseType); - } catch (IOException e) { - return CompletableFuture.failedFuture( - new ApiException("Failed to serialize request body", e)); } - } - private CompletableFuture sendAsync(HttpRequest request, Class responseType) { - BraintrustLogger.debug("API Request: {} {}", request.method(), request.uri()); + private CompletableFuture postAsync( + String path, Object body, Class responseType) { + try { + var jsonBody = objectMapper.writeValueAsString(body); + + var request = + HttpRequest.newBuilder() + .uri(URI.create(config.apiUrl() + path)) + .header("Authorization", "Bearer " + config.apiKey()) + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .timeout(config.requestTimeout()) + .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) + .build(); + + return sendAsync(request, responseType); + } catch (IOException e) { + return CompletableFuture.failedFuture( + new ApiException("Failed to serialize request body", e)); + } + } - return httpClient - .sendAsync(request, HttpResponse.BodyHandlers.ofString()) - .thenApply(response -> handleResponse(response, responseType)); - } + private CompletableFuture sendAsync(HttpRequest request, Class responseType) { + BraintrustLogger.debug("API Request: {} {}", request.method(), request.uri()); - private T handleResponse(HttpResponse response, Class responseType) { - BraintrustLogger.debug("API Response: {} - {}", response.statusCode(), response.body()); + return httpClient + .sendAsync(request, HttpResponse.BodyHandlers.ofString()) + .thenApply(response -> handleResponse(response, responseType)); + } - if (response.statusCode() >= 200 && response.statusCode() < 300) { - try { - return objectMapper.readValue(response.body(), responseType); - } catch (IOException e) { - BraintrustLogger.warn("Failed to parse response body", e); - throw new ApiException("Failed to parse response body", e); + private T handleResponse(HttpResponse response, Class responseType) { + BraintrustLogger.debug("API Response: {} - {}", response.statusCode(), response.body()); + + if (response.statusCode() >= 200 && response.statusCode() < 300) { + try { + return objectMapper.readValue(response.body(), responseType); + } catch (IOException e) { + BraintrustLogger.warn("Failed to parse response body", e); + throw new ApiException("Failed to parse response body", e); + } + } else { + BraintrustLogger.warn( + "API request failed with status {}: {}", + response.statusCode(), + response.body()); + throw new ApiException( + String.format( + "API request failed with status %d: %s", + response.statusCode(), response.body())); } - } else { - BraintrustLogger.warn( - "API request failed with status {}: {}", - response.statusCode(), - response.body()); - throw new ApiException( - String.format( - "API request failed with status %d: %s", - response.statusCode(), response.body())); } - } - private boolean isNotFound(Throwable error) { - if (error instanceof ApiException) { - return ((ApiException) error).getMessage().contains("404"); + private boolean isNotFound(Throwable error) { + if (error instanceof ApiException) { + return ((ApiException) error).getMessage().contains("404"); + } + return false; } - return false; - } - private static HttpClient createDefaultHttpClient(BraintrustConfig config) { - return HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); - } + private static HttpClient createDefaultHttpClient(BraintrustConfig config) { + return HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); + } - private static ObjectMapper createObjectMapper() { - return new ObjectMapper() - .registerModule(new JavaTimeModule()) - .registerModule(new Jdk8Module()) // For Optional support - .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) - .setSerializationInclusion( - JsonInclude.Include.NON_ABSENT) // Skip null and absent Optional - .configure( - com.fasterxml.jackson.databind.DeserializationFeature - .FAIL_ON_UNKNOWN_PROPERTIES, - false); // Ignore unknown fields from API + private static ObjectMapper createObjectMapper() { + return new ObjectMapper() + .registerModule(new JavaTimeModule()) + .registerModule(new Jdk8Module()) // For Optional support + .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE) + .setSerializationInclusion( + JsonInclude.Include.NON_ABSENT) // Skip null and absent Optional + .configure( + com.fasterxml.jackson.databind.DeserializationFeature + .FAIL_ON_UNKNOWN_PROPERTIES, + false); // Ignore unknown fields from API + } } // Request/Response DTOs - public record CreateProjectRequest(String name) {} + record CreateProjectRequest(String name) {} - public record Project( - String id, String name, String orgId, String createdAt, String updatedAt) {} + record Project(String id, String name, String orgId, String createdAt, String updatedAt) {} - private record ProjectList(List projects) {} + record ProjectList(List projects) {} - private record ExperimentList(List experiments) {} + record ExperimentList(List experiments) {} - public record CreateExperimentRequest( + record CreateExperimentRequest( String projectId, String name, Optional description, @@ -227,7 +245,7 @@ public CreateExperimentRequest(String projectId, String name) { } } - public record Experiment( + record Experiment( String id, String projectId, String name, @@ -235,14 +253,13 @@ public record Experiment( String createdAt, String updatedAt) {} - public record CreateDatasetRequest( - String projectId, String name, Optional description) { + record CreateDatasetRequest(String projectId, String name, Optional description) { public CreateDatasetRequest(String projectId, String name) { this(projectId, name, Optional.empty()); } } - public record Dataset( + record Dataset( String id, String projectId, String name, @@ -250,9 +267,9 @@ public record Dataset( String createdAt, String updatedAt) {} - private record DatasetList(List datasets) {} + record DatasetList(List datasets) {} - public record DatasetEvent(Object input, Optional output, Optional metadata) { + record DatasetEvent(Object input, Optional output, Optional metadata) { public DatasetEvent(Object input) { this(input, Optional.empty(), Optional.empty()); } @@ -262,16 +279,16 @@ public DatasetEvent(Object input, Object output) { } } - private record InsertEventsRequest(List events) {} + record InsertEventsRequest(List events) {} - public record InsertEventsResponse(int insertedCount) {} + record InsertEventsResponse(int insertedCount) {} // User and Organization models for login functionality - public record OrganizationInfo(String id, String name) {} + record OrganizationInfo(String id, String name) {} - private record LoginRequest(String token) {} + record LoginRequest(String token) {} - public record LoginResponse(List orgInfo) {} + record LoginResponse(List orgInfo) {} - public record OrganizationAndProjectInfo(OrganizationInfo orgInfo, Project project) {} + record OrganizationAndProjectInfo(OrganizationInfo orgInfo, Project project) {} } diff --git a/src/main/java/dev/braintrust/config/BaseConfig.java b/src/main/java/dev/braintrust/config/BaseConfig.java index edb9da3..6603870 100644 --- a/src/main/java/dev/braintrust/config/BaseConfig.java +++ b/src/main/java/dev/braintrust/config/BaseConfig.java @@ -18,7 +18,8 @@ class BaseConfig { protected @Nonnull T getConfig(@Nonnull String settingName, @Nonnull T defaultValue) { Objects.requireNonNull(defaultValue); - return getConfig(settingName, defaultValue, (Class) defaultValue.getClass()); + return Objects.requireNonNull( + getConfig(settingName, defaultValue, (Class) defaultValue.getClass())); } protected @Nullable T getConfig( diff --git a/src/main/java/dev/braintrust/config/BraintrustConfig.java b/src/main/java/dev/braintrust/config/BraintrustConfig.java index 9c57bba..beae4e7 100644 --- a/src/main/java/dev/braintrust/config/BraintrustConfig.java +++ b/src/main/java/dev/braintrust/config/BraintrustConfig.java @@ -80,7 +80,7 @@ public Optional getBraintrustParentValue() { /** fetch all project info and IDs from the braintrust api */ public URI fetchProjectURI() { try { - var client = new BraintrustApiClient(this); + var client = BraintrustApiClient.of(this); var orgAndProject = client.getProjectAndOrgInfo().orElseThrow(); return new URI( appUrl() diff --git a/src/main/java/dev/braintrust/eval/Eval.java b/src/main/java/dev/braintrust/eval/Eval.java index 7a2f31b..502d3c6 100644 --- a/src/main/java/dev/braintrust/eval/Eval.java +++ b/src/main/java/dev/braintrust/eval/Eval.java @@ -35,7 +35,7 @@ public final class Eval { private Eval(Builder builder) { this.experimentName = builder.experimentName; this.config = Objects.requireNonNull(builder.config); - this.client = new BraintrustApiClient(config); + this.client = BraintrustApiClient.of(config); if (null == builder.projectId) { this.orgAndProject = client.getProjectAndOrgInfo().orElseThrow(); } else { diff --git a/src/main/java/dev/braintrust/instrumentation/openai/BraintrustOpenAI.java b/src/main/java/dev/braintrust/instrumentation/openai/BraintrustOpenAI.java index 1a62468..0a36965 100644 --- a/src/main/java/dev/braintrust/instrumentation/openai/BraintrustOpenAI.java +++ b/src/main/java/dev/braintrust/instrumentation/openai/BraintrustOpenAI.java @@ -5,6 +5,7 @@ import io.opentelemetry.api.OpenTelemetry; public class BraintrustOpenAI { + /** Instrument openai client with braintrust traces */ public static OpenAIClient wrapOpenAI(OpenTelemetry openTelemetry, OpenAIClient openAIClient) { if ("true".equalsIgnoreCase(System.getenv("BRAINTRUST_X_OTEL_LOGS"))) { return io.opentelemetry.instrumentation.openai.v1_1.OpenAITelemetry.builder( diff --git a/src/main/java/dev/braintrust/log/BraintrustLogger.java b/src/main/java/dev/braintrust/log/BraintrustLogger.java index 0f1a401..49d7996 100644 --- a/src/main/java/dev/braintrust/log/BraintrustLogger.java +++ b/src/main/java/dev/braintrust/log/BraintrustLogger.java @@ -21,6 +21,7 @@ public final class BraintrustLogger { static { // BT logger will filter log levels so make the impl most permissive + // FIXME: Don't ship this -- we don't want to set common slf4j log properties System.setProperty("org.slf4j.simpleLogger.defaultLogLevel", "trace"); // Initialize with default SLF4J implementation var defaultLogger = LoggerFactory.getLogger(LOGGER_NAME); diff --git a/src/main/java/dev/braintrust/trace/BraintrustLogExporter.java b/src/main/java/dev/braintrust/trace/BraintrustLogExporter.java index 81dbc83..035f0ad 100644 --- a/src/main/java/dev/braintrust/trace/BraintrustLogExporter.java +++ b/src/main/java/dev/braintrust/trace/BraintrustLogExporter.java @@ -16,7 +16,7 @@ * Custom log exporter for Braintrust that adds the x-bt-parent header dynamically based on log * attributes. */ -public class BraintrustLogExporter implements LogRecordExporter { +class BraintrustLogExporter implements LogRecordExporter { private final BraintrustConfig config; private final String logsEndpoint; diff --git a/src/main/java/dev/braintrust/trace/BraintrustSpanExporter.java b/src/main/java/dev/braintrust/trace/BraintrustSpanExporter.java index d158ee8..0d5a220 100644 --- a/src/main/java/dev/braintrust/trace/BraintrustSpanExporter.java +++ b/src/main/java/dev/braintrust/trace/BraintrustSpanExporter.java @@ -16,7 +16,7 @@ * Custom span exporter for Braintrust that adds the x-bt-parent header dynamically based on span * attributes. */ -public class BraintrustSpanExporter implements SpanExporter { +class BraintrustSpanExporter implements SpanExporter { private final BraintrustConfig config; private final String tracesEndpoint; diff --git a/src/main/java/dev/braintrust/trace/BraintrustTracing.java b/src/main/java/dev/braintrust/trace/BraintrustTracing.java index 8b9a5c9..01fdc5c 100644 --- a/src/main/java/dev/braintrust/trace/BraintrustTracing.java +++ b/src/main/java/dev/braintrust/trace/BraintrustTracing.java @@ -53,7 +53,7 @@ public static Tracer getTracer(OpenTelemetry openTelemetry) { * `enable` method. */ public static OpenTelemetry quickstart() { - return quickstart(BraintrustConfig.fromEnvironment(), true); + return of(BraintrustConfig.fromEnvironment(), true); } /** @@ -62,8 +62,7 @@ public static OpenTelemetry quickstart() { * If you're looking for more options for configuring Braintrust and OpenTelemetry, consult the * `enable` method. */ - public static OpenTelemetry quickstart( - @Nonnull BraintrustConfig config, boolean registerGlobal) { + public static OpenTelemetry of(@Nonnull BraintrustConfig config, boolean registerGlobal) { var tracerBuilder = SdkTracerProvider.builder(); var loggerBuilder = SdkLoggerProvider.builder(); var meterBuilder = SdkMeterProvider.builder(); diff --git a/src/main/java/dev/braintrust/trace/DebugSpanExporter.java b/src/main/java/dev/braintrust/trace/DebugSpanExporter.java deleted file mode 100644 index e93bd52..0000000 --- a/src/main/java/dev/braintrust/trace/DebugSpanExporter.java +++ /dev/null @@ -1,85 +0,0 @@ -package dev.braintrust.trace; - -import dev.braintrust.log.BraintrustLogger; -import io.opentelemetry.sdk.common.CompletableResultCode; -import io.opentelemetry.sdk.trace.data.SpanData; -import io.opentelemetry.sdk.trace.export.SpanExporter; -import java.util.Collection; - -// TODO -- is this just for unit testing? Make package-private? -/** Debug wrapper for SpanExporter that logs all spans being exported. */ -public class DebugSpanExporter implements SpanExporter { - private final SpanExporter delegate; - private final String configuredEndpoint; - - public DebugSpanExporter(SpanExporter delegate) { - this(delegate, null); - } - - public DebugSpanExporter(SpanExporter delegate, String configuredEndpoint) { - this.delegate = delegate; - this.configuredEndpoint = configuredEndpoint; - } - - @Override - public CompletableResultCode export(Collection spans) { - BraintrustLogger.info("braintrust: === EXPORTING {} SPANS ===", spans.size()); - BraintrustLogger.info("braintrust: Exporter type: {}", delegate.getClass().getName()); - if (configuredEndpoint != null) { - BraintrustLogger.info("braintrust: Configured endpoint: {}", configuredEndpoint); - } - - // Check if we actually have spans - if (spans.isEmpty()) { - BraintrustLogger.warn("braintrust: WARNING: No spans to export!"); - return CompletableResultCode.ofSuccess(); - } - - // Try to get more info about the exporter - BraintrustLogger.info("Exporter details: {}", delegate.toString()); - - for (SpanData span : spans) { - BraintrustLogger.info( - "Span: name={}, traceId={}, spanId={}", - span.getName(), - span.getTraceId(), - span.getSpanId()); - BraintrustLogger.info(" Attributes: {}", span.getAttributes()); - BraintrustLogger.info(" Resource: {}", span.getResource().getAttributes()); - - // Check for braintrust.parent attribute - var parentAttr = span.getAttributes().get(BraintrustSpanProcessor.PARENT); - if (parentAttr != null) { - BraintrustLogger.info(" Braintrust Parent: {}", parentAttr); - } else { - BraintrustLogger.warn(" WARNING: No braintrust.parent attribute!"); - } - } - - BraintrustLogger.info("=== END SPAN EXPORT ==="); - - // Delegate to actual exporter - var result = delegate.export(spans); - - result.whenComplete( - () -> { - if (result.isSuccess()) { - BraintrustLogger.info("Export successful"); - } else { - BraintrustLogger.error("Export failed!"); - } - }); - - return result; - } - - @Override - public CompletableResultCode flush() { - return delegate.flush(); - } - - @Override - public CompletableResultCode shutdown() { - return delegate.shutdown(); - } -} diff --git a/src/test/java/dev/braintrust/config/BaseConfigTest.java b/src/test/java/dev/braintrust/config/BaseConfigTest.java index 7b511fa..7402a77 100644 --- a/src/test/java/dev/braintrust/config/BaseConfigTest.java +++ b/src/test/java/dev/braintrust/config/BaseConfigTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.*; import java.util.Map; +import javax.annotation.Nonnull; import org.junit.jupiter.api.Test; public class BaseConfigTest { @@ -186,21 +187,25 @@ static class TestConfig extends BaseConfig { super(envOverrides); } + @Nonnull @Override - public T getConfig(String settingName, T defaultValue) { + public T getConfig(@Nonnull String settingName, @Nonnull T defaultValue) { return super.getConfig(settingName, defaultValue); } @Override - public T getConfig(String settingName, T defaultValue, Class settingClass) { + public T getConfig( + @Nonnull String settingName, T defaultValue, @Nonnull Class settingClass) { return super.getConfig(settingName, defaultValue, settingClass); } + @Nonnull @Override public String getRequiredConfig(String settingName) { return super.getRequiredConfig(settingName); } + @Nonnull @Override public T getRequiredConfig(String settingName, Class settingClass) { return super.getRequiredConfig(settingName, settingClass); diff --git a/src/test/java/dev/braintrust/config/BraintrustConfigTest.java b/src/test/java/dev/braintrust/config/BraintrustConfigTest.java index 2658832..18204bf 100644 --- a/src/test/java/dev/braintrust/config/BraintrustConfigTest.java +++ b/src/test/java/dev/braintrust/config/BraintrustConfigTest.java @@ -1,15 +1,26 @@ package dev.braintrust.config; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; class BraintrustConfigTest { @Test - void parentLogic() { + void parentDefaultsToProjectName() { var defaultConfig = BraintrustConfig.of("BRAINTRUST_API_KEY", "foobar"); - assertTrue( - defaultConfig.getBraintrustParentValue().isPresent(), - "default config should have a parent"); + assertEquals( + "project_name:" + defaultConfig.defaultProjectName().orElseThrow(), + defaultConfig.getBraintrustParentValue().orElseThrow()); + } + + @Test + void parentUsesProjectId() { + var defaultConfig = + BraintrustConfig.of( + "BRAINTRUST_API_KEY", "foobar", + "BRAINTRUST_DEFAULT_PROJECT_ID", "12345"); + assertEquals( + "project_id:" + defaultConfig.defaultProjectId().orElseThrow(), + defaultConfig.getBraintrustParentValue().orElseThrow()); } }