<?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: Rohith Pavithran</title>
    <description>The latest articles on DEV Community by Rohith Pavithran (@rohithtp).</description>
    <link>https://dev.to/rohithtp</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%2F509052%2F1a0f8134-7793-4e1e-9821-f06880c29390.png</url>
      <title>DEV Community: Rohith Pavithran</title>
      <link>https://dev.to/rohithtp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rohithtp"/>
    <language>en</language>
    <item>
      <title>The Alpine Mirage: How Upgrading Python Broke My Build and Led to a Truer Security Posture</title>
      <dc:creator>Rohith Pavithran</dc:creator>
      <pubDate>Thu, 30 Jul 2026 03:50:29 +0000</pubDate>
      <link>https://dev.to/rohithtp/the-alpine-mirage-how-upgrading-python-broke-my-build-and-led-to-a-truer-security-posture-38ne</link>
      <guid>https://dev.to/rohithtp/the-alpine-mirage-how-upgrading-python-broke-my-build-and-led-to-a-truer-security-posture-38ne</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;
  
  
  The Initial Goal: "Upgrade and Secure"
&lt;/h2&gt;

&lt;p&gt;Like many developers, I recently fell into the trap of assuming that "smaller is always better, and newer is always safer." I decided to upgrade my terminal-based web UI project, &lt;code&gt;py_terminal&lt;/code&gt;, to the bleeding-edge &lt;code&gt;python:3.15-rc-alpine&lt;/code&gt; Docker base image. &lt;/p&gt;

&lt;p&gt;The logic was sound: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Alpine Linux&lt;/strong&gt; has a much smaller footprint, meaning a smaller attack surface. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python 3.15 Release Candidate&lt;/strong&gt; would give me early access to performance improvements and patches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What followed was a cascading series of build failures that taught me a valuable lesson about container architecture, Python's C-API, and what &lt;em&gt;actually&lt;/em&gt; makes a container secure.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Descent into Dependency Hell
&lt;/h2&gt;

&lt;p&gt;The moment I pushed the Dockerfile update and ran &lt;code&gt;docker build&lt;/code&gt;, the pipeline exploded.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Missing Wheels
&lt;/h3&gt;

&lt;p&gt;The first error was abrupt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: No matching distribution found for litellm==1.93.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because I was combining a release candidate of Python (3.15-rc) with Alpine (which uses &lt;code&gt;musl&lt;/code&gt; libc instead of the standard &lt;code&gt;glibc&lt;/code&gt;), pre-compiled binaries (wheels) simply didn't exist for several of my packages. &lt;code&gt;pip&lt;/code&gt; was forced to download raw source code and build from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Rust Compiler (Wait, Rust?)
&lt;/h3&gt;

&lt;p&gt;One of &lt;code&gt;litellm&lt;/code&gt;'s underlying dependencies is &lt;code&gt;fastuuid&lt;/code&gt;, which is written in Rust. Because &lt;code&gt;pip&lt;/code&gt; was building from source, it attempted to download the Rust toolchain (&lt;code&gt;cargo&lt;/code&gt;). It immediately failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error loading shared library libgcc_s.so.1: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Alpine is so incredibly stripped down, it didn't even have the basic C runtime library (&lt;code&gt;libgcc&lt;/code&gt;) required to run the Rust compiler.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fighting the PyO3 API
&lt;/h3&gt;

&lt;p&gt;Determined to win, I added the heavy build tools to Alpine (&lt;code&gt;apk add build-base cargo libffi-dev&lt;/code&gt;). The build got further, but then crashed while compiling &lt;code&gt;tiktoken&lt;/code&gt; and &lt;code&gt;pydantic-core&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;The bridge between Rust and Python is handled by a library called &lt;code&gt;PyO3&lt;/code&gt;. It explicitly rejected Python 3.15 because it was too new! I bypassed this by injecting an environment variable: &lt;code&gt;PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;I thought I had won, until the final boss appeared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;E0609&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;tp_new&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;PyTypeObject&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python 3.15 had made fundamental changes to its internal C-API, hiding fields like &lt;code&gt;tp_new&lt;/code&gt; and &lt;code&gt;tp_base&lt;/code&gt;. The Rust bindings in &lt;code&gt;pydantic-core&lt;/code&gt; were incompatible at a structural level. It was physically impossible to build.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Pivot: What Does "Secure" Actually Mean?
&lt;/h2&gt;

