Typed LLM functions replace repeated provider glue
BAML puts model functions in their own source files. A function declares input and output types, chooses a client, and contains the prompt. The generator then produces application code and matching types for the selected host language. This arrangement keeps a resume extractor's schema, prompt, provider choice, and result type together instead of copying them between an SDK call, a validator, and handwritten parsing code.
The current README calls BAML a programming language for agents and lists clients for TypeScript, Python, Go, C#, Java, and other languages. The repository is correspondingly large: our clone contained about 2,211,842 lines across 10,891 files. Adopting BAML is an architectural choice, especially in a polyglot team. Generated code belongs in the workflow, and the docs warn developers not to edit the generated baml_client directory.
Schema-aligned parsing accepts messy model output
BAML's distinctive behavior is schema-aligned parsing. It tries to turn imperfect model text into the declared result instead of requiring exact JSON syntax. That can be useful when a model adds commentary, misses quotes, or emits a value in a nearby representation. Types remain available to generated clients, while prompts can insert the output format through ctx.output_format.
Permissive repair needs its own correctness tests. Issue #4590 reports BAML 0.17 dropping array elements it could not coerce and returning success without a warning. Issue #4589 describes a nested optional class value being replaced with null. Those are open reports rather than settled conclusions, but they identify the failure mode to test: a syntactically valid result may still have lost input-derived data.
Provider switching stays inside the function definition
A short client string can select an OpenAI or Anthropic model, while named clients expose base URLs, headers, temperature, API keys, retries, and routing. OpenAI-compatible local endpoints fit the same model. Runtime registries can select another client for a request, which is useful for controlled traffic splits or failover without duplicating the function's output schema.
That portability does not make providers identical. BAML still sends provider-specific requests, and issue #4585 reports that the OpenAI-generic message content shape changed between 0.15 and 0.17. Software that signs, records, or byte-compares outgoing requests needs version-pinned fixtures. The latest GitHub release on August 25, 2026 was a 0.17.1 nightly, another reason to separate experimental upgrades from a production baseline.
Tests cover behavior, including multimodal inputs
Test blocks sit beside BAML functions and can provide text, images, audio, PDFs, or video as file paths, URLs, or base64 data. Checks can record a property without stopping the rest of the test, while assertions halt that case on failure. The command line can run all tests, select a function, list cases, or set parallel execution. The editor playground also previews the rendered prompt and raw request.
These facilities help only when teams write cases that detect semantic loss. The repository's 48 CI workflow files indicate a serious internal validation effort, yet our workspace test still failed before reaching a suite result. For an application, include awkward nested objects, missing optional members, partial arrays, provider fallback responses, and exact request-shape fixtures where compatibility matters. BAML reduces test plumbing; it does not decide which wrong result would hurt the product.
What happened when we ran it
Our sandbox installed 2,528 pnpm packages in 90 seconds and used 3,740 MB on disk at commit 42117dc. The clone itself occupied 144.4 MB. Installation succeeded without secrets in an unprivileged Node 22 container, which proves the dependency graph resolved in that environment. It also shows the source workspace is much heavier than installing a released CLI for ordinary use.
The build failed after 26 seconds. Turbo reported 2 successful tasks out of 14 before @baml/language-client-python exited with code 1. The tail also shows @baml/rpc starting a Rust binding export and Rustup synchronizing channel 1.93.0. It does not include the lower-level error from the Python client, so the honest finding ends at the named task.
Tests failed after 21 seconds at the same @baml/language-client-python build, with 2 cached tasks and 2 successful tasks out of 15. No test count was produced. The summary says the lifecycle test command failed because that prerequisite build failed. On our box, we never reached evidence about BAML's test assertions themselves.
Fast development is offset by language and release risk
For a team already maintaining several structured model functions, BAML can remove real repetition. Prompt previews expose the rendered request, generated types keep host applications honest, and provider configuration has one home. Semantic streaming also models partial output states so a user interface can distinguish an unfinished field from a completed one.
The cost is ownership of another language toolchain. The source tree spans Rust, pnpm workspaces, generated SDKs, editor tooling, and bindings for several host languages. With 328 open issues and pull requests on August 25, activity is high, and same-day work covered the compiler, LSP, runtime schemas, and dependency updates. Teams should adopt one pinned release, exercise their actual schemas, and upgrade only after those cases pass.

