Building an Asynchronous Event-Driven Service Using Context Variables
This lesson preview is part of the Responsive LLM Applications with Server-Sent Events course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Responsive LLM Applications with Server-Sent Events, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:39] Welcome back. In this lesson, we will start building our agent focusing on the architecture or backend. Here you can see the code we built when we created our chat. It's a generator where we are leveraging the "astrim-lock method" or "long-chain" to yield some recent events anytime a new chunk is being generated by our larger model. The simple notation is simple and it works. However, there are several limitations. The first is that we are coupling network logic and domain logic. And as domain logic grows more complicated, it becomes really a pain.
[00:40 - 01:34] First second is that this code is hard to speak into sub-function. Why is that? Imagine this code may under being hundreds or hundreds of thousands of lines, so you want to create a sub-method like from two, let's say. Now in this function, anything may happen and you want to emit an event to keep the content updated. The problem is that if you yield in the chat function, you will be forced to iterate over the generator and yield the second time. And so on. If you have a call stack with five functions, at every level, you will be forced to yield again and so you'll note with a code which is very valuable. Now let's think our dream implementation. Why would it look like? Well, there is a method which looks very close to what we would like. Which is the print function.
[01:35 - 02:29] Let's take a look at print. It looks very simple, just print a little word. And yet, it's quite magical. Why is that? Because we did not return, we do not yield, and yet we can use it anywhere. And anytime you use print, it will add a new event to the logs. Now this is a really great user experience and this is why this print method is so often used. So that's why we would like as an experience. And our second property of our implementation, we would like to present a second use behavior as we do not want to block our server anytime an event is emitted. Now let's look at our dream implementation. It's a simple method called emit event.
[02:30 - 02:52] We need to use a word as an iconus and then we simply pass it an event, your instance of a class chunk event we created. And that's all. You can create from anywhere in your call stack. How can we make this implementation possible? How can we make this implementation possible?
[02:53 - 03:31] We want to emit those events from anywhere. So we need to share state. It cannot be a global state as we do not want to mix everyone between request. It cannot be a local state as we want to emit from anywhere in the call stack. How can we do that? We can do that with context bar, which is the native Python module, which less does create viable with such shared between a given request. Then once we create the context bar, let me show you how it's used. You simply need to set what you want in your context.
[03:32 - 03:59] Here we are setting our emit event function and then from anywhere in this code , you make all get here it is. And from get you get your emit event helper. Now what do we set within our context? We will set a queue. A queue is a data structure implementing a first in first out logic.
[04:00 - 04:57] And here we are leveraging the asynchronous module to use the asynchronous queue. We are going to create two methods, emit event, which simply add an event to the queue and close, which add a unique object to send your other closure over a queue. And that's all. So now our domain logic, we simply need to call emit event and emit an event and it will be put to the queue and that's all. And as you can see, we've created some very simple class to add some structure to our event. Now let's look at how we are going to build our semi-cent event from this queue . It requires a bit more complication, but it's completely separated from the domain code. Let 's see the core logic. We simply loop over the queue and we say as long as the queue is open, we wait for events.
[04:58 - 05:23] Anytime we find an event in the queue, if it's a chat event, we yield a service event. And once the queue is empty and the task is done, we always run nothing to do so we close and we are finished our job. So that was a bit of setup. And as you'll see, you 'll be in paid evidence making the rest of record much simpler and cleaner. See you soon.