This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
Saathi is a desktop app for solo yoga teachers. It runs Gemma locally through Ollama and does the three jobs that eat a teacher's evenings.
- Class prep. Pick tonight's class and who is attending. Saathi drafts a sequence in Sanskrit and English, plus a callout for every student with an injury or surgery: which poses to avoid and which modification to offer, citing their actual condition.
- After class. Type a few quick notes ("Ravi's knee felt better, Meera skipped inversions, everyone loved bhramari"). Saathi writes a progress log entry for each student and drafts a short follow-up email in the teacher's voice. It never sends anything itself.
- Newsletter. It turns the week's notes into a newsletter draft that never names a student or mentions a health detail.
It ships as a 6 MB native Mac app.
Who would pay for it
I built this for a yoga teacher I build software for. She teaches six 45-minute classes a week over Google Meet, alone. I built her booking site, so I know what it stores: every student's age range, experience, goals, injuries and surgeries.
That last part is the whole reason this is local. Those health notes are the most useful input for planning a safe class, and also the last thing she should paste into a hosted chatbot. Every solo teacher, physio, personal trainer and tutor who keeps client notes has the same problem. A flat monthly price from them is easy to justify when the alternative is an evening of planning and follow-up emails after each teaching day.
Demo
All screenshots use a made-up roster.
Code: https://github.com/harsh8398/saathi-yoga-copilot
How I built it
- Tauri 2 shell with a React and TypeScript front end. It is a small native app, not a bundled browser.
-
Ollama serves Gemma on localhost. The app calls
/api/chatwith a JSON schema in theformatfield, so Ollama constrains the output and I get a typed plan back instead of text I have to parse. - SQLite (via the Tauri SQL plugin) holds students, sessions and progress logs on the teacher's disk.
- Students import straight from her existing booking database's
studentstable, or get added by hand.
A few things I learned the hard way:
-
Gemma 4 thinks by default. My first call returned an empty reply because the model spent its whole token budget on hidden reasoning. Passing
think: falsefixed it. -
A schema is a contract only if you close the loopholes. My first plan had the right 45 minutes and good per-student callouts, but every block had an empty
poseslist, because the schema allowed it. AddingminItems: 2and saying what each block must contain fixed it. - Property order in the schema is generation order. Ollama writes the JSON in the order the schema lists its fields. I moved the per-student callouts to the front, so the part a teacher cares about most is what she sees first.
- A tab switch should not waste five minutes of GPU. I switched tabs mid-generation, the request kept running in the background, and my second click queued behind it. The app now keeps every screen mounted, streams the reply, and has a Stop button that aborts the request.
Why open matters here
Privacy that is structural, not promised. The student's injuries never leave the machine. There is no server to breach, no vendor privacy policy to read, and no data-processing agreement for a one-person studio to sign. For health information in Canada, that is a much easier conversation than "we send it to an API, but we promise".
Cost that fits a $10 class. Her first class is $10 CAD. A per-token bill that scales with every plan and every email makes this a poor business for someone teaching six classes a week. Local inference costs her electricity. Two full plans took my battery from 94% to 92%.
A model she can change. The model is a setting. I started on the default gemma4:e4b build, which was 9.5 GB and too heavy for a 16 GB laptop, moved to the 6 GB e4b-it-qat build, and nothing else in the app changed. If a better or smaller open model lands next month, she swaps it in. If she wants it to sound more like her, the voice lives in a prompt she can edit, and later a fine-tune on her own newsletters is possible without asking anyone's permission.
Where local was worse
I want to be straight about this because it is the real tradeoff.
On my M1 Pro with 16 GB, Gemma generated at about 2 to 4 tokens per second. Without streaming, a full plan took around six minutes of staring at a spinner. With streaming, the first callouts show up after roughly a minute and the plan fills in as it is written, but the last plan I timed, for three students, still took 9 minutes 11 seconds. A hosted API would answer in seconds.
That is the honest price of keeping the data home. For a teacher who plans a class once, it is tolerable. It is not something I would call fast, and a smaller model or a faster machine would change it.
The output has rough edges too, and I would not hide them from a customer:
- The blocks added up to 40 minutes, not the 45 I asked for. A schema can force a field to exist, but it cannot force a sum.
- For a student with knee arthritis the callout said to avoid Warrior II and Triangle, and then the sequence included both as chair variations. That is arguably the modification, but it reads as a contradiction, and it is exactly why every plan says the teacher makes the final call on safety.
Prize categories
Best Use of Gemma. Gemma does all the reasoning: structured class plans, per-student modifications grounded in the student's own notes, and warm follow-ups in the teacher's voice, constrained by a JSON schema, entirely on-device.
Related reading
I found Building Local-First AI Apps: What Changes When the Data Stays on the Device by sanskarcodes useful while thinking through what changes when nothing leaves the machine.




Top comments (0)