Overview
reasonStream() is an async function that calls a LLM and streams back to your app its response. Everything you can do with reason() you can also do with reasonStream().
src/entrypoints/test.ts
reasonStream response
Using reasonStream()
In the previous example, we were just returning reasonStream() in our POST() function, however reasonStream() is an async generator — which means there is much more we can do with it othen than directly return it.
For instance, let’s say we needed to include a picture of the country’s flag in the response. How could we do this?
Well, we could ask the LLM for an URL that contains a picture of the country’s flag but this is not ideal as the URL might be broken, might have expired, etc.
Another way would be to wait for the response from the LLM (that contains the country property) and then search the web for a picture of the country. This seems like a good approach.
To do this, we need to introduce a few concepts first.
The next section will be about JavaScript generators, if you are familiar with them, feel free to skip it.
JavaScript generators
SincereasonStream() is a generator, we need to fully understand what they are and how they work.
Although this is not a feature from RΞASON itself but a native JavaScript feature, it is not widely used so most people are not familiar with them.
What is a generator?
A generator is just a function that can return multiple values rather than just one. For instance:A generator example
async:
An async generator example
- To declare a function as a generator you need to add a
*after thefunctionkeyword; - Generators are just JS iterators, therefore you can
for (const yieldedValue of generator()) { }; - To use an
async generatoryou need to add anawaitinfor await (const yieldedValue of generator()) { }.
Why are they useful?
Because they allows a function to return values as soon as they are available rather than waiting for the whole function to finish and then returning. Suppose the following:An async generator example
- Takes some meaningful time: if some processing takes nanoseconds, there is probably no reason to use generators;
- Has meaningful intermediate values: the processing produces values before ending that are useful to whomever is calling the generator.
Generators and LLMs
Turns out LLMs fits this abstraction perfectly!- A LLM takes meaningful time — up to 30 seconds — to return the full completion of a given prompt;
- A LLM has meaningful intermediate values: since LLMs process the prompt + completion from left to right, they can just output the as they are generated — which is extremely useful.
LLM generator example
reasonStream().
Iterating through reasonStream()
Since reasonStream() is an async generator, you can use all JS generators features. Such as:
description property was filled overtime.
Import to note that while we specified in our City interface a single description property that is a string, reasonStream returned a object that has done & value. Why?
StreamableObjects
All intermediate values that reasonStream() yields are StreamableObjects.
A StreamableObject is just a wrapper for your value that has a done: boolean property to indicate whether the LLM has fully finished processing that particular property. This is to help developers know what state a certain property is while being streamed.
A StreamableObject has three different states:
1
When the LLM has not even started returning the value
The
StreamableObject will be:2
When the LLM has started returning the value but not finished
The
StreamableObject will be:3
When the LLM has finished returning the value
The
StreamableObject will be:A note on incomplete values
A note on incomplete values
A “incomplete value” is a value that the LLM has started returning but has not finished yet.It is important to note that LLMs returns characters from left to right, so incomplete values are filled from left to right as if they were strings.For instance:The
rating property will be filled like this:reasonStream() yields like this is because in almost all scenarios that you need to access intermediate values, you also need to know when a certain value has been fully returned from the LLM or not.StreamableObject in nested properties
All values (and sub-values) are wrapped in StreamableObjects. Even nested properties, such as array elements & object properties.
For instance, given the following interface:
StreamableObject will be:
What this all means?
The reason we introducedStreamableObjects is because to iterate through reasonStream() you’ll to handle them.
In the example we lay out previously, we had this code:
src/entrypoints/test.ts
country property) and then searching the web for a picture of the country.
Let’s do it:
src/entrypoints/test.ts
test entrypoint in the RΞASON Playground:
reasonStream response
Returning StreamableObjects to the client
You may have noticed above that while reasonStream() yields StreamableObjects, the response that was streamed from RΞASON to the client (the Playground in this case) is a regular object and not a StreamableObject.
Why?
Because all StreamableObjects yielded from your entrypoints are unwrapped to their original form. This is done because almost never you actually want to return StreamableObjects to your client.
Modifying StreamableObject
You might have noticed that in the previous example, we added a new property called country_picture:
src/entrypoints/test.ts
StreamableObject that reasonStream() yields to your needs. This can be useful when you want to add some extra information to the object that is not returned from the LLM: add a new property, modify an existing one, etc.
Iterating through nested objects
Going back to our Quickstart example where we had the following code:City interface consists of a complex object that has the points_of_interest property being an array of objects. Here’s what this entrypoint outputs:
- the latitude & longitude of the user and points of interest;
- a function that calculates the distance between two pairs of latitude/longitude (in meters) (
getDistance()).
- call the getDistance() function for each point of interest to get the distance of the user from that location;
- return the distance in the streaming response.

Output from POST /hello with the distance property
A note on modifying nested StreamableObjects
You can only modify the value property of the StreamableObject — the done property is read-only. For instance, in point_of_interest.value.distance = ... we are adding a new property to the value property of the point_of_interest object.
If we did point_of_interest.distance = ..., it would not work as we would be trying to modify the StremableObject itself and its value.
Conclusion
In this page we learned how to work withreasonStream() in your code. Although there is more to learn about reasonStream(), this page should be enough to get you started.
Next, we’ll be going in-depth about the concept of agents in RΞASON.