&lt;p&gt;I took a step back. I realized that by trying to use Alpine to create a "secure, minimal" container, I was actually doing the exact opposite. &lt;/p&gt;

&lt;p&gt;If I wanted to stay on Alpine, I was going to have to leave heavy compilers (GCC, Make, Rust) inside my container or build complex multi-stage pipelines. Compilers inside production images are a massive security risk.&lt;/p&gt;

&lt;p&gt;I pivoted back to &lt;code&gt;python:3.12-slim&lt;/code&gt; (a Debian-based image) and focused on &lt;strong&gt;actual&lt;/strong&gt; security best practices rather than just chasing a smaller megabyte footprint.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;Here is the final Dockerfile we settled on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim&lt;/span&gt;

&lt;span class="c"&gt;# 1. Create a non-root user for security&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;groupadd &lt;span class="nt"&gt;-r&lt;/span&gt; appuser &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; useradd &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; appuser appuser

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . ./&lt;/span&gt;

&lt;span class="c"&gt;# 2. Change ownership of the app to the non-root user&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; appuser:appuser /app

&lt;span class="c"&gt;# 3. Switch to the non-root user before running the app&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PYTHONUNBUFFERED=1&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["python", "terminal_web/main.py"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of fighting &lt;code&gt;musl&lt;/code&gt; compilation, we leaned on Debian's pre-compiled wheels (keeping the image compiler-free). Then, we stripped the application of &lt;code&gt;root&lt;/code&gt; privileges. If a vulnerability is ever exploited in my app, the attacker is trapped inside the restricted &lt;code&gt;appuser&lt;/code&gt;, heavily minimizing the blast radius.&lt;/p&gt;




&lt;h2&gt;
  
  
  Going the Extra Mile: Trivy Automation
&lt;/h2&gt;

&lt;p&gt;To ensure we didn't blindly introduce vulnerable packages again, we integrated &lt;a href="https://trivy.dev/" rel="noopener noreferrer"&gt;Trivy&lt;/a&gt; scanning.&lt;/p&gt;

&lt;p&gt;Not only did we add Trivy to our GitHub Actions CI pipeline to fail the build if &lt;code&gt;CRITICAL&lt;/code&gt; or &lt;code&gt;HIGH&lt;/code&gt; vulnerabilities are found, but we also built a custom AI Agent Skill to automate local reporting. &lt;/p&gt;

&lt;p&gt;Now, with a single command, my local environment spins up a background Trivy scan, extracts the JSON data, and parses it into a beautiful Markdown and HTML table, ensuring that no vulnerable library sneaks in before I even push to the remote.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Alpine is a double-edged sword for Python:&lt;/strong&gt; Unless you are prepared to build multi-stage Dockerfiles and compile C/Rust extensions manually, Debian-slim images are often much safer and significantly faster to build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Small" does not equal "Secure":&lt;/strong&gt; Adding build-tools to an image negates the security benefits of its small size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity is everything:&lt;/strong&gt; Running your container as a non-root user is one of the single highest-impact security improvements you can make, regardless of your base image.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This Bug Smash journey was frustrating, but the end result is a highly stable, automatically-scanned, and truly secure container architecture.&lt;/p&gt;

</description>
      <category>bugsmash</category>
      <category>python</category>
      <category>docker</category>
      <category>security</category>
    </item>
    <item>
      <title>Cleaning up Repository Bloat: Removing Redundant requirements.txt</title>
      <dc:creator>Rohith Pavithran</dc:creator>
      <pubDate>Sat, 18 Jul 2026 13:53:16 +0000</pubDate>
      <link>https://dev.to/rohithtp/cleaning-up-repository-bloat-removing-redundant-requirementstxt-3akk</link>
      <guid>https://dev.to/rohithtp/cleaning-up-repository-bloat-removing-redundant-requirementstxt-3akk</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: Clear the Lineup&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;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;The project &lt;code&gt;rohithtp-cv&lt;/code&gt; is a personal repository dedicated to managing and rendering professional CV/resume configurations and pipelines. It streamlines dependency management to ensure clean, reproducible environments for building modern CV layouts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bug Fix or Performance Improvement
