Skip to content

Commit 589e6e1

Browse files
committed
emit docs
go example
1 parent 6ea5617 commit 589e6e1

File tree

3 files changed

+745
-0
lines changed

3 files changed

+745
-0
lines changed
Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
---
2+
title: Runtime Events
3+
---
4+
5+
<Info>
6+
This feature was added in TODO
7+
</Info>
8+
9+
When running multi-step workflows, you need to be able to get information about
10+
the running workflow. You might need this information to show incremental
11+
results to your app’s users, or to debug a complex workflow combining multiple
12+
LLM calls.
13+
14+
BAML makes this possible though an event system that connects variables in your
15+
BAML Workflow code to the Python/TypeScript/etc client code that you used to
16+
invoke the workflow.
17+
18+
## Using Markdown blocks to track execution
19+
20+
Markdown Blocks are automatically tracked when you run BAML
21+
workflows, and your client code can track which block is currently executing. In
22+
the following example, your client could directly use the markdown headers to
23+
render the current status on a status page:
24+
25+
```baml BAML
26+
struct Post {
27+
title string
28+
content string
29+
}
30+
31+
// Browse a URL and produce a number of posts describing
32+
// its what was found there for our marketing site.
33+
function MakePosts(source_url: string, count: int) -> Post[] {
34+
# Summarize Source
35+
let source = LLMSummarizeSource(source_url);
36+
37+
# Determine Topic
38+
let topic = LLMInferTopic(source);
39+
40+
# Generate Marketing Post Ideas
41+
let ideas: string[] = LLMIdeas(topic, source);
42+
43+
# Generate posts
44+
let posts: Post[] = [];
45+
for (idea in ideas) {
46+
47+
## Create the post
48+
let post = LLMGeneratePost(idea, source);
49+
50+
## Quality control
51+
let quality = LLMJudgePost(post, idea, source);
52+
if (quality > 8) {
53+
posts.push(post);
54+
}
55+
}
56+
}
57+
```
58+
59+
In your client code, you can bind events to callbacks:
60+
61+
<Tabs>
62+
<Tab title="Python" language="python">
63+
```python
64+
# app.py
65+
from baml_client.sync_client import { b }
66+
from baml_client.types import Event
67+
import baml_client.events
68+
69+
def Example():
70+
# Get an Events callback collector with the right type
71+
# for your MakePosts() function.
72+
ev = events.MakePosts()
73+
74+
# Associate the block event with your own callback.
75+
events.on_block(lambda ev: print(ev.block_label))
76+
77+
# Invoke the function.
78+
posts = b.MakePosts("https://wikipedia.org/wiki/DNA", {"events": ev})
79+
print(posts)
80+
```
81+
</Tab>
82+
<Tab title="TypeScript" language="typescript">
83+
```typescript
84+
// index.ts
85+
import { b, events } from "./baml-client"
86+
import type { Event } from "./baml-client/types"
87+
88+
async function Example() {
89+
// Get an Events callback collector with the right type
90+
// for your MakePosts() function.
91+
let ev = events.MakePosts()
92+
93+
// Associate the block event with your own callback.
94+
events.on_block((ev) => {
95+
console.log(ev.block_label)
96+
});
97+
98+
// Invoke the function.
99+
const posts = await b.MakePosts(
100+
"https://wikipedia.org/wiki/DNA",
101+
{"events": ev}
102+
)
103+
console.log(posts)
104+
}
105+
```
106+
</Tab>
107+
<Tab title="Go" language="go">
108+
```go
109+
// main.go
110+
package main
111+
112+
import (
113+
"context"
114+
"fmt"
115+
"log"
116+
117+
b "example.com/myproject/baml_client"
118+
"example.com/myproject/baml_client/events"
119+
"example.com/myproject/baml_client/types"
120+
)
121+
122+
func main() {
123+
ctx := context.Background()
124+
125+
// Get an Events callback collector with the right type
126+
// for your MakePosts() function.
127+
ev := events.NewMakePosts()
128+
129+
// Associate the block event with your own callback.
130+
events.OnBlock(func(ev *types.BlockEvent) {
131+
fmt.Println(ev.BlockLabel)
132+
})
133+
134+
// Invoke the function.
135+
posts, err := b.MakePosts(ctx, "https://wikipedia.org/wiki/DNA", &b.MakePostsOptions{
136+
Events: ev,
137+
})
138+
if err != nil {
139+
log.Fatal(err)
140+
}
141+
fmt.Printf("%+v\n", posts)
142+
}
143+
```
144+
</Tab>
145+
</Tabs>
146+
147+
## Using `emit` to track variables
148+
149+
Variable update can also be tracked with events. To mark a variable as visible
150+
to the event system, use the `emit` keyword when you declare the variable. Let’s
151+
see how we would use this capability to track the progress of our marketing post
152+
generation workflow:
153+
154+
```tsx
155+
function MakePosts(source_url: string) -> Post[] {
156+
# Summarize Source
157+
let source = LLMSummarizeSource(source_url);
158+
159+
# Determine Topic
160+
let topic = LLMInferTopic(source);
161+
162+
# Generate Marketing Post Ideas
163+
let ideas: string[] = LLMIdeas(topic, source);
164+
165+
// Track how many posts we need to generate. <-***
166+
let posts_target_length = ideas.len();
167+
emit let progress_percent: int = 0;
168+
169+
# Generate posts
170+
let posts: Post[] = [];
171+
for ((i,idea) in ideas.enumerate()) {
172+
173+
## Create the post
174+
let post = LLMGeneratePost(idea, source);
175+
176+
## Quality control
177+
let quality = LLMJudgePost(post, idea, source);
178+
if (quality > 8) {
179+
posts.push(post);
180+
} else {
181+
posts_target_length -= 1;
182+
}
183+
184+
// *** This update will trigger events visible to the client.
185+
progress_percent = i * 100 / posts_target_length
186+
}
187+
}
188+
```
189+
190+
When you generate a BAML client, the events structure for ` MakePosts` will
191+
accept callbacks for `progress_percent` because we marked that variable with
192+
`emit`, and the callbacks will receive an `int` data payload, because
193+
`progress_percent` is an `int`.
194+
195+
In your client code, you can track these emitted variables:
196+
197+
<Tabs>
198+
<Tab title="Python" language="python">
199+
```python
200+
# app.py
201+
from baml_client.sync_client import { b }
202+
from baml_client.types import Event
203+
import baml_client.events
204+
205+
def Example():
206+
# Get an Events callback collector with the right type
207+
# for your MakePosts() function.
208+
ev = events.MakePosts()
209+
210+
# Track the progress_percent variable updates
211+
events.on_progress_percent(lambda percent: print(f"Progress: {percent}%"))
212+
213+
# Invoke the function.
214+
posts = b.MakePosts("https://wikipedia.org/wiki/DNA", {"events": ev})
215+
print(posts)
216+
```
217+
</Tab>
218+
<Tab title="TypeScript" language="typescript">
219+
```typescript
220+
// index.ts
221+
import { b, events } from "./baml-client"
222+
import type { Event } from "./baml-client/types"
223+
224+
async function Example() {
225+
// Get an Events callback collector with the right type
226+
// for your MakePosts() function.
227+
let ev = events.MakePosts()
228+
229+
// Track the progress_percent variable updates
230+
events.on_progress_percent((percent) => {
231+
console.log(`Progress: ${percent}%`)
232+
});
233+
234+
// Invoke the function.
235+
const posts = await b.MakePosts(
236+
"https://wikipedia.org/wiki/DNA",
237+
{"events": ev}
238+
)
239+
console.log(posts)
240+
}
241+
```
242+
</Tab>
243+
<Tab title="Go" language="go">
244+
```go
245+
// main.go
246+
package main
247+
248+
import (
249+
"context"
250+
"fmt"
251+
"log"
252+
253+
b "example.com/myproject/baml_client"
254+
"example.com/myproject/baml_client/events"
255+
"example.com/myproject/baml_client/types"
256+
)
257+
258+
func main() {
259+
ctx := context.Background()
260+
261+
// Get an Events callback collector with the right type
262+
// for your MakePosts() function.
263+
ev := events.NewMakePosts()
264+
265+
// Track the progress_percent variable updates
266+
events.OnProgressPercent(func(percent int) {
267+
fmt.Printf("Progress: %d%%\n", percent)
268+
})
269+
270+
// Invoke the function.
271+
posts, err := b.MakePosts(ctx, "https://wikipedia.org/wiki/DNA", &b.MakePostsOptions{
272+
Events: ev,
273+
})
274+
if err != nil {
275+
log.Fatal(err)
276+
}
277+
fmt.Printf("%+v\n", posts)
278+
}
279+
```
280+
</Tab>
281+
</Tabs>
282+
283+
For details about the types of events, see [BAML Language Reference](/ref/baml_client/events)
284+
285+
# Event Details
286+
287+
It helps to understand the following concepts when trying to do more complex
288+
things with Events:
289+
290+
1. **Separate Thread** To avoid interfering with the rest of your BAML code,
291+
callbacks are run concurrently in a separate execution thread.
292+
2. Events are meant for local tracking. If you asign a value to a new (non-
293+
emit) variable, this new variable doesn't get its updates tracked. If you
294+
pass a value as a parameter to a function, updates made within that
295+
function will not be tracked.

0 commit comments

Comments
 (0)