<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anna Villarreal</title>
    <description>The latest articles on DEV Community by Anna Villarreal (@annavi11arrea1).</description>
    <link>https://dev.to/annavi11arrea1</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png</url>
      <title>DEV Community: Anna Villarreal</title>
      <link>https://dev.to/annavi11arrea1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/annavi11arrea1"/>
    <language>en</language>
    <item>
      <title>Exploring Sentry: Reducing Load Time 🐢</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 09 Aug 2026 14:44:41 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/exploring-sentry-reducing-load-time-52gf</link>
      <guid>https://dev.to/annavi11arrea1/exploring-sentry-reducing-load-time-52gf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;🟣 It took a little bit getting used to understanding Sentry, learning how to navigate and what can be done. In my spare time over the last few weeks, I was able to do some poking around. I connected my page at annavillarreal.com since it has a fair amount of content coming in, to make it more interesting to analyze.&lt;/p&gt;





&lt;h2&gt;
  
  
  Investigation
&lt;/h2&gt;

&lt;p&gt;I explored the dashboard, trying to figure out how to get readings from my traffic, which didn't take to long to figure out. I found traces particularly interesting. Why? Because this is where I discovered an actual performance issue.  &lt;/p&gt;

&lt;p&gt;A trace shows a 4,229ms pageload. Here's what's driving the slowness:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The critical path is sequential and bottlenecked at the server:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;th&gt;Span&lt;/th&gt;
    &lt;th&gt;Duration&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.DNS&lt;/td&gt;
    &lt;td&gt;107ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.connect&lt;/td&gt;
    &lt;td&gt;553ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.TLS/SSL&lt;/td&gt;
    &lt;td&gt;281ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.request (TTFB)&lt;/td&gt;
    &lt;td&gt;2,068ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;browser.response&lt;/td&gt;
    &lt;td&gt;1,052ms&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Findings
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;browser.request&lt;/code&gt; at 2,068ms&lt;/strong&gt;. This means that server took over 2 seconds just to start sending a response. That alone is ~49% of the total trace time. &lt;/li&gt;
&lt;li&gt;Notably, &lt;code&gt;browser.response&lt;/code&gt; at 1,052ms (slow download) consumes an additional ~3.1 seconds just in the request/response cycle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The average for this pageload is ~&lt;strong&gt;194ms&lt;/strong&gt;, so the server response time being 2 seconds is the primary reason for the spike. I really don't want a new user waiting 4 or more seconds when visiting my page, that's a long time, relatively speaking.&lt;/p&gt;





&lt;h3&gt;
  
  
  Understanding the Data
&lt;/h3&gt;

&lt;p&gt;Using Seer, Sentry's built-in AI, I was able to quickly understand the information presented before me. It explained to me that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The GET &lt;a href="https://dev.to/api/articles"&gt;https://dev.to/api/articles&lt;/a&gt; call averages 361ms and hits 1.37 seconds at p95 — so it's consistently adding latency to your pageload, and on bad days it's the biggest bottleneck. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiks2yikbxxppu1whlfw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftiks2yikbxxppu1whlfw.png" alt="slow request found" width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;





&lt;ul&gt;
&lt;li&gt;Googlefonts was also eating up some pageload time:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dmmyra1jgcrdhlt5uxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dmmyra1jgcrdhlt5uxw.png" alt="slow loading google fonts" width="800" height="644"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ways to Improve
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt; Instead of the browser fetching dev.to articles on every pageload, the backend can fetch and cache them. By serving the cached result to the browser, load time is reduced. Articles don't change every second, so a background cache refresh leaves users largely unaffected.&lt;/li&gt;
&lt;li&gt;Keep the client-side fetch, but make sure it doesn't block the initial render. Load the dev.to content lazily after the page is interactive and use a loading skeleton so users see content quickly while articles load in the background. &lt;em&gt;Note: This does not make the call faster but removes it from the critical path. Makes a pleasant experience for visitors viewing non-critical data.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
  
  
  Particularly Interesting Performance Feature
&lt;/h2&gt;

&lt;p&gt;The p95 problem specifically is handled by proxy_cache_background_update + proxy_cache_use_stale: when the 15-minute TTL expires, the next visitor gets the existing copy right away while nginx refreshes behind them. Nobody waits on dev.to after the first fill, and if dev.to is down or rate-limiting, the blog section keeps rendering off the last good copy for up to a day rather than showing "could not load posts."&lt;/p&gt;

&lt;p&gt;Changing the cache has a real impact, the data over the last week proves it. Looking at the javascript-microserver project's performance over the past week I see:&lt;/p&gt;

&lt;h3&gt;
  
  
  Overall pageload (p50) trend for annavillarreal.com/
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
&lt;tr&gt;
    &lt;th&gt;Date&lt;/th&gt;
    &lt;th&gt;p50&lt;/th&gt;
    &lt;th&gt;p95&lt;/th&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Jul 31&lt;/td&gt;
    &lt;td&gt;708ms&lt;/td&gt;
    &lt;td&gt;708ms&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 1&lt;/td&gt;
    &lt;td&gt;655ms&lt;/td&gt;
    &lt;td&gt;1.6s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 2&lt;/td&gt;
    &lt;td&gt;2.1s&lt;/td&gt;
    &lt;td&gt;2.6s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 3&lt;/td&gt;
    &lt;td&gt;2.6s&lt;/td&gt;
    &lt;td&gt;3.5s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 4&lt;/td&gt;
    &lt;td&gt;3.3s&lt;/td&gt;
    &lt;td&gt;4.2s&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;Aug 5&lt;/td&gt;
    &lt;td&gt;1.3s&lt;/td&gt;
    &lt;td&gt;2.5s&lt;/td&gt;
  &lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2uh4bbmyamvc4ksjp6kq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2uh4bbmyamvc4ksjp6kq.png" alt="page load drastically cut" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's a ~60% p50 improvement on Aug 5 vs Aug 4. Fantastic.&lt;/p&gt;





&lt;h2&gt;
  
  
  Why the Cache Matters
&lt;/h2&gt;

&lt;p&gt;The page fetches articles via GET /api/devto, which internally calls &lt;a href="https://dev.to/api/articles"&gt;https://dev.to/api/articles&lt;/a&gt;. Without caching, that server-side call to dev.to was taking anywhere from 74ms to 832ms depending on the day. In this current trace, GET /api/devto completed in just 90ms, which is well below even the best prior direct-to-dev.to call. It is now being served from cache rather than hitting the external API.&lt;br&gt;
In this case, the cache particularly valuable because it insulates the page from upstream latency entirely, streamlining performance for visitors to the page.&lt;/p&gt;





&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;I really enjoyed the challenge of exploring this tool and trying to make sense of it. I can definitely see how it could lead to massive performance optimizations for end users, as I experienced it myself. I am happy I found a performance issue before many users might have the bad experience of a slow page load. Gross!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>🛻CSS Art: Smoothie Food Truck</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 09 Aug 2026 13:23:55 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/css-art-smoothie-food-truck-36gg</link>
      <guid>https://dev.to/annavi11arrea1/css-art-smoothie-food-truck-36gg</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/frontend-2026-07-29"&gt;Frontend Challenge - Comfort Food Edition, CSS Art&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspiration
&lt;/h2&gt;

&lt;p&gt;💠I was not sure I was going to enter this challenge at first, because I was not feeling inspired. I do not like to force things. Then I started to think a little outside of the normal. I remembered Space Fruit, a food vendor that is a favorite of mine to find at music festivals. Nothing beats an ice cold smoothie topped with frozen fruit bits on a sweltering, hot day. When you see Space fruit, you stop, and make a direct cut to their truck. You do not hesitate. So, after some nostalgia, I was feeling motivated remembering the days when I used to vend, selling my art and being an active member of the festival community.&lt;/p&gt;





&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;small&gt;Change preview zoom to .5 &lt;/small&gt;&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/editor/AnnaVi11arrea1/embed/019fe698-300e-79e2-a685-4752dfb367c5?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Here is a screenshot of it working on codepen, it might not be great on some devices:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmbmhl6yw6ahndw5i0hv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvmbmhl6yw6ahndw5i0hv.png" alt="working on codepen" width="800" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I start each idea generally by blocking out the approximate size and shape of the object I want to add. &lt;/li&gt;
&lt;li&gt;I use gradients, box shadows, and layering to add depth and dimension.&lt;/li&gt;
&lt;li&gt;A smattering of javascript was used to randomly place stars and have them twinkle.&lt;/li&gt;
&lt;li&gt;Found a way to imitate an LED strip with some fancy background gradient patterns.&lt;/li&gt;
&lt;li&gt;Pushed myself to create a wrinkling effect on the tablecloth to imitate fabric.&lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;⭐ I made a timelapse for you guys, so I'll leave that as my journey! It's sometimes hard to imagine how much work someone puts into something without evidence. A video will communicate this pretty quickly, plus it is also satisfying to see progress happen so quickly. The biggest challenge was getting the window cut out in the back of the food truck, so you could see the stars on the other side, just like in real life. &lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/WsTfEQJA908"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;