&lt;/h2&gt;

&lt;p&gt;In this update, I targeted a common issue in repository maintenance: redundant configuration files. The repository contained a leftover &lt;code&gt;requirements.txt&lt;/code&gt; file that was no longer actively utilized or synchronized with the core dependency management flow. &lt;/p&gt;

&lt;p&gt;Leaving unused configuration files in a codebase increases technical debt, confuses automated dependency scanners (like Dependabot), and risks causing installation mismatches for future collaborators.&lt;/p&gt;

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

&lt;p&gt;You can view the full changes and the exact cleanup in the commit linked below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/rohithtp/rohithtp-cv/commit/0f1f373a33255a6367dd8b9893106178b98e26c7" rel="noopener noreferrer"&gt;Clean up redundant requirements.txt&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Improvements
&lt;/h2&gt;

&lt;p&gt;The optimization involved a direct pruning of the project architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identified that the package dependencies were already properly specified elsewhere or no longer required this file.&lt;/li&gt;
&lt;li&gt;Removed the redundant &lt;code&gt;requirements.txt&lt;/code&gt; to streamline the repository structure.&lt;/li&gt;
&lt;li&gt;Verified that local builds and automated pipelines function optimally without the file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This simple architectural hygiene task reduces the clutter in the codebase, simplifies onboarding, and prevents potential security/version mismatches from unmaintained files.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>devops</category>
      <category>python</category>
    </item>
    <item>
      <title>Free Voice Music Curator: Ditch Premium Music Fees with ElevenLabs &amp; Google AI</title>
      <dc:creator>Rohith Pavithran</dc:creator>
      <pubDate>Sun, 12 Jul 2026 15:18:44 +0000</pubDate>
      <link>https://dev.to/rohithtp/free-voice-music-curator-ditch-premium-music-fees-with-elevenlabs-google-ai-454c</link>
      <guid>https://dev.to/rohithtp/free-voice-music-curator-ditch-premium-music-fees-with-elevenlabs-google-ai-454c</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;I built the &lt;strong&gt;Free Voice Music Curator&lt;/strong&gt;, an intelligent full-stack audio player powered entirely by public APIs, legal Creative Commons music, and zero paid subscription walls.&lt;/p&gt;

&lt;p&gt;Inspired by the challenge prompt of &lt;strong&gt;passion&lt;/strong&gt;, I wanted to capture the absolute devotion fans and hobbyists pour into their musical worlds. However, classic web-streaming apps are trapped behind heavy OAuth logic or aggressive paid premium tiers. The &lt;em&gt;Free Voice Music Curator&lt;/em&gt; solves this by allowing users to command a local audio player using natural voice strings (via ElevenLabs), which are dynamically parsed by Google AI to pull over 500,000 legal, free-streaming tracks from Jamendo.&lt;/p&gt;

