<?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: Aleksandra Fedak</title>
    <description>The latest articles on DEV Community by Aleksandra Fedak (@fedachkaa).</description>
    <link>https://dev.to/fedachkaa</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%2F4162968%2F32b04478-187b-4543-9660-c65423262cb0.jpg</url>
      <title>DEV Community: Aleksandra Fedak</title>
      <link>https://dev.to/fedachkaa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fedachkaa"/>
    <language>en</language>
    <item>
      <title>I Built an AI That Wants You to Stop Using It — Meet SIDEQUEST 🌿</title>
      <dc:creator>Aleksandra Fedak</dc:creator>
      <pubDate>Fri, 09 Oct 2026 19:06:51 +0000</pubDate>
      <link>https://dev.to/fedachkaa/i-built-an-ai-that-wants-you-to-stop-using-it-meet-sidequest-641</link>
      <guid>https://dev.to/fedachkaa/i-built-an-ai-that-wants-you-to-stop-using-it-meet-sidequest-641</guid>
      <description>&lt;p&gt;&lt;em&gt;An offline-first AI quest machine designed to become a physical device that prints real-world adventures&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I'm a software developer, so most of my day happens in front of a screen. And after work? More screens — learning new technologies, building side projects, or scrolling through social media.&lt;/p&gt;

&lt;p&gt;Here's the funny part: &lt;strong&gt;throughout the summer, I was hiking in the Tatras almost every weekend, climbing mountains over 2,000 meters high. But as autumn arrived, I got so caught up in work and tech projects that some days I struggled to convince myself to even go for a short walk.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And most solutions to excessive screen time involve... another app.&lt;/p&gt;

&lt;p&gt;So I started wondering: &lt;strong&gt;What if AI could help us disconnect instead of giving us another reason to stay online?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's how SIDEQUEST was born.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not another app. A future physical device.
&lt;/h3&gt;

&lt;p&gt;SIDEQUEST is a software prototype for a &lt;strong&gt;physical, offline-first AI quest machine.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a small device on your desk with a tiny display, directional buttons, a big PRINT button, and a thermal receipt printer.&lt;/p&gt;

&lt;p&gt;You choose your preferences, press PRINT, and a locally running AI model generates a personalized real-world adventure. A paper receipt comes out with three objectives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take the receipt. Leave the screen. Touch grass.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No phone needed during the quest. No notifications. No endless feed.&lt;/p&gt;

&lt;p&gt;The current version is a browser-based simulation of this future device, built to test the AI quest engine and gameplay before developing the hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  How SIDEQUEST works
&lt;/h3&gt;

&lt;p&gt;Choose a duration (15, 30, or 60 minutes), an environment (City, Park, Nature, or Anywhere), and a mode (Calm, Move, Explore, or Surprise).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemma 3 4B&lt;/strong&gt;, running locally through Ollama, generates a quest with exactly three real-world objectives, displayed as a retro-style receipt.&lt;/p&gt;

&lt;p&gt;After completing a quest, you can return to earn XP, unlock badges, and build a daily streak.&lt;/p&gt;

&lt;p&gt;There's also &lt;strong&gt;Doomscroll Escape&lt;/strong&gt; — a shortcut that generates a 15-minute surprise quest when you catch yourself scrolling without a purpose.&lt;/p&gt;

&lt;p&gt;The gamification is deliberately lightweight: it should motivate you to go outside, not keep you interacting with the machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best SIDEQUEST session is the one where you spend almost no time using SIDEQUEST.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;Here's the working web prototype:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/ujM_ZyXljyk" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;The demo shows the current browser-based prototype, including local AI quest generation and the receipt-printing animation. The long-term vision is to bring this experience to a standalone physical device.&lt;/p&gt;

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

