BAML 0.21.0
SQL queries for recorded runs, per-call tracing, regex, structural map keys, Bedrock streaming, and the migrations needed to upgrade.
Thanks to everyone who contributed!
Special thanks to BenSpex for the nullable JSON, bigint field-assignment, host-float, and prompt-rendering reports.
This canary covers changes since 0.20.1. Update the toolchain and bridge packages together, run baml generate, and rebuild applications and packed executables. Update the wrapper before using BAML_TOOLCHAIN, reinstall the editor with baml ide install, and refresh agent guidance with baml agent install. Read the breaking changes before upgrading.
Query recorded BAML runs with SQL
BAML records runs from the CLI, generated SDKs, and packed executables in a shared format. baml query lets you inspect recordings without adding application logging. The final query model has four relations: processes, spans, span_announcements, and profiler. Profiler rows become available when the process finishes.
baml run main
baml query --local --schema
baml query --local 'SELECT * FROM spans LIMIT 10'
baml query --local 'SELECT * FROM profiler LIMIT 10' --format json
baml playground
Use --local to select recordings on this machine. The playground includes a query interface.
Choose what each function records
Calls accept $trace options. trace.span() records outputs and errors by default; input capture requires inputs = true. trace.timing() records timing without value captures. trace.hidden() hides the call. Capture requests apply to that invocation and combine with the recording policy: false does not veto a capture requested elsewhere.
function increment(value: int) -> int throws never { value + 1 }
function traced() -> int throws never {
increment(1, $trace = trace.span(inputs = true))
}
/// baml:$trace=trace.hidden
function private_helper(value: int) -> int throws never { value }
Reservations provide a one-use span identity. Declaration and call-site options are resolved once when execution starts.
Features
Context follows calls across BAML and host code
Attach distinct_id and metadata to an invocation. Context is immutable, inherited by descendants, and visible in queries. Metadata patches merge keys; a null value removes a key. A child's patch does not mutate its parent's context. CLI runs accept inline JSON, @file, or standard input through --context.
function with_context() -> int throws never {
increment(1, $trace = trace.context(
distinct_id = "customer-123",
metadata = { "request_id": "abc" },
))
}
baml run main --context '{"distinct_id":"customer-123","metadata":{"request_id":"abc"}}'
Structured log.info entries can also carry data and event_name.
Declare a trace policy with a function
A baml:$trace directive can name a policy function. It receives the resolved application arguments plus trace.Settings, returns trace.Options, and must declare throws never. The compiler validates the signature. Hooks run before the body and are skipped when telemetry is off.
function trace_policy(value: int, settings: trace.Settings) -> trace.Options throws never {
trace.span(inputs = true)
}
/// baml:$trace=trace_policy
function selected(value: int) -> int throws never { value }
Instrument Python and Node callbacks
Generated SDKs expose trace.instrument for host functions. Instrumented callbacks record one host span when BAML dispatches them. Context, cancellation, and deadlines follow supported async work and calls back into BAML. Python instrumentation defaults carry into child BAML calls; an explicit invocation trace override can replace them.
from baml_sdk import trace
@trace.instrument
def add_one(value: int) -> int:
return value + 1
assert add_one(7) == 8
import { trace } from './baml_sdk/index.js';
const addOne = trace.instrument((value: number) => value + 1);
addOne(7);
Host code can read trace.current_context() and trace.current_cancel_token(). Browser and Worker code generation does not expose host instrumentation. Experimental dynamic calls live under baml_sdk.experimental in Python and ./baml_sdk/experimental.js in TypeScript.
Inspect network activity and LLM cost
Recordings include HTTP activity, including provider requests and streamed responses. Network events are queryable through span data. HTTP bodies participate in recordings; choose the runtime telemetry policy deliberately when recording sensitive requests.
baml query --local --schema
baml query --local 'SELECT * FROM spans LIMIT 20' --format json
Provider usage comes from recorded responses
Usage and pricing are derived from captured provider responses. Input-token totals include cache reads and writes consistently across providers. Streamed Bedrock metadata and OpenAI cache-write usage are retained.
baml query --local 'SELECT * FROM spans LIMIT 20' --format json
Query types, host captures, and garbage collection
Query results represent BAML types as structured JSON. Requested host captures preserve useful type identity for common Python and Node values. Garbage collections appear as baml.gc spans, with profiler counters and pause context.
baml query --local 'SELECT * FROM profiler LIMIT 20' --format json
Less noise from standard-library helpers
Small string and iterator helpers use trace.hidden so recordings emphasize application work. Hidden helpers still execute normally. Apply the same policy to your own helpers:
/// baml:$trace=trace.hidden
function normalize(text: string) -> string throws never { text }
(#5168)
Share recordings through Boundary
Native CLI and SDK hosts can use saved Boundary credentials. baml query selects cloud recordings when Boundary configuration is available; --local explicitly selects local recordings. The default API endpoint is https://api.prod.bcs.boundaryml.com. Set BOUNDARY_API_URL or boundary.api_url for another endpoint. BOUNDARY_PROJECT overrides the configured project.
[boundary]
project = "my-org/my-project"
baml auth login
baml auth status
baml query 'SELECT * FROM spans LIMIT 10'
baml query --local 'SELECT * FROM spans LIMIT 10'
Embed a publisher-owned telemetry credential
Opt in while packing an executable or generating an SDK. The project and environment must already exist. [pack].telemetry_environment and [bridge].telemetry_environment can supply the environment default; configuring an environment alone does not mint a credential.
baml pack main --embed-telemetry --telemetry-environment=staging
baml generate --embed-telemetry --telemetry-environment=staging
An embedded credential lets the publisher control the recording destination. Initial cloud failure aborts caller-configured cloud execution by default. Newly built artifacts with embedded telemetry default to warning and continuing. An explicit failure policy takes precedence.
Regular expressions
baml.regex.new compiles a regular expression. Use it to search, collect matches and captures, split text, or replace matches. Invalid patterns produce a typed error.
function numbers(text: string) -> string[] {
let rx = baml.regex.new("[0-9]+");
rx.match_all(text).map((m) -> { m.text })
}
(#4981)
Construct a class through its alias
Class aliases can be constructor heads. The value still has the underlying class's identity.
class Point { x: int, y: int }
type Position = Point;
function origin() -> Point throws never { Position { x: 0, y: 0 } }
(#4966)
Structural serialization, equality, and map keys
Classes and enums have structural ToString, ToJson, FromJson, Equals, and Hash implementations. Explicit implementations can override the defaults. A custom equality implementation needs a compatible hash when used for map keys. Maps retain insertion order and can use hashable BAML values as keys. JSON objects and SDK map bindings still require string keys; convert other keys at those boundaries.
class Point { x: int, y: int }
enum Status { Open, Closed }
function round_trip(point: Point) -> bool {
point.eq(Point.from_json(point.to_json()))
}
function lookup() -> string throws never {
let names: map<Status, string> = { Status.Open: "open", Status.Closed: "closed" };
names[Status.Open]
}
Hashes are non-cryptographic and are not a stable persistence format across releases.
(#5105)
Stream from Amazon Bedrock
Bedrock clients support the standard BAML streaming interface. The runtime decodes Bedrock's binary event stream.
function bedrock_answer(question: string) -> string {
client: aws.BedrockClient.new(
model = "anthropic.claude-haiku-4-5-20251001-v1:0",
region = "us-east-1",
)
prompt: `${question}`
}
function final_answer(question: string) -> string {
let stream = bedrock_answer@stream(question);
stream.final()
}
(#5047)
Coerce parsed JSON into a tool argument
baml.sap.coerce<T> applies schema-aligned parsing to an already-parsed JSON value. Reflected tools use this to accept classes, enums, and lists from model-produced arguments. It follows the aliases and skipped fields in the tool schema. Values already of the parameter type pass through unchanged.
class Zone { low: int, high: int }
function decode_zone() -> Zone {
baml.sap.coerce<Zone>({ "low": 1, "high": 2 })
}
(#5182)
Run an expression or a standalone file
baml run -e evaluates an expression. --file selects a standalone BAML file. Compilation diagnoses the selected file without creating or overwriting a project __expr__ source file.
baml run -e '1 + 2'
baml run --file ./repro.baml
(#4400)
Smaller generated SDK payloads
Generated SDKs compress their embedded bytecode payload instead of emitting the uncompressed program as source data. Regenerate with 0.21.0 to receive the new carrier.
baml generate
(#5000)
Breaking changes
Map keys are expressions
A bare identifier in a map literal now refers to a variable. Quote it when you intend a string key. Shorthand {name} still means {"name": name}. Class field names are unchanged.
Before:
let options = { temperature: 0.5 };
After:
let options = { "temperature": 0.5 };
Update configuration maps, prompt metadata, JSON literals, and trace metadata throughout your project.
(#5144)
Function return unions bind to the return type
(A) -> B | C now means (A) -> (B | C). It previously meant ((A) -> B) | C. Parenthesize a function type when it is itself a union member.
Ambiguous unions of function types now require explicit grouping. Before:
type Handler = (int) -> int throws never | (string) -> string throws never;
After:
type Handler = ((int) -> int throws never) | ((string) -> string throws never);
Audit existing function-type aliases. If the intention was a callback returning a union, the unparenthesized spelling now has that meaning.
(#4997)
Multiline backtick whitespace is normalized before escapes
Multiline backticks now remove all body-boundary ASCII layout whitespace, including extra blank lines and trailing spaces. ASCII-whitespace-only interior lines become empty. Common indentation is removed before escape decoding. Single-line whitespace remains unchanged, and authored escapes remain content. Non-breaking spaces at boundaries remain content; a shared Unicode indentation prefix can still be dedented.
Before, the blank line before the closing delimiter left a trailing newline:
function fragment() -> string throws never {
`
hello
`
}
After, the same literal returns "hello". To preserve the previous newline explicitly:
function fragment() -> string throws never { "hello\n" }
Compare rendered prompts after upgrading, especially fragments with blank boundary lines or body-final spaces. If indentation is content, use a quoted string with explicit escapes, such as " hostname\n". This change does not restore removed hash literals or add a formatter migration for them.
(#4914)
Spawn controls use Limit and CancelToken directly
baml.spawn.options, Params, and TaskGroup are removed. Pass Limit, CancelToken, and other Modifier values to spawn with. Plan replaces middleware's old Params representation. Concurrency limits and shared cancellation are separate controls; cancelling a limiter is no longer the cancellation API.
Before:
let group = baml.spawn.TaskGroup.new(2);
let cancel = baml.spawn.CancelToken.new();
let task = spawn with baml.spawn.options(group = group, cancel = cancel) { increment(1) };
After:
let limit = baml.spawn.Limit.new(2);
let cancel = baml.spawn.CancelToken.new();
let task = spawn with limit, cancel { increment(1) };
let result = await task;
Rewrite custom spawn middleware against Modifier/Plan. Use an explicit cancellation token wherever you previously relied on TaskGroup.cancel. Root supplies the new root execution control.
(#5037)
HTTP and LLM timeouts use Duration values
Native HTTP requests and LLM attempts now default to a five-minute total timeout and a ten-second connect timeout. The 13 HTTP-based provider clients accept timeout: Duration | ai.LlmTimeoutOptions | null. Zero or negative transport durations disable that limit. Non-streaming calls ignore token-gap limits. Browser transport enforces total timeouts but not connect timeouts; the playground WASM transport does not enforce these transport limits.
Before:
client Chat = openai.ChatClient.new(model = "gpt-4o-mini", request_timeout_ms = 120000);
After:
client Chat = openai.ChatClient.new(
model = "gpt-4o-mini",
timeout = ai.LlmTimeoutOptions {
timeout: baml.time.Duration.from_seconds(120),
connect_timeout: baml.time.Duration.from_seconds(10),
first_token_timeout: baml.time.Duration.from_seconds(30),
stream_token_timeout: baml.time.Duration.from_seconds(10),
},
);
Remove request_timeout_ms and time_to_first_token_timeout_ms. For outgoing requests, use Request.set_timeout(Duration) or the request operation's Duration override. Custom client authors must configure the request instead of passing request_timeout_ms to ai.wire.send_as or send_as_with_wire. First-token time starts at dispatch and ends on non-empty content. Stream-token time measures provider gaps after content starts and excludes time spent by a slow consumer.
Replace baml.http.fetch_sse(request, ...) with baml.http.send_sse(request, ...); remove first_event_timeout and implement the desired provider content timeout. HTTP server handlers now receive baml.http.ServerRequest, while Request is outgoing-only. Replace a handler's parameter type accordingly. Replace reads of baml.errors.Timeout.duration_ms with duration (an optional Duration), and inspect timeout_type when distinguishing limits. Exhaustive matches on ai.stream events must handle ai.stream.ContentSeen. Audit workloads that previously depended on unlimited requests and explicitly configure their limits.
(#5054)
Prompt cache markers have provider defaults
Use ${cache()} instead of role or message metadata to request prompt caching. It emits the provider's default cache marker for Anthropic, Claude on Vertex, Claude/Nova on Bedrock, and supported OpenAI/Azure model families. Older OpenAI families and unsupported clients get no explicit marker. Non-null custom marker arguments are provider-specific and are not validated by BAML.
Before:
let message = ai.events.UserMessage.of(
[reference],
metadata = { "cache_control": { "type": "ephemeral" } },
);
After:
prompt: `${role("system")} ${reference} ${cache()}`
Move caching metadata from ai.events.UserMessage.metadata or ai.PromptMessage.metadata to cache markers in the prompt. A journal user turn no longer carries a cache marker through metadata; restructure that prefix as prompt content when it needs an explicit marker. role(..., metadata = ...) is newly supported in 0.21.0 for other provider-specific uses, but built-in clients do not use it as a cache channel. Check your rendered provider request after migrating, especially custom cache durations and Bedrock cache points. Azure caching depends on the actual model, not the deployment name; PTUM models do not get automatic markers.
Azure requires an explicit deployment endpoint
The azure/model shorthand is removed. A model name no longer doubles as a deployment ID. Supply deployment_id plus resource_name (or AZURE_OPENAI_ENDPOINT), or provide a full deployment base_url. AZURE_OPENAI_API_KEY is read when making a request. The default API version is 2024-10-21.
Before:
client Azure = "azure/my-deployment";
After:
client Azure = openai.AzureClient.new(
model = "gpt-5-mini",
deployment_id = "my-chat",
resource_name = "my-resource",
);
Update every Azure client, including clients that relied on model to infer the deployment. Set model to the real model for capability and cache decisions. This client does not implement Azure's newer v1 endpoint protocol.
(#5133)
SDK invocation options are separate from application arguments
Regenerate every SDK with 0.21.0, update its bridge package to the matching release, and rebuild native consumers. The native callback/invocation ABI changed; do not mix old generated artifacts or native bindings with the new runtime. Callbacks now inherit cancellation and an absolute deadline through calls back into BAML. Cleanup remains owned until the callback physically exits.
Python replaces _ctx with _baml; generic bindings remain in _types.
Before:
result = await increment_async(1, _ctx=old_context)
After:
from baml_sdk import increment_async, trace
result = await increment_async(1, _baml={
"timeout_ms": 1000,
"trace": trace.context(metadata={"request_id": "abc"}),
})
TypeScript replaces $ctx with $baml; generic bindings remain in $types.
Before:
await increment_async(1, { $ctx: oldContext });
After:
import { increment_async } from './baml_sdk/index.js';
const controller = new AbortController();
await increment_async(1, { $baml: { timeoutMs: 1000, signal: controller.signal } });
Translate old context state into trace/cancel/timeout options; the old context object itself is not accepted. C# moves standalone cancellation into BamlOptions.CancellationToken, with a BAML token in Cancel. Java replaces context overloads with BamlOptions; C++, Rust, Go, and Swift expose their generated options forms. Go keeps its leading context.Context; Kotlin forwards coroutine cancellation and Swift observes awaiting-task cancellation. In millisecond-based options, zero expires immediately; Go's zero Duration adds no deadline. Unknown options and invalid timeouts are rejected before execution.
Legacy host tracing and flush APIs are removed
Replace bridge-owned span/context managers with the generated SDK's trace facade. Remove legacy flush_events calls and use bridge shutdown for process cleanup. Low-level Python Runtime.call_sync no longer accepts its legacy context argument.
Before:
runtime.call_sync(encoded_args, None)
After:
runtime.call_sync(encoded_args)
For host function tracing, use @trace.instrument in Python or trace.instrument(fn) in Node. Re-run code generation before changing imports. Do not retain removed context-manager objects in application state.
(#5059)
Provider failures have distinct error types
ai.errors.InvalidResponse means a malformed provider envelope. ai.errors.ProviderError means an explicit provider-reported error. ParseFailed is reserved for model output that does not fit the requested schema. Stream failures retain their message and output. Update catches and exhaustive error matches that previously treated all of these as parse failures.
Before:
cached_answer(question, question) catch (error) {
ai.errors.ParseFailed => "invalid output or response",
}
After:
cached_answer(question, question) catch (error) {
ai.errors.ParseFailed => "invalid model output",
ai.errors.InvalidResponse => "malformed provider response",
ai.errors.ProviderError => "provider reported an error",
}
In Python, inspect BamlError.value as the generated typed error value; generated error values are data models rather than Python exception classes. Continue catching the bridge exception at the host boundary. Regenerate SDKs to receive the new types.
(#5116)
Files are read when media values are constructed
Image, Audio, Video, and Pdf values hold URL or base64 content. from_file reads immediately and can throw Io | InvalidArgument; it no longer declares throws never. Host SDK constructors also read the file before an awaited call. The file() accessor is removed. name() gives the basename, not the original path.
Before:
function load(path: string) -> image throws never {
baml.media.Image.from_file(path, null)
}
After:
function load(path: string) -> image throws baml.errors.Io | baml.errors.InvalidArgument {
baml.media.Image.from_file(path, null)
}
Handle construction errors at the load site and retain the original path separately if your application needs it. Replace display uses of file() with name(); do not treat the result as a filesystem path. If you already have the content, use from_file_content(path, base64, mime_type) without rereading the file. Rebuild callers whose error contracts assumed lazy file loading.
(#5180)
Environment variables and cache paths are consolidated
Update shell profiles, CI, container configuration, and bridge loaders. Removed names have no compatibility aliases. Empty values mean unset; invalid booleans/enums now produce errors. Booleans accept 1/true/yes/on and 0/false/no/off, case-insensitively.
Before:
BAML_VERSION=0.21.0 baml check
BAML_NO_BYTECODE_CACHE=1 baml check
BAML_LIBRARY_PATH=/path/to/libbridge_cffi.dylib ./my-app
After:
BAML_TOOLCHAIN=0.21.0 baml check
BAML_BUILD_CACHE=false baml check
BAML_BRIDGE_PATH=/path/to/libbridge_cffi.dylib ./my-app
| Previous setting | Required action in 0.21.0 |
|---|---|
BAML_VERSION | Use BAML_TOOLCHAIN; update the wrapper so it recognizes this name. |
BAML_NO_BYTECODE_CACHE | Use BAML_BUILD_CACHE=false. |
BAML_CACHE_DIR and per-feature recording/cache directories | Set one BAML_HOME. Build cache is now build/cache; downloaded bridges are bridges/<version>/<target> under it. Expect one cold build. |
BAML_LIBRARY_PATH, BAML_RUNTIME_PATH and language-specific native-library overrides | Use BAML_BRIDGE_PATH, an exact file with no fallback. |
BAML_LIBRARY_DISABLE_DOWNLOAD, BAML_RUNTIME_DISABLE_DOWNLOAD | Use BAML_BRIDGE_DISABLE_DOWNLOAD=true. |
BAML_LIBRARY_DOWNLOAD_BASE, BAML_RUNTIME_MANIFEST_BASE_URL | Use BAML_MANIFEST_BASE_URL; it must serve release manifests, not just raw library assets. |
| Old runtime telemetry level/body-recording switches | Use `BAML_TELEMETRY=off |
BAML_TELEMETRY_DISABLED, BAML_TELEMETRY=0 for CLI analytics | Use DO_NOT_TRACK=1 or baml telemetry disable. Runtime telemetry and CLI analytics are separate controls. |
BAML_TEST_TIMEOUT_MS, shutdown/concurrency environment overrides | Use --test-timeout, --shutdown-timeout, and --max-concurrency on the applicable CLI command. |
BAML_COLOR, BAML_HYPERLINKS, BAML_OUTPUT_PRESET, BAML_DIAGNOSTIC_FORMAT, BAML_AGENT_SKILL_CHECK | Use --color, --hyperlinks, --output-preset, --diagnostic-format, and --agent-skill-check. |
SECRET_ACCESS_KEY AWS fallback | Use the supported AWS credential variables/provider chain. |
Rust bridge acquisition uses release manifests and no longer searches system library paths. Set BAML_BRIDGE_PATH for an offline local library. Packing and Rust loading reject manifests for a different version. Custom stdlib-directory lookup is removed. See the release's environment variable reference for the complete supported set, including renamed development knobs.
(#5131)
Boundary login is stored under BAML_HOME
Native CLI, SDK, and packed hosts share a plaintext JSON login file scoped to the canonical API endpoint. It is stored with owner-only permissions at <BAML_HOME>/login/cache/<sha256-of-canonical-endpoint>.json. Temporary access tokens remain in memory. Login no longer depends on an OS keychain, and existing keychain credentials are not migrated.
Before: authentication relied on an existing OS-keychain entry.
After:
baml auth login
baml auth status
Log in again after upgrading. If you change BAML_HOME or the API endpoint, authenticate for that location/endpoint too. Update credential backup and cleanup rules for the new location. baml auth logout attempts revocation and removes the endpoint's login file.
(#5158)
Bug fixes
- Package-owned types retain their identity during linking and runtime mounting. Generic aliases resolve constructors and static methods correctly. Optional structural conversions and generic implementation method calls no longer fail at runtime. Invalid mounted enum identities and unsafe interface object shapes are rejected. If compilation now rejects an interface object, adjust it to the compiler's object-safety requirements. (#4966, #5050)
- Cancellation unwinds the task so
defercleanup runs. Another await re-raises a caught cancellation. Rethrown errors retain their context. Self-satisfying blanket implementations no longer hang the compiler. Compiler intrinsics cannot be stored as function values; replace such values with a lambda that calls the intrinsic. (#5037) - Bigint subtraction works when assigned directly to a class field, and bigint operators work across spawned execution. The field-assignment reproduction from issue #4813 passes on 0.21.0. This does not claim a fix for the issue's separate runtime-error exit-status report. (#4917, #4920)
- A host callable returning an integral JavaScript number can satisfy a BAML
floatresult, including float fields inside classes, instead of panicking at the bridge contract check. Update the bridge and regenerate the SDK together. (#4918) - Nested map literals type-check in nullable
baml.json.jsonpositions. Expected types flow into??fallback literals and generic constructors. An existing array value is not silently retyped. Quote string map keys under the new map-literal rules. (#4924, #5003, #5015) - The compiler warns about constant conditions and unreachable statements. Catch-all match bindings receive the type left by earlier arms. The right side of
&&/||sees facts from the left for uncaptured locals; reassignment invalidates those facts. Catch analysis sees errors from function-valued generic arguments. Fields and captured locals are not covered by this narrowing, and the shared-effectrun2(a, b)inference gap remains. (#4995, #5178) - Streaming waits for more text when a partial does not yet parse; the final parse still reports a failure. Fenced JSON parses correctly. Enum descriptions work in scalar and array schema-aligned parsing. (#5060, #5077)
- Parsing a class with a skipped field fills that field with its type's empty value instead of failing during heap landing. Nullable skipped fields become null and ordinary scalar/container fields receive their empty defaults. A skipped field with no empty value, such as media, a function, or a self-containing class, still fails; choose a nullable or otherwise defaultable skipped type. (#5113)
- Retry and fallback clients advance on timeout before the first content delta. Cancellation is not retried. Attempt recordings use distinct
ai.clients.retry_attemptandai.clients.fallback_attemptchild spans. (#5085, #5097) - For text prompts without native tools, OpenAI Responses requests select commentary-first output to avoid empty or hallucinated final responses. Normal native-tool and final-output behavior is retained. (#5128)
- JSON schemas include class, field, and enum-variant descriptions. Reflected tool schemas hoist definitions so nested references resolve correctly. (#5129)
reflect.call_anyaccepts literal unions and enum-variant parameters using the same membership rules asis. Schema and reflection ordering is stable.Agent.rundelivers the unread events of a turn before rethrowing an ordinary run error, soon_eventdoes not lose the final erroring turn. (#5182)- Profiler rows distinguish scheduled work from execution and retain outcomes. SDK shutdown finishes profiler recording; repeated shutdown is safe. Host-exit outcomes can remain unknown when the host does not provide a final result. (#5064, #5161)
- A Python stream keeps its function span, creation context, cancellation, and deadline across pulls and finalization. EOF parsing is cached and the final result settles once, for synchronous and asynchronous consumers. (#5181)
COUNThandles nested missing/null fields correctly. OpenAI cache-write costs use the matching usage fields. Costs accumulate as integer nanodollars and are still presented in dollars. Unknown or overflowing amounts become null instead of an inaccurate number; large token counts retain integer precision. (#5065, #5150, #5154)- Initial authorization failures follow the configured failure policy, and short-lived programs flush diagnostics. Later recording failures do not crash an already authorized application. Transient authorization retries honor bounded
Retry-Afterdelays. Cloud queries return available rows and warn for incomplete results; structured output retains the outcome. SQL and credential errors are reported without silently switching to local recordings. (#5138, #5153, #5157) - Disk recording failure disables recording instead of crashing the program. Generic type arguments are captured only when effective input capture is enabled. (#5066, #5095, #5145)
- Large values and media use shared recording blobs and bounded rendering/upload paths. Snapshot version 4 represents non-string-key maps as tagged pairs; string-key maps remain objects, and older version-3 snapshots remain readable. Media upload accounting uses logical content bytes rather than its JSON encoding size. (#5102, #5156, #5183)
- The garbage collector accounts for native payload buffers. Moving values no longer clones those payloads merely to update their accounting. (#5155)
- Encoding a non-proxy Java value does not initialize the native library unnecessarily. The C++ public codec builds with Windows headers that define
min/maxmacros. Update the bridge package and regenerate/rebuild the affected SDK. (#5126, #4978) - Inlay hints are quieter and clickable. The homepage playground uses the matching language runtime for diagnostics. The TypeScript and introductory guides use the current language APIs. Run
baml ide installto update the editor integration. (#4960, #4955, #4952) baml runandbaml testdefault to info logging; an explicit off level is respected. Select off explicitly if you need quiet application logs. The embedded agent skill uses the current SDK layout andprompt_testnaming. Reinstall it withbaml agent installafter upgrading. (#4930, #5040, #5052)