&lt;h3&gt;
  
  
  🟢 The Free-Tier Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ElevenLabs Agent ] ➔ Webhook ➔ [ Backend Server ] ➔ Jamendo REST API
                                        │
                                        ▼
                        [ HTML5 Audio Player Engine ] ➔ (Plays raw .mp3 stream)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The application is fully configured and ready!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Development Webhook:&lt;/strong&gt; &lt;code&gt;https://ais-dev-vtsuihfhfsyunprfhoszsh-559194232025.asia-east1.run.app/api/webhook/elevenlabs&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Webhook Simulator:&lt;/strong&gt; Included directly inside the UI console so you can fire direct preset configurations (e.g., &lt;code&gt;💤 Lofi Study&lt;/code&gt;, &lt;code&gt;🏎️ Synthwave&lt;/code&gt;, &lt;code&gt;🎸 Acoustic&lt;/code&gt;) to watch the system respond in real time.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The interface features a gorgeous deep interstellar slate and emerald canvas (&lt;code&gt;bg-slate-900&lt;/code&gt; / &lt;code&gt;text-emerald-400&lt;/code&gt;) alongside modern typography pairings (&lt;code&gt;Space Grotesk&lt;/code&gt; and &lt;code&gt;Inter&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Here is the clean &lt;strong&gt;Python Core Engine&lt;/strong&gt; snippet used to process the incoming webhook metadata securely:&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;random&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;vlc&lt;/span&gt;  &lt;span class="c1"&gt;# Install via: pip install python-vlc
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;FreeVoiceMusicCurator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&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;client_id&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;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client_id&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;base_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.jamendo.com/v3.0/tracks/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_curated_stream_urls&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;target_genre&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="n"&gt;target_artist&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="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&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;Querying free cloud library for: Genre=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target_genre&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Artist=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target_artist&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="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;params&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;client_id&lt;/span&gt;&lt;span class="sh"&gt;"&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;client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;format&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;json&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;limit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audioformat&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;mp32&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;order&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;popularity_total&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;target_genre&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tags&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_genre&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;if&lt;/span&gt; &lt;span class="n"&gt;target_artist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artist_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;target_artist&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;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&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;base_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;tracks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&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;span class="n"&gt;stream_targets&lt;/span&gt; &lt;span class="o"&gt;=&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;track&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;stream_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;stream_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;stream_targets&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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;artist&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;artist_name&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;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stream_url&lt;/span&gt;
                    &lt;span class="p"&gt;})&lt;/span&gt;

            &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream_targets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;stream_targets&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&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="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="nf"&gt;print&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;API Fetch Error: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&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="k"&gt;return&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;p&gt;The execution pipeline flows fluidly across three modern technical frameworks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Intelligent Conversational Agent (ElevenLabs):&lt;/strong&gt; I built a blank agent powered by a custom webhook tool named &lt;code&gt;play_free_genre_music&lt;/code&gt;. It listens for spoken triggers like &lt;em&gt;"Play some high-energy synthwave beats"&lt;/em&gt; and extracts clean textual params (&lt;code&gt;prompt&lt;/code&gt;, &lt;code&gt;genre&lt;/code&gt;, &lt;code&gt;artist&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contextual AI Translation Engine (Google AI Studio):&lt;/strong&gt; The backend leverages the &lt;code&gt;gemini-3.5-flash&lt;/code&gt; model to analyze the full natural-language transcripts, scrubbing out specific moods, obscure tags, or strict artist names, complete with a local rule-based keyword fallback parser to guarantee zero downtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Responsive Web Player Platform:&lt;/strong&gt; Built an HTML5 audio context interface complete with a live API resource monitor (tracking Jamendo's 35,000 free monthly request tier cap) and real-time scrolling telemetry log panels recording every single serverless event transcript.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;This application is being submitted to the following prize categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs:&lt;/strong&gt; For mapping custom voice conversational tools to a local audio state architecture seamlessly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Google AI:&lt;/strong&gt; Using Gemini to turn messy, natural speech commands into highly structured metadata payloads.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>googleai</category>
      <category>elevenlabs</category>
    </item>
    <item>
      <title>Terminal Web 2.0: AI Preflight, Healing, and Safety</title>
      <dc:creator>Rohith Pavithran</dc:creator>
      <pubDate>Sun, 07 Jun 2026 11:45:57 +0000</pubDate>
      <link>https://dev.to/rohithtp/terminal-web-20-ai-preflight-healing-and-safety-dbj</link>
      <guid>https://dev.to/rohithtp/terminal-web-20-ai-preflight-healing-and-safety-dbj</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Terminal Web (&lt;code&gt;py_terminal&lt;/code&gt;)&lt;/strong&gt; is a powerful terminal-based UI web project utilizing Python and the &lt;a href="https://github.com/Textualize/rich" rel="noopener noreferrer"&gt;Rich&lt;/a&gt; library for beautiful terminal output combined with advanced command execution capabilities.&lt;/p&gt;