&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Github Repo: &lt;a href="https://github.com/AnnaVi11arrea1/cssart" rel="noopener noreferrer"&gt;cssart&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
      <category>programming</category>
    </item>
    <item>
      <title>Catbot Development Series</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 01 Aug 2026 16:13:32 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/catbot-development-series-nlf</link>
      <guid>https://dev.to/annavi11arrea1/catbot-development-series-nlf</guid>
      <description>&lt;h2&gt;
  
  
  Sequence of Articles Related to Catbot's Developement
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;Updated on an ongoing basis&lt;/em&gt;
&lt;/h3&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-story__hidden-navigation-link"&gt;🐈Catbot: The Custom AI Harness That Lives on Desktop ᓚᘏᗢ&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;DEV Weekend Challenge: Passion Edition Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/annavi11arrea1" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" alt="annavi11arrea1 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/annavi11arrea1" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Anna Villarreal
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Anna Villarreal
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                
              
              &lt;div id="story-author-preview-content-4129482" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/annavi11arrea1" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Anna Villarreal&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" id="article-link-4129482"&gt;
          🐈Catbot: The Custom AI Harness That Lives on Desktop ᓚᘏᗢ
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/weekendchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;weekendchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;11&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-story__hidden-navigation-link"&gt;🐈‍⬛Check Out Catbot's New Leg!&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;The evolution from sketch to robo-leg&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/annavi11arrea1" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" alt="annavi11arrea1 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/annavi11arrea1" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Anna Villarreal
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Anna Villarreal
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                
              
              &lt;div id="story-author-preview-content-4227517" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/annavi11arrea1" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Anna Villarreal&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jul 25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" id="article-link-4227517"&gt;
          🐈‍⬛Check Out Catbot's New Leg!
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/learning"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;learning&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/pet"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;pet&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;22&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              7&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;



&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-story__hidden-navigation-link"&gt;Catbot: Custom Grammar Problem Fixed&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Summer Bug Smash: Smash Stories 🐛🛹&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/annavi11arrea1" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" alt="annavi11arrea1 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/annavi11arrea1" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Anna Villarreal
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Anna Villarreal
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
                
              
              &lt;div id="story-author-preview-content-4288467" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/annavi11arrea1" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1467610%2F3e4ce925-fffd-4643-9aae-8ee77cf01828.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Anna Villarreal&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 1&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" id="article-link-4288467"&gt;
          Catbot: Custom Grammar Problem Fixed
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/bugsmash"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;bugsmash&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/catbot"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;catbot&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;53&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              22&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            10 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Catbot: Custom Grammar Problem Fixed</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 01 Aug 2026 16:03:02 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5</link>
      <guid>https://dev.to/annavi11arrea1/catbot-custom-grammar-problem-fixed-oc5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Smash Stories&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Sometimes we discover our limitations through a series of failures. We don’t understand why something is not working, despite having tried multiple improvements and optimizations. My desktop pet, Catbot, is no exception. I had issues with Catbot cutting me off or not letting me finish before it started talking. It would constantly tell me that I was calling it cat bought and not Catbot. This was an actual pain in the rear. Catbot would spend a lot of time explaining this to me. &lt;/p&gt;

&lt;p&gt;I was able to tweak the listening window, so that Catbot would stop listening after a 1.5 second pause. This gives me enough time to finish my sentence, given my natural pauses and end of sentences. Controlling the pause gap helped tremendously. Going from .5 seconds to 1.5 seconds made all the difference in the world.&lt;/p&gt;

&lt;p&gt;After adding a better layer of control to Catbot’s listening skills, I still had difficulties using Catbot’s name. It thought I was saying “Catfight”, “Cat bought”, and even “Lawrence” one time. This will seem brief to the reader but it was only after multiple sessions of frustration that I came to the realization that perhaps my webcam mic was not a great listening tool for Catbot - as it picks up all sounds in the room. After some quick googling, I was very convinced that a better mic would help. I popped on my fancy cat ear headset and not to my surprise, Catbot could understand what I was saying much better. We need to make sure we are using the best hardware for the job. My headset mic is a close range omni directional mic that does a good job leaving out all the background noise.&lt;/p&gt;

&lt;p&gt;However, Catbot refused to respond to its name and would even argue with me! It would tell me that it wants to be called by it's actual name, Catbot. (Cat bot) So everytime I said its name, there was a debate and some long side tangent dialogue to follow. This was an awful experience.&lt;/p&gt;

&lt;p&gt;THE BUG: Catbot not listening correctly.&lt;/p&gt;





&lt;h2&gt;
  
  
  DEMO
&lt;/h2&gt;


&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/Nhddz9BwU8g"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;


&lt;p&gt;This is an interesting type of bug, not the typical software battle, but rather a conceptual bug. The type of situation where massive learning happens. &lt;/p&gt;





&lt;h2&gt;
  
  
  Much Ado About Graphs
&lt;/h2&gt;

&lt;p&gt;I discovered that runtime graphs are not supported by vosk-model-en-us-0.22, which was the preferred, larger  model. The training graphs were being ignored. Which means training Catbot on how I say “catbot” is ignored. &lt;em&gt;This is an outrage.&lt;/em&gt; The decoding graph is pre-composed into one frozen file, and there is nowhere to swap a grammar into. &lt;/p&gt;

&lt;p&gt;Transversely, a model that does support custom grammar is vosk-model-en-us-0.22-lgraph. However, It is important that we are not really adding custom commands, but controlling the output choices available. These feel like custom commands but they are better stated as custom directions, or disallow lists.&lt;/p&gt;

&lt;p&gt;Catbot uses kaldi, which decodes against a single finite state transducer, HCLG, built by composing 4 layers: H, C, L, G. I will discuss the G layer.&lt;/p&gt;

&lt;p&gt;G is the grammar / language model, which maps words to words. It assigns a cost to every possible word sequence. The decoder then picks a winner. A score is a negative log probability, and every arc in the graph carries weight, which add along the path. Negative log probabilities mean that a lower number is better, and adding costs is multiplying probabilities. Here is the english version:&lt;/p&gt;

&lt;p&gt;Total cost = &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;Acoustic cost&lt;/th&gt;
&lt;th&gt;Language cost&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;“How well do these phones match the audio frames?”&lt;/td&gt;
&lt;td&gt;“How likely is anyone to say this sentence in the first place?”&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Facts about the situation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Neither of these alone is useful! &lt;/li&gt;
&lt;li&gt;The Acoustics can not differentiate “ice cream” from “I scream”. They are the same sounds. &lt;/li&gt;
&lt;li&gt;The language model does not hear. The decoder searches for the sequence minimizing the sum, which is why an LM’s job is best described as scoring sequences. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But wait... why does this situation feel like Helen Keller? Because it's a similar situation.&lt;/p&gt;

&lt;p&gt;In the real life story of Helen Keller and Anne Sullivan, there was a communication problem. An intermediary interface must bridge two completely different sensory and data modalities. In computer science, this is called multimodal alignment. This method is just like the tactile “finger-spelling” method that connected Hellen Keller to the world.&lt;/p&gt;

&lt;p&gt;It was not until realizing I needed two different models to handle specific use cases that I realized this fundamental problem of having AI process real analogue data, which has now placed itself at the forefront of my curiosity and this project.&lt;/p&gt;

&lt;p&gt;A simple table will help you wrap your head around this idea without needing to explain too much:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
    &lt;thead&gt;
        &lt;tr&gt;
            &lt;th&gt;Element&lt;/th&gt;
            &lt;th&gt;Helen Keller&lt;/th&gt;
            &lt;th&gt;Anne Sullivan (The Teacher)&lt;/th&gt;
            &lt;th&gt;The Outside World&lt;/th&gt;
        &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;td&gt;AI Role&lt;/td&gt;
            &lt;td&gt;Acoustics Model (Vosk)&lt;/td&gt;
            &lt;td&gt;The Alignment Interface (Your Code Pipeline)&lt;/td&gt;
            &lt;td&gt;Language Model (LLM)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Data Type&lt;/td&gt;
            &lt;td&gt;Raw sensory inputs (audio waves/frequencies).&lt;/td&gt;
            &lt;td&gt;The translator bridging two completely different modalities.&lt;/td&gt;
            &lt;td&gt;Symbolic language (written words/tokens).&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Function&lt;/td&gt;
            &lt;td&gt;Perceives physical sounds but completely lacks semantic context or logic.&lt;/td&gt;
            &lt;td&gt;Maps raw physical acoustic patterns directly into structured text blocks.&lt;/td&gt;
            &lt;td&gt;Processes structured text tokens logically but cannot directly "hear" physical waves.&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let’s take a look at how words are “chosen”:&lt;/p&gt;

&lt;p&gt;A state in G represents history, the last n-1 words, and each outgoing arc is a next possible word, weighted by -log P(word | history):&lt;/p&gt;