&lt;p&gt;SIDEQUEST is open source:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/fedachkaa" rel="noopener noreferrer"&gt;
        fedachkaa
      &lt;/a&gt; / &lt;a href="https://github.com/fedachkaa/sidequest" rel="noopener noreferrer"&gt;
        sidequest
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      AI-generated side quests for the world outside your screen.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🌿 SIDEQUEST&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Print a quest. Leave the screen. Touch grass.&lt;/strong&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;A screenless-first AI quest machine, designed for the physical world.&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;SIDEQUEST is the software prototype of a future &lt;strong&gt;physical AI-powered quest dispenser&lt;/strong&gt; — a small desk device that generates personalized real-world adventures and prints them on thermal paper.&lt;/p&gt;
&lt;p&gt;The vision is simple: &lt;strong&gt;press a button, receive a quest, and walk away from your screen.&lt;/strong&gt; No endless feeds, no notifications, no app demanding your attention.&lt;/p&gt;
&lt;p&gt;The current implementation is a browser-based simulation of that device, powered by a locally running open-weight AI model. It brings together the quest generation engine, receipt-style interface, and gamification system — with the long-term goal of moving the experience into dedicated hardware.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI that gets you offline, not AI that keeps you online.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Built for the &lt;strong&gt;Hacktoberfest 2026 — Touch Grass Challenge&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Demo&lt;/h2&gt;
&lt;/div&gt;

  
    
    &lt;span class="m-1"&gt;sd-hq.mp4&lt;/span&gt;
    
  

  

  


&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Preview&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/fedachkaa/sidequest/docs/images/sidequest-machine.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffedachkaa%2Fsidequest%2FHEAD%2Fdocs%2Fimages%2Fsidequest-machine.png" alt="SIDEQUEST Receipt Machine"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Your next adventure, printed&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/fedachkaa/sidequest/docs/images/sidequest-quest.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Ffedachkaa%2Fsidequest%2FHEAD%2Fdocs%2Fimages%2Fsidequest-quest.png" alt="Generated SIDEQUEST receipt"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The idea&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;We have apps for…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/fedachkaa/sidequest" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The repository includes the backend, AI integration, frontend, persistent storage, automated tests, and local setup instructions.&lt;/p&gt;

&lt;p&gt;You can run the current prototype on your own computer using Ollama and Gemma 3 4B, without a cloud AI API key.&lt;/p&gt;

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

&lt;p&gt;SIDEQUEST uses &lt;strong&gt;FastAPI, Gemma 3 4B, Ollama, Pydantic, SQLite, and vanilla JavaScript&lt;/strong&gt;.&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%2Fd8okwd0rr6rpqe1wzn3y.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%2Fd8okwd0rr6rpqe1wzn3y.png" alt="SIDEQUEST architecture diagram showing the flow from quest preferences through FastAPI, Ollama with Gemma 3, and Pydantic validation to the generated quest receipt, with SQLite storing player progress." width="799" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The user selects their quest preferences, which are sent to a locally running Gemma 3 model through Ollama. The generated response is validated with Pydantic before being displayed as a printable-style receipt.&lt;/p&gt;

&lt;p&gt;SQLite stores quests and player progress, while the frontend handles the receipt animation, XP, badges, and streaks.&lt;/p&gt;

&lt;p&gt;The architecture is designed to evolve into a physical device with a small display, directional buttons, and a thermal printer. The long-term goal is to move AI inference onto the device itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenges and Lessons Learned
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM output validation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gemma generated a 12-minute quest instead of the requested 15 minutes. The response passed Pydantic validation but violated a business rule.&lt;/td&gt;
&lt;td&gt;Introduced &lt;code&gt;QuestService&lt;/code&gt; to validate request-dependent constraints separately from schema validation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Local inference latency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Running Gemma 3 4B locally caused an &lt;code&gt;httpx.ReadTimeout&lt;/code&gt; during testing.&lt;/td&gt;
&lt;td&gt;Added a configurable timeout to &lt;code&gt;OllamaClient&lt;/code&gt; to accommodate slower local inference.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;Open innovation is central to SIDEQUEST because &lt;strong&gt;local AI is a requirement of the product vision, not just a choice of technology.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A device designed to help people disconnect shouldn't need to contact a cloud AI service every time someone wants to go for a walk.&lt;/p&gt;

&lt;p&gt;With an open-weight model like Gemma, I can run inference on my own hardware, experiment with the generation pipeline, and explore what it would take to move that intelligence into a dedicated physical machine.&lt;/p&gt;

