<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>vigoo&#39;s software development blog - jev</title>
    <link rel="self" type="application/atom+xml" href="https://blog.vigoo.dev/tags/jev/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blog.vigoo.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-09-29T18:00:00+00:00</updated>
    <id>https://blog.vigoo.dev/tags/jev/atom.xml</id>
    <entry xml:lang="en">
        <title>Making decisions with Jev in Golem</title>
        <published>2026-09-29T18:00:00+00:00</published>
        <updated>2026-09-29T18:00:00+00:00</updated>
        
        <author>
          <name>Daniel Vigovszky</name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://blog.vigoo.dev/posts/jev-and-golem/"/>
        <id>https://blog.vigoo.dev/posts/jev-and-golem/</id>
        
        <content type="html" xml:base="https://blog.vigoo.dev/posts/jev-and-golem/">&lt;h2 id=&quot;introduction&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Two weeks ago &lt;a rel=&quot;external&quot; href=&quot;https://typesafe.ai/blog/introducing-system-one-models-and-jev&quot;&gt;TypeSafe AI introduced Jev&lt;/a&gt; and for a while everyone seemed to talk about it, showing more and more interesting use cases. In this post I&#39;m not trying to find the most exciting new application for this AI model; instead I&#39;m going to show how easy it is to introduce it to a &lt;a rel=&quot;external&quot; href=&quot;https://golem.cloud&quot;&gt;Golem&lt;/a&gt; application (just like into any other TypeScript project), and use this opportunity to talk more about Golem. Why would you use Jev in a Golem application? Why would you use Golem for something like the demo we will look into?&lt;/p&gt;