&lt;p&gt;“The cat” –sat (cost 4.1)--&amp;gt;&lt;br&gt;
               –ate (cost 5.3)--&amp;gt;&lt;br&gt;
              –sad (cost 9.8)--&amp;gt;&lt;/p&gt;

&lt;p&gt;…So basically words are chosen via likeliness. Catbot is not a real word, so catbot was never going to understand it as such, thus interpreting “Catbot engage” as “kappa engage” and other oddity phrases that would make sense to the model as it is trained.&lt;/p&gt;

&lt;p&gt;I had Claude synthesize some phrases for me, and it proves the issue I am having plain as day.&lt;/p&gt;

&lt;p&gt;SPOKEN: Catbot, engage.&lt;br&gt;
    1. conf  114.1   kappa engage&lt;br&gt;
    2. conf  112.1   cappa engage&lt;/p&gt;

&lt;p&gt;Kappa engage (a.k.a. “Catbot, engage.”) is a vocabulary trap. There is no path through G that emits Catbot, so the decoder does the only thing it can, it finds the closest match to the sound of the word. This is why saying “Catbot” failed EVERY. SINGLE. TIME. This speech recognition bug lies in the grammar available. The word “Catbot” is not really an actual word. I made that up. This is an infinite impossibility. &lt;/p&gt;

&lt;p&gt;Shortly before this deep dive, I told Catbot to respond to “CB”. This is English alphabet, real letters that have to exist. Catbot is now configured to respond to CB. When I say CB, it knows I am talking about it. Unmistakable. Functional. As a quick fix!&lt;/p&gt;

&lt;p&gt;As stated previously, we can disallow the decoder to consider certain words, such as “kappa” as noted above. &lt;/p&gt;

&lt;p&gt;If I say “Catbot, engage” without restrictions, the model hears “Kappa engage”. If we restrict the model, disallowing “kappa”, Catbot will hear “cat bot engage”. Since both “cat” and “bot” are commonly used words, they are available to the model.&lt;/p&gt;

&lt;p&gt;As you can see, training an interactive voice model is a long journey full of mysteries. From bad hardware to understanding model boundaries, sometimes we can actually gain more, with less! (Less meaning removing words from vocabulary, in this case.) &lt;/p&gt;

&lt;p&gt;And this can only be done by using the vosk-model-en-us-0.22-lgraph alongside vosk-model-en-us-0.22. This gives Catbot the tools it needs to respond in ways that make sense to custom words.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code Samples
&lt;/h2&gt;

&lt;p&gt;So then, I Implemented a dual recognizer into Catbot:&lt;/p&gt;

&lt;p&gt;Catbot_voice.py&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
 &lt;span class="mi"&gt;44&lt;/span&gt;  &lt;span class="n"&gt;KOKORO_VOICES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;voices-v1.0.bin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;45&lt;/span&gt;  &lt;span class="n"&gt;VOSK_BIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-en-us-0.22-lgraph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;46&lt;/span&gt;  &lt;span class="n"&gt;VOSK_SMALL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-small-en-us-0.15&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;47&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# Dynamic-graph model — the only kind that accepts a runtime grammar. A model
&lt;/span&gt; &lt;span class="mi"&gt;48&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# that ships a pre-composed graph/HCLG.fst has no seam to insert one into;
&lt;/span&gt; &lt;span class="mi"&gt;49&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# Vosk logs "Runtime graphs are not supported by this model" and decodes
&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# freely, which is worse than having no grammar recognizer at all. Detected by
&lt;/span&gt; &lt;span class="mi"&gt;51&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# the presence of graph/Gr.fst (the language model kept as a separate file).
&lt;/span&gt; &lt;span class="mi"&gt;52&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;VOSK_DYNAMIC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-en-us-0.22-lgraph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;53&lt;/span&gt;
 &lt;span class="mi"&gt;54&lt;/span&gt;  &lt;span class="n"&gt;SAMPLE_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt;
 &lt;span class="mi"&gt;55&lt;/span&gt;  &lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://localhost:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CATBOT_PORT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8800&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt; &lt;span class="mi"&gt;102&lt;/span&gt;          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;103&lt;/span&gt;  &lt;span class="n"&gt;RE_LEARNED_WAKE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_load_learned_wake&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="mi"&gt;104&lt;/span&gt;
 &lt;span class="mi"&gt;105&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# ---- grammar-constrained wake/command recognizer ------------------------
&lt;/span&gt; &lt;span class="mi"&gt;106&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# A SECOND recognizer decodes the same audio against nothing but the phrases
&lt;/span&gt; &lt;span class="mi"&gt;107&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# below. It can't hear anything else, so "catbot engage" can't come out as
&lt;/span&gt; &lt;span class="mi"&gt;108&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# "kappa engage" — that word doesn't exist in its graph. Anything Anna says
&lt;/span&gt; &lt;span class="mi"&gt;109&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# that isn't a listed phrase decodes as "[unk]", which is exactly why this
&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# only ever SUPPLEMENTS the free recognizer (which still handles all
&lt;/span&gt; &lt;span class="mi"&gt;111&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# conversation) instead of replacing it.
&lt;/span&gt; &lt;span class="mi"&gt;112&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;#
&lt;/span&gt; &lt;span class="mi"&gt;113&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# Every word must be in the model's vocabulary or Vosk silently drops it:
&lt;/span&gt; &lt;span class="mi"&gt;114&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# "catbot" is not an English word, but "cat bot" is two of them. That's the
&lt;/span&gt; &lt;span class="mi"&gt;115&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# whole reason his name is spelled out here — _build_grammar checks each word
&lt;/span&gt; &lt;span class="mi"&gt;116&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# against the model and logs anything it had to drop.
&lt;/span&gt; &lt;span class="mi"&gt;117&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;WAKE_NAME_FORMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat pot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat bought&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat bottom&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cat but&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;118&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                   &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catfight&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;combat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cabot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kat bot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cap pot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;119&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# what can follow his name (empty = just the name, which wakes him too)
&lt;/span&gt; &lt;span class="mi"&gt;120&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;GRAMMAR_TAILS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;engaged&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wake up&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wake&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;121&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;are you listening&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;are you there&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;you there&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;122&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;go to sleep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sleep&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stop listening&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;123&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;be quiet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quiet&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shut up&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hush&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="mi"&gt;124&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;close terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;approve&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;skip&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;125&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="c1"&gt;# phrases that stand alone, with or without his name
&lt;/span&gt; &lt;span class="mi"&gt;126&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;GRAMMAR_BARE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;close terminal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;127&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;128&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;129&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_build_grammar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;130&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;JSON phrase list for the constrained recognizer, filtered to words the
 131 +    model actually knows. Returns None if nothing usable survived.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
 &lt;span class="mi"&gt;132&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WAKE_NAME_FORMS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;133&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="c1"&gt;# whatever wake_training.py learned from Anna's own mic counts too
&lt;/span&gt; &lt;span class="mi"&gt;134&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;135&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;learned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catbot_wake_variants.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
 &lt;span class="mi"&gt;136&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;names&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;learned&lt;/span&gt;
 &lt;span class="mi"&gt;137&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;138&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;139&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;pass&lt;/span&gt;
 &lt;span class="mi"&gt;140&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;141&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;phrases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;lead&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;tail&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="mi"&gt;142&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;               &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;lead&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hey &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;143&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;               &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;names&lt;/span&gt;
 &lt;span class="mi"&gt;144&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;               &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tail&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;GRAMMAR_TAILS&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="mi"&gt;145&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;GRAMMAR_BARE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;146&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;147&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
 &lt;span class="mi"&gt;148&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;in_vocab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;149&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;150&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;151&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;vosk_model_find_word&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
 &lt;span class="mi"&gt;152&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;153&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;      &lt;span class="c1"&gt;# can't check — assume fine
&lt;/span&gt; &lt;span class="mi"&gt;154&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;known&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="mi"&gt;155&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;156&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dropped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="mi"&gt;157&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;phrase&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phrases&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="mi"&gt;158&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;missing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;in_vocab&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
 &lt;span class="mi"&gt;159&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;160&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;161&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;continue&lt;/span&gt;
 &lt;span class="mi"&gt;162&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;163&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;164&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grammar: dropped phrases using words the model doesn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;165&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;know: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;166&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;167&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;168&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="c1"&gt;# "[unk]" is the escape hatch: it lets the decoder say "that wasn't any of
&lt;/span&gt; &lt;span class="mi"&gt;169&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="c1"&gt;# these" instead of forcing Anna's conversation into the nearest command
&lt;/span&gt; &lt;span class="mi"&gt;170&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kept&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[unk]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
 &lt;span class="mi"&gt;171&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;172&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;173&lt;/span&gt;  &lt;span class="c1"&gt;# in-conversation commands
