Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/main/java/dev/braintrust/config/BaseConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode
class BaseConfig {
/** Sentinal used to set null in the env. Only used for testing. */
static final String NULL_OVERRIDE = "BRAINTRUST_NULL_SENTINAL_" + System.currentTimeMillis();
Expand Down
83 changes: 83 additions & 0 deletions src/main/java/dev/braintrust/config/BraintrustConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,87 @@ public URI fetchProjectURI() {
var orgAndProject = client.getProjectAndOrgInfo().orElseThrow();
return BraintrustUtils.createProjectURI(appUrl(), orgAndProject);
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private final Map<String, String> envOverrides = new HashMap<>();

public Builder apiKey(String value) {
envOverrides.put("BRAINTRUST_API_KEY", value);
return this;
}

public Builder apiUrl(String value) {
envOverrides.put("BRAINTRUST_API_URL", value);
return this;
}

public Builder appUrl(String value) {
envOverrides.put("BRAINTRUST_APP_URL", value);
return this;
}

public Builder tracesPath(String value) {
envOverrides.put("BRAINTRUST_TRACES_PATH", value);
return this;
}

public Builder logsPath(String value) {
envOverrides.put("BRAINTRUST_LOGS_PATH", value);
return this;
}

public Builder defaultProjectId(String value) {
if (value != null) {
envOverrides.put("BRAINTRUST_DEFAULT_PROJECT_ID", value);
} else {
envOverrides.put("BRAINTRUST_DEFAULT_PROJECT_ID", NULL_OVERRIDE);
}
return this;
}

public Builder defaultProjectName(String value) {
if (value != null) {
envOverrides.put("BRAINTRUST_DEFAULT_PROJECT_NAME", value);
} else {
envOverrides.put("BRAINTRUST_DEFAULT_PROJECT_NAME", NULL_OVERRIDE);
}
return this;
}

public Builder enableTraceConsoleLog(boolean value) {
envOverrides.put("BRAINTRUST_ENABLE_TRACE_CONSOLE_LOG", String.valueOf(value));
return this;
}

public Builder debug(boolean value) {
envOverrides.put("BRAINTRUST_DEBUG", String.valueOf(value));
return this;
}

public Builder requestTimeout(Duration value) {
envOverrides.put("BRAINTRUST_REQUEST_TIMEOUT", String.valueOf(value.getSeconds()));
return this;
}

// hiding visibility. only used for testing
Builder experimentalOtelLogs(boolean value) {
envOverrides.put("BRAINTRUST_X_OTEL_LOGS", String.valueOf(value));
return this;
}

// hiding visibility. only used for testing
Builder exportSpansInMemoryForUnitTest(boolean value) {
envOverrides.put(
"BRAINTRUST_JAVA_EXPORT_SPANS_IN_MEMORY_FOR_UNIT_TEST", String.valueOf(value));
return this;
}

public BraintrustConfig build() {
return new BraintrustConfig(envOverrides);
}
}
}
9 changes: 9 additions & 0 deletions src/test/java/dev/braintrust/config/BaseConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,15 @@ void testIntegrationWithAllTypes() {
assertEquals(2.71828, config.getConfig("DOUBLE_VAR", 0.0), 0.00001);
}

@Test
void testEquals() {
var cfg1 = new TestConfig(Map.of("FOO", "bar"));
var cfg2 = new TestConfig(Map.of("FOO", "bar"));
var cfg3 = new TestConfig(Map.of("FOO", "baz"));
assertEquals(cfg1, cfg2);
assertNotEquals(cfg1, cfg3);
}

static class TestConfig extends BaseConfig {
TestConfig(Map<String, String> envOverrides) {
super(envOverrides);
Expand Down
46 changes: 45 additions & 1 deletion src/test/java/dev/braintrust/config/BraintrustConfigTest.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package dev.braintrust.config;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.*;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;

class BraintrustConfigTest {
Expand All @@ -28,4 +34,42 @@ void parentUsesProjectId() {
"project_id:" + defaultConfig.defaultProjectId().orElseThrow(),
defaultConfig.getBraintrustParentValue().orElseThrow());
}

@Test
public void testBuilderEqualsEnv() {
var fromEnv =
BraintrustConfig.of(
"BRAINTRUST_API_KEY", "testkey",
"BRAINTRUST_DEFAULT_PROJECT_ID", "unit-test");
var fromBuilder =
BraintrustConfig.builder().apiKey("testkey").defaultProjectId("unit-test").build();
var otherBuilder =
BraintrustConfig.builder().apiKey("otherkey").defaultProjectId("unit-test").build();
assertEquals(fromEnv, fromBuilder);
assertNotEquals(fromEnv, otherBuilder);
}

@Test
public void testBuilderHasMethodForEveryField() {
List<String> fieldsToSkip = List.of("envOverrides");
// Get all fields from BraintrustConfig
Field[] configFields = BraintrustConfig.class.getDeclaredFields();

// Get all methods from Builder
Method[] builderMethods = BraintrustConfig.Builder.class.getDeclaredMethods();
Set<String> builderMethodNames =
Arrays.stream(builderMethods).map(Method::getName).collect(Collectors.toSet());

// For each field, verify there's a corresponding builder method
for (Field field : configFields) {
String configFieldName = field.getName();
// Skip internal fields
if (fieldsToSkip.contains(configFieldName)) {
continue;
}
assertTrue(
builderMethodNames.contains(configFieldName),
"Builder is missing method for field: " + configFieldName);
}
}
}