&lt;p&gt;As part of the Finish-Up-A-Thon, I transformed this tool by integrating an &lt;strong&gt;AI Safety Net&lt;/strong&gt;—a proactive layer designed to protect users from executing destructive shell commands (e.g., &lt;code&gt;rm -rf&lt;/code&gt;, &lt;code&gt;git push --force&lt;/code&gt;) while maintaining a fluid and rapid developer experience. The safety net employs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tier-1 Heuristics:&lt;/strong&gt; Instant, offline protection against common dangerous commands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tier-2 AI Preflight:&lt;/strong&gt; LLM-powered risk analysis that summarizes potential consequences and affected resources for &lt;code&gt;MUTATING+&lt;/code&gt; risk levels.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intelligent Self-Healing:&lt;/strong&gt; A workflow that diagnoses failed commands (e.g., permission errors, missing paths) and suggests safe alternatives to get you back on track.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;&lt;strong&gt;Project Repository:&lt;/strong&gt; &lt;a href="https://github.com/rohithtp/py_terminal" rel="noopener noreferrer"&gt;https://github.com/rohithtp/py_terminal&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;Previously, &lt;code&gt;py_terminal&lt;/code&gt; was an effective CLI wrapper with dual execution modes (interactive and capture) and beautifully formatted outputs. However, it had one critical flaw: it blindly executed whatever bash command was provided, offering no protection against accidental system damage. In a modern development environment where LLMs are sometimes used to generate complex, unverified commands, ensuring those commands are safe to run locally became a high priority.&lt;/p&gt;

&lt;p&gt;To "finish it up" for this hackathon, I drastically leveled up the tool's intelligence and safety by adding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;An AI Preflight Loop:&lt;/strong&gt; Intercepts risky commands, uses LLMs to generate reversibility notes, and blocks execution until explicit user confirmation is given.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Self-Healing Workflow:&lt;/strong&gt; Catches non-zero exit codes, diagnoses the error (e.g., "No such file or directory"), and proposes an LLM-generated fix that gets re-routed through the safety preflight loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite Caching:&lt;/strong&gt; Caches preflight AI evaluations to avoid duplicate API calls and guarantees zero latency for repeated commands.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline Graceful Degradation:&lt;/strong&gt; The UI falls back beautifully to local heuristic warnings when the LLM API is unavailable, ensuring the tool is always usable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Status Capture Utility:&lt;/strong&gt; A built-in telemetry tool to rapidly verify repository and dependency health.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot acted as an exceptionally fast pair programmer throughout this challenge. It was instrumental in scaffolding the &lt;code&gt;pexpect&lt;/code&gt;-based automated test suite for validating the AI Safety Net, mocking out LLM network responses, and quickly laying out the boilerplate for the Rich UI panels. &lt;/p&gt;

&lt;p&gt;When constructing the SQLite caching mechanism, Copilot seamlessly anticipated the database schema I needed and handled boilerplate queries, allowing me to focus entirely on the core business logic of the safety heuristics and healing loop. It transformed complex refactoring and UI layout tasks into smooth, effortless completions.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Team Members:&lt;/strong&gt; &lt;a class="mentioned-user" href="https://dev.to/rohithtp"&gt;@rohithtp&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
    </item>
    <item>
      <title>mnemo — A local-first learning agent that remembers, without leaking</title>
      <dc:creator>Rohith Pavithran</dc:creator>
      <pubDate>Sun, 31 May 2026 09:20:10 +0000</pubDate>
      <link>https://dev.to/rohithtp/mnemo-a-local-first-learning-agent-that-remembers-without-leaking-d49</link>
      <guid>https://dev.to/rohithtp/mnemo-a-local-first-learning-agent-that-remembers-without-leaking-d49</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;: Build With Hermes Agent&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;mnemo&lt;/strong&gt; is a learning-management agent for kids and self-directed learners. It tracks topics, sequences them by prerequisites, schedules spaced-repetition reviews (SM-2), generates quizzes, and adapts the plan over time through a Hermes-driven feedback loop.&lt;/p&gt;

&lt;p&gt;The problem: hosted AI tutors send a child's mistakes, struggles, and reading level to someone else's servers. Browser-only chat loops forget everything the moment the tab closes — no cross-session adaptation, no long-term plan. And LLMs happily hallucinate URLs and quiz answers, so nothing stops an unreviewed link from reaching a 6th-grader.&lt;/p&gt;