&lt;/span&gt; &lt;span class="mi"&gt;174&lt;/span&gt;  &lt;span class="n"&gt;PROVIDER_WORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="mi"&gt;175&lt;/span&gt;      &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As well as…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;
 &lt;span class="mi"&gt;470&lt;/span&gt;          &lt;span class="c1"&gt;# Lower CATBOT_END_SILENCE = faster to answer but more likely to cut you
&lt;/span&gt; &lt;span class="mi"&gt;471&lt;/span&gt;          &lt;span class="c1"&gt;# off mid-sentence; higher = more patient. 0.65s is a good balance.
&lt;/span&gt; &lt;span class="mi"&gt;472&lt;/span&gt;          &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;end_silence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CATBOT_END_SILENCE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.65&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="mi"&gt;473&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;474&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# Second recognizer, constrained to the wake/command grammar above.
&lt;/span&gt; &lt;span class="mi"&gt;475&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# Runs on its own dynamic-graph model so the big model keeps handling
&lt;/span&gt; &lt;span class="mi"&gt;476&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# conversation with its full rescoring stack. Cost, measured: ~200 MB
&lt;/span&gt; &lt;span class="mi"&gt;477&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# of RAM and roughly DOUBLE the decode CPU (4.3 -&amp;gt; 8.0 ms per 62 ms
&lt;/span&gt; &lt;span class="mi"&gt;478&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# block) — the grammar shrinks the search, but a second acoustic model
&lt;/span&gt; &lt;span class="mi"&gt;479&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# still runs on every block. Still only 0.13x realtime, so it adds no
&lt;/span&gt; &lt;span class="mi"&gt;480&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="c1"&gt;# latency; set CATBOT_GRAMMAR=0 if that ever stops being true.
&lt;/span&gt; &lt;span class="mi"&gt;481&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_grec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;482&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CATBOT_GRAMMAR&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; \
 &lt;span class="mi"&gt;483&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;off&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="mi"&gt;484&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;485&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CATBOT_GRAMMAR_MODEL&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
 &lt;span class="mi"&gt;486&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;gdir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;VOSK_DYNAMIC&lt;/span&gt;
 &lt;span class="mi"&gt;487&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Gr.fst&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
 &lt;span class="mi"&gt;488&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="mi"&gt;489&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grammar recognizer OFF: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; has no dynamic &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;489&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;graph (graph/Gr.fst) — runtime grammars need one&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;490&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;491&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="c1"&gt;# keep the Model referenced for as long as the recognizer
&lt;/span&gt; &lt;span class="mi"&gt;492&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="c1"&gt;# lives (the binding only borrows its handle)
&lt;/span&gt; &lt;span class="mi"&gt;493&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vosk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="mi"&gt;494&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;grammar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_build_grammar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;495&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;grammar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;496&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no usable grammar phrases&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;497&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_grec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vosk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;KaldiRecognizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="mi"&gt;498&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SAMPLE_RATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grammar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;499&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Grammar recognizer ready (&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;gdir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;500&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grammar&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; phrases)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;501&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;            &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
 &lt;span class="mi"&gt;502&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;grammar recognizer failed to load — &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
 &lt;span class="mi"&gt;503&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;continuing with the free recognizer only&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="mi"&gt;504&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_grec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_gmodel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
 &lt;span class="mi"&gt;505&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
 &lt;span class="mi"&gt;506&lt;/span&gt;          &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_kokoro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Kokoro&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KOKORO_ONNX&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;KOKORO_VOICES&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
 &lt;span class="mi"&gt;507&lt;/span&gt;          &lt;span class="c1"&gt;# warm up the TTS so the FIRST spoken reply isn't slowed by cold-start
&lt;/span&gt; &lt;span class="mi"&gt;508&lt;/span&gt;          &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So basically, both models run in parallel, as two arguments, against the same regexes. The grammar model helps with commands and the bigger model is used for more conversational interactions. This effectively makes Catbot a more efficient desktop companion. It is now much more pleasant to hang out with Catbot.&lt;/p&gt;





&lt;h2&gt;
  
  
  Learning
&lt;/h2&gt;

&lt;p&gt;This entire project, it's all learning, all new territory. Tuning Catbot's ears is a real challenge. I had an awakening about hardware limitations, and an enlightening experience about why different models are used and for what purpose. Since I cannot recompose the larger model with new grammar, I can use the lgraph model on the side to handle custom needs more gracefully. When I made the connection to Helen Keller, I understood the whole situation from a very global perspective. It's not really something I could fully understand without diving into chaos.&lt;/p&gt;

&lt;p&gt;Thank you for checking out Catbot's progress!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>catbot</category>
      <category>ai</category>
    </item>
    <item>
      <title>🐈‍⬛Check Out Catbot's New Leg!</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 25 Jul 2026 01:40:56 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg</link>
      <guid>https://dev.to/annavi11arrea1/check-out-catbots-new-leg-3cfg</guid>
      <description>&lt;p&gt;This is fun. I absolutely must share.&lt;/p&gt;

&lt;p&gt;So I recently created a desktop pet, called Catbot. Catbot is voice activated and has memory in an obsidian vault that it shares with models of choice as needed. Catbot is a harness that feels like a pet. I quickly built Catbot a body using AI and was excited to see the result was a furry creature with a robotic leg, which I had asked for. This worked for messing around and building Catbot to start. Here are some images I generated with ai in a previous week, just to catch you up to speed:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsxfgg3tro3w0tpe4s6c4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsxfgg3tro3w0tpe4s6c4.png" alt="awkward rendering, but pretty on point" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;
Awkward initial rendering needing refinement



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5uazzg5v337off5q0fi8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5uazzg5v337off5q0fi8.png" alt="high level technical drawing" width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;
A high level mock technical drawing generated by Gemini I used to generate Catbot's original body



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp525lgzgaudl2ol5135y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp525lgzgaudl2ol5135y.png" alt="Catbot's first 3D body" width="452" height="488"&gt;&lt;/a&gt;&lt;/p&gt;
Original Catbot appearance.







&lt;p&gt;But that badly represented robotic pet leg was getting on my nerves. I knew it had to be better. In the back of my head I was like you know what, it needs a way better leg. I let it fester in my head for a while. Then finally, I couldn’t take it anymore. The next moment of free time I sat down and drew up what I thought a robotic pet leg might look like. I drew this quickly, in about 15 minutes or so. I was happy with the overall idea, and believed it made sense. Here is my drawing:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxkysw0u5inpvdjqsmmyt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxkysw0u5inpvdjqsmmyt.jpg" alt="Catbot leg idea" width="800" height="790"&gt;&lt;/a&gt;&lt;/p&gt;
My original drawing







&lt;p&gt;After creating the drawing, I knew that I had to have multiple views to have enough data for ai to create a working prototype. I gave my drawing to Gemini using the following prompt:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“I created this robotic cat leg and I need some good technical drawings of top, side view, front view and back. Can you make me a good technical drawing I can use to share my idea? “&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And having luck previously with a different type of schematic (the original Catbot) I knew that this would at least produce something marginally workable. I was absolutely blown away by the mock schematic it produced for me. It is freaking beautiful, exactly what I wanted!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc09c7kayl99aj6x9uqc8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc09c7kayl99aj6x9uqc8.png" alt="Really awesome robotic leg schematic for Catbot" width="800" height="790"&gt;&lt;/a&gt;&lt;/p&gt;
Nice looking technical drawing by Gemini.







&lt;p&gt;I then gave the schematic with a detailed description to opus for creation using existing Catbot architecture, (using three.js) to create the fantastic leg for me. It did a pretty good job!&lt;/p&gt;

&lt;p&gt;I’ve added a video so you can see an interaction with Catbot. It’s entertaining until it frustrates you. Work in progress for sure. LOL. It was difficult to get Catbot's opintion of it's new leg:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/nc5-3uJkZAY"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Catbot is a brat. Do not give your AI pet sass. (You know, unless you can deal with it.) 😂&lt;/p&gt;

&lt;p&gt;And let's not forget where we started:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4i50qlej8sa9spi8cfe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk4i50qlej8sa9spi8cfe.png" alt="Catbot the first" width="279" height="215"&gt;&lt;/a&gt;&lt;/p&gt;
Catbot the first.



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv2vli5prckqjohi025j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffv2vli5prckqjohi025j.png" alt="Catbot the second" width="438" height="411"&gt;&lt;/a&gt;&lt;/p&gt;
Catbot the second.



&lt;p&gt;So I guess this latest Catbot, is Catbot the 4th. &lt;/p&gt;

&lt;p&gt;Has anyone else created an AI pet?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>learning</category>
      <category>pet</category>
    </item>
    <item>
      <title>FLUX on a 4070 Graphics Card 🖼️</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 18 Jul 2026 14:32:08 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/flux-on-a-4070-graphics-card-297</link>
      <guid>https://dev.to/annavi11arrea1/flux-on-a-4070-graphics-card-297</guid>
      <description>&lt;h2&gt;
  
  
  Preliminary
&lt;/h2&gt;

