<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Daniel Imfeld's blog - All Content]]></title><description><![CDATA[Daniel Imfeld's Writing and Notes]]></description><link>https://imfeld.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 08 Jan 2025 07:14:45 GMT</lastBuildDate><atom:link href="https://imfeld.dev/rss/all.xml" rel="self" type="application/rss+xml"/><copyright><![CDATA[Daniel Imfeld 2021]]></copyright><language><![CDATA[English]]></language><managingEditor><![CDATA[Daniel Imfeld]]></managingEditor><category><![CDATA[technology]]></category><category><![CDATA[programming]]></category><item><title><![CDATA[2025-01-07]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li>Most of my time lately has been spent between my new startup and my new baby, but when I can squeeze in some free time I&apos;m trying my hand at an autonomous programming agent based around Aider. First attempts with prompts I found from elsewhere were just mediocre, but I&apos;m thinking something like this:</li>
        <li>Aside from Aider&apos;s internal repo map, I&apos;ll create a list of all the files in the repository and use an LLM to create a short description of each one.</li>
        <li>Core loop:
          <ul class="list-bullet">
            <li>Get a task from plan.md</li>
            <li>Use file_map.yaml to ask about relevant files, add these to Aider.</li>
            <li>Generate a plan to implement this task. Lay out the interfaces between the frontend and backend, but do not generate any code except for type definitions.</li>
            <li>Generate integration tests for the plan</li>
            <li>Generate the frontend code based on the plan.</li>
            <li>Generate the backend code based on the plan and the generated frontend code.</li>
            <li>Check types and edit until type checks pass</li>
            <li>Generate unit tests for the code</li>
            <li>run/edit loop on tests</li>
            <li>once tests pass, run format step</li>
            <li>run postmortem step updating docs and lessons</li>
            <li>update file_map.yaml file to describe any newly added files or files that</li>
          </ul>        </li>
        <li>As I make progress on this I may need to also come up with a system for developing detailed plan documents for each task. It can be done in ChatGPT/Claude but it gets a bit hard to manage all the different feature-specific chats all in one, and it would be nice to have this live alongside the actual coding agent and be informed by the repository structure.</li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2025/01/2025-01-07</link><guid isPermaLink="false">8276e299937153d26c73e38ee9355bb9f6babc8b23388075a8c94ba9f984c646</guid><pubDate>Tue, 07 Jan 2025 00:00:00 GMT</pubDate></item><item><title><![CDATA[JS Sidecar]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>This is a Rust library which, instead of embedding a JavaScript engine directly into the application, communicates with a persistent pool of engine instances that are set up to execute code.</li>
    <li>It&apos;s somewhat difficult right now to embed a well-equipped JS engine instance into your application if you want to have full API availability, and so this gets around that problem while avoiding the overhead of starting a new process for every expression evaluation.</li>
    <li><h2>Task List</h2>
      <ul class="list-bullet">
        <li><h3>Up Next</h3></li>
        <li><h3>Soon</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Allow passing additional dependencies which can be used in the scripts
              <ul class="list-bullet">
                <li>This would probably be paths to somewhere on the filesystem that contains a library with package.json and all.</li>
                <li>Or maybe just a simple package.json with node_modules directory and then the script can be run with that as the CWD</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Support callbacks from the JS side to the Rust side
              <ul class="list-bullet">
                <li>e.g. to do expensive lookups, mutate state, etc.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Later/Maybe</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Consider using worker_threads instead of cluster
              <ul class="list-bullet">
                <li>This should use a bit less memory (though probably not much less), but is complicated by the lack of ability to send sockets across thread boundaries, unlike cluster.</li>
                <li>The bigger advantage is that it will make it easier to maintain a single code cache, and share data between threads in a zero-copy manner, using SharedArrayBuffers.</li>
                <li>One approach is to have a single master thread create a new MessagePort pair for each connection. A thread gets the port and then the master shuttles buffers back and forth between them.
                  <ul class="list-bullet">
                    <li>Mostly implemented here <a href="https://www.phind.com/search?cache=zdy35n834rpmds1cmiiszwt8">https://www.phind.com/search?cache=zdy35n834rpmds1cmiiszwt8</a>.</li>
                    <li>Feels bad for latency though</li>
                  </ul>                </li>
                <li>Another way would be for each thread to have its own socket and then the Rust side communicates with each one and handles the load balancing. This would also potentially make it easier to share other things like persistent contexts but that is probably over complicating it.
                  <ul class="list-bullet">
                    <li>This isn&apos;t great as a general approach but works well for this case because we know that there&apos;s just a single client (the Rust process) with exact knowledge of how things are structured.</li>
                    <li>I&apos;ll probably go this route.</li>
                  </ul>                </li>
                <li>Need to benchmark these approaches</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Support multiple engines (Bun, Deno)
              <ul class="list-bullet">
                <li>Bun doesn&apos;t support <code>node:cluster</code> yet</li>
                <li>Deno should work with minimal changes</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Run entire projects
              <ul class="list-bullet">
                <li>with <code>node_modules</code> and so on</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Support running Boa or QuickJS in-process with the same interface</li>
            <li><input type="checkbox" disabled> Allow control over which Node built-in libraries can be imported
              <ul class="list-bullet">
                <li>Currently none can be imported, this wouldn&apos;t be hard to change but making it configurable would be great.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Network sandbox
              <ul class="list-bullet">
                <li>This can be done most easily by replacing <code>fetch</code> with a function that wraps fetch and checks the URL before passing on the request</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Filesystem sandbox
              <ul class="list-bullet">
                <li>Same as network, but for the <code>fs</code> functions. This is a bit more difficult to get right since there are so many functions.</li>
                <li>Ideally we could just run the isolate in a chroot, but I don&apos;t think that&apos;s possible.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Benchmarks</li>
            <li><input type="checkbox" disabled checked> Support V8 code cache when evaluating code
              <ul class="list-bullet">
                <li>Need some kind of content-addressable LRU caching so we don&apos;t hold on to code cache values forever</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Customizable number of workers</li>
            <li><input type="checkbox" disabled checked> Improve Rust API for an engine connection
              <ul class="list-bullet">
                <li>Single function to run a script and wait for its result</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Start a single engine</li>
            <li><input type="checkbox" disabled checked> JS code to handle requests, run code, and respond</li>
            <li><input type="checkbox" disabled checked> Send requests to the engine, get a response</li>
            <li><input type="checkbox" disabled checked> Support one-way communication of a script back to the Rust side (e.g. progress messages)</li>
            <li><input type="checkbox" disabled checked> Ability to reuse a context across multiple requests without reinitializing it each time.</li>
            <li><input type="checkbox" disabled checked> Hook into <code>console</code> so that it goes back to Rust in a way that is linked to each request.</li>
            <li><input type="checkbox" disabled checked> Run a pool of engines and load balance requests between them.</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Basic Design</h2>
      <ul class="list-bullet">
        <li><h3>Rust Side</h3>
          <ul class="list-bullet">
            <li>Start and maintain a pool of engines</li>
            <li>Allow passing JS code to the engine, either as strings or as filesystem references</li>
            <li>Support callbacks which</li>
          </ul>        </li>
        <li><h3>JS Side</h3>
          <ul class="list-bullet">
            <li>The core is a small application which takes in requests</li>
            <li>Support auto-importing modules from the filesystem before running the expression.</li>
            <li>Support compilation snapshots, if possible
              <ul class="list-bullet">
                <li>Looks like Node.js support this via <a href="https://nodejs.org/api/vm.html#sourcetextmodulecreatecacheddata">createCachedData</a>. It doesn&apos;t cache stored data but does cache functions.</li>
              </ul>            </li>
            <li>Library which can callback to the Rust side to take actions</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Alternatives</h2>
      <ul class="list-bullet">
        <li><h3>Why not Deno?</h3>
          <ul class="list-bullet">
            <li>In <a href="https://github.com/dimfeld/ergo/tree/master/js">my experience in the past</a> this worked out ok, but
              <ul class="list-bullet">
                <li>you have to set up a lot of runtime stuff yourself</li>
                <li>Updating all the Deno crates together is a pain and there were often breaking changes to be handled.</li>
              </ul>            </li>
            <li>Some of this might be better now as Deno itself has matured, but overall it seemed that embedding a &quot;full-featured Deno&quot; is not really as easy as it should be.</li>
          </ul>        </li>
        <li><h3>Why not bindings to QuickJS/Boa/etc?</h3>
          <ul class="list-bullet">
            <li>Runtime compatibility. Some applications may need to do things that only really work in the most popular JS engines, such as talking to Postgres.</li>
            <li>Harder to include arbitrary NPM packages or similar, without bundling</li>
            <li>For applications that don&apos;t need this, I do think it&apos;s worth providing a mode that just uses bindings and won&apos;t have to start a separate sidecar process.</li>
            <li>As QuickJS gets more WinterCG compatibility this also may be less of an issue.</li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_js_sidecar</link><guid isPermaLink="false">dc3f799b2ead3012016bf7a3b5e02e059d7793905106637085ce36ed1c33811d</guid><category><![CDATA[Projects]]></category><pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-07-11]]></title><description><![CDATA[<html><head></head><body><h3>Learning</h3>
        <p>Today I read <a href="https://arxiv.org/abs/2403.11322">StateFlow: Enhancing LLM Task-Solving through State-Driven Workflows</a>. This paper talks about using state machines to explicitly model the problem that an agent is trying to solve. They got great results, and I agree that it&apos;s a good way to go about solving a problem, but I&apos;m not sure what&apos;s really new here. Maybe it&apos;s just my background, but using state machines to direct the high-level behavior of your agent feels like a very obvious thing to do, and a lot of people in the LLM space have been talking about it for a while now.</p></body></html>]]></description><link>https://imfeld.dev/journals/2024/07/2024-07-11</link><guid isPermaLink="false">8aa3f1cc2cdb6831f38a4e998725312a4f6717dc9f51fd426a590edb328091c1</guid><pubDate>Thu, 11 Jul 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-07-10]]></title><description><![CDATA[<html><head></head><body><h3>Learning</h3>
        <p>Today I presented a review of <a href="https://arxiv.org/abs/2404.11584">The Landscape of Emerging AI Agent Architectures for Reasoning, Planning, and Tool Calling: A Survey</a> at the Latent Space paper club. While it wasn&apos;t recorded, <a href="https://docs.google.com/presentation/d/1EH11pXLanLMoLGEXd_YduYxEEk3X2Y63KOOFsysq1LY">my slides are published on Google Slides</a>.</p></body></html>]]></description><link>https://imfeld.dev/journals/2024/07/2024-07-10</link><guid isPermaLink="false">67b9477b97a7bd465e33bca10187b819f59b22026e8deb15a0c5879313d541cb</guid><pubDate>Wed, 10 Jul 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[The Landscape of Emerging AI Agent Architectures for Reasoning, Planning, and Tool Calling - A Survey]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li><a href="https://arxiv.org/abs/2404.11584">The Landscape of Emerging AI Agent Architectures for Reasoning, Planning, and Tool Calling: A Survey</a></li>
    <li><h2>Introduction</h2>
      <ul class="list-bullet">
        <li>The paper defines agents as a system that uses planning, loops, reflection, and other control structures, as well as leveraging the model&apos;s reasoning abilities to accomplish a task.</li>
        <li>This paper focuses mostly on the difference between single-agent vs. multiple-agent architectures.</li>
        <li>The multiple agent architectures are then subdivided into vertical and horizontal architectures.</li>
        <li>Each agent has a persona which is basically the system prompt as well as the tools that the agent has access to.
          <ul class="list-bullet">
            <li>In addition to the instructions for the task, the persona may define a specific role such as an expert coder or a manager or a reviewer and so on.</li>
          </ul>        </li>
        <li>Tools of course are external function calls that the model can request, such as editing a document, searching the web, or other actions that the model is not able to do inside its computations.</li>
        <li>The paper defines a single agent architecture as those powered by a single language model which performs all the tasks on its own with no feedback from other models or agents. There may be feedback from humans though.</li>
        <li>In a multi-agent setup, each agent typically has a different persona.</li>
        <li>In a vertical multi-agent architecture, one agent access to leader and other agents report to it. There could be multiple levels of hierarchy as well. But the main distinction is the clear division of labor between the different sub-agents.</li>
        <li>In a horizontal architecture, the agents are all more or less equal and are part of a single discussion about the task. Communication between agents is shared between all of the agents. And agents can volunteer themselves to complete certain tasks or call tools.</li>
      </ul>    </li>
    <li><h2>Key Considerations</h2>
      <ul class="list-bullet">
        <li><h3>Reasoning</h3>
          <ul class="list-bullet">
            <li>Reasoning is basically the same thing that we humans do where we think critically about a problem, understand how it fits into the world around us, and make a decision.</li>
            <li>For a model, reasoning is what allows it to go beyond its training data and learn new tasks or make decisions under new circumstances.</li>
          </ul>        </li>
        <li><h3>Planning</h3>
          <ul class="list-bullet">
            <li>Planning is an application of reasoning.</li>
            <li>And there are five major approaches to it. Task decomposition, multi-plan selection, external modulated planning, reflection and refinement, and memory augmented planning. See <a href="https://arxiv.org/pdf/2402.02716">understanding the planning of LLM agents</a>.</li>
            <li>Most agents have a dedicated planning step, which they run before executing any actions. There are many ways to do this. The paper particularly calls out <a href="https://arxiv.org/pdf/2402.02805">Graph-enhanced Large Language Models in Asynchronous Plan Reasoning</a> AKA &quot;Plan like a graph,&quot; and <a href="https://arxiv.org/abs/2305.10601">tree of thought</a> as examples which allow the agent to execute multiple steps in parallel.
              <ul class="list-bullet">
                <li>Although my recollection of Tree of Thought was that it was more about trying different permutations of problem solving and not so much about planning.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Tool Calling</h3>
          <ul class="list-bullet">
            <li>Tool calling goes hand in hand with reasoning and is what really allows the model to make effective and informed decisions.</li>
            <li>Many agents use some iterative process of planning, reasoning, tool calling, and then breaking up the task into further sub steps with more planning and so on.</li>
            <li>But some papers point out that single agent architectures often have trouble with these long chains of subtasks.
              <ul class="list-bullet">
                <li><a href="https://arxiv.org/pdf/2403.03031">https://arxiv.org/pdf/2403.03031</a></li>
                <li><a href="https://arxiv.org/pdf/2401.17464">https://arxiv.org/pdf/2401.17464</a></li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Single Agent Architectures</h2>
      <ul class="list-bullet">
        <li>Proper planning and self-correction is paramount here.</li>
        <li>A big risk with single agent architectures is that because they don&apos;t have any external method of automatically correcting themselves, they may get stuck in an infinite loop where they reason the same step over and over again with the same result.</li>
        <li>ReAct
          <ul class="list-bullet">
            <li><a href="https://arxiv.org/abs/2210.03629">ReAct: Synergizing Reasoning and Acting in Language Models</a> was one of the first single agent methods designed to improve over single-step prompting.  In React, which stands for Reason Plus Act, the agent has a cycle of thinking about a task, performing an action based on that thought and observing the output.</li>
            <li>Aside from improved reliability, one big advantage of this method over previous single-prompt methods was that the sequence of thoughts and actions are all there to see, so it&apos;s easier to figure out how the model arrived at its conclusion.</li>
            <li>But ReAct is susceptible to the infinite loops mentioned above.</li>
          </ul>        </li>
        <li>RAISE
          <ul class="list-bullet">
            <li>RAISE, as described in <a href="https://arxiv.org/abs/2401.02777">From LLM to Conversational Agent: A Memory Enhanced Architecture with Fine-Tuning of Large Language Models</a>, stands for Reasoning and Acting through Scratch pad and Examples. There&apos;s no &apos;I&apos; but I guess they thought RAISE sounded better than RASPE or something.</li>
            <li>It&apos;s based on the ReAct method, but adds the scratchpad for short-term storage and a data set of similar previous examples for long-term storage.
              <ul class="list-bullet">
                <li>NOTE How does this work?</li>
              </ul>            </li>
            <li>One interesting problem that the race paper found was that agents would often exceed their defined roles such as a sales agent role which ended up writing Python code. The authors also cited problems with hallucinations and difficulty understanding complex logic.</li>
          </ul>        </li>
        <li>Reflexion
          <ul class="list-bullet">
            <li><a href="https://arxiv.org/abs/2303.11366">Reflexion: Language Agents with Verbal Reinforcement Learning</a></li>
            <li>Reflexion is a method in which the agent is asked to reflect on its own performance with certain metrics such as success state and if the current trajectory matches the agent&apos;s desired task.</li>
            <li>NOTE look at the paper to determine more about these</li>
            <li>Some limitations cited by the authors
              <ul class="list-bullet">
                <li>Reflextion is prone to falling into non-optimal local minima</li>
                <li>The agent&apos;s memory is simply stored in the model&apos;s context with a sliding window and so older important items may be forgotten</li>
              </ul>            </li>
          </ul>        </li>
        <li>AutoGPT + P
          <ul class="list-bullet">
            <li><a href="https://arxiv.org/abs/2402.10778">AutoGPT+P: Affordance-based Task Planning with Large Language Models</a></li>
            <li>AutoGPT+P is a technique specifically designed for use in robotics. It uses computer vision to detect the objects present in a scene. And then can use four tools to try to complete its task.
              <ul class="list-bullet">
                <li>Plan Tool</li>
                <li>Partial Plan Tool</li>
                <li>Suggest Alternative Tool</li>
                <li>Explore Tool</li>
              </ul>            </li>
            <li>The model also works in concert with a traditional planning tool using PDDL or planning domain definition language. This planner helps with translating the model&apos;s instructions into things that the robot is actually able to do given its physical limitations.</li>
            <li>As with many of the above approaches, it does have some problems such as sometimes choosing the wrong tools or getting stuck in loops. And at least as described in the paper, there&apos;s no opportunity for human interaction such as the agent asking for clarification or the human interrupting if the robot starts to do something wrong.</li>
          </ul>        </li>
        <li>LATS
          <ul class="list-bullet">
            <li><a href="https://arxiv.org/abs/2310.04406">Language Agent Tree Search Unifies Reasoning Acting and Planning in Language Models</a></li>
            <li>LATS is an algorithm based on <a href="https://www.phind.com/search?cache=uq80hftxd0w5iru7qg0t8kny">Monte Carlo Tree Search</a>. You can read the link for more details, but basically, it&apos;s inspired by Monte Carlo simulation in which you do a bunch of random runs to get a better idea of the probability space and the best action to take.</li>
            <li>But as you can imagine, doing a bunch of random runs down a tree with language models can be very slow and expensive. Also, the paper doesn&apos;t tackle particularly complex scenarios.</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Multi Agent Architectures</h2>
      <ul class="list-bullet">
        <li>Common themes with multi-agent architectures
          <ul class="list-bullet">
            <li>Leadership of agent teams</li>
            <li>Dynamic creation of agent teams between stages</li>
            <li>Information sharing between team members</li>
          </ul>        </li>
        <li><a href="https://arxiv.org/abs/2403.12482">Embodied LLM Agents Learn to Cooperate in Organized Teams</a>
          <ul class="list-bullet">
            <li>This method uses a hybrid approach that is mostly a horizontal team, but has a leader agent over the rest of the team.</li>
            <li>They found that teams with a leader finished their tasks about 10% faster and that without a leader the agents spend about half of their time giving orders to each other. Whereas with a single designated leader, the leader spends 60% of its messages giving directions, while the other agents can focus more on actual exchange of useful information.</li>
          </ul>        </li>
        <li>DyLAN
          <ul class="list-bullet">
            <li><a href="https://arxiv.org/abs/2310.02170">Dynamic LLM-Agent Network: An LLM-agent Collaboration Framework with Agent Team Optimization</a></li>
            <li>DyLAN is a dynamic team method which uses elimination rounds to remove the agents that have contributed the least to the task.</li>
            <li>Team Optimization
              <ul class="list-bullet">
                <li>Each agent is asked to rank the other agents&#x2019; results</li>
                <li>These ratings are aggregated</li>
                <li>An &#x201C;Agent Importance Score&#x201D; is calculated</li>
                <li>Low-performing agents are removed from the system</li>
              </ul>            </li>
          </ul>        </li>
        <li>AgentVerse
          <ul class="list-bullet">
            <li><a href="https://arxiv.org/abs/2308.10848">AgentVerse: Facilitating Multi-Agent Collaboration and Exploring Emergent Behaviors</a></li>
            <li>Agentverse uses a four-stage process
              <ul class="list-bullet">
                <li>Recruitment, which uses a &#x201C;recruiter&#x201D; agent to generate personas for a set of agents to work on this iteration, based on the current goal state.</li>
                <li>Collaborative decision-making between the agents.
                  <ul class="list-bullet">
                    <li>This can be vertical or horizontal arrangement, depending on the task.</li>
                  </ul>                </li>
                <li>Independent action execution by each agent
                  <ul class="list-bullet">
                    <li>Each agent uses a ReAct loop with up to 10 iterations to get to the desired output</li>
                  </ul>                </li>
                <li>Evaluation of how close the current state is to the goal.</li>
              </ul>            </li>
            <li>This process can be repeated until the goal is reached.</li>
            <li>One important finding here is that agent feedback is not always reliable.
              <ul class="list-bullet">
                <li>Even if an agent&#x2019;s feedback is not valid, the receiving agent may incorporate it anyway.</li>
              </ul>            </li>
          </ul>        </li>
        <li>MetaGPT
          <ul class="list-bullet">
            <li><a href="https://arxiv.org/abs/2308.00352">MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework</a></li>
            <li>MetaGPT focuses on using structured outputs to communicate between agents instead of plain text in order to reduce unproductive chatter and inefficiencies, such as &quot;how are you? I&apos;m fine&quot;.</li>
            <li>It also implements a message bus which allows agents to publish their information to a common place but only listen to information that is relevant to them.</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Discussion</h2>
      <ul class="list-bullet">
        <li>Single agent patterns tend to work best with a narrowly defined list of tools and well defined processes. They&apos;re also easier to implement because there&apos;s only one agent and set of tools, and they don&apos;t face the limitations of multi-agent systems like poor feedback from other agents or unrelated chatter from other team members. But they are more likely to get stuck in loops and fail to make progress if they find themselves in a situation that does not match their reasoning strengths.</li>
        <li>Multi-agent architectures work best when feedback from different personas helps to accomplish the task, such as drafting a document and then reviewing or proofreading it. They&apos;re also useful for performing parallel execution when there are distinct independent subtasks. Multi-agent architecture is particularly advantageous when no examples of the task have been provided.</li>
        <li>Feedback can be very helpful, but it&apos;s not a panacea. The AgentVerse paper notes a case where an agent gave invalid feedback to another agent, but it was still incorporated. Similarly, human feedback may conflict with the desired behavior of the agent, but because the language models tend to be willing to please, they may incorporate it anyway.</li>
        <li><h3>Information Sharing</h3>
          <ul class="list-bullet">
            <li>Information sharing in a horizontal multi-agent system is very useful, but also has issues. For example, agents can too closely simulate a human when assigned a persona and start asking the other agents small-talk questions such as &quot;how are you?&quot; Agents may also be exposed to information that is irrelevant to their particular task, so systems that allow subscribing or filtering incoming information can be helpful for keeping an agent on task.</li>
            <li>Vertical architectures tend to not have as many of these issues, but can encounter problems when the managing agent does not send enough information to its team for them to do the job. The paper recommends using prompting techniques to help with this.</li>
          </ul>        </li>
        <li>Careful design of the system prompt for the persona can help to keep an agent on task and reduce the amount of unnecessary chatter between agents.</li>
        <li>Dynamic team creation where agents are brought in and out of the system can be a big help because it excludes irrelevant agents from adding noise a particular stage of the problem.</li>
      </ul>    </li>
    <li><h2>Limitations</h2>
      <ul class="list-bullet">
        <li>Evaluating agents is difficult and there are not very many good standard benchmarks.</li>
        <li>Many papers introduce their own benchmarks alongside a new agent system, which makes it difficult to compare agent systems beyond those tested in that particular paper.</li>
        <li>Many agent evals are complex and require manual scoring, which can be tedious, limits the size of the evaluation set, and adds the possibility of evaluator bias. The complexity of agents also leads to a lot more variation in their outputs, so it&apos;s more difficult to properly determine if an agent&apos;s answer is correct or not.</li>
        <li>As with language model evaluations, data set contamination is a problem, where the tasks that the agents are trying to work on can be found in their training data.</li>
        <li id="668de20c-2184-45f6-b70a-246e7fcd684e">Many standard benchmarks designed for language model testing, such as MMLU and GSM8K, are not applicable to agents because they don&apos;t really exercise an agent&apos;s ability to reason beyond what you would find in a single call to a language model.</li>
        <li>Some agent eval systems use simpler answers such as yes or no, which are easier to evaluate, but this limits the real world applicability of the eval, where most tasks require more complex answers. More complex benchmarks that use logic puzzles or video games come closer, but even in those cases it&apos;s questionable how much it translates to the real world, where tasks are less well-defined and data is dirtier.</li>
        <li>The paper mentions <a href="https://arxiv.org/abs/2406.04770">WildBench</a> and SWE-bench as a couple of benchmarks that use real-world data, though WildBench doesn&apos;t seem to be designed for agent testing.</li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/paper_the_landscape_of_emerging_ai_agent_architectures_for_reasoning_planning_and_tool_calling_a_survey</link><guid isPermaLink="false">89849d539adeaf30e02d8b3f085dabc93a00a79288774dd71aa88f595a5dd9bb</guid><pubDate>Tue, 09 Jul 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-07-09]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li>Fixed some issues with <a href="https://github.com/dimfeld/svelte-maplibre">svelte-maplibre</a> and Mapbox&apos;s Draw plugin today. Both of these turned out to be general incompatibilities with Maplibre, where the Mapbox plugin (understandably) assumed the presence of certain Mapbox CSS classes. I released v0.9.9 of the package which includes some new CSS which allows the mouse pointers to work properly in Maplibre. And an <a href="https://svelte-maplibre.vercel.app/examples/draw">example is also now up on the website</a>.</li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/07/2024-07-09</link><guid isPermaLink="false">0050de61ec86016838eb93c74fa8366577179589d1253101889b4dda6769426f</guid><category><![CDATA[Svelte]]></category><pubDate>Tue, 09 Jul 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-07-02]]></title><description><![CDATA[<html><head></head><body><p>One of the best parts about tools like Phind is you can ask it a question, follow up with &quot;I&apos;m on a Mac and this didn&apos;t work&quot; and it will just tell you why.</p>
    <p>Turns out Mac tools often use libedit instead of readline, so your keybindings need to go in .editrc instead of .inputrc.</p></body></html>]]></description><link>https://imfeld.dev/journals/2024/07/2024-07-02</link><guid isPermaLink="false">32d5a7adde7597a0d702367ed3781e0d3b0ce390842311ab95996da1c494a5b5</guid><pubDate>Tue, 02 Jul 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-06-15]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li>I&apos;ve managed to avoid learning Kubernetes for years, but it&apos;s finally time, so I&apos;m setting up <a href="https://imfeld.dev/notes/projects_sbbp">SBBP</a> to run with it. I&apos;ll probably put Caddy in front. Aside from the learning, the main purpose of this is just so I can easily add the transcribed videos to Readwise.</li>
      </ul>
    <h3>Learning</h3>
        <p><a href="https://www.usenix.org/publications/loginonline/sieve-cache-eviction-can-be-simple-effective-and-scalable">SIEVE</a> is a smarter change eviction algorithm that also manages to be simpler and faster than most other algorithms. A clever idea that optimizes for quick eviction, taking advantage of the property that in many cache use cases, a large proportion of the fetched objects are actually not fetched often.</p>

    <h3>Links</h3>
      <ul class="list-bullet">
        <li>This <a href="https://studios.trychroma.com/flo-on-lindy">podcast with Flo Crivello</a> has a lot of good information on how Lindy is training their AI agents. Most interesting to me was the talk about explicit training phases for agents when they encounter new tasks, where the user can give quick feedback on how the agent is performing on a set of initial work items, and have it adapt in real-time based on that feedback until the performance is acceptable.</li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/06/2024-06-15</link><guid isPermaLink="false">05be4fc650c6e307829c8e8f832af1ee141e447be80f9f4d5552808e58a1a458</guid><pubDate>Sat, 15 Jun 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-06-12]]></title><description><![CDATA[<html><head></head><body><h3>Learning</h3>
        <p>Hit an unusual problem today where reading pixels back from a <code>canvas</code> HTML element won&apos;t always give you the same value you wrote, if Brave&apos;s anti-fingerprinting measures are on. (Anti-aliasing is an issue too, but we knew about that already.) I remember reading something about how browser-fingerprinting libraries can sometimes use techniques like this to determine unique users, but was baffled for a while until turning off Brave&apos;s privacy features made the bug go away.</p></body></html>]]></description><link>https://imfeld.dev/journals/2024/06/2024-06-12</link><guid isPermaLink="false">bd4a9e25f0594b98fa7ebc19e49e803440ba1f4750494fc0366cb25186ef07b6</guid><pubDate>Wed, 12 Jun 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[Aviator CLI Cheatsheet]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li><a href="https://docs.aviator.co/aviator-cli">Aviator</a> is a tool for managing stacked branches and PRs. Their CLI can be used separately from the rest of the product. Here are some useful commands for it:</li>
    <li><code>av stack sync</code> rebases branches within the current stack and pushes</li>
    <li><code>av stack sync --trunk</code> is like the above, but also brings the stack up to date with the trunk branch</li>
    <li><code>av stack sync --prune</code> deletes branches for merged PRs</li>
    <li><code>av stack sync --all --trunk --prune</code> brings everything in the repository up to date, deletes merged branches, etc.</li>
    <li><code>av stack submit</code>  to create and sync PRs for every branch in the current stack</li>
    <li><code>av pr create</code> creates a PR for the current branch</li>
    <li><code>av stack branch</code> creates a new branch off of the current branch</li>
    <li><code>av stack branch-commit -b &lt;BRANCHNAME&gt;</code> createe a new branch and commits the currently staged files (see &#x2014;help on this one for more options)</li>
    <li><code>av stack switch</code>  is a stack-aware TUI for switching branches.</li>
    <li><code>av stack tree</code> shows the same diagram used by <code>switch</code> but only prints it.</li>
    <li><code>av stack reorder</code> to move commits between branches in a stack, or also merge and split branches.</li>
    <li>Moving branches between stacks requires a few commands
      <ul class="list-bullet">
        <li><code>git checkout &lt;BRANCH TO MOVE&gt;</code></li>
        <li><code>git rebase &lt;NEW_PARENT_BRANCH&gt;</code></li>
        <li><code>av stack sync --parent &lt;NEW_PARENT_BRANCH&gt;</code></li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/aviator_cli_cheatsheet</link><guid isPermaLink="false">05ec8e87ef2071496826f4140a75bae4940316850ccec1b48e35f2c0a0fca50d</guid><pubDate>Fri, 24 May 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[Chronicle]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>Provides a proxy which can be called instead of the normal URL, then passes a request to the LLM provider and returns the response. Chronicle can be embedded directly into a Rust application or can run as a standalone server.</li>
    <li><h2>Task List</h2>
      <ul class="list-bullet">
        <li><h3>Up Next</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Write documentation</li>
            <li><input type="checkbox" disabled> Basic UI for visualizing runs, steps, etc.</li>
          </ul>        </li>
        <li><h3>Soon</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Option to skip logging model messages</li>
            <li><input type="checkbox" disabled> Python client
              <ul class="list-bullet">
                <li>This should be both a normal client and have the ability to wrap other common clients such as the OpenAI SDK. Needs to be compatible with tools like instructor, DSPy, etc.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Add fixture tests for Fireworks</li>
            <li><input type="checkbox" disabled> Add fixture tests for Together</li>
            <li><input type="checkbox" disabled> Add fixture tests for Ollama</li>
            <li><input type="checkbox" disabled> Add fixture tests for Anyscale</li>
            <li><input type="checkbox" disabled> Add fixture tests for DeepInfra</li>
            <li><input type="checkbox" disabled> When waiting to retry, detect if the request has disconnected and cancel it</li>
            <li><input type="checkbox" disabled> Add test for JSON <code>response_format</code></li>
          </ul>        </li>
        <li><h3>Later/Maybe</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Function to validate configuration and return a list of errors</li>
            <li>API Management
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Endpoints to manage aliases</li>
                <li><input type="checkbox" disabled> Endpoints to manage API keys</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Watch and reload config file</li>
            <li>Request Management
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Allow passing through user-agent header</li>
                <li><input type="checkbox" disabled> Simple Request caching
                  <ul class="list-bullet">
                    <li>Cache responses based on provider/model/messages</li>
                    <li>Support in-memory, disk, database, Redis</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Fetch secrets from AWS secrets store</li>
                <li><input type="checkbox" disabled> When looping around providers with retries, omit providers who had an unrecoverable error.</li>
                <li><input type="checkbox" disabled> Option for provider-level or model-level rate limiting
                  <ul class="list-bullet">
                    <li>So that new requests coming in will automatically wait or fallback</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Monitor error rates from providers and auto-switch
                  <ul class="list-bullet">
                    <li>This can be built into the alias system perhaps</li>
                  </ul>                </li>
              </ul>            </li>
            <li>New Providers
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> New Provider: Cohere
                  <ul class="list-bullet">
                    <li>Very different API than the others, it&#x2019;s different enough from a feature perspective that I&#x2019;m not sure it&#x2019;s worth translating between request formats.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> New Provider: OpenRouter</li>
                <li><input type="checkbox" disabled> New Provider: Google Gemini</li>
                <li><input type="checkbox" disabled> OctoAI</li>
                <li><input type="checkbox" disabled> <a href="https://lepton.ai">Lepton</a></li>
              </ul>            </li>
            <li>Specific Provider Upgrades
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Claude: support &quot;is_error&quot; flag in tool results</li>
                <li><input type="checkbox" disabled> Claude: support images in tool results</li>
              </ul>            </li>
            <li>Other Modalities
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Support binary upload APIs like Deepgram as well</li>
              </ul>            </li>
            <li>Logging
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Send logged data to arbitrary HTTP endpoint
                  <ul class="list-bullet">
                    <li>This should be done in a way that it can sent to something like Kafka, Elasticsearch, or Clickhouse using only the configuration, no custom code</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Send logged data to S3
                  <ul class="list-bullet">
                    <li>As JSON files? As Parquet?</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Figure out what to do about large data
                  <ul class="list-bullet">
                    <li>saving input and outputs is useful but can start taking up a lot of space. Ideally we can have something that places records in cloud storage, just need to figure out the formats and so on and if/how to make things queryable before they land in storage.</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Analysis
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> visualize by arbitrary metadata</li>
                <li><input type="checkbox" disabled> Ability to create database indexes on arbitrary metadata even in JSON fields</li>
              </ul>            </li>
            <li>Price Tracking
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Associate each provider and its calls with a pricing plan</li>
                <li><input type="checkbox" disabled> Fetch and update prices for each provider</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Support <a href="https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching">prompt caching</a> &#x2014; Aug 25th, 2024</li>
            <li><input type="checkbox" disabled checked> Support tools with Ollama</li>
            <li><input type="checkbox" disabled checked> New Provider: AWS Bedrock &#x2014; Jul 8th, 2024
              <ul class="list-bullet">
                <li><a href="https://lib.rs/crates/aws-sdk-bedrockruntime">https://lib.rs/crates/aws-sdk-bedrockruntime</a></li>
                <li><a href="https://docs.aws.amazon.com/bedrock/latest/APIReference/API_Operations_Amazon_Bedrock_Runtime.html">https://docs.aws.amazon.com/bedrock/latest/APIReference/API_Operations_Amazon_Bedrock_Runtime.html</a></li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Run and Step tracing &#x2014; Jun 30th, 2024</li>
            <li><input type="checkbox" disabled checked> Add Mistral support &#x2014; Jun 19th, 2024</li>
            <li><input type="checkbox" disabled checked> Streaming with Groq and Ollama &#x2014; Jun 1st, 2024</li>
            <li><input type="checkbox" disabled checked> Enhance test suite with real-world cases &#x2014; May 31st, 2024
              <ul class="list-bullet">
                <li>This uses streaming and non-streaming responses from various provider types, for both regular text and tool calls.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Streaming with Claude &#x2014; May 31st, 2024</li>
            <li><input type="checkbox" disabled checked> Streaming support &#x2014; May 31st, 2024</li>
            <li><input type="checkbox" disabled checked> &quot;Simple&quot; API can build for Postgres
              <ul class="list-bullet">
                <li>Dropped the &quot;full web app&quot; version of the API. This will come back at some later time</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Anthropic now supports &quot;required&quot; tool mode</li>
            <li><input type="checkbox" disabled checked> Recover from Groq function calling failure</li>
            <li><input type="checkbox" disabled checked> Endpoint for generic event logging &#x2014; May 8th, 2024
              <ul class="list-bullet">
                <li>Take the same metadata that we use for LLM calls, store them in a different table with just an event type and data json blob.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Support tool use fields &#x2014; May 1st, 2024</li>
            <li><input type="checkbox" disabled checked> Simpler API server that supports SQLite &#x2014; Apr 30th, 2024
              <ul class="list-bullet">
                <li>This will just use the built-in proxy tables, but is better for simpler use since it writes to SQLite</li>
                <li>Autoload config files from the XDG directories</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Javascript client &#x2014; Apr 29th, 2024
              <ul class="list-bullet">
                <li>This comes with a Chronicle-specific client, and can also redirect clients such as the OpenAI SDK using a custom fetch function.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Submit request metadata (org/user/workflow id) via HTTP headers &#x2014; Apr 29th, 2024</li>
            <li><input type="checkbox" disabled checked> API should have default to do everything without authorization
              <ul class="list-bullet">
                <li>Do this by not only setting up a default user, but also adding it as the anonymous fallback</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Testing &#x2014; Apr 26th, 2024</li>
            <li><input type="checkbox" disabled checked> For API mode, add data tables as <a href="https://imfeld.dev/notes/projects_filigree">Filigree</a> models instead of using the built-in tables</li>
            <li><input type="checkbox" disabled checked> When multiple providers are in use, keep retrying even on normally un-retryable errors</li>
            <li><input type="checkbox" disabled checked> Allow configuring fallback provider and model on retry. &#x2014; Apr 24th, 2024
              <ul class="list-bullet">
                <li>This is part of the model alias configuration. Basically instead of a single provider and model there&apos;s an array of provider/model/apikey tuples</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Support model/provider aliases &#x2014; Apr 23rd, 2024</li>
            <li><input type="checkbox" disabled checked> Support api keys &#x2014; Apr 23rd, 2024
              <ul class="list-bullet">
                <li>These can only be referenced by aliases</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Save metadata into SQLite or Postgres &#x2014; Apr 22nd, 2024</li>
            <li><input type="checkbox" disabled checked> Load model and provider definitions from a configuration file &#x2014; Apr 22nd, 2024</li>
            <li><input type="checkbox" disabled checked> Store and load model and provider definitions from the database &#x2014; Apr 22nd, 2024</li>
            <li><input type="checkbox" disabled checked> Configurable user agent for HTTP client &#x2014; Apr 21st, 2024</li>
            <li><input type="checkbox" disabled checked> Link requests to internal users/orgs/projects &#x2014; Apr 21st, 2024</li>
            <li><input type="checkbox" disabled checked> Configurable timeout &#x2014; Apr 20th, 2024</li>
            <li><input type="checkbox" disabled checked> Common format chat messages and responses &#x2014; Apr 19th, 2024</li>
            <li><input type="checkbox" disabled checked> Automatic retry with rate-limit support &#x2014; Apr 19th, 2023</li>
            <li><input type="checkbox" disabled checked> Endpoint that proxies the call &#x2014; Apr 19th, 2024</li>
            <li><input type="checkbox" disabled checked> Send all relevant metadata as Otel traces &#x2014; Apr 19th, 2024</li>
          </ul>        </li>
      </ul>    </li>
    <li>Probably take some code from Promptbox and change that to use this as a library, since it already has some of the needed functionality</li>
    <li>Maintain a price sheet with input/output token price per provider and model
      <ul class="list-bullet">
        <li>Each price sheet entry as an active flag</li>
        <li>When prices are updated for a model, add a new entry and mark it active</li>
        <li>In the future have a scraper or other mechanism of getting latest price data for each model</li>
      </ul>    </li>
    <li>Support multiple methods of output:
      <ul class="list-bullet">
        <li>Record in a postgres table</li>
        <li>Output OpenTelemetry</li>
      </ul>    </li>
    <li>Consider allowing metadata such as org and user ID can be sent in a cookie or in HTTP headers in addition to the body. Not sure how useful this is though.</li>
    <li>For each entry, record:
      <ul class="list-bullet">
        <li>Org ID</li>
        <li>User ID</li>
        <li>Run ID (ID linking related prompt calls together)</li>
        <li>Workflow Name</li>
        <li>Workflow Step</li>
        <li>Arbitrary other metadata</li>
        <li>endpoint called</li>
        <li>provider and model used</li>
        <li>input text</li>
        <li>output text</li>
        <li>input token count</li>
        <li>output token count</li>
        <li>which price sheet row was used</li>
        <li>response time</li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_chronicle</link><guid isPermaLink="false">b97a05dc2befd6da39729daae543f21d3c89eff9a647c749f51f25bb0ef77cee</guid><category><![CDATA[Projects]]></category><pubDate>Fri, 29 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[Ramus]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>Ramus is an in-process DAG and state machine executor designed for running agents with a focus on observability.</li>
    <li><h2>Task List</h2>
      <ul class="list-bullet">
        <li><h3>Up Next</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Probably drop this and use LangGraph instead?</li>
            <li><input type="checkbox" disabled> Completely serialize and restore an agent
              <ul class="list-bullet">
                <li>This needs to account for tool context as well, shared state between nodes, nested state machines, etc.
                  <ul class="list-bullet">
                    <li>Probably split up &quot;context&quot; and &quot;tools&quot; where &quot;context&quot; always needs to be serializable and tools can contain functions.</li>
                    <li>Could go more complicated with serialize/deserialize functions but this gets messy in JS.</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Agent visualization
              <ul class="list-bullet">
                <li>This should be both a generic application that can visualize agents in any database with a compliant schema, and also a set of components that make it easy to do so in a customized fashion.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Soon</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Helper function for simple chain -&gt; DAG</li>
            <li><input type="checkbox" disabled> Chatbot interface that can be adapted to web, Slack, Discord, etc.
              <ul class="list-bullet">
                <li>Standard set of incoming and outgoing events for this case</li>
                <li>Implement serialization</li>
                <li>Multiple levels of verbosity</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Serializable runner functions
              <ul class="list-bullet">
                <li>This has the runner in the config as an object instead of a function, where the object contains a key into some runner registry that points to the function, and additional arguments to the function.</li>
                <li>This will be very useful for DAGs generated by agents that may not be fixed in the code.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Dynamic node type that can expand its inputs into a DAG or state machine</li>
            <li><input type="checkbox" disabled> Cache system
              <ul class="list-bullet">
                <li>Mainly for tools that make network requests and for LLMs. This is mostly a debugging feature but in some cases will probably be useful for other things.</li>
                <li>LLM request caching could be built into chronicle</li>
                <li>Support in-memory, file-system, and network like Redis</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Central orchestrator for multiple sub-state machines and agents.
              <ul class="list-bullet">
                <li>This can be a convenient point for human in the loop and other types of things like that.</li>
                <li>There should be some affordance here for cases where we want to allow the user to bail out of a sub-agent.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Later/Maybe</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Replay a DAG from any point in its execution
              <ul class="list-bullet">
                <li>Basically just rerunning a node from its inputs and setting things up so that the descendant nodes will all run appropriately too.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Replay a state machine from any point in its execution
              <ul class="list-bullet">
                <li>At each step, save the input/output and a diff of the context.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Update inputs and rerun only affected parts of a DAG
              <ul class="list-bullet">
                <li>This adds a new config key to nodes, where they can indicate which keys in <code>rootInput</code> they care about</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Ability to edit a node&apos;s output that wasn&apos;t what the user wanted
              <ul class="list-bullet">
                <li>Say, if a node got the wrong result and the user wants to correct it.</li>
                <li><input type="checkbox" disabled> Ability to cancel just a single node and its downstream</li>
                <li><input type="checkbox" disabled> Modify output of any node and rerun downstream nodes in the DAG</li>
                <li><input type="checkbox" disabled> Make &quot;cancelled&quot; state non-permanent. Nodes return to a ready state or something when a cancel occurs.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Dynamic modification of a DAG or state machine
              <ul class="list-bullet">
                <li>Add new nodes to the existing DAG or state machine. This shouldn&apos;t be too hard to do and will help with introspection if doing dynamic planning.</li>
                <li>This should also be able to take another DAG configuration and merge it into the current one, along with support for mapping or prefixing the node names so that multiple copies of a DAG can be merged.</li>
                <li>Where do the outputs of the merged DAG go? Need to think more about how this makes sense and fits together.</li>
                <li>A simple version of this is the node that is dynamically configured as a subgraph. This probably makes the most sense as a longer-term solutiona s well.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Look at integrating with <a href="https://github.com/transitive-bullshit/agentic">https://github.com/transitive-bullshit/agentic</a> tools</li>
            <li><input type="checkbox" disabled> Short term memory system
              <ul class="list-bullet">
                <li>Sometimes we want to do RAG or something while an agent executes but won&apos;t need most of this data afterward. This basically becomes the ability to purge and/or archive the RAG data after we don&apos;t need it anymore.</li>
                <li>One-file-per-agent using LanceDB or something like that could work well here.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Script to parse DAG dependencies and generate a Typescript interface with proper types for the inputs and outputs of each node</li>
            <li><input type="checkbox" disabled> Manage multiple related state machines
              <ul class="list-bullet">
                <li>e.g. multiple agents running similar tasks on different input for a usre</li>
                <li>or multiple parts of a single plan all executing concurrently</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Send an event to multiple state machines based on some criteria
              <ul class="list-bullet">
                <li>This should allow some level of arbitrary criteria, like we want to be able to select based on an organization, a user, workflow type, start time, some other tag</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Global rate limiting of active tasks</li>
            <li><input type="checkbox" disabled> Evals
              <ul class="list-bullet">
                <li><a href="https://www.phind.com/agent?cache=clxp4o8ch000amj0c749z3xcw&amp;source=sidebar">Design conversation</a></li>
                <li>Eval database tables
                  <ul class="list-bullet">
                    <li>Eval job - name, time, source (CI, manual, etc), user, optional description</li>
                    <li>Eval item - a single item from an eval job that links to a run. Contains additional info around the run</li>
                    <li>Eval score - one score for an eval run</li>
                    <li>Eval data set - inputs and outputs</li>
                    <li>Need ability to attach extra scores by manual curation.</li>
                    <li>Associate metadata with a run</li>
                  </ul>                </li>
                <li>Views:
                  <ul class="list-bullet">
                    <li>compare results of a particular eval across different runs</li>
                    <li>Results for a particular eval over time</li>
                    <li>Total results for a particular category of run over time (e.g. for a particular workflow, the trend of success rate)</li>
                    <li>All results for a run</li>
                    <li>Filter by whatever, including metadata values</li>
                  </ul>                </li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Chronicle function for converting metadata into span attributes</li>
            <li><input type="checkbox" disabled checked> Convert to multipackage monorepo</li>
            <li><input type="checkbox" disabled checked> Allow overriding name of DAG or state machine on each run</li>
            <li><input type="checkbox" disabled checked> Basic State machine support
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Configuration</li>
                <li><input type="checkbox" disabled checked> Function to execute one step of the state machine</li>
                <li><input type="checkbox" disabled checked> Ability to call a DAG, other state machines, or an arbitrary promise-returning function in a state</li>
                <li><input type="checkbox" disabled checked> Tracing/events on all transitions</li>
                <li><input type="checkbox" disabled checked> Configurable transitions</li>
                <li><input type="checkbox" disabled checked> Global error handler/state</li>
                <li><input type="checkbox" disabled checked> Configurable override of error transition on a per-state basis</li>
                <li><input type="checkbox" disabled checked> Send events to state machines</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Semaphores per class of node</li>
            <li><input type="checkbox" disabled checked> Stepwise mode for DAGs</li>
            <li><input type="checkbox" disabled checked> Some way to send events out while still running (e.g. to send updates to the user)</li>
            <li><input type="checkbox" disabled checked> Tracing spans for every state</li>
            <li><input type="checkbox" disabled checked> Log state transitions, inputs, and outputs to a database</li>
            <li><input type="checkbox" disabled checked> DAG excutor</li>
            <li><input type="checkbox" disabled checked> Spawn state machines or DAGs from an existing task and wait for them to finish
              <ul class="list-bullet">
                <li>This is just done via normal code, nothing special.</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li>The main features are:
      <ul class="list-bullet">
        <li>&quot;human in the loop&quot; states</li>
        <li>cyclic workflow graphs
          <ul class="list-bullet">
            <li>with some control on the number of steps taken</li>
          </ul>        </li>
        <li>Fully embrace &quot;workflow as state machine&quot; rather than just using them behind the scenes</li>
        <li>arbitrary delays</li>
        <li>rate limiting on how quickly an agent can make requests?</li>
      </ul>    </li>
    <li>What do we <em class="italic">really</em> need a workflow engine for?
      <ul class="list-bullet">
        <li>Arbitrary delays e.g. resume after a month</li>
        <li>Stop to get user input, and resume
          <ul class="list-bullet">
            <li>Sort of. This makes it somewhat easier but a simpler system can accomplish the same thing.</li>
          </ul>        </li>
        <li>Global rate limiting</li>
        <li>Long-running tasks so we can resume from somewhere on failure
          <ul class="list-bullet">
            <li>This can be somewhat emulated with checkpoints or by doing a state machine inside the worker and having it save its state on every transition</li>
          </ul>        </li>
        <li>Events that can be sent to multiple dormant workflows based on some tag
          <ul class="list-bullet">
            <li>Can be done manually though</li>
          </ul>        </li>
      </ul>    </li>
    <li>A good first effort would be to write agents as state machines where the state machine runs in the agent itself instead of being externally orchestrated
      <ul class="list-bullet">
        <li>This simplifies the running a lot, especially for agents that don&apos;t need to sleep for a long time.</li>
        <li>This leaves the external component as something that can resume and run a state machine, which doesn&apos;t have to be generic at first. It can just be a normal call to the service that runs it.</li>
        <li>Downsides:
          <ul class="list-bullet">
            <li>Not resilient to full process crash, although with an external sweeper this could be mollified somewhat</li>
            <li>Are there any downsides around spawning sub-state machines? I think the agent software would have to be designed to allow resuming from a state like this, but otherwise it wouldn&apos;t be a problem.</li>
          </ul>        </li>
        <li>Take a look at napi-rs or neon or maybe even just in WASM, to see if we can write a lot of this in Rust. Would simplify code reuse and reliability in some ways and look forward to more full-featured plans. Might be more complex than it&apos;s worth though, need to see.</li>
        <li>Initial Layout
          <ul class="list-bullet">
            <li>This is a library which can run an arbitrary state machine and persist the config and state into a database</li>
            <li>Two modes
              <ul class="list-bullet">
                <li>State machine</li>
                <li>DAG
                  <ul class="list-bullet">
                    <li>The difference is that because a DAG is non-looping, we can just run all the root nodes concurrently and then run other nodes as soon as their parent dependencies are done.</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Each node in a state machine or DAG can spawn other DAGs and state machines, and wait for them to finish</li>
            <li>Support running either preconfigured machine from the database or taking an ad hoc state machine</li>
            <li>Some way to send events to a paused state machine to start it up again</li>
            <li>State machines can run other state machines
              <ul class="list-bullet">
                <li>Need to figure out how much explicit support this really needs</li>
              </ul>            </li>
            <li>Ensure that we&apos;re never processing more than one state machine event at a time (except perhaps a cancel event)</li>
            <li>When entering a state, supply
              <ul class="list-bullet">
                <li>Previous state name</li>
                <li>Current machine context</li>
                <li>Data emitted from the previous state for this state</li>
                <li>Data emitted from any submachine results that ran between the previous and current state.</li>
              </ul>            </li>
            <li>When leaving a state, the function returns
              <ul class="list-bullet">
                <li>Next state</li>
                <li>Data for that state</li>
                <li>Context updates</li>
                <li>Submachines to run before transitioning, if any, and their inputs</li>
              </ul>            </li>
            <li>Create a tracing span for every state</li>
            <li>Global error state</li>
            <li>States can override the global error state with some other state</li>
            <li>Parallel states?
              <ul class="list-bullet">
                <li>Need to figure out the right configuration for merging these back into a single state again though.</li>
                <li>Probably easier if either
                  <ul class="list-bullet">
                    <li>These are just run from within a state
                      <ul class="list-bullet">
                        <li>But then it&apos;s just a normal call. Works but not great</li>
                      </ul>                    </li>
                    <li>These are submachines which run to completion and there&apos;s a fixed state on the other end that receives all the results.
                      <ul class="list-bullet">
                      </ul>                    </li>
                  </ul>                </li>
              </ul>            </li>
          </ul>        </li>
        <li>Library V2
          <ul class="list-bullet">
            <li>Eventually have preconfigured blocks for certain common things, that can be called without any custom code
              <ul class="list-bullet">
                <li>Maybe do this sooner than later depending on how agent planning goes</li>
              </ul>            </li>
            <li>Built-in loop control with loop count limit.
              <ul class="list-bullet">
                <li>Probably do loops as a submachine since the scoped context is useful here</li>
              </ul>            </li>
            <li>Submachines
              <ul class="list-bullet">
                <li>Scoped context for groups of states</li>
                <li>Also useful for embedding state machines from other sources into a larger workflow.</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li>A particular instance of a workflow can have its own priority</li>
    <li>Workflows can stream events out to the caller</li>
    <li>Optionally use a time-weighted priority system for queuing</li>
    <li>Model workflows as a Moore state machine
      <ul class="list-bullet">
        <li>Probably will end up with a hybrid Moore/Mealy though</li>
      </ul>    </li>
    <li>State executor code can run child state machines
      <ul class="list-bullet">
        <li>Have some way to time out on these, like we run 10 searches with a timeout of 15 seconds and when we reach the timeout use whatever results have come back.</li>
        <li>Each submachine will run in parallel</li>
        <li>I think it&apos;s best if these child machines are actually their own separate machines which can be spawned from the state, rather than part of the parent state machine config. This is because many of these child machines will be used by multiple workflows.</li>
      </ul>    </li>
    <li>States have categories (eg makes an LLM call or not)</li>
    <li>Rate limits global, per workflow type, and per category</li>
    <li>Scoped context so that a set of sub-machines can have its own context separate from the global context</li>
    <li>Workflow and state priority</li>
    <li>States can define a transition to a particular state on timeout</li>
    <li>Support websockets or 2-way gRPC for simpler one-off runs where we don&apos;t really want to integrate a whole web server</li>
    <li>Support running as a library for cases where the agent is in Rust</li>
    <li>States and Transitions can log events for analysis
      <ul class="list-bullet">
        <li>Things like &quot;agent required correction&quot;, &quot;this is an error&quot;, &quot;user accepted the answer&quot;</li>
      </ul>    </li>
    <li>Each state (or state transition? whatever runs code) also has an &quot;error&quot; property which defines the state that it goes to upon an uncaught error. The state machine as a whole also has an error state which can perform some action.</li>
    <li>jobs register their state machine with a version and a place to call back when triggering states. Then anything can start the job with some initial context.
      <ul class="list-bullet">
        <li>Allow upserting a config where it makes a new version if the config is different from the latest version.</li>
      </ul>    </li>
    <li>Some way to generate typescript types from state machine config
      <ul class="list-bullet">
        <li>This should include a list of permitted next states from any particular state</li>
      </ul>    </li>
    <li>Each step returns:
      <ul class="list-bullet">
        <li>next state. There should be some way of type checking the returned value from the worker.</li>
        <li>Input for the next state, specific to that state?</li>
        <li>Conditions to go to next state:
          <ul class="list-bullet">
            <li>Optional delay before next transition. This can be encoded in the state machine config but also overridden</li>
            <li>Some kind of polling of an endpoint?
              <ul class="list-bullet">
                <li>Maybe the orchestrator calls an endpoint on some backoff schedule and depending on status code it either goes ot the next state or tries again. Need to think about this it might not be too useful</li>
              </ul>            </li>
          </ul>        </li>
        <li>Context updates</li>
      </ul>    </li>
    <li><h2>Bot Abstraction</h2>
      <ul class="list-bullet">
        <li>Keep track of conversations.</li>
        <li>Each &quot;server&quot; is a mapping of a platform (Discord, Slack, etc) and the ID of the team (Guild, Team, Org, etc.) on that platform to one of our orgs.</li>
        <li>Each conversation has:
          <ul class="list-bullet">
            <li>Our own ID</li>
            <li>The server it belongs to</li>
            <li>Which user started it</li>
            <li>The ID of the conversation in the platform. This should be JSON since it could have multiple components (e.g. a channel and thread ID in Discord) but also should be easy to look up.
              <ul class="list-bullet">
                <li>Might be best to just use separate tables for each platform for the platform-specific part. We do still need an indication in the main conversation table of which platform a conversation belongs to so we don&apos;t have to look it up in every table.</li>
              </ul>            </li>
          </ul>        </li>
        <li>Sending events
          <ul class="list-bullet">
            <li>Figure out which platform the conversation belongs to</li>
            <li>Send the conversation ID and the standardized event to the platform adapter</li>
            <li>Platform adapter translates it to the platform-specific format and sends it.</li>
          </ul>        </li>
        <li>Receiving an event
          <ul class="list-bullet">
            <li>Look up the conversation, if one exists</li>
            <li>Translate the event to the standard format</li>
            <li>send the conversation ID and the event to the main layer</li>
          </ul>        </li>
      </ul>    </li>
    <li><h3>Notes from Phind</h3>
      <ul class="list-bullet">
        <li>Here are some suggestions to improve and expand on your notes for a workflow orchestrator that models workflows as Moore state machines:

1. State Machine Definition:
          <ul class="list-bullet">
            <li>Allow defining state machines using a declarative format (e.g., YAML or JSON) for easy configuration and versioning.</li>
            <li>Include additional properties for each state, such as:
              <ul class="list-bullet">
                <li>State name and description</li>
                <li>Input and output data schemas for type checking and validation</li>
                <li>Error handling and retry mechanisms</li>
                <li>Timeouts and maximum execution time</li>
              </ul>            </li>
            <li><strong class="font-bold">Support nested state machines or sub-workflows for modular and reusable components.</strong>

2. Step Categories and Rate Limiting:</li>
            <li>Define a clear set of step categories based on the type of operation (e.g., LLM calls, database queries, external API calls).</li>
            <li>Implement rate limiting at both the global and category levels to control resource utilization and prevent abuse.</li>
            <li>Allow configuring rate limits per category and provide options to adjust them dynamically based on load or priority.

3. Job Priority and Scheduling:</li>
            <li>Introduce a priority system for jobs to determine their execution order and resource allocation.</li>
            <li>Support different priority levels (e.g., low, medium, high) and allow assigning priorities to individual jobs or workflows.</li>
            <li>Implement a scheduler that takes into account job priorities and available resources to optimize execution order and minimize waiting times.

4. Callback Mechanism:</li>
            <li>Provide a flexible callback mechanism for triggering state transitions and notifying external systems.</li>
            <li><strong class="font-bold">Allow registering multiple callback endpoints or functions for different events (e.g., state entry, state exit, job completion).</strong></li>
            <li>Support various callback protocols (e.g., HTTP, WebSocket, message queues) to integrate with different systems.

5. Context Management:</li>
            <li>Enhance the context object to store and pass data between states and steps.</li>
            <li><strong class="font-bold">Allow defining default context values and merging them with job-specific context during execution.</strong></li>
            <li>Provide methods to update, retrieve, and delete context variables within steps.

6. Error Handling and Recovery:</li>
            <li>Implement robust error handling mechanisms to handle failures gracefully and prevent workflow disruptions.</li>
            <li><strong class="font-bold">Allow defining error handling strategies per state or step (e.g., retry, skip, abort).</strong></li>
            <li>Support compensating actions or rollback mechanisms to revert partial changes in case of failures.

7. Monitoring and Observability:</li>
            <li>Provide built-in monitoring and logging capabilities to track workflow execution, performance metrics, and errors.</li>
            <li>Expose APIs or dashboards for real-time monitoring and visualization of workflow progress and status.</li>
            <li>Integrate with external monitoring and alerting systems for centralized observability and incident management.

8. Scalability and Distribution:</li>
            <li>Design the orchestrator to be scalable and distributed to handle a large number of concurrent workflows.</li>
            <li>Implement load balancing and horizontal scaling mechanisms to distribute workload across multiple nodes or workers.</li>
            <li>Consider using distributed locking and coordination mechanisms (e.g., ZooKeeper, etcd) for managing shared resources and ensuring consistency.

9. Testing and Debugging:</li>
            <li>Provide testing utilities and frameworks to facilitate unit testing and integration testing of workflows and steps.</li>
            <li>Allow simulating and debugging workflows in a controlled environment without affecting production systems.</li>
            <li>Implement logging and tracing capabilities to aid in troubleshooting and identifying issues during development and production.

10. Documentation and Examples:
              <ul class="list-bullet">
                <li>Create comprehensive documentation that explains the concepts, architecture, and usage of the workflow orchestrator.</li>
                <li>Provide clear examples and tutorials demonstrating how to define state machines, implement steps, and integrate with external systems.</li>
                <li>Maintain a collection of common workflow patterns and best practices to guide users in designing efficient and maintainable workflows.

These suggestions aim to enhance the functionality, reliability, and usability of your workflow orchestrator. They cover aspects such as state machine definition, rate limiting, job scheduling, error handling, monitoring, scalability, testing, and documentation. Incorporating these features and improvements will make your orchestrator more robust, flexible, and suitable for a wide range of workflow orchestration scenarios.</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_ramus</link><guid isPermaLink="false">c45e7946d79e84d75429a37498dfdc98651f716d1f8f4105c570d53645b3f4be</guid><category><![CDATA[Projects]]></category><pubDate>Fri, 29 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-03-11]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li><a href="https://imfeld.dev/notes/projects_filigree">Filigree</a>
          <ul class="list-bullet">
            <li>Got the object storage abstraction and configuration finished up over the weekend.</li>
            <li>Now I&quot;m starting on a &quot;File&quot; type of model, which will allow the template system to generate code that handles uploads, and optionally stores things like file hash, image type, etc. in the database.</li>
          </ul>        </li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/03/2024-03-11</link><guid isPermaLink="false">48b1793717ea63eebdcb4a9dd2a8ed142886ff0fb036789756ea6a867a2bb0dc</guid><pubDate>Mon, 11 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-03-06]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li><a href="https://imfeld.dev/notes/projects_filigree">Filigree</a>
          <ul class="list-bullet">
            <li>Starting on object storage support. Once done, this will add a File type to the database model, set up the client, and also make it easy to do things like stream files to and from the client. And of course the template system will initialize everything for you.</li>
          </ul>        </li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/03/2024-03-06</link><guid isPermaLink="false">5b0d9ded1011f90e27a604a0b3cd6a3572d0d198ed0a7ecf400d890b86689a73</guid><pubDate>Wed, 06 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-03-05]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li><a href="https://imfeld.dev/notes/projects_smelter">Smelter</a>
          <ul class="list-bullet">
            <li>The worker framework now handles SIGINT for you, and gives your code a channel that will change when the task should be cancelled.</li>
            <li>Published v0.1.0 of the Smelter crates, since it&apos;s about to go into production at work!</li>
          </ul>        </li>
        <li><a href="https://imfeld.dev/notes/projects_effectum">Effectum</a>
          <ul class="list-bullet">
            <li>More work on outbox pattern. The code is pretty much done, just needs tests.</li>
          </ul>        </li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/03/2024-03-05</link><guid isPermaLink="false">796ffb89ccf42a0458936b353165b5b8fdbd7493b35d334c376b0b21e81916ae</guid><pubDate>Tue, 05 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-03-04]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li><a href="https://imfeld.dev/notes/projects_effectum">Effectum</a>
          <ul class="list-bullet">
            <li>Started on support for transactional outbox pattern, since I&apos;m starting to add background worker support to <a href="https://imfeld.dev/notes/projects_filigree">Filigree</a></li>
          </ul>        </li>
        <li><a href="https://imfeld.dev/notes/projects_smelter">Smelter</a>
          <ul class="list-bullet">
            <li>The AWS Rust SDK doesn&apos;t handle the <code>ThrottlingError</code> returned from the ECS API, and so they don&apos;t get retried. I <a href="https://github.com/dimfeld/smelter/commit/986f72efa966a4c54dbb85ea5db851cc74ac42b0">added a custom `ClassifyRetry` layer</a> to make this work.</li>
            <li>Improved job cancellation on error, specifically around ensuring that there is time to send cancel requests to all the workers. There&apos;s still room for improvement around the ergonomics here I think, but it&apos;s an improvement.</li>
          </ul>        </li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/03/2024-03-04</link><guid isPermaLink="false">50e0e0068b3e3d672ba7d4ed2696f0be0df0dcbc9a476a982917ea3a80cad9f1</guid><category><![CDATA[rust]]></category><pubDate>Mon, 04 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-03-03]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li><a href="https://imfeld.dev/notes/projects_smelter">Smelter</a>
          <ul class="list-bullet">
            <li>Worker containers collect statistics such as RAM used and load average, and return these to the job manager.</li>
            <li>Added comments to all the public exports, in preparation for the first published version on crates.io</li>
          </ul>        </li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/03/2024-03-03</link><guid isPermaLink="false">df8dc419150441e87657b2083d585c71ca4e1c922c65c18460533ccf8846e0fa</guid><pubDate>Sun, 03 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-03-02]]></title><description><![CDATA[<html><head></head><body><h3>Building</h3>
      <ul class="list-bullet">
        <li><a href="https://imfeld.dev/notes/projects_smelter">Smelter</a>
          <ul class="list-bullet">
            <li>Added better job cancellation support</li>
            <li>Each job now as its own UUID</li>
            <li>QOL improvements around status messages</li>
          </ul>        </li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/03/2024-03-02</link><guid isPermaLink="false">b95f467694850a227bf10ddf72c3602ab74ba520638a4215d48be9e3ce5a3ae7</guid><pubDate>Sat, 02 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[AWS VPC Configuration]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>AWS sets up a bunch of convenient things in the default VPC for an account, which you may need to recreate when making a new VPC. Here&apos;s how to do that in Terraform.</li>
    <li>First, the VPC and subnets:
      <ul class="list-bullet">
        <li><pre><code><span class="sy-source sy-terraform"><span class="sy-meta sy-type sy-terraform"><span class="sy-storage sy-type sy-terraform">resource</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>aws_vpc<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-meta sy-block sy-terraform"><span class="sy-punctuation sy-section sy-block sy-begin sy-terraform">{</span></span></span><span class="sy-meta sy-block sy-terraform">
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">cidr_block</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-support sy-constant sy-terraform">var</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">my_app_cidr</span>
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">tags</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-meta sy-braces sy-terraform"><span class="sy-punctuation sy-section sy-braces sy-begin sy-terraform">{</span>
    <span class="sy-meta sy-mapping sy-key sy-terraform"><span class="sy-string sy-unquoted sy-terraform">Name</span></span> <span class="sy-keyword sy-operator sy-terraform">=</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
  <span class="sy-punctuation sy-section sy-braces sy-end sy-terraform">}</span></span>
<span class="sy-punctuation sy-section sy-block sy-end sy-terraform">}</span></span>

<span class="sy-meta sy-type sy-terraform"><span class="sy-storage sy-type sy-terraform">resource</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>aws_subnet<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-meta sy-block sy-terraform"><span class="sy-punctuation sy-section sy-block sy-begin sy-terraform">{</span></span></span><span class="sy-meta sy-block sy-terraform">
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">vpc_id</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span>aws_vpc<span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">my_app</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">id</span>
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">cidr_block</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-support sy-constant sy-terraform">var</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">my_app_cidr</span>
  <span class="sy-comment sy-line sy-terraform"><span class="sy-punctuation sy-definition sy-comment sy-terraform">#</span> Set appropriately for your needs<span class="sy-punctuation sy-definition sy-comment sy-terraform">
</span></span>  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">map_public_ip_on_launch</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-constant sy-language sy-terraform">true</span>
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">availability_zone</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-support sy-constant sy-terraform">var</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">az</span>
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">tags</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-meta sy-braces sy-terraform"><span class="sy-punctuation sy-section sy-braces sy-begin sy-terraform">{</span>
    <span class="sy-meta sy-mapping sy-key sy-terraform"><span class="sy-string sy-unquoted sy-terraform">Name</span></span> <span class="sy-keyword sy-operator sy-terraform">=</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
  <span class="sy-punctuation sy-section sy-braces sy-end sy-terraform">}</span></span>
<span class="sy-punctuation sy-section sy-block sy-end sy-terraform">}</span></span>

