11# BamlElixir
22
3- Call BAML functions from Elixir.
4- Uses the BAML Rust NIF to call the BAML library.
3+ Call BAML functions from Elixir, using a Rust NIF.
4+
5+ ## First of all, can this be used in production?
6+
7+ Well, I use it in production. But it's way too early for you if you expect stable APIs
8+ and things to not break at all. If you're okay with debugging issues with me when things go wrong,
9+ please go ahead!
510
611What this library does:
712
8- - Call functions in BAML files.
13+ - Generates Elixir structs, types and functions from BAML files.
14+ - Gives you autocomplete and dialyzer type checking.
15+ - Parses BAML results into Elixir structs.
916- Switch between different LLM clients.
1017- Get usage data using collectors.
1118
1219What this library does not do:
1320
14- - Code generation of Elixir ` baml_client ` from BAML files.
21+ - Generate Elixir ` baml_client ` files from BAML files. Codegen happens at compile time .
1522- Automatically parse BAML results into Elixir structs.
1623
1724## Usage
1825
19- First add a BAML file in the ` priv ` directory.
26+ Create a baml_src directory in priv and add a BAML file in there:
2027
2128``` baml
2229client GPT4 {
@@ -53,98 +60,86 @@ function ExtractResume(resume: string) -> Resume {
5360}
5461```
5562
56- Now call the BAML function :
63+ Now create a BAML client module :
5764
5865``` elixir
59- BamlElixir . Client . call ( " ExtractResume " , %{ resume: " John Doe is the CTO of Acme Inc. " }, %{
60- path: " priv/baml_src"
61- })
66+ defmodule MyApp . BamlClient do
67+ use BamlElixir . Client , path: " priv/baml_src"
68+ end
6269```
6370
64- ### Stream results
71+ Now call the BAML function:
6572
6673``` elixir
67- BamlElixir .Client .stream! (" ExtractResume" , %{resume: " John Doe is the CTO of Acme Inc." }, %{
68- path: " priv/baml_src"
69- })
70- |> Enum .each (& IO .inspect / 1 )
74+ MyApp .BamlClient .ExtractResume .call (%{resume: " John Doe is the CTO of Acme Inc." })
7175```
7276
73- #### Parsing results
74-
75- If BAML returns a class type, you will get a map with keys as atoms and a special key ` __baml_class__ ` with the BAML class name.
76-
77- Example:
77+ ### Stream results
7878
7979``` elixir
80- %{
81- __baml_class__: " Resume" ,
82- name: " John Doe" ,
83- job_title: " CTO" ,
84- company: %{
85- __baml_class__: " Company" ,
86- name: " Acme Inc."
87- }
88- }
89- ```
80+ MyApp .BamlClient .ExtractResume .stream (%{resume: " John Doe is the CTO of Acme Inc." }, fn
81+ {:ok , result} ->
82+ IO .inspect (result)
9083
91- If BAML returns an enum type, you will get a map two special keys: ` __baml_enum__ ` with the BAML enum name and ` value ` with the enum value.
84+ {:error , error} ->
85+ IO .inspect (error)
9286
93- Example:
94-
95- ``` elixir
96- %{
97- __baml_enum__: " Color" ,
98- value: " Red"
99- }
87+ :done ->
88+ IO .inspect (" Done" )
89+ end )
10090```
10191
10292### Images
10393
10494Send an image URL:
10595
10696``` elixir
107- BamlElixir . Client . call (" DescribeImage " , %{
97+ MyApp . BamlClient . DescribeImage . call (%{
10898 myImg: %{
109- " url" => " https://upload.wikimedia.org/wikipedia/en/4/4d/Shrek_%28character%29.png"
99+ url: " https://upload.wikimedia.org/wikipedia/en/4/4d/Shrek_%28character%29.png"
110100 }
111101})
102+ |> IO .inspect ()
112103```
113104
114105Or send base64 encoded image data:
115106
116107``` elixir
117- BamlElixir . Client . call ( " DescribeImage" , %{
108+ MyApp . BamlClient . DescribeImage . stream ( %{
118109 myImg: %{
119- " base64" => " data:image/png;base64,..."
110+ base64: " data:image/png;base64,..."
120111 }
121- })
112+ }, fn result ->
113+ IO .inspect (result)
114+ end )
122115```
123116
124117### Collect usage data
125118
126119``` elixir
127120collector = BamlElixir .Collector .new (" my_collector" )
128121
129- BamlElixir . Client . call (" ExtractResume " , %{resume: " John Doe is the CTO of Acme Inc." }, %{
122+ MyApp . BamlClient . ExtractResume . call (%{resume: " John Doe is the CTO of Acme Inc." }, %{
130123 collectors: [collector]
131124})
132125
133126BamlElixir .Collector .usage (collector)
134127```
135128
129+ When streaming, you can get the usage after : done message is received.
130+
136131### Switch LLM clients
137132
138133From the existing list of LLM clients, you can switch to a different one by calling ` Client.use_llm_client/2 ` .
139134
140135``` elixir
141- BamlElixir . Client . call (" WhichModel " , %{}, %{
136+ MyApp . BamlClient . WhichModel . call (%{}, %{
142137 llm_client: " GPT4oMini"
143138})
144139|> IO .inspect ()
145140# => "gpt-4o-mini"
146141
147- BamlElixir . Client . call (" WhichModel " , %{}, %{
142+ MyApp . BamlClient . WhichModel . call (%{}, %{
148143 llm_client: " DeepSeekR1"
149144})
150145|> IO .inspect ()
@@ -170,6 +165,12 @@ This also downloads the pre built NIFs for these targets:
170165
171166If you need to build the NIFs for other targets, you need to clone the repo and build it locally as documented below.
172167
168+ ### TODO
169+
170+ - Type aliases
171+ - Dynamic types
172+ - Partial types for streaming
173+
173174### Development
174175
175176This project includes Git submodules. To clone the repository with all its submodules, use:
0 commit comments