&lt;p&gt;In the past, I have tried a few things to get local video and image generation to work to my satisfaction. I was irritated for a while that my older macbook didn’t have an M chip because I know you can do some cool stuff with Ollama. But I am not buying a computer every year. That’s unrealistic and wasteful. One of my recent stunts involved trying to get fable to setup a workflow on comfyUI. While that’s novel and impressive-looking, the output was poor at best and I was not amused. Node confusion, half of them in languages I cannot read. I already have too many projects, I’m not about to decipher foreign languages on the fly or watch 30 node handling videos on Youtube over the course of 3 months. Time is a real factor here.&lt;/p&gt;

&lt;p&gt;With the 3rd extension of free fable use I’m squeezing every last drop out of it. I’m not wasting it either - I’ve got it dropping all kinds of notes to my vault so I can reproduce things on my own later. And honestly, that’s serious real world value for my workflow.&lt;/p&gt;

&lt;p&gt;Before I ramble, I must give credit to Youtuber Richard Aragon for sharing his magnificent google colab notebook. I stumbled into this whole process on accident looking for videos about quantization in an effort to know more about what the eff is quantization really. Little did I know this attempted deep dive would lead me down a path to resolve a personal problem of local generation. By the way, he mentions being able to do this on a 4060, so guys, all is not lost if you have a little bit of patience and good timing. I have come to find out that as developers we have unparalleled patience. So then, this post is for you. If I can save a single person from massive frustration, then I have done my job. It is better to share than to hoard. Because, like Richard Aragon, I would much prefer to share and help. &amp;lt;3&lt;/p&gt;





&lt;h2&gt;
  
  
  Start Your Own Workflow
&lt;/h2&gt;

&lt;p&gt;Here is Richard’s Youtube link and the google colab is in the notes: &lt;a href="https://www.youtube.com/watch?v=gjkVW3qOUMA%22" rel="noopener noreferrer"&gt;AI Quantization Easily Explained&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is really amazing is that I discovered I can use a quantized model locally and train it on my own content. As an artist, I was extremely curious to see how AI might create images for me in my art style. Mission accepted!&lt;/p&gt;

&lt;h3&gt;
  
  
  Here is EXACTLY what I did with no shame:
&lt;/h3&gt;

&lt;p&gt;❤️ I gave Fable the link to the google colab notebook, explaining what I was trying to accomplish. Fable fetched the data from the notebook and created it locally - a private copy of the workflow for me to modify as I wish.&lt;br&gt;
🧡 Using the 34GB  base model from Hugging Face, I am now able to train it on my own images to create my own style. This is accomplished by using a LoRA file which acts as a “lense” that attaches to the base model. This means that you can train it to have many lens (styles!) on your local workflow.  In his video, Richard uses pieces from the late Alphonse Mucha (Famous artist if you haven’t heard) to train the model. The results were on point, and even after quantization they still held enough data to hold onto most defining characteristics.  Using this knowledge, I ran the same tests as him for a control.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One test without training.&lt;/li&gt;
&lt;li&gt;One test with training.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💛 After testing, I knew the structure was in place for experimentation. The first thing I needed to do was to create a folder with my artwork, 14 images, and a metadata.jsonl file. (made my first jsonl file from scratch yesterday, cool haha). This metadata pairs the description of the image to the actual image so FLUX can interpret it for training.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demystification
&lt;/h2&gt;

&lt;p&gt;Here is essentially how we get a 12GB card to handle a 24 GB model. I’ve simplified the terminology for learning and demystification purposes:&lt;/p&gt;

&lt;p&gt;💚 &lt;strong&gt;Shrink/Quantization&lt;/strong&gt; Specifically 4-bit NF4 quantization (NormalFloat4), done by the bitsandbytes library. "8-bit" and "4-bit" refer to how many bits store each model weight.            &lt;/p&gt;

&lt;p&gt;🩵 &lt;strong&gt;Freeze/Parameter-efficient fine-tuning&lt;/strong&gt; (PEFT), specifically LoRA (Low-Rank Adaptation). The base model's parameters are frozen, so you train small low-rank adapter matrices instead.  &lt;/p&gt;

&lt;p&gt;💜 &lt;strong&gt;Precompute/Embedding&lt;/strong&gt; caching and and latent caching.                                                                                                &lt;/p&gt;

&lt;p&gt;🩷 &lt;strong&gt;Right-size/No single fancy word&lt;/strong&gt; Memory footprint management: lower training resolution, batch size 1 with gradient accumulation, gradient checkpointing (recompute instead of store), and an 8-bit optimizer. Each trades a little speed or precision for a lot of memory.  &lt;/p&gt;

&lt;p&gt;No fancy UI at the moment (...already have plan for that 😂) but using the terminal and running the python script afterwards with optional parameters offers additional flexibility of image generation. &lt;/p&gt;




&lt;h2&gt;
  
  
  Here are the generations from my first local tests
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Control 1 - untrained
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzci6u1cp86zw5z50k9di.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzci6u1cp86zw5z50k9di.png" alt="untrained local" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Control 2 - Alphonse Mucha
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkea1ajslb3fvlzgtcctd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkea1ajslb3fvlzgtcctd.png" alt="trained control" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Training 1 - 100 step artwork (fast, minutes)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1pzdvu3mhntdgidwug6z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1pzdvu3mhntdgidwug6z.png" alt="14 pieces of art" width="493" height="534"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Test 1 - 100 step artwork (EVERFLUO style)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdrlf3ivariie4mwvi4fb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdrlf3ivariie4mwvi4fb.png" alt="custom baseline" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Training 2 - 700 step artwork (over an hour)
&lt;/h3&gt;
&lt;h3&gt;
  
  
  Test 2 - 700 step artwork (EVERFLUO style)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwp9593i2x4i7tvewt596.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwp9593i2x4i7tvewt596.png" alt="refined model style" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;
Much closer resemblance to style.






&lt;h3&gt;
  
  
  Test 3 - Ran EVERFLUO style with very custom prompt to see limitations, needs work/additional training to be extra fancy.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwe8knlap1ewwuifjv1i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdwe8knlap1ewwuifjv1i.png" alt="Pushing model outside of training" width="799" height="520"&gt;&lt;/a&gt;&lt;br&gt;
Pushing model outside of training to see what happens.&lt;/p&gt;




&lt;h2&gt;
  
  
  What about video?
&lt;/h2&gt;

&lt;p&gt;I was able to piggy-back off of the pre-existing image generation setup to build a video generation workflow. I am currently taking a stab at a 5 second video on the 4070 on one of my paintings. It's going to be at least an hour and I will update this post.&lt;/p&gt;

&lt;p&gt;Here is the first video!&lt;/p&gt;


&lt;div&gt;
    &lt;iframe src="https://www.youtube.com/embed/plcdkiTGEZY"&gt;
    &lt;/iframe&gt;
  &lt;/div&gt;






&lt;p&gt;On that note, I'll survive another year without a 5090, maybe XD.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>image</category>
      <category>video</category>
      <category>local</category>
    </item>
    <item>
      <title>Bitarazzi Short Film: Imperfect Awesomeness🌱</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Thu, 16 Jul 2026 01:07:02 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/bitarazzi-short-film-imperfect-awesomeness-5ceb</link>
      <guid>https://dev.to/annavi11arrea1/bitarazzi-short-film-imperfect-awesomeness-5ceb</guid>
      <description>&lt;p&gt;I want to give you spoilers but you must watch it first. Here's basically what happened. &lt;/p&gt;