</span></code></pre></li>
      </ul>    </li>
    <li>For tasks that access the internet, you also need an internet gateway and a routing table to use it.
      <ul class="list-bullet">
        <li>You can also use a NAT gateway or something, but this is the simplest and cheapest way to go.</li>
        <li>Here we&apos;ll also set up a VPC endpoint to link directly into S3, which saves egress charges for going through public routes.</li>
        <li><pre><code><span class="sy-source sy-terraform"><span class="sy-meta sy-type sy-terraform"><span class="sy-storage sy-type sy-terraform">resource</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>aws_internet_gateway<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-meta sy-block sy-terraform"><span class="sy-punctuation sy-section sy-block sy-begin sy-terraform">{</span></span></span><span class="sy-meta sy-block sy-terraform">
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">vpc_id</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span>aws_vpc<span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">my_app</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">id</span>

  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">tags</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-meta sy-braces sy-terraform"><span class="sy-punctuation sy-section sy-braces sy-begin sy-terraform">{</span>
    <span class="sy-meta sy-mapping sy-key sy-terraform"><span class="sy-string sy-unquoted sy-terraform">Name</span></span> <span class="sy-keyword sy-operator sy-terraform">=</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
  <span class="sy-punctuation sy-section sy-braces sy-end sy-terraform">}</span></span>