&lt;p&gt;That changes what's possible for a small independent project.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Why it matters for SIDEQUEST&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cloud independence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gemma 3 runs locally through Ollama, without a hosted LLM API. The future device aims to work entirely offline.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Privacy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Quest preferences and progress stay on local hardware, without being sent to an external AI provider.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Freedom to experiment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-weight models allow me to explore quantization, smaller models, and edge AI hardware without depending on a cloud provider.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are still challenges — especially hardware limitations, inference speed, and model optimization — but that's what makes building SIDEQUEST exciting.&lt;/p&gt;

&lt;p&gt;We're surrounded by technology designed to capture our attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if we built more technology designed to give it back?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;p&gt;I used Codex as a coding assistant while building SIDEQUEST, focusing my own work on architecture, AI behavior, product decisions, and reviewing the implementation.&lt;/p&gt;

&lt;p&gt;Here's a curated session showing part of the development process:&lt;/p&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Codex"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Building SIDEQUEST's Local Gemma Quest-Generation Boundary&lt;/span&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-scroll"&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;We are implementing the quest generation milestone for SIDEQUEST.&lt;/p&gt;

&lt;p&gt;Current state:&lt;br&gt;
- FastAPI application exists&lt;br&gt;
- QuestRequest and Quest Pydantic models exist&lt;br&gt;
- Tests for the models exist and pass&lt;br&gt;
- Ollama is installed locally&lt;br&gt;
- gemma3:4b is installed and working&lt;br&gt;
- Ollama API is available locally&lt;br&gt;
- httpx is already a project dependency&lt;/p&gt;

&lt;p&gt;Implement the first Ollama integration.&lt;/p&gt;

&lt;p&gt;Requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an AI layer for communicating with Ollama.&lt;/li&gt;
&lt;li&gt;Use httpx directly — do not add the Ollama Python SDK.&lt;/li&gt;
&lt;li&gt;Use gemma3:4b as the default model.&lt;/li&gt;
&lt;li&gt;Keep Ollama-specific code separate from business logic.&lt;/li&gt;
&lt;li&gt;Add a prompt builder for generating SIDEQUEST quests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The prompt must instruct the model to:&lt;br&gt;
- return only structured JSON&lt;br&gt;
- generate exactly 3 tasks&lt;br&gt;
- respect the requested duration, environment, and mode&lt;br&gt;
- use difficulty 1–3&lt;br&gt;
- not require a phone&lt;br&gt;
- not require purchases&lt;br&gt;
- not require entering private property&lt;br&gt;
- avoid dangerous activities&lt;br&gt;
- not assume that specific landmarks, animals, facilities, trails,&lt;br&gt;
  bodies of water, or other environmental features exist&lt;br&gt;
- make every task achievable in a typical environment of the selected type&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse and validate the LLM response using the existing Quest Pydantic model.&lt;/li&gt;
&lt;li&gt;Add appropriate tests. Mock Ollama in tests — tests must not require
a running local model.&lt;/li&gt;
&lt;li&gt;Do NOT create the FastAPI generation endpoint yet.&lt;/li&gt;
&lt;li&gt;Do NOT add retries, fallback models, databases, auth, RAG, or other
unrelated infrastructure yet.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep the implementation small and easy to understand.&lt;/p&gt;

&lt;p&gt;Before changing files, briefly tell me:&lt;br&gt;
- which files you plan to create/change&lt;br&gt;
- the responsibility of each file&lt;/p&gt;

&lt;p&gt;Then implement it.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;Planned changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app/ai/__init__.py&lt;/code&gt; — marks the new AI integration package.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app/ai/prompts.py&lt;/code&gt; — builds the SIDEQUEST-specific prompt from a &lt;code&gt;QuestRequest&lt;/code&gt;; it owns all generation and safety constraints.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app/ai/ollama_client.py&lt;/code&gt; — contains the Ollama HTTP boundary, defaults to &lt;code&gt;gemma3:4b&lt;/code&gt;, calls &lt;code&gt;/api/generate&lt;/code&gt; with &lt;code&gt;httpx&lt;/code&gt;, extracts the generated JSON, and validates it as &lt;code&gt;Quest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tests/test_prompts.py&lt;/code&gt; — verifies requested inputs and every required constraint appear in the prompt.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tests/test_ollama_client.py&lt;/code&gt; — uses &lt;code&gt;httpx.MockTransport&lt;/code&gt;, so it verifies the request and response validation without contacting a running Ollama instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won’t change the FastAPI app or add an endpoint. I’ll make the client injectable enough for clean tests, while keeping its public API small.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;The AI boundary and prompt builder are now in place. I’m running the full test suite next; I’ll also inspect the diff for accidental scope creep and formatting issues before wrapping up.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;Important product context:&lt;/p&gt;