&lt;p&gt;mnemo's answer is a three-part stance baked into the schema, not bolted on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local-first by default.&lt;/strong&gt; SQLite (WAL) on-device, 7 tables, 3 migrations. Hermes-3 runs locally via Ollama. A HuggingFace API path exists for constrained hardware but requires explicit opt-in and is clearly labelled in the UI — every hosted call is logged to the &lt;code&gt;events&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two gates before any content reaches the learner.&lt;/strong&gt; Every external &lt;code&gt;resource&lt;/code&gt; starts &lt;code&gt;approved = 0&lt;/code&gt; in the schema. A second gate joins a &lt;code&gt;source_allowlist&lt;/code&gt; table at the SQL layer — even an approved resource is suppressed if its origin isn't a trusted source (&lt;code&gt;khan-academy&lt;/code&gt;, &lt;code&gt;ck12&lt;/code&gt;, internal-curriculum). Both gates have dedicated scenario tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The agent proposes; a human approves.&lt;/strong&gt; Pace changes, resource-type shifts, and any preference write goes through a confirmation gate. The agent literally cannot mutate learner preferences without a human checkbox — enforced via an MCP deny-list, not just UI.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;Pitch deck (single-file Reveal.js, no build step):&lt;br&gt;
&lt;strong&gt;&lt;a href="https://plain-frost-2119.rohithtp.workers.dev/" rel="noopener noreferrer"&gt;pitch.html&lt;/a&gt;&lt;/strong&gt; — 6 slides covering the problem, the Hermes differentiator, architecture, the two-gate safety model, and a realistic dashboard mockup of the shipped UI.&lt;/p&gt;

&lt;p&gt;Quick local run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
sqlite3 learning.db &amp;lt; migrations/001_init.sql
sqlite3 learning.db &amp;lt; migrations/002_quiz_bank.sql
sqlite3 learning.db &amp;lt; migrations/003_source_allowlist.sql
python &lt;span class="nt"&gt;-m&lt;/span&gt; uvicorn webapp.app:app &lt;span class="nt"&gt;--reload&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt; 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8000&lt;/code&gt; for the dashboard, or run a Hermes session end-to-end:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ollama pull hermes3
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/api/agent/session &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"learner_id": 1, "backend": "ollama"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dashboard streams each tool call the agent makes, then refreshes progress and the review queue automatically.&lt;/p&gt;

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

&lt;p&gt;Repository: &lt;strong&gt;&lt;a href="https://gitea.com/rohithtp/mnemo" rel="noopener noreferrer"&gt;gitea.com/rohithtp/mnemo&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Key directories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;webapp/&lt;/code&gt; — FastAPI REST + SSE + static dashboard (25+ endpoints)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mcp_server/&lt;/code&gt; — stdio MCP server exposing 7 tools to Hermes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/agent/&lt;/code&gt; — Hermes session orchestrator, allowlist, memory, insights&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;src/learning/&lt;/code&gt; — SM-2 algorithm, sequencing logic, safety/privacy gates&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;migrations/&lt;/code&gt; — 3 SQL files, schema-version tracked&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tests/&lt;/code&gt; — 69 passing tests (SM-2 unit + integration + API + scenario)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docs/&lt;/code&gt; — full build plan, decisions log, 25-risk register, two-profile docs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  My Tech Stack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;SQLite&lt;/strong&gt; (WAL mode) on-device · libSQL/Turso documented as forward path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; + uvicorn + SSE streaming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent runtime&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Hermes-3&lt;/strong&gt; via &lt;strong&gt;Ollama&lt;/strong&gt; (local) · HuggingFace Inference API fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool surface&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;stdio MCP&lt;/strong&gt; (Hermes) + &lt;strong&gt;WebMCP&lt;/strong&gt; progressive enhancement (Chrome 146+)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Vanilla JS + plain CSS — no framework, no build step&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spaced repetition&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;SM-2&lt;/strong&gt; algorithm, implemented as pure functions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tests&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;pytest&lt;/strong&gt; — 15 SM-2 unit + 18 integration + 31 API + 5 scenario = &lt;strong&gt;69 passing&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Dockerfile&lt;/strong&gt; + systemd unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Language&lt;/td&gt;
&lt;td&gt;Python 3.11+ (3.14 tested)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;All Python deps are pinned in &lt;code&gt;requirements.txt&lt;/code&gt;. The Hermes-3 model digest is pinned by SHA256 and asserted at startup so a silent upstream change can't drift the agent's behaviour mid-session (R02 mitigation).&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Hermes Agent
&lt;/h2&gt;