<span class="sy-punctuation sy-section sy-block sy-end sy-terraform">}</span></span>

<span class="sy-meta sy-type sy-terraform"><span class="sy-storage sy-type sy-terraform">resource</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>aws_route_table<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span> <span class="sy-meta sy-block sy-terraform"><span class="sy-punctuation sy-section sy-block sy-begin sy-terraform">{</span></span></span><span class="sy-meta sy-block sy-terraform">
  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">vpc_id</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span>aws_vpc<span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">my_app</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">id</span>

  <span class="sy-meta sy-type sy-terraform"><span class="sy-entity sy-name sy-type sy-terraform">route</span> <span class="sy-meta sy-block sy-terraform"><span class="sy-punctuation sy-section sy-block sy-begin sy-terraform">{</span></span></span><span class="sy-meta sy-block sy-terraform">
    <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">cidr_block</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>0.0.0.0/0<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
    <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">gateway_id</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span>aws_internet_gateway<span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">my_app</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">id</span>
  <span class="sy-punctuation sy-section sy-block sy-end sy-terraform">}</span></span>

  <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">tags</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-meta sy-braces sy-terraform"><span class="sy-punctuation sy-section sy-braces sy-begin sy-terraform">{</span>
    <span class="sy-meta sy-mapping sy-key sy-terraform"><span class="sy-string sy-unquoted sy-terraform">Name</span></span> <span class="sy-keyword sy-operator sy-terraform">=</span> <span class="sy-string sy-quoted sy-double sy-terraform"><span class="sy-punctuation sy-definition sy-string sy-begin sy-terraform">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
  <span class="sy-punctuation sy-section sy-braces sy-end sy-terraform">}</span></span>
<span class="sy-punctuation sy-section sy-block sy-end sy-terraform">}</span></span>
</span></code></pre></li>
      </ul>    </li>
    <li>Finally, we set up a VPC endpoint to communicate directly with S3, without needing to go through the public internet. This can improve performance and save money.
      <ul class="list-bullet">
        <li><pre><code><span class="sy-text sy-plain">resource &quot;aws_vpc_endpoint&quot; &quot;my_app_s3&quot; {
  vpc_id       = aws_vpc.my_app.id
  service_name = &quot;com.amazonaws.us-west-2.s3&quot;

  tags = {
    Name = &quot;my_app_s3&quot;
  }
}

