- BAML owns prompt authoring, model calls, structured parsing, and the generated
baml_client. - Smithers owns orchestration, durable execution, retries, approvals, caching, and persistence.
.baml file directly into a Smithers JSX workflow and render each BAML function as a task component.
What this integration does
For each BAML function in an imported.baml file, Smithers exposes:
- a React component named after the function
- a Zod schema export named
<FunctionName>Output
.baml import is already a task.
Do not wrap a generated BAML export in <Task agent={...}>, and do not treat it as prompt content for another <Task>. The generated component renders a Smithers compute task internally.
Prerequisites
Start with the JSX Installation guide, then add BAML support. Install BAML:baml_src/ directory, initialize one:
1. Configure BAML generators
Create or updatebaml_src/generators.baml:
baml_client, and the Smithers generator for task wrappers and output schemas.
2. Register the Bun preload plugin
Createpreload.ts:
bunfig.toml:
baml-cli generate in your build and CI flow so generated artifacts stay current.
3. Write a BAML function
4. Import the .baml file into a workflow
Generated API
For a BAML function namedWriteMeAStory, the generated .baml import exposes:
WriteMeAStory— a React component that renders a Smithers compute taskWriteMeAStoryOutput— the generated Zod schema for the persisted output
id— the Smithers node idargs— the BAML function arguments, grouped under one prop to avoid collisions with Smithers task propsbamlOptions— forwarded to the generatedbaml_clientcall- normal Smithers task controls such as
dependsOn,needs,skipIf,needsApproval,timeoutMs,retries,retryPolicy,continueOnFail,cache,label, andmeta
Output registration
Register the generated output schema withcreateSmithers(...) like any other Zod schema:
createSmithers(...) is your Smithers storage name. It does not need to match the BAML function name.
Downstream tasks read it through outputs like any other schema-driven output:
Object returns vs scalar returns
If a BAML function returns an object-like type, that shape is persisted directly. If a BAML function returns a scalar, enum, list, map, or union, the generated Smithers schema boxes the value underresult so the output stays object-shaped for persistence.
Example:
ctx.outputMaybe(outputs.classify, { nodeId: "classify" })?.result.
How validation works
BAML handles:- prompt rendering
- model execution
- structured parsing
- repair / retry logic inside the BAML call path
- the generated wrapper calls
baml_client - Smithers validates the final parsed value against the generated Zod schema
- Smithers persists the validated result
- the workflow re-renders and downstream nodes can read the output
<Task> nodes, approvals, loops, and caching.
When to use BAML vs MDX prompts
Use BAML when:- the prompt and output schema belong together
- you want BAML’s structured-output ergonomics
- you want to reuse the same BAML function outside Smithers
- the prompt is mostly prose or documentation
- you want JSX/MDX composition for prompt text
- you want to keep using Smithers agent mode directly
Current limitations
This integration is intentionally narrow in v1. Supported well:- static BAML return types
- normal Smithers task controls
- mixing generated BAML tasks with normal Smithers JSX tasks
- return types whose shape changes at runtime via
@@dynamic/TypeBuilder - streaming partial outputs into Smithers
- multimodal return values as Smithers task outputs
Troubleshooting
”Cannot import .baml”
Make surebamlPlugin() is registered in a Bun preload file and that bunfig.toml uses top-level preload.
”Generated files are missing”
Run:”My downstream task cannot read the output”
Register the generated schema increateSmithers(...) and read it through the returned outputs object.
”My BAML task output shape does not match”
Check whether the function returns a scalar or dynamic type. Scalars are boxed underresult, and dynamic output shapes are not supported in v1.