workshops/2025-05/sections/09-state-management/README.md
Add state management and async clarification support.
for this section, we'll disable the baml logs. You can optionally enable them if you want to see more details.
export BAML_LOG=off
Add some simple in-memory state management for threads
cp ./walkthrough/09-state.ts src/state.ts
// ./walkthrough/09-state.ts
import crypto from 'crypto';
import { Thread } from '../src/agent';
// you can replace this with any simple state management,
// e.g. redis, sqlite, postgres, etc
export class ThreadStore {
private threads: Map<string, Thread> = new Map();
create(thread: Thread): string {
const id = crypto.randomUUID();
this.threads.set(id, thread);
return id;
}
get(id: string): Thread | undefined {
return this.threads.get(id);
}
update(id: string, thread: Thread): void {
this.threads.set(id, thread);
}
}
update the server to use the state management
ThreadStoresrc/server.ts
import express from 'express';
import { Thread, agentLoop } from '../src/agent';
+import { ThreadStore } from '../src/state';
const app = express();
app.set('json spaces', 2);
+const store = new ThreadStore();
+
// POST /thread - Start new thread
app.post('/thread', async (req, res) => {
data: req.body.message
}]);
- const result = await agentLoop(thread);
- res.json(result);
+
+ const threadId = store.create(thread);
+ const newThread = await agentLoop(thread);
+
+ store.update(threadId, newThread);
+
+ const lastEvent = newThread.events[newThread.events.length - 1];
+ // If we exited the loop, include the response URL so the client can
+ // push a new message onto the thread
+ lastEvent.data.response_url = `/thread/${threadId}/response`;
+
+ console.log("returning last event from endpoint", lastEvent);
+
+ res.json({
+ thread_id: threadId,
+ ...newThread
+ });
});
app.get('/thread/:id', (req, res) => {
- // optional - add state
- res.status(404).json({ error: "Not implemented yet" });
+ const thread = store.get(req.params.id);
+ if (!thread) {
+ return res.status(404).json({ error: "Thread not found" });
+ }
+ res.json(thread);
});
+// POST /thread/:id/response - Handle clarification response
+app.post('/thread/:id/response', async (req, res) => {
+ let thread = store.get(req.params.id);
+ if (!thread) {
+ return res.status(404).json({ error: "Thread not found" });
+ }
+
+ thread.events.push({
+ type: "human_response",
+ data: req.body.message
+ });
+
+ // loop until stop event
+ const newThread = await agentLoop(thread);
+
+ store.update(req.params.id, newThread);
+
+ const lastEvent = newThread.events[newThread.events.length - 1];
+ lastEvent.data.response_url = `/thread/${req.params.id}/response`;
+
+ console.log("returning last event from endpoint", lastEvent);
+
+ res.json(newThread);
+});
+
const port = process.env.PORT || 3000;
app.listen(port, () => {
cp ./walkthrough/09-server.ts src/server.ts
Start the server
npx tsx src/server.ts
Test clarification flow
curl -X POST http://localhost:3000/thread \
-H "Content-Type: application/json"
-d '{"message":"can you multiply 3 and xyz"}'