resource &quot;aws_vpc_endpoint_route_table_association&quot; &quot;my_app_s3&quot; {
  vpc_endpoint_id = aws_vpc_endpoint.my_app_s3.id
  route_table_id = aws_route_table.my_app.id
}

</span></code></pre></li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/aws_vpc_configuration</link><guid isPermaLink="false">8f492b21ec958b1e7a8787ec085b4e46ce2abf480a96a9f3e70aabaa8d08305c</guid><category><![CDATA[AWS]]></category><pubDate>Fri, 01 Mar 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[2024-03-01]]></title><description><![CDATA[<html><head></head><body><p><a href="https://imfeld.dev/notes/projects_smelter">Smelter</a> had been on hold for a while, but this week at work a need came up for parallel processing, so I finished it up and ran the first real job!</p>
    <p>This data processing pipeline formerly took 6 hours to run with a single process on a 24 vCPU machine, and now runs in 23 minutes using 32 8vCPU containers on Fargate.</p>
    <p>The original vision for Smelter was to run it with Lambdas and other similar FaaS runtimes, but because it uses an adapter model to support new platforms, I was able to add Fargate with no changes to the existing code. Really happy how this turned out.</p>
    <p>I do still want to implement the originally envisioned massively-parallel Lambda computation at some point too, for more interactive use cases.</p>

    <h3>Building</h3>
      <ul class="list-bullet">
        <li><a href="https://imfeld.dev/notes/projects_filigree">Filigree</a>
          <ul class="list-bullet">
            <li>Started on many-to-many relationships using a through table and model</li>
          </ul>        </li>
        <li><a href="https://imfeld.dev/notes/projects_smelter">Smelter</a>
          <ul class="list-bullet">
            <li>Tested out the Fargate orchestration code with real containers and made some fixes</li>
            <li>Ergonomic improvements to spawning Fargate tasks</li>
            <li>First real run of Smelter! This is a data processing pipeline at work that formerly ran with a single process, now running with 32 containers in parallel across the dataset.</li>
          </ul>        </li>
      </ul></body></html>]]></description><link>https://imfeld.dev/journals/2024/03/2024-03-01</link><guid isPermaLink="false">295c654e6644f62d563a1325781395b138bc4b74e1b65f1eaefb874e20eb8747</guid><pubDate>Fri, 01 Mar 2024 00:00:00 GMT</pubDate></item></channel></rss>