&lt;p&gt;Hermes-3 is the orchestration brain for the whole adaptive loop. The reason it had to be Hermes specifically, not a stateless prompt loop, comes down to four capabilities:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Tool-calling over a real schema.&lt;/strong&gt; &lt;code&gt;src/agent/session.py&lt;/code&gt; runs a tool-calling loop where Hermes drives a study session by calling the 7 MCP tools (&lt;code&gt;get_progress_summary&lt;/code&gt;, &lt;code&gt;get_ready_topics&lt;/code&gt;, &lt;code&gt;start_topic&lt;/code&gt;, &lt;code&gt;get_next_review_items&lt;/code&gt;, &lt;code&gt;record_review_result&lt;/code&gt;, &lt;code&gt;recommend_resources&lt;/code&gt;, &lt;code&gt;generate_quiz&lt;/code&gt;). Each call is logged to the &lt;code&gt;events&lt;/code&gt; table with arguments and result. The agent isn't generating advice in prose — it's writing to SQLite through audited tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Cross-session memory.&lt;/strong&gt; Every session writes a &lt;code&gt;memory_note&lt;/code&gt; event summarising patterns the agent noticed. The next session's system prompt is injected with the most recent notes for that learner, so Hermes "remembers" that this kid struggles with unlike denominators across sessions, not just within one tab. Memory notes are per-learner and stay on-device.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Pattern analysis + human-approvable proposals.&lt;/strong&gt; Phase 6 added &lt;code&gt;POST /api/agent/insights/{learner_id}&lt;/code&gt;. Hermes reads the event trail across a configurable window, computes &lt;code&gt;avg_quality&lt;/code&gt;, &lt;code&gt;days_active&lt;/code&gt;, resource-type variety, and emits structured proposals like &lt;code&gt;{"type": "pace", "direction": "increase", "reason": "avg quality above 4.0"}&lt;/code&gt;. Each proposal is stored with &lt;code&gt;resolved_at = NULL&lt;/code&gt; and surfaces in the dashboard with Approve / Dismiss buttons. The agent never silently applies pace changes — a human resolves each proposal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Allowlist-enforced tool boundary.&lt;/strong&gt; &lt;code&gt;src/agent/allowlist.py&lt;/code&gt; defines &lt;code&gt;AGENT_ALLOWLIST&lt;/code&gt; and &lt;code&gt;AGENT_DENY_LIST&lt;/code&gt; as constants. &lt;code&gt;set_learner_preferences&lt;/code&gt; is on the deny-list, so even if the model is jailbroken into trying to mutate preferences directly, the session orchestrator raises &lt;code&gt;PermissionError&lt;/code&gt; before the tool runs. This is the difference between "the UI hides it" and "the agent cannot do it."&lt;/p&gt;

&lt;p&gt;The combination — local model + persistent memory + audited tool calls + human-in-the-loop on every state-mutating proposal — is what makes mnemo trustable as a coach for a child, not just for an adult tinkerer. That posture is what the Hermes agent makes practical; a hosted stateless chatbot can't enforce any of it.&lt;/p&gt;

