diff --git a/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java b/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java index f2cd758..09a3383 100644 --- a/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java +++ b/examples/src/main/java/dev/braintrust/examples/ExperimentExample.java @@ -51,6 +51,8 @@ public static void main(String[] args) throws Exception { // .dataset(braintrust.fetchDataset("my-dataset-name")) .taskFunction(getFoodType) .scorers( + // to fetch a remote scorer: + // braintrust.fetchScorer("my-remote-scorer-6d9f"), Scorer.of( "exact_match", (expected, result) -> expected.equals(result) ? 1.0 : 0.0)) diff --git a/src/main/java/dev/braintrust/Braintrust.java b/src/main/java/dev/braintrust/Braintrust.java index 4b723f3..a85df96 100644 --- a/src/main/java/dev/braintrust/Braintrust.java +++ b/src/main/java/dev/braintrust/Braintrust.java @@ -4,6 +4,7 @@ import dev.braintrust.config.BraintrustConfig; import dev.braintrust.eval.Dataset; import dev.braintrust.eval.Eval; +import dev.braintrust.eval.Scorer; import dev.braintrust.prompt.BraintrustPromptLoader; import dev.braintrust.trace.BraintrustTracing; import io.opentelemetry.api.OpenTelemetry; @@ -173,4 +174,40 @@ public Dataset fetchDataset( var projectName = apiClient.getOrCreateProjectAndOrgInfo(config).project().name(); return Dataset.fetchFromBraintrust(apiClient(), projectName, datasetName, datasetVersion); } + + /** + * Fetch a scorer from Braintrust by slug, using the default project from configuration. + * + * @param scorerSlug the unique slug identifier for the scorer + * @return a Scorer that invokes the remote function + */ + public Scorer fetchScorer(String scorerSlug) { + return fetchScorer(scorerSlug, null); + } + + /** + * Fetch a scorer from Braintrust by slug, using the default project from configuration. + * + * @param scorerSlug the unique slug identifier for the scorer + * @param version optional version of the scorer to fetch + * @return a Scorer that invokes the remote function + */ + public Scorer fetchScorer( + String scorerSlug, @Nullable String version) { + var projectName = apiClient.getOrCreateProjectAndOrgInfo(config).project().name(); + return Scorer.fetchFromBraintrust(apiClient, projectName, scorerSlug, version); + } + + /** + * Fetch a scorer from Braintrust by project name and slug. + * + * @param projectName the name of the project containing the scorer + * @param scorerSlug the unique slug identifier for the scorer + * @param version optional version of the scorer to fetch + * @return a Scorer that invokes the remote function + */ + public Scorer fetchScorer( + String projectName, String scorerSlug, @Nullable String version) { + return Scorer.fetchFromBraintrust(apiClient, projectName, scorerSlug, version); + } } diff --git a/src/main/java/dev/braintrust/api/BraintrustApiClient.java b/src/main/java/dev/braintrust/api/BraintrustApiClient.java index 20dc278..126e91f 100644 --- a/src/main/java/dev/braintrust/api/BraintrustApiClient.java +++ b/src/main/java/dev/braintrust/api/BraintrustApiClient.java @@ -66,6 +66,26 @@ Optional getPrompt( /** Query datasets by project name and dataset name */ List queryDatasets(String projectName, String datasetName); + /** + * Get a function by project name and slug, with optional version. + * + * @param projectName the name of the project containing the function + * @param slug the unique slug identifier for the function + * @param version optional version identifier (transaction id or version string) + * @return the function if found + */ + Optional getFunction( + @Nonnull String projectName, @Nonnull String slug, @Nullable String version); + + /** + * Invoke a function (scorer, prompt, or tool) by its ID. + * + * @param functionId the ID of the function to invoke + * @param request the invocation request containing input, expected output, etc. + * @return the result of the function invocation + */ + Object invokeFunction(@Nonnull String functionId, @Nonnull FunctionInvokeRequest request); + static BraintrustApiClient of(BraintrustConfig config) { return new HttpImpl(config); } @@ -296,6 +316,54 @@ public List queryDatasets(String projectName, String datasetName) { } } + @Override + public Optional getFunction( + @Nonnull String projectName, @Nonnull String slug, @Nullable String version) { + Objects.requireNonNull(projectName, "projectName must not be null"); + Objects.requireNonNull(slug, "slug must not be null"); + try { + var uriBuilder = new StringBuilder("/v1/function?"); + uriBuilder.append("slug=").append(slug); + uriBuilder.append("&project_name=").append(projectName); + + if (version != null && !version.isEmpty()) { + uriBuilder.append("&version=").append(version); + } + + FunctionListResponse response = + getAsync(uriBuilder.toString(), FunctionListResponse.class).get(); + + if (response.objects() == null || response.objects().isEmpty()) { + return Optional.empty(); + } + + if (response.objects().size() > 1) { + throw new ApiException( + "Multiple functions found for slug: " + + slug + + ", projectName: " + + projectName); + } + + return Optional.of(response.objects().get(0)); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object invokeFunction( + @Nonnull String functionId, @Nonnull FunctionInvokeRequest request) { + Objects.requireNonNull(functionId, "functionId must not be null"); + Objects.requireNonNull(request, "request must not be null"); + try { + String path = "/v1/function/" + functionId + "/invoke"; + return postAsync(path, request, Object.class).get(); + } catch (InterruptedException | ExecutionException e) { + throw new ApiException("Failed to invoke function: " + functionId, e); + } + } + private CompletableFuture getAsync(String path, Class responseType) { var request = HttpRequest.newBuilder() @@ -399,6 +467,9 @@ class InMemoryImpl implements BraintrustApiClient { private final Set experiments = Collections.newSetFromMap(new ConcurrentHashMap<>()); private final List prompts = new ArrayList<>(); + private final List functions = new ArrayList<>(); + private final Map> + functionInvokers = new ConcurrentHashMap<>(); public InMemoryImpl(OrganizationAndProjectInfo... organizationAndProjectInfos) { this.organizationAndProjectInfos = @@ -583,6 +654,18 @@ public Optional getDataset(String datasetId) { public List queryDatasets(String projectName, String datasetName) { return List.of(); } + + @Override + public Optional getFunction( + @Nonnull String projectName, @Nonnull String slug, @Nullable String version) { + throw new RuntimeException("will not be invoked"); + } + + @Override + public Object invokeFunction( + @Nonnull String functionId, @Nonnull FunctionInvokeRequest request) { + throw new RuntimeException("will not be invoked"); + } } // Request/Response DTOs @@ -681,4 +764,80 @@ record Prompt( Optional metadata) {} record PromptListResponse(List objects) {} + + // Function models for remote scorers/prompts/tools + + /** + * Represents a Braintrust function (scorer, prompt, tool, or task). Functions can be invoked + * remotely via the API. + */ + record Function( + String id, + String projectId, + String orgId, + String name, + String slug, + Optional description, + String created, + Optional functionData, + Optional promptData, + Optional> tags, + Optional metadata, + Optional functionType, + Optional origin, + Optional functionSchema) {} + + record FunctionListResponse(List objects) {} + + /** + * Request body for invoking a function. The input field wraps the function arguments. + * + *

For remote Python/TypeScript scorers, the scorer handler parameters (input, output, + * expected, metadata) must be wrapped in the outer input field. + */ + record FunctionInvokeRequest(@Nullable Object input, @Nullable String version) { + + /** Create a simple invoke request with just input */ + public static FunctionInvokeRequest of(Object input) { + return new FunctionInvokeRequest(input, null); + } + + /** Create a simple invoke request with input and version */ + public static FunctionInvokeRequest of(Object input, @Nullable String version) { + return new FunctionInvokeRequest(input, version); + } + + /** + * Create an invoke request for a scorer with input, output, expected, and metadata. This + * maps to the standard scorer handler signature: handler(input, output, expected, metadata) + * + *

The scorer args are wrapped in the outer input field as required by the invoke API. + */ + public static FunctionInvokeRequest forScorer( + Object input, Object output, Object expected, Object metadata) { + return forScorer(input, output, expected, metadata, null); + } + + /** + * Create an invoke request for a scorer with input, output, expected, metadata, and + * version. This maps to the standard scorer handler signature: handler(input, output, + * expected, metadata) + * + *

The scorer args are wrapped in the outer input field as required by the invoke API. + */ + public static FunctionInvokeRequest forScorer( + Object input, + Object output, + Object expected, + Object metadata, + @Nullable String version) { + // Wrap scorer args in an inner map that becomes the outer "input" field + var scorerArgs = new java.util.LinkedHashMap(); + scorerArgs.put("input", input); + scorerArgs.put("output", output); + scorerArgs.put("expected", expected); + scorerArgs.put("metadata", metadata); + return new FunctionInvokeRequest(scorerArgs, version); + } + } } diff --git a/src/main/java/dev/braintrust/devserver/Devserver.java b/src/main/java/dev/braintrust/devserver/Devserver.java index c3b2071..be89be8 100644 --- a/src/main/java/dev/braintrust/devserver/Devserver.java +++ b/src/main/java/dev/braintrust/devserver/Devserver.java @@ -288,7 +288,18 @@ private void handleEval(HttpExchange exchange) throws IOException { return; } - // TODO: support remote scorers + // Resolve remote scorers from the request + List> remoteScorers = new ArrayList<>(); + if (request.getScores() != null) { + var apiClient = context.getBraintrust().apiClient(); + for (var remoteScorer : request.getScores()) { + remoteScorers.add(resolveRemoteScorer(remoteScorer, apiClient)); + } + log.debug( + "Resolved {} remote scorer(s): {}", + remoteScorers.size(), + remoteScorers.stream().map(Scorer::getName).toList()); + } String datasetDescription = hasInlineData @@ -308,7 +319,7 @@ private void handleEval(HttpExchange exchange) throws IOException { if (isStreaming) { // SSE streaming response - errors handled inside log.debug("Starting streaming evaluation for '{}'", request.getName()); - handleStreamingEval(exchange, eval, request, context); + handleStreamingEval(exchange, eval, request, context, remoteScorers); } else { throw new NotSupportedYetException("non-streaming responses"); } @@ -325,7 +336,11 @@ private void handleEval(HttpExchange exchange) throws IOException { @SuppressWarnings({"unchecked", "rawtypes"}) private void handleStreamingEval( - HttpExchange exchange, RemoteEval eval, EvalRequest request, RequestContext context) + HttpExchange exchange, + RemoteEval eval, + EvalRequest request, + RequestContext context, + List> remoteScorers) throws Exception { // Set SSE headers exchange.getResponseHeaders().set("Content-Type", "text/event-stream"); @@ -423,7 +438,12 @@ private void handleStreamingEval( taskResult); } // run scorers - one score span per scorer - for (var scorer : (List>) eval.getScorers()) { + // Combine local scorers from RemoteEval with remote scorers + // from request + List> allScorers = + new ArrayList<>(eval.getScorers()); + allScorers.addAll(remoteScorers); + for (var scorer : allScorers) { var scoreSpan = tracer.spanBuilder("score").startSpan(); try (var unused = Context.current() @@ -1037,6 +1057,30 @@ private static ParentInfo extractParentInfo(EvalRequest request) { } } + /** + * Resolve a remote scorer from the eval request into a Scorer instance. + * + * @param remoteScorer the remote scorer specification from the request + * @param apiClient the API client to use for invoking the scorer function + * @return a Scorer that invokes the remote function + * @throws IllegalArgumentException if the function_id is missing + */ + private static Scorer resolveRemoteScorer( + EvalRequest.RemoteScorer remoteScorer, BraintrustApiClient apiClient) { + var functionIdSpec = remoteScorer.getFunctionId(); + + if (functionIdSpec == null || functionIdSpec.getFunctionId() == null) { + throw new IllegalArgumentException( + "Remote scorer '" + remoteScorer.getName() + "' missing function_id"); + } + + return new ScorerBrainstoreImpl<>( + apiClient, + functionIdSpec.getFunctionId(), + remoteScorer.getName(), + functionIdSpec.getVersion()); + } + public static class Builder { private @Nullable BraintrustConfig config = null; private String host = "localhost"; diff --git a/src/main/java/dev/braintrust/eval/Scorer.java b/src/main/java/dev/braintrust/eval/Scorer.java index 2ea8c9a..f6dea50 100644 --- a/src/main/java/dev/braintrust/eval/Scorer.java +++ b/src/main/java/dev/braintrust/eval/Scorer.java @@ -1,8 +1,10 @@ package dev.braintrust.eval; +import dev.braintrust.api.BraintrustApiClient; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; +import javax.annotation.Nullable; /** * A scorer evaluates the result of a test case with a score between 0 (inclusive) and 1 @@ -49,4 +51,33 @@ public List score(TaskResult taskResult) { } }; } + + /** + * Fetch a scorer from Braintrust by project name and slug. + * + * @param apiClient the API client to use + * @param projectName the name of the project containing the scorer + * @param scorerSlug the unique slug identifier for the scorer + * @param version optional version of the scorer to fetch + * @return a Scorer that invokes the remote function + * @throws RuntimeException if the scorer is not found + */ + static Scorer fetchFromBraintrust( + BraintrustApiClient apiClient, + String projectName, + String scorerSlug, + @Nullable String version) { + var function = + apiClient + .getFunction(projectName, scorerSlug, version) + .orElseThrow( + () -> + new RuntimeException( + "Scorer not found: project=" + + projectName + + ", slug=" + + scorerSlug)); + + return new ScorerBrainstoreImpl<>(apiClient, function.id(), function.name(), version); + } } diff --git a/src/main/java/dev/braintrust/eval/ScorerBrainstoreImpl.java b/src/main/java/dev/braintrust/eval/ScorerBrainstoreImpl.java new file mode 100644 index 0000000..88d32d3 --- /dev/null +++ b/src/main/java/dev/braintrust/eval/ScorerBrainstoreImpl.java @@ -0,0 +1,156 @@ +package dev.braintrust.eval; + +import dev.braintrust.api.BraintrustApiClient; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +/** + * A scorer that invokes a remote Braintrust function to compute scores. + * + *

This implementation fetches a scorer function from Braintrust and invokes it via the API for + * each task result. The remote function receives the input, output, expected, and metadata as + * arguments. + */ +public class ScorerBrainstoreImpl implements Scorer { + private final BraintrustApiClient apiClient; + private final String functionId; + private final String scorerName; + private final @Nullable String version; + + /** + * Create a new remote scorer. + * + * @param apiClient the API client to use for invoking the function + * @param functionId the ID of the function to invoke + * @param scorerName the name of the scorer (used as default score name) + * @param version optional version of the function to invoke. null always invokes latest + * version. + */ + public ScorerBrainstoreImpl( + BraintrustApiClient apiClient, + String functionId, + String scorerName, + @Nullable String version) { + this.apiClient = apiClient; + this.functionId = functionId; + this.scorerName = scorerName; + this.version = version; + } + + @Override + public String getName() { + return scorerName; + } + + @Override + public List score(TaskResult taskResult) { + var request = + BraintrustApiClient.FunctionInvokeRequest.forScorer( + taskResult.datasetCase().input(), + taskResult.result(), + taskResult.datasetCase().expected(), + taskResult.datasetCase().metadata(), + version); + + Object result = apiClient.invokeFunction(functionId, request); + return parseScoreResult(result); + } + + /** + * Parse the result from the function invocation into a list of scores. + * + *

Handles various response formats: + * + *

    + *
  • A single number (0.0-1.0) - converted to a Score with the scorer's name + *
  • A Score object: {"score": 0.8, "name": "score_name", "metadata": {...}} + *
  • An LLM judge response: {"name": "judge-name", "score": null, "metadata": {"choice": + * "0.9", ...}} + *
  • A list of Score objects + *
  • null - returns empty list (score skipped) + *
+ */ + @SuppressWarnings("unchecked") + private List parseScoreResult(Object result) { + if (result == null) { + // Scorer returned null to skip scoring + return List.of(); + } + + // Handle a single number + if (result instanceof Number number) { + return List.of(new Score(scorerName, number.doubleValue())); + } + + // Handle a list of scores + if (result instanceof List list) { + List scores = new ArrayList<>(); + for (Object item : list) { + scores.addAll(parseScoreResult(item)); + } + return scores; + } + + // Handle a score object (Map) + if (result instanceof Map map) { + Map scoreMap = (Map) map; + + // Extract name (use scorer name as fallback) + String name = scorerName; + Object nameValue = scoreMap.get("name"); + if (nameValue instanceof String s) { + name = s; + } + + // Extract score value + Object scoreValue = scoreMap.get("score"); + + // If score is null, check for LLM judge response with metadata.choice + if (scoreValue == null) { + Object metadataObj = scoreMap.get("metadata"); + if (metadataObj instanceof Map metadata) { + Object choiceValue = ((Map) metadata).get("choice"); + if (choiceValue != null) { + double score = parseNumericValue(choiceValue); + return List.of(new Score(name, score)); + } + } + // No score field and no choice in metadata - skip + return List.of(); + } + + double score = parseNumericValue(scoreValue); + return List.of(new Score(name, score)); + } + + throw new IllegalArgumentException( + "Unexpected score result type: " + + result.getClass() + + ". Expected Number, Map, or List."); + } + + /** + * Parse a numeric value from either a Number or a String. + * + * @param value the value to parse + * @return the double value + * @throws IllegalArgumentException if the value cannot be parsed as a number + */ + private double parseNumericValue(Object value) { + if (value instanceof Number number) { + return number.doubleValue(); + } + if (value instanceof String str) { + try { + return Double.parseDouble(str); + } catch (NumberFormatException e) { + throw new IllegalArgumentException( + "Cannot parse score value as number: '" + str + "'", e); + } + } + throw new IllegalArgumentException( + "Score value must be a number or numeric string, got: " + value.getClass()); + } +} diff --git a/src/test/java/dev/braintrust/ForbiddenTextCheckingTransformer.java b/src/test/java/dev/braintrust/ForbiddenTextCheckingTransformer.java new file mode 100644 index 0000000..f04d3ec --- /dev/null +++ b/src/test/java/dev/braintrust/ForbiddenTextCheckingTransformer.java @@ -0,0 +1,55 @@ +package dev.braintrust; + +import com.github.tomakehurst.wiremock.common.FileSource; +import com.github.tomakehurst.wiremock.common.Json; +import com.github.tomakehurst.wiremock.extension.Parameters; +import com.github.tomakehurst.wiremock.extension.StubMappingTransformer; +import com.github.tomakehurst.wiremock.stubbing.StubMapping; +import java.util.List; + +/** + * WireMock transformer that checks stub mappings for forbidden text (e.g., API keys) before they + * are written to disk. Throws an exception immediately if any forbidden text is found, preventing + * accidental commit of secrets. + */ +public class ForbiddenTextCheckingTransformer extends StubMappingTransformer { + + public static final String NAME = "forbidden-text-checking-transformer"; + + private final List forbiddenTexts; + + public ForbiddenTextCheckingTransformer(List forbiddenTexts) { + // Filter out null/empty strings + this.forbiddenTexts = + forbiddenTexts.stream().filter(s -> s != null && !s.isEmpty()).toList(); + } + + @Override + public String getName() { + return NAME; + } + + @Override + public StubMapping transform(StubMapping stubMapping, FileSource files, Parameters parameters) { + if (forbiddenTexts.isEmpty()) { + return stubMapping; + } + + // Serialize the stub mapping to JSON to check the full content + String json = Json.write(stubMapping); + + for (String forbidden : forbiddenTexts) { + if (json.contains(forbidden)) { + throw new IllegalStateException( + "SECURITY: Recording contains forbidden text (likely an API key). " + + "URL: " + + stubMapping.getRequest().getUrl() + + ". " + + "This recording should not be saved. " + + "Check that sensitive data is being properly redacted."); + } + } + + return stubMapping; + } +} diff --git a/src/test/java/dev/braintrust/LoginBodyRedactingTransformer.java b/src/test/java/dev/braintrust/LoginBodyRedactingTransformer.java new file mode 100644 index 0000000..f77344a --- /dev/null +++ b/src/test/java/dev/braintrust/LoginBodyRedactingTransformer.java @@ -0,0 +1,36 @@ +package dev.braintrust; + +import com.github.tomakehurst.wiremock.common.FileSource; +import com.github.tomakehurst.wiremock.extension.Parameters; +import com.github.tomakehurst.wiremock.extension.StubMappingTransformer; +import com.github.tomakehurst.wiremock.matching.RequestPattern; +import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder; +import com.github.tomakehurst.wiremock.stubbing.StubMapping; + +/** + * WireMock transformer that removes request body patterns from login endpoint recordings. This + * prevents API keys from being stored in cassette files, since the login endpoint sends the token + * in the request body. + */ +public class LoginBodyRedactingTransformer extends StubMappingTransformer { + + public static final String NAME = "login-body-redacting-transformer"; + + @Override + public String getName() { + return NAME; + } + + @Override + public StubMapping transform(StubMapping stubMapping, FileSource files, Parameters parameters) { + RequestPattern request = stubMapping.getRequest(); + String url = request.getUrl(); + if (url != null && url.contains("/api/apikey/login")) { + // Create a new request pattern without body patterns to avoid storing API keys + RequestPattern newRequest = + new RequestPatternBuilder(request.getMethod(), request.getUrlMatcher()).build(); + stubMapping.setRequest(newRequest); + } + return stubMapping; + } +} diff --git a/src/test/java/dev/braintrust/TestHarness.java b/src/test/java/dev/braintrust/TestHarness.java index 87a3235..613363c 100644 --- a/src/test/java/dev/braintrust/TestHarness.java +++ b/src/test/java/dev/braintrust/TestHarness.java @@ -28,12 +28,22 @@ public class TestHarness { private static final VCR vcr; static { + // Collect all API keys that should never appear in recorded cassettes + List apiKeysToNeverRecord = + List.of( + getEnv("OPENAI_API_KEY", ""), + getEnv("ANTHROPIC_API_KEY", ""), + getEnv("GOOGLE_API_KEY", getEnv("GEMINI_API_KEY", "")), + getEnv("BRAINTRUST_API_KEY", "")); + vcr = new VCR( java.util.Map.of( "https://api.openai.com/v1", "openai", "https://api.anthropic.com", "anthropic", - "https://generativelanguage.googleapis.com", "google")); + "https://generativelanguage.googleapis.com", "google", + "https://api.braintrust.dev", "braintrust"), + apiKeysToNeverRecord); vcr.start(); Runtime.getRuntime().addShutdownHook(new Thread(vcr::stop)); } @@ -61,19 +71,19 @@ public static synchronized TestHarness setup(BraintrustConfig config) { @Getter @Accessors(fluent = true) - private static final String defaultProjectId = "01234"; + private static final String defaultProjectId = "6ae68365-7620-4630-921b-bac416634fc8"; @Getter @Accessors(fluent = true) - private static final String defaultProjectName = "Unit Test"; + private static final String defaultProjectName = "java-unit-test"; @Getter @Accessors(fluent = true) - private static final String defaultOrgId = "567890"; + private static final String defaultOrgId = "5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e"; @Getter @Accessors(fluent = true) - private static final String defaultOrgName = "Test Org"; + private static final String defaultOrgName = "braintrustdata.com"; private static final AtomicReference INSTANCE = new AtomicReference<>(); @@ -135,6 +145,14 @@ public String googleApiKey() { return getEnv("GOOGLE_API_KEY", getEnv("GEMINI_API_KEY", "test-key")); } + public String braintrustApiBaseUrl() { + return vcr.getUrlForTargetBase("https://api.braintrust.dev"); + } + + public String braintrustApiKey() { + return getEnv("BRAINTRUST_API_KEY", "test-key"); + } + /** flush all pending spans and return all spans which have been exported so far */ public List awaitExportedSpans() { assertTrue( diff --git a/src/test/java/dev/braintrust/VCR.java b/src/test/java/dev/braintrust/VCR.java index 880fffb..bb43eab 100644 --- a/src/test/java/dev/braintrust/VCR.java +++ b/src/test/java/dev/braintrust/VCR.java @@ -4,13 +4,16 @@ import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.recording.RecordSpecBuilder; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Stream; +import javax.annotation.concurrent.ThreadSafe; import lombok.extern.slf4j.Slf4j; -import opennlp.tools.commons.ThreadSafe; /** VCR (Video Cassette Recorder) for recording and replaying HTTP interactions. */ @Slf4j @@ -30,17 +33,29 @@ public enum VcrMode { private final Map proxyMap; private final VcrMode mode; private final Map targetUrlToMappingsDir; + private final List textToNeverRecord; private boolean recordingStarted = false; public VCR(Map targetUrlToCassettesDir) { + this(targetUrlToCassettesDir, List.of()); + } + + public VCR(Map targetUrlToCassettesDir, List textToNeverRecord) { this( VcrMode.valueOf(System.getenv().getOrDefault("VCR_MODE", "replay").toUpperCase()), - targetUrlToCassettesDir); + targetUrlToCassettesDir, + textToNeverRecord); } - private VCR(VcrMode mode, Map targetUrlToCassettesDir) { + private VCR( + VcrMode mode, + Map targetUrlToCassettesDir, + List textToNeverRecord) { this.mode = mode; this.targetUrlToMappingsDir = Map.copyOf(targetUrlToCassettesDir); + // Filter out null/empty strings + this.textToNeverRecord = + textToNeverRecord.stream().filter(s -> s != null && !s.isEmpty()).toList(); // Create a WireMockServer for each provider this.proxyMap = new LinkedHashMap<>(); @@ -53,7 +68,13 @@ private VCR(VcrMode mode, Map targetUrlToCassettesDir) { WireMockServer wireMock = new WireMockServer( - wireMockConfig().dynamicPort().usingFilesUnderDirectory(cassettesDir)); + wireMockConfig() + .dynamicPort() + .usingFilesUnderDirectory(cassettesDir) + .extensions( + new LoginBodyRedactingTransformer(), + new ForbiddenTextCheckingTransformer( + this.textToNeverRecord))); proxyMap.put(targetUrl, wireMock); } } @@ -141,7 +162,12 @@ private void startRecording(String targetBaseUrl) { // Use JSON matching: // - ignoreArrayOrder=true // - ignoreExtraElements=false - .matchRequestBodyWithEqualToJson(true, false); + .matchRequestBodyWithEqualToJson(true, false) + // Remove API keys from login endpoint recordings, then check for forbidden + // text + .transformers( + LoginBodyRedactingTransformer.NAME, + ForbiddenTextCheckingTransformer.NAME); wireMock.startRecording(recordSpec); } @@ -255,8 +281,10 @@ private void stopRecording() { if (mode == VcrMode.RECORD && recordingStarted) { for (Map.Entry entry : proxyMap.entrySet()) { String targetUrl = entry.getKey(); + String mappingsDir = targetUrlToMappingsDir.get(targetUrl); WireMockServer wireMock = entry.getValue(); wireMock.stopRecording(); + validateNoForbiddenText(mappingsDir); log.info("Recording saved for {}", targetUrl); } recordingStarted = false; @@ -265,6 +293,47 @@ private void stopRecording() { // The servers will be stopped when the JVM shuts down via the shutdown hook. } + /** + * Validate that no forbidden text (e.g., API keys) appears in recorded cassettes. Throws an + * exception if any forbidden text is found, preventing accidental commit of secrets. + */ + private void validateNoForbiddenText(String mappingsDir) { + if (textToNeverRecord.isEmpty()) { + return; + } + + Path cassettesPath = Paths.get(CASSETTES_ROOT, mappingsDir); + if (!Files.exists(cassettesPath)) { + return; + } + + try (Stream files = Files.walk(cassettesPath)) { + files.filter(Files::isRegularFile) + .filter(p -> p.toString().endsWith(".json")) + .forEach(this::validateFileContainsNoForbiddenText); + } catch (IOException e) { + log.warn("Failed to validate cassettes for forbidden text: {}", e.getMessage()); + } + } + + private void validateFileContainsNoForbiddenText(Path file) { + try { + String content = Files.readString(file); + for (String forbidden : textToNeverRecord) { + if (content.contains(forbidden)) { + throw new IllegalStateException( + "SECURITY: Cassette file contains forbidden text (likely an API key). " + + "File: " + + file + + ". This cassette should not be committed. Please delete it" + + " and ensure the LoginBodyRedactingTransformer is working."); + } + } + } catch (IOException e) { + log.warn("Failed to read cassette file {}: {}", file, e.getMessage()); + } + } + private void assertStarted() { for (WireMockServer wireMock : proxyMap.values()) { if (!wireMock.isRunning()) { diff --git a/src/test/java/dev/braintrust/devserver/DevserverTest.java b/src/test/java/dev/braintrust/devserver/DevserverTest.java index b1a4065..3490534 100644 --- a/src/test/java/dev/braintrust/devserver/DevserverTest.java +++ b/src/test/java/dev/braintrust/devserver/DevserverTest.java @@ -4,16 +4,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.sun.net.httpserver.HttpServer; import dev.braintrust.TestHarness; +import dev.braintrust.TestUtils; import dev.braintrust.config.BraintrustConfig; import dev.braintrust.eval.Scorer; import io.opentelemetry.sdk.trace.data.SpanData; import java.io.BufferedReader; import java.io.InputStreamReader; -import java.io.OutputStream; import java.net.HttpURLConnection; -import java.net.InetSocketAddress; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -28,88 +26,26 @@ class DevserverTest { private static Devserver server; private static Thread serverThread; private static TestHarness testHarness; - private static HttpServer mockApiServer; - private static final int TEST_PORT = 18300; - private static final int MOCK_API_PORT = 18301; + private static final int TEST_PORT = TestUtils.getRandomOpenPort(); private static final String TEST_URL = "http://localhost:" + TEST_PORT; private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + // Remote scorer from java-unit-test project (returns 1.0 for exact match, 0.0 otherwise) + private static final String REMOTE_SCORER_FUNCTION_ID = "efa5f9c3-6ece-4726-a9d6-4ba792980b3f"; + private static final String REMOTE_SCORER_NAME = "typescript_exact_match"; + @BeforeAll static void setUp() throws Exception { - // Set up mock Braintrust API server - mockApiServer = HttpServer.create(new InetSocketAddress("localhost", MOCK_API_PORT), 0); - - // Mock /v1/project endpoint - mockApiServer.createContext( - "/v1/project", - exchange -> { - String response = - JSON_MAPPER.writeValueAsString( - Map.of( - "id", "test-project-id", - "name", "test-project", - "org_id", "test-org-id", - "created", "2023-01-01T00:00:00Z", - "updated", "2023-01-01T00:00:00Z")); - exchange.getResponseHeaders().set("Content-Type", "application/json"); - exchange.sendResponseHeaders( - 200, response.getBytes(StandardCharsets.UTF_8).length); - try (OutputStream os = exchange.getResponseBody()) { - os.write(response.getBytes(StandardCharsets.UTF_8)); - } - }); - - // Mock /v1/org endpoint - mockApiServer.createContext( - "/v1/org", - exchange -> { - String response = - JSON_MAPPER.writeValueAsString( - Map.of( - "results", - List.of( - Map.of( - "id", "test-org-id", - "name", "test-org")))); - exchange.getResponseHeaders().set("Content-Type", "application/json"); - exchange.sendResponseHeaders( - 200, response.getBytes(StandardCharsets.UTF_8).length); - try (OutputStream os = exchange.getResponseBody()) { - os.write(response.getBytes(StandardCharsets.UTF_8)); - } - }); - - // Mock /api/apikey/login endpoint (using snake_case as per API client naming strategy) - mockApiServer.createContext( - "/api/apikey/login", - exchange -> { - String response = - JSON_MAPPER.writeValueAsString( - Map.of( - "org_info", - List.of( - Map.of( - "id", "test-org-id", - "name", "test-org")))); - exchange.getResponseHeaders().set("Content-Type", "application/json"); - exchange.sendResponseHeaders( - 200, response.getBytes(StandardCharsets.UTF_8).length); - try (OutputStream os = exchange.getResponseBody()) { - os.write(response.getBytes(StandardCharsets.UTF_8)); - } - }); - - mockApiServer.start(); - - // Set up test harness with config pointing to mock API + // Set up test harness with VCR (records/replays HTTP interactions) + testHarness = TestHarness.setup(); + + // Create config pointing to VCR-proxied Braintrust API BraintrustConfig testConfig = BraintrustConfig.of( - "BRAINTRUST_API_KEY", "test-key", - "BRAINTRUST_API_URL", "http://localhost:" + MOCK_API_PORT, - "BRAINTRUST_APP_URL", "http://localhost:3000", - "BRAINTRUST_DEFAULT_PROJECT_NAME", "test-project", + "BRAINTRUST_API_KEY", testHarness.braintrustApiKey(), + "BRAINTRUST_API_URL", testHarness.braintrustApiBaseUrl(), + "BRAINTRUST_DEFAULT_PROJECT_NAME", TestHarness.defaultProjectName(), "BRAINTRUST_JAVA_EXPORT_SPANS_IN_MEMORY_FOR_UNIT_TEST", "true"); - testHarness = TestHarness.setup(testConfig); // Create a shared eval for all tests RemoteEval testEval = @@ -135,7 +71,7 @@ static void setUp() throws Exception { server = Devserver.builder() - .config(testHarness.braintrust().config()) + .config(testConfig) .registerEval(testEval) .host("localhost") .port(TEST_PORT) @@ -169,9 +105,6 @@ static void tearDown() { if (serverThread != null) { serverThread.interrupt(); } - if (mockApiServer != null) { - mockApiServer.stop(0); - } } @Test @@ -218,6 +151,14 @@ void testStreamingEval() throws Exception { Map.of("span_attributes", Map.of("generation", "test-gen-1"))); evalRequest.setParent(parentSpec); + // Add remote scorer from Braintrust + EvalRequest.RemoteScorer remoteScorer = new EvalRequest.RemoteScorer(); + remoteScorer.setName(REMOTE_SCORER_NAME); + EvalRequest.FunctionId functionId = new EvalRequest.FunctionId(); + functionId.setFunctionId(REMOTE_SCORER_FUNCTION_ID); + remoteScorer.setFunctionId(functionId); + evalRequest.setScores(List.of(remoteScorer)); + String requestBody = JSON_MAPPER.writeValueAsString(evalRequest); // Make POST request to /eval with auth headers @@ -225,9 +166,9 @@ void testStreamingEval() throws Exception { (HttpURLConnection) new URI(TEST_URL + "/eval").toURL().openConnection(); conn.setRequestMethod("POST"); conn.setRequestProperty("Content-Type", "application/json"); - conn.setRequestProperty("x-bt-auth-token", "test-token-123"); - conn.setRequestProperty("x-bt-project-id", "test-project-id"); - conn.setRequestProperty("x-bt-org-name", "test-org"); + conn.setRequestProperty("x-bt-auth-token", testHarness.braintrustApiKey()); + conn.setRequestProperty("x-bt-project-id", TestHarness.defaultProjectId()); + conn.setRequestProperty("x-bt-org-name", TestHarness.defaultOrgName()); conn.setDoOutput(true); // Write request body @@ -299,29 +240,39 @@ void testStreamingEval() throws Exception { Map summaryEvent = summaryEvents.get(0); JsonNode summaryData = JSON_MAPPER.readTree(summaryEvent.get("data")); - assertEquals("test-project", summaryData.get("projectName").asText()); + assertEquals(TestHarness.defaultProjectName(), summaryData.get("projectName").asText()); assertTrue(summaryData.has("projectId")); assertEquals("food-type-classifier", summaryData.get("experimentName").asText()); // Verify scores in summary assertTrue(summaryData.has("scores")); JsonNode scores = summaryData.get("scores"); - assertTrue(scores.has("simple_scorer")); + + // Verify local scorer + assertTrue(scores.has("simple_scorer"), "Summary should have simple_scorer"); JsonNode simpleScorer = scores.get("simple_scorer"); assertEquals("simple_scorer", simpleScorer.get("name").asText()); assertEquals(0.7, simpleScorer.get("score").asDouble(), 0.001); + + // Verify remote scorer (returns 0.0 because output "java-fruit" != expected + // "fruit"/"vegetable") + assertTrue(scores.has(REMOTE_SCORER_NAME), "Summary should have remote scorer"); + JsonNode remoteScorerResult = scores.get(REMOTE_SCORER_NAME); + assertEquals(REMOTE_SCORER_NAME, remoteScorerResult.get("name").asText()); + assertEquals(0.0, remoteScorerResult.get("score").asDouble(), 0.001); } // Get exported spans from test harness (since devserver uses global tracer) List exportedSpans = testHarness.awaitExportedSpans(); assertFalse(exportedSpans.isEmpty(), "Should have exported spans"); - // We should have 2 eval traces (one per dataset case), each with task, score, and custom + // We should have 2 eval traces (one per dataset case), each with task, scores, and custom // spans - // Each trace has: 1 eval span, 1 task span, 1 score span, 1 custom-task-span = 4 spans + // Each trace has: 1 eval span, 1 task span, 2 score spans (local + remote), 1 + // custom-task-span = 5 spans // per case - // Total: 2 cases * 4 spans = 8 spans - assertEquals(8, exportedSpans.size(), "Should have 8 spans (4 per dataset case)"); + // Total: 2 cases * 5 spans = 10 spans + assertEquals(10, exportedSpans.size(), "Should have 10 spans (5 per dataset case)"); // Verify span types var evalSpans = exportedSpans.stream().filter(s -> s.getName().equals("eval")).toList(); @@ -332,7 +283,7 @@ void testStreamingEval() throws Exception { assertEquals(2, evalSpans.size(), "Should have 2 eval spans"); assertEquals(2, taskSpans.size(), "Should have 2 task spans"); - assertEquals(2, scoreSpans.size(), "Should have 2 score spans"); + assertEquals(4, scoreSpans.size(), "Should have 4 score spans (2 scorers x 2 cases)"); assertEquals(2, customSpans.size(), "Should have 2 custom-task-span spans"); // Verify eval spans have all required attributes @@ -446,9 +397,14 @@ void testStreamingEval() throws Exception { assertNotNull(spanAttrsJson, "Score span should have span_attributes"); JsonNode spanAttrs = JSON_MAPPER.readTree(spanAttrsJson); assertEquals("score", spanAttrs.get("type").asText()); - assertEquals("simple_scorer", spanAttrs.get("name").asText()); assertEquals("test-gen-1", spanAttrs.get("generation").asText()); + // Scorer name should be either simple_scorer or the remote scorer + String scorerName = spanAttrs.get("name").asText(); + assertTrue( + scorerName.equals("simple_scorer") || scorerName.equals(REMOTE_SCORER_NAME), + "Score span name should be simple_scorer or " + REMOTE_SCORER_NAME); + // Verify braintrust.output_json contains scores String outputJson = scoreSpan @@ -458,8 +414,17 @@ void testStreamingEval() throws Exception { "braintrust.output_json")); assertNotNull(outputJson, "Score span should have output_json"); JsonNode output = JSON_MAPPER.readTree(outputJson); - assertTrue(output.has("simple_scorer"), "Output should contain scorer results"); - assertEquals(0.7, output.get("simple_scorer").asDouble(), 0.001); + + if (scorerName.equals("simple_scorer")) { + assertTrue( + output.has("simple_scorer"), "Output should contain simple_scorer results"); + assertEquals(0.7, output.get("simple_scorer").asDouble(), 0.001); + } else { + assertTrue( + output.has(REMOTE_SCORER_NAME), + "Output should contain remote scorer results"); + assertEquals(0.0, output.get(REMOTE_SCORER_NAME).asDouble(), 0.001); + } } for (SpanData customSpan : customSpans) { @@ -506,9 +471,9 @@ void testEvaluatorNotFound() throws Exception { .uri(URI.create(TEST_URL + "/eval")) .POST(HttpRequest.BodyPublishers.ofString(requestJson)) .header("Content-Type", "application/json") - .header("x-bt-auth-token", "test-token") - .header("x-bt-project-id", "test-project-id") - .header("x-bt-org-name", "test-org") + .header("x-bt-auth-token", testHarness.braintrustApiKey()) + .header("x-bt-project-id", TestHarness.defaultProjectId()) + .header("x-bt-org-name", TestHarness.defaultOrgName()) .build(); HttpResponse response = @@ -536,9 +501,9 @@ void testListEndpoint() throws Exception { HttpRequest.newBuilder() .uri(URI.create(TEST_URL + "/list")) .GET() - .header("x-bt-auth-token", "test-token") - .header("x-bt-project-id", "test-project-id") - .header("x-bt-org-name", "test-org") + .header("x-bt-auth-token", testHarness.braintrustApiKey()) + .header("x-bt-project-id", TestHarness.defaultProjectId()) + .header("x-bt-org-name", TestHarness.defaultOrgName()) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); @@ -585,9 +550,9 @@ void testListEndpointWithCors() throws Exception { .uri(URI.create(TEST_URL + "/list")) .GET() .header("Origin", "https://www.braintrust.dev") - .header("x-bt-auth-token", "test-token") - .header("x-bt-project-id", "test-project-id") - .header("x-bt-org-name", "test-org") + .header("x-bt-auth-token", testHarness.braintrustApiKey()) + .header("x-bt-project-id", TestHarness.defaultProjectId()) + .header("x-bt-org-name", TestHarness.defaultOrgName()) .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); diff --git a/src/test/java/dev/braintrust/eval/ScorerBrainstoreImplTest.java b/src/test/java/dev/braintrust/eval/ScorerBrainstoreImplTest.java new file mode 100644 index 0000000..ffbc35c --- /dev/null +++ b/src/test/java/dev/braintrust/eval/ScorerBrainstoreImplTest.java @@ -0,0 +1,135 @@ +package dev.braintrust.eval; + +import static org.junit.jupiter.api.Assertions.*; + +import dev.braintrust.TestHarness; +import dev.braintrust.api.BraintrustApiClient; +import dev.braintrust.config.BraintrustConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class ScorerBrainstoreImplTest { + // NOTE: the remote scorers under test are standard boilerplate + // TODO: test is VCR'd so it's fine, but would be nice to have logic to (re)create the score + // objects if they are absent + + // returns 1.0 for an exact match, 0.0 otherwise + private static final String SCORER_SLUG = "typescriptexactmatch-9e44"; + + // LLM judge scorer that returns {"name":"close-enough-judge","metadata":{"choice":"0.9",...}} + private static final String LLM_JUDGE_SLUG = "close-enough-judge-d31b"; + + private TestHarness testHarness; + private BraintrustApiClient apiClient; + + @BeforeEach + void beforeEach() { + testHarness = TestHarness.setup(); + + var config = + BraintrustConfig.builder() + .apiKey(testHarness.braintrustApiKey()) + .apiUrl(testHarness.braintrustApiBaseUrl()) + .build(); + + apiClient = BraintrustApiClient.of(config); + } + + @Test + void testScorerReturnsOneForExactMatch() { + Scorer scorer = + Scorer.fetchFromBraintrust( + apiClient, + testHarness.braintrust().config().defaultProjectName().orElseThrow(), + SCORER_SLUG, + null); + assertNotNull(scorer); + assertNotNull(scorer.getName()); + + var datasetCase = DatasetCase.of("test input", "hello world"); + var taskResult = new TaskResult<>("hello world", datasetCase); + + var scores = scorer.score(taskResult); + + assertFalse(scores.isEmpty(), "Expected scores but got empty list"); + assertEquals(1.0, scores.get(0).value(), 0.001, "Exact match should return 1.0"); + } + + @Test + void testScorerReturnsZeroForMismatch() { + Scorer scorer = + Scorer.fetchFromBraintrust( + apiClient, + testHarness.braintrust().config().defaultProjectName().orElseThrow(), + SCORER_SLUG, + null); + assertNotNull(scorer); + assertNotNull(scorer.getName()); + + var datasetCase = DatasetCase.of("test input", "expected"); + var taskResult = new TaskResult<>("different", datasetCase); + + var scores = scorer.score(taskResult); + + assertFalse(scores.isEmpty(), "Expected scores but got empty list"); + assertEquals(0.0, scores.get(0).value(), 0.001, "Mismatch should return 0.0"); + } + + @Test + void testScorerOldVersion() { + // Version 485dbf64e486ab3a of the exact match scorer always returns 0, even for exact + // matches + String oldVersion = "485dbf64e486ab3a"; + Scorer scorer = + Scorer.fetchFromBraintrust( + apiClient, + testHarness.braintrust().config().defaultProjectName().orElseThrow(), + SCORER_SLUG, + oldVersion); + assertNotNull(scorer); + assertNotNull(scorer.getName()); + + var datasetCase = DatasetCase.of("test input", "hello world"); + var taskResult = new TaskResult<>("hello world", datasetCase); + + var scores = scorer.score(taskResult); + + assertFalse(scores.isEmpty(), "Expected scores but got empty list"); + assertEquals( + 0.0, + scores.get(0).value(), + 0.001, + "Old version %s should always return 0.0, even for exact match" + .formatted(oldVersion)); + } + + @Test + void testLlmJudgeScorerReturnsScoreFromMetadataChoice() { + Scorer scorer = + Scorer.fetchFromBraintrust( + apiClient, + testHarness.braintrust().config().defaultProjectName().orElseThrow(), + LLM_JUDGE_SLUG, + null); + assertNotNull(scorer); + assertNotNull(scorer.getName()); + + // LLM judge evaluates whether output is "close enough" to expected + var datasetCase = DatasetCase.of("What is 2+2?", "4"); + var taskResult = new TaskResult<>("four", datasetCase); + + var scores = scorer.score(taskResult); + + assertFalse(scores.isEmpty(), "Expected scores but got empty list"); + // LLM judge returns score in metadata.choice field + // The score should be between 0.0 and 1.0 + assertTrue( + scores.get(0).value() >= 0.0 && scores.get(0).value() <= 1.0, + "LLM judge score should be between 0.0 and 1.0, got: " + scores.get(0).value()); + // Verify the scorer name comes from the response + assertEquals( + "close-enough-judge", + scores.get(0).name(), + "Scorer name should come from the LLM judge response"); + } +} diff --git a/src/test/resources/cassettes/anthropic/__files/v1_messages-4c99ad9a-3bc5-4f73-bd08-84f892478c4d.json b/src/test/resources/cassettes/anthropic/__files/v1_messages-10bd174c-61dd-42e0-b078-0f899387c789.json similarity index 70% rename from src/test/resources/cassettes/anthropic/__files/v1_messages-4c99ad9a-3bc5-4f73-bd08-84f892478c4d.json rename to src/test/resources/cassettes/anthropic/__files/v1_messages-10bd174c-61dd-42e0-b078-0f899387c789.json index 4481d62..f08a839 100644 --- a/src/test/resources/cassettes/anthropic/__files/v1_messages-4c99ad9a-3bc5-4f73-bd08-84f892478c4d.json +++ b/src/test/resources/cassettes/anthropic/__files/v1_messages-10bd174c-61dd-42e0-b078-0f899387c789.json @@ -1 +1 @@ -{"model":"claude-3-5-haiku-20241022","id":"msg_01MhSBYGdz56JjxtCkyqLNMi","type":"message","role":"assistant","content":[{"type":"text","text":"The capital of France is Paris."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":19,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard"}} \ No newline at end of file +{"model":"claude-3-5-haiku-20241022","id":"msg_012gishQVgVX9SbBiEo4dCp8","type":"message","role":"assistant","content":[{"type":"text","text":"The capital of France is Paris."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":19,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":10,"service_tier":"standard"}} \ No newline at end of file diff --git a/src/test/resources/cassettes/anthropic/__files/v1_messages-68182ecb-89f2-4d6d-9d6a-49cb3f036357.txt b/src/test/resources/cassettes/anthropic/__files/v1_messages-8120a850-75ab-4fe2-8c4b-f0367beada3a.txt similarity index 73% rename from src/test/resources/cassettes/anthropic/__files/v1_messages-68182ecb-89f2-4d6d-9d6a-49cb3f036357.txt rename to src/test/resources/cassettes/anthropic/__files/v1_messages-8120a850-75ab-4fe2-8c4b-f0367beada3a.txt index 65b3b61..63ec779 100644 --- a/src/test/resources/cassettes/anthropic/__files/v1_messages-68182ecb-89f2-4d6d-9d6a-49cb3f036357.txt +++ b/src/test/resources/cassettes/anthropic/__files/v1_messages-8120a850-75ab-4fe2-8c4b-f0367beada3a.txt @@ -1,24 +1,24 @@ event: message_start -data: {"type":"message_start","message":{"model":"claude-3-5-haiku-20241022","id":"msg_01YTLrex9iVq8MvNEBcvw3cG","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":19,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":5,"service_tier":"standard"}} } +data: {"type":"message_start","message":{"model":"claude-3-5-haiku-20241022","id":"msg_012tS1cYEUH4trpwrzTXPaif","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":19,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":5,"service_tier":"standard"}} } event: content_block_start -data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } +data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } event: ping data: {"type": "ping"} event: content_block_delta -data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"The capital of France is"} } +data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"The capital of France is"} } event: content_block_delta -data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" Paris."} } +data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" Paris."}} event: content_block_stop -data: {"type":"content_block_stop","index":0} +data: {"type":"content_block_stop","index":0 } event: message_delta -data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":19,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":10} } +data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":19,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":10} } event: message_stop -data: {"type":"message_stop" } +data: {"type":"message_stop" } diff --git a/src/test/resources/cassettes/anthropic/mappings/v1_messages-4c99ad9a-3bc5-4f73-bd08-84f892478c4d.json b/src/test/resources/cassettes/anthropic/mappings/v1_messages-10bd174c-61dd-42e0-b078-0f899387c789.json similarity index 68% rename from src/test/resources/cassettes/anthropic/mappings/v1_messages-4c99ad9a-3bc5-4f73-bd08-84f892478c4d.json rename to src/test/resources/cassettes/anthropic/mappings/v1_messages-10bd174c-61dd-42e0-b078-0f899387c789.json index 7ad4058..4f85bba 100644 --- a/src/test/resources/cassettes/anthropic/mappings/v1_messages-4c99ad9a-3bc5-4f73-bd08-84f892478c4d.json +++ b/src/test/resources/cassettes/anthropic/mappings/v1_messages-10bd174c-61dd-42e0-b078-0f899387c789.json @@ -1,5 +1,5 @@ { - "id" : "4c99ad9a-3bc5-4f73-bd08-84f892478c4d", + "id" : "10bd174c-61dd-42e0-b078-0f899387c789", "name" : "v1_messages", "request" : { "url" : "/v1/messages", @@ -17,33 +17,33 @@ }, "response" : { "status" : 200, - "bodyFileName" : "v1_messages-4c99ad9a-3bc5-4f73-bd08-84f892478c4d.json", + "bodyFileName" : "v1_messages-10bd174c-61dd-42e0-b078-0f899387c789.json", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:40 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:11 GMT", "Content-Type" : "application/json", "anthropic-ratelimit-requests-limit" : "10000", "anthropic-ratelimit-requests-remaining" : "9999", - "anthropic-ratelimit-requests-reset" : "2026-01-13T18:55:39Z", + "anthropic-ratelimit-requests-reset" : "2026-01-23T05:36:11Z", "anthropic-ratelimit-input-tokens-limit" : "5000000", "anthropic-ratelimit-input-tokens-remaining" : "5000000", - "anthropic-ratelimit-input-tokens-reset" : "2026-01-13T18:55:39Z", + "anthropic-ratelimit-input-tokens-reset" : "2026-01-23T05:36:11Z", "anthropic-ratelimit-output-tokens-limit" : "1000000", "anthropic-ratelimit-output-tokens-remaining" : "1000000", - "anthropic-ratelimit-output-tokens-reset" : "2026-01-13T18:55:40Z", + "anthropic-ratelimit-output-tokens-reset" : "2026-01-23T05:36:11Z", "anthropic-ratelimit-tokens-limit" : "6000000", "anthropic-ratelimit-tokens-remaining" : "6000000", - "anthropic-ratelimit-tokens-reset" : "2026-01-13T18:55:39Z", - "request-id" : "req_011CX5tMzY85axeEQMUkdDni", + "anthropic-ratelimit-tokens-reset" : "2026-01-23T05:36:11Z", + "X-Robots-Tag" : "none", + "request-id" : "req_011CXPmVCSBbJwY2Ysqo5QAg", "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", "anthropic-organization-id" : "27796668-7351-40ac-acc4-024aee8995a5", "Server" : "cloudflare", - "x-envoy-upstream-service-time" : "538", + "x-envoy-upstream-service-time" : "455", "cf-cache-status" : "DYNAMIC", - "X-Robots-Tag" : "none", - "CF-RAY" : "9bd71befef2c76b2-SEA" + "CF-RAY" : "9c24ee95dd89db33-SEA" } }, - "uuid" : "4c99ad9a-3bc5-4f73-bd08-84f892478c4d", + "uuid" : "10bd174c-61dd-42e0-b078-0f899387c789", "persistent" : true, "insertionIndex" : 1 } \ No newline at end of file diff --git a/src/test/resources/cassettes/anthropic/mappings/v1_messages-68182ecb-89f2-4d6d-9d6a-49cb3f036357.json b/src/test/resources/cassettes/anthropic/mappings/v1_messages-8120a850-75ab-4fe2-8c4b-f0367beada3a.json similarity index 69% rename from src/test/resources/cassettes/anthropic/mappings/v1_messages-68182ecb-89f2-4d6d-9d6a-49cb3f036357.json rename to src/test/resources/cassettes/anthropic/mappings/v1_messages-8120a850-75ab-4fe2-8c4b-f0367beada3a.json index 9e01635..f482886 100644 --- a/src/test/resources/cassettes/anthropic/mappings/v1_messages-68182ecb-89f2-4d6d-9d6a-49cb3f036357.json +++ b/src/test/resources/cassettes/anthropic/mappings/v1_messages-8120a850-75ab-4fe2-8c4b-f0367beada3a.json @@ -1,5 +1,5 @@ { - "id" : "68182ecb-89f2-4d6d-9d6a-49cb3f036357", + "id" : "8120a850-75ab-4fe2-8c4b-f0367beada3a", "name" : "v1_messages", "request" : { "url" : "/v1/messages", @@ -17,34 +17,34 @@ }, "response" : { "status" : 200, - "bodyFileName" : "v1_messages-68182ecb-89f2-4d6d-9d6a-49cb3f036357.txt", + "bodyFileName" : "v1_messages-8120a850-75ab-4fe2-8c4b-f0367beada3a.txt", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:41 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:12 GMT", "Content-Type" : "text/event-stream; charset=utf-8", "Cache-Control" : "no-cache", "anthropic-ratelimit-requests-limit" : "10000", "anthropic-ratelimit-requests-remaining" : "9999", - "anthropic-ratelimit-requests-reset" : "2026-01-13T18:55:40Z", + "anthropic-ratelimit-requests-reset" : "2026-01-23T05:36:11Z", "anthropic-ratelimit-input-tokens-limit" : "5000000", "anthropic-ratelimit-input-tokens-remaining" : "5000000", - "anthropic-ratelimit-input-tokens-reset" : "2026-01-13T18:55:40Z", + "anthropic-ratelimit-input-tokens-reset" : "2026-01-23T05:36:11Z", "anthropic-ratelimit-output-tokens-limit" : "1000000", "anthropic-ratelimit-output-tokens-remaining" : "1000000", - "anthropic-ratelimit-output-tokens-reset" : "2026-01-13T18:55:40Z", + "anthropic-ratelimit-output-tokens-reset" : "2026-01-23T05:36:11Z", "anthropic-ratelimit-tokens-limit" : "6000000", "anthropic-ratelimit-tokens-remaining" : "6000000", - "anthropic-ratelimit-tokens-reset" : "2026-01-13T18:55:40Z", - "request-id" : "req_011CX5tN5bEBPFm9bcjPAmud", + "anthropic-ratelimit-tokens-reset" : "2026-01-23T05:36:11Z", + "request-id" : "req_011CXPmVFdRQeFhPG37DhYW6", "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", "anthropic-organization-id" : "27796668-7351-40ac-acc4-024aee8995a5", "Server" : "cloudflare", - "x-envoy-upstream-service-time" : "371", + "x-envoy-upstream-service-time" : "346", "cf-cache-status" : "DYNAMIC", "X-Robots-Tag" : "none", - "CF-RAY" : "9bd71bf7499f347d-SEA" + "CF-RAY" : "9c24ee9a8aadec98-SEA" } }, - "uuid" : "68182ecb-89f2-4d6d-9d6a-49cb3f036357", + "uuid" : "8120a850-75ab-4fe2-8c4b-f0367beada3a", "persistent" : true, "insertionIndex" : 2 } \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/api_apikey_login-68a49a40-dfe4-41d9-a6a7-5023c431254b.json b/src/test/resources/cassettes/braintrust/__files/api_apikey_login-68a49a40-dfe4-41d9-a6a7-5023c431254b.json new file mode 100644 index 0000000..9a18a48 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/api_apikey_login-68a49a40-dfe4-41d9-a6a7-5023c431254b.json @@ -0,0 +1 @@ +{"org_info":[{"id":"5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e","name":"braintrustdata.com","api_url":"https://staging-api.braintrust.dev","git_metadata":{"fields":["commit","branch","tag","author_name","author_email","commit_message","commit_time","dirty"],"collect":"some"},"is_universal_api":true,"proxy_url":"https://staging-api.braintrust.dev","realtime_url":"wss://realtime.braintrustapi.com"}]} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/api_apikey_login-720807f6-1acb-4361-8f82-8fd8c71d6a99.json b/src/test/resources/cassettes/braintrust/__files/api_apikey_login-720807f6-1acb-4361-8f82-8fd8c71d6a99.json new file mode 100644 index 0000000..9a18a48 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/api_apikey_login-720807f6-1acb-4361-8f82-8fd8c71d6a99.json @@ -0,0 +1 @@ +{"org_info":[{"id":"5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e","name":"braintrustdata.com","api_url":"https://staging-api.braintrust.dev","git_metadata":{"fields":["commit","branch","tag","author_name","author_email","commit_message","commit_time","dirty"],"collect":"some"},"is_universal_api":true,"proxy_url":"https://staging-api.braintrust.dev","realtime_url":"wss://realtime.braintrustapi.com"}]} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function-32c90375-557c-4ef8-9263-383a8386366a.json b/src/test/resources/cassettes/braintrust/__files/v1_function-32c90375-557c-4ef8-9263-383a8386366a.json new file mode 100644 index 0000000..b0a4be5 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function-32c90375-557c-4ef8-9263-383a8386366a.json @@ -0,0 +1 @@ +{"objects":[{"id":"efa5f9c3-6ece-4726-a9d6-4ba792980b3f","_xact_id":"1000196534222689290","project_id":"6ae68365-7620-4630-921b-bac416634fc8","log_id":"p","org_id":"5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e","name":"typescript_exact_match","slug":"typescriptexactmatch-9e44","description":null,"created":"2026-01-23T02:07:39.293Z","prompt_data":null,"tags":null,"metadata":null,"function_type":"scorer","function_data":{"data":{"code":"// Enter handler function that returns a numeric score between 0 and 1,\n// or null to skip scoring\nfunction handler({\n input,\n output,\n expected,\n metadata,\n}: {\n input: any;\n output: any;\n expected: any;\n metadata: Record;\n}): number | null {\n if (expected === null) return null;\n return output === expected ? 0 : 0;\n}","type":"inline","runtime_context":{"runtime":"node","version":"22"}},"type":"code"},"origin":null,"function_schema":null}]} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function-a40fed81-8f60-44a2-819c-afa1c6941fe1.json b/src/test/resources/cassettes/braintrust/__files/v1_function-a40fed81-8f60-44a2-819c-afa1c6941fe1.json new file mode 100644 index 0000000..5dd6d39 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function-a40fed81-8f60-44a2-819c-afa1c6941fe1.json @@ -0,0 +1 @@ +{"objects":[{"id":"5dd8a26d-3be8-4ecd-af5b-df2a6a592277","_xact_id":"1000196533616093054","project_id":"6ae68365-7620-4630-921b-bac416634fc8","log_id":"p","org_id":"5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e","name":"close-enough-judge","slug":"close-enough-judge-d31b","description":null,"created":"2026-01-22T23:33:23.674Z","prompt_data":{"parser":{"type":"llm_classifier","use_cot":true,"choice_scores":{"":0}},"prompt":{"type":"chat","messages":[{"role":"system","content":"you are an LLM eval scorer. you will evaluate output compared to expected results and return a value between 0.0 and 1.0 (0 is worst 1 is best)"},{"role":"user","content":"how closely does my eval case output: `{{output}}` match the expected result of `{{expected}}`"}]},"options":{"model":"gpt-5-mini","params":{"use_cache":true,"temperature":0},"position":"0|hzzzzz:"}},"tags":null,"metadata":null,"function_type":"scorer","function_data":{"type":"prompt"},"origin":null,"function_schema":null}]} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function-ce46228b-9279-4285-9695-5d3ab0ebb857.json b/src/test/resources/cassettes/braintrust/__files/v1_function-ce46228b-9279-4285-9695-5d3ab0ebb857.json new file mode 100644 index 0000000..69c0e3c --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function-ce46228b-9279-4285-9695-5d3ab0ebb857.json @@ -0,0 +1 @@ +{"objects":[{"id":"efa5f9c3-6ece-4726-a9d6-4ba792980b3f","_xact_id":"1000196534223020329","project_id":"6ae68365-7620-4630-921b-bac416634fc8","log_id":"p","org_id":"5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e","name":"typescript_exact_match","slug":"typescriptexactmatch-9e44","description":null,"created":"2026-01-23T02:07:44.212Z","prompt_data":null,"tags":null,"metadata":null,"function_type":"scorer","function_data":{"data":{"code":"// Enter handler function that returns a numeric score between 0 and 1,\n// or null to skip scoring\nfunction handler({\n input,\n output,\n expected,\n metadata,\n}: {\n input: any;\n output: any;\n expected: any;\n metadata: Record;\n}): number | null {\n if (expected === null) return null;\n return output === expected ? 1 : 0;\n}","type":"inline","runtime_context":{"runtime":"node","version":"22"}},"type":"code"},"origin":null,"function_schema":null}]} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function-ec6186f4-a80d-4a8c-81fb-ad828ba038b8.json b/src/test/resources/cassettes/braintrust/__files/v1_function-ec6186f4-a80d-4a8c-81fb-ad828ba038b8.json new file mode 100644 index 0000000..69c0e3c --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function-ec6186f4-a80d-4a8c-81fb-ad828ba038b8.json @@ -0,0 +1 @@ +{"objects":[{"id":"efa5f9c3-6ece-4726-a9d6-4ba792980b3f","_xact_id":"1000196534223020329","project_id":"6ae68365-7620-4630-921b-bac416634fc8","log_id":"p","org_id":"5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e","name":"typescript_exact_match","slug":"typescriptexactmatch-9e44","description":null,"created":"2026-01-23T02:07:44.212Z","prompt_data":null,"tags":null,"metadata":null,"function_type":"scorer","function_data":{"data":{"code":"// Enter handler function that returns a numeric score between 0 and 1,\n// or null to skip scoring\nfunction handler({\n input,\n output,\n expected,\n metadata,\n}: {\n input: any;\n output: any;\n expected: any;\n metadata: Record;\n}): number | null {\n if (expected === null) return null;\n return output === expected ? 1 : 0;\n}","type":"inline","runtime_context":{"runtime":"node","version":"22"}},"type":"code"},"origin":null,"function_schema":null}]} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke-c67cd10d-7d05-4e66-a861-360e14dace92.json b/src/test/resources/cassettes/braintrust/__files/v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke-c67cd10d-7d05-4e66-a861-360e14dace92.json new file mode 100644 index 0000000..e35193e --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke-c67cd10d-7d05-4e66-a861-360e14dace92.json @@ -0,0 +1 @@ +{"name":"close-enough-judge","score":null,"metadata":{"rationale":"1) Identify expected: the expected result is the numeral '4', which represents the integer 4.\n2) Identify output: the candidate output is the word 'four', which is the English lexical representation of the same integer.\n3) Semantic comparison: 'four' and '4' denote the same value; under semantic or normalized numeric evaluation they are equivalent.\n4) Token/string exact-match consideration: as raw strings they differ ('four' != '4'), so under a strict exact-byte/string-match metric they would score 0.\n5) Typical evaluation practice: most evaluation for numeric answers either normalize number words and digits or compare numeric value; in that common case they match perfectly.\nConclusion: under a sensible normalization/numeric equivalence metric the match is perfect, so score 1.0; under strict raw string match it would be 0.0. I choose the normalized/numeric-equivalence interpretation.","choice":"1.0"}} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-69d15cfb-87c7-41a1-a99a-df653a67ec0a.json b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-69d15cfb-87c7-41a1-a99a-df653a67ec0a.json new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-69d15cfb-87c7-41a1-a99a-df653a67ec0a.json @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a5548413-7bea-4a3b-8dd6-575e0594ca85.json b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a5548413-7bea-4a3b-8dd6-575e0594ca85.json new file mode 100644 index 0000000..56a6051 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a5548413-7bea-4a3b-8dd6-575e0594ca85.json @@ -0,0 +1 @@ +1 \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a599c78a-4aec-4bf0-b64e-e9b183b45c57.json b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a599c78a-4aec-4bf0-b64e-e9b183b45c57.json new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a599c78a-4aec-4bf0-b64e-e9b183b45c57.json @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-d2eef3e5-3348-4b4d-8ea5-08d7da45f22b.json b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-d2eef3e5-3348-4b4d-8ea5-08d7da45f22b.json new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-d2eef3e5-3348-4b4d-8ea5-08d7da45f22b.json @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0.json b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0.json new file mode 100644 index 0000000..c227083 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0.json @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/__files/v1_project_6ae68365-7620-4630-921b-bac416634fc8-3e4fd9ae-b027-4c4e-a714-5ba10db840dd.json b/src/test/resources/cassettes/braintrust/__files/v1_project_6ae68365-7620-4630-921b-bac416634fc8-3e4fd9ae-b027-4c4e-a714-5ba10db840dd.json new file mode 100644 index 0000000..f2e7801 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/__files/v1_project_6ae68365-7620-4630-921b-bac416634fc8-3e4fd9ae-b027-4c4e-a714-5ba10db840dd.json @@ -0,0 +1 @@ +{"id":"6ae68365-7620-4630-921b-bac416634fc8","org_id":"5d7c97d7-fef1-4cb7-bda6-7e3756a0ca8e","name":"java-unit-test","description":null,"created":"2026-01-21T01:32:52.137Z","deleted_at":null,"user_id":"a5ca7f9c-bf20-40c4-a82b-5c992f6a38f5","settings":{"remote_eval_sources":[{"url":"http://localhost:8301","name":"java-devserver","description":null}]}} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/api_apikey_login-68a49a40-dfe4-41d9-a6a7-5023c431254b.json b/src/test/resources/cassettes/braintrust/mappings/api_apikey_login-68a49a40-dfe4-41d9-a6a7-5023c431254b.json new file mode 100644 index 0000000..8b8aeb2 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/api_apikey_login-68a49a40-dfe4-41d9-a6a7-5023c431254b.json @@ -0,0 +1,35 @@ +{ + "id" : "68a49a40-dfe4-41d9-a6a7-5023c431254b", + "name" : "api_apikey_login", + "request" : { + "url" : "/api/apikey/login", + "method" : "POST" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_apikey_login-68a49a40-dfe4-41d9-a6a7-5023c431254b.json", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "X-Amz-Cf-Pop" : [ "SEA900-P2", "SEA900-P10" ], + "Date" : "Fri, 23 Jan 2026 05:36:05 GMT", + "access-control-allow-credentials" : "true", + "x-amzn-RequestId" : "23f528d7-ebf7-41e2-9f0b-ef2bb770438b", + "x-amzn-Remapped-content-length" : "395", + "x-bt-internal-trace-id" : "697308c50000000068d808765efbc7e1", + "x-amz-apigw-id" : "Xn5O5HVWIAMEZvA=", + "vary" : "Origin, Accept-Encoding", + "etag" : "W/\"18b-OPCBBHzVVuCPaglXVbFjmsFzOoE\"", + "access-control-expose-headers" : "x-bt-cursor,x-bt-found-existing,x-bt-query-plan", + "X-Amzn-Trace-Id" : "Root=1-697308c5-09266ed92479ae524fed16c7;Parent=569846c52114e196;Sampled=0;Lineage=1:24be3d11:0", + "Via" : "1.1 5f438acc4bc4bcf2bce0445584c013cc.cloudfront.net (CloudFront), 1.1 0df7f27a01014ab815259ca2d88193c6.cloudfront.net (CloudFront)", + "X-Cache" : "Miss from cloudfront", + "X-Amz-Cf-Id" : "q2fthlL0f-de6l-0TpHMQBwTLJEaA3fth2klx168qpqNVdklUPXP4Q==" + } + }, + "uuid" : "68a49a40-dfe4-41d9-a6a7-5023c431254b", + "persistent" : true, + "scenarioName" : "scenario-1-api-apikey-login", + "requiredScenarioState" : "Started", + "newScenarioState" : "scenario-1-api-apikey-login-2", + "insertionIndex" : 1 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/api_apikey_login-720807f6-1acb-4361-8f82-8fd8c71d6a99.json b/src/test/resources/cassettes/braintrust/mappings/api_apikey_login-720807f6-1acb-4361-8f82-8fd8c71d6a99.json new file mode 100644 index 0000000..791cefc --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/api_apikey_login-720807f6-1acb-4361-8f82-8fd8c71d6a99.json @@ -0,0 +1,34 @@ +{ + "id" : "720807f6-1acb-4361-8f82-8fd8c71d6a99", + "name" : "api_apikey_login", + "request" : { + "url" : "/api/apikey/login", + "method" : "POST" + }, + "response" : { + "status" : 200, + "bodyFileName" : "api_apikey_login-720807f6-1acb-4361-8f82-8fd8c71d6a99.json", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "X-Amz-Cf-Pop" : [ "SEA900-P2", "SEA900-P10" ], + "Date" : "Fri, 23 Jan 2026 05:36:06 GMT", + "access-control-allow-credentials" : "true", + "x-amzn-RequestId" : "9714cf06-3569-4d6c-a9a2-fb3fe1b886e8", + "x-amzn-Remapped-content-length" : "395", + "x-bt-internal-trace-id" : "697308c60000000064f3337c94881869", + "x-amz-apigw-id" : "Xn5PBGhPIAMEZNA=", + "vary" : "Origin, Accept-Encoding", + "etag" : "W/\"18b-OPCBBHzVVuCPaglXVbFjmsFzOoE\"", + "access-control-expose-headers" : "x-bt-cursor,x-bt-found-existing,x-bt-query-plan", + "X-Amzn-Trace-Id" : "Root=1-697308c6-1b2bc45629f791de2a39b5b5;Parent=5c1a0861db4a7a74;Sampled=0;Lineage=1:24be3d11:0", + "Via" : "1.1 d178790752746ce7e53fab1b13e75448.cloudfront.net (CloudFront), 1.1 74e8c76139b8c7f9b11d5e4441c2a7a2.cloudfront.net (CloudFront)", + "X-Cache" : "Miss from cloudfront", + "X-Amz-Cf-Id" : "8v0fRj1prIwPHYubr8dcUNM_L5XiFvlYG4U7mq97jOFy9VaDLpQ87A==" + } + }, + "uuid" : "720807f6-1acb-4361-8f82-8fd8c71d6a99", + "persistent" : true, + "scenarioName" : "scenario-1-api-apikey-login", + "requiredScenarioState" : "scenario-1-api-apikey-login-2", + "insertionIndex" : 3 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function-32c90375-557c-4ef8-9263-383a8386366a.json b/src/test/resources/cassettes/braintrust/mappings/v1_function-32c90375-557c-4ef8-9263-383a8386366a.json new file mode 100644 index 0000000..a76b203 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function-32c90375-557c-4ef8-9263-383a8386366a.json @@ -0,0 +1,32 @@ +{ + "id" : "32c90375-557c-4ef8-9263-383a8386366a", + "name" : "v1_function", + "request" : { + "url" : "/v1/function?slug=typescriptexactmatch-9e44&project_name=java-unit-test&version=485dbf64e486ab3a", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function-32c90375-557c-4ef8-9263-383a8386366a.json", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "X-Amz-Cf-Pop" : [ "SEA900-P2", "SEA900-P10" ], + "Date" : "Fri, 23 Jan 2026 05:36:10 GMT", + "access-control-allow-credentials" : "true", + "x-amzn-RequestId" : "91343f02-6a2d-49e1-9f20-1f8451a31cff", + "x-amzn-Remapped-content-length" : "913", + "x-bt-internal-trace-id" : "697308ca000000002493f4d436c47279", + "x-amz-apigw-id" : "Xn5PpExmIAMElFA=", + "vary" : "Origin, Accept-Encoding", + "etag" : "W/\"391-e2sQ3WLqbgDUML3AlteZzSuvEIE\"", + "access-control-expose-headers" : "x-bt-cursor,x-bt-found-existing,x-bt-query-plan", + "X-Amzn-Trace-Id" : "Root=1-697308ca-5fd10a1b6da42bcc783c927e;Parent=3e4f51cc37168d91;Sampled=0;Lineage=1:24be3d11:0", + "Via" : "1.1 0c387c49a68a0638a07ea76c5853a28e.cloudfront.net (CloudFront), 1.1 dbfd9bcc806d4c322e72b461b2458112.cloudfront.net (CloudFront)", + "X-Cache" : "Miss from cloudfront", + "X-Amz-Cf-Id" : "M9QwhiYeDHthLfHd0s3JH-VI1WMdF0La0k4wOdV9QD9t7oV7534xNg==" + } + }, + "uuid" : "32c90375-557c-4ef8-9263-383a8386366a", + "persistent" : true, + "insertionIndex" : 12 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function-a40fed81-8f60-44a2-819c-afa1c6941fe1.json b/src/test/resources/cassettes/braintrust/mappings/v1_function-a40fed81-8f60-44a2-819c-afa1c6941fe1.json new file mode 100644 index 0000000..b76eda1 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function-a40fed81-8f60-44a2-819c-afa1c6941fe1.json @@ -0,0 +1,32 @@ +{ + "id" : "a40fed81-8f60-44a2-819c-afa1c6941fe1", + "name" : "v1_function", + "request" : { + "url" : "/v1/function?slug=close-enough-judge-d31b&project_name=java-unit-test", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function-a40fed81-8f60-44a2-819c-afa1c6941fe1.json", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "X-Amz-Cf-Pop" : [ "SEA900-P2", "SEA900-P10" ], + "Date" : "Fri, 23 Jan 2026 05:36:09 GMT", + "access-control-allow-credentials" : "true", + "x-amzn-RequestId" : "832d2cf0-bb5e-4fc9-bb74-9dc9c0c84b88", + "x-amzn-Remapped-content-length" : "970", + "x-bt-internal-trace-id" : "697308c9000000002bc838a48543b28d", + "x-amz-apigw-id" : "Xn5PjHfBIAMEOGQ=", + "vary" : "Origin, Accept-Encoding", + "etag" : "W/\"3ca-U7pLamkO1Ncw3QGMXz5hnQvgnvk\"", + "access-control-expose-headers" : "x-bt-cursor,x-bt-found-existing,x-bt-query-plan", + "X-Amzn-Trace-Id" : "Root=1-697308c9-1459d1e630f5ca357924cbc9;Parent=2a38a45e854d66b2;Sampled=0;Lineage=1:24be3d11:0", + "Via" : "1.1 aae9b7724185120bb8d23ea2cb4efe0c.cloudfront.net (CloudFront), 1.1 da32b45f2cc22dc818960285c4e91b66.cloudfront.net (CloudFront)", + "X-Cache" : "Miss from cloudfront", + "X-Amz-Cf-Id" : "dzrdY7PjXY0KMewRRMFmwZC4voyAuGScJqUsv_-UB1G9EYr73afWmg==" + } + }, + "uuid" : "a40fed81-8f60-44a2-819c-afa1c6941fe1", + "persistent" : true, + "insertionIndex" : 10 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function-ce46228b-9279-4285-9695-5d3ab0ebb857.json b/src/test/resources/cassettes/braintrust/mappings/v1_function-ce46228b-9279-4285-9695-5d3ab0ebb857.json new file mode 100644 index 0000000..cc0859f --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function-ce46228b-9279-4285-9695-5d3ab0ebb857.json @@ -0,0 +1,34 @@ +{ + "id" : "ce46228b-9279-4285-9695-5d3ab0ebb857", + "name" : "v1_function", + "request" : { + "url" : "/v1/function?slug=typescriptexactmatch-9e44&project_name=java-unit-test", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function-ce46228b-9279-4285-9695-5d3ab0ebb857.json", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "X-Amz-Cf-Pop" : [ "SEA900-P2", "SEA900-P10" ], + "Date" : "Fri, 23 Jan 2026 05:36:08 GMT", + "access-control-allow-credentials" : "true", + "x-amzn-RequestId" : "ee2d4de1-4414-4c32-af31-cfb92621a650", + "x-amzn-Remapped-content-length" : "913", + "x-bt-internal-trace-id" : "697308c800000000320afa6b6d4db01a", + "x-amz-apigw-id" : "Xn5PZF7boAMETbw=", + "vary" : "Origin, Accept-Encoding", + "etag" : "W/\"391-iVW1u69I859gEDeC7XzXAO2kbf4\"", + "access-control-expose-headers" : "x-bt-cursor,x-bt-found-existing,x-bt-query-plan", + "X-Amzn-Trace-Id" : "Root=1-697308c8-64ccd42e3bd115736333a8c8;Parent=28a1de37d9e32ef7;Sampled=0;Lineage=1:24be3d11:0", + "Via" : "1.1 b542d7019a03585dbf3c5588bc1da03a.cloudfront.net (CloudFront), 1.1 e6b2537b87653726af8a79e6da505188.cloudfront.net (CloudFront)", + "X-Cache" : "Miss from cloudfront", + "X-Amz-Cf-Id" : "8f6jXBYwrKBtvL6dlOwjelCeJlyQQbVkStpx2LFr5-x7K0mLslSvJQ==" + } + }, + "uuid" : "ce46228b-9279-4285-9695-5d3ab0ebb857", + "persistent" : true, + "scenarioName" : "scenario-2-v1-function", + "requiredScenarioState" : "scenario-2-v1-function-2", + "insertionIndex" : 8 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function-ec6186f4-a80d-4a8c-81fb-ad828ba038b8.json b/src/test/resources/cassettes/braintrust/mappings/v1_function-ec6186f4-a80d-4a8c-81fb-ad828ba038b8.json new file mode 100644 index 0000000..6f4c2c5 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function-ec6186f4-a80d-4a8c-81fb-ad828ba038b8.json @@ -0,0 +1,35 @@ +{ + "id" : "ec6186f4-a80d-4a8c-81fb-ad828ba038b8", + "name" : "v1_function", + "request" : { + "url" : "/v1/function?slug=typescriptexactmatch-9e44&project_name=java-unit-test", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function-ec6186f4-a80d-4a8c-81fb-ad828ba038b8.json", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "X-Amz-Cf-Pop" : [ "SEA900-P2", "SEA900-P10" ], + "Date" : "Fri, 23 Jan 2026 05:36:07 GMT", + "access-control-allow-credentials" : "true", + "x-amzn-RequestId" : "7ae431e0-39e6-4e0a-a5ed-16c7c8e33268", + "x-amzn-Remapped-content-length" : "913", + "x-bt-internal-trace-id" : "697308c70000000000288f5631c3d9db", + "x-amz-apigw-id" : "Xn5PREqQoAMEC6g=", + "vary" : "Origin, Accept-Encoding", + "etag" : "W/\"391-iVW1u69I859gEDeC7XzXAO2kbf4\"", + "access-control-expose-headers" : "x-bt-cursor,x-bt-found-existing,x-bt-query-plan", + "X-Amzn-Trace-Id" : "Root=1-697308c7-761424a613fddcae70423c3f;Parent=21d49aded2c44837;Sampled=0;Lineage=1:24be3d11:0", + "Via" : "1.1 09a1a54fdbf13d19927a903f4a7d5186.cloudfront.net (CloudFront), 1.1 ffe9646b2ea911744e2d51fc0715cedc.cloudfront.net (CloudFront)", + "X-Cache" : "Miss from cloudfront", + "X-Amz-Cf-Id" : "dKsTT6vOCgxlJsdHHKpOY_yKKxMe8TwWJ4IodLwzpp4ZJDJdW_JJuQ==" + } + }, + "uuid" : "ec6186f4-a80d-4a8c-81fb-ad828ba038b8", + "persistent" : true, + "scenarioName" : "scenario-2-v1-function", + "requiredScenarioState" : "Started", + "newScenarioState" : "scenario-2-v1-function-2", + "insertionIndex" : 6 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke-c67cd10d-7d05-4e66-a861-360e14dace92.json b/src/test/resources/cassettes/braintrust/mappings/v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke-c67cd10d-7d05-4e66-a861-360e14dace92.json new file mode 100644 index 0000000..b34699b --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke-c67cd10d-7d05-4e66-a861-360e14dace92.json @@ -0,0 +1,59 @@ +{ + "id" : "c67cd10d-7d05-4e66-a861-360e14dace92", + "name" : "v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke", + "request" : { + "url" : "/v1/function/5dd8a26d-3be8-4ecd-af5b-df2a6a592277/invoke", + "method" : "POST", + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + }, + "bodyPatterns" : [ { + "equalToJson" : "{\"input\":{\"input\":\"What is 2+2?\",\"output\":\"four\",\"expected\":\"4\",\"metadata\":{}}}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : false + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function_5dd8a26d-3be8-4ecd-af5b-df2a6a592277_invoke-c67cd10d-7d05-4e66-a861-360e14dace92.json", + "headers" : { + "Content-Type" : "application/json", + "Date" : "Fri, 23 Jan 2026 05:36:09 GMT", + "x-bt-used-endpoint" : "OPENAI_API_KEY", + "x-amzn-RequestId" : "46e7b13d-1760-4604-b100-59262ce4a2c5", + "cf-ray" : "9c2301680d22b4ba-IAD", + "x-ratelimit-remaining-requests" : "29999", + "openai-processing-ms" : "7267", + "cf-cache-status" : "DYNAMIC", + "strict-transport-security" : "max-age=31536000; includeSubDomains; preload", + "x-bt-cached" : "HIT", + "x-openai-proxy-wasm" : "v0.1", + "x-ratelimit-reset-requests" : "2ms", + "x-envoy-upstream-service-time" : "7402", + "openai-organization" : "braintrust-data", + "x-request-id" : "req_e0eec56f2a354da0ad52cca9f8fb1aa5", + "set-cookie" : "_cfuvid=Wfvzi.WlyOXcpGIt_lOzuCyfYKl5KGyA9p61vfKm8.g-1769126378024-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", + "cache-control" : "max-age=604800", + "x-ratelimit-limit-tokens" : "180000000", + "x-content-type-options" : "nosniff", + "x-ratelimit-remaining-tokens" : "179999943", + "x-bt-function-creds-cached" : "HIT", + "X-Amzn-Trace-Id" : "Root=1-697308c9-62f383997faeb1d40789390a;Parent=1d54fe41e5502c92;Sampled=0;Lineage=1:8be8f50d:0", + "x-bt-function-meta-cached" : "HIT", + "x-ratelimit-reset-tokens" : "0s", + "openai-version" : "2020-10-01", + "x-ratelimit-limit-requests" : "30000", + "openai-project" : "proj_wMRY6YpEiASXMxPIIcI9nQRi", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 b669d9add7767f73665f1f8b7e8cd802.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "SEA900-P10", + "X-Amz-Cf-Id" : "AZnkOMP4ybJrHTO6ilSDGF-yLqzLAFHC4WWqa1pUwW2FR8NTnNxhxw==", + "Age" : "20189" + } + }, + "uuid" : "c67cd10d-7d05-4e66-a861-360e14dace92", + "persistent" : true, + "insertionIndex" : 11 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-69d15cfb-87c7-41a1-a99a-df653a67ec0a.json b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-69d15cfb-87c7-41a1-a99a-df653a67ec0a.json new file mode 100644 index 0000000..43c6112 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-69d15cfb-87c7-41a1-a99a-df653a67ec0a.json @@ -0,0 +1,37 @@ +{ + "id" : "69d15cfb-87c7-41a1-a99a-df653a67ec0a", + "name" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke", + "request" : { + "url" : "/v1/function/efa5f9c3-6ece-4726-a9d6-4ba792980b3f/invoke", + "method" : "POST", + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + }, + "bodyPatterns" : [ { + "equalToJson" : "{\"input\":{\"input\":\"carrot\",\"output\":\"java-fruit\",\"expected\":\"vegetable\",\"metadata\":{}}}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : false + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-69d15cfb-87c7-41a1-a99a-df653a67ec0a.json", + "headers" : { + "Content-Type" : "application/json", + "Date" : "Fri, 23 Jan 2026 05:36:07 GMT", + "X-Amzn-Trace-Id" : "Root=1-697308c7-7b5db7f70a2cde7f55dc3d37;Parent=5b5163543a97cc2d;Sampled=0;Lineage=1:8be8f50d:0", + "x-bt-function-meta-cached" : "HIT", + "x-amzn-RequestId" : "d90b3df5-3157-4e1c-bce5-b948f859eb72", + "x-bt-function-creds-cached" : "HIT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 f8731007efc5ab360d90cee573a1e916.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "SEA900-P10", + "X-Amz-Cf-Id" : "FHz-qaF3V-vDYJN3ao-7VkQT5W2lIdgx25TbM_-yo6J0XE_b1jHF2w==" + } + }, + "uuid" : "69d15cfb-87c7-41a1-a99a-df653a67ec0a", + "persistent" : true, + "insertionIndex" : 5 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a5548413-7bea-4a3b-8dd6-575e0594ca85.json b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a5548413-7bea-4a3b-8dd6-575e0594ca85.json new file mode 100644 index 0000000..1dd1140 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a5548413-7bea-4a3b-8dd6-575e0594ca85.json @@ -0,0 +1,37 @@ +{ + "id" : "a5548413-7bea-4a3b-8dd6-575e0594ca85", + "name" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke", + "request" : { + "url" : "/v1/function/efa5f9c3-6ece-4726-a9d6-4ba792980b3f/invoke", + "method" : "POST", + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + }, + "bodyPatterns" : [ { + "equalToJson" : "{\"input\":{\"input\":\"test input\",\"output\":\"hello world\",\"expected\":\"hello world\",\"metadata\":{}}}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : false + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a5548413-7bea-4a3b-8dd6-575e0594ca85.json", + "headers" : { + "Content-Type" : "application/json", + "Date" : "Fri, 23 Jan 2026 05:36:09 GMT", + "X-Amzn-Trace-Id" : "Root=1-697308c9-400c4ada2b683df94fee5133;Parent=4fe28c055c2e986f;Sampled=0;Lineage=1:8be8f50d:0", + "x-bt-function-meta-cached" : "HIT", + "x-amzn-RequestId" : "9cb21a50-2965-4088-ac88-c8154e5a5a2a", + "x-bt-function-creds-cached" : "HIT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 d525041695bdb6325f78ebba5c11b8a2.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "SEA900-P10", + "X-Amz-Cf-Id" : "hs6E1WkHWi7zdZ6-9xXQ98nPuCZQpFYWMq6voCMmz83LmAuoI1CfdA==" + } + }, + "uuid" : "a5548413-7bea-4a3b-8dd6-575e0594ca85", + "persistent" : true, + "insertionIndex" : 9 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a599c78a-4aec-4bf0-b64e-e9b183b45c57.json b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a599c78a-4aec-4bf0-b64e-e9b183b45c57.json new file mode 100644 index 0000000..be03d5b --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a599c78a-4aec-4bf0-b64e-e9b183b45c57.json @@ -0,0 +1,37 @@ +{ + "id" : "a599c78a-4aec-4bf0-b64e-e9b183b45c57", + "name" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke", + "request" : { + "url" : "/v1/function/efa5f9c3-6ece-4726-a9d6-4ba792980b3f/invoke", + "method" : "POST", + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + }, + "bodyPatterns" : [ { + "equalToJson" : "{\"input\":{\"input\":\"apple\",\"output\":\"java-fruit\",\"expected\":\"fruit\",\"metadata\":{}}}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : false + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-a599c78a-4aec-4bf0-b64e-e9b183b45c57.json", + "headers" : { + "Content-Type" : "application/json", + "Date" : "Fri, 23 Jan 2026 05:36:06 GMT", + "X-Amzn-Trace-Id" : "Root=1-697308c6-2d7a2814638a885665cbbf0c;Parent=6f2db2d141c9837d;Sampled=0;Lineage=1:8be8f50d:0", + "x-bt-function-meta-cached" : "HIT", + "x-amzn-RequestId" : "2619c576-7c27-4ea8-8bb3-2594fb9c5231", + "x-bt-function-creds-cached" : "HIT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 b669d9add7767f73665f1f8b7e8cd802.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "SEA900-P10", + "X-Amz-Cf-Id" : "cVWp2u0rg5QNTANlInRhVfQ3qWVDRPObVqv9u2JzyKdANNMfE-8uXg==" + } + }, + "uuid" : "a599c78a-4aec-4bf0-b64e-e9b183b45c57", + "persistent" : true, + "insertionIndex" : 4 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-d2eef3e5-3348-4b4d-8ea5-08d7da45f22b.json b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-d2eef3e5-3348-4b4d-8ea5-08d7da45f22b.json new file mode 100644 index 0000000..b64fba6 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-d2eef3e5-3348-4b4d-8ea5-08d7da45f22b.json @@ -0,0 +1,37 @@ +{ + "id" : "d2eef3e5-3348-4b4d-8ea5-08d7da45f22b", + "name" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke", + "request" : { + "url" : "/v1/function/efa5f9c3-6ece-4726-a9d6-4ba792980b3f/invoke", + "method" : "POST", + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + }, + "bodyPatterns" : [ { + "equalToJson" : "{\"input\":{\"input\":\"test input\",\"output\":\"hello world\",\"expected\":\"hello world\",\"metadata\":{}},\"version\":\"485dbf64e486ab3a\"}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : false + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-d2eef3e5-3348-4b4d-8ea5-08d7da45f22b.json", + "headers" : { + "Content-Type" : "application/json", + "Date" : "Fri, 23 Jan 2026 05:36:10 GMT", + "X-Amzn-Trace-Id" : "Root=1-697308ca-0550de0169605a955c9369ea;Parent=2824946edb1ecfdd;Sampled=0;Lineage=1:8be8f50d:0", + "x-bt-function-meta-cached" : "HIT", + "x-amzn-RequestId" : "d3c5512b-29ee-4e90-bd0d-5d499be5b39a", + "x-bt-function-creds-cached" : "HIT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 74e8c76139b8c7f9b11d5e4441c2a7a2.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "SEA900-P10", + "X-Amz-Cf-Id" : "QEYzusVCnA1b_6QzcDlZyjIYx0Iw18uK-fwBXeS5PNh2UWuUDAMO4g==" + } + }, + "uuid" : "d2eef3e5-3348-4b4d-8ea5-08d7da45f22b", + "persistent" : true, + "insertionIndex" : 13 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0.json b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0.json new file mode 100644 index 0000000..e1ab6c3 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0.json @@ -0,0 +1,37 @@ +{ + "id" : "f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0", + "name" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke", + "request" : { + "url" : "/v1/function/efa5f9c3-6ece-4726-a9d6-4ba792980b3f/invoke", + "method" : "POST", + "headers" : { + "Content-Type" : { + "equalTo" : "application/json" + } + }, + "bodyPatterns" : [ { + "equalToJson" : "{\"input\":{\"input\":\"test input\",\"output\":\"different\",\"expected\":\"expected\",\"metadata\":{}}}", + "ignoreArrayOrder" : true, + "ignoreExtraElements" : false + } ] + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_function_efa5f9c3-6ece-4726-a9d6-4ba792980b3f_invoke-f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0.json", + "headers" : { + "Content-Type" : "application/json", + "Date" : "Fri, 23 Jan 2026 05:36:08 GMT", + "X-Amzn-Trace-Id" : "Root=1-697308c8-1a7890d73a86eb5a551f2af3;Parent=033072685970f626;Sampled=0;Lineage=1:8be8f50d:0", + "x-bt-function-meta-cached" : "HIT", + "x-amzn-RequestId" : "e46331ea-d1a9-4811-8bdd-3d3e571222f8", + "x-bt-function-creds-cached" : "HIT", + "X-Cache" : "Miss from cloudfront", + "Via" : "1.1 87247d9a9b2f9e51b0c72b364948aefa.cloudfront.net (CloudFront)", + "X-Amz-Cf-Pop" : "SEA900-P10", + "X-Amz-Cf-Id" : "rLbEuM9iZpq8FYhFPaxiPDahQm5k7Pt3NohzD5oLJzunIX4P2U715Q==" + } + }, + "uuid" : "f8a0cc9c-fc09-4c58-bb90-f7e3c4de7cc0", + "persistent" : true, + "insertionIndex" : 7 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/braintrust/mappings/v1_project_6ae68365-7620-4630-921b-bac416634fc8-3e4fd9ae-b027-4c4e-a714-5ba10db840dd.json b/src/test/resources/cassettes/braintrust/mappings/v1_project_6ae68365-7620-4630-921b-bac416634fc8-3e4fd9ae-b027-4c4e-a714-5ba10db840dd.json new file mode 100644 index 0000000..11d9cf0 --- /dev/null +++ b/src/test/resources/cassettes/braintrust/mappings/v1_project_6ae68365-7620-4630-921b-bac416634fc8-3e4fd9ae-b027-4c4e-a714-5ba10db840dd.json @@ -0,0 +1,32 @@ +{ + "id" : "3e4fd9ae-b027-4c4e-a714-5ba10db840dd", + "name" : "v1_project_6ae68365-7620-4630-921b-bac416634fc8", + "request" : { + "url" : "/v1/project/6ae68365-7620-4630-921b-bac416634fc8", + "method" : "GET" + }, + "response" : { + "status" : 200, + "bodyFileName" : "v1_project_6ae68365-7620-4630-921b-bac416634fc8-3e4fd9ae-b027-4c4e-a714-5ba10db840dd.json", + "headers" : { + "Content-Type" : "application/json; charset=utf-8", + "X-Amz-Cf-Pop" : [ "SEA900-P2", "SEA900-P10" ], + "Date" : "Fri, 23 Jan 2026 05:36:05 GMT", + "access-control-allow-credentials" : "true", + "x-amzn-RequestId" : "715743ae-8eb9-4235-a28c-fbf74121ac58", + "x-amzn-Remapped-content-length" : "352", + "x-bt-internal-trace-id" : "697308c5000000000f234b78b61b494f", + "x-amz-apigw-id" : "Xn5O-Hi-IAMEsWQ=", + "vary" : "Origin, Accept-Encoding", + "etag" : "W/\"160-RUdtZR434qrXxI13debDoTK1a2s\"", + "access-control-expose-headers" : "x-bt-cursor,x-bt-found-existing,x-bt-query-plan", + "X-Amzn-Trace-Id" : "Root=1-697308c5-10ae7bd9215b2deb252c8ca1;Parent=77f8a8291cf951c4;Sampled=0;Lineage=1:24be3d11:0", + "Via" : "1.1 09a1a54fdbf13d19927a903f4a7d5186.cloudfront.net (CloudFront), 1.1 b669d9add7767f73665f1f8b7e8cd802.cloudfront.net (CloudFront)", + "X-Cache" : "Miss from cloudfront", + "X-Amz-Cf-Id" : "cbuFSM1PKiaoTqAB6KkzUk86-V4shoX_eS44HzxuiTLJPlnKWOXang==" + } + }, + "uuid" : "3e4fd9ae-b027-4c4e-a714-5ba10db840dd", + "persistent" : true, + "insertionIndex" : 2 +} \ No newline at end of file diff --git a/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-f1d892bb-4783-475f-9320-1a5800d4f293.json b/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07.json similarity index 87% rename from src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-f1d892bb-4783-475f-9320-1a5800d4f293.json rename to src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07.json index 8a7e387..d729731 100644 --- a/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-f1d892bb-4783-475f-9320-1a5800d4f293.json +++ b/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07.json @@ -10,7 +10,7 @@ "role": "model" }, "finishReason": "STOP", - "avgLogprobs": -0.050756204873323441 + "avgLogprobs": -0.054626878350973129 } ], "usageMetadata": { @@ -31,5 +31,5 @@ ] }, "modelVersion": "gemini-2.0-flash-lite", - "responseId": "LZVmadvKL7O6mtkPxbjguAg" + "responseId": "zQhzabOJBLuhz7IPvoi5sAc" } diff --git a/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-47e92aee-8b68-4d7a-bd8a-f07c4ce231c2.json b/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-bbe54c72-90d7-4770-ac32-ee2b1bdcfc27.json similarity index 87% rename from src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-47e92aee-8b68-4d7a-bd8a-f07c4ce231c2.json rename to src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-bbe54c72-90d7-4770-ac32-ee2b1bdcfc27.json index 40afc75..6bf76ee 100644 --- a/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-47e92aee-8b68-4d7a-bd8a-f07c4ce231c2.json +++ b/src/test/resources/cassettes/google/__files/v1beta_models_gemini-20-flash-litegeneratecontent-bbe54c72-90d7-4770-ac32-ee2b1bdcfc27.json @@ -10,7 +10,7 @@ "role": "model" }, "finishReason": "STOP", - "avgLogprobs": -0.0095387622714042664 + "avgLogprobs": -0.054125471247567072 } ], "usageMetadata": { @@ -31,5 +31,5 @@ ] }, "modelVersion": "gemini-2.0-flash-lite", - "responseId": "L5VmaYSPFpmdz7IP2PWQmAc" + "responseId": "zghzaffCAr2dz7IPjd6imQM" } diff --git a/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-f1d892bb-4783-475f-9320-1a5800d4f293.json b/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07.json similarity index 81% rename from src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-f1d892bb-4783-475f-9320-1a5800d4f293.json rename to src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07.json index 4c5b0c8..62e8fca 100644 --- a/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-f1d892bb-4783-475f-9320-1a5800d4f293.json +++ b/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07.json @@ -1,5 +1,5 @@ { - "id" : "f1d892bb-4783-475f-9320-1a5800d4f293", + "id" : "a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07", "name" : "v1beta_models_gemini-20-flash-litegeneratecontent", "request" : { "url" : "/v1beta/models/gemini-2.0-flash-lite:generateContent", @@ -17,20 +17,20 @@ }, "response" : { "status" : 200, - "bodyFileName" : "v1beta_models_gemini-20-flash-litegeneratecontent-f1d892bb-4783-475f-9320-1a5800d4f293.json", + "bodyFileName" : "v1beta_models_gemini-20-flash-litegeneratecontent-a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07.json", "headers" : { "Content-Type" : "application/json; charset=UTF-8", "Vary" : [ "Origin", "X-Origin", "Referer" ], - "Date" : "Tue, 13 Jan 2026 18:55:43 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:13 GMT", "Server" : "scaffolding on HTTPServer2", "X-XSS-Protection" : "0", "X-Frame-Options" : "SAMEORIGIN", "X-Content-Type-Options" : "nosniff", - "Server-Timing" : "gfet4t7; dur=1344", + "Server-Timing" : "gfet4t7; dur=380", "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" } }, - "uuid" : "f1d892bb-4783-475f-9320-1a5800d4f293", + "uuid" : "a96a8ad5-2ea3-4afe-a2cb-409bf8e0bf07", "persistent" : true, "insertionIndex" : 1 } \ No newline at end of file diff --git a/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-47e92aee-8b68-4d7a-bd8a-f07c4ce231c2.json b/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-bbe54c72-90d7-4770-ac32-ee2b1bdcfc27.json similarity index 81% rename from src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-47e92aee-8b68-4d7a-bd8a-f07c4ce231c2.json rename to src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-bbe54c72-90d7-4770-ac32-ee2b1bdcfc27.json index 60e578d..01f0437 100644 --- a/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-47e92aee-8b68-4d7a-bd8a-f07c4ce231c2.json +++ b/src/test/resources/cassettes/google/mappings/v1beta_models_gemini-20-flash-litegeneratecontent-bbe54c72-90d7-4770-ac32-ee2b1bdcfc27.json @@ -1,5 +1,5 @@ { - "id" : "47e92aee-8b68-4d7a-bd8a-f07c4ce231c2", + "id" : "bbe54c72-90d7-4770-ac32-ee2b1bdcfc27", "name" : "v1beta_models_gemini-20-flash-litegeneratecontent", "request" : { "url" : "/v1beta/models/gemini-2.0-flash-lite:generateContent", @@ -17,20 +17,20 @@ }, "response" : { "status" : 200, - "bodyFileName" : "v1beta_models_gemini-20-flash-litegeneratecontent-47e92aee-8b68-4d7a-bd8a-f07c4ce231c2.json", + "bodyFileName" : "v1beta_models_gemini-20-flash-litegeneratecontent-bbe54c72-90d7-4770-ac32-ee2b1bdcfc27.json", "headers" : { "Content-Type" : "application/json; charset=UTF-8", "Vary" : [ "Origin", "X-Origin", "Referer" ], - "Date" : "Tue, 13 Jan 2026 18:55:43 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:14 GMT", "Server" : "scaffolding on HTTPServer2", "X-XSS-Protection" : "0", "X-Frame-Options" : "SAMEORIGIN", "X-Content-Type-Options" : "nosniff", - "Server-Timing" : "gfet4t7; dur=490", + "Server-Timing" : "gfet4t7; dur=340", "Alt-Svc" : "h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000" } }, - "uuid" : "47e92aee-8b68-4d7a-bd8a-f07c4ce231c2", + "uuid" : "bbe54c72-90d7-4770-ac32-ee2b1bdcfc27", "persistent" : true, "insertionIndex" : 2 } \ No newline at end of file diff --git a/src/test/resources/cassettes/openai/__files/chat_completions-01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4.txt b/src/test/resources/cassettes/openai/__files/chat_completions-01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4.txt new file mode 100644 index 0000000..e6803a4 --- /dev/null +++ b/src/test/resources/cassettes/openai/__files/chat_completions-01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4.txt @@ -0,0 +1,22 @@ +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"jS8NXSKZe"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Js3TqhAu"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Jz2"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AnZ1V706"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"content":" France"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"TS27"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"TIi3l6Qr"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"content":" Paris"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"wW2vg"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"lcsLFUWS2L"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"aayiy"} + +data: {"id":"chatcmpl-D1487Z7gvsZSSqonER8ySm2HNyhKC","object":"chat.completion.chunk","created":1769146575,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_c4585b5b9c","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":7,"total_tokens":21,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"5caYMQAl22o"} + +data: [DONE] + diff --git a/src/test/resources/cassettes/openai/__files/chat_completions-541b9306-6880-41df-bb44-69ea82f417f8.txt b/src/test/resources/cassettes/openai/__files/chat_completions-0663776c-5dfc-4fb8-9904-6a415fca9550.txt similarity index 50% rename from src/test/resources/cassettes/openai/__files/chat_completions-541b9306-6880-41df-bb44-69ea82f417f8.txt rename to src/test/resources/cassettes/openai/__files/chat_completions-0663776c-5dfc-4fb8-9904-6a415fca9550.txt index 32380a2..f3ec1f6 100644 --- a/src/test/resources/cassettes/openai/__files/chat_completions-541b9306-6880-41df-bb44-69ea82f417f8.txt +++ b/src/test/resources/cassettes/openai/__files/chat_completions-0663776c-5dfc-4fb8-9904-6a415fca9550.txt @@ -1,22 +1,22 @@ -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"i0DEsxe0d"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"QMPctD3MA"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"f9lVHuPX"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ZDH7bwkm"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"LJo"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"6EM"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"4Ush7Uov"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"ck78DMdc"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" France"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"w29M"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" France"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"W7tM"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"oGMJtqrv"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Gf9inOSg"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" Paris"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"oulmu"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" Paris"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"sf7Cx"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"Q7jCyF6KeB"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"PDZSCfW8AE"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"Hy3sv"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"EqWDj"} -data: {"id":"chatcmpl-CxdqO7Gy8Lx4oY0QTk40yFak8aXlQ","object":"chat.completion.chunk","created":1768330548,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[],"usage":{"prompt_tokens":23,"completion_tokens":7,"total_tokens":30,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"8yIDij1lDhf"} +data: {"id":"chatcmpl-D148CFcgUHTYaD1ZdHpPl9Tk0yUE2","object":"chat.completion.chunk","created":1769146580,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[],"usage":{"prompt_tokens":23,"completion_tokens":7,"total_tokens":30,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"7DzqE0n111t"} data: [DONE] diff --git a/src/test/resources/cassettes/openai/__files/chat_completions-13e69bf3-e90d-497f-bb66-47b448dc86bd.json b/src/test/resources/cassettes/openai/__files/chat_completions-2446f774-b17e-4abf-811c-6a10681dbf94.json similarity index 86% rename from src/test/resources/cassettes/openai/__files/chat_completions-13e69bf3-e90d-497f-bb66-47b448dc86bd.json rename to src/test/resources/cassettes/openai/__files/chat_completions-2446f774-b17e-4abf-811c-6a10681dbf94.json index 1c89a82..16156c7 100644 --- a/src/test/resources/cassettes/openai/__files/chat_completions-13e69bf3-e90d-497f-bb66-47b448dc86bd.json +++ b/src/test/resources/cassettes/openai/__files/chat_completions-2446f774-b17e-4abf-811c-6a10681dbf94.json @@ -1,7 +1,7 @@ { - "id": "chatcmpl-CxdqPl3mdEMREYg8YcrJrp2tmqRTJ", + "id": "chatcmpl-D148DkNpJKm9pxT5Afq9fO3Tw0pos", "object": "chat.completion", - "created": 1768330549, + "created": 1769146581, "model": "gpt-4o-mini-2024-07-18", "choices": [ { @@ -32,5 +32,5 @@ } }, "service_tier": "default", - "system_fingerprint": "fp_c4585b5b9c" + "system_fingerprint": "fp_29330a9688" } diff --git a/src/test/resources/cassettes/openai/__files/chat_completions-d6eda521-a7dd-4216-911e-4a916e45fca1.json b/src/test/resources/cassettes/openai/__files/chat_completions-80cbaecd-6aec-4d80-a719-c572a45d9d82.json similarity index 70% rename from src/test/resources/cassettes/openai/__files/chat_completions-d6eda521-a7dd-4216-911e-4a916e45fca1.json rename to src/test/resources/cassettes/openai/__files/chat_completions-80cbaecd-6aec-4d80-a719-c572a45d9d82.json index 148499c..f28c5f6 100644 --- a/src/test/resources/cassettes/openai/__files/chat_completions-d6eda521-a7dd-4216-911e-4a916e45fca1.json +++ b/src/test/resources/cassettes/openai/__files/chat_completions-80cbaecd-6aec-4d80-a719-c572a45d9d82.json @@ -1,14 +1,14 @@ { - "id": "chatcmpl-CxdqMP3DDF5hyKQhHBRTrBCTtd0BE", + "id": "chatcmpl-D148A41Uq23LlobqzgVVmSYJM9bL6", "object": "chat.completion", - "created": 1768330546, + "created": 1769146578, "model": "gpt-4o-mini-2024-07-18", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "The current temperature is the same in both Paris and New York, at 72°F, and both cities are experiencing sunny weather.", + "content": "The current temperature in both Paris and New York is 72°F, so it is equally hot in both cities right now.", "refusal": null, "annotations": [] }, @@ -18,8 +18,8 @@ ], "usage": { "prompt_tokens": 177, - "completion_tokens": 27, - "total_tokens": 204, + "completion_tokens": 26, + "total_tokens": 203, "prompt_tokens_details": { "cached_tokens": 0, "audio_tokens": 0 diff --git a/src/test/resources/cassettes/openai/__files/chat_completions-9016066f-1bbd-49ec-a403-4311b29fe2af.txt b/src/test/resources/cassettes/openai/__files/chat_completions-9016066f-1bbd-49ec-a403-4311b29fe2af.txt deleted file mode 100644 index 40cd446..0000000 --- a/src/test/resources/cassettes/openai/__files/chat_completions-9016066f-1bbd-49ec-a403-4311b29fe2af.txt +++ /dev/null @@ -1,22 +0,0 @@ -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"zhm3yj623"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":"The"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"AhGJkfcK"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" capital"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"5JD"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" of"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"9E3BimxD"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" France"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"I0ue"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" is"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"zhZ6meZ6"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":" Paris"},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"RtrPa"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null,"obfuscation":"0SIqKB167Z"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null,"obfuscation":"wtORO"} - -data: {"id":"chatcmpl-CxdqKsEuotLHjXL9yh6KxVGkZFeSq","object":"chat.completion.chunk","created":1768330544,"model":"gpt-4o-mini-2024-07-18","service_tier":"default","system_fingerprint":"fp_29330a9688","choices":[],"usage":{"prompt_tokens":14,"completion_tokens":7,"total_tokens":21,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}},"obfuscation":"yNAPCwHY0Pf"} - -data: [DONE] - diff --git a/src/test/resources/cassettes/openai/__files/chat_completions-d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b.json b/src/test/resources/cassettes/openai/__files/chat_completions-c881e7aa-85d0-4de6-92ec-da5dbed72027.json similarity index 86% rename from src/test/resources/cassettes/openai/__files/chat_completions-d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b.json rename to src/test/resources/cassettes/openai/__files/chat_completions-c881e7aa-85d0-4de6-92ec-da5dbed72027.json index e9fca78..1f97dcf 100644 --- a/src/test/resources/cassettes/openai/__files/chat_completions-d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b.json +++ b/src/test/resources/cassettes/openai/__files/chat_completions-c881e7aa-85d0-4de6-92ec-da5dbed72027.json @@ -1,7 +1,7 @@ { - "id": "chatcmpl-CxdqLHL6kBkvILQ1S5QOt1qeWw3b5", + "id": "chatcmpl-D1488Eu9ZBPsuAAbqFou0rdKplKq6", "object": "chat.completion", - "created": 1768330545, + "created": 1769146576, "model": "gpt-4o-mini-2024-07-18", "choices": [ { @@ -11,7 +11,7 @@ "content": null, "tool_calls": [ { - "id": "call_O0vPA17lB2sSTvNui5KkMr7E", + "id": "call_YANyIGtCtTJowRa5Emu3rNQp", "type": "function", "function": { "name": "getWeather", @@ -19,7 +19,7 @@ } }, { - "id": "call_gVz1cYvAomaPcTrheam72S8H", + "id": "call_qTfvi4D3JjATbUYoQd84Hbnf", "type": "function", "function": { "name": "getWeather", diff --git a/src/test/resources/cassettes/openai/__files/chat_completions-4706df90-aa52-4bd3-97c0-3448a4e0005f.json b/src/test/resources/cassettes/openai/__files/chat_completions-d0bef16c-03e9-44da-ab7e-a72110eb2028.json similarity index 91% rename from src/test/resources/cassettes/openai/__files/chat_completions-4706df90-aa52-4bd3-97c0-3448a4e0005f.json rename to src/test/resources/cassettes/openai/__files/chat_completions-d0bef16c-03e9-44da-ab7e-a72110eb2028.json index 1cc12fa..9228df0 100644 --- a/src/test/resources/cassettes/openai/__files/chat_completions-4706df90-aa52-4bd3-97c0-3448a4e0005f.json +++ b/src/test/resources/cassettes/openai/__files/chat_completions-d0bef16c-03e9-44da-ab7e-a72110eb2028.json @@ -1,7 +1,7 @@ { - "id": "chatcmpl-CxdqNcT26jSeKfx2QAWkScbokWofM", + "id": "chatcmpl-D148BsNl5OIpVvrqmECQ4fLoxjJUv", "object": "chat.completion", - "created": 1768330547, + "created": 1769146579, "model": "gpt-4o-mini-2024-07-18", "choices": [ { diff --git a/src/test/resources/cassettes/openai/mappings/chat_completions-9016066f-1bbd-49ec-a403-4311b29fe2af.json b/src/test/resources/cassettes/openai/mappings/chat_completions-01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4.json similarity index 66% rename from src/test/resources/cassettes/openai/mappings/chat_completions-9016066f-1bbd-49ec-a403-4311b29fe2af.json rename to src/test/resources/cassettes/openai/mappings/chat_completions-01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4.json index d7ff5b9..b4bb002 100644 --- a/src/test/resources/cassettes/openai/mappings/chat_completions-9016066f-1bbd-49ec-a403-4311b29fe2af.json +++ b/src/test/resources/cassettes/openai/mappings/chat_completions-01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4.json @@ -1,5 +1,5 @@ { - "id" : "9016066f-1bbd-49ec-a403-4311b29fe2af", + "id" : "01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4", "name" : "chat_completions", "request" : { "url" : "/chat/completions", @@ -17,34 +17,34 @@ }, "response" : { "status" : 200, - "bodyFileName" : "chat_completions-9016066f-1bbd-49ec-a403-4311b29fe2af.txt", + "bodyFileName" : "chat_completions-01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4.txt", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:44 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:15 GMT", "Content-Type" : "text/event-stream; charset=utf-8", "access-control-expose-headers" : "X-Request-ID", "openai-organization" : "braintrust-data", - "openai-processing-ms" : "163", + "openai-processing-ms" : "262", "openai-project" : "proj_vsCSXafhhByzWOThMrJcZiw9", "openai-version" : "2020-10-01", - "x-envoy-upstream-service-time" : "295", + "x-envoy-upstream-service-time" : "375", "x-ratelimit-limit-requests" : "30000", "x-ratelimit-limit-tokens" : "150000000", "x-ratelimit-remaining-requests" : "29999", "x-ratelimit-remaining-tokens" : "149999990", "x-ratelimit-reset-requests" : "2ms", "x-ratelimit-reset-tokens" : "0s", - "x-request-id" : "req_54de4d24315b4956916c34f431127db5", + "x-request-id" : "req_3b6df98ab842452ba801ae44d01fb3e2", "x-openai-proxy-wasm" : "v0.1", "cf-cache-status" : "DYNAMIC", - "Set-Cookie" : [ "__cf_bm=_UjMnl_QDwQVpxokprrHLdnoEskB5r7JY8twdegieGo-1768330544-1.0.1.1-J0MsKAdxFAJi7gXyTdIY5xGElzghGXqWT3jZB7J_idg15FnZdRUgy.ZylsUo74UEwCL2AQDf4nu0oRUBxH7c8.DgsbIiU8E4QPM84OyZgrE; path=/; expires=Tue, 13-Jan-26 19:25:44 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=yPo_7QFyEr5D0p4gVCxgxZusm3_C.Ms7TeOjEsGEjZM-1768330544555-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], + "Set-Cookie" : [ "__cf_bm=2X0RC1RS9O3oPKF5RMJ4HK8nCbsE1ELw9sKno3wraUY-1769146575-1.0.1.1-qa6h7i46YVaDkApDd_dB5nXg37Kjis81F1KLj_TntBl4r7sN8AH9D4zRGSpU2hgtq69l35IVJIrDrIk28HAX.Jm6NHnnhsC6Xtxn4tIDjeo; path=/; expires=Fri, 23-Jan-26 06:06:15 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=RCbXCG2kNzL2y4V2kRg4reQXbnvNOHX.sty8iEMCicg-1769146575546-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], "Strict-Transport-Security" : "max-age=31536000; includeSubDomains; preload", "X-Content-Type-Options" : "nosniff", "Server" : "cloudflare", - "CF-RAY" : "9bd71c0d1f2f7538-SEA", + "CF-RAY" : "9c24eeaca80e7209-SEA", "alt-svc" : "h3=\":443\"; ma=86400" } }, - "uuid" : "9016066f-1bbd-49ec-a403-4311b29fe2af", + "uuid" : "01f57d6d-f85b-4093-8dcf-2aa7a75c2ab4", "persistent" : true, "insertionIndex" : 1 } \ No newline at end of file diff --git a/src/test/resources/cassettes/openai/mappings/chat_completions-541b9306-6880-41df-bb44-69ea82f417f8.json b/src/test/resources/cassettes/openai/mappings/chat_completions-0663776c-5dfc-4fb8-9904-6a415fca9550.json similarity index 66% rename from src/test/resources/cassettes/openai/mappings/chat_completions-541b9306-6880-41df-bb44-69ea82f417f8.json rename to src/test/resources/cassettes/openai/mappings/chat_completions-0663776c-5dfc-4fb8-9904-6a415fca9550.json index 4a08405..96ad058 100644 --- a/src/test/resources/cassettes/openai/mappings/chat_completions-541b9306-6880-41df-bb44-69ea82f417f8.json +++ b/src/test/resources/cassettes/openai/mappings/chat_completions-0663776c-5dfc-4fb8-9904-6a415fca9550.json @@ -1,5 +1,5 @@ { - "id" : "541b9306-6880-41df-bb44-69ea82f417f8", + "id" : "0663776c-5dfc-4fb8-9904-6a415fca9550", "name" : "chat_completions", "request" : { "url" : "/chat/completions", @@ -17,34 +17,34 @@ }, "response" : { "status" : 200, - "bodyFileName" : "chat_completions-541b9306-6880-41df-bb44-69ea82f417f8.txt", + "bodyFileName" : "chat_completions-0663776c-5dfc-4fb8-9904-6a415fca9550.txt", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:48 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:20 GMT", "Content-Type" : "text/event-stream; charset=utf-8", "access-control-expose-headers" : "X-Request-ID", "openai-organization" : "braintrust-data", - "openai-processing-ms" : "183", + "openai-processing-ms" : "250", "openai-project" : "proj_vsCSXafhhByzWOThMrJcZiw9", "openai-version" : "2020-10-01", - "x-envoy-upstream-service-time" : "196", + "x-envoy-upstream-service-time" : "354", "x-ratelimit-limit-requests" : "30000", "x-ratelimit-limit-tokens" : "150000000", "x-ratelimit-remaining-requests" : "29999", "x-ratelimit-remaining-tokens" : "149999982", "x-ratelimit-reset-requests" : "2ms", "x-ratelimit-reset-tokens" : "0s", - "x-request-id" : "req_a5315e01514f4b9298b1727d17a7a63e", + "x-request-id" : "req_0b00db9feb0e4ef58a01bb7ac876bf4f", "x-openai-proxy-wasm" : "v0.1", "cf-cache-status" : "DYNAMIC", - "Set-Cookie" : [ "__cf_bm=JXEJvLpTetK2tdCv6rCOsWm9XsCh35xX80RYSdbKlOQ-1768330548-1.0.1.1-LU.AUqqeHIJMar9S7gWLrEIivCcP4MJUEnfNDdc0mSsxnUNyUxhElI0Hk9_JklQpx0ceQrZ0v8T5VBY5twFxgLeJ9Rws3dzYTIx0I2L2Y_U; path=/; expires=Tue, 13-Jan-26 19:25:48 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=.9AGf0FnX2Yz1lpFeCSgRDTEkbpoZxJQOmHuxZaOYiU-1768330548584-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], + "Set-Cookie" : [ "__cf_bm=bYIPyEKfuTm409VPkQCtLzpmMg2nxTeFyykwHWGgA_U-1769146580-1.0.1.1-ZCCSd64AQcFy.CRADYK_IVDPXnmNu2C29QtbMdjQJri.6VVxE054Zig.XR4WT6pKbmLQ7pVUaeDFxZz12wNcgNs86ZhgjFV0FWwVoMKtowU; path=/; expires=Fri, 23-Jan-26 06:06:20 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=FxzNO4rIHjSTd7qpLrk6jpjSoK0E50RPpOkalVDhcmY-1769146580890-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], "Strict-Transport-Security" : "max-age=31536000; includeSubDomains; preload", "X-Content-Type-Options" : "nosniff", "Server" : "cloudflare", - "CF-RAY" : "9bd71c270a48d7dd-SEA", + "CF-RAY" : "9c24eecfec977688-SEA", "alt-svc" : "h3=\":443\"; ma=86400" } }, - "uuid" : "541b9306-6880-41df-bb44-69ea82f417f8", + "uuid" : "0663776c-5dfc-4fb8-9904-6a415fca9550", "persistent" : true, "insertionIndex" : 5 } \ No newline at end of file diff --git a/src/test/resources/cassettes/openai/mappings/chat_completions-13e69bf3-e90d-497f-bb66-47b448dc86bd.json b/src/test/resources/cassettes/openai/mappings/chat_completions-2446f774-b17e-4abf-811c-6a10681dbf94.json similarity index 65% rename from src/test/resources/cassettes/openai/mappings/chat_completions-13e69bf3-e90d-497f-bb66-47b448dc86bd.json rename to src/test/resources/cassettes/openai/mappings/chat_completions-2446f774-b17e-4abf-811c-6a10681dbf94.json index 3041718..b5687b6 100644 --- a/src/test/resources/cassettes/openai/mappings/chat_completions-13e69bf3-e90d-497f-bb66-47b448dc86bd.json +++ b/src/test/resources/cassettes/openai/mappings/chat_completions-2446f774-b17e-4abf-811c-6a10681dbf94.json @@ -1,5 +1,5 @@ { - "id" : "13e69bf3-e90d-497f-bb66-47b448dc86bd", + "id" : "2446f774-b17e-4abf-811c-6a10681dbf94", "name" : "chat_completions", "request" : { "url" : "/chat/completions", @@ -17,34 +17,34 @@ }, "response" : { "status" : 200, - "bodyFileName" : "chat_completions-13e69bf3-e90d-497f-bb66-47b448dc86bd.json", + "bodyFileName" : "chat_completions-2446f774-b17e-4abf-811c-6a10681dbf94.json", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:49 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:22 GMT", "Content-Type" : "application/json", "access-control-expose-headers" : "X-Request-ID", "openai-organization" : "braintrust-data", - "openai-processing-ms" : "414", + "openai-processing-ms" : "306", "openai-project" : "proj_vsCSXafhhByzWOThMrJcZiw9", "openai-version" : "2020-10-01", - "x-envoy-upstream-service-time" : "553", + "x-envoy-upstream-service-time" : "449", "x-ratelimit-limit-requests" : "30000", "x-ratelimit-limit-tokens" : "150000000", "x-ratelimit-remaining-requests" : "29999", "x-ratelimit-remaining-tokens" : "149999982", "x-ratelimit-reset-requests" : "2ms", "x-ratelimit-reset-tokens" : "0s", - "x-request-id" : "req_bc56bf172e1b41cda76cb06a5ca73ed0", + "x-request-id" : "req_ff8b6d5e346e4ed885fa0898a92f6a0a", "x-openai-proxy-wasm" : "v0.1", "cf-cache-status" : "DYNAMIC", - "Set-Cookie" : [ "__cf_bm=KnxsdeOSd50kaFk76pJ9ETxarjO99vDibkW9gcRSZF0-1768330549-1.0.1.1-WjfuxAjZSzqtvv3lnyq9bnx3pxzRUbCjfXzeXwBJKRzcvutTMQfCl6z4L1pE42fFq93ODxppcSpBpRt.84GY4apv7AufWbLfugBz5mnyZp0; path=/; expires=Tue, 13-Jan-26 19:25:49 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=ACDXQn3j61tpIi9gPK9jM.jFHQdh0jpS7JKxLogWKpY-1768330549707-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], + "Set-Cookie" : [ "__cf_bm=kjb4Pwx.qIHTEO4OoD_3xTOpJaGW5fSOiU9zwXB7Wv0-1769146582-1.0.1.1-_giyGbaRzHz_9IYno8kYmmpV5GhMPud1LXIByQIEgG8APuGOgEUkvEDSKj2dRkBlXJpEJzExftWl1TuKPsAZjOdWnpKG6J5A2neSGHGcMdg; path=/; expires=Fri, 23-Jan-26 06:06:22 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=v9TvVXzQKYp5xe.IdnuOzXZvKFmYXxiIw85Hif87nPc-1769146582202-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], "Strict-Transport-Security" : "max-age=31536000; includeSubDomains; preload", "X-Content-Type-Options" : "nosniff", "Server" : "cloudflare", - "CF-RAY" : "9bd71c2bbc74b9c4-SEA", + "CF-RAY" : "9c24eed52936a366-SEA", "alt-svc" : "h3=\":443\"; ma=86400" } }, - "uuid" : "13e69bf3-e90d-497f-bb66-47b448dc86bd", + "uuid" : "2446f774-b17e-4abf-811c-6a10681dbf94", "persistent" : true, "insertionIndex" : 6 } \ No newline at end of file diff --git a/src/test/resources/cassettes/openai/mappings/chat_completions-d6eda521-a7dd-4216-911e-4a916e45fca1.json b/src/test/resources/cassettes/openai/mappings/chat_completions-80cbaecd-6aec-4d80-a719-c572a45d9d82.json similarity index 72% rename from src/test/resources/cassettes/openai/mappings/chat_completions-d6eda521-a7dd-4216-911e-4a916e45fca1.json rename to src/test/resources/cassettes/openai/mappings/chat_completions-80cbaecd-6aec-4d80-a719-c572a45d9d82.json index c745847..8f2b465 100644 --- a/src/test/resources/cassettes/openai/mappings/chat_completions-d6eda521-a7dd-4216-911e-4a916e45fca1.json +++ b/src/test/resources/cassettes/openai/mappings/chat_completions-80cbaecd-6aec-4d80-a719-c572a45d9d82.json @@ -1,5 +1,5 @@ { - "id" : "d6eda521-a7dd-4216-911e-4a916e45fca1", + "id" : "80cbaecd-6aec-4d80-a719-c572a45d9d82", "name" : "chat_completions", "request" : { "url" : "/chat/completions", @@ -10,41 +10,41 @@ } }, "bodyPatterns" : [ { - "equalToJson" : "{\n \"model\" : \"gpt-4o-mini\",\n \"messages\" : [ {\n \"role\" : \"user\",\n \"content\" : \"is it hotter in Paris or New York right now?\"\n }, {\n \"role\" : \"assistant\",\n \"tool_calls\" : [ {\n \"id\" : \"call_O0vPA17lB2sSTvNui5KkMr7E\",\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getWeather\",\n \"arguments\" : \"{\\\"arg0\\\": \\\"Paris\\\"}\"\n }\n }, {\n \"id\" : \"call_gVz1cYvAomaPcTrheam72S8H\",\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getWeather\",\n \"arguments\" : \"{\\\"arg0\\\": \\\"New York\\\"}\"\n }\n } ]\n }, {\n \"role\" : \"tool\",\n \"tool_call_id\" : \"call_O0vPA17lB2sSTvNui5KkMr7E\",\n \"content\" : \"The weather in Paris is sunny with 72°F temperature.\"\n }, {\n \"role\" : \"tool\",\n \"tool_call_id\" : \"call_gVz1cYvAomaPcTrheam72S8H\",\n \"content\" : \"The weather in New York is sunny with 72°F temperature.\"\n } ],\n \"temperature\" : 0.0,\n \"stream\" : false,\n \"tools\" : [ {\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getForecast\",\n \"description\" : \"Get weather forecast for next N days\",\n \"parameters\" : {\n \"type\" : \"object\",\n \"properties\" : {\n \"arg0\" : {\n \"type\" : \"string\"\n },\n \"arg1\" : {\n \"type\" : \"integer\"\n }\n },\n \"required\" : [ \"arg0\", \"arg1\" ]\n }\n }\n }, {\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getWeather\",\n \"description\" : \"Get current weather for a location\",\n \"parameters\" : {\n \"type\" : \"object\",\n \"properties\" : {\n \"arg0\" : {\n \"type\" : \"string\"\n }\n },\n \"required\" : [ \"arg0\" ]\n }\n }\n } ]\n}", + "equalToJson" : "{\n \"model\" : \"gpt-4o-mini\",\n \"messages\" : [ {\n \"role\" : \"user\",\n \"content\" : \"is it hotter in Paris or New York right now?\"\n }, {\n \"role\" : \"assistant\",\n \"tool_calls\" : [ {\n \"id\" : \"call_YANyIGtCtTJowRa5Emu3rNQp\",\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getWeather\",\n \"arguments\" : \"{\\\"arg0\\\": \\\"Paris\\\"}\"\n }\n }, {\n \"id\" : \"call_qTfvi4D3JjATbUYoQd84Hbnf\",\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getWeather\",\n \"arguments\" : \"{\\\"arg0\\\": \\\"New York\\\"}\"\n }\n } ]\n }, {\n \"role\" : \"tool\",\n \"tool_call_id\" : \"call_YANyIGtCtTJowRa5Emu3rNQp\",\n \"content\" : \"The weather in Paris is sunny with 72°F temperature.\"\n }, {\n \"role\" : \"tool\",\n \"tool_call_id\" : \"call_qTfvi4D3JjATbUYoQd84Hbnf\",\n \"content\" : \"The weather in New York is sunny with 72°F temperature.\"\n } ],\n \"temperature\" : 0.0,\n \"stream\" : false,\n \"tools\" : [ {\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getForecast\",\n \"description\" : \"Get weather forecast for next N days\",\n \"parameters\" : {\n \"type\" : \"object\",\n \"properties\" : {\n \"arg0\" : {\n \"type\" : \"string\"\n },\n \"arg1\" : {\n \"type\" : \"integer\"\n }\n },\n \"required\" : [ \"arg0\", \"arg1\" ]\n }\n }\n }, {\n \"type\" : \"function\",\n \"function\" : {\n \"name\" : \"getWeather\",\n \"description\" : \"Get current weather for a location\",\n \"parameters\" : {\n \"type\" : \"object\",\n \"properties\" : {\n \"arg0\" : {\n \"type\" : \"string\"\n }\n },\n \"required\" : [ \"arg0\" ]\n }\n }\n } ]\n}", "ignoreArrayOrder" : true, "ignoreExtraElements" : false } ] }, "response" : { "status" : 200, - "bodyFileName" : "chat_completions-d6eda521-a7dd-4216-911e-4a916e45fca1.json", + "bodyFileName" : "chat_completions-80cbaecd-6aec-4d80-a719-c572a45d9d82.json", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:47 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:19 GMT", "Content-Type" : "application/json", "access-control-expose-headers" : "X-Request-ID", "openai-organization" : "braintrust-data", - "openai-processing-ms" : "725", + "openai-processing-ms" : "803", "openai-project" : "proj_vsCSXafhhByzWOThMrJcZiw9", "openai-version" : "2020-10-01", - "x-envoy-upstream-service-time" : "782", + "x-envoy-upstream-service-time" : "935", "x-ratelimit-limit-requests" : "30000", "x-ratelimit-limit-tokens" : "150000000", "x-ratelimit-remaining-requests" : "29999", "x-ratelimit-remaining-tokens" : "149999955", "x-ratelimit-reset-requests" : "2ms", "x-ratelimit-reset-tokens" : "0s", - "x-request-id" : "req_3fd434fe36e5457387641e91bddd872e", + "x-request-id" : "req_e102c2cce7804951971407ff595137bb", "x-openai-proxy-wasm" : "v0.1", "cf-cache-status" : "DYNAMIC", - "Set-Cookie" : [ "__cf_bm=GqbfmxZoOlS1Ff1BF3Pbb7tWbU6gizh68j2IIgjH.CE-1768330547-1.0.1.1-X3.JfC8E_qdMD_um.SLG7_uLhhxfj3letN8jGjFgDK2ZPDWk.lQYUWqkOUoshTf8UIxpi7UBmB2_C68nkATEom3o5SMBkYQ6I9bBYW32Y00; path=/; expires=Tue, 13-Jan-26 19:25:47 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=bEAnkV1ZOc_LuPfi3M1oNKr3OyHkhoGrgkd6IxqpeK0-1768330547136-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], + "Set-Cookie" : [ "__cf_bm=YNdj5EqdOkbf3kW7UwY85FeszzF4Hqdrrd7SgsnlrWA-1769146579-1.0.1.1-tBNepI43o2C3UdWbmqNH3UsvsJwtA4ulBudNkwhcodCsKIVzlRJbL_8O1nu1llUXs3KSfyqyc5QSClpxxZsTLiNlne.M7kR1OfoJSUcs4Kk; path=/; expires=Fri, 23-Jan-26 06:06:19 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=2J72JctaoJwYevB_t8pgB89EJH0tGa.ZQyc2GiCdy4k-1769146579229-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], "Strict-Transport-Security" : "max-age=31536000; includeSubDomains; preload", "X-Content-Type-Options" : "nosniff", "Server" : "cloudflare", - "CF-RAY" : "9bd71c1a3ce5d44f-SEA", + "CF-RAY" : "9c24eebfcd07ad72-SEA", "alt-svc" : "h3=\":443\"; ma=86400" } }, - "uuid" : "d6eda521-a7dd-4216-911e-4a916e45fca1", + "uuid" : "80cbaecd-6aec-4d80-a719-c572a45d9d82", "persistent" : true, "insertionIndex" : 3 } \ No newline at end of file diff --git a/src/test/resources/cassettes/openai/mappings/chat_completions-d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b.json b/src/test/resources/cassettes/openai/mappings/chat_completions-c881e7aa-85d0-4de6-92ec-da5dbed72027.json similarity index 74% rename from src/test/resources/cassettes/openai/mappings/chat_completions-d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b.json rename to src/test/resources/cassettes/openai/mappings/chat_completions-c881e7aa-85d0-4de6-92ec-da5dbed72027.json index 29775d6..25e3f4f 100644 --- a/src/test/resources/cassettes/openai/mappings/chat_completions-d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b.json +++ b/src/test/resources/cassettes/openai/mappings/chat_completions-c881e7aa-85d0-4de6-92ec-da5dbed72027.json @@ -1,5 +1,5 @@ { - "id" : "d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b", + "id" : "c881e7aa-85d0-4de6-92ec-da5dbed72027", "name" : "chat_completions", "request" : { "url" : "/chat/completions", @@ -17,34 +17,34 @@ }, "response" : { "status" : 200, - "bodyFileName" : "chat_completions-d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b.json", + "bodyFileName" : "chat_completions-c881e7aa-85d0-4de6-92ec-da5dbed72027.json", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:46 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:17 GMT", "Content-Type" : "application/json", "access-control-expose-headers" : "X-Request-ID", "openai-organization" : "braintrust-data", - "openai-processing-ms" : "1118", + "openai-processing-ms" : "1236", "openai-project" : "proj_vsCSXafhhByzWOThMrJcZiw9", "openai-version" : "2020-10-01", - "x-envoy-upstream-service-time" : "1134", + "x-envoy-upstream-service-time" : "1377", "x-ratelimit-limit-requests" : "30000", "x-ratelimit-limit-tokens" : "150000000", "x-ratelimit-remaining-requests" : "29999", "x-ratelimit-remaining-tokens" : "149999987", "x-ratelimit-reset-requests" : "2ms", "x-ratelimit-reset-tokens" : "0s", - "x-request-id" : "req_986e2d3165064709ac5ed23dc003dbd7", + "x-request-id" : "req_45a7f0c5ca6949b0bf20035b8f02624c", "x-openai-proxy-wasm" : "v0.1", "cf-cache-status" : "DYNAMIC", - "Set-Cookie" : [ "__cf_bm=BUHB2FaXynubGP92D6G2IvABiHu2eEGLvn8wFbWPmSo-1768330546-1.0.1.1-T6ZEZ.yw9bAhRfralFWrawOZlupeGnKzWk2CEzY7ok6AOXGdnWY5SL1.mewnFFHj6vLfMOIn0CPdULLrBjhYGOfb02DSZ4XJoHpYosO3KUU; path=/; expires=Tue, 13-Jan-26 19:25:46 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=xY.tpG.J8nfj5Le3HLJpq.g736HU9coaVEmWUc98mx4-1768330546132-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], + "Set-Cookie" : [ "__cf_bm=jwB44RgFskbI4cQEbTE4psDX8Bq7z9G4PeMDMVeabPY-1769146577-1.0.1.1-BEkhAl3uAVtQ7rCMUL33pBAwd_xCsE9R5LsuuZ0tfGKrKKVns6CWIE6tWgyzvQg1gEGEqr5sByC.2GNfPOokiDdjLwAbQSWE95Bc1yGCB5k; path=/; expires=Fri, 23-Jan-26 06:06:17 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=QFttWNtqPpE05MC54d0ecFgzddWNtjmW6TCvrc1wKCI-1769146577730-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], "Strict-Transport-Security" : "max-age=31536000; includeSubDomains; preload", "X-Content-Type-Options" : "nosniff", "Server" : "cloudflare", - "CF-RAY" : "9bd71c11cdf8c39a-SEA", + "CF-RAY" : "9c24eeb3685db9c2-SEA", "alt-svc" : "h3=\":443\"; ma=86400" } }, - "uuid" : "d0ddbec9-910b-4f8b-a0b8-a605e6a23b4b", + "uuid" : "c881e7aa-85d0-4de6-92ec-da5dbed72027", "persistent" : true, "insertionIndex" : 2 } \ No newline at end of file diff --git a/src/test/resources/cassettes/openai/mappings/chat_completions-4706df90-aa52-4bd3-97c0-3448a4e0005f.json b/src/test/resources/cassettes/openai/mappings/chat_completions-d0bef16c-03e9-44da-ab7e-a72110eb2028.json similarity index 65% rename from src/test/resources/cassettes/openai/mappings/chat_completions-4706df90-aa52-4bd3-97c0-3448a4e0005f.json rename to src/test/resources/cassettes/openai/mappings/chat_completions-d0bef16c-03e9-44da-ab7e-a72110eb2028.json index f67468f..21a14ed 100644 --- a/src/test/resources/cassettes/openai/mappings/chat_completions-4706df90-aa52-4bd3-97c0-3448a4e0005f.json +++ b/src/test/resources/cassettes/openai/mappings/chat_completions-d0bef16c-03e9-44da-ab7e-a72110eb2028.json @@ -1,5 +1,5 @@ { - "id" : "4706df90-aa52-4bd3-97c0-3448a4e0005f", + "id" : "d0bef16c-03e9-44da-ab7e-a72110eb2028", "name" : "chat_completions", "request" : { "url" : "/chat/completions", @@ -17,34 +17,34 @@ }, "response" : { "status" : 200, - "bodyFileName" : "chat_completions-4706df90-aa52-4bd3-97c0-3448a4e0005f.json", + "bodyFileName" : "chat_completions-d0bef16c-03e9-44da-ab7e-a72110eb2028.json", "headers" : { - "Date" : "Tue, 13 Jan 2026 18:55:47 GMT", + "Date" : "Fri, 23 Jan 2026 05:36:20 GMT", "Content-Type" : "application/json", "access-control-expose-headers" : "X-Request-ID", "openai-organization" : "braintrust-data", - "openai-processing-ms" : "403", + "openai-processing-ms" : "297", "openai-project" : "proj_vsCSXafhhByzWOThMrJcZiw9", "openai-version" : "2020-10-01", - "x-envoy-upstream-service-time" : "421", + "x-envoy-upstream-service-time" : "460", "x-ratelimit-limit-requests" : "30000", "x-ratelimit-limit-tokens" : "150000000", "x-ratelimit-remaining-requests" : "29999", "x-ratelimit-remaining-tokens" : "149999990", "x-ratelimit-reset-requests" : "2ms", "x-ratelimit-reset-tokens" : "0s", - "x-request-id" : "req_e6ad6e08adaf4bafbb57a63d4f06081f", + "x-request-id" : "req_af046753669347ffb931da8968d60d3b", "x-openai-proxy-wasm" : "v0.1", "cf-cache-status" : "DYNAMIC", - "Set-Cookie" : [ "__cf_bm=Kf0PbpZlmshlH.OMcmGSPEKIneXZrbLpMkYluWZ53rM-1768330547-1.0.1.1-dyEibCy4JMqWjO47uv.WMSRPxMrD4E.v7QDrQ9e9D1qyPfRzJoKmz2v5zMtvapNpQ5Xv.RYHD0MIgE2GXDk6vYpwO_sTmRvtYdbtEIiEcuU; path=/; expires=Tue, 13-Jan-26 19:25:47 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=a2Iqf.uw0VrBcSHQkzxsmfmy9XzkFde5gjk5LB7Tpb8-1768330547777-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], + "Set-Cookie" : [ "__cf_bm=z3G4hl.rYDWjZse9PNgVi0tZLHDR5O04WdLkkXpMVj4-1769146580-1.0.1.1-OHy5BXW1875sGlXQoo9Hqe_L9qiG0bWF3MEs5p7v8r8lugfjzZ77qfXVl3V0UTnQdI0SBO55T5uO8snJgq8ybHHhVne9KfoWIUhEQiUVjl8; path=/; expires=Fri, 23-Jan-26 06:06:20 GMT; domain=.api.openai.com; HttpOnly; Secure; SameSite=None", "_cfuvid=pzSfHK3yGW2i0qHi7.SbpjutNAXNegUcggHRzVxYzWQ-1769146580232-0.0.1.1-604800000; path=/; domain=.api.openai.com; HttpOnly; Secure; SameSite=None" ], "Strict-Transport-Security" : "max-age=31536000; includeSubDomains; preload", "X-Content-Type-Options" : "nosniff", "Server" : "cloudflare", - "CF-RAY" : "9bd71c206d2f280d-SEA", + "CF-RAY" : "9c24eec918c9dede-SEA", "alt-svc" : "h3=\":443\"; ma=86400" } }, - "uuid" : "4706df90-aa52-4bd3-97c0-3448a4e0005f", + "uuid" : "d0bef16c-03e9-44da-ab7e-a72110eb2028", "persistent" : true, "insertionIndex" : 4 } \ No newline at end of file