&lt;p&gt;SIDEQUEST is not primarily a conventional web application.&lt;/p&gt;

&lt;p&gt;It is a software prototype/reference implementation for a future&lt;br&gt;
screenless physical device with physical controls and a thermal receipt&lt;br&gt;
printer.&lt;/p&gt;

&lt;p&gt;The intended interaction is:&lt;/p&gt;

&lt;p&gt;select time/environment/mode&lt;br&gt;
→ generate one mystery quest&lt;br&gt;
→ print it as a physical receipt&lt;br&gt;
→ take the receipt&lt;br&gt;
→ leave the screen&lt;/p&gt;

&lt;p&gt;The current browser version will simulate that physical device.&lt;/p&gt;

&lt;p&gt;Keep this hardware-first architecture in mind:&lt;br&gt;
- the LLM generates structured quest data, never presentation/HTML&lt;br&gt;
- quest generation must not depend on the browser UI&lt;br&gt;
- receipt rendering must eventually be independent from quest generation&lt;br&gt;
- a future thermal-printer output adapter should be possible without&lt;br&gt;
  changing the AI layer&lt;br&gt;
- avoid architecture that assumes SIDEQUEST will always be a web app&lt;/p&gt;

&lt;p&gt;Do not implement printer support yet.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Understood. The implementation already keeps the AI layer transport- and presentation-agnostic: it accepts a typed request and returns a validated &lt;code&gt;Quest&lt;/code&gt;, with no FastAPI, browser, HTML, or receipt concerns. That boundary can later feed either a browser receipt renderer or a thermal-printer adapter unchanged.&lt;/p&gt;

&lt;p&gt;I’ll keep printer support out of this milestone and finish verification of the current change.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;I isolated the stall to this sandbox’s Python event-loop startup (&lt;code&gt;asyncio.run(asyncio.sleep(0))&lt;/code&gt; itself hangs), not to the integration. Since this milestone does not require an async client, I’m simplifying the boundary to synchronous &lt;code&gt;httpx.Client&lt;/code&gt;. FastAPI supports calling synchronous application code safely, and this avoids unnecessary async complexity in the first integration.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;The real Ollama integration smoke test exposed an issue:&lt;/p&gt;

&lt;p&gt;httpx.ReadTimeout: timed out&lt;/p&gt;

&lt;p&gt;gemma3:4b runs locally on relatively modest hardware and inference can&lt;br&gt;
take longer than httpx's default timeout.&lt;/p&gt;

&lt;p&gt;Update OllamaClient to use an explicit configurable timeout suitable for&lt;br&gt;
local LLM inference.&lt;/p&gt;

&lt;p&gt;Requirements:&lt;br&gt;
- default timeout: 60 seconds&lt;br&gt;
- make it configurable through OllamaClient.&lt;strong&gt;init&lt;/strong&gt;&lt;br&gt;
- pass it to the real httpx.Client&lt;br&gt;
- do not change the existing injected test client behavior&lt;br&gt;
- add/update tests to verify the configured timeout where appropriate&lt;br&gt;
- do not add retries or other error handling yet&lt;/p&gt;

&lt;p&gt;Keep the change minimal.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;I’ll make one focused production change in &lt;code&gt;app/ai/ollama_client.py&lt;/code&gt;: add a &lt;code&gt;timeout&lt;/code&gt; constructor argument defaulting to 60 seconds and pass it only when creating the real &lt;code&gt;httpx.Client&lt;/code&gt;. I’ll update the client tests to verify both the default and an override, while leaving injected clients untouched.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Updated &lt;code&gt;OllamaClient&lt;/code&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Default timeout of 60 seconds.&lt;/li&gt;
&lt;li&gt;Configurable &lt;code&gt;timeout&lt;/code&gt; constructor argument.&lt;/li&gt;
&lt;li&gt;Timeout passed only to the internally created &lt;code&gt;httpx.Client&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No changes to injected test-client behavior.&lt;/li&gt;
&lt;li&gt;Tests covering the default and a 120-second override.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Verification: 10 tests passed.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;We found a real integration issue during testing.&lt;/p&gt;

