Learn the fundamentals in 10 minutes
The directory structure
src/entrypoints
: Is where all your (routes) are defined. We’ll talk more about entrypoints below.src/entrypoints/hello.ts
: An accessible entrypoint (route/endpoint) at http://localhost:1704/hello..eslintrc.json
: ESLint configuration file used to configure the RΞASON ESLint plugin..reason.config.js
: Configuration file for RΞASON, to set your OpenAI Key and default model, amongst other things.src/actions
: Is where all your actions are defined. We talk more about actions here.src/agents
: Is where all your agents are defined. We talk more about agents here..ts
files created under /entrypoints
, /agents
and /actions
will be respectively treated as entrypoints, agents or actions.
For instance, if you want to create a new entrypoint called qa-agent
you just need to create the file /entrypoints/qa-agent.ts
.
When you run npx use-reason
, the project that is created for you has no agents & actions, but it has a single entrypoint /entrypoints/hello.ts
— which is what we’ll explore now.
Setting your key
.reason.config.js
and add your key there:RΞASON Playground initial screen
hello
entrypoint and send a request. You will see something like:
POST /hello response
Why have a Playground at all?
POST /hello
entrypoint returns a JSON object with some cool points of interest about the city you are located at. How does it work?
POST
http://localhost:1704/hello, the entrypoints/hello.ts
file is called and the function POST()
is executed. Here’s how the file looks:
export async function* POST() {}
POST()
: We are defining that this function is responsible for handling POST
requests;function*
: By adding *
we are telling RΞASON this function returns a streaming response;await fetch('http://ip-api.com/json/')
fetches the user’s location from their IP address;reasonStream<City>('Tell me about ${city}')
reasonStream()
is a RΞASON function that prompts a LLM and streams the response;'Tell me about ${city}'
is the prompt passed to the LLM;reasonStream<City>
: By passing an interface
to reasonStream()
, RΞASON ensures the response conforms to the City
interface structure. More details here./** A two sentence description of the city */
comment is passed directly to the LLM along your prompt. You can think of it as the prompt for the property that the comment is above (in this case, the description
property).About JSDoc
Description when you hover over `fs.readFileSync()`
fs.readFileSync()
definition:The definition of `fs.readFileSync()`
/** comment */
;reason()
and reasonStream()
functions, which we’ll explore more in-depth later.
Next, we’ll modify the hello
entrypoint to enhance its functionality.
/hello
entrypoint.
However, what if we want to pin the points of interest in an actual map? Since the address
property is just a string this would be a bit hard.
City
interfaceaddress
property to have latitude & longitude as well?
POST /hello
using the Playground we should see the new address
property:
Output from `POST /hello`
reasonStream()
we change the output as well.
http://ip-api.com/json/
returns that as well!
http://ip-api.com/json/
:
src/actions/getDistance.ts
that will calculate the distance for us:
getDistance()
function for each point of interest to get the distance of the user from that location;POST /hello
entrypoint.reasonStream()
as an actual generator — which is somewhat advanced and may look complicated, but we’ll over it step-by-step in the next page.
reasonStream()
reasonStream()
is an async generator — which means there is much more we can do with it othen than directly return it (which is what we are doing now).
We can, for instance:
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
. These are called StreamableObject
and we’ll go in-depth in them later.
For now, what is important to know is that every property that you specified in your interface will be wrapped in a { done: boolean, value: actualValue }
.
For instance, description: string
became an object with { done: boolean, value: string }
.
getDistance()
reasonStream()
, let’s take a step back and remember our current problem.
We now want to return the distance from the user to the point of interest — something like: “Golden Gate Bridge is 12km away from you”. For that we have:
getDistance()
function that uses the Haversine formula to calculate the distance between two pairs of latitude/longitude.getDistance()
function for each point of interest to get the distance of the user from that location;
reasonStream()
, we can check whenever a point_of_interest
has had its latitude
& longitude
returned and then calculate the distance from that to the user’s latitude/longitude.POST /hello
entrypoint.
reasonSream()
, we’ll stream back the value reasonStream()
yielded to us — eventually, when we calculate the distance of each point_of_interest
, we’ll stream that as well.Output from `POST /hello` with the distance property
Obversability
Zipkin dashboard
hello
entrypoint using the RΞASON Playground and hit the RUN QUERY
button in Zipkin, it will appear there:Zipkin dashboard with a trace
SHOW
will open the trace:A Zipkin trace
POST /hello
— it is an icicle graph.It shows the time each took during your entrypoint execution. It also shows the inputs & outputs.getDistance()
?getDistance()
multiple times per each point_of_interest
. This not necessary and is wasteful because there is no need to calculate the distance of a point_of_interest
more than once.Try to think how you can solve that.In any case, a solution is below:A solution