&lt;h2 id=&quot;the-use-case&quot;&gt;The use case&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Jev&lt;/strong&gt; is basically a fast general purpose classifier - and it can be used for arbitrary tasks without training it first. &lt;strong&gt;Golem&lt;/strong&gt; is a platform for running agents - where agents are stateful, can communicate with each other, and can use LLMs or other AI (or non-AI) services in a fault tolerant and very safe, sandboxed environment.&lt;/p&gt;
&lt;p&gt;A simple use case I came up with for this experiment is to use &lt;strong&gt;Jev&lt;/strong&gt; as a filter and router among Golem agents. We are going to build a log ingress that maintains a sliding window of logs, and for each log line it asks Jev to classify it based on a configurable set of categories (for example security incident, service outage, data loss, performance degradation or none of these). Then we wait a bit more to acquire the surroundings of a matched log, and we forward it, together with a configurable amount of surrounding logs, to one or more other agents corresponding to the categories the classifier selected. This per-category agent then summarizes the event using an LLM and stores it in an event log.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/jev-golem-concept.svg&quot; alt=&quot;Conceptual flow from HTTP log ingress through Jev classification to per-category incident registries&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;building-it&quot;&gt;Building it&lt;/h2&gt;
&lt;p&gt;In the past I really enjoyed explaining step by step in blog posts like this how to build something using a given technology. I don&#39;t think it makes too much sense anymore - nobody is going to follow it step by step, and coding agents can learn the &lt;em&gt;how&lt;/em&gt; from the source code (Golem itself is open-source) and the &lt;a rel=&quot;external&quot; href=&quot;https://blog.vigoo.dev/posts/golem15-part9-skills/&quot;&gt;provided skills&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In this particular case I just took the last pre-release version of Golem (&lt;strong&gt;Golem 1.6&lt;/strong&gt; is to be released within a few weeks!) and created a new Golem application using the &lt;a rel=&quot;external&quot; href=&quot;https://effect.website&quot;&gt;Effect.ts&lt;/a&gt; template provided by the &lt;code&gt;golem&lt;/code&gt; CLI:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ golem new jev-demo&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;? Select languages:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  TypeScript&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; Effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Rust&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Scala&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  MoonBit&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;? Select templates:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;gt; [x] effect: A simple Effect agent implementing a durable counter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [ ] effect/streaming: A runnable Effect Durable Streams agent with HTTP routes, external streams, and forks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Finished applying template(s) [OK]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then I just gave a prompt similar to the following to a coding agent:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let&#39;s remove the counter example and build a demo. First I want you to understand how Jev can be used (https://typesafe.ai) and in addition to that, how to use it from within Effect.ts.&lt;/p&gt;
&lt;p&gt;What I want to build is the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We will have a durable ingress agent that continuously receives log lines (structured json documents). The agent&#39;s state will be a circular buffer holding always just the last 100 lines (configurable through agent config).&lt;/li&gt;
&lt;li&gt;We will also have an ephemeral facade agent that takes a plain-text log and parses timestamp / content out of it and forwards to the durable ingress&lt;/li&gt;
&lt;li&gt;Every time the durable ingress gets a new line, we will call Jev to classify it among a number of watched preconfigured categories (+ none of them). If there is a match, we remember it and whenever it gets to the middle of the circular buffer OR a given TTL expired, we forward it and the whole surrounding circular buffer to an inner durable agent that&#39;s keyed by the category name.&lt;/li&gt;
&lt;li&gt;Maybe we should use a second round of jev-based filtering on the buffer before we send it and keep only the ones it flags. Not sure&lt;/li&gt;
&lt;li&gt;The per-category actors receive one &quot;incident&quot; with the surrounding logs, and run an LLM (using effect.ai) to summarize the incident based on the logs, then stores it in its own state keyed by timestamp. It should also have a method to query the stored incidents. As these queries cannot interleave with the LLM generation, we need some trick here, maybe use the read-only method feature to cache results, or spawn a sub-agent. Let&#39;s discover what&#39;s best.&lt;/li&gt;
&lt;li&gt;All features must be exposed via HTTP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let&#39;s not use stream types / durable streams yet - we want to demonstrate that capability separately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I gave some pointers to it about how I imagine our architecture but intentionally did not give it too much detail - I was curious what kind of solution it would come up with for the interleaving problem I mentioned, for example. Using read-only methods, that I hinted in the prompt, is actually not a good solution in this case but I wanted to see how it answers.&lt;/p&gt;
&lt;p&gt;From this the agent created, deployed and tested a fully working Golem application implementing my use case correctly, with basically no further interaction needed. To test it I asked the agent to download some example log archives, run Golem locally, send sections of this downloaded archive to the ingress and then examine the various incident categories through our application&#39;s HTTP API.&lt;/p&gt;
&lt;h2 id=&quot;architecture&quot;&gt;Architecture&lt;/h2&gt;
&lt;p&gt;So instead of focusing on how it built it, let&#39;s start from the end result. The resulting architecture looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/jev-golem-architecture.svg&quot; alt=&quot;Architecture of the Jev and Golem log incident processing demo&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;understanding-the-components&quot;&gt;Understanding the components&lt;/h3&gt;
&lt;p&gt;The left section contains three categories of the public HTTP API we implemented:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is possible to post a plain text log line&lt;/li&gt;
&lt;li&gt;Or post a structured log JSON&lt;/li&gt;
&lt;li&gt;A few GET endpoints provide access to the collected incident summaries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In Golem we don&#39;t have to write HTTP endpoint definitions and routing separately (although we can for custom cases) - they can be directly defined on the &lt;strong&gt;agents&lt;/strong&gt; that are going to be serving the requests.&lt;/p&gt;
&lt;p&gt;The middle section contains our agent types, with arrows representing the message flow between them.&lt;/p&gt;
&lt;p&gt;The right section represents the 3rd party AI providers we are going to connect to - in this example it&#39;s TypeSafe AI&#39;s Jev and OpenAI.&lt;/p&gt;
&lt;h4 id=&quot;agent-types&quot;&gt;Agent types&lt;/h4&gt;
&lt;p&gt;Before talking about all the 4 agent types on the above diagram, take a note of the labels on them: &lt;strong&gt;ephemeral&lt;/strong&gt; and &lt;strong&gt;durable&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ephemeral agents&lt;/strong&gt; are stateless, one-shot entities, perfect for example for request handlers of an HTTP API. They are fast and an arbitrary number of them can run in parallel, but they are not fault tolerant - they are designed for short-living, retry-able units of work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Durable agents&lt;/strong&gt; are the default, and they represent one of Golem&#39;s main powers. Durable agents can run arbitrarily long and they survive crashes, redeployments, scaling and so on. Durable agents support &lt;em&gt;idempotency keys&lt;/em&gt; and automatically provide agent-to-agent communication with &lt;em&gt;exactly-once&lt;/em&gt; calling semantics - if an agent A calls an agent B, you can be sure that it is going to happen, and the call is not going to reach B twice even during outages. Another nice property of durable agents - which enables the fault tolerance we&#39;ve just talked about - is that they can be &lt;em&gt;suspended&lt;/em&gt; when they are idle. The runtime can remove them completely from memory, and still they are ready to be restored (by a new invocation, by a timer, etc) and they just continue running from where they were.&lt;/p&gt;
&lt;p&gt;An interesting feature emerges from the above properties - if it can be suspended and resumed, and &quot;survives anything&quot; (crashes, redeployments, etc...), we can simply store arbitrary state in an agent&#39;s memory (in regular variables) and there is no need to synchronize data into a database. This is something we are going to take advantage of in our demo!&lt;/p&gt;
&lt;p&gt;Another important property of durable agents is that their invocations are processed &lt;em&gt;sequentially&lt;/em&gt; and they cannot overlap. Maybe surprisingly this no-overlap constraint also includes promises. The invocation is only considered done when all promises have been resolved (or cancelled). This is a limitation that we probably will resolve in future versions. I actually like it though, as it also makes it much easier to understand how the whole system is executing. As we will see, this limitation requires some attention in some cases to make sure things are not blocked in the invocation queues.&lt;/p&gt;
&lt;h4 id=&quot;categoryincidents-agent&quot;&gt;CategoryIncidents agent&lt;/h4&gt;
&lt;p&gt;Let&#39;s start with the &lt;strong&gt;CategoryIncidents&lt;/strong&gt; agent. One &lt;strong&gt;instance&lt;/strong&gt; of this agent will represent the incidents of &lt;strong&gt;a given category&lt;/strong&gt; for &lt;strong&gt;a given log source&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For example if we have a log source &lt;code&gt;payment-service&lt;/code&gt; and three categories (&lt;code&gt;security-incident&lt;/code&gt;, &lt;code&gt;data-loss&lt;/code&gt;, &lt;code&gt;service-outage&lt;/code&gt;), then the following &lt;strong&gt;agent IDs&lt;/strong&gt; are going to identify different instances of the same type:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CategoryIncidents(&quot;payment-service&quot;, &quot;data-loss&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CategoryIncidents(&quot;payment-service&quot;, &quot;service-outage&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CategoryIncidents(&quot;account-service&quot;, &quot;service-outage&quot;)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The agent itself is going to be a simple agent, not having any AI enabled functionalities - just storing a list of incidents in its own state, and providing HTTP bindings to query that. When using the Effect.ts SDK for Golem, we define our data types used in our agents (whether it&#39;s on the public API or to store the state, etc.) using &lt;strong&gt;Effect Schema&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;// .. more domain data types defined with Effect Schema ..&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; Incident&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Struct&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  context&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; IncidentContext,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  status&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Literals&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt;&amp;quot;pending&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;summarized&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;failed&amp;quot;&lt;/span&gt;&lt;span&gt;]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  summary&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;NullOr&lt;/span&gt;&lt;span&gt;(Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  error&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;NullOr&lt;/span&gt;&lt;span&gt;(Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export type&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt; Incident&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; = typeof&lt;/span&gt;&lt;span&gt; Incident.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Type&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; CategoryState&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Struct&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  source&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  category&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  incidents&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Array&lt;/span&gt;&lt;span&gt;(Incident),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export type&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt; CategoryState&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; = typeof&lt;/span&gt;&lt;span&gt; CategoryState.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Type&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our agent&#39;s state will store the &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;category&lt;/code&gt; (the pair that identifies the agent) and an array of &lt;code&gt;Incident&lt;/code&gt;s, each storing the log lines belonging to the incident, and the LLM-based summarization (to be explained later).&lt;/p&gt;
&lt;p&gt;The agent itself defines not only its name, identity and invokable methods, but also the public HTTP API it is bound to:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; CategoryIncidents&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; defineAgent&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;CategoryIncidents&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  description&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;Durable incident store for one source and watched category&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  mode&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;durable&amp;quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  id&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; source&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; category&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  http&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Http.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;mount&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt;&amp;quot;/categories/{source}/{category}&amp;quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  snapshotting&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Snapshot.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;define&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    schema&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; CategoryState,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    policy&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Snapshot.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;policy&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;everyN&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt;10&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  methods&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    list&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; method&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      input&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {},&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      success&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Array&lt;/span&gt;&lt;span&gt;(Incident),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      http&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; [Http.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt;&amp;quot;/incidents&amp;quot;&lt;/span&gt;&lt;span&gt;)],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    receive&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; method&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      input&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; incident&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; IncidentContext },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      success&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Boolean&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    complete&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; method&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      input&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; incidentId&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; summary&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      success&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Boolean&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    fail&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; method&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      input&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; incidentId&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; error&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      success&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Boolean&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }),    &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is just the &lt;em&gt;definition&lt;/em&gt; of our agent - we need to actually &lt;em&gt;implement&lt;/em&gt; each method as well. It&#39;s pretty simple, just storing data in our agent&#39;s state, backed by an Effect.ts &lt;code&gt;Ref&lt;/code&gt;, for example:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;CategoryIncidents.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;implement&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt;Ref&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt;Ref&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt;CategoryState&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;  init&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; ({ source, category })&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; Ref.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;make&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt;CategoryState&lt;/span&gt;&lt;span&gt;&amp;gt;({ source, category,&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; incidents&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; [] }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;  methods&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; (state)&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;    list&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; ()&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; Ref.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;get&lt;/span&gt;&lt;span&gt;(state).&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;pipe&lt;/span&gt;&lt;span&gt;(Effect.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;map&lt;/span&gt;&lt;span&gt;(({ incidents })&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; incidents)),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  snapshot&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Snapshot.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;ref&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt;CategoryState&lt;/span&gt;&lt;span&gt;&amp;gt;(),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This agent demonstrates how the durability feature can be used to build agents corresponding to domain entities, simply storing their data in their own state. The &lt;em&gt;snapshotting&lt;/em&gt; feature (appears in both the agent definition and the implementation) is optional - Golem can preserve the agent&#39;s state even without it, but our agent was right to use it as it makes recovery from suspended state much faster.&lt;/p&gt;
&lt;h4 id=&quot;summarizationjob-agent&quot;&gt;SummarizationJob agent&lt;/h4&gt;
&lt;p&gt;The behavior we want from &lt;code&gt;CategoryIncidents&lt;/code&gt; is that when it &lt;code&gt;receive&lt;/code&gt;s a new incident, it uses an LLM to summarize it before storing the final incident in the incident list. (We can still store the non-summarized version in the incident list temporarily, so there is no delay in incident queries). The summarization, however, is a slow operation involving sending one or more requests to a 3rd party AI API. This is one of the cases where the &lt;strong&gt;sequential, non-overlapping invocation processing&lt;/strong&gt; has to be dealt with. If we simply start the (async!) summarization in the &lt;code&gt;receive&lt;/code&gt; call, our agent is not going to process any other invocations until it is fully done, including the summarization. For subsequent incoming &lt;code&gt;receive&lt;/code&gt; calls this is not a problem - we want to process them one by one anyway. But it also means our agent cannot create responses for the incident listing endpoint as it&#39;s bound to invoking the &lt;code&gt;list&lt;/code&gt; method!&lt;/p&gt;
&lt;p&gt;Our coding agent used a simple trick to avoid this problem - spawning another agent for each &lt;code&gt;receive&lt;/code&gt;ed incident, that runs in parallel to our &lt;code&gt;CategoryIncidents &lt;/code&gt;agent and just sends back the summarization result to it when it is done.&lt;/p&gt;
&lt;p&gt;This agent shows us a couple of new features of Golem.&lt;/p&gt;
&lt;p&gt;First of all, the LLM call requires an API key (and an API URL and model name). We can define this required configuration using Golem&#39;s &lt;code&gt;defineConfig&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export class&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt; SummarizationConfig&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; defineConfig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt;&amp;quot;SummarizationJob.Config&amp;quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  openAiApiUrl&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  openAiModel&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  openAiApiKey&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Redacted&lt;/span&gt;&lt;span&gt;(Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}) {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in the &lt;em&gt;agent definition&lt;/em&gt; we just add it as a property:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;defineAgent&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    config&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; SummarizationConfig,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and in the &lt;em&gt;agent implementation&lt;/em&gt; we can just get it from our Effect&#39;s dependencies:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; yield*&lt;/span&gt;&lt;span&gt; SummarizationConfig;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the implementation of our summarization agent&#39;s &lt;code&gt;run&lt;/code&gt; method just uses the &lt;code&gt;@effect/ai-openai&lt;/code&gt; package to call OpenAI with the configured URL, token and model, with a prompt constructed from the log lines passed to &lt;code&gt;run&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;run&lt;/code&gt; method is defined like this:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;defineAgent&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    methods&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      run&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; method&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        input&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; incident&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; IncidentContext },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        success&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Void&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;    // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the interesting feature that &lt;code&gt;defineAgent&lt;/code&gt; provides is a fully type-safe way to &lt;strong&gt;invoke an agent from another agent&lt;/strong&gt;. Here is how &lt;code&gt;receive&lt;/code&gt; in the &lt;code&gt;CategoryIncidents&lt;/code&gt; agent&#39;s implementation triggers the summarization:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; job&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; yield*&lt;/span&gt;&lt;span&gt; SummarizationJob.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;client&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;get&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  source&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; current.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;source&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  category&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; current.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;category&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  incidentId&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; incident.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;id&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;yield*&lt;/span&gt;&lt;span&gt; job.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;run&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;trigger&lt;/span&gt;&lt;span&gt;({ incident });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;get&lt;/code&gt; means &quot;get or create&quot; - the parameters passed are the constructor parameters for our &lt;code&gt;SummarizationJob&lt;/code&gt; agent, and they uniquely identify an agent instance. This means there is going to be &lt;strong&gt;one summarization job per incident&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Then we &lt;strong&gt;trigger&lt;/strong&gt; the run method, passing the incident. Triggering means we put the invocation in the other agent&#39;s invocation queue (with guaranteed delivery) and then continue - so we are not blocking our sender agent with the summarization job.&lt;/p&gt;
&lt;p&gt;When the summarization is done, the other agent uses the same technique to send back the summarization result:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; categoryStore&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; yield*&lt;/span&gt;&lt;span&gt; CategoryIncidents.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;client&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;get&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  source&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; current.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;source&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  category&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; current.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;category&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;yield*&lt;/span&gt;&lt;span&gt; categoryStore.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;complete&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  incidentId&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; current.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;incidentId&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  summary&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; result.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;text&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here our agent wrote &lt;code&gt;.complete&lt;/code&gt; and not &lt;code&gt;.complete.trigger&lt;/code&gt; - this is the variant that awaits the remote invocation&#39;s completion. In this particular case it is not making a lot of difference, but helps a little with debugging our application&#39;s state - the summarization job&#39;s &lt;strong&gt;status&lt;/strong&gt; field (in its own state) only becomes &lt;code&gt;completed&lt;/code&gt; when it knows the &lt;code&gt;CategoryIncidents&lt;/code&gt; agent already received the summary.&lt;/p&gt;
&lt;h5 id=&quot;read-only-methods&quot;&gt;Read-only methods&lt;/h5&gt;
&lt;p&gt;In the agent prompt I mentioned read-only methods to the agent as a possible way to deal with the problem of not being able to return an incident list while summarization is running. Read-only methods cache their values (with some configurable expiration) and if the cache is hot, it requires no actual invocation to serve the HTTP request bound to them.
This is a useful optimisation technique but it would not solve our problem reliably here - it is not guaranteed that the cache is valid, so from time to time external requests trying to query the incident list would block on ongoing summarizations, if we would not have this separate &lt;code&gt;SummarizationJob&lt;/code&gt; agent.&lt;/p&gt;
&lt;h4 id=&quot;logingress-agent&quot;&gt;LogIngress agent&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;LogIngress&lt;/code&gt; agent is where we integrate &lt;strong&gt;Jev&lt;/strong&gt; in our application. It is a durable agent, with one instance per log source. The agent&#39;s state is a sliding window of observed structured log lines, and observed incidents. When we detect an incident with &lt;strong&gt;Jev&lt;/strong&gt; we are not immediately sending it to our &lt;code&gt;CategoryIncidents&lt;/code&gt; agent. First we wait a bit for follow-up log lines. We send it either when the classified log line is in the middle of our buffer, or after a configurable TTL (to make sure we handle the matches even if no follow-up log lines are arriving).&lt;/p&gt;
&lt;p&gt;This is an example of having a more complicated in-memory state, where Golem&#39;s &lt;strong&gt;durable execution&lt;/strong&gt; guarantees make this very easy to implement. Imagine writing this on a traditional runtime. We would have to persist the log lines, deal with throwing away old ones (or choose a database that supports such a construct), we would separately have to persist the candidates until we are ready to send them to the incident store agents, and so on. With Golem we don&#39;t have to do any of that - just simple in-memory state, guaranteed to never get lost:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; PendingMatch&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Struct&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  id&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  category&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  probability&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  matchedAt&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  matchedSequence&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; LogIngressState&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Struct&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  source&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  nextSequence&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Number&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  lines&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Array&lt;/span&gt;&lt;span&gt;(SequencedLogLine),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  pending&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Array&lt;/span&gt;&lt;span&gt;(PendingMatch),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export type&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt; LogIngressState&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; = typeof&lt;/span&gt;&lt;span&gt; LogIngressState.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Type&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sending a pending incident to the other agent works with the same generated type-safe client interface as the one we&#39;ve already seen. The interesting part of this agent is how we use &lt;strong&gt;Jev&lt;/strong&gt; for routing among the agents, and how we deal with the TTL.&lt;/p&gt;
&lt;p&gt;To ask &lt;strong&gt;Jev&lt;/strong&gt;, we can use the official Effect.ts library &lt;code&gt;&quot;@effect/ai-typesafe&quot;&lt;/code&gt;. We store the classifier configuration itself in the agent&#39;s configuration:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;export class&lt;/span&gt;&lt;span style=&quot;color: #C18401;&quot;&gt; LogIngressConfig&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; extends&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt; defineConfig&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt;&amp;quot;LogIngress.Config&amp;quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  bufferSize&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; WitTypes.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Int32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  contextTtlSeconds&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; WitTypes.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;Int32&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  jevApiUrl&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  jevModel&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  categories&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Array&lt;/span&gt;&lt;span&gt;(WatchedCategory),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  typesafeApiKey&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Schema.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;Redacted&lt;/span&gt;&lt;span&gt;(Schema.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;String&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}) {}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#39;ve already seen this where we specified the OpenAI API keys, model and URL. The nice thing about Golem&#39;s configuration support is not only that it provides these values through the Effect.ts SDK as a layer; it also &lt;strong&gt;ensures&lt;/strong&gt; that all the configuration is present at deploy-time, and guarantees that it is type safe. In addition to that, secrets (&lt;code&gt;Schema.Redacted(...)&lt;/code&gt; in Effect.ts) are treated as secrets on the Golem deployment level too. They are redacted everywhere, and Golem provides special APIs and CLI commands to &lt;strong&gt;rotate them&lt;/strong&gt; without redeploying your application.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;golem.yaml&lt;/code&gt; (our application&#39;s Golem specific manifest) we can specify our agent&#39;s default configuration and the initial values for the secrets (&lt;em&gt;initial&lt;/em&gt;, because they can be rotated later):&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;agents&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  LogIngress&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    config&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      bufferSize&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; 100&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      contextTtlSeconds&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; 30&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      jevApiUrl&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;https://api.typesafe.ai/v1&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      jevModel&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;jev-latest&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;      categories&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; security-incident&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        description&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;Authentication attacks, unauthorized access, suspicious activity, or security policy violations&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        threshold&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; 0.65&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; service-outage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        description&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;A service is unavailable, unhealthy, repeatedly crashing, or failing requests&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        threshold&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; 0.65&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; data-loss&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        description&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;Data was deleted, corrupted, lost, or cannot be recovered&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        threshold&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; 0.65&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; performance-degradation&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        description&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;Latency, saturation, resource pressure, or throughput is materially worse than normal&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;        threshold&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; 0.65&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;# ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;secretDefaults&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  local&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    typesafeApiKey&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;{{ TYPESAFE_API_KEY }}&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    openAiApiKey&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span style=&quot;color: #50A14F;&quot;&gt; &amp;quot;{{ OPENAI_API_KEY }}&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above secret syntax tells Golem to use the &lt;code&gt;TYPESAFE_API_KEY&lt;/code&gt; and &lt;code&gt;OPENAI_API_KEY&lt;/code&gt; environment variables from the machine we are deploying from.&lt;/p&gt;
&lt;p&gt;Getting the above config from our agent&#39;s dependencies and using the &lt;strong&gt;Jev&lt;/strong&gt; API is straightforward:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; config&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; yield*&lt;/span&gt;&lt;span&gt; LogIngressConfig;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A0A1A7;font-style: italic;&quot;&gt;// ... extract decisions and other properties from config&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; definition&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span&gt; Decision.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;make&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; input&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; LogDecisionInput, decisions });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; clientLayer&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span&gt; TypeSafeClient.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;layer&lt;/span&gt;&lt;span&gt;({ apiKey, apiUrl }).&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Layer.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;provide&lt;/span&gt;&lt;span&gt;(FetchHttpClient.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;layer&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; modelLayer&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span&gt; TypeSafeDecisionModel.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;layer&lt;/span&gt;&lt;span&gt;({ model }).&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Layer.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;provide&lt;/span&gt;&lt;span&gt;(clientLayer),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; response&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; yield*&lt;/span&gt;&lt;span&gt; DecisionModel.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;decide&lt;/span&gt;&lt;span&gt;(definition, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;  input&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    timestamp&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; line.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;timestamp&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    content&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; line.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;content&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;    attributes&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; Record.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;fromIterableWith&lt;/span&gt;&lt;span&gt;(line.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;attributes&lt;/span&gt;&lt;span&gt;, ({ key, value })&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; [key, value]),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}).&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;pipe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Effect.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;provide&lt;/span&gt;&lt;span&gt;(modelLayer),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can call &lt;code&gt;decide&lt;/code&gt; for each incoming log-line and get back probabilities of matching each configured category. We convert this to a list of category matches - if the list is empty, it did not match &lt;em&gt;any&lt;/em&gt; of our classifier categories. Then we store the matches in our agent&#39;s in-memory state, and check if any previously pending items are now ready to be sent (when they are in the &quot;middle&quot; of our sliding window). Sending them is trivial - just one more agent-to-agent call using the typed client that comes free from &lt;code&gt;defineAgent&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The only new thing here is how to deal with the TTL; what if we have a match, but then nobody sends a new log line ever again - we would have the pending match pending forever. Spawning a timer purely in the JavaScript (Effect.ts) world is not an option because of the requirement that invocations cannot overlap - if we still had a pending promise at the end of &lt;code&gt;receive&lt;/code&gt;, new log lines would never arrive. But we also use the &lt;em&gt;clients&lt;/em&gt; defined by &lt;code&gt;defineAgent&lt;/code&gt; to &lt;strong&gt;schedule&lt;/strong&gt; an invocation in the future, and we can even do that on ourselves!&lt;/p&gt;
&lt;p&gt;Here is what this looks like in code:&lt;/p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #383A42; background-color: #FAFAFA;&quot; &gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; self&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; yield*&lt;/span&gt;&lt;span&gt; LogIngress.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;client&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;get&lt;/span&gt;&lt;span&gt;({ source });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; match&lt;/span&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt; of&lt;/span&gt;&lt;span&gt; newPending) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #A626A4;&quot;&gt;  yield*&lt;/span&gt;&lt;span&gt; self.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;flushPending&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;schedule&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #4078F2;&quot;&gt;    scheduledAt&lt;/span&gt;&lt;span&gt;(now&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; +&lt;/span&gt;&lt;span&gt; ttlSeconds&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color: #986801;&quot;&gt; 1_000&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt; matchId&lt;/span&gt;&lt;span style=&quot;color: #0184BC;&quot;&gt;:&lt;/span&gt;&lt;span&gt; match.&lt;/span&gt;&lt;span style=&quot;color: #E45649;&quot;&gt;id&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;h4 id=&quot;plaintextlogfacade-agent&quot;&gt;PlainTextLogFacade agent&lt;/h4&gt;
&lt;p&gt;The last agent from our diagram is an &lt;strong&gt;ephemeral agent&lt;/strong&gt;. It has no state, and Golem spawns a fresh instance for each invocation - in our case, for each incoming POST request on the plain-text log ingress endpoint.
In our demo there is no serious reason to have this log parsing in its own ephemeral agent - the reason I wanted to have it is to demonstrate how ephemeral agents can be used as the implementation of HTTP APIs while they can still communicate with the underlying durable agents.
We can argue for using ephemeral agents as the plain-text parsers to avoid the computation cost of parsing the log lines on the &lt;code&gt;LogIngress&lt;/code&gt; agent itself.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Adding &lt;strong&gt;Jev&lt;/strong&gt; based decisions to a Golem application is trivial. You can use it to control agent loops, make routing decisions between multiple agents, and so on.
This post used the &lt;em&gt;Effect.ts SDK&lt;/em&gt; for Golem, but you can also write Golem applications in plain TypeScript, in Rust, Scala and MoonBit.&lt;/p&gt;
&lt;p&gt;The above example was built using a pre-release version of &lt;strong&gt;Golem 1.6&lt;/strong&gt;. The final version is coming out soon. We did not really use any 1.6 features in it though - everything described here is possible using the current stable Golem 1.5 release, except that its Effect.ts SDK depends on an &lt;em&gt;older&lt;/em&gt; version of Effect.ts with no &lt;code&gt;@effect/ai-typesafe&lt;/code&gt; package, so we would have had to write our Jev client for ourselves using Effect&#39;s HTTP client API.&lt;/p&gt;
&lt;p&gt;The whole demo is available &lt;a rel=&quot;external&quot; href=&quot;https://github.com/vigoo/jev-demo&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
</content>
        
    </entry>
</feed>
