Tracing
Tracing records what your application does as spans you can inspect in Braintrust. The recommended way to capture AI calls is auto-instrumentation: callinitLogger() once at startup and run your app with the Braintrust import or build hook, and supported AI clients are traced with no per-call changes (see Install and instrument). Or, to learn how to instrument a specific provider client by hand, see Wrappers.
The APIs below configure tracing and let you trace your own application code.
initLogger()
Creates the logger that sends your traces to Braintrust, your starting point for tracing. Call it once on startup: by default it becomes the current logger that traced(), startSpan(), and auto-instrumentation log through, and auto-instrumentation requires it.
Logger
Options (all optional):
projectName(string): project name for logs. If omitted, logs go to the global project.projectId(string): project ID. Takes precedence overprojectName.apiKey(string): API key. Defaults toBRAINTRUST_API_KEY.appUrl(string): Braintrust app URL. Defaults tohttps://www.braintrust.dev.orgName(string): organization name, useful when credentials can access multiple orgs.asyncFlush(boolean): defaults totrue. Setfalseto make logger calls likelogger.log()awaitable.setCurrent(boolean): defaults totrue. Controls whethercurrentLogger()returns this logger and what logger auto-instrumentation logs through.forceLogin(boolean): log in again even if the SDK already has credentials.fetch(typeof fetch): custom fetch implementation for SDK requests. Takes a function that follows the same API as nativefetch.debugLogLevel("error" | "warn" | "info" | "debug" | false): enables SDK troubleshooting output. Setfalseto also silence the SDK’s default warnings.
log(event)→string: logs a span and returns its row ID. ReturnsPromise<string>when the logger usesasyncFlush: false.traced(callback, args?)→ the callback’s result: creates a root span under the logger and ends it when the callback finishes. Spans started within the callback are nested under thetracedspan.startSpan(args?)→Span: starts a root span that you end manually.logFeedback(event)→void: adds feedback scores, expected values, tags, or comments to an existing row.updateSpan(event)→void: updates an already-written span by ID. Flush the original span before updating it.export()→Promise<string>: a serialized parent string for distributed tracing.flush()→Promise<void>: sends pending rows to Braintrust, resolving when they’re all flushed.trace(body, options?),debug(body, options?),info(body, options?),warn(body, options?),error(body, options?),fatal(body, options?)→string: emits an application log at that severity level and returns its row ID.options.metadataadds metadata. ReturnsPromise<string>when the logger usesasyncFlush: false.trace()doesn’t start a trace.fmt`...`(template literal tag) → a template object: preserves a template literal and its values for a severity method, such aslogger.info(logger.fmt`User ${userId} signed in`). See Preserve message templates.
wrapConsole()
Forwards future console.log(), console.trace(), console.debug(), console.info(), console.warn(), and console.error() calls to Braintrust as application logs. console.log() maps to info, and the other methods keep their severity. Console output is unchanged.
() => void, a cleanup function that stops forwarding for this wrap.
Arguments (all optional):
target(Pick<Console, "log" | "trace" | "debug" | "info" | "warn" | "error">): the console to wrap. Defaults to the globalconsole.options.logger(Logger): the logger that receives console messages. Defaults to the current logger at the time of each call.
currentLogger()
The logger most recently installed by initLogger({ setCurrent: true }), or undefined if no current logger exists.
Logger | undefined
traced()
Creates a span, sets it as the current span for the duration of your callback, and passes the span to your callback. Nested spans and traced LLM calls attach to it while it is current. Errors thrown in the callback are logged to the span, and the span ends when the callback returns. Returns the callback’s result. setCurrent defaults to true.
callback((span) => R, required): the function to trace. Braintrust passes the span as its argument.args(optional): span options.name(string): span name shown in Braintrust.type(SpanType): span type, such asfunction,task,tool,llm,score, oreval.spanAttributes(Record<PropertyKey, unknown>): extra span attributes.startTime(number): start time as a Unix timestamp in seconds.parent(string | PropagationContext): exported parent span string or W3C trace context.event(ExperimentLogPartialArgs): initial event data to log on the span.propagatedEvent(ExperimentLogPartialArgs): event data propagated from another span.spanId(string): explicit span ID.parentSpanIds(ParentSpanIds | MultiParentSpanIds): explicit parent span IDs.setCurrent(boolean): controls whethercurrentSpan()returns this span while the callback runs. Defaults totrue.asyncFlush(boolean): controls whether the span flushes asynchronously.
wrapTraced()
Wraps a function so each call creates a span, sets it as the current span while the function runs, logs thrown errors, and ends the span when the call finishes. Logs the function arguments as input and the return value as output. To trace a callback and log fields yourself, use traced() instead. setCurrent defaults to true.
fn((...args) => R, required): the function to wrap. Its return value is returned by the wrapped function.args(optional): span options for each call to the wrapped function.name(string): span name shown in Braintrust. If omitted, Braintrust uses the wrapped function’s name.type(SpanType): span type, such asfunction,task,tool,llm,score, oreval. If omitted, defaults tofunction.spanAttributes(Record<PropertyKey, unknown>): extra span attributes.startTime(number): start time as a Unix timestamp in seconds.parent(string | PropagationContext): exported parent span string or W3C trace context.event(ExperimentLogPartialArgs): initial event data to log on the span.propagatedEvent(ExperimentLogPartialArgs): event data propagated from another span.spanId(string): explicit span ID.parentSpanIds(ParentSpanIds | MultiParentSpanIds): explicit parent span IDs.setCurrent(boolean): controls whethercurrentSpan()returns this span while the wrapped function runs. Defaults totrue.asyncFlush(boolean): controls whether the span flushes asynchronously.noTraceIO(boolean): for generator functions only, preventswrapTraced()from automatically logging arguments asinputand collected yielded values asoutput. For other functions, usetraced()and callspan.log()yourself.
startSpan()
Starts a span manually. See traced() for callback-based tracing, or wrapTraced() to trace a function automatically.
A span from startSpan() is not current, so currentSpan() and nested spans will not attach to it unless you use withCurrent(). Call span.end() when the span is finished.
Span
Parameters:
args(optional): span options.name(string): span name shown in Braintrust.type(SpanType): span type, such asfunction,task,tool,llm,score, oreval.spanAttributes(Record<PropertyKey, unknown>): extra span attributes.startTime(number): start time as a Unix timestamp in seconds.parent(string | PropagationContext): exported parent span string or W3C trace context.event(ExperimentLogPartialArgs): initial event data to log on the span.propagatedEvent(ExperimentLogPartialArgs): event data propagated from another span.spanId(string): explicit span ID.parentSpanIds(ParentSpanIds | MultiParentSpanIds): explicit parent span IDs.asyncFlush(boolean): controls whether the span flushes asynchronously.
currentSpan()
Returns the current span, so you can log to it without holding a direct reference, for example from a helper called while a traced() callback runs. If no span is current, returns a no-op span whose log() calls are ignored, so currentSpan().log(...) will not throw.
Span
withCurrent()
Makes an existing span the current span for the duration of a callback. A span from startSpan() isn’t current on its own, so spans and traced LLM calls created afterward won’t nest under it. Wrap that work in withCurrent(span, callback) and it nests under span, with currentSpan() returning it inside the callback.
span(Span, required): the span to make current inside the callback.callback((span) => R, required): the work to run withspancurrent. Its return value is returned.
withParent()
Attaches the spans created inside its callback to a parent that lives somewhere else, such as another process, service, or job. Get the parent’s location by calling export() on a span or logger, pass that string to withParent(), and the work inside nests under the original trace. This is how you trace across process boundaries (distributed tracing).
parent(string, required): an exported parent string, from a span’s or logger’sexport().callback(() => R, required): the work to run under that parent. Its return value is returned.
injectTraceContext()
Serializes the current span’s context into W3C traceparent and baggage HTTP headers so a downstream service can continue the same trace. This is the standards-based alternative to export() and withParent(): it works across any HTTP boundary and interoperates with OpenTelemetry and other systems that respect W3C Trace Context.
carrier(optional): an existing header carrier to add trace context to and return, such as aHeadersinstance, a plain object, or an ordinary Node.js http request object. Pass one to combine trace-context headers with the headers you are already sending. Omit it to get a fresh headers object.options.span(Span, optional): the span to inject. Defaults to the current span.
extractTraceContextFromHeaders()
Reads W3C trace-context headers from an inbound request and returns an opaque parent context you pass to traced() or startSpan() as parent, continuing the trace started by the calling service. This is the receive-side counterpart of injectTraceContext().
PropagationContext | undefined — an opaque context for parent, or undefined when no valid traceparent header is present, in which case traced() starts a fresh root span.
Parameters:
headers(required): the inbound request headers, such as a WebRequest’sHeaders, a plain object, or an ordinary Node.js http request object. Header lookups are case-insensitive.
logError()
Logs an error and stack trace to a span’s error field.
void
Parameters:
span(Span, required): the span to attach the error to.error(unknown, required): the caught error, whose message and stack are logged.
traced() are logged automatically, so use logError() for errors you catch yourself.
permalink()
Turns an exported span string into a shareable Braintrust UI URL, so you can link straight to a specific trace from your own logs, dashboards, or alerts. Pass the string returned by a span’s export().
Promise<string>
Links can be generated before flushing, but they become viewable only after the span and its root have been flushed and ingested. If you have a span object, span.permalink() is usually simpler.
flush()
Sends any buffered log rows to Braintrust and returns a promise that resolves once they’re all sent. Call it before a script or short-lived process exits so pending events aren’t lost.
In long-lived Node.js processes, logged rows wait up to 1 second before they’re sent, so nearby rows share one upload request. The SDK flushes pending rows when the process exits normally, but not when it exits through process.exit() or a signal. Call flush() in those shutdown paths. To change the delay, set BRAINTRUST_FLUSH_DELAY_MS.
Promise<void>
setMaskingFunction()
Installs a global masking function that runs over your logged data before it leaves your process, so you can redact sensitive values like PII or secrets before they ever reach Braintrust. Set it to null to remove a previously configured masking function.
void
Masking applies to input, output, expected, metadata, context, scores, and metrics, just before the SDK uploads each record. It covers every record the SDK logs, including manually created spans, dataset rows, and feedback scores, but not feedback comments or feedback metadata. If you also configure span customizers, masking runs after them.
See Masking functions for field coverage, examples, and error handling.
configureInstrumentation()
Configures auto-instrumentation. Import it from braintrust/instrumentation and call it at startup, before you import braintrust or any AI SDK. The same function is also exported from braintrust, but importing braintrust turns instrumentation on, so a call through that import logs a warning and has no effect. Static imports are hoisted, so load braintrust and your AI SDKs with dynamic imports after the call.
void
Options (all optional):
integrations(InstrumentationIntegrationsConfig): an object of boolean flags keyed by integration name, such asopenai,anthropic,aisdk,googleGenAI,groq, andlangchain. All integrations are enabled by default. Setting a flag tofalsedoesn’t disable the integration. To disable integrations, use theBRAINTRUST_DISABLE_INSTRUMENTATIONenvironment variable. Thelivekitandmcpkeys also accept options objects. See the LiveKit Agents and MCP SDK integration guides for configuration details.spanCustomizers(readonly SpanCustomizer[]): span customizers that modify each outgoing record from an SDK span before upload. They run in array order. A later call that passesspanCustomizersreplaces the list.streamingAttachmentBufferBytes(number): target size in bytes of each attachment that instrumentation uploads from streaming media, such as LiveKit Agents call audio. Defaults to 4 MiB (4194304). Must be a positive safe integer. Other values throw an error. Instrumentation splits media only at valid boundaries, and whole-file responses can exceed this size. Audio recordings count raw PCM before compression, so compressed uploads are smaller. It doesn’t limit total memory and doesn’t turn on attachment capture.
SpanCustomizer
An object with an onSpanExport(data) method that modifies outgoing records from SDK spans. Pass customizers in the spanCustomizers option of configureInstrumentation(). See Span customizers for examples and behavior.
Methods:
onSpanExport(data)→SpanExportData: called synchronously on each record the SDK logs. The SDK logs a span as several records as its data becomes available, such as one with the input and a later one with the output, so the method runs several times per span. See How customizers run.SpanExportDataisRecord<string, unknown>. Return the modified record or a replacement plain object.
- Customizers run on all native SDK spans, including spans you create with
traced(),startSpan(), andlogger.log(). They don’t run on dataset rows or feedback. In v3.35.0, they run only on spans created by instrumentation and wrappers. - With v3.38.0 or later and
@braintrust/otelv1.1.0 or later, customizers also run on OpenTelemetry spans thatBraintrustSpanProcessororBraintrustExportersends to Braintrust, and in OpenTelemetry compatibility mode. - Each record holds only part of the span, so a field can be absent from any single call. Check for a field before you change it.
- The SDK restores protected fields, such as
id,span_id,root_span_id, andproject_id, after every call.
Evaluations
An evaluation runs your task over a set of cases, scores each output, and logs the results to an experiment, which is how you measure quality and catch regressions as you change prompts or models. UseEval() to run most evaluations. The other APIs here run workflow evaluations and summarize and report results.
Eval()
Runs an evaluation from your data, a task, and scorers or classifiers: it runs the task over every case, scores the outputs, logs each row to an experiment, and returns a result with a summary you can compare across runs.
Promise<EvalResultWithSummary>
Eval(project, evaluator, options?) takes three arguments.
project
project
The project name (a
string) to log the experiment to. To identify the project by ID, set projectId on the evaluator.evaluator
evaluator
Defines the evaluation: the data to run against, the task to test, and how to score the results. Its core fields:
-
data(EvalData, required): the evaluation cases. Provide an array of cases, a function or promise that returns an array, anAsyncIterable/AsyncGenerator, orBaseExperiment(...). Each case has:input(required): the value passed to the task.expected: the expected output for this case, if any.metadata/tags: per-case metadata and tags.trialCount: how many times to run the task for this case.
-
task((input, hooks?) => output, required): the function under test. It receives two arguments:input: the current case’s input value.hooks(optional): a context object the framework passes in for the current evaluation row. Use its fields to read parameters, log to the task’s span, or adjust the row:span: the task’s span. Log extra data or child spans to it.parameters: the validated runtime parameters.metadata/tags: mutate to change the current row’s metadata or tags.expected: the expected output for the current case, if it provided one.trialIndex: zero-based trial index, useful whentrialCountis greater than 1.reportProgress: reports task progress for long-running evals.signal: anAbortSignalthat aborts when the evaluation times out or itssignalaborts. Pass it to the APIs your task calls, or checksignal.aborted, to stop work early. Requires v3.37.0 or later.
-
scores(EvalScorer[]): scorer functions, required unlessclassifiersis set. Each receives the case fields plusoutputand an optionaltrace, and returns a number, aScore, an array of scores, ornullto skip the row. A bare number is named after the function (orscorer_<index>); return aScorefor a custom name or metadata.Scorers and classifiers receive each case’sidand post-tasktagsthroughEvalScorerArgs, in addition to the otherEvalCasefields. -
classifiers(EvalClassifier[]): like scorers, but return aClassificationand write to theclassificationsfield instead ofscores(required unlessscoresis set).idis the machine-readable outcome,labelis the display label (defaults toid), andnameis the key (defaults to the function name, orclassifier_<index>). -
parameters(EvalParameters | loadParameters()): named, typed settings that make the eval configurable, so you can re-run it with different values (a temperature, a model, a prompt) without editing code. Declare them with defaults here, and the task reads the chosen values fromhooks.parameters. Pass runtime values in theoptionsargument (validated against your schema), or load a saved set withloadParameters(), whose defaults merge with any runtime overrides. Supports Zod values, model parameters (exposed as strings), and prompt parameters (exposed asPromptobjects).
experimentName(string): experiment name.trialCount(number): number of task runs per input.maxConcurrency(number): maximum concurrent task/scorer work.timeout(number): evaluation timeout in milliseconds.0times out immediately.signal(AbortSignal): stops the evaluation when it aborts.metadata(Record<string, unknown>): experiment metadata.tags(string[]): experiment tags.baseExperimentName(string): base experiment name for comparison.baseExperimentId(string): base experiment ID, taking precedence overbaseExperimentName.summarizeScores(boolean): defaults totrue.flushBeforeScoring(boolean): flushes task spans before scorers run.
signal aborts, it skips the cases that haven’t started and aborts hooks.signal. It then waits for running tasks and scorers to finish before it rejects with Evaluator timed out or Evaluator aborted, so no work the evaluation started keeps running after it. A task that ignores hooks.signal runs to completion and delays the rejection. In versions before v3.37.0, the evaluation rejects right away and running tasks continue in the background.options
options
An optional object of run settings:
noSendLogs(boolean): run locally and build a local summary without sending logs.onStart((summary) => void): called when the experiment starts.stream((event) => void): receives progress events.parent(string): exported parent span for nested or distributed eval logging.parameters(Record<string, unknown>): runtime parameter values.returnResults(boolean): defaults totrue. Setfalsefor large evals to keep only aggregate summary data.enableCache(boolean): defaults totrue. Controls the local span cache used by scorers.
defineWorkflowEval()
Defines an evaluation whose task or scorers submit requests to an asynchronous provider API, such as a batch API, and collect the results later. The run saves its progress to a store, so your application can resume it by polling or from a webhook. With a shared store such as Redis, a different process can resume it.
Requires TypeScript SDK v3.37.0 or later. Import from the top-level
braintrust package. The API is marked experimental in the SDK. See Workflow evaluations for a complete OpenAI example.submitBatch(), pollBatch(), collectText(), and exactMatch(). It defines an evaluation with a polling batch task:
start().
defineWorkflowEval(projectName, evaluator) takes two arguments.
projectName
projectName
The project name (a
string) to log the experiment to. Braintrust creates the project if it doesn’t exist. To log to an existing project by ID, also set projectId on the evaluator. The name then only sets the default experiment name, and it must stay the same when you resume the run.evaluator
evaluator
Defines the evaluation, the store used to save progress, and any task or scorer work that completes asynchronously. Data, plain tasks, plain scorers, classifiers, parameters, and optional experiment settings use the same contracts as
Eval(). On this page, a workflow task or scorer means a WorkflowTask, WorkflowScorer, WorkflowBatchTask, or WorkflowBatchScorer.store(WorkflowEvalStore, required): persistence backend shared by every process that starts or resumes the run. SeeWorkflowEvalStore.data(EvalData, required): evaluation cases, using the same formats and case fields asEval(). When using a workflow task or scorer, each case needs a unique, non-emptyidorupsert_id, or acaseIdcallback that supplies one.task(EvalTask | WorkflowTask | WorkflowBatchTask, required): a plain task function using the sametask(input, hooks)contract asEval(), aWorkflowTask, or aWorkflowBatchTask.scores(Array<EvalScorer | WorkflowScorer | WorkflowBatchScorer>): any mix of plain scorer functions,WorkflowScorerinstances, andWorkflowBatchScorerinstances. Scorers start as soon as their case’s task output is collected, except that aWorkflowBatchScorerwaits to fill a batch.classifiers(EvalClassifier[]): plain classifier functions using the same contract asEval(). Classifiers start as soon as their case’s task output is collected.caseId((datum) => string | Promise<string>): returns a stable string ID when a case has neitheridnorupsert_id. All trials of a case share the ID.maxConcurrency(number): positive maximum number ofsubmit,getExternalId,poll, andcollectcallbacks that run at once during eachstart(),poll(), orprocessSubmissionResult()call. Defaults to10. For a batch task or scorer, eachsubmitcall submits one batch. When the evaluation uses a workflow task or scorer, plain tasks, scorers, and classifiers aren’t affected, and neither is the number of requests pending at the provider. Without either,start()runs the evaluation likeEval(), andmaxConcurrencylimits tasks and scorers as it does there.parameters(EvalParameters | loadParameters()): parameter definitions or a saved parameter set, using the same contract asEval(). Pass runtime values tostart({ parameters }).
baseExperimentId(string): ID of the experiment to compare this run against. Takes precedence overbaseExperimentName.baseExperimentName(string): experiment to compare this run against.description(string): experiment description.errorScoreHandler(ErrorScoreHandler): handles missing scores when tasks or scorers fail. Ignored when the evaluation uses a workflow task or scorer.experimentName(string): experiment name. If omitted, a unique name is generated for each run. If set, every run logs to the same experiment.flushBeforeScoring(boolean): whether to flush task spans before running scorers.gitMetadataSettings(GitMetadataSettings): settings for collecting Git metadata.isPublic(boolean): whether the experiment is public.metadata(Record<string, unknown>): experiment metadata.projectId(string): ID of an existing project to log to instead.projectNameis still required, sets the default experiment name, and must stay the same when you resume the run.repoInfo(RepoInfo): Git metadata to record for the experiment. Takes precedence overgitMetadataSettings.state(BraintrustState): logger state used to initialize Braintrust objects.summarizeScores(boolean): whether to summarize experiment scores. Defaults totrue.tags(string[]): experiment tags.trialCount(number): positive number of trials per case. Defaults to1. A case’strialCountoverrides this value.
defineWorkflowEval() does not support the timeout, signal, or update evaluator options. When the evaluation uses a workflow task or scorer, its maxConcurrency limits provider callbacks, not task and scorer work as in Eval().Case data, submission data, and collected values must be JSON-serializable.Workflow evaluation methods
Start and resume runs of a workflow evaluation.defineWorkflowEval() returns an object with these methods.
Requires TypeScript SDK v3.37.0 or later. Import from the top-level
braintrust package. The API is marked experimental in the SDK. See Workflow evaluations for a complete OpenAI example.defineWorkflowEval() example, then polls until the run completes:
start(options?): creates a new run, saves the cases, and submits the first requests.options.parameters: runtime values for the evaluation’s parameters.options.noSendLogs: true: builds the summary locally without sending logs to Braintrust.- Every call starts a new run and submits every request to the provider again. To continue a run, pass its
runIdtopoll()orprocessSubmissionResult()instead. - Without a workflow task or scorer,
start()runs the whole evaluation at once, likeEval().
status({ runId }): reads saved progress without contacting the provider or advancing work.poll({ runId }): checks existing polling submissions once, collects completed results, and advances the cases that are ready. Scorer requests submitted during this call are polled on a later call. It also resubmits any submission whosesubmitcallback threw, for both polling and webhook completion.processSubmissionResult({ runId?, submissionId?, externalId? }): tells the SDK that one request finished, so it collects the result and advances the cases in it. Call it from your webhook handler.- Identify the request by
externalId, the provider ID yourgetExternalIdfunction returned, or bysubmissionId, the SDK’s ID for the request. Pass at least one. If you pass both, they must identify the same submission. runIdis required withsubmissionId. WithexternalIdalone, you can omitrunIdwhen each provider ID belongs to only one run of the evaluation. If several runs share theexternalId, it throws an error and you must passrunId.- Throws
Error("No submission matches this result")if the ID doesn’t match a request in this run, and an error if the run isn’t in the store. Throws an error if the submission’ssubmitcallback threw. The nextpoll()resubmits it. - Doesn’t check the webhook event. Before calling it, check that the event came from the provider and that the request succeeded.
- Identify the request by
start(), status(), poll(), and processSubmissionResult() all return the run’s current state. All results have status, runId, and pending, and a completed result also has summary:
status("waiting" | "completed"): the run’s status.runId(string): identifier to save and pass tostatus(),poll(), andprocessSubmissionResult().pending({ poll: number; webhook: number }): number of submitted requests not yet collected, split intopollandwebhookcounts. A batch counts as one request, and so does a submission waiting to be resubmitted.summary(ExperimentSummary): the experiment’s scores and a link to it.
WorkflowTask and WorkflowScorer
Define a task or scorer that submits one request per case and trial to an asynchronous provider, then collects one result when the request completes. To submit many cases and trials in one provider batch, use WorkflowBatchTask and WorkflowBatchScorer.
Requires TypeScript SDK v3.37.0 or later. Import from the top-level
braintrust package. The API is marked experimental in the SDK. See Workflow evaluations for a complete OpenAI example.submitBatch(), pollBatch(), and collectText() functions from the OpenAI Batch API example, as in Submit one request per case:
WorkflowTask as task or a WorkflowScorer in scores when calling defineWorkflowEval().
new WorkflowTask(processor) and new WorkflowScorer(processor) take a processor with these fields:
name(string, required forWorkflowScorer): scorer name. Must be unique and non-empty, and must stay the same when you resume a run.WorkflowTaskhas noname.submit(item, context)(required): called to submit the request.WorkflowScorercalls it after the task output is ready. Returns JSON-serializable submission data, which the SDK saves and passes to the completion and collection callbacks.completion(required): how the SDK learns that the request finished. See completion configuration.collect(submission, context)(required): called after the request completes.WorkflowTaskreturns one task result, andWorkflowScorerreturns one scorer result. Must tolerate replay, including concurrent webhook delivery.
submit, poll, and collect must return a Promise. context has SDK-generated runId and submissionId values, which your integration can use to correlate requests with the provider.
Items passed to submit:
WorkflowTask:id,input,expected,metadata,tags,parameters, andtrialIndex. Theididentifies one case and trial, such as"france:trial:0", andtrialIndexcounts from0.WorkflowScorer: the same fields exceptparameters, plus the task’soutput.
collect:
WorkflowTask:{ output, metadata?, tags? }, the task output. Returned metadata merges into case metadata. Returned tags replace case tags.WorkflowScorer:{ score }, a score in the same formats accepted by plain scorer functions, including a number, a score object, or an array of scores. An unnamed numeric score uses the scorer’sname.
submit throws an error, the SDK saves the submission as failed, and the next poll({ runId }) calls submit again with the same submissionId. If your provider supports idempotency keys, use the submissionId as the key so a retry doesn’t create a duplicate request. When submit throws during start(), start() throws without returning the runId, so save context.runId in your submit callback to resume the run.
The SDK runs getExternalId and plain task, scorer, and classifier functions only once per case and trial. If one of them throws an error, or the process exits while one of them or submit runs, that case never completes and the run stays "waiting". Start a new run to recover. Polling and collect callbacks are safe to retry.
WorkflowBatchTask and WorkflowBatchScorer
Define a task or scorer that submits many cases and trials in each asynchronous provider batch, such as an OpenAI batch, then collects one result per item when the batch completes. Use them when a provider limits how many batches you can create.
Requires TypeScript SDK v3.37.0 or later. Import from the top-level
braintrust package. The API is marked experimental in the SDK. See Workflow evaluations for a complete OpenAI example.submitBatch(), collectText(), and pollBatch() functions from the OpenAI Batch API example:
WorkflowBatchTask as task or a WorkflowBatchScorer in scores when calling defineWorkflowEval(). An evaluation can mix them with plain functions, WorkflowTask, and WorkflowScorer.
new WorkflowBatchTask(processor) and new WorkflowBatchScorer(processor) take a processor with these fields:
name(string, required forWorkflowBatchScorer): scorer name. Must be unique and non-empty, and must stay the same when you resume a run.WorkflowBatchTaskhas noname.batching(required): how the SDK groups items into batches.maxSize(number, required): the maximum number of items in one batch. Must be a positive integer.maxWaitMs(number,WorkflowBatchScoreronly): how long a partial batch waits for more items before the SDK submits it. Must be0or greater.
submit(items, context)(required): called to submit one batch. Receives up tomaxSizeitems and returns JSON-serializable submission data, which the SDK saves and passes to the completion and collection callbacks.completion(required): how the SDK learns that the batch finished. See completion configuration.collect(submission, context)(required): called after the batch completes. Returns one entry per item, as an array, an iterable, or an async iterable. Must tolerate replay, including concurrent webhook delivery.
submit have the same fields as the items a WorkflowTask or WorkflowScorer receives, plus a customId. The customId identifies the item within its batch, such as "item-0", and uses only characters that providers accept in request IDs. Use it as the provider’s request ID, such as OpenAI’s custom_id.
Entries returned from collect:
{ customId, result }: the item’s result. ForWorkflowBatchTask,resultis{ output, metadata?, tags? }. ForWorkflowBatchScorer, it’s{ score }. Both take the same formats as the results aWorkflowTaskorWorkflowScorerreturns.{ customId, error }: the item failed.
collect returns a customId that isn’t in the batch, or returns the same customId twice. The SDK records an item as failed when collect returns an error for it or returns no entry for it. A failed task item logs the error on its row and skips that case’s scorers and classifiers. A failed scorer item omits that score and records the error in the row’s scorer_errors metadata. The run still completes. To report the items in a batch that expired or was cancelled, return { status: "complete" } from your polling callback for those states.
When the SDK submits batches:
WorkflowBatchTask:start()submits every case and trial, in batches of up tomaxSizeitems.WorkflowBatchScorer: submits a batch whenmaxSizeitems have task outputs, or when no more items can get one. If you setmaxWaitMs, it also submits a partial batch that has waited that long. The SDK checks the wait during eachstart(),poll(), andprocessSubmissionResult()call and doesn’t start timers, so a partial batch is submitted on the first call aftermaxWaitMspasses.
submit throws an error, the next poll({ runId }) resubmits the same items with the same submissionId, as it does for a WorkflowTask. Each case’s metadata.workflow_eval.batch_submission_id records the submissionId of its task batch.
Completion configuration
Configure how a workflow task or scorer learns that its provider request or batch finished: by polling when your application callspoll(), or from a webhook your application receives.
Requires TypeScript SDK v3.37.0 or later. Import from the top-level
braintrust package. The API is marked experimental in the SDK. See Workflow evaluations for a complete OpenAI example.createEvaluation() does in the OpenAI Batch API example. It uses that example’s client, Submission type, submitBatch(), and collectText(). pollBatch() reports failed, expired, and cancelled batches as complete, so the SDK records the requests without results as failed and the run can finish:
completion to one of these objects:
{ mode: "poll", poll }:poll(submission, context)is called for each pending submission when your application callspoll({ runId }). It returns{ status: "pending" },{ status: "complete" }, or{ status: "failed", error }. Completion triggers collection. If a callback fails,poll()still checks the other submissions and advances the run before it throws. With one failure, it throws that error. With several, it throws anAggregateError. The submission stays pending, so the nextpoll()call checks it again.{ mode: "webhook", getExternalId }:getExternalId(submission, context)is called right aftersubmitreturns. It synchronously returns a non-empty provider request ID to associate with the saved submission. Pass that ID asexternalIdtoprocessSubmissionResult()after verifying a completion event. For a batch task or scorer, return the provider’s batch ID.
WorkflowEvalStore
Persists workflow evaluation state so a run can be resumed. Use the same store and evaluation definition across invocations, including project name, experiment name, and scorer names.
Requires TypeScript SDK v3.37.0 or later. Import from the top-level
braintrust package. The API is marked experimental in the SDK. See Workflow evaluations for a complete OpenAI example.REDIS_URL environment variable. Call await redis.connect() before you start or resume a run:
new WorkflowEvalMemoryStore(): keeps state in memory for single-process runs. State is lost when the process exits.new WorkflowEvalRedisStore({ client, keyPrefix?, ttlMs? }): accepts a connected client fromredis(node-redis),ioredis, or@upstash/redis.keyPrefixdefaults to"braintrust-eval:". Records and progress sets expire after seven days by default. SetttlMsto a positive value longer than the full evaluation and expected resumption delays. Close the client when your application shuts down.
WorkflowEvalStore interface:
read(key: string): Promise<Uint8Array | undefined>: read the saved value, or returnundefinedif absent.write(key: string, value: Uint8Array): Promise<void>: save a value.getOrSet(key: string, value: Uint8Array): Promise<{ value: Uint8Array; created: boolean }>: atomically insert a value only when the key is absent. Return the stored bytes asvalueand whether this call inserted them ascreated.addToSet(key: string, member: string): Promise<void>: atomically add a unique member to a set.getSetSize(key: string): Promise<number>: return the number of unique members.
agentAssertionScorer()
A built-in trace scorer that evaluates an agent through a declarative list of assertions, so you can score tool usage and outputs without writing span-fetching boilerplate. It reads tool call spans from the trace automatically. The score is the fraction of assertions that passed, or 1 when there are no assertions. Score metadata includes each assertion’s name and pass/fail state, plus failure messages for failing assertions.
Tool-call assertions (
calledTool, notCalledTool, toolOrder, usedNoTools, maxToolCalls) read your agent’s tool calls from the tool spans in the eval trace, so your task’s tool calls must be traced (through auto-instrumentation or tools wrapped in traced) for these assertions to find them. The output assertions (equals, notEquals, contains, matches) inspect the task’s return value directly and don’t need this.assert helper passed to your callback exposes these assertion methods:
buildLocalSummary()
Builds an ExperimentSummary from local eval results. The SDK uses this when an eval runs without a remote experiment, such as Eval(..., { noSendLogs: true }).
ExperimentSummary
Parameters:
-
evaluatorDef(EvaluatorDef, required): yourEval()evaluator object plus the SDK-populatedprojectNameandevalNamefields. -
results(EvalResult[], required): the row-level eval results to summarize. -
precomputedScores(ScoreAccumulator, optional): score totals and counts to use instead of deriving them fromresults. Pass it when you ran withreturnResults: false(so per-row results aren’t available) or you’ve already accumulated scores yourself. It maps each score’s name to a runningtotalandcount:
reportFailures()
Prints failing eval rows to the console in the same format the default reporter uses. Use it when you run evals with a custom reporter but still want readable failure output.
void
Parameters:
evaluatorDef(EvaluatorDef, required): evaluator metadata used in error output.failingResults(EvalResult[], required): results whoseerrorfield is set.options({ verbose: boolean; jsonl: boolean }, required):verboseprints full errors.jsonlemits a JSON line containing evaluator name and error strings.
BaseExperiment()
Uses a previous experiment as evaluation data. The previous experiment’s output
becomes the current eval’s expected value. This is useful if you want to test,
for example, a new prompt for regressions.
BaseExperiment
If name is provided, the SDK uses that experiment both as the comparison base for the new experiment and as the read-only dataset for eval rows.
If name is omitted, the SDK derives the base experiment from git metadata:
- During experiment registration, the SDK sends
ancestor_commitswhen no explicitbaseExperimentIdorbaseExperimentNameis set. ancestor_commitscontains up to 1,000 commit hashes from the current branch, starting at the merge base with the default remote base branch and ending atHEAD. For a clean working tree, the merge base is computed fromHEAD^; for a dirty working tree, it is computed fromHEAD.- Braintrust chooses the newest experiment in the same project on the nearest commit in that ancestor list that you can read.
- The chosen experiment ID is stored as the new experiment’s base experiment and then opened as dataset data for
BaseExperiment().
main, master, or develop branch when exactly one exists, otherwise from the remote HEAD branch, and falls back to main if the remote HEAD cannot be read. If no experiment matches the ancestor commits, later base lookup can use a project-configured baseline experiment or the nearest earlier experiment in the same project by creation time. If no base experiment can be found, BaseExperiment() fails.
Experiments
An experiment is a single evaluation run logged to a project. Use these APIs when you want to create an experiment and log rows yourself, instead of lettingEval() manage one for you.
initExperiment()
Initializes an experiment in a project. Use it when you want to log experiment rows manually.
Experiment
Options:
project(string): project name. Required unlessprojectIdis set.projectId(string): project ID. Takes precedence overproject.experiment(string): experiment name. Generated automatically if omitted.description(string): experiment description.dataset(string | Dataset): dataset to associate with the experiment.metadata(Record<string, unknown>): experiment metadata.tags(string[]): experiment tags.baseExperiment(string): base experiment name for comparison.baseExperimentId(string): base experiment ID. Takes precedence overbaseExperiment.update(boolean): continue logging to an existing experiment with the same name.open(boolean): open an existing experiment in read-only mode.isPublic(boolean): whether links are public. Defaults to private.setCurrent(boolean): defaults totrue. Controls whether globallog()uses this experiment.
Datasets
A dataset is a versioned collection of cases you manage in Braintrust and reuse across experiments and evals. UseinitDataset() to create a dataset or open an existing one.
initDataset()
Creates or opens a dataset in a project.
Dataset
Options:
project(string): project name. Required unlessprojectIdis set.projectId(string): project ID. Takes precedence overproject.dataset(string): dataset name. Generated automatically if omitted.description(string): dataset description.metadata(Record<string, unknown>): dataset metadata.version(string): when fetching or iterating rows, read the dataset at this transaction ID. Takes precedence oversnapshotNameandenvironment.snapshotName(string): when fetching or iterating rows, read the dataset version captured by this named snapshot. Takes precedence overenvironment.environment(string): when fetching or iterating rows, read the dataset version assigned to this environment slug.useOutput(boolean): deprecated legacy mode that mapsexpectedtooutputwhen fetching rows.
version, snapshotName, and environment select the committed dataset version used for reads. Calls such as insert() and flush() still write new rows to the dataset.
Prompts and functions
In Braintrust, functions are units of logic you define and version in the UI, then load or invoke from your code. A prompt is a function whose job is to call a model with a templated set of messages. Other functions include scorers, tools, and code you deploy. Load and render a saved prompt withloadPrompt(), or call a function directly with invoke().
loadPrompt()
Loads a saved prompt from Braintrust. Use the returned Prompt object’s build() method to render model parameters, messages, tools, or completion text with runtime variables.
Promise<Prompt>
Options:
projectName(string): project containing the prompt. Required unlessprojectIdoridis set.projectId(string): project ID. Takes precedence overprojectName.slug(string): prompt slug. Required unlessidis set.id(string): prompt ID. Takes precedence over project and slug.version(string): prompt version. Takes precedence overenvironment.environment(string): environment assignment such asproductionorstaging.defaults(Record<string, unknown>): default model, parameter, message, or completion fields used bybuild(). Saved prompt fields override these defaults.noTrace(boolean): iftrue, built prompt metadata is not attached to traces.
version nor environment is set, the SDK loads the latest version and can fall back to the local prompt cache.
build(args, options?) renders the prompt. Parameters:
args(object, required): the template variables. Object keys become variables, and the whole value is available asinput. Rendered with Mustache by default, with non-string values JSON-stringified.options(optional):flavor("chat" | "completion"): defaults to"chat". Use"completion"for completion prompts.messages(Message[]): additional chat messages to append. If the prompt already has a system message, extra system messages are omitted.strict(boolean): validates that referenced template variables are present.templateFormat("mustache" | "nunjucks" | "none"): overrides the prompt’s template format. Nunjucks requires@braintrust/template-nunjucks.noneleaves template strings unrendered.
buildWithAttachments() instead of build() when prompt variables contain Braintrust attachment references that need to be resolved before rendering.
getPromptVersions()
Lists the version IDs for a saved prompt, one for each change recorded when the prompt was created or updated. Use it to find an earlier version to load or pin.
projectId(string, required): the project’s ID.promptId(string, required): the prompt’s ID.
loadPrompt({ version }) to load a specific historical prompt version.
getPromptVersions() logs in internally with the global SDK state. If BRAINTRUST_API_KEY is set, or if a previous SDK call already logged in, you can call it directly. Call login() first when you need to provide apiKey, orgName, appUrl, or a custom fetch, because getPromptVersions() does not accept those options itself.
loadParameters()
Loads a set of parameters you’ve saved in Braintrust, so an eval or application can run with configuration managed in the UI instead of hardcoded values. The example below passes them to Eval().
projectName with slug, or projectId with slug, or id. version takes precedence over environment.
invoke()
Invokes a Braintrust function, prompt, scorer, or tool.
BraintrustStream when stream: true
Identification options. Provide one of these: a function_id, a projectName or projectId plus a slug, or a globalFunction.
function_id(string): function ID.projectName(string): project containing the function.projectId(string): ID of the project containing the function. Can be used instead ofprojectName.slug(string): function slug.globalFunction(string): global function name. See Global Functions.functionType(FunctionType): global function type. Defaults to scorer for global functions.version(string): function version.
input(Input, required): logged as the span input.messages(Message[]): additional OpenAI-style messages for LLM functions.metadata(Record<string, unknown>): logged as span metadata and available to the function.tags(string[]): logged as span tags.parent(Exportable | string): parent span, logger, experiment, or exported parent string.stream(boolean): returns aBraintrustStreamwhentrue.mode(StreamingMode): function streaming mode.strict(boolean): throws when prompt variable names do not match input keys.schema(z.ZodSchema): validates non-streaming output and returns the typed value.
initFunction()
Creates a callable wrapper around a Braintrust function for use as an eval task or scorer.
(input) => Promise<output>
Options:
projectName(string, required): project containing the function.slug(string, required): function slug.version(string): function version.state(BraintrustState): SDK state override.
Attachments
Attachments let you log files or large payloads without storing the full bytes inline in the span. This is useful to circumvent the 6 MB request limit to Braintrust. When you trace AI calls, Braintrust automatically converts base64 attachments in provider messages into uploaded attachments, so you rarely need the APIs below for instrumented calls. Reach for them when you’re attaching binary content to a span yourself.Attachment
Uploads local or in-memory file data and replaces it with an AttachmentReference during logging.
data (string, Blob, or ArrayBuffer), filename, contentType, and optional state.
ExternalAttachment
References a file that already exists in an external object store.
ExternalAttachment.upload() is a no-op because the file already exists externally.
JSONAttachment
Serializes JSON and uploads it as an application/json attachment. Use it for large JSON objects that should be viewable but do not need to be indexed for search.
filename, pretty, and optional state.
ReadonlyAttachment
Reads an already-uploaded attachment from a dataset, experiment, or raw AttachmentReference.
data()→Promise<Blob>: downloads the attachment as aBlob.asBase64Url()→Promise<string>: returns adata:<content-type>;base64,...URL for prompts.metadata()→Promise<AttachmentMetadata>: fetches download URL and upload status.status()→Promise<AttachmentStatus>: fetches current upload status.
Wrappers
Each wrapper returns the same client or module you pass in, with its methods traced, so the return type always matches the argument type. If Braintrust logging is not configured, wrapper calls are effectively no-ops. Initialize a logger before wrapped calls:OpenAI
wrapOpenAI(openai): wraps OpenAI SDK clients and supports current v4, v5, and v6 style clients.wrapOpenAIv4(openai): lower-level OpenAI wrapper used bywrapOpenAI(). UsewrapOpenAI()unless you know you need this compatibility entry point.
Anthropic
wrapAnthropic() traces messages.create, beta messages.create, and beta tool runner calls when available.
Vercel AI SDK
wrapAISDK(ai, options?): wraps AI SDK namespace functions such asgenerateText,streamText,generateObject,streamObject,embed,embedMany,rerank, and agent constructors.wrapAgentClass(AgentClass, options?): wraps an AI SDK agent class directly.
wrapAISDK() accepts denyOutputPaths?: string[] to omit selected output paths from logged output. Pass the @ai-sdk/code-mode module to wrapAISDK() to trace experimental_runCodeMode() calls.
startAISDKBatchTrace({ batch, requests, startTime? })→Promise<string | undefined>: records the requests of an AI SDK text batch after you submit it withexperimental_startBatch(), and returns a context string to store with the batch. Returnsundefinedif the batch or requests aren’t supported or AI SDK instrumentation is disabled.startTimeis a Unix timestamp in seconds and defaults to the batch’screatedAt, or the current time. Doesn’t call the provider.completeAISDKBatchTrace({ batch, requests, status, results, context?, endTime?, denyOutputPaths? })→Promise<void>: records collected batch results and ends the batch spans. Pass the context fromstartAISDKBatchTrace(), including from another process.endTimeis a Unix timestamp in seconds.
Agent SDKs
wrapClaudeAgentSDK(sdk): wraps the Claude Agent SDK module, includingquery,tool, andcreateSdkMcpServerintegration points.wrapOpenRouterAgent(agent): wraps@openrouter/agentclients.wrapGoogleADK(adkModule): wraps Google ADK modules, including runner, agent, and tool execution methods.wrapLiveKit(module): wraps the@livekit/agentsmodule (v1.9.x). Sessions created with the returned module’svoice.AgentSessionare traced. See LiveKit Agents.wrapMCPServer(server): traces the requests and notifications that anMcpServeror low-levelServerfrom@modelcontextprotocol/sdkor@modelcontextprotocol/serverhandles, and returns the same instance. You can wrap a server before or after you connect it to a transport. Requires v3.42.0 or later. See MCP SDK.
Model provider SDKs
wrapGoogleGenAI(): wraps the entire@google/genaipackage export (similar to the AI SDK wrapper). Tracesmodels.generateContent,models.generateContentStream, andmodels.embedContent.wrapOpenRouter(): pass an OpenRouter client. Traces chat, responses, embeddings, rerank, andcallModel()where available.wrapMistral(): pass a Mistral client. Traces chat, FIM (fill-in-the-middle), agents, and embeddings.wrapCohere(): pass a Cohere client. Traces chat, chat streaming, embed, and rerank.wrapGroq(): pass a Groq client. Traces chat completions and embeddings.wrapHuggingFace(): pass a Hugging Face Inference module or client. Traces chat completion, text generation, streaming variants, and feature extraction.wrapWorkersAI(): pass a Cloudflare Workers AI binding, such asenv.AI. Tracesrun()calls to the Clef decision models and the Clairic extraction model. Other models, and streaming or raw-response calls, pass through unchanged. See Cloudflare Workers AI.
Testing
Run Braintrust evaluations inside your test runner, so eval cases live alongside your unit tests and your suite fails when scores regress.wrapVitest()
Wraps Vitest test APIs so tests can create Braintrust experiment rows with inputs, expected values, scorer results, and pass/fail status.
test, it, expect, describe, lifecycle hooks, and Braintrust helpers)
wrapVitest() returns wrapped test, it, expect, describe, lifecycle hooks, logOutputs, logFeedback, getCurrentSpan, and flushExperiment().
initNodeTestSuite()
Creates a Braintrust-backed suite for Node’s built-in node:test runner.
eval() and flush()
The suite exposes eval(config, fn) and flush(). If you pass Node’s after hook, flush() is registered automatically.
Configuration
Most configuration is passed per call, such asapiKey and projectName on initLogger(). You can also authenticate once with login(), or set credentials and deployment options through environment variables.
login()
Authenticates the SDK to Braintrust and stores the credentials in its global state, where every later SDK call reads them. Most SDK APIs log in automatically when they first need credentials.
Promise<BraintrustState>
Options (all optional):
apiKey(string): API key. Defaults toBRAINTRUST_API_KEY.appUrl(string): Braintrust app URL. Defaults tohttps://www.braintrust.dev.orgName(string): organization name, useful when credentials can access multiple orgs.fetch(typeof fetch): custom fetch implementation for SDK requests.forceLogin(boolean): log in again even if the SDK already has credentials.noExitFlush(boolean): disables the process exit handler that flushes pending writes.onFlushError((error) => void): handles errors from the background flusher.disableSpanCache(boolean): disables the local span cache used by eval scorers.debugLogLevel("error" | "warn" | "info" | "debug" | false): enables SDK troubleshooting output. Setfalseto also silence the SDK’s default warnings.
login() yourself. When BRAINTRUST_API_KEY is set, APIs like initLogger(), initExperiment(), initDataset(), loadPrompt(), and loadParameters() authenticate automatically using it. Call login() yourself when you want to authenticate with something other than that environment variable, such as a specific API key, organization, or custom fetch. Logging in stores those credentials in the SDK’s global state, so every later SDK call uses them, including APIs like getPromptVersions() that take no auth options of their own.
Environment variables
BRAINTRUST_API_KEY: API key used to authenticate. Same as theapiKeyoption.BRAINTRUST_API_URL: Braintrust API URL. Set this for self-hosted or data plane deployments.BRAINTRUST_APP_URL: Braintrust app URL, used for permalinks. Defaults tohttps://www.braintrust.dev. Same as theappUrloption.BRAINTRUST_APP_PUBLIC_URL: public Braintrust app URL used when generating permalinks. Defaults to the app URL.BRAINTRUST_PROXY_URL: AI proxy URL. Overrides the proxy URL returned for your organization.BRAINTRUST_ORG_NAME: organization name, useful when credentials can access multiple orgs. Same as theorgNameoption.BRAINTRUST_DISABLE_INSTRUMENTATION: comma-separated list of integrations to skip during auto-instrumentation, for exampleopenai,anthropic.BRAINTRUST_CAPTURE_ATTACHMENTS: set to1ortrueto upload files from OpenAI image and audio calls, base64 image and file inputs to OpenAI Chat Completions calls, and images and resource blobs in MCP server results, as attachments. Off by default, which records the file contents as<omitted>. In v3.41.0 and later, it doesn’t record LiveKit Agents call audio. Use the two variables that follow instead.BRAINTRUST_CAPTURE_USER_AUDIO_ATTACHMENTS: set to1ortrueto record the caller’s audio in LiveKit Agents sessions as attachments. Off by default. Requires v3.41.0 or later.BRAINTRUST_CAPTURE_AGENT_AUDIO_ATTACHMENTS: set to1ortrueto record the agent’s audio in LiveKit Agents sessions as attachments. Off by default. Requires v3.41.0 or later.BRAINTRUST_DEBUG_LOG_LEVEL: SDK troubleshooting output:error,warn,info, ordebug. When unset, the SDK prints only warnings that need your attention, such as a client it can’t wrap. Same as thedebugLogLeveloption.BRAINTRUST_VERBOSE: set to a truthy value to enable verbose evaluation logging.BRAINTRUST_ENVIRONMENT_TYPE: environment type for span-origin provenance (for example,ciorserver). Detected automatically from common CI and server runtimes when unset.BRAINTRUST_ENVIRONMENT_NAME: environment name for span-origin provenance (for example,github_actionsoraws_lambda). Detected automatically when unset.
BRAINTRUST_SYNC_FLUSH: set to a truthy value to flush logs synchronously instead of in the background.BRAINTRUST_FLUSH_DELAY_MS: maximum time, in milliseconds, that a logged row waits so nearby rows can be sent in one upload request. A full batch ofBRAINTRUST_DEFAULT_BATCH_SIZErows is sent right away. Defaults to1000in long-lived Node.js processes, and to0(send right away) in other runtimes and in serverless environments such as AWS Lambda, Vercel, Netlify, Google Cloud Run and Cloud Functions, App Engine standard, and Azure Functions. Requires v3.40.0 or later.BRAINTRUST_DEFAULT_BATCH_SIZE: number of log events sent per upload request.BRAINTRUST_MAX_REQUEST_SIZE: override the maximum upload request size, in bytes.BRAINTRUST_NUM_RETRIES: number of times to retry a failed upload.BRAINTRUST_MAX_GENERATOR_ITEMS: maximum number of items pulled from a generator when tracing. Defaults to1000;0disables the limit.
BRAINTRUST_CACHE_LOCATION: where prompt and parameters caches are stored:mixed,memory,disk, ornone. Defaults tomixed.BRAINTRUST_PROMPT_CACHE_DIR: directory for the on-disk prompt cache. Defaults to~/.braintrust/prompt_cache.BRAINTRUST_PROMPT_CACHE_MEMORY_MAX: maximum number of entries in the in-memory prompt cache.BRAINTRUST_PROMPT_CACHE_DISK_MAX: maximum number of entries in the on-disk prompt cache.BRAINTRUST_PARAMETERS_CACHE_DIR: directory for the on-disk parameters cache. Defaults to~/.braintrust/parameters_cache.BRAINTRUST_PARAMETERS_CACHE_MEMORY_MAX: maximum number of entries in the in-memory parameters cache.BRAINTRUST_PARAMETERS_CACHE_DISK_MAX: maximum number of entries in the on-disk parameters cache.