<?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 - Notes]]></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:46 GMT</lastBuildDate><atom:link href="https://imfeld.dev/rss/notes.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[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[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[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[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[Build Docker Containers from a Monorepo]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li><a href="https://docs.docker.com/build/building/context/#dockerignore-files">A .dockerignore file</a> is very important here to reduce the amount of context that needs to be sent to the builder process. Note that the <code>.dockerfile</code> file requires prepending <code>**/</code> to any match that you want to apply outside of the root directory.
      <ul class="list-bullet">
        <li><pre><code><span class="sy-text sy-plain">**/node_modules
**/*.js
**/*.ts
**/apps
**/target
!apps/my-app
apps/my-app/target
</span></code></pre></li>
      </ul>    </li>
    <li>Then you will want to build with the context directory being the root of the monorepo
      <ul class="list-bullet">
        <li><pre><code><span class="sy-source sy-shell sy-bash"><span class="sy-comment sy-line sy-number-sign sy-shell"><span class="sy-punctuation sy-definition sy-comment sy-begin sy-shell">#</span></span><span class="sy-comment sy-line sy-number-sign sy-shell">!/bin/bash</span><span class="sy-comment sy-line sy-number-sign sy-shell">
</span><span class="sy-meta sy-function-call sy-shell"><span class="sy-variable sy-function sy-shell">docker</span></span><span class="sy-meta sy-function-call sy-arguments sy-shell"> build <span class="sy-punctuation sy-separator sy-continuation sy-line sy-shell">\
</span><span class="sy-variable sy-parameter sy-option sy-shell"><span class="sy-punctuation sy-definition sy-parameter sy-shell">  -</span>t</span> <span class="sy-meta sy-group sy-expansion sy-parameter sy-shell"><span class="sy-punctuation sy-definition sy-variable sy-shell">$</span><span class="sy-variable sy-other sy-readwrite sy-shell">NAME</span></span> <span class="sy-punctuation sy-separator sy-continuation sy-line sy-shell">\
</span><span class="sy-variable sy-parameter sy-option sy-shell"><span class="sy-punctuation sy-definition sy-parameter sy-shell">  -</span>f</span> Dockerfile <span class="sy-punctuation sy-separator sy-continuation sy-line sy-shell">\
</span><span class="sy-variable sy-parameter sy-option sy-shell"><span class="sy-punctuation sy-definition sy-parameter sy-shell">  --</span>ignorefile</span> .dockerignore <span class="sy-punctuation sy-separator sy-continuation sy-line sy-shell">\
</span>  ../../ <span class="sy-comment sy-line sy-number-sign sy-shell"><span class="sy-punctuation sy-definition sy-comment sy-begin sy-shell">#</span></span><span class="sy-comment sy-line sy-number-sign sy-shell"> The monorepo root</span><span class="sy-comment sy-line sy-number-sign sy-shell">
</span></span></span></code></pre></li>
      </ul>    </li>
    <li><h2>Building for Rust</h2></li>
    <li>If your application is built in Rust, then you can use <code>cargo-chef</code> to help speed up the builds, but the default recipe needs a few directory tweaks if your application has dependencies elsewhere in the monorepo.</li>
    <li>Something like this works well:</li>
    <li><pre><code><span class="sy-text sy-plain"># This Dockerfile works with cargo chef, which prebuilds dependencies in a
# separate Docker image to speed up builds.

FROM lukemathwalker/cargo-chef:latest-rust-1 AS chef
RUN apt-get update &amp;&amp; apt-get install -y pkg-config libssl-dev
WORKDIR /app/apps/my-app

FROM chef as planner
COPY ./libs/some-lib /app/libs/some-lib
COPY ./apps/my-app /app/apps/my-app
RUN cargo chef prepare --recipe-path recipe.json

FROM chef as builder
COPY --from=planner /app/apps/my-app/recipe.json recipe.json
# Build dependencies - this is the caching Docker layer!
COPY ./libs/some-lib /app/libs/some-lib
RUN cargo chef cook --release --recipe-path recipe.json
# Build application
COPY ./apps/my-app /app/apps/my-app
RUN cargo build --release --bin my-app

FROM debian:bookworm-slim as runtime
RUN apt-get update &amp;&amp; apt-get install -y pkg-config libssl-dev ca-certificates
RUN update-ca-certificates
COPY --from=builder /app/apps/my-app/target/release/my-app /usr/local/bin
ENTRYPOINT [&quot;/usr/local/bin/my-app&quot;]
</span></code></pre></li>
    <li>If building from Mac or Windows, you may also see benefits from increasing the amount of RAM available to the Docker VM. For example, the default VM from Podman uses only 2GB and while the Rust compiler can run within those boundaries, it&apos;s very slow. Using a 16 or 32GB VM can speed up the compilation by an order of magnitude.</li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/build_docker_containers_from_a_monorepo</link><guid isPermaLink="false">495aad259f20f7c8a2425c43d07e6071c8504d98be1fcb0ea1feeca396b9e274</guid><pubDate>Thu, 29 Feb 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[Setting up AWS Fargate on ECS]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>This mostly focuses on using Fargate for one-off jobs. Configuration is in Terraform.</li>
    <li>Set up a <a href="https://imfeld.dev/notes/aws_vpc_configuration">VPC for your Cluster</a> if you need to.</li>
    <li>ECS Cluster
      <ul class="list-bullet">
        <li>You need to create an ECS cluster but it doesn&apos;t need any configuration beyond being created.</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_ecs_cluster<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>pipeline<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">name</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>pipeline<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