&lt;p&gt;QuestRequest requested:&lt;br&gt;
    duration_minutes=15&lt;/p&gt;

&lt;p&gt;but Gemma returned:&lt;br&gt;
    duration_minutes=12&lt;/p&gt;

&lt;p&gt;The response still passed Pydantic validation because Quest.duration_minutes&lt;br&gt;
is intentionally an int. We want to keep it that way because Quest should not&lt;br&gt;
be coupled to the currently supported UI duration options.&lt;/p&gt;

&lt;p&gt;We now need validation for constraints that depend on both the QuestRequest&lt;br&gt;
and the generated Quest.&lt;/p&gt;

&lt;p&gt;Implement this as a small business-logic layer.&lt;/p&gt;

&lt;p&gt;Requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Introduce a QuestService responsible for quest generation business logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;QuestService should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;accept a QuestRequest&lt;/li&gt;
&lt;li&gt;use OllamaClient to generate a Quest&lt;/li&gt;
&lt;li&gt;validate request-dependent constraints&lt;/li&gt;
&lt;li&gt;return the validated Quest&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For now, the only request-dependent constraint is:&lt;br&gt;
quest.duration_minutes must exactly equal request.duration_minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the generated duration does not match the requested duration,&lt;br&gt;
raise a clear domain-specific exception.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Keep responsibilities separated:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OllamaClient = communication with Ollama + parsing/schema validation&lt;/li&gt;
&lt;li&gt;PromptBuilder = prompt construction&lt;/li&gt;
&lt;li&gt;QuestService = business rules involving request vs generated quest&lt;/li&gt;
&lt;li&gt;Quest Pydantic model = structural validation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make OllamaClient injectable into QuestService so the service can be&lt;br&gt;
tested without running Ollama.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add tests for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;matching duration -&amp;gt; Quest is returned&lt;/li&gt;
&lt;li&gt;mismatching duration -&amp;gt; domain exception is raised&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Do not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;change Quest.duration_minutes to Literal&lt;/li&gt;
&lt;li&gt;add retries or automatic regeneration yet&lt;/li&gt;
&lt;li&gt;add the FastAPI endpoint&lt;/li&gt;
&lt;li&gt;add fallback models&lt;/li&gt;
&lt;li&gt;change the prompt&lt;/li&gt;
&lt;li&gt;add unrelated abstractions or infrastructure&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Keep the implementation minimal.&lt;/p&gt;

&lt;p&gt;Before changing files, briefly explain:&lt;br&gt;
- which files you will create/change&lt;br&gt;
- why QuestService is the appropriate layer for this validation&lt;/p&gt;

&lt;p&gt;Then implement it.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;I’ll create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;app/services/__init__.py&lt;/code&gt; — declares the business-service package.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;app/services/quest_service.py&lt;/code&gt; — adds &lt;code&gt;QuestService&lt;/code&gt; and a domain-specific &lt;code&gt;QuestDurationMismatchError&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tests/test_quest_service.py&lt;/code&gt; — tests matching and mismatching generated durations using an injected mock &lt;code&gt;OllamaClient&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No existing production files need changing. &lt;code&gt;QuestService&lt;/code&gt; is the right layer because duration equality is not a structural property of a &lt;code&gt;Quest&lt;/code&gt;; it is a business invariant that only exists when comparing generated output with the originating request. This keeps Pydantic reusable and Ollama transport-focused.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;The service now owns the cross-object invariant and returns the exact validated &lt;code&gt;Quest&lt;/code&gt; instance when it passes. I’m running the service tests alongside the existing model, prompt, and Ollama-client tests, then checking the diff for formatting problems.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        12 of 12 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;


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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — SIDEQUEST uses Gemma 3 4B for local AI-powered quest generation.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