</description>
      <category>hermesagentchallenge</category>
      <category>devchallenge</category>
      <category>agents</category>
      <category>hermesagent</category>
    </item>
    <item>
      <title>StudyTrak // Astra Core – Gemma 4 Challenge Submission</title>
      <dc:creator>Rohith Pavithran</dc:creator>
      <pubDate>Sat, 23 May 2026 17:42:27 +0000</pubDate>
      <link>https://dev.to/rohithtp/studytrak-astra-core-gemma-4-challenge-submission-cc6</link>
      <guid>https://dev.to/rohithtp/studytrak-astra-core-gemma-4-challenge-submission-cc6</guid>
      <description>&lt;h1&gt;
  
  
  StudyTrak // Astra Core – Gemma 4 Challenge Submission
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Build with Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;StudyTrak (Astra Core)&lt;/strong&gt; is an immersive, high-fidelity student spaced repetition planner and neural coaching console. Designed to work completely local-first or proxy securely via remote endpoints, StudyTrak solves the "academic attention fragmentation" problem. It merges class schedules, homework tracker boards, spaced-repetition card decks (SM-2 scheduler), and deep neural academic helpers into a single HUD-inspired dashboard. &lt;/p&gt;

&lt;h3&gt;
  
  
  Core Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;🗓️ &lt;strong&gt;Astra Chrono Scheduler&lt;/strong&gt;: Interactive class blocks and room assignments synced with responsive real-time layout structures.&lt;/li&gt;
&lt;li&gt;🎯 &lt;strong&gt;Spaced Repetition Flashcards&lt;/strong&gt;: Localized FSRS/SM-2 simulator (with grades: &lt;em&gt;Forgot&lt;/em&gt;, &lt;em&gt;Hard Link&lt;/em&gt;, &lt;em&gt;Good Match&lt;/em&gt;, &lt;em&gt;Perfect Spot&lt;/em&gt;) scheduling item intervals adaptively.&lt;/li&gt;
&lt;li&gt;🎙️ &lt;strong&gt;Multi-Modal Capture Panel&lt;/strong&gt;: Scan worksheets (Camera/Photo uploads) or type natural language inputs to automatically parse schedule drafts and pending homework proposals.&lt;/li&gt;
&lt;li&gt;💬 &lt;strong&gt;Subject Advisory Chat Tutor&lt;/strong&gt;: Academic conversational helper grounded directly in defined high-school/college subjects.&lt;/li&gt;
&lt;li&gt;🧘‍♀️ &lt;strong&gt;Empathetic Reflection Journal&lt;/strong&gt;: Conversational weekly debriefs tracking achievements, struggles, and proposed behavioral corrections.&lt;/li&gt;
&lt;li&gt;🔊 &lt;strong&gt;Voice Speech Synthesis&lt;/strong&gt;: Listen to high-fidelity audio versions of daily briefings with an interactive audio player.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  App In Google AI
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AiStudio&lt;/strong&gt;: &lt;a href="https://ai.studio/apps/97a83485-4fc2-43ae-8eb7-b82d63cdb1e9" rel="noopener noreferrer"&gt;https://ai.studio/apps/97a83485-4fc2-43ae-8eb7-b82d63cdb1e9&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;The complete, pristine full-stack source code is published on Gitea at &lt;a href="https://gitea.com/rohithtp/studytrak" rel="noopener noreferrer"&gt;https://gitea.com/rohithtp/studytrak&lt;/a&gt;. You can clone, audit, or deploy the application directly from this repository.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Used Gemma 4
&lt;/h2&gt;

&lt;p&gt;StudyTrak leverages the lightweight yet highly competent capabilities of the &lt;strong&gt;Gemma 4&lt;/strong&gt; model lineup to deliver a privacy-preserving co-pilot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Model Choices and Fit&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 Tiny (E2B - 1.5GB)&lt;/strong&gt;: Configured as the optimal on-device WebLLM/MediaPipe companion. We chose the E2B formulation because it easily fits within the standard modern browser sandbox, enabling sub-150ms real-time token generation for micro-scans and speech transcription parsing without any outbound network latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 4 Standard (E4B - 3GB)&lt;/strong&gt;: Selected for core desktop semantic analysis, empathetic reflection summaries, and structuring homework proposals from handwritten worksheet snapshots. It balances rapid execution speeds with reasoning traits needed to provide high-quality structural mentorship.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context Grounding&lt;/strong&gt;:&lt;br&gt;
Every prompt generated goes through a deep localized context packaging layer, supplying active subject indices, schedule constraints, and historic reflection streaks to completely ground prompt results with zero external leak risk.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
    </item>
  </channel>
</rss>