<span class="sy-punctuation sy-section sy-block sy-end sy-terraform">}</span></span>
</span></code></pre></li>
      </ul>    </li>
    <li>Running on ARM
      <ul class="list-bullet">
        <li>Add <code>&quot;runtimePlatform&quot;: { &quot;cpuArchitecture&quot;: &quot;ARM64&quot; }</code> to your task definition.</li>
      </ul>    </li>
    <li>Roles
      <ul class="list-bullet">
        <li>A task can have a task role and an execution role.</li>
        <li>The task role is given to your containers.</li>
        <li>The execution role is given to the instance that runs your containers.</li>
        <li>Both of these need an &quot;assume role policy&quot; that allows ECS to assume those roles.</li>
        <li>Terraform for the roles. This also sets up extra S3 permissions on the task execution role in case you are sending your logs to S3 (see below).
          <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">data</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_iam_policy_document<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>ecs_assume_role<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-meta sy-type sy-terraform"><span class="sy-entity sy-name sy-type sy-terraform">statement</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">actions</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-punctuation sy-section sy-brackets sy-begin 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>sts:AssumeRole<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span><span class="sy-punctuation sy-section sy-brackets sy-end sy-terraform">]</span>
    <span class="sy-variable sy-declaration sy-terraform"><span class="sy-variable sy-other sy-readwrite sy-terraform">effect</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>Allow<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
    <span class="sy-meta sy-type sy-terraform"><span class="sy-entity sy-name sy-type sy-terraform">principals</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">type</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>Service<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">identifiers</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-punctuation sy-section sy-brackets sy-begin 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>ecs-tasks.amazonaws.com<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span><span class="sy-punctuation sy-section sy-brackets sy-end sy-terraform">]</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-entity sy-name sy-type sy-terraform">condition</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">test</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>ArnLike<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">variable</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>aws:SourceArn<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">values</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-punctuation sy-section sy-brackets sy-begin sy-terraform">[</span>
        <span class="sy-meta sy-function-call sy-terraform"><span class="sy-support sy-function sy-builtin sy-terraform">format</span><span class="sy-punctuation sy-section sy-parens sy-begin 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>arn:aws:ecs:%s:%s:*<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span><span class="sy-punctuation sy-separator sy-terraform">,</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">aws_region</span><span class="sy-punctuation sy-separator sy-terraform">,</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">aws_account_id</span><span class="sy-punctuation sy-section sy-parens sy-end sy-terraform">)</span></span>
      <span class="sy-punctuation sy-section sy-brackets sy-end sy-terraform">]</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-entity sy-name sy-type sy-terraform">condition</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">test</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>StringEquals<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">variable</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>aws:SourceAccount<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">values</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-punctuation sy-section sy-brackets sy-begin sy-terraform">[</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">aws_account_id</span>
      <span class="sy-punctuation sy-section sy-brackets sy-end sy-terraform">]</span>
    <span class="sy-punctuation sy-section sy-block sy-end sy-terraform">}</span></span>

  <span class="sy-punctuation sy-section sy-block 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_iam_role<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_task_execution_role<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">name</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>fargate_my_task_execution<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">assume_role_policy</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-support sy-constant sy-terraform">data</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">aws_iam_policy_document</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">ecs_assume_role</span><span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">json</span>

  <span class="sy-meta sy-type sy-terraform"><span class="sy-entity sy-name sy-type sy-terraform">inline_policy</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">name</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>s3_put<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">policy</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-meta sy-function-call sy-terraform"><span class="sy-support sy-function sy-builtin sy-terraform">jsonencode</span><span class="sy-punctuation sy-section sy-parens sy-begin sy-terraform">(</span><span class="sy-meta sy-braces sy-terraform"><span class="sy-punctuation sy-section sy-braces sy-begin sy-terraform">{</span>
      &quot;Version&quot;: &quot;2012-10-17&quot;,
      &quot;Statement&quot;: [        
        {
          &quot;Effect&quot;: &quot;Allow&quot;,
          &quot;Action&quot;: [
             &quot;s3:*&quot;
          ],
          &quot;Resource&quot;: [
            &quot;arn:aws:s3:::my-app-logs&quot;,
            &quot;arn:aws:s3:::my-app-logs<span class="sy-comment sy-block sy-terraform"><span class="sy-punctuation sy-definition sy-comment sy-terraform">/*</span>&quot;
           ]
        },
        {
          &quot;Effect&quot;: &quot;Allow&quot;,
          &quot;Action&quot;: [
            &quot;ecr:GetAuthorizationToken&quot;,
            &quot;logs:CreateLogGroup&quot;,
            &quot;logs:CreateLogStream&quot;,
            &quot;logs:PutLogEvents&quot;
          ],
          &quot;Resource&quot;: &quot;*&quot;
        },
        {
          &quot;Effect&quot;: &quot;Allow&quot;,
          &quot;Action&quot;: [
            &quot;ecr:BatchCheckLayerAvailability&quot;,
            &quot;ecr:BatchGetImage&quot;,
            &quot;ecr:DescribeImages&quot;,
            &quot;ecr:DescribeRepositories&quot;,
            &quot;ecr:GetDownloadUrlForLayer&quot;,
            &quot;ecr:GetRepositoryPolicy&quot;,
            &quot;ecr:Images&quot;
          ],
          &quot;Resource&quot;: [
            format(&quot;arn:aws:ecr:%s:%s:repository/*&quot;, var.aws_region, var.aws_account_id)
          ]
        }
      ]
    })
  }
}

resource &quot;aws_iam_role&quot; &quot;my_task_role&quot; {
  name = &quot;fargate_my_task&quot;

  assume_role_policy = data.aws_iam_policy_document.ecs_assume_role.json

  inline_policy {
    name = &quot;s3_put&quot;
    policy = jsonencode({
      &quot;Version&quot;: &quot;2012-10-17&quot;,
      &quot;Statement&quot;: [{
        &quot;Effect&quot;: &quot;Allow&quot;,
        &quot;Action&quot;: [
          &quot;s3:*&quot;
        ],
        &quot;Resource&quot;: &quot;*&quot;
      }]
    })
  }
}

output &quot;my_task_role_arn&quot; {
  value = aws_iam_role.my_task_role.arn
}

output &quot;my_task_execution_role_arn&quot; {
  value = aws_iam_role.my_task_execution_role.arn
}

</span></span></span></span></span></span></code></pre></li>
          </ul>        </li>
      </ul>    </li>
    <li>Sending Logs to S3
      <ul class="list-bullet">
        <li>Cloudwatch is the AWS-recommended way to ship logs out but you can also send them to S3.</li>
        <li>First you&apos;ll need roles like the ones in the previous section to give S3 permissions.</li>
        <li>Then create your S3 bucket.
          <ul class="list-bullet">
            <li>This configuration also autodeletes the files after 90 days.</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_s3_bucket<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_logs<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">bucket</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>my-app-logs<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</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_s3_bucket_lifecycle_configuration<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_logs<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">bucket</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span>aws_s3_bucket<span class="sy-keyword sy-operator sy-accessor sy-terraform">.</span><span class="sy-variable sy-other sy-member sy-terraform">my_app_logs</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">rule</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">id</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>delete-old-logs<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
    <span class="sy-meta sy-type sy-terraform"><span class="sy-entity sy-name sy-type sy-terraform">expiration</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">days</span> <span class="sy-keyword sy-operator sy-assignment sy-terraform">= </span></span><span class="sy-constant sy-numeric sy-integer sy-terraform">90</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">status</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>Enabled<span class="sy-punctuation sy-definition sy-string sy-end sy-terraform">&quot;</span></span>
  <span class="sy-punctuation sy-section sy-block 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>Then set up your task definition like so.</li>
        <li><pre><code><span class="sy-source sy-js"><span class="sy-meta sy-block sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
  <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>taskRoleArn<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>ARN of the task role above<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
  <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>executionRoleArn<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>ARN of execution role above<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
  <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>requiresCompatibilities<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span>: <span class="sy-meta sy-sequence sy-js"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-js">[</span><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>FARGATE<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-section sy-brackets sy-end sy-js">]</span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
  <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>containerDefinitions<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span>: <span class="sy-meta sy-sequence sy-js"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-js">[</span>
    <span class="sy-comment sy-line sy-double-slash sy-js"><span class="sy-punctuation sy-definition sy-comment sy-js">//</span> Only needed for custom log routing.
</span>    <span class="sy-meta sy-mapping sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>name<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>log_router<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>essential<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-constant sy-language sy-boolean sy-true sy-js">true</span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>image<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>amazon/aws-for-fluent-bit:stable<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>firelensConfiguration<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-mapping sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
        <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>type<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>fluentbit<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span>
      <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>logConfiguration<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-mapping sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
        <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>logDriver<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>awslogs<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
        <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>options<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-mapping sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>awslogs-group<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>firelens-container<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>awslogs-region<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>us-west-2<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>awslogs-create-group<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>true<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>awslogs-stream-prefix<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>firelens<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span>
        <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span>
      <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>memoryReservation<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-constant sy-numeric sy-integer sy-decimal sy-js">50</span>
    <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
    <span class="sy-meta sy-mapping sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>name<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>my_app<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>image<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>acctnum.dkr.ecr.region.amazonaws.com/image:tag<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>portMappings<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-sequence sy-js"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-js">[</span><span class="sy-punctuation sy-section sy-brackets sy-end sy-js">]</span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>essential<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-constant sy-language sy-boolean sy-true sy-js">true</span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>logConfiguration<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-mapping sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
        <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>logDriver<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>awsfirelens<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
        <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>options<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-mapping sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>Name<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>s3<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-comment sy-line sy-double-slash sy-js"><span class="sy-punctuation sy-definition sy-comment sy-js">//</span> Optional, to customize key format which can be useful for batch jobs
</span>          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>s3_key_format<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span>
            <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>/%Y/%m/%Y-%m-%d-my-app-$TAG-%S<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>region<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>your-aws-region<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>bucket<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>my-app-logs<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-comment sy-line sy-double-slash sy-js"><span class="sy-punctuation sy-definition sy-comment sy-js">//</span> Rotate to a new log file when this size is reached.
</span>          <span class="sy-comment sy-line sy-double-slash sy-js"><span class="sy-punctuation sy-definition sy-comment sy-js">//</span> For persistent services this should be much smaller.
</span>          <span class="sy-comment sy-line sy-double-slash sy-js"><span class="sy-punctuation sy-definition sy-comment sy-js">//</span> For batch jobs I set it large since it&apos;s nice to have
</span>          <span class="sy-comment sy-line sy-double-slash sy-js"><span class="sy-punctuation sy-definition sy-comment sy-js">//</span> all the logs for a run in one file.
</span>          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>total_file_size<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>10G<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>upload_timeout<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>1m<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
          <span class="sy-meta sy-mapping sy-key sy-js"><span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>retry_limit<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-double sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&quot;</span>2<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&quot;</span></span></span>
        <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span><span class="sy-punctuation sy-separator sy-comma sy-js">,</span>
      <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span>
    <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span>
  <span class="sy-punctuation sy-section sy-brackets sy-end sy-js">]</span></span>
<span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span>
</span></code></pre></li>
        <li>The  <code>logConfiguration</code> options are documented at <a href="https://docs.fluentbit.io/manual/pipeline/outputs/s3">https://docs.fluentbit.io/manual/pipeline/outputs/s3</a></li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/setting_up_aws_fargate_on_ecs</link><guid isPermaLink="false">d1f910172fc7fc41c6cdb6c5c2274d0a09d28ac8929a821e82b851064e64a3de</guid><category><![CDATA[AWS]]></category><pubDate>Thu, 29 Feb 2024 00:00:00 GMT</pubDate></item><item><title><![CDATA[Filigree]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>Filigree is a web app framework based on Rust with <a href="https://kit.svelte.dev">SvelteKit</a> or <a href="https://htmx.org">Htmx</a>, which includes common components such as authentication and background workers, and also makes it easier to set up data models and generate SQL, endpoints, and so on to use them.</li>
    <li><h2>Task List</h2>
      <ul class="list-bullet">
        <li><h3>Up Next</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Figure out what to do with this project
              <ul class="list-bullet">
                <li>I&apos;m still not getting something I really like with SQL generation, but ORMs are still too inflexible. What&apos;s the proper solution here?</li>
                <li>Maybe
                  <ul class="list-bullet">
                    <li>Rework auth to use JWT method</li>
                    <li>Autogenerate Drizzle table definitions for database so that the frontend code can just use Drizzle for database access.</li>
                    <li>Maybe use GraphQL for complex object fetches?</li>
                  </ul>                </li>
              </ul>            </li>
            <li id="669f04f7-f837-4f1c-849f-0d2f308cd39d"><input type="checkbox" disabled> Make test objects easier to manage
              <ul class="list-bullet">
                <li>Instead of a one-size-fits-all bootstrapping we should just do it with a custom function per file, which will speed up the tests overall a bit, and also greatly simplify testing of through models where we need to make sure that the IDs all line up between the different objects.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Soon</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Investigate making it easier to auto-forward page actions through SvelteKit to the API</li>
            <li><input type="checkbox" disabled> Finish tests for through relationships
              <ul class="list-bullet">
                <li>Do this after <a class="block-ref" href="https://imfeld.dev/notes/projects_filigree#669f04f7-f837-4f1c-849f-0d2f308cd39d"><input type="checkbox" disabled> Make test objects easier to manage</a></li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Evaluate faster API authentication methods between SvelteKit Backend and Rust Backend
              <ul class="list-bullet">
                <li>Basically to make it work better to make multiple API calls from a single request without hitting the database every time to look up the user/roles/etc.</li>
                <li>Instead of just a session ID, have an option to generate a short-lived JWT or similar token that contains all the auth info. Store the JWT in the cookie, on each request check the expiration time and get a new one if getting close to expiring.</li>
                <li>Might also need some hook for client-side requests that can do the renewal if they talk directly to the API.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Use docker compose with separate containers for API and SvelteKit parts
              <ul class="list-bullet">
                <li>More trouble than it&apos;s worth to force the two parts into a single container</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> More flexible configuration
              <ul class="list-bullet">
                <li>Switch from <code>clap</code> to <code>config</code> or something for the <code>serve</code> command&apos;s config</li>
                <li>Main impetus here is to make it possible to read configuration from ConfigMap-sourced volumes in K8S and similar systems.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Later/Maybe</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Simplify permissions system when using custom auth
              <ul class="list-bullet">
                <li>Since we aren&apos;t creating the roles and users, we need a way to make sure that people have reasonable permissions when they start.</li>
                <li>Perhaps just a function that adds default permissions or something like that when we first see a user.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> organization_members needs to be extendable like users is
              <ul class="list-bullet">
                <li>This is so that there&apos;s a place to attach per-org metadata and fields</li>
                <li>I think the proper thing to do here is that once we fully support <a class="block-ref" href="https://imfeld.dev/notes/projects_filigree#65d828c3-0e80-454f-bb1c-05e0715bef6d"><input type="checkbox" disabled checked> Joining tables &#x2014; Jul 22nd, 2024</a> we can just put this into the system as a joining table.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Improve API error printing
              <ul class="list-bullet">
                <li>Some errors use <code>source</code> and error_stack&apos;s default printer ignores it. Should walk the source chain as well. This seems difficult though without using nightly features</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Metrics tracking and alerts
              <ul class="list-bullet">
                <li>Consistently high CPU, RAM usage, etc.</li>
                <li>Might just use a service for this, need to see</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Option to only allow connections from certain IP ranges
              <ul class="list-bullet">
                <li>eg <a href="https://www.cloudflare.com/ips/">https://www.cloudflare.com/ips/</a> when using Cloudflare</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Helpers for retrying requests and other operations
              <ul class="list-bullet">
                <li>rate limit retry-after</li>
                <li>exponential retry with jitter, etc. for regular retryable failures</li>
                <li>ability to not retry for certain errors</li>
                <li>Maybe just fork backon for this?</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> QoL Issues Encountered
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Conflict in src/main.rs on first run
                  <ul class="list-bullet">
                    <li>Maybe detect if this is the first run by looking at absence of .state directory and ask if it&apos;s ok to just replace it</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Add auto-bootstrap option
              <ul class="list-bullet">
                <li>When appropriate environment variables are set, allows the API to do the bootstrapping itself when it first starts up</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Ops helpers
              <ul class="list-bullet">
                <li>This isn&apos;t part of the core framework, but a set of tools for automatically doing ops-related tasks.</li>
                <li><input type="checkbox" disabled> Back up database to cloud storage on a regular basis</li>
                <li><input type="checkbox" disabled> Deploy on Git push</li>
                <li><input type="checkbox" disabled> Preview environments?</li>
                <li><input type="checkbox" disabled> Set up Postgres slow statement tracking and create a report on what was slow</li>
                <li><input type="checkbox" disabled> Drop partitions of old data</li>
              </ul>            </li>
            <li>Frontend
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> SvelteKit: Error page</li>
                <li><input type="checkbox" disabled> Add dependencies to package.json like we do for the Rust side</li>
                <li><input type="checkbox" disabled> App shell with nav bar, etc.</li>
                <li><input type="checkbox" disabled> Password reset page</li>
                <li><input type="checkbox" disabled> Profile management page</li>
                <li><input type="checkbox" disabled> Manage your organization</li>
                <li><input type="checkbox" disabled> Default Model editor page</li>
                <li><input type="checkbox" disabled> Default Model list page</li>
                <li><input type="checkbox" disabled> Admin: invite users to app</li>
                <li><input type="checkbox" disabled> Admin: administer organizations</li>
                <li><input type="checkbox" disabled> 404 page</li>
                <li><input type="checkbox" disabled> TS Type Enhancements
                  <ul class="list-bullet">
                    <li><input type="checkbox" disabled> Permission names per model</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled> HTMX support improvements
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Debugging option to show a toast whenever a livereload finishes or an htmx error occurs</li>
                <li><input type="checkbox" disabled> move client-side code from SBBP into Filigree for reuse by other projects</li>
                <li><input type="checkbox" disabled> theming, dark/light mode with persistence via localStorage and header script to inject dark class
                  <ul class="list-bullet">
                    <li>DaisyUI provides a lot of this already</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Island architecture support for Svelte components
                  <ul class="list-bullet">
                    <li>Each of these should be its own entry point in the Vite config, and from there we can use the manifest support to get the necessary tags</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Flash/toast support
                  <ul class="list-bullet">
                    <li>Can probably be done with HX-Trigger header, or maybe an extension</li>
                    <li><input type="checkbox" disabled> Adapter function for actions to have errors return a toast</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Implement fancy dropdown menu in Alpine with floating-ui
                  <ul class="list-bullet">
                    <li><a href="https://github.com/awcodes/alpine-floating-ui">https://github.com/awcodes/alpine-floating-ui</a> may work well</li>
                    <li><a href="https://alpinejs.dev/plugins/anchor">https://alpinejs.dev/plugins/anchor</a> is the official one, seems to support fewer options</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Improve livereload performance
                  <ul class="list-bullet">
                    <li>Not sure how much opportunity there is here but it feels like it can be made 20% faster</li>
                    <li>Dioxus is working on <a href="https://twitter.com/dioxuslabs/status/1797206374960476530">a solution</a> that I should be able to adapt once it&apos;s public.</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Models Backend
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Move a lot of the auth queries like <code>add_permissions_to_role</code> into the template system, so they can be placed on the model and/or customized
                  <ul class="list-bullet">
                    <li>Everything in <code>fiiligree/src/users</code> except <code>add_user_email_login</code> which is used by the OAuth system and so is harder to move.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Generate endpoints for grandchild objects
                  <ul class="list-bullet">
                    <li>Probably need to make this generally recursive which may difficult to do via the current template system, might need a macro that can call itself or some fancier fragment rendering.</li>
                    <li>I think what we want to do here is for each item in <code>children</code> to potentially have its own <code>children</code>, instead of recalculating each one independently. Also need some way to walk up the stack of children here for URL generation (or do we? Probably it doesn&apos;t matter to have the grandparent in the URL itself).</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Make it easier for differing get/list model types to be used interchangeably
                  <ul class="list-bullet">
                    <li>This matters more for HTMX mode</li>
                    <li>Basically we might have a function that renders something for a particular object, but it gets difficult when the input can be either the Get or List result types when they are different.</li>
                    <li>One option may be to generate a type of view struct which can take a reference to either type and then generate a bunch of methods for the shared fields which can return either value. This is a lot of boilerplate, but its all autogenerated, but these aren&apos;t used too often</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Allow joining models to have their own extra data fields</li>
                <li><input type="checkbox" disabled> For runtime queries, create query builders that use SeaQuery to add the auth clause automatically
                  <ul class="list-bullet">
                    <li>Add an enum of columns in the table</li>
                    <li>Allow adding any population of data or id at runtime</li>
                    <li>Each child population can be seen as a column as well (but probably don&apos;t do this, it should )</li>
                    <li>Add functions for &quot;all base columns&quot; to drop into the query</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> tests for list endpoint filtering/pagination/sorting</li>
                <li><input type="checkbox" disabled> Models with non-writable, but required, fields don&apos;t generate valid tests
                  <ul class="list-bullet">
                    <li>This can happen when the normal &quot;create&quot; workflow happens in some other way than calling a REST create endpoint.</li>
                    <li>Also consider detecting this and just not generating an &quot;insert&quot; script that can&apos;t work.
                      <ul class="list-bullet">
                        <li>The criteria is a model that has at least one field which
                          <ul class="list-bullet">
                            <li>is not <code>fixed</code></li>
                            <li>is not <code>nullable</code></li>
                            <li>has no default (SQL or Rust)</li>
                            <li>is not writable by owner</li>
                          </ul>                        </li>
                      </ul>                    </li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Support file uploads in multipart form submissions
                  <ul class="list-bullet">
                    <li>A lot of the underlying framework for this is ready but the code itself needs to be written. Main issue is that the current <code>FormOrJson</code> can&apos;t be used in that case. Instead we probably need something that even on a JSON file looks like a multipart submission, but just doesn&apos;t return any files.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Test populated get and list when update<em class="italic">with</em>parent is false</li>
                <li><input type="checkbox" disabled> Models can define extra permissions</li>
              </ul>            </li>
            <li>CLI / Template System
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Bug: CLI doesn&apos;t throw an error if a field is defined twice</li>
                <li><input type="checkbox" disabled> Use <a href="https://lib.rs/crates/toml_edit">toml_edit</a> to allow adding features to Cargo.toml</li>
                <li><input type="checkbox" disabled> When generating migrations, read in the actual app migrations and ignore tables that we don&apos;t care about.
                  <ul class="list-bullet">
                    <li>This lets us see things like model tables having been removed if, say, a migration file was deleted since it did the wrong thing.</li>
                    <li>Do need to figure out how to handle extra columns added in the model via manual SQL migrations here.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Command to print config and diff files against last gen and current config gen</li>
                <li><input type="checkbox" disabled> Consider using something like sea_query to generate queries instead of a bunch of fixed queries
                  <ul class="list-bullet">
                    <li>Will add flexibility, and could make a lot of things simpler. Need to see if there&apos;s still a good way to pre-generate queries when running the CLI</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Data-driven default org contents, even if just a JSON blob to start
                  <ul class="list-bullet">
                    <li>This can piggyback off of the fixture system</li>
                    <li>Considering not doing this since it may be simpler to just do it in a function.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Some way to easily add hooks to CRUD operations
                  <ul class="list-bullet">
                    <li>Developer should probably just edit the generated code here.</li>
                    <li>Maybe add stub functions to each model to allow adding pre/post save hooks</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Make it easy for additional queries to use the permissions macros
                  <ul class="list-bullet">
                    <li>Probably moving away from SQL and toward Rust code for permissions checks so this won&apos;t matter as much</li>
                    <li>Can suggest using sqlweld here</li>
                    <li>See if there&apos;s a path to using a query builder like <code>sea_query</code> instead, maybe as an addition to the existing thing.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Autogenerate belongs_to field if necessary when the inverse has is detected without a through table.
                  <ul class="list-bullet">
                    <li>Currently this returns an error instead</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Authentication and Registration
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Make organization_members a real model instead of just a table
                  <ul class="list-bullet">
                    <li>Once joining tables work properly</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Make SessionBackend work with custom auth
                  <ul class="list-bullet">
                    <li>It mostly works but when the tables are in another schema then it doesn&apos;t. Need a less-fixed query method here.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Rate limiting for login-related endpoints
                  <ul class="list-bullet">
                    <li><a href="https://docs.rs/governor/latest/governor/">https://docs.rs/governor/latest/governor/</a></li>
                    <li><a href="https://lib.rs/crates/tower_governor">https://lib.rs/crates/tower_governor</a></li>
                    <li><a href="https://github.com/brandur/redis-cell">https://github.com/brandur/redis-cell</a> if i need this to be distributed
                      <ul class="list-bullet">
                        <li>DragonflyDB is a redis-compatible that has this built-in</li>
                      </ul>                    </li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Passwordless login should include a code that can be pasted into the UI to log in instead.</li>
                <li><input type="checkbox" disabled> Password requirements
                  <ul class="list-bullet">
                    <li>length</li>
                    <li>mix of character types</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Prevent reuse of recent passwords</li>
                <li><input type="checkbox" disabled> Expire passwords that need a rehash due to upgraded standards</li>
                <li><input type="checkbox" disabled> Option to expire passwords after a certain amount of time
                  <ul class="list-bullet">
                    <li>This isn&apos;t recommended practice but some enterprise clients require this in order to sign with you.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Passkeys
                  <ul class="list-bullet">
                    <li><a href="https://passkeys.dev/">https://passkeys.dev/</a></li>
                    <li><a href="https://github.com/1Password/passkey-rs">https://github.com/1Password/passkey-rs</a></li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> RSA timecode Authenticator app</li>
                <li><input type="checkbox" disabled> Authenticator Account recovery codes</li>
                <li><input type="checkbox" disabled> Account self-deletion</li>
                <li><input type="checkbox" disabled> Optional TOS acceptance at registration</li>
                <li><input type="checkbox" disabled> Service accounts</li>
                <li><input type="checkbox" disabled> Record info about sessions (geoip, user-agent, etc.)</li>
                <li><input type="checkbox" disabled> Allow viewing other sessions for your user, and potentially logging them out</li>
                <li><input type="checkbox" disabled> Invite users to your team</li>
                <li><input type="checkbox" disabled> Invite users to the app, but not in your team</li>
                <li><input type="checkbox" disabled> Link a new OAuth login that may not have a matching email to an existing account</li>
                <li><input type="checkbox" disabled> When logging in via OAuth, fetch all known emails from the OAuth provider and see if any of them match an existing account if we haven&apos;t seen the account before.
                  <ul class="list-bullet">
                    <li>Need to handle the case where there are multiple emails and they match different accounts. This probably requires an interstitial after the login page to select the matching account.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Waitlist functionality</li>
                <li><input type="checkbox" disabled> Put auth code behind an adapter so it can be swapped out for a 3rd-party service</li>
              </ul>            </li>
            <li>Permissions and Authorization
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> <a class="block-ref" href="https://imfeld.dev/notes/projects_filigree#657a981d-8503-427b-a6ab-319c7879b94c">Project-based Access Control</a></li>
                <li><input type="checkbox" disabled> Consider separating permissions checks from the SQL queries themselves
                  <ul class="list-bullet">
                    <li>This has the upside that it would simplify a lot of checking for permissions and make more complex permissions easier</li>
                    <li>Upsides
                      <ul class="list-bullet">
                        <li>SQL templates become simpler</li>
                        <li>Much easier to implement ABAC</li>
                        <li>Easier to differentiate authz errors from missing objects</li>
                      </ul>                    </li>
                    <li>Downsides
                      <ul class="list-bullet">
                        <li>Requires remembering to actually check for permissions, though that&apos;s alleviated some by the template system since the checks can be placed in the access functions.</li>
                        <li>We also end up reading objects multiple times in update/delete situations, though this isn&apos;t that big of a deal. For project-based permissions we could potentially load all the projects though would have to see about that.</li>
                      </ul>                    </li>
                    <li>Should probably do this as permissions is becoming more and more complex as more options are added.</li>
                    <li>Maybe consider something like casbin or biscuits too. Feels like overkill at this point for most things though, probably more appropriate for an API-first product.</li>
                    <li>list query would still need permissions checks in the SQL</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> API Key lookup query needs to change to make permissions a subset of the user&apos;s permissions when inherit<em class="italic">user</em>permissions is false
                  <ul class="list-bullet">
                    <li>This way if a user has some permissions removed, they can&apos;t bypass those limits using an old API key which still had those permissions</li>
                    <li>This is actually a good use case for casbin or biscuit, though could still be implemented without it.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Feature flags</li>
                <li><input type="checkbox" disabled> Add &quot;all&quot; model permissions so that some roles have permissions on everything even when new models are added.
                  <ul class="list-bullet">
                    <li>This needs some more thought though. Some models like user, org, etc don&apos;t want to have this blanket rule applied. Might be better to just do this via migrations.</li>
                    <li>Better: Have some models use global org-wide permissions of just &quot;read,&quot; &quot;write,&quot; etc. and only specific models have their own set of permissions.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Ability to apply limits on how many objects are created
                  <ul class="list-bullet">
                    <li>There&apos;s a lot of flexibility here, since the limits could be something simple like number of objects, or some derived metric such as &quot;number of minutes&quot; for an audio-based model.</li>
                    <li>The source for the limits could also come from a number of different places, most commonly pricing plan but could be other stuff such as number of community contributions, upvotes, etc.</li>
                    <li>So the question is what&apos;s the proper framework to make it easy to write code that accomplishes all this.
                      <ul class="list-bullet">
                        <li>Make it easy to fetch the limits data.</li>
                        <li>Make it easy to enforce rules around these limits.</li>
                      </ul>                    </li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> API call quotas
                  <ul class="list-bullet">
                    <li>e.g. 3000 calls per month, 20  calls per hour</li>
                    <li>This should be able to group endpoints by categories where they all share a quota</li>
                    <li id="65b71168-ac29-47fc-b3f5-6913e2ea63c2">Might need to do this with a combination of event tracking and a rate limiter</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Usage-based pricing</li>
                <li><input type="checkbox" disabled> Ability to add rate limiting on any endpoint
                  <ul class="list-bullet">
                    <li>keyed by org</li>
                    <li>keyed by user</li>
                    <li>endpoints can share a bucket</li>
                    <li>Local tracking is fine at first, should eventually support doing it in Redis or similar to support multiple API servers.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Ability for admins to assume permissions of other users
                  <ul class="list-bullet">
                    <li>This needs to be audited as well even if nothing else is audited</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Workers
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Ability to run worker tasks in some other process/VM/FaaS
                  <ul class="list-bullet">
                    <li>Need to figure out configuration for this... this might be better implemented in the task queue itself</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Outbox pattern support for transactional starting of background jobs
                  <ul class="list-bullet">
                    <li>Not important for now since <a href="https://imfeld.dev/notes/projects_effectum">Effectum</a> only runs in-process but at some point it will have a server mode and then it will make more sense.</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Block Storage and File Uploads
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Option for Uploads via signed URL</li>
                <li><input type="checkbox" disabled> Generate tests for file upload endpoints</li>
                <li><input type="checkbox" disabled> When retain<em class="italic">file</em>on_delete is set, optionally add an entry to a table that lists orphaned files.
                  <ul class="list-bullet">
                    <li>The idea being that we might want to hold on to orphaned files for some period of time, and then delete them later.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Image statistics on upload
                  <ul class="list-bullet">
                    <li>format, width, height
                      <ul class="list-bullet">
                        <li>Can detect on upload using code from <a href="https://imfeld.dev/notes/projects_pic_store">Pic Store</a></li>
                      </ul>                    </li>
                  </ul>                </li>
                <li><input type="checkbox" disabled>  Generate image thumbnail and blurhash
                  <ul class="list-bullet">
                    <li>Best done in background worker</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Potentially need a way to upload a file before the parent object exists. Will probably wait for a real use case on this one.
                  <ul class="list-bullet">
                    <li>Could make the parent ID nullable here and then add an endpoint to &quot;claim&quot; a file for a particular parent object</li>
                    <li>Also could just do the Github style thing and upload them to a publicly available bucket. We don&apos;t maintain a file database entry  in this case, and just return the public URL for whatever use it is.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> Allow specifying storage provider presets from env vars, not just in config</li>
              </ul>            </li>
            <li>Notifications
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Notifications: Other types of notifications?</li>
                <li><input type="checkbox" disabled> Notifications Internal notifications
                  <ul class="list-bullet">
                    <li>e.g. Discord webhook notifying myself about events</li>
                    <li>This should have notification templates and also destinations for the notifications</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Event logging
              <ul class="list-bullet">
                <li>Should support arbitrary types of events and also allow setting up queries that filter on various event sequences for a user/org</li>
              </ul>            </li>
            <li>Workflow System
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Build the workflow system
                  <ul class="list-bullet">
                    <li>A lot from <a href="https://imfeld.dev/notes/projects_ergo">Ergo</a> can be reused</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> State chart implementation</li>
                <li><input type="checkbox" disabled> Workflows can &quot;sleep&quot; by enqueueing a scheduled task that will continue running when it wakes up</li>
                <li><input type="checkbox" disabled> Endpoint to send an event to a workflow</li>
                <li><input type="checkbox" disabled> Define workflow actions</li>
                <li><input type="checkbox" disabled> Ability to define workflows in the app config</li>
                <li><input type="checkbox" disabled> Scripting for Workflows
                  <ul class="list-bullet">
                    <li>Piggyback on Ergo but maybe switch to QuickJS or <a href="https://github.com/saghul/txiki.js">saghul/txiki.js: A tiny JavaScript runtime</a></li>
                  </ul>                </li>
              </ul>            </li>
            <li>Hosting
              <ul class="list-bullet">
                <li>Not sure if I&apos;ll do any of this, it&apos;s really better handled by a reverse proxy</li>
                <li><input type="checkbox" disabled> LetsEncrypt support</li>
                <li><input type="checkbox" disabled> TLS/HTTPS support</li>
                <li><input type="checkbox" disabled> HTTP -&gt; HTTPS redirect</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> User model list/get functions need to return users which are in multiple orgs, and whose current org is a different one</li>
            <li><input type="checkbox" disabled> Easy setup for commercialization</li>
            <li><input type="checkbox" disabled> Collect and export metrics to some tool
              <ul class="list-bullet">
                <li>Looks like Honeycomb supports Prometheus format</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Basic Analytics</li>
            <li><input type="checkbox" disabled> More complex workflow/funnel analytics</li>
            <li>Data Hooks and Similar
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> Delete log table for possibility of undelete later</li>
                <li><input type="checkbox" disabled> Option for auditing on all operations</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Password confirmation flow for &quot;dangerous&quot; actions</li>
            <li><input type="checkbox" disabled> User profile photos</li>
            <li><input type="checkbox" disabled> Copy all tests in test-app into filigree crate where it makes sense
              <ul class="list-bullet">
                <li>Some tests are more convenient to test in the test app, but rely on minimal scaffolded functionality. Should add a minimal server/migration setup in filigree crate tests so that they can run there as well.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Support SQLite
              <ul class="list-bullet">
                <li>Not sure how much work it will be to support both Postgres and SQLite, could turn out to be a big hassle due to differing feature support and syntax, but I&apos;ll see at some point</li>
                <li>Things I use that are different in SQLite
                  <ul class="list-bullet">
                    <li><input type="checkbox" disabled>AL JOIN (it supports them, just not with this syntax)</li>
                    <li>No native <code>uuid</code> and <code>timestamptz</code> types</li>
                    <li>No <code>array_agg</code> - this is probably the biggest one</li>
                    <li><code>jsonb</code> functions are a bit different</li>
                    <li>parameter binding syntax is different</li>
                  </ul>                </li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Use a build script to inject the current <code>filigree</code> version into the value used by <code>add_deps</code> &#x2014; Aug 6th, 2024
              <ul class="list-bullet">
                <li>Minor thing but makes the release process a bit smoother. Right now this has to be updated manually.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> When using &quot;string auth IDs&quot; or custom auth in general, make it a different field that references the user/organization/role ID in the external system, and continue using a normal ObjectId type internally &#x2014; Jul 22nd, 2024
              <ul class="list-bullet">
                <li>This simplifies a whole bunch of code that otherwise has to go back and forth depending on the actual underlying type of the auth object.</li>
              </ul>            </li>
            <li id="65d828c3-0e80-454f-bb1c-05e0715bef6d"><input type="checkbox" disabled checked> Joining tables &#x2014; Jul 22nd, 2024</li>
            <li><input type="checkbox" disabled checked> Model should have functions for adding/updating/removing child objects &#x2014; Jul 17th, 2024</li>
            <li id="6694db13-c3b4-4e96-977b-c1a50c0d314a"><input type="checkbox" disabled checked> Rework query template system &#x2014; Jul 16th, 2024
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Create more of the query template stuff in the Rust code instead of in Tera
                  <ul class="list-bullet">
                    <li>This will make certain things a lot simpler especially when dealing with binding parameters</li>
                    <li><input type="checkbox" disabled checked> When generating a static query, keep a mapping of binding parameter to position, and have some method for the template system to generate a bunch of bindings from an object.
                      <ul class="list-bullet">
                        <li>This allows the query system to call a single function with data generated along with the bindings, which adds the relevant parameter bindings and doesn&apos;t require us to keep the two places in sync.</li>
                      </ul>                    </li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Remove _permission from all the structs
                  <ul class="list-bullet">
                    <li>This removes the ability to have the custom Serialize impl that only serializes fields visible to the Owner if the permission is write.</li>
                    <li>How useful is it anyway to have fields which are only viewable by the owner? Maybe it doesn&apos;t matter.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Remove permissions checks from queries for global auth scope cases where we already know the permissions from the roles
                  <ul class="list-bullet">
                    <li>project-level and object-level still need it</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Change all the query functions into objects on a struct. Consider making the struct into a thing that takes a reference to the AuthInfo.
                  <ul class="list-bullet">
                    <li>This mostly just makes the typing experience nicer since instead of <code>post::queries::create</code> you call <code>Post::create</code>.</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Make user/org/role/etc. models optional &#x2014; May 26th, 2024</li>
            <li><input type="checkbox" disabled checked> Allow user/org/role IDs to be strings when using custom auth &#x2014; May 26th, 2024</li>
            <li><input type="checkbox" disabled checked> Add more test apps &#x2014; May 24th, 2024
              <ul class="list-bullet">
                <li>Add more as big new features are added</li>
                <li>These should mostly be .gitignored except for the files that filigree doesn&apos;t generate</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Bug: Unique fields need to be unique per org, not globally &#x2014; May 23rd, 2024</li>
            <li><input type="checkbox" disabled checked> Pluggable auth system &#x2014; May 22nd, 2024
              <ul class="list-bullet">
                <li>Support talking to some other auth microservice, external service, using JWTs or Biscuits, etc.</li>
                <li>This requires
                  <ul class="list-bullet">
                    <li>A new function on the <code>AuthQueries</code> trait that can take a request <code>Parts</code> instead of a pre-parsed API key or session ID
                      <ul class="list-bullet">
                        <li>Or maybe just change the api_key to pass the raw Bearer token, and make it easy to unpack that into an API key for standard uses</li>
                      </ul>                    </li>
                    <li>trait functions should return some error type other than an sqlx::Error. An <code>AuthError</code> is probably the way to go or maybe even just start using <code>Report</code> there so we can save the internal error</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Interactive bootstrap command &#x2014; Apr 26th, 2024</li>
            <li><input type="checkbox" disabled checked> Command to list all the environment variables that will be read along with their defaults if not provided
              <ul class="list-bullet">
                <li>Might be a bit complex but worth it since there can be a lot</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Bug: when deleting a model the files under <code>src/model/NAME/</code> are not removed.
              <ul class="list-bullet">
                <li>Probably need to read the <code>.state</code> directory to see what files we generated before but aren&apos;t being generated this time</li>
                <li>Also don&apos;t delete files which have been edited</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Anonymous user mode
              <ul class="list-bullet">
                <li>Anonymous users get a default Authed with a specific org and user ID created just for the anonymous user.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Allow specifying a user ID for anonymous users to impersonate. This lets them have specific permissions, see certain objects, etc. &#x2014; Apr 16th, 2024</li>
            <li><input type="checkbox" disabled checked> Add <code>init</code> command that runs Cargo init, create-svelte, and creates basic config.toml? &#x2014; Apr 16th, 2024</li>
            <li><input type="checkbox" disabled checked> Add a command that creates a new Postgres user, password, and database for your project &#x2014; Apr 16th, 2024</li>
            <li><input type="checkbox" disabled checked> Support maud/htmx as a SvelteKit alternative for less complicated apps &#x2014; Apr 16th, 2024</li>
            <li><input type="checkbox" disabled checked> Switch out deprecated Jaeger tracing crate for opentelemetry-otlp &#x2014; Apr 15th, 2024</li>
            <li><input type="checkbox" disabled checked> When a type and its populated type are the same, don&apos;t generate e.g &quot;ListAndPopulatedListResult&quot;, just &quot;ListResult&quot; &#x2014; Apr 15th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: manifest alterations need to be a vite plugin so they work in dev mode &#x2014; Apr 12th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: live reload &#x2014; Apr 12th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: asset pipeline config to bundle CSS and scripts, compress assets, etc. &#x2014; Apr 11th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: build JS with vite or esbuild, read from file manifest to get list of static files with hash &#x2014; Apr 11th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: Simple bundling with vite &#x2014; Apr 11th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: Read vite manifest to support hashed values &#x2014; Apr 11th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: Instantiate manifest object in app, set up watcher in dev mode  &#x2014; Apr 11th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: obfuscate error middleware should only run on JSON responses (although maybe do HTML obfuscation too)  &#x2014; Apr 10th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: Potentially a new extractor that lives alongside FormOrJson that always returns the form  &#x2014; Apr 10th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: Customize <code>/</code> route in pages &#x2014; Apr 10th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: Tailwind CSS support &#x2014; Apr 10th, 2024</li>
            <li><input type="checkbox" disabled checked> Auth failures need to redirect to login page instead of just returning a 404 error -  &#x2014; Apr 9th, 2024
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Errors in general need to do that</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Skip rendering sveltekit files when it is not enabled  &#x2014; Apr 8th, 2024</li>
            <li><input type="checkbox" disabled checked> Remove &quot;sync types&quot; and TS generation code when not using sveltekit  &#x2014; Apr 8th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: <code>Page</code> wrapper that gets standard &lt;head&gt; tag contents for all  &#x2014; Apr 8th, 2024</li>
            <li><input type="checkbox" disabled checked> htmx: Allow defining pages and actions  &#x2014; Apr 8th, 2024
              <ul class="list-bullet">
                <li>This works similar to the existing endpoints stuff but made for HTML app</li>
                <li>Autogenerate a rust module for each path and its actions</li>
                <li>Should this be in a nested directory structure or just by filenames? Maybe nested directories</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Backend Error reporting &#x2014; Apr 7th, 2024
              <ul class="list-bullet">
                <li>Almost done, just need to hook up the error reporting interface into FiligreeServerState so that I can report errors outside of the automatic system.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Frontend error reporting &#x2014; Apr 7th, 2024</li>
            <li><input type="checkbox" disabled checked> Generate JS code to talk to the API
              <ul class="list-bullet">
                <li>This is done for custom endpoint functions, not yet for CRUD</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Add configuration to enable trace export &#x2014; Apr 6th, 2024</li>
            <li><input type="checkbox" disabled checked> Add SQL functions to go between an object ID text and UUID forms. &#x2014; Apr 5th, 2024
              <ul class="list-bullet">
                <li>Should have these left over from Ergo</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Frontend: Generate client-side validation &#x2014; Apr 5th, 2024
              <ul class="list-bullet">
                <li>This is mostly done with the generated zod schema, but some things are needed such as checking against create or update schema as appropriate</li>
                <li>Right now the UI doesn&apos;t update right on a client-side validation failure</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Set it up to work in production &#x2014; Apr 5th, 2024
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Dockerfile</li>
                <li><input type="checkbox" disabled checked> handleFetch hook</li>
                <li><input type="checkbox" disabled checked> Rust side has a fallback route which directs data to the SvelteKit server
                  <ul class="list-bullet">
                    <li>Most uses will also have a reverse proxy in front but this will let it work without that</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Command to print all the environment variables that the generated application will check &#x2014; Apr 2nd, 2024</li>
            <li><input type="checkbox" disabled checked> Specify custom endpoints per model &#x2014; Mar 29th, 2024
              <ul class="list-bullet">
                <li>This generates an endpoint function, a route, path/query/body parameters, input/output/query structures, and corresponding Typescript functions and interfaces to use it.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Allow omitting certain normal fields on list &#x2014; Mar 27th, 2024
              <ul class="list-bullet">
                <li>e.g. if there&apos;s a large JSON field not needed for the list page we should omit it.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Easy generation of TS types from Rust types &#x2014; Mar 25th, 2024</li>
            <li><input type="checkbox" disabled checked> Background jobs v1 &#x2014; Mar 23rd, 2024
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Define background jobs in config
                  <ul class="list-bullet">
                    <li>Option to set recurrence in config</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Global and per-job concurrency settings
                  <ul class="list-bullet">
                    <li>Actually do this via multiple worker configurations. Can configure workers equal to your concurrency desires.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Generate a payload structure for the background job
                  <ul class="list-bullet">
                    <li>No need to define the contents in the config, the user can just fill in what they need</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Generate a helper to enqueue a background job</li>
                <li><input type="checkbox" disabled checked> Generate a stub payload and function that runs the job</li>
                <li><input type="checkbox" disabled checked> Hook it all up
                  <ul class="list-bullet">
                    <li>Add queue to ServerState</li>
                    <li>Make environment variables</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Option to allow setting an object ID when creating an object &#x2014; Mar 22nd, 2024
              <ul class="list-bullet">
                <li>This can help with scenarios where we want to create an object and potentially write it to the database later, but have the ID to use beforehand.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Generate Typescript interfaces for models &#x2014; Mar 19th, 2024</li>
            <li><input type="checkbox" disabled checked> Generate tests for child object url endpoints &#x2014; Mar 17th, 2024</li>
            <li><input type="checkbox" disabled checked> File model type &#x2014; Mar 15th, 2024
              <ul class="list-bullet">
                <li>Files are a separate model, and when they need to be referenced by another model they can just form a parent-child relationship.</li>
                <li><input type="checkbox" disabled checked> Add a function for upload
                  <ul class="list-bullet">
                    <li>where should this function go? probably a new file which handles upload, delete, and whatever else might be necessary</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Add an endpoint to process an upload directly</li>
                <li><input type="checkbox" disabled checked> Delete file when model is deleted</li>
                <li><input type="checkbox" disabled checked> Delete files when parent model is deleted</li>
                <li><input type="checkbox" disabled checked> Special file configuration
                  <ul class="list-bullet">
                    <li>upload path template</li>
                    <li>a bucket from <code>storage.bucket</code> to store an upload</li>
                    <li>permissions for download or not</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Extra optional fields
                  <ul class="list-bullet">
                    <li>hash</li>
                    <li>file size</li>
                    <li>original filename</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Initial Block Storage Support &#x2014; Mar 10th, 2024
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Block storage abstraction
                  <ul class="list-bullet">
                    <li>use object_store crate for the underlying interface</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Storage configuration
                  <ul class="list-bullet">
                    <li>The idea here is that each storage location can be configured but the details should be completely configurable at runtime.
                      <ul class="list-bullet">
                        <li>Or should it always be environment based? Probably yes actually except maybe some basics like bucket name, endpoint, etc.</li>
                      </ul>                    </li>
                    <li>something like <code>STORAGE_${location_name}_BUCKET</code> and so on for each provider</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Use storage configuration in template</li>
                <li><input type="checkbox" disabled checked> Stream request body into object storage</li>
                <li><input type="checkbox" disabled checked> Stream multipart files into object storage</li>
                <li><input type="checkbox" disabled checked> Allow examining data as it streams through</li>
                <li><input type="checkbox" disabled checked> Stream file from object storage as Response body</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Create endpoints should return children when doing update<em class="italic">with</em>parent &#x2014; Feb 29th, 2024</li>
            <li><input type="checkbox" disabled checked> <del class="line-through">Store .gen files in a tar file instead</del> - Cancelled Feb 28th, 2024
              <ul class="list-bullet">
                <li>This reduces the huge number of files in the state directory, but makes merge conflicts more difficult to deal with.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Parent/child model relationships &#x2014; Feb 27th, 2024
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Configuration</li>
                <li><input type="checkbox" disabled checked> Add fields for <code>belongs_to</code> relationships to model structures</li>
                <li><input type="checkbox" disabled checked> Generate composite primary key for joining tables and update queries appropriately</li>
                <li><input type="checkbox" disabled checked> handle has_one relationships in fetch queries with ID fetch</li>
                <li><input type="checkbox" disabled checked> handle has_one relationships in fetch queries with data fetch</li>
                <li><input type="checkbox" disabled checked> handle has_many relationships in fetch queries with ID fetch</li>
                <li><input type="checkbox" disabled checked> handle has_many relationships in fetch queries with data fetch</li>
                <li><input type="checkbox" disabled checked> Allow populating <code>references</code> fields on fetch when configured</li>
                <li><input type="checkbox" disabled checked> Updates for has_one relationships with data payload</li>
                <li><input type="checkbox" disabled checked> Updates for has_many relationships with data payload</li>
                <li><input type="checkbox" disabled checked> Generate CRUD endpoints for children of a particular object
                  <ul class="list-bullet">
                    <li>This is basically the same as the normal endpoint, but with the parent ID specified in the URL instead of in the payload.</li>
                    <li>When it&apos;s not a many relationship, omit the child ID parameter</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Populated query functions
                  <ul class="list-bullet">
                    <li><input type="checkbox" disabled checked> Call populated functions from endpoints when appropriate</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> SQL Queries for child objects
                  <ul class="list-bullet">
                    <li><input type="checkbox" disabled checked> List by parent ID
                      <ul class="list-bullet">
                        <li>This could be done as just another filter on the list query instead of a new query template</li>
                      </ul>                    </li>
                    <li><input type="checkbox" disabled checked> insert/update multiple objects</li>
                    <li><input type="checkbox" disabled checked> Delete by parent ID where ID is not in the set of IDs just inserted/deleted</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Ensure that each model is not referenced my more than one non-through <code>has</code> relationship</li>
                <li><input type="checkbox" disabled checked> Sort CREATE TABLE statements so that parents come first</li>
                <li><input type="checkbox" disabled checked> First round of Tests
                  <ul class="list-bullet">
                    <li><input type="checkbox" disabled checked> Create a test model with children
                      <ul class="list-bullet">
                        <li>Post</li>
                        <li>has one Poll</li>
                        <li>has many Comments</li>
                        <li>has many Reactions</li>
                      </ul>                    </li>
                    <li><input type="checkbox" disabled checked> populated queries with has_one</li>
                    <li><input type="checkbox" disabled checked> populated queries with has_many</li>
                    <li><input type="checkbox" disabled checked> insert with child object</li>
                    <li><input type="checkbox" disabled checked> update with child object</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> create and update functions should return the resulting objects</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Test passwordless login in Glance &#x2014; Feb 14th, 2024</li>
            <li><input type="checkbox" disabled checked> Test login with password in Glance &#x2014; Feb 13th, 2024</li>
            <li><input type="checkbox" disabled checked> Frontend: Handle validation errors from server &#x2014; Feb 11th, 2024</li>
            <li><input type="checkbox" disabled checked> On validation failure, return the sent data
              <ul class="list-bullet">
                <li>This helps when SvelteKit has forwarded the request straight to the server, since it allows Kit to return the form data back again to repopulate the form</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Command to bootstrap initial org and user &#x2014; Feb 11th, 2024
              <ul class="list-bullet">
                <li>This should have the ability to take admin user&apos;s email from either the command line or the environment</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Initial OAuth login support &#x2014; Feb 10th, 2024
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Update create<em class="italic">new</em>user to implement <code>UserCreator</code> trait</li>
                <li><input type="checkbox" disabled checked> Allow creating a user without an email address</li>
                <li><input type="checkbox" disabled checked> Look for client id/secret environment variables for each provider and create the ones that are found  &#x2014; Feb 3rd, 2024</li>
                <li><input type="checkbox" disabled checked> Hook up oauth code to endpoints  &#x2014; Feb 3rd, 2024</li>
                <li><input type="checkbox" disabled checked> Link to existing account when it&apos;s a new OAuth login but a known email  &#x2014; Feb 2nd, 2024</li>
                <li><input type="checkbox" disabled checked> Client-side code for OAuth login &#x2014; Feb 9th, 2024</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Login page &#x2014; Feb 10th, 2024</li>
            <li><input type="checkbox" disabled checked> Easier way to add new fields to base models than redefining the whole model &#x2014; Jan 29th, 2024</li>
            <li><input type="checkbox" disabled checked> Integrate into <a href="https://imfeld.dev/notes/projects_glance">Glance</a></li>
            <li><input type="checkbox" disabled checked> User Creation updates &#x2014; Jan 27th, 2024
              <ul class="list-bullet">
                <li>Add default role field to org, for users who are added without any set of roles</li>
                <li>Add roles field to email_invites</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Validation for form submissions &#x2014; Jan 22nd, 2024</li>
            <li><input type="checkbox" disabled checked> Use hosts list in post-login redirect processing</li>
            <li><input type="checkbox" disabled checked> JSON validation that checks multiple fields
              <ul class="list-bullet">
                <li>do this using axum-jsonschema</li>
                <li>Derive <code>schemars::JsonSchema</code> on every structure that might need to be validated</li>
                <li>Might want to add additional validation in the future with an extension to this</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> CORS configuration &#x2014; Jan 17th, 2024
              <ul class="list-bullet">
                <li>Host allow list for apps that want to lock to the app&apos;s host</li>
                <li>Use <code>CorsLayer::permissive()</code> on <code>/api</code> for apps that want to expose the API to browsers</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Initial SQL migration should only be written once &#x2014; Jan 17th, 2024</li>
            <li><input type="checkbox" disabled checked> New models should be added in new SQL migrations &#x2014; Jan 17th, 2024</li>
            <li><input type="checkbox" disabled checked> Column changes to models should generate new SQL migrations &#x2014; Jan 17th, 2024
              <ul class="list-bullet">
                <li><code>sql-migration-sim</code> crate is written, just need diff functionality</li>
                <li>Add index tracking to <code>sql-migration-sim</code></li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> New runs diff against previous run and only apply diffed changes &#x2014; Jan 16th, 2024
              <ul class="list-bullet">
                <li>When running the CLI, save the generated templates (post-formatter) to a state directory</li>
                <li>On each run, diff the generated templates against the previous state in that directory</li>
                <li>Apply that diff to the actual output file
                  <ul class="list-bullet">
                    <li>Need a patch style that matches against nearby lines instead of just using line numbers.</li>
                  </ul>                </li>
                <li>Point out conflicts as they occur, probably use git style merge conflict markers</li>
                <li><code>diffy</code> supports three-way merge so that&apos;s also worth looking at</li>
                <li>This system allows the user to make changes to the generated files but still update the config.</li>
                <li>Need a command to rewrite a particular file</li>
                <li>Need a command to just regenerate the state cache, for when changing formatter settings</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Ability to make signup open to the public or admin-only &#x2014; Jan 12th, 2024</li>
            <li><input type="checkbox" disabled checked> signup endpoint to create a new org that also creates default roles, adds a user, etc. &#x2014; Jan 12th, 2024</li>
            <li><input type="checkbox" disabled checked> ability to sign up via passwordless email auth if enabled &#x2014; Jan 12th, 2024</li>
            <li><input type="checkbox" disabled checked> Password forgot/reset endpoints &#x2014; Jan 12th, 2024</li>
            <li><input type="checkbox" disabled checked> Passwordless email auth &#x2014; Jan 11th, 2024</li>
            <li><input type="checkbox" disabled checked> Email template system &#x2014; Jan 11th, 2024</li>
            <li><input type="checkbox" disabled checked> List all permissions in a file with descriptions &#x2014; Jan 9th, 2024</li>
            <li><input type="checkbox" disabled checked> For models with global permissions, add permissions check in route layer before hitting database &#x2014; Jan 9th, 2024</li>
            <li><input type="checkbox" disabled checked> MVP Basic email sending, enough to do password management and such &#x2014; Jan 9th, 2024</li>
            <li><input type="checkbox" disabled checked> Integrate with email sending services &#x2014; Jan 9th, 2024</li>
            <li><input type="checkbox" disabled checked> Endpoints for retrieving and updating your own user &#x2014; Jan 7th, 2024</li>
            <li><input type="checkbox" disabled checked> Auto-add appropriate dependencies to Cargo.toml &#x2014; Jan 7th, 2024</li>
            <li><input type="checkbox" disabled checked> Tests for users without proper permissions for each action &#x2014; Jan 6th, 2024</li>
            <li><input type="checkbox" disabled checked> Generate tests for models &#x2014; Jan 6th, 2024</li>
            <li><input type="checkbox" disabled checked> Tests for login/logout &#x2014; Jan 5th, 2024</li>
            <li><input type="checkbox" disabled checked> Generate fixtures for tests &#x2014; Jan 5th, 2024</li>
            <li><input type="checkbox" disabled checked> Write functions needed to bootstrap test data &#x2014; Jan 4th, 2024
              <ul class="list-bullet">
                <li>These should basically be the version that the actual app will use, just deferring all the surrounding functionality that isn&apos;t needed to write basic tests</li>
                <li>Add org</li>
                <li>Add roles</li>
                <li>Add user to org with roles/permissions</li>
                <li>Add new org (using all the above)</li>
                <li>Add API key for user</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Password login/logout endpoints &#x2014; Dec 29th, 2023</li>
            <li><input type="checkbox" disabled checked> Get server to start &#x2014; Dec 27th, 2023</li>
            <li><input type="checkbox" disabled checked> Add permissions checks to endpoints &#x2014; Dec 27th, 2023</li>
            <li><input type="checkbox" disabled checked> Add app-specific AuthInfo object &#x2014; Dec 23rd, 2023</li>
            <li><input type="checkbox" disabled checked> Add AuthQueries implementation &#x2014; Dec 23rd, 2023</li>
            <li><input type="checkbox" disabled checked> Select queries need to return the current permission level on each object for project/object-level permissions &#x2014; Dec 22nd, 2023</li>
            <li><input type="checkbox" disabled checked> Add middleware layer to do permissions check &#x2014; Dec 22nd, 2023</li>
            <li><input type="checkbox" disabled checked> Add authz middleware to do generic predicate check &#x2014; Dec 22nd, 2023</li>
            <li><input type="checkbox" disabled checked> query to load user/roles/org for auth info &#x2014; Dec 21st, 2023</li>
            <li><input type="checkbox" disabled checked> Generate router for all model endpoints &#x2014; Dec 21st, 2023</li>
            <li><input type="checkbox" disabled checked> Create endpoints for the model &#x2014; Dec 20th, 2023</li>
            <li><input type="checkbox" disabled checked> Bearer token auth middleware &#x2014; Dec 20th, 2023</li>
            <li><input type="checkbox" disabled checked> Session cookie auth middleware &#x2014; Dec 20th, 2023</li>
            <li><input type="checkbox" disabled checked> Figure out querystring filter parameters &#x2014; Dec 18th, 2023</li>
            <li><input type="checkbox" disabled checked> Pagination &#x2014; Dec 18th, 2023</li>
            <li><input type="checkbox" disabled checked> Rename team to organization</li>
            <li><input type="checkbox" disabled checked> Create Rust structures for each model</li>
            <li><input type="checkbox" disabled checked> Bootstrap CLI utility</li>
            <li><input type="checkbox" disabled checked> Figure out main config format</li>
            <li><input type="checkbox" disabled checked> Figure out model definition</li>
            <li><input type="checkbox" disabled checked> Create team, user, role, permissions, etc. tables</li>
            <li><input type="checkbox" disabled checked> Create SQL migration</li>
            <li><input type="checkbox" disabled checked> Create SQL queries to read/write the model</li>
          </ul>        </li>
      </ul>    </li>
    <li><h3>Setup</h3>
      <ul class="list-bullet">
        <li>This isn&apos;t really full documentation., just notes on how I set things up.  More of this will be automated and rough edges smoothed out over time.</li>
        <li><h4>Initial Setup</h4>
          <ul class="list-bullet">
            <li>Create a git repository</li>
            <li>Create a Rust project in it</li>
            <li>Create a SvelteKit project, add <code>filigree-web</code> as a dependency</li>
            <li>Add the <code>filigree</code> directory with <code>config.toml</code> and other files</li>
            <li>Create a <code>rustfmt.toml</code></li>
            <li>Delete the default <code>src/main.rs</code></li>
            <li>Run <code>filigree</code> to write the source code</li>
            <li>Add this to the vite config
              <ul class="list-bullet">
                <li><pre><code><span class="sy-source sy-js">  <span class="sy-constant sy-other sy-object sy-key sy-js"><span class="sy-string sy-unquoted sy-label sy-js">server</span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span></span> <span class="sy-meta sy-group sy-braces sy-curly sy-js"><span class="sy-meta sy-brace sy-curly sy-begin sy-js">{</span>
    <span class="sy-constant sy-other sy-object sy-key sy-js"><span class="sy-string sy-unquoted sy-label sy-js">proxy</span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span></span> <span class="sy-meta sy-group sy-braces sy-curly sy-js"><span class="sy-meta sy-brace sy-curly sy-begin sy-js">{</span>
      <span class="sy-string sy-quoted sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>/api<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span> <span class="sy-meta sy-group sy-braces sy-curly sy-js"><span class="sy-meta sy-brace sy-curly sy-begin sy-js">{</span>
        <span class="sy-constant sy-other sy-object sy-key sy-js"><span class="sy-string sy-unquoted sy-label sy-js">target</span><span class="sy-punctuation sy-separator sy-key-value sy-js">:</span></span> <span class="sy-string sy-quoted sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>http://localhost:7823<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span><span class="sy-meta sy-delimiter sy-comma sy-js">,</span>
      <span class="sy-meta sy-brace sy-curly sy-end sy-js">}</span></span><span class="sy-meta sy-delimiter sy-comma sy-js">,</span>
    <span class="sy-meta sy-brace sy-curly sy-end sy-js">}</span></span><span class="sy-meta sy-delimiter sy-comma sy-js">,</span>
  <span class="sy-meta sy-brace sy-curly sy-end sy-js">}</span></span><span class="sy-meta sy-delimiter sy-comma sy-js">,</span>

</span></code></pre></li>
              </ul>            </li>
          </ul>        </li>
        <li><h4>Set up Database</h4>
          <ul class="list-bullet">
            <li>Create Database and Database User
              <ul class="list-bullet">
                <li><code>cat /dev/urandom | head | shasum</code> for an easy random password</li>
                <li><pre><code><span class="sy-source sy-sql"><span class="sy-comment sy-line sy-double-dash sy-sql"><span class="sy-punctuation sy-definition sy-comment sy-sql">--</span> Create the user and database
</span>create role USER login password <span class="sy-string sy-quoted sy-single sy-sql"><span class="sy-punctuation sy-definition sy-string sy-begin sy-sql">&apos;</span>PASSWORD<span class="sy-punctuation sy-definition sy-string sy-end sy-sql">&apos;</span></span>;
<span class="sy-meta sy-create sy-sql"><span class="sy-keyword sy-other sy-create sy-sql">create</span> <span class="sy-keyword sy-other sy-sql">database</span> </span><span class="sy-meta sy-toc-list sy-full-identifier sy-sql">THE_DB with owner </span><span class="sy-meta sy-toc-list sy-full-identifier sy-sql"><span class="sy-entity sy-name sy-function sy-sql">USER</span></span>;
</span></code></pre></li>
                <li>Set <code>DATABASE_URL</code> in your .env</li>
                <li><code>cd YOUR_API_DIRECTORY &amp;&amp; sqlx migrate run</code></li>
              </ul>            </li>
            <li>Bootstrap the Database
              <ul class="list-bullet">
                <li><pre><code><span class="sy-text sy-plain">cargo run --release -- db bootstrap \
  --email YOUR_EMAIL \
  --password &apos;PASSWORD&apos; \
  --name &apos;YOUR NAME&apos; \
  --org-name &apos;ORG NAME&apos;
</span></code></pre></li>
                <li>The only required option is <code>email</code>. Without a password you can log in via OAuth or through a passwordless email login.</li>
                <li>You can also supply a prehashed password by using the <code>--password-hash</code> argument instead, and hashes can be created by running <code>cargo run --release -- util hash-password YOURPASSWORD</code>.</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li>Authorization
      <ul class="list-bullet">
        <li>Concepts
          <ul class="list-bullet">
            <li>Organization</li>
            <li>User
              <ul class="list-bullet">
                <li>Users can potentially be in multiple organizations</li>
              </ul>            </li>
            <li>Role
              <ul class="list-bullet">
                <li>Basically a collection of permissions</li>
              </ul>            </li>
            <li>Group
              <ul class="list-bullet">
                <li>This is similar to Role</li>
                <li>Might not do this, but it can be useful to make this different when more advanced sharing options are created.</li>
              </ul>            </li>
            <li>Actor - all the things (users, roles, groups) that can have permissions on objects
              <ul class="list-bullet">
                <li>When looking at permissions we gather up all the actors that apply to the current user, and use that whole list for permissions checks</li>
              </ul>            </li>
          </ul>        </li>
        <li>The permissions table is then a list of the permissions each actor has.</li>
        <li>Permissions
          <ul class="list-bullet">
            <li>These should be definable per model once more than one is supported.</li>
            <li>Tables
              <ul class="list-bullet">
                <li>Global permissions
                  <ul class="list-bullet">
                    <li>A many-to-many mapping of actor to permission</li>
                  </ul>                </li>
                <li>Object permissions
                  <ul class="list-bullet">
                    <li>A mapping of actor to object and permission</li>
                    <li>For a project-based model, the projects are also just objects</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Models
              <ul class="list-bullet">
                <li>Role-based Access Control (RBAC)
                  <ul class="list-bullet">
                    <li>Each role has permissions on whether it can read/write/admin certain types of objects.</li>
                    <li>This is the first one we&apos;ll support</li>
                    <li>Might be useful to have two sets of roles, some which can have the global permissions and are only created by administrators, and others which are generic groups of people for managing sharing of projects and objects
                      <ul class="list-bullet">
                        <li>Need to think about how much this needs to be in the data model vs. just something in the UI. Probably just a single boolean flag is sufficient.</li>
                      </ul>                    </li>
                  </ul>                </li>
                <li>Resource-based access control
                  <ul class="list-bullet">
                    <li>Read/write/admin on individual objects</li>
                    <li>Each role/user has specific permissions for each object</li>
                    <li>More complex, need to make sure the relevant entries get added for each object when it is created</li>
                    <li>Best for a model where objects are not shared by default.</li>
                  </ul>                </li>
                <li id="657a981d-8503-427b-a6ab-319c7879b94c">Project-based Access Control
                  <ul class="list-bullet">
                    <li>This combines with the above concepts but is a middle level of granularity compared to the all-or-nothing of plain RBAC and the often-overly-granular resource-based system.</li>
                    <li>A project acts as a container for objects, and actors can have permissions on the project instead of the individual objects.</li>
                    <li>Will probably implement this after RBAC, just need additional functionality for project management and ability to</li>
                    <li>Both individuals and roles can be added to projects</li>
                  </ul>                </li>
                <li>Attribute-based Access Control (ABAC)
                  <ul class="list-bullet">
                    <li>Objects have attributes and actors</li>
                  </ul>                </li>
                <li>Public sharing
                  <ul class="list-bullet">
                    <li>Some sites are built around publicly sharing your stuff.</li>
                    <li>This is basically the same as the resource-based access control model, but with the ability to share with an &quot;anyone&quot; user, and then all the permissions checks check for that actor ID as well.</li>
                    <li>The main change here is that the organization ID checks need to be relaxed when looking for publicly-shared objects.</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Types of default permissions
              <ul class="list-bullet">
                <li>creator only until shared</li>
                <li>Creator gets write access, everyone else gets read access</li>
                <li>everyone can see, only creator can write</li>
                <li>Everyone in the project can see</li>
                <li>Everyone in the project can edit</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li>Model relationships
      <ul class="list-bullet">
        <li>Types
          <ul class="list-bullet">
            <li>parent-child one to many</li>
            <li>parent-child one to one</li>
            <li>Many to one
              <ul class="list-bullet">
                <li>Usually this isn&apos;t really a parent-child paradigm so much as related objects. This is actually similar to ActiveRecord <code>belongs_to</code> on the parent object, but without the meaning implied by the word &quot;belongs&quot;.</li>
                <li>e.g. Image uploads can all reference a single conversion configuration, but any image can be switched to a different configuration.</li>
                <li>The main thing that we want here is the ability to easily fetch the associated objects instead of just their IDs</li>
              </ul>            </li>
            <li>Many to many
              <ul class="list-bullet">
                <li>Almost always best implemented with a join table</li>
              </ul>            </li>
          </ul>        </li>
        <li>Configuration keywords
          <ul class="list-bullet">
            <li><code>has</code> - For one-to-one or one-to-many parent-child relationships.</li>
            <li><code>has</code> with <code>through</code> for many-to-many relationships
              <ul class="list-bullet">
                <li>In this case <code>through</code> is the name of the model that holds the joins. Making it a full-fledged model allows adding other information to the relationship, if applicable.</li>
                <li>Need a special mode for through models that will omit the normal id field since its primary key will be a composite of the IDs of the two other models.</li>
              </ul>            </li>
            <li><code>references</code> for many-to-one relationships
              <ul class="list-bullet">
                <li>This is actually accomplished by the existing <code>references</code> member on a model field definition.</li>
                <li>Added 2 extra fields that allow setting the option to automatically populate the field on list or get</li>
              </ul>            </li>
          </ul>        </li>
        <li>Where does the linking field actually go?
          <ul class="list-bullet">
            <li>When there&apos;s a linking model, it goes into that model&apos;s table, of course.</li>
            <li>Otherwise for a <code>has</code> relationship it goes into the child object. This is slightly inefficient in a true <code>has_one</code> case but those are rare in practice. In general those turn into either a complex JSON field inside the model itself, or it would be a has_many.</li>
          </ul>        </li>
      </ul>    </li>
    <li>Pricing Plan Support
      <ul class="list-bullet">
        <li>Add pricing tiers and permissions that go along with those tiers - this should mostly be data driven
          <ul class="list-bullet">
            <li>Need ability for numeric values as well (e.g. max 3 projects, 500MB storage, up to 60 minutes of something, etc.)</li>
          </ul>        </li>
        <li>Need ability to do grandfathering of pricing tiers and permissions
          <ul class="list-bullet">
            <li>Essentially this means that both price plans and permissions sets should be versioned, and separately.</li>
            <li>And there should be the ability to update existing plans and all previous versions</li>
          </ul>        </li>
      </ul>    </li>
    <li>Workflows
      <ul class="list-bullet">
        <li>Workflows are basically state machine definitions which can be instantiated with some data, linked to users/orgs/etc.</li>
        <li>Workflows can perform some set of predefined actions, which may include sending emails, notifying a user, spawning other workflows, etc.</li>
        <li>Workflows can define transitions to run after a certain amount of time</li>
        <li>Consider a scripting engine or something for checking state when transitioning (perhaps just reuse a bunch of code from Ergo for this)</li>
      </ul>    </li>
    <li><h2>Old Notes</h2>
      <ul class="list-bullet">
        <li>This should be made modular somehow so it&apos;s easy to maintain and add new stuff.
          <ul class="list-bullet">
            <li>Modules define dependencies</li>
          </ul>        </li>
        <li>Config
          <ul class="list-bullet">
            <li>formatters to use</li>
            <li>SQL dialect (postgres or sqlite)</li>
          </ul>        </li>
        <li>Model definition
          <ul class="list-bullet">
            <li>Write in TOML</li>
            <li>Each field has
              <ul class="list-bullet">
                <li>SQL type</li>
                <li>nullable (default not null)</li>
                <li>Rust type (can be inferred from the SQL type, but can be overridden)</li>
              </ul>            </li>
            <li>Define Indexes and primary key
              <ul class="list-bullet">
                <li>indexes can maybe just be defined in raw sql</li>
              </ul>            </li>
            <li>Can have child relationships, one -to-one and one-to-many</li>
            <li>Can have JSON fields, which deserialize to either JSON values or other Rust structs
              <ul class="list-bullet">
                <li>The rust structs can be defined in the config as strings or something like that</li>
              </ul>            </li>
            <li>List of other query functions to generate (find by field, etc.)</li>
            <li>Permissions
              <ul class="list-bullet">
                <li>Automatic checking on all operations</li>
                <li>Ability to define which permissions are needed for what</li>
              </ul>            </li>
            <li>some kind of hook on update? or is this better done by just changing the generated code myself</li>
          </ul>        </li>
        <li>Consider some kind of codegen for even easier crud endpoint generation
          <ul class="list-bullet">
            <li>Need ability to generate migrations that add/remove columns too
              <ul class="list-bullet">
                <li>this can definitely be manual to start though.</li>
              </ul>            </li>
          </ul>        </li>
        <li>Authentication
          <ul class="list-bullet">
            <li>Need a user/account mapping to support multiple auth methods per user.</li>
          </ul>        </li>
        <li id="660233f8-5485-446f-be9b-291572417c2a">Scrapped attempt to switch to SeaORM
          <ul class="list-bullet">
            <li>The experience starting to try this ended up with <a class="block-ref" href="https://imfeld.dev/notes/projects_filigree#6694db13-c3b4-4e96-977b-c1a50c0d314a"><input type="checkbox" disabled checked> Rework query template system &#x2014; Jul 16th, 2024</a></li>
            <li>Problems
              <ul class="list-bullet">
                <li><code>exec_with_returning</code> only supports returning a single model, this isn&apos;t an issue with raw sqlx</li>
                <li><code>find_with_related</code> only supports a single related model, not an issue with the current query building</li>
              </ul>            </li>
            <li>But the nice things were...
              <ul class="list-bullet">
                <li>Easy to add auth query with a wrapper</li>
                <li>Easy dynamic query construction</li>
                <li>ActiveModel is pretty nice</li>
                <li>Easier to decide at runtime which things to populate</li>
                <li>Templates were easier to deal with, no more gnarly templates with a bunch of ifs generating SQL or functions to call queries.</li>
                <li>Partial Models were nice</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_filigree</link><guid isPermaLink="false">47e9c7725d459be0c6b1fb2f5a9f5f5b01eb6527d5d67ab8634968827013190e</guid><category><![CDATA[Projects]]></category><pubDate>Sat, 09 Dec 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[PDF Extraction]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>A full pipeline that includes the below techniques and more: <a href="https://github.com/VikParuchuri/marker">VikParuchuri/marker: Convert PDF to markdown quickly with high accuracy</a></li>
    <li><a href="https://github.com/tesseract-ocr/tesseract">tesseract</a> is one of the leading OCR applications/libraries
      <ul class="list-bullet">
        <li>Usually want PSM mode 4 (single column of text) or 6 (single uniform block of text)</li>
        <li><a href="https://pyimagesearch.com/2021/11/15/tesseract-page-segmentation-modes-psms-explained-how-to-improve-your-ocr-accuracy/">Tesseract Page Segmentation Modes (PSMs) Explained: How to Improve Your OCR Accuracy - PyImageSearch</a></li>
      </ul>    </li>
    <li>OCR often works best with some preprocessing
      <ul class="list-bullet">
        <li><pre><code><span class="sy-source sy-python"><span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> get grayscale image
</span><span class="sy-meta sy-function sy-python"><span class="sy-storage sy-type sy-function sy-python"><span class="sy-keyword sy-declaration sy-function sy-python">def</span></span> <span class="sy-entity sy-name sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">get_grayscale</span></span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-punctuation sy-section sy-parameters sy-begin sy-python">(</span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-variable sy-parameter sy-python">image</span><span class="sy-punctuation sy-section sy-parameters sy-end sy-python">)</span></span><span class="sy-meta sy-function sy-python"><span class="sy-punctuation sy-section sy-function sy-begin sy-python">:</span></span>
    <span class="sy-keyword sy-control sy-flow sy-return sy-python">return</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">cv2</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">cvtColor</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">image</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">cv2</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-variable sy-other sy-constant sy-python">COLOR_BGR2GRAY</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-meta sy-function sy-python"><span class="sy-storage sy-type sy-function sy-python"><span class="sy-keyword sy-declaration sy-function sy-python">def</span></span> <span class="sy-entity sy-name sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">thresholding</span></span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-punctuation sy-section sy-parameters sy-begin sy-python">(</span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-variable sy-parameter sy-python">image</span><span class="sy-punctuation sy-section sy-parameters sy-end sy-python">)</span></span><span class="sy-meta sy-function sy-python"><span class="sy-punctuation sy-section sy-function sy-begin sy-python">:</span></span>
    <span class="sy-keyword sy-control sy-flow sy-return sy-python">return</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">cv2</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">threshold</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">image</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">0</span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">255</span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">cv2</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-variable sy-other sy-constant sy-python">THRESH_BINARY</span></span> <span class="sy-keyword sy-operator sy-arithmetic sy-python">+</span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">cv2</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-variable sy-other sy-constant sy-python">THRESH_OTSU</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">1</span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span>
</span></code></pre></li>
        <li><a href="https://pyimagesearch.com/2017/02/20/text-skew-correction-opencv-python/">Deskew</a></li>
      </ul>    </li>
    <li>Table extraction
      <ul class="list-bullet">
        <li><a href="https://pyimagesearch.com/2022/02/28/multi-column-table-ocr/">Multi-Column Table OCR - PyImageSearch</a></li>
        <li><a href="https://github.com/microsoft/table-transformer">Microsoft Table Transformer (TATR) </a></li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/pdf_extraction</link><guid isPermaLink="false">5533d0d59c1f8cee941e241c229b2a3b57889abaf0d8a1ce180720df67efcd7f</guid><pubDate>Sat, 02 Dec 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[SBBP]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>&quot;Should&apos;ve been a Blog Post&quot; is a project that downloads a video, generates a transcript using Whisper, and places that transcript along side screenshots from the video so you can follow along with slides.</li>
    <li>The name is a bit of snark, but I have two aims for this project:
      <ul class="list-bullet">
        <li>Some videos really should have just been blog posts, and I feel like I&apos;ve just wasted time.</li>
        <li>Others are actually valuable, but as a parent with young kids my time is limited, and so this allows faster consumption or in settings where watching a video isn&apos;t feasible.</li>
      </ul>    </li>
    <li><a href="https://github.com/dimfeld/sbbp">Github Link</a></li>
    <li><h2>Task List</h2>
      <ul class="list-bullet">
        <li><h3>Up Next</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Host this somewhere so I can access it over the web and add to Readwise
              <ul class="list-bullet">
                <li>Maybe just host locally and expose subdomain</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Soon</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Get thumbnail image from video metadata</li>
            <li><input type="checkbox" disabled> Summarize comments (can yt-dlp grab them?)</li>
            <li><input type="checkbox" disabled> For videos with chapters, summarize each chapter</li>
            <li><input type="checkbox" disabled> Table of contents with video chapters</li>
            <li><input type="checkbox" disabled> Improve reader layout</li>
          </ul>        </li>
        <li><h3>Later</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> For longer videos without chapters, use LLM to try to generate chunks</li>
            <li><input type="checkbox" disabled> Click on/near a paragraph to play the video at that point.</li>
            <li><input type="checkbox" disabled> Better image similarity algorithm</li>
            <li><input type="checkbox" disabled> Some way for multiple users to link to the same video without downloading it each time (if opened to public)
              <ul class="list-bullet">
                <li>Probably put the video metadata itself in a separate table and have the user&apos;s video model link to that.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Video Host whitelist? (if opened to public)
              <ul class="list-bullet">
                <li>Might be a hassle to maintain but also potentially prevents issues with shady content</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Move processing into Rust
              <ul class="list-bullet">
                <li>Steps
                  <ul class="list-bullet">
                    <li>Download video</li>
                    <li>Images
                      <ul class="list-bullet">
                        <li>Extract Images</li>
                        <li>Similar Image Algorithm</li>
                      </ul>                    </li>
                    <li>Transcript
                      <ul class="list-bullet">
                        <li>Extract Audio</li>
                        <li>Transcription</li>
                        <li>Summary of Transcription</li>
                      </ul>                    </li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Simple auth</li>
            <li><input type="checkbox" disabled checked> Allow storing files in cloud storage</li>
            <li><input type="checkbox" disabled checked> Use Deepgram for transcription</li>
            <li><input type="checkbox" disabled checked> Hosting: Store non-temporary downloaded files in Backblaze or Cloudflare object storage</li>
            <li><input type="checkbox" disabled checked> Integrate with <a href="https://imfeld.dev/notes/projects_filigree">Filigree</a></li>
            <li><input type="checkbox" disabled checked> Allow triggering video download and processing from inside the app &#x2014; Nov 20th, 2023</li>
            <li><input type="checkbox" disabled checked> Queue up multiple videos for processing &#x2014; Nov 20th, 2023</li>
            <li><input type="checkbox" disabled checked> Add &quot;read&quot; state and progress tracking &#x2014; Nov 20th, 2023</li>
            <li><input type="checkbox" disabled checked> Speed up SSIM process &#x2014; Nov 18th, 2023</li>
            <li><input type="checkbox" disabled checked> Generate a summary of the video &#x2014; Nov 18th, 2023</li>
            <li><input type="checkbox" disabled checked> Use structural similarity (SSIM) metric to remove duplicate screenshots &#x2014; Nov 17th, 2023</li>
            <li><input type="checkbox" disabled checked> Screenshot extraction</li>
            <li><input type="checkbox" disabled checked> Whisper transcript generation</li>
            <li><input type="checkbox" disabled checked> Align transcript to images and</li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_sbbp</link><guid isPermaLink="false">80e452c7c8089a2b2f5a8ba037d3520ad0892a770a53f0875a8645f9dcd095d0</guid><category><![CDATA[Projects]]></category><pubDate>Sun, 19 Nov 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[Preview a CSV In-Browser]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>This uses <a href="https://www.papaparse.com/">Papa Parse</a> to parse the first few rows of a CSV to preview it before uploading.</li>
    <li><pre><code><span class="sy-text sy-html sy-svelte"><span class="sy-meta sy-tag sy-script sy-begin sy-html"><span class="sy-punctuation sy-definition sy-tag sy-begin sy-html">&lt;</span><span class="sy-entity sy-name sy-tag sy-script sy-html">script</span></span><span class="sy-meta sy-tag sy-script sy-begin sy-html"> <span class="sy-meta sy-attribute-with-value sy-html"><span class="sy-entity sy-other sy-attribute-name sy-html">lang</span></span></span><span class="sy-meta sy-tag sy-script sy-begin sy-html"><span class="sy-meta sy-attribute-with-value sy-html"><span class="sy-punctuation sy-separator sy-key-value sy-html">=</span></span></span><span class="sy-meta sy-tag sy-script sy-begin sy-html"><span class="sy-meta sy-attribute-with-value sy-html"></span></span><span class="sy-meta sy-tag sy-script sy-begin sy-html"><span class="sy-meta sy-attribute-with-value sy-html"><span class="sy-string sy-quoted sy-double sy-html"><span class="sy-punctuation sy-definition sy-string sy-begin sy-html">&quot;</span>ts<span class="sy-punctuation sy-definition sy-string sy-end sy-html">&quot;</span></span></span><span class="sy-punctuation sy-definition sy-tag sy-end sy-html">&gt;</span></span><span class="sy-source sy-ts sy-embedded sy-html"><span class="sy-source sy-ts">
  <span class="sy-meta sy-import sy-ts"><span class="sy-keyword sy-control sy-import sy-ts">import</span> <span class="sy-variable sy-other sy-readwrite sy-alias sy-ts">Papa</span> <span class="sy-keyword sy-control sy-from sy-ts">from</span> <span class="sy-string sy-quoted sy-single sy-ts"><span class="sy-punctuation sy-definition sy-string sy-begin sy-ts">&apos;</span>papaparse<span class="sy-punctuation sy-definition sy-string sy-end sy-ts">&apos;</span></span></span><span class="sy-punctuation sy-terminator sy-statement sy-ts">;</span>
  
  <span class="sy-meta sy-var sy-expr sy-ts"><span class="sy-storage sy-type sy-ts">let</span> <span class="sy-meta sy-var-single-variable sy-expr sy-ts"><span class="sy-meta sy-definition sy-variable sy-ts"><span class="sy-variable sy-other sy-readwrite sy-ts">columns</span></span><span class="sy-meta sy-type sy-annotation sy-ts"><span class="sy-keyword sy-operator sy-type sy-annotation sy-ts">:</span> <span class="sy-support sy-type sy-primitive sy-ts">string</span><span class="sy-keyword sy-operator sy-type sy-ts">|</span><span class="sy-support sy-type sy-builtin sy-ts">undefined</span></span></span></span><span class="sy-punctuation sy-terminator sy-statement sy-ts">;</span>
  <span class="sy-meta sy-var sy-expr sy-ts"><span class="sy-storage sy-type sy-ts">let</span> <span class="sy-meta sy-var-single-variable sy-expr sy-ts"><span class="sy-meta sy-definition sy-variable sy-ts"><span class="sy-variable sy-other sy-readwrite sy-ts">previewRows</span></span><span class="sy-meta sy-type sy-annotation sy-ts"><span class="sy-keyword sy-operator sy-type sy-annotation sy-ts">:</span> <span class="sy-entity sy-name sy-type sy-ts">Array</span><span class="sy-meta sy-type sy-parameters sy-ts"><span class="sy-punctuation sy-definition sy-typeparameters sy-begin sy-ts">&lt;</span></span><span class="sy-meta sy-type sy-parameters sy-ts"><span class="sy-entity sy-name sy-type sy-ts">Record</span><span class="sy-meta sy-type sy-parameters sy-ts"><span class="sy-punctuation sy-definition sy-typeparameters sy-begin sy-ts">&lt;</span></span><span class="sy-meta sy-type sy-parameters sy-ts"><span class="sy-support sy-type sy-primitive sy-ts">string</span><span class="sy-punctuation sy-separator sy-comma sy-ts">,</span> <span class="sy-support sy-type sy-primitive sy-ts">string</span></span><span class="sy-meta sy-type sy-parameters sy-ts"><span class="sy-punctuation sy-definition sy-typeparameters sy-end sy-ts">&gt;</span></span></span><span class="sy-meta sy-type sy-parameters sy-ts"><span class="sy-punctuation sy-definition sy-typeparameters sy-end sy-ts">&gt;</span></span> </span></span><span class="sy-keyword sy-operator sy-assignment sy-ts">=</span><span class="sy-meta sy-array sy-literal sy-ts"> <span class="sy-meta sy-brace sy-square sy-ts">[</span><span class="sy-meta sy-brace sy-square sy-ts">]</span></span></span><span class="sy-punctuation sy-terminator sy-statement sy-ts">;</span>
  
  <span class="sy-meta sy-function sy-ts"><span class="sy-storage sy-modifier sy-async sy-ts">async</span> <span class="sy-storage sy-type sy-function sy-ts">function</span> <span class="sy-meta sy-definition sy-function sy-ts"><span class="sy-entity sy-name sy-function sy-ts">openFile</span></span><span class="sy-meta sy-parameters sy-ts"><span class="sy-punctuation sy-definition sy-parameters sy-begin sy-ts">(</span><span class="sy-variable sy-parameter sy-ts">file</span><span class="sy-meta sy-type sy-annotation sy-ts"><span class="sy-keyword sy-operator sy-type sy-annotation sy-ts">:</span> <span class="sy-entity sy-name sy-type sy-ts">File</span><span class="sy-keyword sy-operator sy-type sy-ts">|</span><span class="sy-support sy-type sy-builtin sy-ts">undefined</span></span><span class="sy-punctuation sy-definition sy-parameters sy-end sy-ts">)</span></span> <span class="sy-meta sy-block sy-ts"><span class="sy-punctuation sy-definition sy-block sy-ts">{</span>
    <span class="sy-keyword sy-control sy-conditional sy-ts">if</span><span class="sy-meta sy-brace sy-round sy-ts">(</span><span class="sy-keyword sy-operator sy-logical sy-ts">!</span><span class="sy-variable sy-other sy-readwrite sy-ts">file</span><span class="sy-meta sy-brace sy-round sy-ts">)</span> <span class="sy-meta sy-block sy-ts"><span class="sy-punctuation sy-definition sy-block sy-ts">{</span>
      <span class="sy-keyword sy-control sy-flow sy-ts">return</span><span class="sy-punctuation sy-terminator sy-statement sy-ts">;</span>
    <span class="sy-punctuation sy-definition sy-block sy-ts">}</span></span>
    
    <span class="sy-meta sy-function-call sy-ts"><span class="sy-variable sy-other sy-object sy-ts">Papa</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-support sy-function sy-ts">parse</span></span><span class="sy-meta sy-brace sy-round sy-ts">(</span><span class="sy-variable sy-other sy-readwrite sy-ts">file</span><span class="sy-punctuation sy-separator sy-comma sy-ts">,</span> <span class="sy-meta sy-objectliteral sy-ts"><span class="sy-punctuation sy-definition sy-block sy-ts">{</span>
      <span class="sy-meta sy-object sy-member sy-ts"><span class="sy-meta sy-object-literal sy-key sy-ts">header</span></span><span class="sy-meta sy-object sy-member sy-ts"><span class="sy-meta sy-object-literal sy-key sy-ts"><span class="sy-punctuation sy-separator sy-key-value sy-ts">:</span></span> <span class="sy-constant sy-language sy-boolean sy-true sy-ts">true</span></span><span class="sy-punctuation sy-separator sy-comma sy-ts">,</span>
      <span class="sy-meta sy-object sy-member sy-ts"><span class="sy-meta sy-object-literal sy-key sy-ts">preview</span></span><span class="sy-meta sy-object sy-member sy-ts"><span class="sy-meta sy-object-literal sy-key sy-ts"><span class="sy-punctuation sy-separator sy-key-value sy-ts">:</span></span> <span class="sy-constant sy-numeric sy-decimal sy-ts">3</span></span><span class="sy-punctuation sy-separator sy-comma sy-ts">,</span> <span class="sy-comment sy-line sy-double-slash sy-ts"><span class="sy-punctuation sy-definition sy-comment sy-ts">//</span></span><span class="sy-comment sy-line sy-double-slash sy-ts"> To read first 3 data rows</span>
      <span class="sy-meta sy-object sy-member sy-ts"><span class="sy-meta sy-object-literal sy-key sy-ts">skipEmptyLines</span></span><span class="sy-meta sy-object sy-member sy-ts"><span class="sy-meta sy-object-literal sy-key sy-ts"><span class="sy-punctuation sy-separator sy-key-value sy-ts">:</span></span> <span class="sy-string sy-quoted sy-single sy-ts"><span class="sy-punctuation sy-definition sy-string sy-begin sy-ts">&apos;</span>greedy<span class="sy-punctuation sy-definition sy-string sy-end sy-ts">&apos;</span></span></span><span class="sy-punctuation sy-separator sy-comma sy-ts">,</span> <span class="sy-comment sy-line sy-double-slash sy-ts"><span class="sy-punctuation sy-definition sy-comment sy-ts">//</span></span><span class="sy-comment sy-line sy-double-slash sy-ts"> Well-formed CSV files don&apos;t need this</span>
<span class="sy-meta sy-method sy-declaration sy-ts">      <span class="sy-meta sy-definition sy-method sy-ts"><span class="sy-entity sy-name sy-function sy-ts">complete</span></span><span class="sy-meta sy-parameters sy-ts"><span class="sy-punctuation sy-definition sy-parameters sy-begin sy-ts">(</span><span class="sy-variable sy-parameter sy-ts">parsed</span><span class="sy-punctuation sy-definition sy-parameters sy-end sy-ts">)</span></span> <span class="sy-meta sy-block sy-ts"><span class="sy-punctuation sy-definition sy-block sy-ts">{</span>
        <span class="sy-keyword sy-control sy-conditional sy-ts">if</span><span class="sy-meta sy-brace sy-round sy-ts">(</span><span class="sy-variable sy-other sy-object sy-ts">parsed</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-variable sy-other sy-property sy-ts">errors</span><span class="sy-punctuation sy-accessor sy-optional sy-ts">?.</span><span class="sy-meta sy-array sy-literal sy-ts"><span class="sy-meta sy-brace sy-square sy-ts">[</span><span class="sy-constant sy-numeric sy-decimal sy-ts">0</span><span class="sy-meta sy-brace sy-square sy-ts">]</span></span><span class="sy-meta sy-brace sy-round sy-ts">)</span> <span class="sy-meta sy-block sy-ts"><span class="sy-punctuation sy-definition sy-block sy-ts">{</span>
          <span class="sy-variable sy-other sy-readwrite sy-ts">error</span> <span class="sy-keyword sy-operator sy-assignment sy-ts">=</span> <span class="sy-variable sy-other sy-object sy-ts">parsed</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-variable sy-other sy-property sy-ts">errors</span><span class="sy-meta sy-array sy-literal sy-ts"><span class="sy-meta sy-brace sy-square sy-ts">[</span><span class="sy-constant sy-numeric sy-decimal sy-ts">0</span><span class="sy-meta sy-brace sy-square sy-ts">]</span></span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-variable sy-other sy-property sy-ts">message</span><span class="sy-punctuation sy-terminator sy-statement sy-ts">;</span>
        <span class="sy-punctuation sy-definition sy-block sy-ts">}</span></span> <span class="sy-keyword sy-control sy-conditional sy-ts">else</span> <span class="sy-keyword sy-control sy-conditional sy-ts">if</span><span class="sy-meta sy-brace sy-round sy-ts">(</span><span class="sy-variable sy-other sy-object sy-ts">parsed</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-variable sy-other sy-object sy-property sy-ts">meta</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-variable sy-other sy-property sy-ts">fields</span><span class="sy-meta sy-brace sy-round sy-ts">)</span> <span class="sy-meta sy-block sy-ts"><span class="sy-punctuation sy-definition sy-block sy-ts">{</span>
          <span class="sy-variable sy-other sy-readwrite sy-ts">columns</span> <span class="sy-keyword sy-operator sy-assignment sy-ts">=</span> <span class="sy-variable sy-other sy-object sy-ts">parsed</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-variable sy-other sy-object sy-property sy-ts">meta</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-variable sy-other sy-property sy-ts">fields</span><span class="sy-punctuation sy-terminator sy-statement sy-ts">;</span>
          <span class="sy-variable sy-other sy-readwrite sy-ts">previewRows</span> <span class="sy-keyword sy-operator sy-assignment sy-ts">=</span> <span class="sy-variable sy-other sy-object sy-ts">parsed</span><span class="sy-punctuation sy-accessor sy-ts">.</span><span class="sy-support sy-variable sy-property sy-dom sy-ts">data</span><span class="sy-punctuation sy-terminator sy-statement sy-ts">;</span>
        <span class="sy-punctuation sy-definition sy-block sy-ts">}</span></span>
      <span class="sy-punctuation sy-definition sy-block sy-ts">}</span></span></span>
    <span class="sy-punctuation sy-definition sy-block sy-ts">}</span></span><span class="sy-meta sy-brace sy-round sy-ts">)</span>
  <span class="sy-punctuation sy-definition sy-block sy-ts">}</span></span></span>
</span></span><span class="sy-meta sy-tag sy-script sy-end sy-html"><span class="sy-punctuation sy-definition sy-tag sy-begin sy-html">&lt;/</span><span class="sy-entity sy-name sy-tag sy-script sy-html">script</span><span class="sy-punctuation sy-definition sy-tag sy-end sy-html">&gt;</span></span>

<span class="sy-meta sy-tag sy-inline sy-form sy-html"><span class="sy-punctuation sy-definition sy-tag sy-begin sy-html">&lt;</span><span class="sy-entity sy-name sy-tag sy-inline sy-form sy-html">input</span> 
  <span class="sy-meta sy-attribute-with-value sy-html"><span class="sy-entity sy-other sy-attribute-name sy-html">type</span><span class="sy-punctuation sy-separator sy-key-value sy-html">=</span><span class="sy-string sy-quoted sy-double sy-html"><span class="sy-punctuation sy-definition sy-string sy-begin sy-html">&quot;</span>file<span class="sy-punctuation sy-definition sy-string sy-end sy-html">&quot;</span></span></span>
  <span class="sy-meta sy-attribute-with-value sy-html"><span class="sy-entity sy-other sy-attribute-name sy-html">name</span><span class="sy-punctuation sy-separator sy-key-value sy-html">=</span><span class="sy-string sy-quoted sy-double sy-html"><span class="sy-punctuation sy-definition sy-string sy-begin sy-html">&quot;</span>csvfile<span class="sy-punctuation sy-definition sy-string sy-end sy-html">&quot;</span></span></span>
  <span class="sy-meta sy-attribute-with-value sy-html"><span class="sy-entity sy-other sy-attribute-name sy-html">accept</span><span class="sy-punctuation sy-separator sy-key-value sy-html">=</span><span class="sy-string sy-quoted sy-double sy-html"><span class="sy-punctuation sy-definition sy-string sy-begin sy-html">&quot;</span>.csv,text/csv<span class="sy-punctuation sy-definition sy-string sy-end sy-html">&quot;</span></span></span>
  <span class="sy-entity sy-other sy-attribute-name sy-svelte"><span class="sy-support sy-function sy-svelte">on</span><span class="sy-punctuation sy-separator sy-svelte">:</span><span class="sy-string sy-unquoted sy-svelte">change</span></span><span class="sy-punctuation sy-separator sy-key-value sy-html">=</span><span class="sy-meta sy-embedded sy-block sy-svelte"><span class="sy-punctuation sy-section sy-embedded sy-begin sy-svelte">{</span></span><span class="sy-meta sy-embedded sy-block sy-svelte"><span class="sy-source sy-js sy-svelte"><span class="sy-meta sy-function sy-js"></span><span class="sy-meta sy-function sy-declaration sy-js"><span class="sy-punctuation sy-section sy-group sy-begin sy-js">(</span><span class="sy-meta sy-binding sy-name sy-js"><span class="sy-variable sy-parameter sy-function sy-js">e</span></span><span class="sy-punctuation sy-section sy-group sy-end sy-js">)</span> <span class="sy-storage sy-type sy-function sy-arrow sy-js">=&gt;</span></span><span class="sy-meta sy-function sy-js"> <span class="sy-meta sy-block sy-js"><span class="sy-meta sy-function-call sy-js"><span class="sy-variable sy-function sy-js">openFile</span><span class="sy-meta sy-group sy-js"><span class="sy-punctuation sy-section sy-group sy-begin sy-js">(</span><span class="sy-variable sy-other sy-readwrite sy-js">e</span><span class="sy-punctuation sy-accessor sy-js">.</span><span class="sy-meta sy-property sy-object sy-js">currentTarget</span><span class="sy-punctuation sy-accessor sy-js">.</span><span class="sy-meta sy-property sy-object sy-js">files</span><span class="sy-keyword sy-operator sy-ternary sy-js">?</span><span class="sy-punctuation sy-accessor sy-js">.</span><span class="sy-meta sy-brackets sy-js"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-js">[</span><span class="sy-constant sy-numeric sy-integer sy-decimal sy-js">0</span><span class="sy-punctuation sy-section sy-brackets sy-end sy-js">]</span></span><span class="sy-punctuation sy-section sy-group sy-end sy-js">)</span></span></span></span></span></span><span class="sy-punctuation sy-section sy-embedded sy-end sy-svelte">}</span></span> 
<span class="sy-punctuation sy-definition sy-tag sy-end sy-html">/&gt;</span></span>
</span></code></pre></li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/preview_a_csv_in_browser</link><guid isPermaLink="false">55d7f4a78e5ae053e28e7e5bb09dbecef49bad31be6c9289dbff640354f0e5d5</guid><pubDate>Fri, 10 Nov 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[PromptBox]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>This utility allows maintaining libraries of LLM prompt templates which can be filled in and submitted from the command line.</li>
    <li><a href="https://github.com/dimfeld/promptbox">Github</a>, <a href="https://promptbox.imfeld.dev">Website</a></li>
    <li>A sample prompt. Each of the options below becomes a CLI flag which can fill in the template.
      <ul class="list-bullet">
        <li><pre><code><span class="sy-source sy-toml"><span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">description</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>Summarize some files<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span>

<span class="sy-comment sy-line sy-number-sign sy-toml"><span class="sy-punctuation sy-definition sy-comment sy-toml">#</span> This can also be template_path to read from another file.</span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">template</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-triple sy-literal sy-block sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&apos;&apos;&apos;</span>
Create a {{style}} summary of the below files
which are on the topic of {{topic}}. The summary should be about {{ len }} sentences long.

{% for f in file -%}
File {{ f.filename }}:
{{ f.contents }}


{%- endfor %}
<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&apos;&apos;&apos;</span></span>

<span class="sy-punctuation sy-definition sy-table sy-begin sy-toml">[</span><span class="sy-meta sy-tag sy-table sy-toml"><span class="sy-entity sy-name sy-table sy-toml">model</span></span><span class="sy-punctuation sy-definition sy-table sy-end sy-toml">]</span>
<span class="sy-comment sy-line sy-number-sign sy-toml"><span class="sy-punctuation sy-definition sy-comment sy-toml">#</span> These model options can also be defined in a config file to apply to the whole directory of templates.</span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">model</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>gpt-3.5-turbo<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">temperature</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-constant sy-numeric sy-float sy-toml">0.7</span>
<span class="sy-comment sy-line sy-number-sign sy-toml"><span class="sy-punctuation sy-definition sy-comment sy-toml">#</span> Also supports top_p, frequency_penalty, presence_penalty, stop, and max_tokens</span>

<span class="sy-punctuation sy-definition sy-table sy-begin sy-toml">[</span><span class="sy-meta sy-tag sy-table sy-toml"><span class="sy-entity sy-name sy-table sy-toml">options</span></span><span class="sy-punctuation sy-definition sy-table sy-end sy-toml">]</span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">len</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-punctuation sy-definition sy-inline-table sy-begin sy-toml">{</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">type</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>int<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span><span class="sy-punctuation sy-separator sy-inline-table sy-toml">,</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">description</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>The length of the summary<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span><span class="sy-punctuation sy-separator sy-inline-table sy-toml">,</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">default</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-constant sy-numeric sy-integer sy-toml">4</span> <span class="sy-punctuation sy-definition sy-inline-table sy-end sy-toml">}</span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">topic</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-punctuation sy-definition sy-inline-table sy-begin sy-toml">{</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">type</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>string<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span><span class="sy-punctuation sy-separator sy-inline-table sy-toml">,</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">description</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>The topic of the summary<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span> <span class="sy-punctuation sy-definition sy-inline-table sy-end sy-toml">}</span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">style</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-punctuation sy-definition sy-inline-table sy-begin sy-toml">{</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">type</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>string<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span><span class="sy-punctuation sy-separator sy-inline-table sy-toml">,</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">default</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>concise<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span> <span class="sy-punctuation sy-definition sy-inline-table sy-end sy-toml">}</span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">file</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-punctuation sy-definition sy-inline-table sy-begin sy-toml">{</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">type</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>file<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span><span class="sy-punctuation sy-separator sy-inline-table sy-toml">,</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">array</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-constant sy-language sy-toml">true</span><span class="sy-punctuation sy-separator sy-inline-table sy-toml">,</span> <span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">description</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>The files to summarize<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span> <span class="sy-punctuation sy-definition sy-inline-table sy-end sy-toml">}</span>
</span></code></pre></li>
      </ul>    </li>
    <li><h2>Task List</h2>
      <ul class="list-bullet">
        <li><h3>Up Next</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Switch request layer to use <a href="https://imfeld.dev/notes/projects_chronicle">Chronicle</a></li>
          </ul>        </li>
        <li><h3>Soon</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Verbose mode should print token stats at end</li>
            <li><input type="checkbox" disabled> List command
              <ul class="list-bullet">
                <li>List all templates in a directory</li>
                <li>should also take a filter</li>
                <li>Short mode for completion</li>
                <li>by default print template name and description in a table</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Show command to output the information from a template</li>
            <li><input type="checkbox" disabled> &quot;run&quot; command detection is fragile</li>
            <li><input type="checkbox" disabled> Add tools definitions to templates
              <ul class="list-bullet">
                <li>This lets us run a script and get a JSON output</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Later/Maybe</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Save all invocations in a database? (will do as part of Chronicle switch)</li>
            <li><input type="checkbox" disabled> Allow templates to reference partials in same directory</li>
            <li><input type="checkbox" disabled> Allow templates to reference partials in parent template directories</li>
            <li><input type="checkbox" disabled> Define ChatGPT functions in the prompt? Probably skip this, more appropriate for some other project</li>
            <li><input type="checkbox" disabled> bash/zsh Autocomplete template names</li>
            <li><input type="checkbox" disabled> Can we autocomplete options as well once the template name is present?</li>
            <li><input type="checkbox" disabled> Recall previous invocations</li>
            <li><input type="checkbox" disabled> Option to trim context in the middle with a <code>&lt;truncated&gt;</code> message or something like that</li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Pass images to Ollama &#x2014; v0.3.0 Dec 13th, 2023</li>
            <li><input type="checkbox" disabled checked> Support for GPT4 Vision &#x2014; v0.3.0 Dec 13th, 2023
              <ul class="list-bullet">
                <li>Can <a href="https://platform.openai.com/docs/guides/vision/uploading-base-64-encoded-images">pass images in base64</a></li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Support <a href="https://openrouter.ai/">OpenRouter</a> &#x2014; v0.2.0 Dec 8th, 2023
              <ul class="list-bullet">
                <li>OpenRouter offers an OpenAI compatible API which is probably the easiest way to add this.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Set prompt format, context length, etc. per model - v0.2.0 Dec 8th, 2023
              <ul class="list-bullet">
                <li>Done specifically for Together right now, can expand this to generic host at some point</li>
                <li>Support standard formats and allow custom formats too</li>
                <li>Needed for some providers that don&apos;t apply the template for you, or who don&apos;t provide accurate info about context length and other things.</li>
                <li>This can be defined in the model definition once it can be an object (see <a class="block-ref" href="https://imfeld.dev/notes/projects_promptbox#656e51d7-5951-46f1-9945-f52622163231"><input type="checkbox" disabled checked> Support multiple hosts - v0.2.0 Dec 8th, 2023</a>).</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Support together.xyz model host - v0.2.0 Dec 8th, 2023
              <ul class="list-bullet">
                <li>Fetch model info from <code>https://api.together.xyz/models/info</code></li>
                <li>Short term cache on the model info</li>
                <li>Get the config from the model info to determine how to format the prompt, stop tokens, etc.
                  <ul class="list-bullet">
                    <li>Some of the configs here actually don&apos;t include things like system prompt...
                      <ul class="list-bullet">
                        <li>Maybe just build in templates to the tool and lets them be specified in the config somehow</li>
                      </ul>                    </li>
                    <li>Looks like context length is missing from some models as well</li>
                  </ul>                </li>
                <li>max_tokens seems have a very small default, need to set this higher to be useful</li>
              </ul>            </li>
            <li id="656e51d7-5951-46f1-9945-f52622163231"><input type="checkbox" disabled checked> Support multiple hosts - v0.2.0 Dec 8th, 2023
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> Allow defining hosts beyond the built-in hosts
                  <ul class="list-bullet">
                    <li>API url</li>
                    <li>Request Format (currently just OpenAI and Ollama format but probably more in the future)</li>
                    <li>Environment variable that holds the API key</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Ability to configure the default host for non-GPT-3.5/4 models (whereas now Ollama is the default)</li>
                <li><input type="checkbox" disabled checked> Need a way to specify in the model name which host to use
                  <ul class="list-bullet">
                    <li>Actually the way to do this is to allow the model name to be either a string or a <code>{ host: Option&lt;String&gt;, model: String }</code> structure.</li>
                  </ul>                </li>
                <li><input type="checkbox" disabled checked> Tests
                  <ul class="list-bullet">
                    <li><input type="checkbox" disabled checked> config file overrides specific fields of bulit-in hosts
                      <ul class="list-bullet">
                        <li>e.g. <code>host.openai.api_key = &quot;DIFFERENT_VAR_NAME&quot;</code></li>
                      </ul>                    </li>
                    <li><input type="checkbox" disabled checked> Adding new hosts from the config file</li>
                    <li><input type="checkbox" disabled checked> Use default provider when none is specified</li>
                    <li><input type="checkbox" disabled checked> Set <code>default_host</code> to something else</li>
                    <li><input type="checkbox" disabled checked> Complain when default_host refers to a nonexistent host</li>
                    <li><input type="checkbox" disabled checked> Alias handling
                      <ul class="list-bullet">
                        <li>Alias can be a full model spec</li>
                        <li>Model can be a full model spec and also reference an alias which is a full model spec. Should fetch the alias from <code>model</code> and merge the remaining fields together in this case</li>
                      </ul>                    </li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Testing
              <ul class="list-bullet">
                <li><input type="checkbox" disabled checked> stop at the top_level config</li>
                <li><input type="checkbox" disabled checked> Resolution of model options between different configs</li>
                <li><input type="checkbox" disabled checked> Don&apos;t require a config in every directory</li>
                <li><input type="checkbox" disabled checked> Malformed configs raise an error</li>
                <li><input type="checkbox" disabled checked> Malformed templates throw an error</li>
                <li><input type="checkbox" disabled checked> templates resolved in order from the current directory</li>
                <li><input type="checkbox" disabled checked> Look under <code>./promptbox.toml</code> and <code>./promptbox/promptbox.toml</code></li>
                <li><input type="checkbox" disabled checked> Prompts can be in subdirectories</li>
                <li><input type="checkbox" disabled checked> Prepend</li>
                <li><input type="checkbox" disabled checked> Append</li>
                <li><input type="checkbox" disabled checked> Prepend and append</li>
                <li><input type="checkbox" disabled checked> all types of arguments</li>
                <li><input type="checkbox" disabled checked> Bool arguments are always optional</li>
                <li><input type="checkbox" disabled checked> required arguments (switch from required to optional)</li>
                <li><input type="checkbox" disabled checked> Array arguments</li>
                <li><input type="checkbox" disabled checked> Template model options should override config model options</li>
                <li><input type="checkbox" disabled checked> make sure it works to invoke with command-line options and template options at the same time</li>
                <li><input type="checkbox" disabled checked> system prompt, embedded and in separate file</li>
                <li><input type="checkbox" disabled checked> json mode</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Handle 429 from OpenAI &#x2014; v0.1.2 Dec 4th, 2023</li>
            <li><input type="checkbox" disabled checked> Chop off too-large context, option to keep beginning or end &#x2014; v0.1.1 Dec 1st, 2023
              <ul class="list-bullet">
                <li>Should also be able to specify which inputs to slice off i.e. keep the fixed template intact but remove some of the piped-in input</li>
                <li>Ideally have per-model context values.
                  <ul class="list-bullet">
                    <li>Ollama can get this from the API.</li>
                    <li>OpenAI has few enough models that we can do some basic pattern matching to make a guess</li>
                    <li>But need ability to specify a lower cap too, e.g. maybe we never actually want to send 128K tokens to GPT4</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Token counter functionality &#x2014; v0.1.1 Nov 30th, 2023</li>
            <li><input type="checkbox" disabled checked> Set up CI and distribution &#x2014; Nov 21th, 2023</li>
            <li><input type="checkbox" disabled checked> Streaming support for openai &#x2014; Nov 14th, 2023</li>
            <li><input type="checkbox" disabled checked> Append any additional positional arguments</li>
            <li><input type="checkbox" disabled checked> Append input from stdin</li>
            <li><input type="checkbox" disabled checked> Support <code>format=&quot;json&quot;</code></li>
            <li><input type="checkbox" disabled checked> Streaming support for ollama</li>
            <li><input type="checkbox" disabled checked> Integrate with <a href="https://github.com/jmorganca/ollama">ollama</a></li>
            <li><input type="checkbox" disabled checked> Option type to paste a file contents in, and allow wildcards for array files</li>
            <li><input type="checkbox" disabled checked> Send request to model</li>
            <li><input type="checkbox" disabled checked> Move the main command to be a &quot;run&quot; subcommand</li>
            <li><input type="checkbox" disabled checked> Basic functionality</li>
            <li><input type="checkbox" disabled checked> Define CLI options in template file</li>
            <li><input type="checkbox" disabled checked> Help output always shows openai_key (maybe due to .env?)</li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_promptbox</link><guid isPermaLink="false">b20ad29e8d9f6aef49ca749a44dc7121b4b5fa72bfe75b37e4e723ba853780e0</guid><category><![CDATA[Projects]]></category><pubDate>Tue, 31 Oct 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[New Project Checklist]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>A lot of this applies mostly to library projects that are more likely to draw outside contributions.</li>
    <li><h2>General Tasks</h2>
      <ul class="list-bullet">
        <li>Set up a <a href="https://github.com/casey/just">justfile</a> to run common tasks
          <ul class="list-bullet">
            <li><pre><code><span class="sy-text sy-plain">_default:
  @just --list
</span></code></pre></li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>JS Projects</h2>
      <ul class="list-bullet">
        <li><a href="https://github.com/typicode/husky">husky</a> to install Git hooks</li>
        <li>For public projects, set up Github PR template with a note to add a changeset.</li>
        <li><a href="https://github.com/okonet/lint-staged">lint-staged</a> to help with running prettier on commit
          <ul class="list-bullet">
            <li><a href="https://github.com/dimfeld/svelte-maplibre/blob/master/.lintstagedrc">Sample .lintstagedrc file</a></li>
          </ul>        </li>
        <li><a href="https://github.com/changesets/changesets">changesets</a> for changelog generation
          <ul class="list-bullet">
            <li>Add <code>@changesets/changelog-github</code> for including Github PR info in changelogs</li>
            <li>Add these package.json scripts
              <ul class="list-bullet">
                <li><pre><code><span class="sy-text sy-plain">&quot;changeset&quot;: &quot;changeset&quot;,
&quot;generate-changelog&quot;: &quot;source ghtoken; GITHUB_TOKEN=${GITHUB_TOKEN} changeset version&quot;,
</span></code></pre></li>
              </ul>            </li>
            <li>Install <a href="https://github.com/apps/changeset-bot">changeset-bot</a>.</li>
          </ul>        </li>
        <li><h3>ESLint</h3>
          <ul class="list-bullet">
            <li>Disable some annoying rules which are enabled by common config packs</li>
            <li><pre><code><span class="sy-source sy-js"><span class="sy-entity sy-name sy-label sy-js">rules</span><span class="sy-punctuation sy-separator sy-js">:</span> <span class="sy-meta sy-block sy-js"><span class="sy-punctuation sy-section sy-block sy-begin sy-js">{</span>
    <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>prefer-const<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>off<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
    <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>@typescript-eslint/explicit-module-boundary-types<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>off<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
    <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>@typescript-eslint/ban-ts-comment<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>off<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
    <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>@typescript-eslint/ban-types<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>off<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
    <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>@typescript-eslint/no-empty-function<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>off<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
    <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>@typescript-eslint/no-non-null-assertion<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>off<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
    <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>@typescript-eslint/no-unused-vars<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span>: <span class="sy-meta sy-string sy-js"><span class="sy-string sy-quoted sy-single sy-js"><span class="sy-punctuation sy-definition sy-string sy-begin sy-js">&apos;</span>warn<span class="sy-punctuation sy-definition sy-string sy-end sy-js">&apos;</span></span></span><span class="sy-keyword sy-operator sy-comma sy-js">,</span>
 <span class="sy-punctuation sy-section sy-block sy-end sy-js">}</span></span>
</span></code></pre></li>
          </ul>        </li>
        <li><h3>Prettier</h3>
          <ul class="list-bullet">
            <li>Install prettier-plugin-tailwindcss</li>
            <li><pre><code><span class="sy-source sy-json"><span class="sy-meta sy-mapping sy-json"><span class="sy-punctuation sy-section sy-mapping sy-begin sy-json">{</span>
  </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>useTabs<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-constant sy-language sy-json">false</span><span class="sy-punctuation sy-separator sy-mapping sy-pair sy-json">,</span>
  </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>singleQuote<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-constant sy-language sy-json">true</span><span class="sy-punctuation sy-separator sy-mapping sy-pair sy-json">,</span>
  </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>trailingComma<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>es5<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span><span class="sy-punctuation sy-separator sy-mapping sy-pair sy-json">,</span>
  </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>printWidth<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-constant sy-numeric sy-integer sy-decimal sy-json">100</span><span class="sy-punctuation sy-separator sy-mapping sy-pair sy-json">,</span>
  </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>plugins<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-meta sy-sequence sy-json"><span class="sy-punctuation sy-section sy-sequence sy-begin sy-json">[</span><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>prettier-plugin-svelte<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span><span class="sy-punctuation sy-separator sy-sequence sy-json">,</span> <span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>prettier-plugin-tailwindcss<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span><span class="sy-punctuation sy-section sy-sequence sy-end sy-json">]</span></span><span class="sy-punctuation sy-separator sy-mapping sy-pair sy-json">,</span>
  </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>overrides<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-meta sy-sequence sy-json"><span class="sy-punctuation sy-section sy-sequence sy-begin sy-json">[</span><span class="sy-meta sy-mapping sy-json"><span class="sy-punctuation sy-section sy-mapping sy-begin sy-json">{</span> </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>files<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>*.svelte<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span><span class="sy-punctuation sy-separator sy-mapping sy-pair sy-json">,</span> </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>options<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-meta sy-mapping sy-json"><span class="sy-punctuation sy-section sy-mapping sy-begin sy-json">{</span> </span><span class="sy-meta sy-mapping sy-key sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>parser<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span></span><span class="sy-meta sy-mapping sy-json"><span class="sy-punctuation sy-separator sy-mapping sy-key-value sy-json">:</span> </span><span class="sy-meta sy-mapping sy-value sy-json"><span class="sy-string sy-quoted sy-double sy-json"><span class="sy-punctuation sy-definition sy-string sy-begin sy-json">&quot;</span>svelte<span class="sy-punctuation sy-definition sy-string sy-end sy-json">&quot;</span></span> <span class="sy-punctuation sy-section sy-mapping sy-end sy-json">}</span></span> <span class="sy-punctuation sy-section sy-mapping sy-end sy-json">}</span></span><span class="sy-punctuation sy-section sy-sequence sy-end sy-json">]</span></span>
<span class="sy-punctuation sy-section sy-mapping sy-end sy-json">}</span></span>
</span></code></pre></li>
            <li>For now, update package.json format and lint scripts to workaround a bug in Prettier 3.0. (Note: this seems to be fixed, but need to confirm)
              <ul class="list-bullet">
                <li><pre><code><span class="sy-text sy-plain">    &quot;lint&quot;: &quot;prettier --ignore-unknown --check &apos;./**/*&apos; &amp;&amp; eslint .&quot;,
    &quot;format&quot;: &quot;prettier --ignore-unknown --write &apos;./**/*&apos;&quot;
</span></code></pre></li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Rust Projects</h2>
      <ul class="list-bullet">
        <li>Set up rustfmt.toml the way I like it
          <ul class="list-bullet">
            <li><pre><code><span class="sy-source sy-toml"><span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">edition</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>2021<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">imports_granularity</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>Crate<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span>
<span class="sy-meta sy-tag sy-key sy-toml"><span class="sy-entity sy-name sy-tag sy-toml">group_imports</span></span> <span class="sy-punctuation sy-definition sy-key-value sy-toml">=</span> <span class="sy-string sy-quoted sy-double sy-basic sy-toml"><span class="sy-punctuation sy-definition sy-string sy-begin sy-toml">&quot;</span>StdExternalCrate<span class="sy-punctuation sy-definition sy-string sy-end sy-toml">&quot;</span></span>
</span></code></pre></li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/new_project_checklist</link><guid isPermaLink="false">a6704d8560a062f6eaebf8d39996a1cc0801fe1567342251f4b198df22987b64</guid><pubDate>Tue, 03 Oct 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[Glance]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>This is a project that allows creation of a bunch of mini-apps which can do things like scrape data on some interval and push it to a dashboard which shows data from all the apps.</li>
    <li><a href="https://github.com/dimfeld/glance">Github</a></li>
    <li><h2>Task List</h2>
      <ul class="list-bullet">
        <li><h3>Up Next</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Option to store apps within database
              <ul class="list-bullet">
                <li>Each app&apos;s code has two Javascript functions, one which does the work and another which formats the result</li>
                <li>These apps should be bundled and then run on the backend.</li>
                <li>Add a new table: app_code, which is linked to the app id, contains the code, and has a version r
                  <ul class="list-bullet">
                    <li>Hold both the original code and the bundled version.</li>
                  </ul>                </li>
                <li>An app with in-app code links to a specific row with the same app id</li>
                <li>When running, execute using <a href="https://imfeld.dev/notes/projects_js_sidecar">JS Sidecar</a></li>
              </ul>            </li>
            <li><input type="checkbox" disabled> In-app code editor
              <ul class="list-bullet">
                <li>Can take a lot from Ergo for this but worth seeing if there&apos;s new stuff for the code editor
                  <ul class="list-bullet">
                    <li><a href="https://github.com/val-town/codemirror-ts">https://github.com/val-town/codemirror-ts</a> for example might have some improvements over what I wrote before, need to check</li>
                    <li>What&apos;s the best way to do bundling now? The existing method worked pretty well, probably worth just continuing that.</li>
                  </ul>                </li>
                <li>Code should have a &quot;latest&quot; version which autosyncs and then the active version activates when clicking save</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Soon</h3>
          <ul class="list-bullet">
            <li>In App Code
              <ul class="list-bullet">
                <li><input type="checkbox" disabled> KV interface for functions to persist data
                  <ul class="list-bullet">
                    <li>Consider just implementing a light version of <code>node:fs</code> which uses SQLite behind the scenes
                      <ul class="list-bullet">
                        <li>Won&apos;t really work with seeking or anything but that&apos;s ok, doesn&apos;t come up often anyway</li>
                      </ul>                    </li>
                  </ul>                </li>
                <li><input type="checkbox" disabled> AI code generation</li>
                <li><input type="checkbox" disabled> AI-generated UI</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Inbox-style interface</li>
            <li><input type="checkbox" disabled> Notifications</li>
            <li><input type="checkbox" disabled> Customize layout of the data items
              <ul class="list-bullet">
                <li>Get rid of the existing format for items, and make this into a block-based thing with rows and columns, where blocks can be text/image/chart/etc.</li>
                <li>Block types
                  <ul class="list-bullet">
                    <li>Text</li>
                    <li>Chart</li>
                    <li>Row</li>
                    <li>Column</li>
                    <li>Expansion Pane</li>
                    <li>Image</li>
                  </ul>                </li>
                <li>Blocks can reference a separate data block using JSON pointer syntax</li>
                <li>Text can be markdown</li>
                <li>Probably keep &quot;title&quot; and maybe subtitle but the rest should be new</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Later</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Calculate a hash of all data fields and store/compare in the database to avoid unneeded writes when nothing has changed. Can possibly use the new MERGE syntax to help with this.</li>
            <li><input type="checkbox" disabled> Publish live on web</li>
            <li><input type="checkbox" disabled> Menubar App</li>
            <li><input type="checkbox" disabled> Item actions
              <ul class="list-bullet">
                <li>See <a class="block-ref" href="https://imfeld.dev/notes/projects_glance#6528a6dc-beaf-462e-b58a-18bcae7407c7">Apps can define actions to be done.</a></li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Detail pages can be a web app served by the app itself?</li>
            <li><input type="checkbox" disabled> Main Item display can be a simple chart or other viz</li>
            <li><input type="checkbox" disabled> Item detail can be a vega viz or something
              <ul class="list-bullet">
                <li>See <a class="block-ref" href="https://imfeld.dev/notes/projects_glance#652e3b52-1fc2-4826-ab2e-8e8a5bfaf97a">Details</a></li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Let apps expose their config through the platform
              <ul class="list-bullet">
                <li>Things like your location for getting the weather</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> SSE update notification</li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> REST endpoints to add/update/delete an app &#x2014; Jul 29th, 2024</li>
            <li><input type="checkbox" disabled checked> REST endpoints to add/update items within an app &#x2014; Jul 29th, 2024</li>
            <li><input type="checkbox" disabled checked> Simple auth system so that I can publish this readonly but still log in and make changes &#x2014; Feb 14th, 2024</li>
            <li><input type="checkbox" disabled checked> Platform can run apps on a schedule &#x2014; Dec 12th, 2023</li>
            <li><input type="checkbox" disabled checked> Web app reloads data on a schedule &#x2014; Dec 11th, 2023</li>
            <li><input type="checkbox" disabled checked> Dismissible items &#x2014; Nov 28th, 2023</li>
            <li><input type="checkbox" disabled checked> Render simple UI &#x2014; Nov 28th, 2023</li>
            <li><input type="checkbox" disabled checked> Figure out more structured layout for app data &#x2014; Nov 27th, 2023</li>
            <li><input type="checkbox" disabled checked> HTTP Server &#x2014; Nov 27th, 2023</li>
            <li><input type="checkbox" disabled checked> Read items from each data file</li>
            <li><input type="checkbox" disabled checked> Watch app data directory</li>
            <li><input type="checkbox" disabled checked> Write a simple app that always has data to show (Weather or HN API or something super basic)</li>
            <li><input type="checkbox" disabled checked> Create app helper library
              <ul class="list-bullet">
                <li>Common paths, JSON data definition, etc.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Firm up designs</li>
            <li><input type="checkbox" disabled checked> Design JSON schema for data</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Basic Design</h2>
      <ul class="list-bullet">
        <li>The platform has a directory, where each app can place a JSON data file.</li>
        <li><h3>App Data Format</h3>
          <ul class="list-bullet">
            <li>This is a JSON file with some information about the app and its current data.</li>
            <li>App Title</li>
            <li>URL to reverse proxy to for detailed web UI
              <ul class="list-bullet">
                <li>And index page path, if applicable</li>
              </ul>            </li>
            <li>Data Items Array
              <ul class="list-bullet">
                <li>Something like this</li>
                <li>Item UUID</li>
                <li>Item type</li>
                <li>Description
                  <ul class="list-bullet">
                    <li>This is HTML that gets injected into the page.</li>
                  </ul>                </li>
                <li>Chart?</li>
                <li>Structured data
                  <ul class="list-bullet">
                    <li>This can be used to allow more customization of the displayed data</li>
                  </ul>                </li>
                <li>Notification - optional
                  <ul class="list-bullet">
                    <li>Notification also has a UUID so that a single item can show multiple notifications even if it already exists.</li>
                    <li>Notification title and text</li>
                    <li>Icon/image?</li>
                  </ul>                </li>
                <li>Persistent or dismissable
                  <ul class="list-bullet">
                    <li>Dismissable items can be closed by the user until they are updated again, if ever.</li>
                  </ul>                </li>
                <li id="652e3ae1-7983-4681-8826-6c711bbe94d3">Updated time</li>
                <li id="652e3b52-1fc2-4826-ab2e-8e8a5bfaf97a">Details
                  <ul class="list-bullet">
                    <li>This can be a path under the reverse proxy URL, if present</li>
                    <li>Or some defined visualization
                      <ul class="list-bullet">
                        <li>This will be something like a SQLite database, a set of queries to run, and maybe some Vega viz to render?</li>
                      </ul>                    </li>
                  </ul>                </li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Backend Platform</h3>
          <ul class="list-bullet">
            <li>Watch the data directory</li>
            <li>On load and when a file updates, read the file and see what the data items are</li>
            <li>Serve a web app which shows all the data</li>
            <li>Track things like notifications, dismissible item state, and so on.</li>
          </ul>        </li>
        <li><h3>UI</h3>
          <ul class="list-bullet">
            <li>Menubar app</li>
            <li>More detailed page for use in a browser/phone.</li>
            <li>Good case for some kind of microcontroller with an OLED</li>
            <li>Should be possible to run all these on a different computer from the platform.</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Apps</h2>
      <ul class="list-bullet">
        <li>Weather</li>
        <li>Hurricane and weather alert status</li>
        <li>HN and Lobste.rs top links with actions for summarization</li>
        <li>Watch Github releases for certain projects</li>
      </ul>    </li>
    <li><h2>Notes</h2>
      <ul class="list-bullet">
        <li>V1 of this is a few components:
          <ul class="list-bullet">
            <li>A mini-app template project that can be forked</li>
            <li>A main server
              <ul class="list-bullet">
                <li>Manage and start the mini-apps</li>
                <li>Set environment for the mini-apps with some well-known keys:
                  <ul class="list-bullet">
                    <li>directory for each app to create its SQLite database and other stuff like that</li>
                  </ul>                </li>
                <li>Provide a way the mini-apps can call to push new data
                  <ul class="list-bullet">
                    <li>indicate that a notification should be shown for important stuff</li>
                    <li>push new data to be shown when the app is opened</li>
                    <li>some way to define how to lay out the UI for that data</li>
                    <li>Could be HTTP endpoints that the server exposes</li>
                    <li>Items can be persistent until the app remove them, or acknowledgeable, where the stay until the user dismisses them.</li>
                    <li>Actually just using the filesystem would work well
                      <ul class="list-bullet">
                        <li>Data goes in a sqlite database</li>
                        <li>schema file defines both visualizations and the queries to run to create the data for them.</li>
                        <li>Server watches for changes in the schema file.</li>
                        <li>How do we read all the data in an efficient manner?
                          <ul class="list-bullet">
                            <li>Having to read the dashboard databases all at once when the dashboard opens could be messy.</li>
                            <li>OTOH it could be massively parallelized and the reads would generally be very quick, just a few rows per DB.</li>
                            <li>In reality, having all the summary data in the JSON itself will be fine. There won&apos;t be that much data.</li>
                          </ul>                        </li>
                        <li>Notifications would probably still need an endpoint?
                          <ul class="list-bullet">
                            <li>Could have a field in the JSON that would let an app set a notification, with a UUID or something to handle idempotency.</li>
                          </ul>                        </li>
                      </ul>                    </li>
                  </ul>                </li>
                <li>A web-based UI
                  <ul class="list-bullet">
                    <li>Maybe both as a menu-bar app and as a normal app</li>
                    <li>Lists and manages the mini-apps</li>
                    <li>Displays the pushed data</li>
                  </ul>                </li>
              </ul>            </li>
          </ul>        </li>
        <li>V2
          <ul class="list-bullet">
            <li>Some way for mini-apps to expose a more in-depth look into their data.
              <ul class="list-bullet">
                <li>Probably do this through some kind of reverse proxy that just assigns some path to proxy through to the mini-app so it can serve its own UI.</li>
              </ul>            </li>
            <li id="6528a6dc-beaf-462e-b58a-18bcae7407c7">Apps can define actions to be done.
              <ul class="list-bullet">
                <li>Each item can have its own actions</li>
                <li>Actions have a label, maybe an icon, styling, and a way to do the action
                  <ul class="list-bullet">
                    <li>HTTP endpoint to hit.</li>
                    <li>Website to visit for more details</li>
                    <li>Command to run</li>
                  </ul>                </li>
              </ul>            </li>
            <li>Auto-deploy from Git/HTTP sources?</li>
            <li>LLM summarization of current stuff?</li>
            <li>AI Agents which can send their status and results to the app (this probably wouldn&apos;t require any new functionality)</li>
          </ul>        </li>
        <li>How to run mini-apps?
          <ul class="list-bullet">
            <li>In some ways, it would be simplest for the app to be a single file which gets started by a worker. The file can export certain functions to deliver the relevant functionality.</li>
            <li>A full-fledged app is much more flexible, but also somewhat harder to distribute and harder to proxy to. I think in the end this will be a better solution though.
              <ul class="list-bullet">
                <li>Distribution: this can be a zip file. Run bun to install deps and then start it. This also technically allows the server to be written in any language, though that adds complexity.</li>
              </ul>            </li>
            <li>Could just have them run separately, which would be simplest in some ways. Some apps would run persistently, but many apps would just be a cron job. The cron jobs could also be done as an internal system in the platform.</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Notes from Initial Idea</h2>
      <ul class="list-bullet">
        <li>Perhaps what I  really want is just a way to have some kind of PaaS system for mini-apps, where each one is somewhat separate despite being hosted behind a single place. So this turns into a reverse proxy with easy-to-add upstreams at arbitrary paths, and an easy way to write servers and set up storage, manage notifications, etc.</li>
        <li>So we have a server which handles the reverse proxy. Each mini app is hosted at a separate path on the server but otherwise runs separately.  Notifications and other actions are also endpoints in a private part of the server which the mini app can call back into.  Maybe function  as a tracing sidecar too?</li>
        <li>How much of this could be done with off the shelf components?
          <ul class="list-bullet">
            <li>Consul for service registration</li>
            <li>Vault for pulling secrets like webhook keys</li>
            <li>Nomad for running the mini apps and pulling in the secrets into the containers</li>
            <li>caddy or nginx for the web server, using dynamically generated templates from consul service records?</li>
            <li>Task scheduler to poke services every so often, or should this be built in to the apps themselves?
              <ul class="list-bullet">
                <li>How do we manage concurrency here? Probably by just having each app that needs it manage its own queue. Could do this using effectum to make it easy.</li>
                <li>Would it be useful to provide a shell library that could allow writing each service as just a simple function or as close as possible? I think this would be helpful. Create the database connections, pull in the proper secrets, and so on. Apps that needs more functionality could use an http router or SvelteKit or something. But a simple approach like this could potentially allow for creating mini-apps within some other UI again, perhaps even with dashboard building blocks.</li>
              </ul>            </li>
          </ul>        </li>
        <li>Is there a good platform in here to experiment with AI coding solutions as well?</li>
        <li>I think I&apos;m ending up with 2 models here:
          <ul class="list-bullet">
            <li>mini apps that are actually full apps</li>
            <li>Mini apps that are actually just executable functions but with some extra functionality compared to what&apos;s there now in Ergo.</li>
            <li>So the former is just a matter of hosting, proxy, etc.  The latter is where the design comes in.</li>
          </ul>        </li>
        <li>Micro apps (probably not going to do this)
          <ul class="list-bullet">
            <li>single function to execute the actual code</li>
            <li>Optional schedule on which the function runs.</li>
            <li>UI defined with simple building blocks
              <ul class="list-bullet">
                <li>Queries to run against the stored data, something like a stored procedure</li>
                <li>Charts to display those queries</li>
                <li>controls to update the parameters for the queries</li>
                <li>A way to define all this easily, not necessarily no-code, but low-code to start, similar to Gradio</li>
              </ul>            </li>
            <li>compared to the current method, the function should be uploaded from a file and executed in something external like Node, Deno, or Bun.
              <ul class="list-bullet">
                <li>This allows for it to actually be tested and developed externally, without much fuss.</li>
                <li>Optionally, perhaps even update these functions from a Git repository, where pushing to the master branch updates the function to be run.</li>
                <li>Then we could just do a shebang to decide in what context it actually runs (with the inherent security problems this has? Maybe a whitelist of safe executors, and this is especially appealing with Deno and Bun(?) which have permissions systems).</li>
                <li>Also look into WASM execution now that WASIX and similar standards are making that easier.</li>
                <li>Provide an SQLite database path for each function so that it can implement its own persistence.</li>
              </ul>            </li>
            <li>An app should provide four functions: query, act, cron (optional) and buildUI.
              <ul class="list-bullet">
                <li>Query gets data from the database for the UI to show</li>
                <li>Act performs actions based on button clicks</li>
                <li>Cron, if implemented, will do scheduled data updates</li>
                <li>buildUI shows what kind of dashboards to show</li>
                <li>Something like this anyway. Need to figure out the relation between query and buildUI, maybe they should be merged.</li>
              </ul>            </li>
            <li>Application package defines:</li>
            <li>sqlite database connection with predetermined location</li>
            <li>Sqlite migration system</li>
            <li>A way to define UI via code</li>
            <li>A way to send notifications through the app host</li>
            <li>a job queue</li>
          </ul>        </li>
        <li>Or... just make templates, use the template, and have a way to autodeploy them all behind an reverse proxy.
          <ul class="list-bullet">
            <li>The main downside of this is that it&apos;s harder to make a dashboard with status from various apps all in one.</li>
            <li>But this could be done through a common endpoint that returns data and ui definitions.</li>
            <li>So in this paradigm we have:
              <ul class="list-bullet">
                <li>server that exists independently and with its own UI</li>
                <li>a separate dashboards app that apps can register  with</li>
                <li>A /dashboard endpoint on each server that tells the dashboard what to display.</li>
                <li>Or an endpoint on the dashboards app through which the servers can push data. This is probably the way to go since it doesn&apos;t enforce any particular structure for each app.</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_glance</link><guid isPermaLink="false">6b5f1e766a545759dce43a66183675e897545e497cc0f291256d375da440f23a</guid><category><![CDATA[Projects]]></category><pubDate>Fri, 29 Sep 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[Time Series Forecasting Libraries]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li><a href="https://nixtla.github.io/statsforecast/">https://nixtla.github.io/statsforecast/</a></li>
    <li><a href="https://neuralprophet.com/">https://neuralprophet.com/</a></li>
    <li><a href="https://github.com/unit8co/darts">https://github.com/unit8co/darts</a></li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/time_series_forecasting_libraries</link><guid isPermaLink="false">07573b559064be00f048cedf0835e8398919add9db026c8e2ec1391be2dab776</guid><pubDate>Tue, 26 Sep 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[Extracting Contacts from an iOS backup]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>This is tricky since the format is not well-documented and there are a bunch of files with just random hexadecimal strings as names.</li>
    <li>On Mac, the backups are stored at <code>~/Library/Application Support/MobileSync/Backup</code>. For some reason the contents of this directory aren&apos;t visible from the terminal, but are in Finder. If you copy the backup into another directory then you can use the backup in Terminal as well.</li>
    <li><h2>Extracting Contacts</h2>
      <ul class="list-bullet">
        <li>The address book file is in the backup at <code>31/31bb7ba8914766d4ba40d6dfb6113c8b614be442</code>. This is a SQLite3 database.</li>
        <li>The main tables you care about here are <code>ABPerson</code> and <code>ABMultiValue</code>. To do a simple extraction of names and phone/email you can use a query like this.
          <ul class="list-bullet">
            <li><pre><code><span class="sy-source sy-sql"><span class="sy-keyword sy-other sy-DML sy-sql">select</span> <span class="sy-constant sy-other sy-database-name sy-sql">ABPerson</span>.<span class="sy-constant sy-other sy-table-name sy-sql">last</span>, <span class="sy-constant sy-other sy-database-name sy-sql">ABPerson</span>.<span class="sy-constant sy-other sy-table-name sy-sql">first</span>, <span class="sy-constant sy-other sy-database-name sy-sql">ABMultiValue</span>.<span class="sy-constant sy-other sy-table-name sy-sql">value</span>
<span class="sy-keyword sy-other sy-DML sy-sql">from</span> ABPerson,ABMultiValue
<span class="sy-keyword sy-other sy-DML sy-sql">where</span> <span class="sy-constant sy-other sy-database-name sy-sql">ABMultiValue</span>.<span class="sy-constant sy-other sy-table-name sy-sql">record_id</span><span class="sy-keyword sy-operator sy-comparison sy-sql">=</span><span class="sy-constant sy-other sy-database-name sy-sql">ABPerson</span>.<span class="sy-constant sy-other sy-table-name sy-sql">ROWID</span> <span class="sy-keyword sy-other sy-DML sy-sql">order by</span> Last, First
</span></code></pre></li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Locating Files</h2>
      <ul class="list-bullet">
        <li>The <code>Manifest.db</code> is a database of all the files. You can run a query like this to find a file.
          <ul class="list-bullet">
            <li><pre><code><span class="sy-source sy-sql"><span class="sy-keyword sy-other sy-DML sy-sql">SELECT</span> fileID, relativePath
<span class="sy-keyword sy-other sy-DML sy-sql">FROM</span> Files
<span class="sy-keyword sy-other sy-DML sy-sql">WHERE</span> relativePath <span class="sy-keyword sy-operator sy-logical sy-sql">like</span> <span class="sy-string sy-quoted sy-single sy-sql"><span class="sy-punctuation sy-definition sy-string sy-begin sy-sql">&apos;</span>%Address%<span class="sy-punctuation sy-definition sy-string sy-end sy-sql">&apos;</span></span>;
</span></code></pre></li>
          </ul>        </li>
        <li>From there, the first two digits of the <code>fileId</code> indicate the directory to look into, and the entire fieId is then the filename in that directory.</li>
        <li>The <a href="https://pypi.org/project/iOSbackup/">pypi page for the iOSbackup Python package</a> also has a list of commonly needed files and their fileID values.</li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/extracting_contacts_from_an_ios_backup</link><guid isPermaLink="false">0fcc641c3a4e966f9249578b06d0d0e7721c3fa3c06f8b168abaaf57fd6b9087</guid><pubDate>Thu, 21 Sep 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[Buzzy]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>Experimental AI bot to talk with my kids and answer questions.</li>
    <li>The ecosystem around voice-based chats has improved a lot since I started this project. On hold for now, but I&apos;ll probably start this up again later in 2024 and just use more services instead of trying to do it all on CPU.</li>
    <li><a href="https://github.com/dimfeld/buzzy">https://github.com/dimfeld/buzzy</a></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 id="651362e9-deb6-439d-9b3c-c85b3356517b"><input type="checkbox" disabled> Basic intent detection</li>
            <li id="651362e9-2c46-420e-ba11-b311988e87ca"><input type="checkbox" disabled> Run web searches and generate an answer from the results.</li>
            <li><input type="checkbox" disabled> read system message from a file</li>
            <li><input type="checkbox" disabled> record llm pipeline actions for later analysis</li>
          </ul>        </li>
        <li><h3>Later</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Check out <a href="https://github.com/FL33TW00D/whisper-turbo">whisper-turbo</a> for in-browser voice recognition</li>
            <li><input type="checkbox" disabled> optional configuration to use better models that require GPU</li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> set up basic ChatGPT workflow</li>
            <li><input type="checkbox" disabled checked> Voice recognition</li>
            <li><input type="checkbox" disabled checked> Basic TTS</li>
            <li><input type="checkbox" disabled checked> Websocket-based communication</li>
            <li><input type="checkbox" disabled checked> Stream results back to client</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>System Prompt Example</h2>
      <ul class="list-bullet">
        <li>You are Buzzy, an AI bot that answers questions for children. Your answers should be appropriate for a smart six year old boy, but also don&apos;t dumb your answers down too much.</li>
      </ul>    </li>
    <li><h2>Ideas</h2>
      <ul class="list-bullet">
        <li>Do I want to do a RAG-based conversation learning/memory?</li>
        <li><input type="checkbox" disabled> Decide whether to send back previous chat messages based on how much time has passed.</li>
        <li>Voice recognition to ask questions
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Choice:
              <ul class="list-bullet">
                <li>Decision: <a class="block-ref" href="https://imfeld.dev/notes/projects_buzzy#651347dc-edce-43bf-8535-54b5c3767a55"></a><a href="https://huggingface.co/nvidia/stt_en_fastconformer_transducer_large">nvidia/stt_en_fastconformer_transducer_large</a></li>
                <li>Options:
                  <ul class="list-bullet">
                    <li>Run <a href="https://whisper.ggerganov.com/">Whisper in browser</a>?</li>
                    <li>Whisper on server?
                      <ul class="list-bullet">
                        <li>Too slow when running on CPU</li>
                      </ul>                    </li>
                    <li id="651347dc-edce-43bf-8535-54b5c3767a55"><a href="https://huggingface.co/nvidia/stt_en_fastconformer_transducer_large">nvidia/stt_en_fastconformer_transducer_large</a>
                      <ul class="list-bullet">
                        <li>runs fast (~500ms for short passages) and works well</li>
                      </ul>                    </li>
                  </ul>                </li>
              </ul>            </li>
            <li>Needs to run ok on just CPU</li>
            <li>Seems to work best to just send the audio in one big chunk to the server.</li>
            <li><a href="https://huggingface.co/spaces/hf-audio/open_asr_leaderboard">Huggingface Voice Recognition Leaderboard</a></li>
          </ul>        </li>
        <li>TTS to say responses
          <ul class="list-bullet">
            <li>How easy is it to use one of the new models for this?</li>
            <li><input type="checkbox" disabled checked> Choice:
              <ul class="list-bullet">
                <li>Decision: <a class="block-ref" href="https://imfeld.dev/notes/projects_buzzy#651346cb-b67c-4640-81b7-79dd3998fcb5"></a><a href="https://github.com/MycroftAI/mimic3">Mimic 3</a></li>
                <li>Options:
                  <ul class="list-bullet">
                    <li>Bark seems promising
                      <ul class="list-bullet">
                        <li><a href="https://github.com/suno-ai/bark">https://github.com/suno-ai/bark</a></li>
                        <li><a href="https://github.com/serp-ai/bark-with-voice-clone">https://github.com/serp-ai/bark-with-voice-clone</a></li>
                        <li><a href="https://github.com/gitmylo/bark-voice-cloning-HuBERT-quantizer">https://github.com/gitmylo/bark-voice-cloning-HuBERT-quantizer</a></li>
                        <li>Very slow on CPU, RTF of 6-8</li>
                      </ul>                    </li>
                    <li>XTTS
                      <ul class="list-bullet">
                        <li><a href="https://github.com/coqui-ai/TTS/blob/dev/docs/source/models/xtts.md">https://github.com/coqui-ai/TTS/blob/dev/docs/source/models/xtts.md</a></li>
                        <li>Sounds the best, but has RTF of 4-5 on CPU</li>
                      </ul>                    </li>
                    <li id="651346cb-b67c-4640-81b7-79dd3998fcb5"><a href="https://github.com/MycroftAI/mimic3">Mimic 3</a>
                      <ul class="list-bullet">
                        <li>This turns out to be the only solution that both sounds good and runs in realtime on CPU</li>
                        <li>Running with lengthScale 1.2 slows it down a bit and seems to give best results</li>
                      </ul>                    </li>
                  </ul>                </li>
              </ul>            </li>
          </ul>        </li>
        <li>Some kind of 3D avatar that&apos;s like a dinosaur or robot or something?</li>
        <li>Scrensaver mode that does a photo carousel</li>
      </ul>    </li>
    <li><h2>Intent Detection</h2>
      <ul class="list-bullet">
        <li><a href="https://huggingface.co/MoritzLaurer/DeBERTa-v3-base-mnli-fever-anli">DeBERTa-v3-base-mnli-fever-anli </a> seems to work well for this at first try. Haven&apos;t really exercised it significantly yet though. The creator of that model now also has <a href="https://huggingface.co/MoritzLaurer/deberta-v3-large-zeroshot-v1">deberta-v3-large-zeroshot-v1</a>.</li>
        <li>Tasks
          <ul class="list-bullet">
            <li>Figure out if something is a question that can be answered by searching the web and/or wikipedia</li>
            <li>How many days until...</li>
            <li>Show me pictures of...</li>
          </ul>        </li>
        <li>When doing web search and intent , also need to detect if a query builds on the previous queries or not.
          <ul class="list-bullet">
            <li>&quot;No, the blue one&quot; has no context but the context is probably in the previous message.</li>
            <li>Small models don&apos;t seem to do great on this but gpt-3.5-turbo-instruct does well.
              <ul class="list-bullet">
                <li><pre><code><span class="sy-text sy-plain">Assistant: {assistant&apos;s last message}

User: {user&apos;s question}

Does the user&apos;s question ask for clarification on the assistant&apos;s statement? Only answer yes or no.
</span></code></pre></li>
              </ul>            </li>
            <li>For this we can probably just pair up the last assistant message with the latest query, since they tend to include all the necessary info again in every message. Then use GPT to create a proper search for it.</li>
            <li>Do we even need to do the detection? Will it work to just ask GPT to make a search for the query?
              <ul class="list-bullet">
                <li>Takes some tweaking of the prompt but this seems to work well.</li>
                <li><pre><code><span class="sy-text sy-plain">This is an excerpt of a chat with an assistant and a user:

Assistant: {assistant&apos;s last message}

User: {user&apos;s question}

What would be a good web search to answer the user&apos;s question? If the question is asking for clarification on the assistant&apos;s statement, then the web search should account for that. If it is a new line of questioning, then ignore the assistant&apos;s statement. Respond only with the web search and nothing else.

Web search:
</span></code></pre></li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Web Search</h2>
      <ul class="list-bullet">
        <li>Use Brave Search API to do web searches to answer questions
          <ul class="list-bullet">
            <li>Should searches be an intent, or should we run searches for anything that doesn&apos;t return another intent?</li>
            <li>Maybe also wikipedia/wikidata?</li>
          </ul>        </li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_buzzy</link><guid isPermaLink="false">be52106817c8d27e7229011113af79f80de5071c19c9241b1ca30af49d734457</guid><category><![CDATA[Projects]]></category><pubDate>Thu, 07 Sep 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[Smelter]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li>This is a project to do map-reduce style calculations using a bunch of serverless workers reading from S3.</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> Global queue system and persistent workers for tasks
              <ul class="list-bullet">
                <li>This should speed up the time between tasks a lot and makes things more flexible as new tasks are added dynamically.</li>
                <li>Start a pool of workers, each of which is qualified to handle one or more stages</li>
                <li>Each worker communicates with the manager and fetches tasks from it</li>
                <li>The biggest change this requires is ensuring that the manager and the workers can actually communicate. Can be an issue when it requires punching a hole in a VPC for example, maybe there&apos;s a better way to do it.
                  <ul class="list-bullet">
                    <li>Just running the manager as a task inside the VPC as well would accomplish some of this.</li>
                    <li>Opening a hole in the VPC could work:
                      <ul class="list-bullet">
                        <li>Set up VPC ACLs to allow inbound comms on some port</li>
                        <li>Manager generates a client SSL cert and sends the pubkey along with the worker payload</li>
                        <li>Worker gets a public IPv6 IP</li>
                        <li>Once a worker starts,
                          <ul class="list-bullet">
                            <li>manager connects to the worker</li>
                            <li>worker verifies the client cert</li>
                            <li>uses a 2-way GRPC connection to swap info both ways</li>
                            <li>This would also help with shipping logs back to the manager.</li>
                          </ul>                        </li>
                      </ul>                    </li>
                    <li>Could also work to use some other queue service like SQS which is easy to talk to from both sides
                      <ul class="list-bullet">
                        <li>But this requires supporting a bunch of different APIs on both sides.</li>
                      </ul>                    </li>
                  </ul>                </li>
                <li>This also opens things up in some ways for running in Spot mode
                  <ul class="list-bullet">
                    <li>Add a function that allows a worker to checkpoint its work. The checkpoint function sends a payload to the manager, and then if the worker dies during the execution the manager can put the task back in the queue with checkpoint info, and the worker can resume from where it left off, if possible.</li>
                  </ul>                </li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Later/Maybe</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled> Platform adapters could make it easier to build the local and platform-side code
              <ul class="list-bullet">
                <li>Not sure how useful this actually is, but this would mostly be a preconfigured way for a binary to determine if its running in the platform or not and then run your manager code or your worker code when you&apos;re doing the single-source-code configuration</li>
                <li>I think a simple abstraction would be something like:
                  <ul class="list-bullet">
                    <li>The spawner has an easy way to set a &quot;SMELTER_TASK&quot; variable in the environment (or whatever is the equivalent) of the spawned task.</li>
                    <li>Provide a function that retrieves this value, and then you can match on it and run your own code. If it&apos;s not set, then you assume that you are the job manager and run that code instead.</li>
                    <li>Ultimately this isn&apos;t too different from doing it yourself, might be a nice convenience though especially when developing the pipeline.</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Ability for spawner to send an event when a container goes from pending to running</li>
            <li><input type="checkbox" disabled> Option to run a server within the worker container that can collect logs and stream them back to the manager</li>
            <li><input type="checkbox" disabled> Option to timeout if we don&apos;t see any logs for too long
              <ul class="list-bullet">
                <li>This depends on being able to get logs shipped from the container down to the manager.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> AWS Lambda adapter</li>
            <li><input type="checkbox" disabled> Support Fargate spot instances
              <ul class="list-bullet">
                <li>A way for SpawnedTask to indicate that a task failure is from a spot instance shutdown
                  <ul class="list-bullet">
                    <li>This can be a new error code alongside <code>TaskFailed</code>, call it <code>TaskPreempted</code></li>
                  </ul>                </li>
                <li>Specify which capacity provider to use for Spot vs. Normal launching</li>
                <li>An option to launch it as a non-spot instance if the spot instance gets preempted too much or if it takes too long to start.</li>
                <li>Currently Fargate doesn&apos;t support spot on ARM</li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Convert a single SQL statement into a multi-stage set of tasks
              <ul class="list-bullet">
                <li>This may be better done by integrating with something like <a href="https://arrow.apache.org/datafusion/">Datafusion</a> which already supports creating query plans
                  <ul class="list-bullet">
                    <li>Check out <a href="https://github.com/datafusion-contrib/ray-sql">https://github.com/datafusion-contrib/ray-sql</a> and <a href="https://github.com/datafusion-contrib/datafusion-federation">https://github.com/datafusion-contrib/datafusion-federation</a> which have some common concerns.</li>
                  </ul>                </li>
                <li>Consider also supporting <a href="https://substrait.io/">Substrait</a> query plan format
                  <ul class="list-bullet">
                    <li>DuckDB supports consuming Substrait so I may be able to generate Substrait from SQL, split it into stages, and then run the nodes as needed</li>
                  </ul>                </li>
              </ul>            </li>
            <li><input type="checkbox" disabled> Clean up inter-level channels
              <ul class="list-bullet">
                <li>There are a bunch of channels for communicating cancel state, task results, etc. between the tasks, stages, job, and manager. See if there&apos;s a way to make this cleaner.</li>
              </ul>            </li>
          </ul>        </li>
        <li><h3>Done</h3>
          <ul class="list-bullet">
            <li><input type="checkbox" disabled checked> Retry inside the spawner when Fargate returns a capacity error</li>
            <li><input type="checkbox" disabled checked> Cancel all the jobs when an error occurs, and wait for them to be cancelled</li>
            <li><input type="checkbox" disabled checked> Handle rate exceeded errors from Fargate API
              <ul class="list-bullet">
                <li>The Rust AWS SDK doesn&apos;t properly handle <code>ThrottlingError</code> and flag it for retry, but with a custom retry layer this can be done.</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> Workers automatically track RAM usage, load factor, time running, etc., and return these with the task results</li>
            <li><input type="checkbox" disabled checked> Full test of Fargate jobs</li>
            <li><input type="checkbox" disabled checked> Allow a cancel signal to kill all containers</li>
            <li><input type="checkbox" disabled checked> StatusSender needs to work with a raw StatusUpdateItem, and the StatusCollector should be able to handle native StatusUpdateItem and have its own separate mechanism for the read operations. (Maybe just use a mutex here to simplify?)</li>
            <li><input type="checkbox" disabled checked> Build fargate container</li>
            <li><input type="checkbox" disabled checked> Have something that helps generate task definitions for fargate</li>
            <li><input type="checkbox" disabled checked> AWS Fargate Spawner
              <ul class="list-bullet">
                <li>How to figure out if a container succeeded or not? &#x2014; exit code is exposed though there&apos;s no other way to pass back data</li>
              </ul>            </li>
            <li><input type="checkbox" disabled checked> AWS Fargate Worker Framework</li>
            <li id="651362f2-b0ec-4bf6-b14a-226b2f8cc350"><input type="checkbox" disabled checked> Example to run workers as local processes</li>
            <li><input type="checkbox" disabled checked> Remove the <code>Spawner</code> trait altogether
              <ul class="list-bullet">
                <li>It&apos;s no longer necessary and isn&apos;t sufficiently expressive for most spawners</li>
              </ul>            </li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Test Datasets</h2>
      <ul class="list-bullet">
        <li><a href="https://opensky-network.org/datasets/raw/">https://opensky-network.org/datasets/raw/</a></li>
      </ul>    </li>
    <li><h2>Initial Features</h2>
      <ul class="list-bullet">
        <li>Split up queries across a number of workers
          <ul class="list-bullet">
            <li>Split queries into chunks</li>
            <li>Max number of concurrent workers</li>
          </ul>        </li>
        <li>Run workers</li>
        <li>Gather results</li>
        <li>Reduce results using 1 or more reducers</li>
        <li>Retryable workers with some threshold for retrying
          <ul class="list-bullet">
            <li>e.g. autoretry remaining workers once the first 90% have returned</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Pluggable Backends</h2>
      <ul class="list-bullet">
        <li>Execution
          <ul class="list-bullet">
            <li>AWS Lambda</li>
            <li>Docker/Kubernetes/Nomad</li>
          </ul>        </li>
        <li>Storage
          <ul class="list-bullet">
            <li>Various Cloud storage</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Applications</h2>
      <ul class="list-bullet">
        <li>Query over some data and output a DuckDB file with the results for further analysis.</li>
      </ul>    </li>
    <li><h3>From Honeycomb&apos;s O11y Book

But serverless functions and cloud object storage aren&apos;t 100% reliable. In practice, the latency at the tails of the distribution of invocation time can be significant orders of magnitude higher than the median time. That last 5% to 10% of results may take tens of seconds to return, or may never complete. In the Retriever implementation, we use impatience to return results in a timely manner. Once 90% of the requests to process segments have completed, the remaining 10% are re-requested, without canceling the still-pending requests. The parallel attempts race each other, with whichever returns first being used to populate the query result. Even if it is 10% more expensive to always retry the slowest 10% of subqueries, a different read attempt against the cloud provider&apos;s backend will likely perform faster than a &quot;stuck&quot; query blocked on S3, or network I/O that may never finish before it times out.


What happens if someone attempts to group results by a high-cardinality field? How can you still return accurate values without running out of memory? The simplest solution is to fan out the reduce step by assigning reduce workers to handle only a proportional subset of the possible groups. For instance, you could follow the pattern Chord does by creating a hash of the group and looking up the hash correspondence in a ring covering the keyspace.</h3></li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/projects_smelter</link><guid isPermaLink="false">b5a275245dbcad1fe628e0a9d6aead924720fcc2ae58425f1b582ab6e9a810f0</guid><category><![CDATA[Projects]]></category><pubDate>Tue, 27 Jun 2023 00:00:00 GMT</pubDate></item><item><title><![CDATA[Huggingface Transformers]]></title><description><![CDATA[<html><head></head><body><ul class="list-bullet">
    <li><span><span class="font-medium text-gray-800">source:</span> <span><a href="https://huggingface.co/learn/nlp-course/chapter0/1?fw=pt">https://huggingface.co/learn/nlp-course/chapter0/1?fw=pt</a></span></span></li>
    <li><h2>Pipelines</h2>
      <ul class="list-bullet">
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-from sy-python">from</span></span><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-meta sy-import-source sy-python"> <span class="sy-meta sy-import-path sy-python"><span class="sy-meta sy-import-name sy-python">transformers</span></span> <span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span></span></span></span><span class="sy-meta sy-statement sy-import sy-python"></span><span class="sy-meta sy-statement sy-import sy-python"> <span class="sy-meta sy-generic-name sy-python">pipeline</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">classifier</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">pipeline</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">sentiment-analysis<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

</span></code></pre></li>
        <li>Full list of premade pipelines: <a href="https://huggingface.co/docs/transformers/main_classes/pipelines">https://huggingface.co/docs/transformers/main_classes/pipelines</a></li>
        <li>Pipelines take a <code>model</code> argument. Some models also specify a particular pipeline to use, and in that case you can omit the pipeline name.</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">generator</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">pipeline</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">text-generation<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">model</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">distilgpt2<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
</span></code></pre></li>
        <li>Mask model for filling in words</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-from sy-python">from</span></span><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-meta sy-import-source sy-python"> <span class="sy-meta sy-import-path sy-python"><span class="sy-meta sy-import-name sy-python">transformers</span></span> <span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span></span></span></span><span class="sy-meta sy-statement sy-import sy-python"></span><span class="sy-meta sy-statement sy-import sy-python"> <span class="sy-meta sy-generic-name sy-python">pipeline</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">unmasker</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">pipeline</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">fill-mask<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">unmasker</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">This course will teach you all about &lt;mask&gt; models.<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">top_k</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">2</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

</span></code></pre></li>
        <li><h3>Pipeline Implementation</h3></li>
        <li>Each pipeline is just running the few steps needed to run a model. For example, a BERT sequence classification pipeline may do something like this:
          <ul class="list-bullet">
            <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-from sy-python">from</span></span><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-meta sy-import-source sy-python"> <span class="sy-meta sy-import-path sy-python"><span class="sy-meta sy-import-name sy-python">transformers</span></span> <span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span></span></span></span><span class="sy-meta sy-statement sy-import sy-python"></span><span class="sy-meta sy-statement sy-import sy-python"> <span class="sy-meta sy-generic-name sy-python">AutoTokenizer</span><span class="sy-punctuation sy-separator sy-import-list sy-python">,</span> <span class="sy-meta sy-generic-name sy-python">AutoModelForSequenceClassification</span></span>
<span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">torch</span></span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">text_inputs</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-sequence sy-list sy-python"><span class="sy-punctuation sy-section sy-sequence sy-begin sy-python">[</span>
    <span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">I&apos;ve been waiting for a HuggingFace course my whole life.<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-sequence sy-python">,</span>
    <span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">I hate this so much!<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-sequence sy-python">,</span>
<span class="sy-punctuation sy-section sy-sequence sy-end sy-python">]</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">checkpoint</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">distilbert-base-uncased-finetuned-sst-2-english<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span>

<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> Tokenize the input
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">AutoTokenizer</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">from_pretrained</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">checkpoint</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">inputs</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">text_inputs</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">padding</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-language sy-python">True</span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">truncation</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-language sy-python">True</span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">return_tensors</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">pt<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> Run the model
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">AutoModelForSequenceClassification</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">from_pretrained</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">checkpoint</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">outputs</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-keyword sy-operator sy-arithmetic sy-python">*</span><span class="sy-keyword sy-operator sy-arithmetic sy-python">*</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">inputs</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> Softmax to convert from logits to probabilities
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">predictions</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">torch</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-meta sy-generic-name sy-python">nn</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-meta sy-generic-name sy-python">functional</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">softmax</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">outputs</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-meta sy-generic-name sy-python">logits</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">dim</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-keyword sy-operator sy-arithmetic sy-python">-</span><span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">1</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> predictions == tensor([[4.0195e-02, 9.5980e-01],
</span><span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span>        [9.9946e-01, 5.4418e-04]], grad_fn=&lt;SoftmaxBackward&gt;)
</span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">model</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-meta sy-generic-name sy-python">config</span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-meta sy-generic-name sy-python">id2label</span></span>
<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> {0: &apos;NEGATIVE&apos;, 1: &apos;POSITIVE&apos;}
</span><span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> So the first input is positive, second input is negative.
</span></span></code></pre></li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Specific Model Types</h2>
      <ul class="list-bullet">
        <li>In addition to <code>AutoModel</code> the <code>transformers</code> library provides classes for specific model types, such as <code>BertConfig</code> and <code>BertModel</code>. In most inference-only cases <code>AutoModel</code> is fine though.</li>
        <li>When not training a model from scratch, you will usually preload a specific config.</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">BertModel</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">from_pretrained</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">bert-base-cased<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">BertTokenizer</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">from_pretrained</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">bert-base-cased<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
</span></code></pre></li>
        <li><code>from_pretrained</code> will also download the weights and related files, if needed.</li>
        <li>When you have performed additional training, you can use <code>model.save_pretrained(filename)</code> to save the config and the weights to disk.</li>
      </ul>    </li>
    <li><h2>Tokenizers</h2>
      <ul class="list-bullet">
        <li>Tokenizers can both go from raw text to token IDs with <code>tokenizer.tokenize</code> and <code>tokenizer.convert_tokens_to_ids</code>, and back from token IDs to words again with <code>tokenizer.decode</code>, which will both convert IDs to tokens and combine subword tokens into full words.</li>
        <li>The padding token ID can be retrieved from <code>tokenizer.pad_token_id</code>.</li>
        <li>Attention masks can be used to tell the model to ignore certain tokens. This usually matches to the locations of the padding tokens being <code>0</code> and everything else being <code>1</code>.</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">ids</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">torch</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tensor</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-sequence sy-list sy-python"><span class="sy-punctuation sy-section sy-sequence sy-begin sy-python">[</span><span class="sy-constant sy-language sy-python">...</span><span class="sy-punctuation sy-section sy-sequence sy-end sy-python">]</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">attention_mask</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">torch</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tensor</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-sequence sy-list sy-python"><span class="sy-punctuation sy-section sy-sequence sy-begin sy-python">[</span><span class="sy-constant sy-language sy-python">...</span><span class="sy-punctuation sy-section sy-sequence sy-end sy-python">]</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">outputs</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">ids</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">attention_mask</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">attention_mask</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
</span></code></pre></li>
        <li>Depending on the tensor framework in use, you can ask the tokenizer for different types of tensors.</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">inputs</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">texts</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">return_tensor</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">pt<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span> <span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> PyTorch
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">inputs</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">texts</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">return_tensor</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">tf<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span> <span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> TensorFlow
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">inputs</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">texts</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">return_tensor</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">np<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span> <span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> NumPy
</span></span></code></pre></li>
        <li>Tokenizers support all the standard configuration
          <ul class="list-bullet">
            <li><code>truncation=True</code> to truncate inputs longer than model context. <code>max_length=16</code> to use a custom truncation length</li>
            <li><code>padding=True</code> to pad inputs to the same length</li>
          </ul>        </li>
      </ul>    </li>
    <li><h2>Training</h2>
      <ul class="list-bullet">
        <li>To train a model, you tokenize your inputs and then add an additional <code>labels</code> property which is a tensor with the expected answer for each one.</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">torch</span></span></span>
<span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-from sy-python">from</span></span><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-meta sy-import-source sy-python"> <span class="sy-meta sy-import-path sy-python"><span class="sy-meta sy-import-name sy-python">transformers</span></span> <span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span></span></span></span><span class="sy-meta sy-statement sy-import sy-python"></span><span class="sy-meta sy-statement sy-import sy-python"> <span class="sy-meta sy-generic-name sy-python">AdamW</span><span class="sy-punctuation sy-separator sy-import-list sy-python">,</span> <span class="sy-meta sy-generic-name sy-python">AutoTokenizer</span><span class="sy-punctuation sy-separator sy-import-list sy-python">,</span> <span class="sy-meta sy-generic-name sy-python">AutoModelForSequenceClassification</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">checkpoint</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">bert-base-uncased<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">AutoTokenizer</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">from_pretrained</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">checkpoint</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">AutoModelForSequenceClassification</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">checkpoint</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">sequences</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-sequence sy-list sy-python"><span class="sy-punctuation sy-section sy-sequence sy-begin sy-python">[</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">string1<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-sequence sy-python">,</span> <span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">string2<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-sequence sy-python">,</span> <span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">etc<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-sequence sy-end sy-python">]</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">batch</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">sequences</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">padding</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-language sy-python">True</span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">truncation</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-language sy-python">True</span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">return_tensors</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">pt<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> The answers for each of the above
</span><span class="sy-meta sy-item-access sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">batch</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">labels<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">torch</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tensor</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-sequence sy-list sy-python"><span class="sy-punctuation sy-section sy-sequence sy-begin sy-python">[</span><span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">1</span><span class="sy-punctuation sy-separator sy-sequence sy-python">,</span> <span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">0</span><span class="sy-punctuation sy-separator sy-sequence sy-python">,</span> <span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">1</span><span class="sy-punctuation sy-section sy-sequence sy-end sy-python">]</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> Single step, usually a whole training loop would go here.
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">optimizer</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">AdamW</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">parameters</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">loss</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-keyword sy-operator sy-arithmetic sy-python">*</span><span class="sy-keyword sy-operator sy-arithmetic sy-python">*</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">batch</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span><span class="sy-meta sy-generic-name sy-python">loss</span>
<span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">loss</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">backward</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">optimizer</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">step</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
</span></code></pre></li>
        <li>Because <code>bert-base-uncased</code> is not originally set up for sequence classification, the library will discard the original model head and add a new randomly-weighted head for sequence classification.</li>
        <li>The <code>datasets</code> library automatically splits a dataset into training, validation, and test sets.</li>
        <li><code>dataset.features</code> describes the feature names and types, including (when applicable) the descriptions of what each label number actually means</li>
        <li>You can use <code>dataset.map</code> to tokenize while keeping all the data in the much more efficient Apache Arrow format. It also does multiprocessing and caches results.
          <ul class="list-bullet">
            <li>e.g. for a BERT next-sentence prediction:</li>
            <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-from sy-python">from</span></span><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-meta sy-import-source sy-python"> <span class="sy-meta sy-import-path sy-python"><span class="sy-meta sy-import-name sy-python">transformers</span></span> <span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span></span></span></span><span class="sy-meta sy-statement sy-import sy-python"></span><span class="sy-meta sy-statement sy-import sy-python"> <span class="sy-meta sy-generic-name sy-python">DataCollatorWithPadding</span></span>

<span class="sy-meta sy-function sy-python"><span class="sy-storage sy-type sy-function sy-python"><span class="sy-keyword sy-declaration sy-function sy-python">def</span></span> <span class="sy-entity sy-name sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tokenize</span></span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-punctuation sy-section sy-parameters sy-begin sy-python">(</span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-variable sy-parameter sy-python">row</span><span class="sy-punctuation sy-section sy-parameters sy-end sy-python">)</span></span><span class="sy-meta sy-function sy-python"><span class="sy-punctuation sy-section sy-function sy-begin sy-python">:</span></span>
  <span class="sy-keyword sy-control sy-flow sy-return sy-python">return</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-item-access sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">row</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">sentence1<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-meta sy-item-access sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">row</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">sentence2<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">truncation</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-language sy-python">True</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenized</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">dataset</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">map</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenize</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">batched</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-language sy-python">True</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> Using the collator to pad this way per batch is more efficient than padding everything to the max length across all items
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">collator</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">DataCollatorWithPadding</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-variable sy-parameter sy-python">tokenizer</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">batch_size</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">512</span>
<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">samples</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">collator</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-item-access sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenized</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">train<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-punctuation sy-separator sy-slice sy-python">:</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">batch_size</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
</span></code></pre></li>
          </ul>        </li>
        <li>With that set up, you can start your training loop using the <code>Trainer</code> class, which handles all the batching, gradient descent, etc.</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-from sy-python">from</span></span><span class="sy-meta sy-statement sy-import sy-python"><span class="sy-meta sy-import-source sy-python"> <span class="sy-meta sy-import-path sy-python"><span class="sy-meta sy-import-name sy-python">transformers</span></span> <span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span></span></span></span><span class="sy-meta sy-statement sy-import sy-python"></span><span class="sy-meta sy-statement sy-import sy-python"> <span class="sy-meta sy-generic-name sy-python">TrainingArguments</span><span class="sy-punctuation sy-separator sy-import-list sy-python">,</span> <span class="sy-meta sy-generic-name sy-python">Trainer</span><span class="sy-punctuation sy-separator sy-import-list sy-python">,</span> <span class="sy-meta sy-generic-name sy-python">AutoModelForSequenceClassification</span></span>
<span class="sy-meta sy-statement sy-import sy-python"><span class="sy-keyword sy-control sy-import sy-python">import</span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">evaluate</span></span></span>
<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> Can also pass `push_to_hub=True` to automatically push to Huggingface Hub when done
</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">training_args</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">TrainingArguments</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">directory-to-save-to<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">evaluation_strategy</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">epoch<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">AutoModelForSequenceClassification</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">from_pretrained</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">checkpoint</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">num_labels</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">2</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> A function to report metrics at the end of each `evaluation_strategy` from the TrainingArguments
</span><span class="sy-meta sy-function sy-python"><span class="sy-storage sy-type sy-function sy-python"><span class="sy-keyword sy-declaration sy-function sy-python">def</span></span> <span class="sy-entity sy-name sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">compute_metrics</span></span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-punctuation sy-section sy-parameters sy-begin sy-python">(</span></span><span class="sy-meta sy-function sy-parameters sy-python"><span class="sy-variable sy-parameter sy-python">eval_preds</span><span class="sy-punctuation sy-section sy-parameters sy-end sy-python">)</span></span><span class="sy-meta sy-function sy-python"><span class="sy-punctuation sy-section sy-function sy-begin sy-python">:</span></span>
	<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">metric</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">evaluate</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">load</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">same</span></span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">arguments</span></span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">that</span></span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">loaded</span></span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">the</span></span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">dataset</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
    <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">logits</span></span>, <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">labels</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">eval_preds</span></span>
    <span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">predictions</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">np</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">argmax</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">logits</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">axis</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-keyword sy-operator sy-arithmetic sy-python">-</span><span class="sy-constant sy-numeric sy-integer sy-decimal sy-python">1</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
    <span class="sy-keyword sy-control sy-flow sy-return sy-python">return</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">metric</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">compute</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-variable sy-parameter sy-python">predictions</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">predictions</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span> <span class="sy-variable sy-parameter sy-python">references</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">labels</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">trainer</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">Trainer</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span>
	<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">model</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span>
	<span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">training_args</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span>
	<span class="sy-variable sy-parameter sy-python">train_dataset</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-item-access sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenized</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">train<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span>
	<span class="sy-variable sy-parameter sy-python">eval_dataset</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-item-access sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenized</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">validation<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span>
  	<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> This can be skipped if using a DataCollatorWithPadding since that&apos;s the default when omitted.
</span>	<span class="sy-variable sy-parameter sy-python">data_collator</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">collator</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span>
	<span class="sy-variable sy-parameter sy-python">tokenizer</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenizer</span></span><span class="sy-punctuation sy-separator sy-arguments sy-python">,</span>
  	<span class="sy-variable sy-parameter sy-python">compute_metrics</span><span class="sy-keyword sy-operator sy-assignment sy-python">=</span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">compute_metrics</span></span>
<span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>

<span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">trainer</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">train</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
</span></code></pre></li>
        <li>Full training loop example at <a href="https://huggingface.co/learn/nlp-course/chapter3/4?fw=pt">https://huggingface.co/learn/nlp-course/chapter3/4?fw=pt</a></li>
        <li>Once the model has finished training, the Trainer will let you run the model.</li>
        <li><pre><code><span class="sy-source sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">predictions</span></span> <span class="sy-keyword sy-operator sy-assignment sy-python">=</span> <span class="sy-meta sy-function-call sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">trainer</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-punctuation sy-accessor sy-dot sy-python">.</span></span><span class="sy-meta sy-qualified-name sy-python"><span class="sy-variable sy-function sy-python"><span class="sy-meta sy-generic-name sy-python">predict</span></span></span></span><span class="sy-meta sy-function-call sy-arguments sy-python"><span class="sy-punctuation sy-section sy-arguments sy-begin sy-python">(</span><span class="sy-meta sy-item-access sy-python"><span class="sy-meta sy-qualified-name sy-python"><span class="sy-meta sy-generic-name sy-python">tokenized</span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-begin sy-python">[</span></span><span class="sy-meta sy-item-access sy-arguments sy-python"><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python"><span class="sy-punctuation sy-definition sy-string sy-begin sy-python">&quot;</span></span></span><span class="sy-meta sy-string sy-python"><span class="sy-string sy-quoted sy-double sy-python">validation<span class="sy-punctuation sy-definition sy-string sy-end sy-python">&quot;</span></span></span></span><span class="sy-meta sy-item-access sy-python"><span class="sy-punctuation sy-section sy-brackets sy-end sy-python">]</span></span><span class="sy-punctuation sy-section sy-arguments sy-end sy-python">)</span></span>
<span class="sy-comment sy-line sy-number-sign sy-python"><span class="sy-punctuation sy-definition sy-comment sy-python">#</span> { predictions: [predicted logits for each row], label_ids: [correct answers], metrics }
</span></span></code></pre></li>
      </ul>    </li>
  </ul></body></html>]]></description><link>https://imfeld.dev/notes/huggingface_transformers</link><guid isPermaLink="false">29de84798293dbbe339031c985061c00e862eb69364ebbe58eab0025fd5988f5</guid><pubDate>Sun, 14 May 2023 00:00:00 GMT</pubDate></item></channel></rss>