baml
Learn how to use the BAML programming language for LLM structured outputs
What this skill does
# BAML Quick Reference
BAML (Boundary AI Markup Language) is a DSL for building LLM applications with structured, type-safe outputs. It generates client code for Python, TypeScript, Go, and Ruby.
## How BAML Works
- All `.baml` files in `baml_src/` are globally accessible to each other
- Generate the client with `baml-cli generate`
- The generated `baml_client/` provides type-safe functions you import and call
- BAML types become Pydantic models (Python), TypeScript types, Go structs, or Sorbet types (Ruby)
---
## Essential Syntax
### Types
```baml
// Primitives
string, int, float, bool, null
// Composite
Type? // optional (nullable)
Type[] // array
Type1 | Type2 // union
map<K, V> // dictionary
// Literals
"spam" | "ham" // literal string union
// Multimodal
image, audio, video, pdf
```
### Classes
Define structured data shapes. **No colons between field name and type.**
```baml
class Resume {
name string
email string? // optional
skills string[] // array
experience Experience[] // nested type
seniority SeniorityLevel // enum
}
class Experience {
company string
role string @description("Job title") // hint for the LLM
years int @alias("duration_years") // JSON key mapping
}
```
### Enums
Fixed set of values. Great for classification tasks.
```baml
enum SeniorityLevel {
JUNIOR @description("0-2 years experience")
MID @description("2-5 years experience")
SENIOR @description("5+ years experience")
}
enum Category {
SPAM
HAM
UNKNOWN @skip // excluded from prompts
}
```
### Functions
Define the LLM interaction. **Function names must start with a capital letter.**
```baml
function ExtractResume(resume_text: string) -> Resume {
client "openai/gpt-4o"
prompt #"
Extract structured information from this resume.
{{ ctx.output_format }}
Resume:
---
{{ resume_text }}
---
"#
}
```
### Clients
Configure LLM providers. Two styles:
```baml
// Shorthand (uses env vars automatically)
client "openai/gpt-4o"
client "anthropic/claude-sonnet-4-20250514"
// Named client (full control)
client<llm> GPT4 {
provider openai
options {
model "gpt-4o"
api_key env.OPENAI_API_KEY
temperature 0.0
}
}
// Fallback chain
client<llm> Resilient {
provider fallback
options {
clients [GPT4, Claude, GPT4Mini]
}
}
```
### Generator
Configure code generation:
```baml
generator target {
output_type "python/pydantic" // or "typescript", "go", "ruby/sorbet"
output_dir "../"
default_client_mode "sync" // or "async"
version "0.203.1"
}
```
You may set up codegen for multiple locations in multiple languages.
For example, you may want to keep backend and frontend types aligned for your respective BAML clients.
You can do this by initializing two `generator` blocks.
---
## The Two Critical Concepts
### 1. `{{ ctx.output_format }}`
This Jinja macro **must be included in every prompt**. It renders the return type schema so the LLM knows what structure to produce.
```baml
function ClassifyEmail(email: string) -> Category {
client GPT4
prompt #"
Classify this email.
{{ ctx.output_format }}
Email: {{ email }}
"#
}
```
For a `Category` enum, this renders something like:
```
Answer with any of the categories:
SPAM
HAM
```
For a class, it renders the JSON schema with field descriptions.
### 2. Schema-Aligned Parsing (SAP)
BAML's parser is intentionally lenient. It automatically fixes common LLM output issues:
- Missing quotes around strings
- Trailing commas
- Comments in JSON
- Incomplete sequences
- Unescaped characters
This means you get **87-93% better accuracy** than strict JSON parsing or function calling.
---
## Prompt Syntax (Jinja)
BAML prompts use Jinja templating:
```baml
prompt #"
{# Comments don't appear in output #}
{{ _.role("system") }}
You are a helpful assistant.
{{ _.role("user") }}
{% for msg in messages %}
{{ msg.content }}
{% endfor %}
{% if verbose %}
Be detailed in your response.
{% endif %}
{{ ctx.output_format }}
"#
```
**Key constructs:**
- `{{ variable }}` - interpolate values
- `{% for item in list %}...{% endfor %}` - loops
- `{% if cond %}...{% endif %}` - conditionals
- `{{ _.role("system"|"user"|"assistant") }}` - set message role
- `{{ value|filter }}` - apply filters (e.g., `|upper`, `|length`, `|join(",")`)
---
## Calling BAML Functions
### Python
```python
from baml_client import b
from baml_client.types import Resume
# Sync
resume = b.ExtractResume(resume_text)
print(resume.name, resume.skills)
# Async
from baml_client.async_client import b
resume = await b.ExtractResume(resume_text)
# Streaming
stream = b.stream.ExtractResume(resume_text)
for partial in stream:
print(partial) # partial Resume object
final = stream.get_final_response()
```
### TypeScript
```typescript
import { b } from './baml_client'
// Async (default)
const resume = await b.ExtractResume(resumeText)
console.log(resume.name, resume.skills)
// Streaming
const stream = b.stream.ExtractResume(resumeText)
for await (const partial of stream) {
console.log(partial)
}
const final = await stream.getFinalResponse()
```
### Go
```go
import b "example.com/myproject/baml_client"
resume, err := b.ExtractResume(ctx, resumeText, nil)
if err != nil {
log.Fatal(err)
}
fmt.Println(resume.Name, resume.Skills)
```
---
## Testing
Define tests directly in BAML files:
```baml
test SimpleExtraction {
functions [ExtractResume]
args {
resume_text "John Doe, Software Engineer at Acme Corp for 5 years"
}
@@assert({{ this.name == "John Doe" }})
}
```
Run tests:
```bash
baml-cli test # run all
baml-cli test -i "ExtractResume" # filter by function
baml-cli test --parallel 5 # parallel execution
```
---
## Attributes Reference
**Field-level:**
- `@alias("name")` - rename field in JSON output
- `@description("...")` - add context for the LLM
- `@skip` - exclude from prompts (enums only)
**Block-level:**
- `@@dynamic` - allow runtime modification of class/enum
**Validation:**
- `@check(expr, name)` - soft validation (returns result, doesn't fail)
- `@assert(expr, name)` - hard validation (throws on failure)
**Streaming:**
- `@@stream.done` - object only appears when complete
- `@stream.not_null` - field must have value before parent streams
- `@stream.with_state` - include completion state metadata
---
## CLI Commands
```bash
baml-cli init # initialize new project
baml-cli generate # generate client code
baml-cli dev # dev server with hot reload
baml-cli test # run tests
baml-cli serve # start REST API server (port 2024)
baml-cli fmt # format BAML files
```
---
## Common Patterns
See the `examples/` directory for complete working examples:
- `extraction.baml` - structured data extraction
- `classification.baml` - enum-based classification
- `chat.baml` - multi-turn chat with message history
- `multimodal.baml` - image/audio/pdf inputs
- `usage.py` - Python calling patterns
---
## Further Resources
**Getting Started**
- Installation: https://docs.boundaryml.com/guide/installation-language/python.md
- TypeScript setup: https://docs.boundaryml.com/guide/installation-language/typescript.md
- Go setup: https://docs.boundaryml.com/guide/installation-language/go.md
**Language Reference**
- Complete syntax: https://docs.boundaryml.com/ref/overview.md
- Type system: https://docs.boundaryml.com/ref/baml/types.md
- Classes: https://docs.boundaryml.com/ref/baml/class.md
- Enums: https://docs.boundaryml.com/ref/baml/enum.md
- Functions: https://docs.boundaryml.com/ref/baml/function.md
- Generator: https://docs.boundaryml.com/ref/baml/generator.md
**LLM ProvideRelated in AI Agents
skill-development
IncludedComprehensive meta-skill for creating, managing, validating, auditing, and distributing Claude Code skills and slash commands (unified in v2.1.3+). Provides skill templates, creation workflows, validation patterns, audit checklists, naming conventions, YAML frontmatter guidance, progressive disclosure examples, and best practices lookup. Use when creating new skills, validating existing skills, auditing skill quality, understanding skill architecture, needing skill templates, learning about YAML frontmatter requirements, progressive disclosure patterns, tool restrictions (allowed-tools), skill composition, skill naming conventions, troubleshooting skill activation issues, creating custom slash commands, configuring command frontmatter, using command arguments ($ARGUMENTS, $1, $2), bash execution in commands, file references in commands, command namespacing, plugin commands, MCP slash commands, Skill tool configuration, or deciding between skills vs slash commands. Delegates to docs-management skill for official documentation.
reprompter
IncludedTransform messy prompts into well-structured, effective prompts — single or multi-agent. Use when: "reprompt", "reprompt this", "clean up this prompt", "structure my prompt", rough text needing XML tags and best practices, "reprompter teams", "repromptception", "run with quality", "smart run", "smart agents", multi-agent tasks, audits, parallel work, anything going to agent teams. Don't use when: simple Q&A, pure chat, immediate execution-only tasks. See "Don't Use When" section for details. Outputs: Structured XML/Markdown prompt, quality score (before/after), optional team brief + per-agent sub-prompts, agent team output files. Success criteria: Single mode quality score ≥ 7/10; Repromptception per-agent prompt quality score 8+/10; all required sections present, actionable and specific.
adaptive-compaction
IncludedAdaptive add-on policy and recovery layer that decides WHEN to compact, prune, snapshot, or fork -- replacing fixed-percent auto-compaction across Claude Code, Codex, and MCP-capable hosts. Trigger on auto-compact timing or damage: "when should I compact", "is it safe to compact now or start a fresh session", "auto-compact fires too early/mid-task", "switching to an unrelated task but the window still has space", "context rot", "answers get worse the longer the session runs", "the agent forgot the plan or my decisions after it summarized", "add a layer on top that manages context without changing the agent", raising autoCompactWindow to give the policy room, or installing/tuning a cross-tool compaction policy or PreCompact hook -- even when "compaction" is never said but the problem is context-window pressure or post-summarization memory loss. Do NOT use to summarize a conversation, build RAG, write a summarization prompt (decides WHEN not HOW), or answer max-context-length trivia.
agent-skill-creator
IncludedCreate cross-platform agent skills from workflow descriptions. Activates when users ask to create an agent, automate a repetitive workflow, create a custom skill, or need advanced agent creation. Triggers on phrases like create agent for, automate workflow, create skill for, every day I have to, daily I need to, turn process into agent, need to automate, create a cross-platform skill, validate this skill, export this skill, migrate this skill. Supports single skills, multi-agent suites, transcript processing, template-based creation, interactive configuration, cross-platform export, and spec validation.
llm-wiki
IncludedUse when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include "second brain", "Obsidian wiki", "personal knowledge management", "ingest this paper/article/book", "build a research wiki", "compound knowledge", "Memex", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query.
skill-master
IncludedAgent Skills authoring, evaluation, and optimization. Create, edit, validate, benchmark, and improve skills following the agentskills.io specification. Use when designing SKILL.md files, structuring skill folders (references, scripts, assets), ingesting external documentation into skills, running trigger evals, benchmarking skill quality, optimizing descriptions, or performing blind A/B comparisons. Keywords: agentskills.io, SKILL.md, skill authoring, eval, benchmark, trigger optimization.