&lt;p&gt;Firstly, this project was for fun.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The lyrics where generated by Claude first with a prompt including what we were after. &lt;/li&gt;
&lt;li&gt;The music was generated by Suno after telling it we wanted something that sounded like "Paparazzi" by Lady Gaga, shortly after coining the term "bitarazzi"&lt;/li&gt;
&lt;li&gt;The videos and images were generated by Gemini and I ended up developing a character, or two.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I found myself creating a story with meaning in the video, that related to my own life as I pieced it together in 10 second spurts (3 ten second videos per day on Gemini free tier, don't mess up or you have to wait!)&lt;/p&gt;

&lt;p&gt;AI is not all evil or bad. To me, I see it as a creative outlet. It's fun. Sure (Spoiler alert!!!!!) she might be plugging the ethernet cord in upside down, but the idea is generally communicated in a decent way.&lt;/p&gt;

&lt;p&gt;I completed this in the mornings as I had time over the course of a few weeks. I could refine it forever, but I think it's cool how fast you can buzz along with a creative project/prototype/idea with AI. So cool. They didn't have this when I was a teenager!&lt;/p&gt;

&lt;p&gt;I did actual human editing in adobe premier to string it together in a way that semi made sense! Hope you like the bitarazzi debut, haha!&lt;/p&gt;

&lt;p&gt;Thank you &lt;a class="mentioned-user" href="https://dev.to/trickell"&gt;@trickell&lt;/a&gt; for your help and inspiration! &amp;lt;3&lt;/p&gt;

</description>
      <category>ai</category>
      <category>gemini</category>
      <category>claude</category>
    </item>
    <item>
      <title>🐈Catbot: The Custom AI Harness That Lives on Desktop ᓚᘏᗢ</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Mon, 13 Jul 2026 04:55:27 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb</link>
      <guid>https://dev.to/annavi11arrea1/catbot-the-custom-ai-harness-that-lives-on-desktop-layottu-35kb</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/challenges/weekend-2026-07-09"&gt;Weekend Challenge: Passion Edition&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;This weekend, I went on a journey to build a voice activated assistant on my desktop, named Catbot. In the process, I switched from notion over to obsidian. Obsidian acts as a knowledge base for the agents to communicate. Catbot is a custom harness I built that I can use with any of my agents. Catbot is voice activated and sleeps in a bed when I tell him too. I also created a small skin modifier to change it's appearance.&lt;/p&gt;





&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/zv6Clx7aemk"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
Catbot switches voices in real time on my desktop!







&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Here is a snippet from my catbot_voice.py. This is a private project at the moment, but I am comfortable sharing the initial setup with everyone!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;threading&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sounddevice&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sd&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;vosk&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;kokoro_onnx&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Kokoro&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.env&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;catbot.voice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__file__&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;
&lt;span class="n"&gt;KOKORO_ONNX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kokoro-v1.0.onnx&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;KOKORO_VOICES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;voices-v1.0.bin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;VOSK_BIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-en-us-0.22-lgraph&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;VOSK_SMALL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ROOT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;vosk-model-small-en-us-0.15&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;SAMPLE_RATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;16000&lt;/span&gt;
&lt;span class="n"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://localhost:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CATBOT_PORT&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8800&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# Tolerant of how recognizers mangle "catbot": fuzzing Vosk against Kokoro
# speech produced "cat bought", "that bought", "cat ba bought", "can't bought",
# "cat bottom gauge"... A false wake just means he listens, so err generous.
&lt;/span&gt;&lt;span class="n"&gt;BOT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(?:cat|kat|cad|catt|that|can&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;?t|cap)\s*-?\s*(?:\w{1,3}\s+)?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
       &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(?:bottom|bought|boat|both|body|bert|bird|bot|but)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# every way of telling him to be quiet actually works now — "shut down",
# "stop", "quiet" etc. used to fall through to the LLM, which would *claim*
# to shut down while nothing happened
&lt;/span&gt;&lt;span class="n"&gt;STOP_WORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(?:go\s+to\s+)?(?:sleep|slee|asleep)|shut\s*(?:down|up)|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;(?:be\s+)?quiet|stop(?:\s+(?:listening|talking))?|&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
              &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;power\s+(?:down|off)|turn\s+off|pause|hush|silence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;RE_SLEEP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BOT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[,!.\s]*(?:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;STOP_WORDS&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;)\b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;RE_ENGAGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BOT&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[,!.\s]*(?:(?:en\s*)?(?:engage|gage|gauge|engaged|gaged)d?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                             &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;|wake\s*up|wake)[,!.\s]*(.*)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# While ASLEEP, bias hard toward waking: a missed wake makes him unusable,
# a false wake just means he listens. Any engage-ish or wake-ish word wakes.
&lt;/span&gt;&lt;span class="n"&gt;RE_WAKE_LOOSE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\b(?:en|in|and|un)?[\s-]*(?:gage|gauge|engage)[ds]?\b|\bwake+\s*(?:up|it)?\b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# in-conversation commands
&lt;/span&gt;&lt;span class="n"&gt;PROVIDER_WORDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;anthropic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;google&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gemini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chatgpt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chat gpt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;open ai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ollama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ollama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;local&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ollama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;qwen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ollama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ollama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;







&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A FastAPI &lt;strong&gt;server&lt;/strong&gt; server.py is the single brain-stem: every surface talks to Catbot over HTTPS on port 8800. &lt;/li&gt;
&lt;li&gt;Catbot routes chats to chosen &lt;strong&gt;provider&lt;/strong&gt; in providers.py, injects the shared memory from the obsidian vault.&lt;/li&gt;
&lt;li&gt;runs background agent missions, and turns &lt;code&gt;catbot-publish&lt;/code&gt; blocks in replies into real files like vualt notes or web pages.&lt;/li&gt;
&lt;li&gt;Catbot is a transparent always-on-top window with its own offline voice engine catbot_voice.py: Vosk hears speech, intents route commands, Kokoro answers aloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was first using kokoro for voices and then I discovered the amazing possibilities for voices at eleven labs. I recorded enough of my voice to make a pretty close clone, and it's definitely interesting to hear yourself talk back to you.&lt;/p&gt;

&lt;p&gt;There is a web view but he mostly just lives floating on the desktop - where I want Catbot to live.&lt;/p&gt;





&lt;p&gt;I went through some modifications during this process. Catbot started out Grey and then I added a skin modifier:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9z82r4gt61vvmqxqec7l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9z82r4gt61vvmqxqec7l.png" alt="Grey Catbot" width="279" height="215"&gt;&lt;/a&gt;&lt;/p&gt;
Original rendering of Catbot



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fji78s28aajf93pnck7gm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fji78s28aajf93pnck7gm.png" alt="Skin modifier" width="608" height="498"&gt;&lt;/a&gt;&lt;/p&gt;
Skin modifier for Catbot!



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr2a0pjxuztqluoof8ndk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr2a0pjxuztqluoof8ndk.png" alt="Bedtime" width="373" height="333"&gt;&lt;/a&gt;&lt;/p&gt;
Catbot goes to sleep!



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9943922jw5s06gp56uvu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9943922jw5s06gp56uvu.png" alt="Web interface" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;
Catbot optional web interface



&lt;p&gt;I am obsessed with my new digital pet cat, and that's why I had to share him for this passion project. Thank you for reading!&lt;/p&gt;





&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;I am submitting for Best Use of ElevenLabs. Using a custom built voice, I was able to integrate it through the API easily to Catbot to add to my voice bank!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Simple Benchmark Review: Ollama on Jetson Nano</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 12 Jul 2026 05:43:01 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/simple-benchmark-review-ollama-on-jetson-nano-5gee</link>
      <guid>https://dev.to/annavi11arrea1/simple-benchmark-review-ollama-on-jetson-nano-5gee</guid>
      <description>&lt;p&gt;Previous Related Post: &lt;a href="https://dev.to/annavi11arrea1/jetson-nano-ollama-optimal-quantization-2de8"&gt;Part 1&lt;/a&gt;&lt;/p&gt;





&lt;p&gt;This particular rabbit hole was created due to a previous conversation I had here with another member on DEV about benchmarking and tests. Lot's of questions sparked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's the best way to do them? &lt;/li&gt;
&lt;li&gt;How do we do them? &lt;/li&gt;
&lt;li&gt;What are good numbers? &lt;/li&gt;
&lt;li&gt;What information is actually important?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The truth is that it heavily depends on what you are doing and what is important to you, first and foremost. For me, and my little test generation app, my purpose seemed simple: create a simple app using ai, locally, for free, and take text I have and generate flashcards and quizzes from it so I can review. I can tell you by this point I have spent less time reviewing and more time digging into interesting tools. &lt;/p&gt;

&lt;p&gt;On this particular journey, man - I did so much I'm having trouble knowing what to explain first. But I'll try to stick to testing. So basically, I wanted to see which model would run the best on my nano, you know without crashing it, because I did do that actually at one point. That's a tangent I'll save for later but I saved by conversation with claude in my techdocs if you want some entertainment. ( &lt;a href="https://techdocs.annavillarreal.com" rel="noopener noreferrer"&gt;techdocs&lt;/a&gt; --&amp;gt; then click jetson nano on the left. ) I did not have enough ram on my nano to handle the test run. This led me to create a swap file to accommodate for lack of ram in case of emergency. Just as a safety net - since I get into trouble.&lt;/p&gt;

&lt;p&gt;Safety net in place, I create a quick quiz of the OSI model from a web page to use as source of truth - something to compare the results to. Thanks Vinicius Pereira for the ideas in our chat! So, I ran this test against each one of the models and varying 'quantizations' (is that a word yet?) to bring to light the mystery of this concept. &lt;/p&gt;

&lt;h3&gt;
  
  
  The high level - if a model is heavily quantized, you lose quality. Brass tax.
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvu88h6fh4w4g1kdi0m71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvu88h6fh4w4g1kdi0m71.png" alt="benchmark tests" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I went to these great lengths and then only come to realize, sheesh, this is only for one specific use case. This would take a minute to map performance across many different use cases. I wonder, if you have nano, what have you used Ollama for on it? Not like I need another reason to hoard data. XD&lt;/p&gt;

&lt;p&gt;Anyways, in case you don't feel like navigating away, here's a table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;Model&lt;/th&gt;
      &lt;th&gt;Quant&lt;/th&gt;
      &lt;th&gt;Accuracy&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
&lt;td&gt;qwen2.5:3b-instruct&lt;/td&gt;
&lt;td&gt;q4_K_M&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;qwen2.5:3b-instruct&lt;/td&gt;
&lt;td&gt;q5_K_M&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;qwen2.5:3b-instruct&lt;/td&gt;
&lt;td&gt;q8_0&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;qwen3.5:2b&lt;/td&gt;
&lt;td&gt;q4_K_M&lt;/td&gt;
&lt;td&gt;0% (empty output)&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;qwen3.5:2b&lt;/td&gt;
&lt;td&gt;q8_0&lt;/td&gt;
&lt;td&gt;0% (empty output)&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;llama3.2:3b-instruct&lt;/td&gt;
&lt;td&gt;q2_K&lt;/td&gt;
&lt;td&gt;40%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;llama3.2:3b-instruct&lt;/td&gt;
&lt;td&gt;q4_K_M&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;llama3.2:3b-instruct&lt;/td&gt;
&lt;td&gt;q5_K_M&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;llama3.2:3b-instruct&lt;/td&gt;
&lt;td&gt;q8_0&lt;/td&gt;
&lt;td&gt;90%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;mistral:7b-instruct&lt;/td&gt;
&lt;td&gt;q2_K&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;mistral:7b-instruct&lt;/td&gt;
&lt;td&gt;q4_K_M&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
    &lt;tr&gt;
&lt;td&gt;mistral:7b-instruct&lt;/td&gt;
&lt;td&gt;q5_K_M&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Some cells above are empty because they would not fit on the gpu of the nano, so no point in running a test for a model that will never perform as needed due to hardware limitations.&lt;/p&gt;

&lt;p&gt;The numbers are based on a pool of 10 questions, so this is why the numbers are so perfectly precise. Obviously this is simple high level test, but I wanted to work through this fully. Having gone through the motions, I know that the rabbit hole is deep and vast and this is barely the surface. But from simple test point of view for my use case of quiz generation, qwen2.5:3b-instruct takes the cake from the bakery to the house. Guess ima have a slice once I recombobulate my app, switching from llama3.2:3b-instruct to qwen ~ a task for another day. &lt;/p&gt;

&lt;p&gt;I'm sure many of you reading this have seen other benchmarks elsewhere. I'd love to have some peer review here to tell me if I'm kind of on point here or if my data looks odd. Feel like I'm falling into a dark trap of testing things. I know others have done this and there is other information out there, but what kind of actual appreciation for the technology does that offer? &lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>nvidia</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Jetson Nano: Ollama &amp; Optimal Quantization</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 05 Jul 2026 16:06:31 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/jetson-nano-ollama-optimal-quantization-2de8</link>
      <guid>https://dev.to/annavi11arrea1/jetson-nano-ollama-optimal-quantization-2de8</guid>
      <description>&lt;p&gt;I am delighted to announce that a user reported dysfunction so that I could go down the rabbit hole of fixing it. Messing around locally is one thing, but building a tolerable app for end users has other considerations when using a ‘local’ AI. This led to interesting findings about the limitations of hardware and gaining a high-level understanding of quantization and it's importance. I also explain how to creatively get around limitations. It's called flippy card, and it's an app that helps you study via custom uploaded content. Wanna see it? It's here: &lt;a href="https://flippycard.annavillarreal.com/" rel="noopener noreferrer"&gt;Flippycard&lt;/a&gt;&lt;/p&gt;





&lt;h2&gt;
  
  
  Problems:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ollama was stuck behind cloudflare in a local loop.&lt;/li&gt;
&lt;li&gt;Ollama needs to be accessed outside of systemctl with config file.&lt;/li&gt;
&lt;li&gt;Ollama was running painfully slow, and needed to set it to use GPU, not CPU. &lt;/li&gt;
&lt;li&gt;Had to build Ollama from source and tell it to use GPU instead. &lt;/li&gt;
&lt;/ul&gt;





&lt;h2&gt;
  
  
  Implementations:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Make sure Ollama goes through cloudflare tunnel.&lt;/li&gt;
&lt;li&gt;Create configuration file so that Ollama is reachable externally.&lt;/li&gt;
&lt;li&gt;Build from source to save resources, Docker is too resource intensive for Jetson Nano to be performant.&lt;/li&gt;
&lt;li&gt;Increase speed by switching from CPU -&amp;gt; GPU, you need to be explicit about these things.&lt;/li&gt;
&lt;li&gt;Keep a watchful eye on system resources to make sure the Nano is not at risk of crashing.
I also added a Notes section to my new techdocs page to start documenting implementations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I built from source to spare resources on the jetson orin nano which took about 30 minutes. I had concerns about pushing my 8GB of ram to its limit, which is a very valid concern. I need to keep it as lightweight as possible. As much as a docker container appealed to me, it is slim-pickin’s on a budget. &lt;/p&gt;

&lt;h3&gt;
  
  
  Building from source steps:
&lt;/h3&gt;

&lt;p&gt;Step 1: Install CUD toolkit + cmake with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Sudo apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; cuda-toolkit-13 cmake
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 2: Install Go (arm 64):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Curl &lt;span class="nt"&gt;-LO&lt;/span&gt; https://go.dev/dl/go1.24.4.linix-arm64.tar.gx &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo tar&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /usr/local &lt;span class="nt"&gt;-xzf&lt;/span&gt; go1.24.4.linux-arm64.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;go1.24.4.linux-arm64.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 3: Add Go and CUDA to PATH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Echo ‘export &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/local/bint:/usr/local/cuda-13/bin’ &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; esport &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/usr/local/go/bin:/usr/local/cuda-13/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4: Clone and build ollama with sm_87:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Git clone https://github.com/ollama/ollama /home/anna/ollama-src &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; /hom/anna/ollama-src &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;CUDA_ARCHITECTURES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;87 cmake
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This whole process took roughly 30 minutes.&lt;/p&gt;





&lt;h2&gt;
  
  
  Effeciency
&lt;/h2&gt;

&lt;p&gt;I wanted to share this process easily, so I connected the gmail MCP server to claude so I can send emails of the process summary to myself. &lt;/p&gt;

&lt;p&gt;Maximizing efficiency on the jetson nano was a good choice. I really don't want to be managing a bunch of documentation from my server, so I've created a new place to document my journeys on my new techdocs page. I built this while ollama was building. Understanding resource considerations, I asked claude to also build me a new page for me to put all my tech findings only if it didn't push my resources on my tiny server to the limit - knowing the process at hand was of the utmost importance. It analyzed the situation, gave me real feedback, and told me what could be done since I was already pushing the limits of hardware capabilities with my current install. This was a great insight to not crash the PC while an important process was running. Since everything was already running I was able to add new routes easily with a few lines. Of course I realize I could have done this on another computer, but it’s fun to push the limits of hardware.&lt;/p&gt;





&lt;h2&gt;
  
  
  Quantization - New Topic (for me)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Quantization:&lt;/strong&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Quantization is the process of reducing the precision of a digital signal, typically from a higher-precision format to a lower-precision format.” &lt;a href="https://www.ibm.com/think/topics/quantization" rel="noopener noreferrer"&gt;(Brian Clark, IBM)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After finally getting everything to work, the request took 13 minutes and 20 seconds using the Q8_0 version. This is where I learned about quantization. I then tried another variant, Q4_0 to improve the results. &lt;/p&gt;





&lt;h3&gt;
  
  
  Benchmarks
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbc5jp4sjdd5g8d554avs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbc5jp4sjdd5g8d554avs.png" alt="cyber level" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the benchmark breakdown of Q8_0 vs Q4_K-M quantization:&lt;/p&gt;

&lt;p&gt;Since the bottleneck was "model doesn't fit in available GPU memory," we tested a smaller quantization of the exact same model (llama3.2:1b), rather than switching to a different, weaker model family.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Q8_0 (original)&lt;/th&gt;
&lt;th&gt;Q4_K_M&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model file size&lt;/td&gt;
&lt;td&gt;1.5 GB&lt;/td&gt;
&lt;td&gt; 808 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPU layers loaded&lt;/td&gt;
&lt;td&gt;3–9 of 17&lt;/td&gt;
&lt;td&gt;17 of 17 (100%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generation speed&lt;/td&gt;
&lt;td&gt;~1.2–1.35 tok/s&lt;/td&gt;
&lt;td&gt;~30.7 tok/s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real 971-token test&lt;/td&gt;
&lt;td&gt;13m 20s&lt;/td&gt;
&lt;td&gt;~35–45s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Q8_0 (original) Q4_K_M (new)&lt;br&gt;
Model file size 1.5 GB 808 MB&lt;br&gt;
GPU layers loaded 3–9 of 17 17 of 17 (100%)&lt;br&gt;
Generation speed ~1.2–1.35 tok/s ~30.7 tok/s&lt;br&gt;
Real 971-token test 13m 20s ~35–45s (estimated at this rate)&lt;/p&gt;

&lt;p&gt;That's roughly a 25x speedup, because the entire model now fits on the GPU instead of mostly running on the slow CPU path. Amazing! But what's the catch? &lt;/p&gt;





&lt;h3&gt;
  
  
  Issues
&lt;/h3&gt;

&lt;p&gt;Q4_K_M is fast but produced malformed output sometimes.&lt;/p&gt;

&lt;p&gt;Before switching, ran 6 back-to-back test generations with Q4_K_M to check reliability, since lower-precision quantization can be less consistent. &lt;/p&gt;

&lt;p&gt;Here's what happened:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 of 6: perfectly valid JSON, correct structure&lt;/li&gt;
&lt;li&gt;1 of 6: valid JSON, but used a slightly different field name than expected&lt;/li&gt;
&lt;li&gt;3 of 6: malformed JSON (e.g., a mismatched bracket) that would have crashed the app's parser outright&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's roughly a 50-65% failure rate per attempt. I cannot knowingly ship that, even with a massive speed improvement.&lt;/p&gt;





&lt;p&gt;So how do we handle the error-prone behavior of Q4?&lt;/p&gt;

&lt;p&gt;Rather than giving up on the faster model, automatic retry logic was added to the app itself. If the model's response is malformed, the app now silently tries again up to 3 times before showing an error. Because each Q4_K_M attempt only takes about 30-45s, even a worst-case 3 attempts is still far faster than a single guaranteed-slow Q8_0 request, while pushing the effective success rate up to roughly 85-95%.&lt;/p&gt;

&lt;p&gt;This handles the potential parsing errors gracefully. Since Q4 is 25x times faster, the client won’t really feel it.&lt;/p&gt;





&lt;p&gt;Side note: I noticed my Jetson had the “super” abilities after a month of setting it up. Don’t do what I did. Check for super abilities first. It’s a free download. XD&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
    <item>
      <title>👾 🧚🏼‍♀️Maximizing Fable for Life Admin</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sat, 04 Jul 2026 12:31:09 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/maximizing-fable-for-life-admin-c62</link>
      <guid>https://dev.to/annavi11arrea1/maximizing-fable-for-life-admin-c62</guid>
      <description>&lt;p&gt;TLDR: The most powerful AI on the planet, only a few days of access. Maximize it.&lt;/p&gt;





&lt;p&gt;I'd first like to give credit where it's due: &lt;a class="mentioned-user" href="https://dev.to/trickell"&gt;@trickell&lt;/a&gt; - Thank you for sharing Network Chuck's youtube video with me. The reference video is found here guys if you missed it: &lt;a href="https://www.youtube.com/watch?v=YC77Lb_cN6c" rel="noopener noreferrer"&gt;Network Chuck's Video on Fable&lt;/a&gt;&lt;/p&gt;





&lt;p&gt;I first started by creating a nice template for tech documentation for personal use. It created a beautiful piece of work in about 5 minutes - something I could easily expand on in the future. Here is what it generated for me with after a one or two careful prompts:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9rn97tebs0lfvnda6lp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9rn97tebs0lfvnda6lp2.png" alt="tech docs by fable" width="800" height="589"&gt;&lt;/a&gt;&lt;/p&gt;
Clean UI, Easy Navigation! Created this personal reference guide for studying for CCNA (Network Chucks Summer of CCNA)



&lt;p&gt;Wanna see it? It lives here: &lt;a href="https://techdocs.annavillarreal.com/" rel="noopener noreferrer"&gt;Techdocs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But after learning about the true span of Fable's power, I started asking the serious questions, the ones that are life-changing. How can I increase my quality of life based on my resume, experience, and current life circumstances? I wrote about 2 pages of life issues that needed fixing - you know the stuff that slowly eats away at your soul, like student loan debt and people that are challenging to work with? Yes - I told it my biggest issues and instructed it to give me actionable plans that are free or low-cost. Even fable told me that this was a lot. 😅&lt;/p&gt;





&lt;h2&gt;
  
  
  Getting Organized
&lt;/h2&gt;

&lt;p&gt;Knowing the scope of my own problems I knew that my thoughts and processes had to be organized. Luckily for me, I remembered I had a good place to do that. A place that Fable could connect to and place documentation in place for me with checklists, notes, summaries and actionable plans. That app is called Notion, and some of you may have heard of it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv42f8yc3g8lmkemcpwb2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv42f8yc3g8lmkemcpwb2.png" alt="Notion documentation to reference later" width="799" height="289"&gt;&lt;/a&gt;&lt;/p&gt;
No one is going to organize your life for you, no one, except for AI







&lt;p&gt;I couldn’t think of a better place for lightning fast critical life-admin documentation on the spot. And I can tell you, this integration works like a charm, and I highly recommend it. For a busy person with a million ideas, this is great.&lt;/p&gt;





&lt;h2&gt;
  
  
  Anxiety Relief
&lt;/h2&gt;

&lt;p&gt;I had a tremendous amount of anxiety about my pending student loans. After bringing things to light with Fable, I realized that even though I am forced to make payments, it would be entirely manageable. I had massive fears about this because of all the ongoing litigation. Fable helped me think clearly through this - and I found the solution that would best work for my particular position it would seem.&lt;/p&gt;





&lt;h2&gt;
  
  
  Future Hope
&lt;/h2&gt;

&lt;p&gt;I fed it my resume, my linked in, my server website. I told it my struggles about finding work although feeling quite capable. It made me realize something. Even though Junior dev positions are a wasteland at the moment, fable was able to reframe my current position for me in an elegant manner. &lt;/p&gt;

&lt;p&gt;Direct copy from fable: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are not "a junior dev in a dead market." You are a hybrid: IT support + full-stack dev + technical writer with a real audience + CCNA in progress + self-hosted infrastructure experience. Pure junior dev roles are brutal right now, but hybrid roles are NOT&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;… monumentally wicked powerful outlook rescoping!&lt;/p&gt;

&lt;p&gt;In less than an hour, fable has given me a greater peace of mind, hope, and actions I can work on right now to improve my life and the life of those around me. If you haven’t messed with fable, times a-ticking, go do it now.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>mentalhealth</category>
      <category>productivity</category>
    </item>
    <item>
      <title>👾 Server Access Logs with GoAccess</title>
      <dc:creator>Anna Villarreal</dc:creator>
      <pubDate>Sun, 21 Jun 2026 23:49:41 +0000</pubDate>
      <link>https://dev.to/annavi11arrea1/server-access-logs-with-goaccess-333d</link>
      <guid>https://dev.to/annavi11arrea1/server-access-logs-with-goaccess-333d</guid>
      <description>&lt;p&gt;Part 1: &lt;a href="https://dev.to/annavi11arrea1/self-hosting-experience-with-jetson-orin-nano-and-ollama-5a9c"&gt;Self-hosting on Jetson Orin Nano&lt;/a&gt;&lt;/p&gt;





&lt;h3&gt;
  
  
  👽 Jetson Orin Nano Web Server Follow-up 👽
&lt;/h3&gt;

&lt;p&gt;Cool! Now that the mini web server is up and running, how can I see web traffic easily? I discovered GoAccess recently, which is a free and open source tool for checking out server logs in real time. There are two way to view it. At first I was happy to just see nicely-parsed server logs in the terminal. Ahhhh, organization! It gives you a bunch of interesting stuff to look at.&lt;/p&gt;

&lt;p&gt;Here is what the terminal view looks like:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flav1ura1myngxxin6gtk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flav1ura1myngxxin6gtk.png" alt="goaccess terminal view" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;
Dats cool







&lt;p&gt;You know, inviting traffic to a webserver invokes anxiety. Having half an idea of what is happening helps ease tension for sure. I was really excited to find this tool. You can open go access in the terminal to display different information with different views. I will leave the explaining to the official documentation found here: &lt;a href="https://goaccess.io/get-started" rel="noopener noreferrer"&gt;GoAccess Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the web developer in me was super excited to find a very human-readable html version readily available. Using a reverse proxy through nginx, you can view all the stats on a web page locally. It also allows you to pick a theme and customize how the information is displayed. Be sure to check out the settings and chart options!&lt;/p&gt;

&lt;p&gt;Here is what the html view produces:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7g6a2pcn65uppscfl944.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7g6a2pcn65uppscfl944.png" alt="goaccess html view" width="799" height="399"&gt;&lt;/a&gt;&lt;/p&gt;
Stats for silly hoomins.







&lt;p&gt;Idk about you, but this is my super exciting find for the day.&lt;/p&gt;

&lt;p&gt;I think my next step is to connect an agent that reads the logs and alerts me on set parameters.&lt;/p&gt;

&lt;p&gt;I'm interested to hear what tools all of you use to enhance web server monitoring?&lt;/p&gt;

&lt;p&gt;What's the best agent for web analytics, in your opinion?&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>learning</category>
      <category>linux</category>
    </item>
  </channel>
</rss>
