DEV Community

Abhijeet Mohanta
Abhijeet Mohanta

Posted on AI-assisted

Killedar: a fort guide that works where the signal dies

Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass Submission 🌿

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass

What I Built

Try it: killedar.onrender.com · Code: github.com/abhijeetkaze/killedar

Demo :

Halfway up Rajgad the bars on your phone go one by one. By Padmavati Machi you're carrying a very expensive torch.

That's the problem with every AI trek assistant I could imagine building on a hosted API: it works in Pune and stops working at the trailhead, which is exactly where you need it.

Killedar (किल्लेदार, the keeper of a Maratha fort) is an offline trek companion for the Sahyadri forts around Pune: Sinhagad, Rajgad and Lohagad to start. It's a PWA with three screens, and all three work in airplane mode:

  • Plan: pick a fort and a trail and enter the last bus back from the base village. Killedar predicts your climb time from your past hikes and works backwards from that bus, so it tells you when to leave Pune and not just how long the climb takes.
  • Ask: "Is there water on Padmavati Machi?" "Is the Chor Darwaja route OK for beginners?" Answered by Gemma running on the phone, grounded only in that fort's notes, in English or Marathi.
  • Listen: the fort's history read aloud in English or Marathi, so you can look at the walls instead of the screen.

On Wi-Fi at home you tap Use offline once. Killedar saves the app, the fort notes, your predicted climb times, the narration and the Gemma model, shows the progress, and turns green when it's ready. After that, airplane mode is fine.

Who it's for: the weekend crowd that leaves Pune before dawn to climb a fort, from first-timers on Sinhagad to regulars on Rajgad, and loses signal somewhere on the way up.

How it gets you off the screen: everything in Killedar is built to be put away quickly. Plan takes a minute the night before and gives you one time to remember: Leave Pune by 10:48. On the fort, a question gets an answer of about 80 words, and the history plays aloud while you look at the walls. The best session is a short one.

Demo

Live app: killedar.onrender.com. Open it on your phone, tap Use offline, then try it in airplane mode. Plan and Listen work in any modern browser. Ask needs WebGPU: Chrome on Android, or desktop Chrome with a supported GPU.

Killedar on a phone: Plan, offline setup, Listen
Left: Plan for Rajgad, working back from a last bus of 18:30 (an example; the app never guesses bus times) with TabPFN's predicted climb time. Middle: the one-time offline setup, partway through downloading Gemma. Right: Listen, playing the ElevenLabs narration in Marathi.

Code

GitHub logo abhijeetkaze / killedar

Offline trek companion for Sahyadri forts — Gemma on-device, TabPFN climb-time prediction. DEV Hacktoberfest 2026.

Killedar · किल्लेदार

The keeper of a Maratha fort — this one fits in your pocket and works where the network doesn't.

An offline trek companion for Sahyadri forts near Pune. Plan at home in a minute, then put the phone away. On the fort, with no signal, ask questions and hear the fort's story — answered by Gemma running on your phone.

Built for the DEV Hacktoberfest 2026 Open-Source AI Challenge, Week 1: Touch Grass Repository started 2026-10-10.

What it does

Screen Works offline What happens
Plan Yes Pick a fort and trail. Killedar predicts your climb time from your past hikes (TabPFN, precomputed) and works backwards from the last bus so you know when to leave Pune.
Ask Yes Questions answered on-device by Gemma via WebLLM, grounded only in that fort's notes. English or Marathi.
Listen Yes Fort history read aloud — ElevenLabs narration bundled
…

Repository: github.com/abhijeetkaze/killedar

A static PWA, plus a little Python that runs on my laptop. There is no backend. Both halves are ports-and-adapters with a pure domain at the centre:

web/  PWA, zero runtime deps             api/  Python tooling, not a server
  domain   timing, prompt, model pick      domain     features, Naismith baseline
  ports    LanguageModel, Predictor        adapters   TabPFN, hikes CSV, fort trails
  app      planTrek, askFort, offline      evaluation leave-one-out vs Naismith
  adapters WebLLM (Gemma), table, data     precompute → web/public/predictions.json
Enter fullscreen mode Exit fullscreen mode

95 web tests and 23 Python tests. CI runs both on every push, including the real TabPFN model.

How I Built It

Gemma, on the phone

The Ask screen runs Gemma through WebLLM, which compiles models to WebGPU. WebLLM is imported from a CDN at runtime, so the web app has no runtime dependencies: TypeScript compiles straight to ES modules and there's no bundler.

Which Gemma? That depends on the phone, so the choice is a small pure function. It takes the newest supported generation that fits under ~2.6B parameters, uses q4f16 if the GPU supports shader-f16 (half the memory of q4f32), and avoids WebLLM's -1k builds, because the fort notes plus the question don't fit in 1,024 tokens.

My first pick was Gemma 3 1B: the newest and smallest, a 600 MB download. It loaded, and then answered "What happened here in history?" with "- Sinhagad" fifty times. Gemma 3 mixes 512-token local-attention layers with global ones, and WebLLM 0.2 can give the whole model only one attention window. Switch it off and the local layers read the full ~2,000-token fort prompt they were never trained on. Switch it on and every layer forgets the fort notes after 512 tokens. So Killedar runs Gemma 2 2B (q4f16, about 1.5 GB, downloaded once), whose local window equals its context. It's a bigger download, but its answers come from the notes.

Small models make things up, so the prompt is built to keep the answers short and true:

  • The fort data is rendered as compact prose notes, not JSON. Small models follow prose better, and it saves context.
  • The instruction says answer only from the notes; if they don't cover it, say so, and never invent bus times, distances or phone numbers. On a mountain, a confident wrong answer is worse than "I don't know".
  • Answers are capped at about 80 words, because the reader is walking.

TabPFN: a model of you, from a few dozen rows

Naismith's rule (an hour per 5 km plus an hour per 600 m of climb) is the classic estimate, and it was written for Scottish hills, not for a laterite staircase at 9 a.m. in October humidity. The useful question isn't "how long does Rajgad take" but "how long does Rajgad take me".

That's a regression problem with almost no data: one person's hike history, a few dozen rows at best. It's exactly the case TabPFN is made for. TabPFN is a pre-trained tabular foundation model, so "fitting" it on a few dozen rows is in-context learning. There's no training loop and nothing to overfit with hyperparameters.

Features: distance, elevation gain, start hour, group size. Target: trailhead-to-summit minutes. The evaluation is leave-one-out, so each hike is predicted by a model that never saw it.

To be upfront: the hike history in the repo today is a synthetic placeholder, 25 climbs of the app's five real trails by a made-up hiker who is about 20% slower than Naismith and slows down in big groups and midday heat. My own hikes replace it next. On that placeholder data:

Mean absolute error (leave-one-out, 25 hikes)
Naismith's rule 35.9 min
TabPFN 9.4 min

That only shows TabPFN can recover a personal pattern from 25 rows. The real test is my own hikes, and swapping them in is one CSV and two commands.

My first version served TabPFN from a small web API on Render. It worked, but it was the wrong shape twice over. It needed signal, which is the one thing this app can't assume. And PyTorch plus TabPFN sat right at the 512 MB limit of a free instance, so every prediction knocked the server over.

The fix was to notice how small the question space is. The app only ever asks about its own trails, at a start hour between 4 and 14 and a group of 1 to 30. That's about 1,650 questions. So TabPFN answers all of them ahead of time, on my laptop, in about 13 seconds:

python -m killedar_api.precompute data/hikes.csv   # → web/public/predictions.json (7 KB)
Enter fullscreen mode Exit fullscreen mode

The app ships that file and the service worker caches it. The Plan screen looks up the predicted climb time in about 20 ms, online or on the summit, and anything outside the table falls back to Naismith. TabPFN still does the learning; the phone just reads its answers.

Narration with ElevenLabs

The Listen screen plays each fort's history in English and in Marathi, narrated by ElevenLabs. The narration is generated once, at build time, by web/scripts/generate-audio.ts: it sends each history to the ElevenLabs text-to-speech API and writes the MP3s into the app. Six clips, 1.4 MB in all, saved for offline with everything else. On the fort they play with no signal and no API key.

Marathi was the catch. The usual eleven_multilingual_v2 model rejects it outright (does not support language_code 'mr'), so the script uses eleven_v3 and sends each clip's language code, which means the Marathi history is read as Marathi.

Offline setup you can see

A Use offline button in the header opens a short checklist: the app files first (about 2 MB: shell, fort notes, climb-time table, narration), then the Gemma model (about 1.5 GB). Each step has its own progress bar, the button becomes a progress ring, and it turns into a green Offline ready check when everything is on the phone. If the phone runs out of storage, it says so plainly. If the browser has no WebGPU, Plan and Listen still go offline and the checklist says why Ask can't.

Facts about real places

Fort data is facts about real mountains, and wrong facts there have consequences. Every fort file cites its sources and stays "verified": false until it's been checked. Bus times are left blank on purpose: the app asks you for the last bus rather than guessing one, because rural ST timings change and a hallucinated 6:30 p.m. bus is how people end up walking down in the dark.

Deploy

One render.yaml blueprint deploys the PWA to Render as a static site: the app, the fort notes, the TabPFN table and the narration. That's the whole deployment. Gemma runs on the phone, so there's no server to keep awake or pay for.

Why Does Open Innovation Matter?

Because the top of a Sahyadri fort has no signal, and that settles the argument. With a closed API, Killedar's Ask screen would be a spinner at exactly the moment you need it. With open weights, it answers in airplane mode.

A closed model behind an API is a service. It's there when the network is, when the bill is paid, and when the provider still offers it. On Rajgad's Balekilla none of those hold. Open weights are an artifact: once they're on the phone, nobody can switch them off, rate-limit them or price them per question.

That's also why the one closed service in Killedar, ElevenLabs, runs only at build time. Its output is an MP3 that ships with the app, so nothing on the mountain depends on it.

Running on the device also keeps things private. A hiker's history (where they go, when, how slowly) goes into TabPFN on their own laptop, and only the predicted minutes ship with the app. Questions asked on the fort never leave the phone at all.

It also matters who gets to build. Everything in Killedar is open: Gemma's weights, WebLLM's compiler, TabPFN's model, the fort notes and their sources. So a trekking group in Nashik can fork it, add Harishchandragad, correct my Marathi, and ship their own version without asking anyone's permission or paying for anyone's API. Forts were built to stand on their own when cut off. The tool for climbing them should too.

Prize Categories

  • Best Use of Gemma: Gemma 2 2B runs entirely on the phone through WebLLM and answers questions about each fort in English or Marathi, offline, grounded only in that fort's cited notes.
  • Best Use of TabPFN: TabPFN predicts a hiker's personal climb time from their hike history (a CSV), evaluated leave-one-out against Naismith's rule, and its predictions are precomputed into the app so Plan uses them offline on the fort.
  • Best Use of ElevenLabs: ElevenLabs generates the narration for every fort's history, in English and Marathi (eleven_v3), at build time. The clips ship with the app and play on the summit with no signal.
  • Best Use of Render: Render hosts the app's front end, the whole PWA deployed as a static site from one render.yaml blueprint.

Built solo in Pune, October 2026. Fort data is unverified. Check locally before you climb, and send corrections.

Agent Session:

Killedar session <synthetic>
You

<ide_opened_file>The user opened the file ~/Desktop/poc/killedar/render.yaml in the IDE. This may or may not be related to the current task.</ide_opened_file>

Paste this into the new Claude Code session, with the killedar repo connected or killedar.zip attached:

Project: Killedar (किल्लेदार) — offline trek companion for Sahyadri forts near Pune.
Entry for DEV Hacktoberfest 2026 "Touch Grass" challenge. Deadline: Mon Oct 12, 12:29 PM IST.
Personal project — no company/work code or material in this repo.

Read CLAUDE.md first; it has the architecture rules (hexagonal, pure domain, tests for
everything) and all commands. Follow it.

Current state (3 commits, all tests green):
- web/  PWA, TypeScript, no bundler. Plan / Ask / Listen screens. Gemma runs on-device via
  WebLLM (loaded from CDN). Service worker precaches shell + fort JSON + audio.
  72 node:test tests. `npm run check` = typecheck + tests + build.
- api/  Starlette service, POST /predict → TabPFN climb-time prediction from data/hikes.csv,
  falls back to Naismith's rule. 30 unittest tests (real-TabPFN test skipped — wasn't installed).
- render.yaml (Render blueprint), .github/workflows/ci.yml, README.md.
- Forts: sinhagad, rajgad, lohagad — verified:false, bus times null on purpose.

Built in a sandbox without npm/PyPI access, so these are NOT yet verified:
1. `cd web && npm install && npm run check` — commit package-lock.json.
2. `cd api && pip install -r requirements-dev.txt && python -m unittest discover -s tests -t .`
   — make the real TabPFN test pass; fix the adapter if the tabpfn API differs.
3. WebLLM: confirm the CDN URL (https://esm.run/@mlc-ai/web-llm@0.2) works and that
   pickGemmaModel() finds a Gemma id in prebuiltAppConfig.model_list. Fix if needed.

Your tasks, in order:
1. Do the 3 checks above, fix anything broken, keep all tests green.
2. Push to GitHub (github.com/abhijeetkaze/killedar, branch main); make sure CI passes.
3. Help me deploy with render.yaml (api + static site); update KILLEDAR_ALLOWED_ORIGINS /
   KILLEDAR_API_URL to the real Render URLs.
4. I'll add my real hikes to api/data/hikes.csv — then run
   `python -m killedar_api.evaluation data/hikes.csv` and give me the numbers.
5. Optional if time: `npm run audio` with my ElevenLabs key.
6. Draft the DEV post (docs/dev-post.md) using the challenge template sections:
   What I Built / Demo / Code / How I Built It / Why Does Open Innovation Matter? /
   Prize Categories (Gemma, TabPFN, Render, ElevenLabs). Writing quality is judged most —
   angle: no signal on a Sahyadri fort, so a closed API is useless; open weights run on the
   phone, free and private. Leave placeholders for my trek photo, demo video and real numbers.

Small commits, run checks before each. Ask me before anything irreversible.

lets start

Agent

Failed to refresh OAuth token: another Claude Code process is refreshing it or exited mid-refresh. This is usually transient; retry in a minute, and if it persists close other Claude Code processes or sign in again

You

hi

Switched to claude-opus-5-5
Agent

Your pasted brief describes an earlier state. The git log shows more has been done since (lockfile committed, TabPFN pinned, dev post drafted), so I'll check where things actually stand before redoing anything.

Agent
Agent

Next I'm running both test suites and checking CI on the last push.

Agent
Agent

CI is green on main, including the new real-TabPFN job. Locally there's just no node_modules or venv yet. I'll install and run the web check to confirm it locally too.

Agent
Agent

Hi. Most of the brief is already done, from earlier sessions:

Task Status
1. Checks (npm, TabPFN, WebLLM) Done. Locally just now: 73/73 web tests pass and the build succeeds. CI runs the real TabPFN tests (7/7 pass), and the Gemma model picking was fixed in e0ae30b.
2. Push and CI Done. main matches origin/main and the latest CI run on main passed.
3. Render deploy Not started. I need you for this one.
4. Real hikes evaluation Waiting on your data. There's no api/data/hikes.csv yet, only the synthetic sample.
5. ElevenLabs audio Not done. No audio files exist yet.
6. DEV post Drafted in docs/dev-post.md. Needs your real numbers and media.

The CI evaluation showing TabPFN at 7.0 min error vs Naismith at 39.6 min ran on the synthetic sample data. Don't quote those numbers in the post.

Next step, the Render deploy:
1. In the Render dashboard, go to New → Blueprint and pick abhijeetkaze/killedar. It reads render.yaml and creates killedar-api and killedar.
2. Send me the two URLs Render assigns. render.yaml currently assumes killedar.onrender.com and killedar-api.onrender.com. If those names were taken, Render adds a suffix, and I'll update KILLEDAR_ALLOWED_ORIGINS and KILLEDAR_API_URL and push.
3. The API is on the free plan (512 MB), and torch plus TabPFN may run out of memory there. Without a hikes.csv it falls back to Naismith anyway, so free is fine to start. Once your real hikes are in, we may need the starter plan.

In parallel, you can add your hikes to api/data/hikes.csv using the same columns as hikes.sample.csv. I'll then run the evaluation and give you the numbers. If you want narration, put ELEVENLABS_* in your shell environment and I'll run npm run audio.

Which do you want to start with?

You

i currently dont have render or elven labs keys let me know if you need them but rest everything should work

Agent

I'll check that everything works without either key. That means the Listen screen with no audio files, and the app with no API URL.

Agent
Agent

The app works without either key, so you don't need to get them for it to function:

  • Without ElevenLabs: Listen falls back to the browser's built-in text-to-speech when there's no mp3 (views.ts:305). Your build has 0 audio files and still builds and passes all tests.
  • Without the API: if KILLEDAR_API_URL isn't set at build time, Plan uses Naismith's rule (main.ts:11).

What each one gets you:

  • Render doesn't need a key. You sign up at render.com with your GitHub account (free, no card for the free plan). Then go to New → Blueprint and pick the repo. It's the only way to get a public URL for the "Try it" link and to qualify for the Render prize category. It takes about 5 minutes, and I can't do it for you because it's a login flow.
  • ElevenLabs is optional. The free tier is enough for six short narrations (3 forts × English and Marathi). If you skip it, the post has two TODO-marked ElevenLabs lines to delete and you drop that prize category.

The rest needs your input, not keys:
1. Your real hikes in api/data/hikes.csv. This matters most, because the post's TabPFN numbers are still XX.
2. Your own words in the post: the trek story at line 18, a photo and a demo video.

What I'd do now:
- Set up Render (5 min).
- Add your hikes. Even 10–15 rows works. I'll run the evaluation and fill the table in the post.
- Send me 2–3 sentences about the trek, and I'll polish the post around them.

Want me to start tightening the post's wording now, leaving the personal parts as placeholders?

You

can you gnerate hikes.csv based on current convet read the claude.md and this repo

Agent
Agent

I don't think I should generate hikes.csv. By the repo's own rules, that file is where your real hikes go. A generated version already exists: hikes.sample.csv, produced by make_sample.py. Making another one wouldn't add anything new, and it would cause two problems:

  • The post would be making a false claim. It says "TabPFN on my hikes: XX min". Filling that from invented hikes would present made-up results as your own, and writing quality is what's judged. A judge who asks how you got the numbers would catch it.
  • The model wouldn't learn anything real. With generated data, TabPFN only learns the formula I picked, like the "20% slower than Naismith" in make_sample.py. The 7.0 vs 39.6 minute result in CI is just TabPFN recovering that formula.

What I can do is make filling in your real hikes take about 10 minutes. Each row needs five values:

distance_km,elevation_gain_m,start_hour,group_size,duration_min

The first two are already in the fort files for routes you may have done, such as Sinhagad from the base village (2.7 km, 600 m) or Rajgad from Pali (4 km, 700 m). For each trek you remember, you only need:
- fort and route, e.g. "Rajgad via Pali", which gives me the distance and climb
- start hour: rough is fine; phone photo timestamps help
- group size
- time to the top in minutes: your first summit photo minus your first photo at the base is a good proxy

Send them like Sinhagad, 7am, 4 people, 95 min, one per line, and I'll build the CSV. For forts not in the app, I'll look up the distance and climb and mark them as estimates. If you have Strava or GPX exports, drop them in and I'll parse them instead.

Even 8–10 honest rows are enough. If TabPFN only roughly ties with Naismith on that little data, the post can say so honestly, which reads better than a suspiciously large win.

If you have no past hike records at all, the honest fallback is to keep the sample data and say in the post that the numbers are synthetic and you'll add real ones after your next trek. Which of these works for you?

You

https://killedar-api.onrender.com
its live but not seeing any thing

Agent
Agent
Agent

Nothing's wrong. That URL is the API, which has no page to show. It only has two routes, /health and /predict (app.py:47), so opening the root in a browser gives "Not Found".

The app itself is at https://killedar.onrender.com.

I tested the live deploy:

Check Result
GET /health {"status":"ok","model":"naismith"}
POST /predict (Sinhagad, 2.7 km, 600 m) Returns 92 min.
CORS from killedar.onrender.com Allowed.
Static site and sw.js Both load (HTTP 200), and the API URL is built into the page.

Render gave you the exact names in render.yaml, so there's nothing to update or push.

The API reports "model":"naismith" because there's no hikes.csv yet. Once your real hikes are committed, it will switch to TabPFN on the next deploy. Watch the memory on the free plan when that happens.

To try it on your phone, open killedar.onrender.com and use "Add to Home Screen". Then switch to airplane mode and check that Plan and Listen still work. Ask needs one online visit first to download Gemma. A screen recording of that airplane-mode test would make a good demo video for the post.

You

now that u have added the csv, can we use that ? also wgat this model naismith ?

Agent
Agent

I haven't added a CSV. I decided against generating one in my last-but-one reply, and there's still no api/data/hikes.csv, only the synthetic hikes.sample.csv. That's why the API reports "model":"naismith": it looks for data/hikes.csv, doesn't find it, and falls back.

What "naismith" means

Naismith's rule is a hiking rule of thumb from 1892, made by a Scottish mountaineer: 12 minutes per km walked, plus 1 minute per 10 m climbed. For Sinhagad, 2.7 km × 12 + 600 m ÷ 10 = 32 + 60 = 92 min, which is exactly what your live API returned.

It's the app's safe fallback. It needs no data or internet, so the phone uses it offline too. Its weakness is that it assumes the same fit hiker for everyone, at any hour, in any group size. The whole point of TabPFN here is to learn how you differ from it: slower on steep Sahyadri rock, slower at noon, slower in big groups.

Can we use the sample CSV?

Technically yes: setting HIKES_CSV=data/hikes.sample.csv on Render would switch the live API to TabPFN today. But I'd advise against it:
- It would make the app predict times for a made-up hiker, labelled as personalised.
- Torch plus TabPFN may run out of memory on the free plan, and right now the API is up and stable.

What I recommend

Keep the live API on Naismith until you have real hikes. The app is honest as it stands: it says it uses the rule of thumb when it has no personal data. Send me your treks, even rough ones like Sinhagad, 7am, 4 people, ~95 min. I'll build the real hikes.csv, run the evaluation, put the numbers in the post, and push. Render will then redeploy with TabPFN.

If you'd still rather have TabPFN live on the sample data for the demo, I can do that. The post would need to say clearly that the data is synthetic. Which way do you want to go?

You

i think you should create the hikes.csv for now

Agent

The file will be clearly marked as a placeholder, and I'll base it on the app's real trails so the demo looks realistic. First I'll check how the API loads it and what TabPFN costs on Render.

Agent
Agent
Agent
Agent

Now I'll extend the sample generator so it can also write hikes.csv from those trails, using the same fake-hiker formula.

Agent
Agent

Next I'll confirm the sample file still comes out byte-identical, generate hikes.csv, and set up a venv to run the evaluation.

Agent
Agent

The sample file is byte-identical, so tests and CI aren't affected. Local Python can't create a venv, so I'll look for another way to install the dependencies.

Agent
Agent
Agent

All 30 API tests pass with real TabPFN installed. Last, I'll measure how much memory the API takes once TabPFN has fitted, to see whether it fits Render's 512 MB free plan.

Agent
Agent
Agent

Git has no author set on this machine. I'll reuse the author from your earlier commits for this one commit, without changing your git config.

Agent
Agent
Agent

I created api/data/hikes.csv and committed it locally. It's not pushed yet, because pushing redeploys your live API and that could break it, as explained below.

What's in the file: 25 made-up hikes, five on each of the app's five real trails (Sinhagad, the two Rajgad routes, the two Lohagad routes). Start hours and group sizes vary, and the times come from the existing fake-hiker formula: 20% slower than Naismith, slower in big groups and midday heat. You can regenerate it with python data/make_sample.py --trails, and hikes.sample.csv came out byte-identical, so tests and CI are unaffected. The README, CLAUDE.md and the commit message all say the file is synthetic.

Test results: all 30 API tests pass with real TabPFN, and the web check passes 73/73. The evaluation:

25 hikes, leave-one-out mean absolute error:
TabPFN 9.4 min
Naismith 35.9 min

TabPFN is only recovering the formula I wrote, so these numbers are fine for showing the app works but not as "my results" in the post.

The risk: fitting TabPFN needs about 538 MB of memory, and Render's free plan allows 512 MB. If you push now, the redeploy will probably run out of memory. Render normally keeps the old, working deploy if the new one fails its health check, but I can't guarantee that. You have three options:

  1. Push and try the free plan. Most likely outcome: the deploy fails and the live API stays on Naismith.
  2. Upgrade killedar-api to Starter (about $7/month, 512 MB → 2 GB), then push. TabPFN should then run live. That's your call because it costs money.
  3. Keep it local. Don't push and stay on Naismith in production. You still have TabPFN working locally for the post and demo.

Which one do you want?

I committed under your name (abhijeetkaze <[REDACTED]>), set for this one commit only. Git on this machine has no author configured, and the earlier commits were authored as "Claude". Tell me if you'd prefer a different name.

You

push now

Agent
Agent

The commit is pushed. Now I'll watch CI and the Render redeploy.

Agent
Agent

The live API switched to TabPFN. I'll check CI and send a real prediction request.

Agent
Agent

CI passed, but the first /predict returned a 502. I'll check whether the API is restarting.

Agent
Agent

The push went through and CI passed. Render redeployed, and at 09:25 /health reported "model":"tabpfn". So the app started and fitted TabPFN within the free plan.

But the first real /predict returned a 502. That's most likely the memory limit I flagged: fitting fits in 512 MB, but running a prediction needs extra memory and Render kills the process. I'm checking whether it keeps crashing and restarting; you moved that check to the background, and I'll report once it finishes.

Your app keeps working either way. When the API fails, the PWA falls back to Naismith (main.ts:11); CI tests that fallback on every push. Users just won't get the TabPFN number.

If the 502s continue, the fixes are:
- Upgrade killedar-api to Starter in the Render dashboard. That's the real fix if you want TabPFN live, and it costs about $7/month.
- Free option: set HIKES_CSV to a missing path in Render's environment settings to go back to Naismith live. You'd keep TabPFN for the local evaluation and the post.

You

<task-notification>
<task-id>bjpf5xr6l</task-id>
<tool-use-id>toolu_01VaqqKhU4jtzkYVT7x1eKgJ</tool-use-id>
<output-file>/tmp/claude/-home-user-Desktop-poc-killedar/78ac7393-b8b5-4de6-ba58-6251fc023415/tasks/bjpf5xr6l.output</output-file>
<status>completed</status>
<summary>Background command "Poll API health and retry one prediction" completed (exit code 0)</summary>
</task-notification>

Agent
Agent

The API is stuck in a crash loop on the free plan, and every /predict call crashes it.

Time /health
09:26–09:27 200, tabpfn
09:27:33 No response (process killed)
09:28–09:29 502 (restarting)
09:29:31 200, tabpfn again
Then /predict 502 again

TabPFN fits within 512 MB when the app starts, but running a prediction pushes it over and Render kills the process. It refits on restart, and the next prediction kills it again. The PWA still shows Naismith times because of the fallback, but the API is down for a minute after every request.

There are three ways to fix it:

  1. Upgrade killedar-api to Starter (about $7/month, 2 GB) in the Render dashboard. No code change, and it's the most reliable way to get TabPFN live.
  2. Try to make TabPFN fit in 512 MB for free. I'd lower its ensemble size (n_estimators, default 8) and use its low-memory mode in tabpfn_model.py. Then I'd measure peak memory locally for fit plus prediction before pushing. I can't promise it gets under 512 MB, and the predictions get slightly less accurate.
  3. Go back to Naismith live for now by setting HIKES_CSV to a missing path in Render's settings. It's free and stable immediately, and TabPFN stays in the local evaluation and the post.

I'd try 2 first, since 25 rows is a tiny dataset and a smaller ensemble should cost little accuracy, and fall back to 3 if it doesn't get under the limit. Should I go ahead?

You

what is tabpfn and why we need it ?

Agent

What it is

TabPFN is an open-weights AI model for tables of numbers, from Prior Labs. You give it a small spreadsheet of past examples and it predicts the missing column for a new row. It was pre-trained on millions of artificial datasets, so it can learn from a handful of rows instantly. That suits personal hiking data, where you'd have 10–30 rows, not 10,000.

What it does in Killedar

The Plan screen predicts how long you'll take to climb a fort, so it can tell you when to leave Pune to catch the last bus back.

  • Naismith's rule is a fixed formula, the same for everyone: 12 min per km plus 1 min per 10 m climbed.
  • TabPFN learns from your own past hikes (distance, climb, start hour, group size, actual time). If you're always 25 minutes slower than the formula at midday with a big group, it picks that up.

Do you need it?

For the app to work, no. On the fort there's no signal, so the phone always uses Naismith offline anyway. TabPFN only helps when you plan at home, online, and only if it has your real hikes.

It's in the project for two reasons:
1. The challenge. TabPFN is one of the prize categories you're targeting, along with Gemma, Render and ElevenLabs.
2. The story. The post's argument is that open models let you run AI on your own data, privately, on cheap hardware. Gemma does that on your phone, and TabPFN does it with your own hiking history.

What that means for the current problem

Since it's optional, getting it live on the free plan matters only for the demo and the TabPFN prize. Your post can show the local evaluation numbers whether or not the live API runs it. If keeping the API stable for free matters more, go back to Naismith live (option 3). If you want the TabPFN category to look strong, I'll try the low-memory fix (option 2).

You

are we using gemma model ?

Agent
Agent
Agent

Yes, Gemma powers the Ask screen:

  • How it runs: the PWA loads WebLLM from the CDN (webllmModel.ts:11). WebLLM downloads Gemma's open weights once while you're online, caches them on the phone, and then runs Gemma on the phone's GPU (WebGPU). No server and no API key are involved, and it keeps working in airplane mode on the fort.
  • What it does: it answers questions about the fort ("where's water?", "how far to Balekilla?") using the fort's facts as context (prompt.ts), in English or Marathi.
  • Which version: pickGemmaModel() in model.ts chooses from WebLLM's built-in list. I just checked that list on the CDN. It has Gemma 2 (2B and 9B), the original Gemma 2B, and Gemma 3 1B. The code prefers the newest generation, so phones get gemma3-1b-it-q4f16_1-MLC. 9B is excluded as too big for a phone.

One thing I'd flag: I believe Gemma 3 1B is mainly an English model, and Google's broad multilingual support starts at the 4B size. Gemma 2 2B is larger and should handle Marathi better, though I haven't tested either in Marathi. Since Killedar offers Marathi answers, it may be better to prefer Gemma 2 2B. The cost is a bigger download (roughly 1.5 GB vs 0.7 GB).

Two ways to go:
- Test it: open killedar.onrender.com on a laptop with Chrome, ask the same question in Marathi on the current model, and judge the answer. I can't run WebGPU from here.
- Switch now: I change pickGemmaModel to prefer the larger 2B over Gemma 3 1B, update its tests, run npm run check, and commit.

Also still open: the TabPFN crash loop on Render. I'd suggest the low-memory fix, or going back to Naismith live. Tell me which, and whether to change the Gemma pick.

You

we should use litter gemma MOIDEL CHECK CONTEXCT PAGE IF IT HAS NO restrictions then use the model which can we quick and fast in mobile

Agent

The current pick, Gemma 3 1B, is already the lightest Gemma in WebLLM. Before keeping it, I'll check the challenge page for any rule about which Gemma counts, and Gemma 3 1B's context window in WebLLM's config.

Agent
Agent
Agent

Gemma 3 1B checks out technically: about 711 MB of GPU memory (vs 1,895 MB for Gemma 2 2B), flagged by WebLLM as fine for low-resource devices, with a 4,096-token context. Now the challenge page.

Agent
Agent
Agent
Agent

No change was needed: the app already uses the lightest, fastest Gemma.

Restrictions: none. The Best Use of Gemma category reads: "Use Gemma, Google's open-weight model… run it locally, fine-tune it, or serve it through Google Cloud or another provider." It doesn't require any version or size, and running it on the phone is the first option listed. The weights come from huggingface.co/mlc-ai without a login.

Model: pickGemmaModel() already picks Gemma 3 1B (gemma3-1b-it-q4f16_1-MLC). It's the smallest Gemma WebLLM offers:

Gemma 3 1B (current) Gemma 2 2B
GPU memory about 711 MB about 1,895 MB
WebLLM "low-resource" flag yes no
Context window 4,096 tokens 4,096 tokens

On phones whose GPU lacks the f16 feature, it falls back to Gemma 2 2B (q4f32), because that's the only Gemma build WebLLM has for those GPUs. docs/dev-post.md:80 already describes exactly this, so the post and the code agree.

The trade-off: Marathi answers may be weaker on the 1B model. Test one Marathi question in Ask before you record the demo.

Two things from the challenge page:
- Deadline: submissions close Oct 11. Your Mon 12:29 PM IST cutoff matches 11:59 PM US Pacific on Oct 11.
- Free credits: the page mentions partner credits at hacktoberfest.com/my/promos. If Render credits are there, the Starter plan could cost you nothing. That would fix the TabPFN crash loop with no code changes, and it's the simplest path to a live TabPFN.

The TabPFN crash loop on Render is still unresolved. The options are credits plus Starter, my low-memory change, or going back to Naismith live. Which one?

You

<pasted_content id="8e01">
Render
Credits
Hacktoberfest 2026 DEV Challenge Tie-In
Sep 15, 2026 – Nov 15, 2026

[REDACTED]
·
Sign out
Request received

This event does not have any codes in its pool yet. Your request is saved. Check back here after codes are assigned to the event, or watch your email if the team contacts you.
</pasted_content id="8e01">

Agent

No Render credits are available yet, so I'll try the free low-memory fix. I'll only change and measure things locally; nothing gets pushed without your OK.

Agent
Agent
Agent
Agent

The low-memory fix won't work. I measured peak memory locally:

TabPFN setting Just importing torch + TabPFN After fit Peak during prediction
Default (8 estimators) 396 MB 494 MB 513 MB
2 estimators 396 MB 494 MB 513 MB
2 estimators + low-memory modes, 1 thread 396 MB 491 MB 509 MB

The memory goes to loading torch and TabPFN themselves, not to the model's size. TabPFN's settings barely change the total, and the web server adds its own share on top. On Render the API will stay right at or over 512 MB, so it would keep crashing on the free plan. I haven't changed any code.

Realistic options before the deadline:

  1. Go back to Naismith live (recommended for today). I change HIKES_CSV in render.yaml to a path that doesn't exist and push. The API becomes stable again. TabPFN stays in the repo and in CI, and your post uses the local evaluation numbers. That's a two-minute change and still a legitimate entry for the TabPFN prize, since the prize allows "deployed as agent tool or standalone".
  2. Precompute TabPFN predictions into the app. The app only ever asks about five trails, so TabPFN could run once, on my machine or in CI, for every trail, start hour and group size. The results would ship in the fort JSON files. TabPFN predictions would then work offline on the fort, which fits the post's story better. But it's a real architecture change (new script, domain lookup, tests) on deadline day: a few hours of work, with some risk.
  3. Pay for Starter (about $7/month) and push nothing. TabPFN runs live as built.

My recommendation is 1 now, and 2 only if you finish the post and video with time to spare. Should I do 1?

You

check according to context for offline mode do we need the backend service (since context said offline so how will it work iin phone user opened a page offline wifi no wifie and it didn't worked ?) ideally it should be a single page, in browser it should download the models related to tabfpn or gemma webllm and then do processing (only when user clicks setup offline setup) question is can we get rid of backend thing ?

Agent

I'll start with why the page failed offline, then answer whether the backend can go. First, how the service worker is set up.

Agent
Agent

The service worker design looks right. Next I'll check the live deploy for a common failure: if any one precached file returns an error, the browser discards the entire offline cache.

Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent
Agent

The challenge rules have been checked; there's one wrong tag in the post, which I'll explain below. Now I'm running a real offline test of the live site in headless Chrome.

Agent
Agent

The page itself does load offline. But the estimate stays stuck on "Working out your day…", so I'm checking how long the offline fallback takes.

Agent
Agent
Agent

The page never finishes loading because the call to the crashing API hangs. That's probably what you saw. I'll measure it directly.

Agent
Agent

I tested the live site in headless Chrome, online and offline, and re-read the full challenge rules.

Offline test results

Result
Opened once online, then reloaded offline The page loads from the phone's cache.
Plan screen offline Shows "1 h 32 min (rule of thumb)" after about 3 s.
Plan screen online Hangs on "Working out your day…" for about 25 s, then shows the rule of thumb.
Ask screen Needs the "Download trail guide" step done once on Wi-Fi first. Headless Chrome has no GPU, so I couldn't test Gemma itself.

What you saw was most likely the online case. The app waits on the crashing TabPFN API, which takes ~25 s and can look broken. The backend is what makes the experience worse. The other likely cause is opening Ask before the guide was downloaded.

Can we get rid of the backend?

Yes, and it's the better design for this app, with one honest limit:
- Gemma already runs fully in the browser, so nothing changes there.
- TabPFN can't run in a phone browser. It's a Python/PyTorch model with no official way to run in a browser. But the app only ever asks about 5 trails × ~10 start hours × ~10 group sizes, which is about 500 numbers. We run TabPFN once, ahead of time, on your hikes CSV, and ship the results as a small JSON file. The service worker caches it, so TabPFN predictions then work offline on the fort. The trade-off is that new hikes mean re-running one script and redeploying.

The result is one static PWA with no server. "Download trail guide" grabs Gemma, and the TabPFN table is already cached with the app.

Challenge rules, checked against this plan

  • Gemma: "Build with Gemma 2 or Gemma 3, whether locally or via Hugging Face…" ✅ Gemma 3 1B qualifies.
  • TabPFN: "Use TabPFN… to forecast, predict, classify, or spot anomalies from historical data like a CSV." ✅ No hosting is required, so precomputed predictions qualify.
  • Render: "Use Render as your project's AI runtime, to host an agent's front end, or to run Hermes or OpenClaw." ⚠️ Without the API, Render only hosts the front end. That still qualifies but is weaker than an "AI runtime". You'd trade a slightly weaker Render case for a much stronger offline story.
  • ElevenLabs ($100): "Give an open-source agent a voice, transcribe audio for a local model, or generate narration for your demo." You could qualify just by narrating the demo video.
  • Multiple categories: "One project can enter as many categories as it genuinely uses." ✅
  • Required tags: #devchallenge and #hf26challenge. ❌ Your post has hacktoberfestchallenge, which needs fixing either way.

Proposed changes (about 1–2 hours, small commits, checks before each)
1. Add a script in api/ that runs TabPFN on hikes.csv and writes the prediction table into web/public/.
2. In web/, add a lookup-table predictor adapter with tests, and use it in main.ts instead of the HTTP one. The table falls back to Naismith for anything outside it.
3. Remove killedar-api from render.yaml. You'd then delete that service in the Render dashboard, which is your step.
4. Update the README, the post (tags, architecture, Render angle) and CLAUDE.md.

Should I go ahead with all four?

You

<task-notification>
<task-id>bjx3zos1s</task-id>
<tool-use-id>toolu_01R5B9PiVDkdFajVnJ61sGbq</tool-use-id>
<output-file>/tmp/claude/-home-user-Desktop-poc-killedar/78ac7393-b8b5-4de6-ba58-6251fc023415/tasks/bjx3zos1s.output</output-file>
<status>completed</status>
<summary>Background command "Find a usable Playwright install" completed (exit code 0)</summary>
</task-notification>

Agent

That notification was only my leftover search for a Playwright install finishing. It changes nothing above.

I'm still waiting for your answer: should I go ahead with the four changes that remove the backend?

You

yes

Agent

I'll start by reading the parts of the code the changes touch.

Agent
Agent

Next, the UI's input ranges (to size the table) and the API's model port.

Agent
Agent

The table will cover the UI's real ranges: start hours 4–14 and groups of 1–30, which comes to about 1,650 numbers. Next I'll read the rest of the API and CI files to plan the removal.

Agent
Agent
Agent

Now I'll write the precompute module and the trail loader, and trim the TabPFN adapter down to what precompute needs.

Agent
Agent
Agent
Agent

Iterating two iterators in lockstep inside a nested comprehension is fragile, so I'll restructure that part with an explicit zip and slicing.

Agent
Agent
Agent

Next: tidy the leftovers, then rewrite the model tests for precompute.

Agent
Agent
Agent
Agent
Agent
Agent

All 23 API tests pass, including the real-TabPFN one. TabPFN produced the whole table in about 13 s, and the file is 6.7 KB. Commit 1:

Agent
Agent

Commit 1 is in. Now the web side; first, how the existing tests are written.

Agent
Agent

Now the domain table lookup and the adapter that loads it.

Agent
Agent
Agent

Next I'll wire it in main.ts, remove the HTTP pieces, and update the related tests.

Agent
Agent
Agent

Now the tests: a new domain test file, plus replacing the HTTP and Naismith adapter tests with table-predictor tests.

Agent
Agent
Agent
Agent
Agent

75 tests pass now that the duplicates are gone, and predictions.json is in the offline cache. Before committing I'll check it in a real browser, served locally, online and offline.

Agent
Agent

The TabPFN estimate appears in about 20 ms, online and offline, down from 25 s. Committing the web part:

Agent
Agent

Commit 3 is the deploy config, CI and docs. I'll read the README and the post's architecture sections first.

Agent
Agent
Agent
Agent

Now the README.

Agent
Agent

Now the DEV post: the tags, the architecture block, test counts, and the TabPFN, privacy, Deploy and Render sections.

Agent
Agent

Running both suites one last time before committing.

Agent
Agent

Everything passes and the three commits are in. Pushing to main, which redeploys the static site:

Agent
Agent
Agent

The backend is gone and the live site works offline with TabPFN predictions. I tested it in headless Chrome:

Before Now
Plan screen, online Hung ~25 s on the crashing API, then showed the rule of thumb. 21 ms: "Climb about 2 h 3 min at your pace, 31 min slower than the rule of thumb."
Plan screen, offline Rule of thumb only. 23 ms, same TabPFN estimate.

CI passed and Render redeployed the site.

How it works now: TabPFN runs once on your laptop and answers every question the Plan screen can ask: 5 trails × start hours 4–14 × groups of 1–30. The answers go into a 7 KB predictions.json that the service worker caches with the app. Gemma still runs on the phone, so the whole app is one static site. Anything outside the table, such as a new trail, falls back to Naismith.

Three commits, all pushed:
- API: the server, /predict and Starlette are gone, replaced by a precompute script. 23 Python tests pass, including the real-TabPFN one.
- Web: a table predictor replaces the HTTP one. 75 tests pass, including one that checks the shipped predictions.json covers every fort trail.
- Deploy and docs: render.yaml is now one static site. CI runs evaluation and precompute with the real TabPFN. The README, CLAUDE.md and the post are updated, and the post now uses the required #hf26challenge tag. I added a short paragraph to the post about why the API was dropped; it's true and makes a good story.

What you need to do:
1. Delete the killedar-api service in the Render dashboard. Removing it from render.yaml doesn't delete the running service.
2. Replace the synthetic hikes. Once api/data/hikes.csv has real hikes, run:
bash
cd api && . .venv/bin/activate
python -m killedar_api.evaluation data/hikes.csv # numbers for the post
python -m killedar_api.precompute data/hikes.csv # then commit web/public/predictions.json

Until then, the post's TabPFN numbers must stay as placeholders or be labelled synthetic.
3. Record the demo on a real phone. WebGPU doesn't run headless, so I couldn't test Gemma itself.

The Render category is weaker now: Render only hosts the front end, which the rules allow ("host an agent's front end"). Everything else gets stronger, because the whole app works in airplane mode.

You

<ide_opened_file>The user opened the file ~/Desktop/poc/killedar/.github/workflows/ci.yml in the IDE. This may or may not be related to the current task.</ide_opened_file>

wait so give users options ability to say if they want to app to work offline if that is selected nice icon, show progress of download (like any models or any thing else) once setup is ready show success

Agent

The app already has a "Download trail guide" panel, but it only covers Gemma. I'll read the current UI and model port to see how to widen it into a full offline setup.

Agent
Agent

I'll build this as an "Offline setup" feature covering everything the app needs on the fort, not just Gemma. First, the styles and test fakes.

Agent
Agent

Quick answers to both before I continue:

Agent
Agent
Agent

The app is running at http://localhost:5173; open it in Chrome. Gemma needs WebGPU, so desktop Chrome works. Now the offline setup, starting with its pure domain logic:

Agent
Agent
Agent

Now the use case that runs the two steps and reports progress:

Agent
Agent
Agent

Now the service worker side, which answers those messages:

Agent
Agent
Agent
Agent

Next, the icons and the panel module itself:

Agent
Agent
Agent

Wiring it into the screens: the badge goes in the header, the old "On the fort" panel is removed, and Ask points to the new setup.

Agent
Agent
Agent

The source typechecks; only the old copy test needs replacing. Now the tests, for the copy, the domain and the use case:

Agent
Agent

All 88 tests pass. Next I'll check it in a real browser at phone width and take screenshots. Headless Chrome has no GPU, so this run will exercise the "Ask can't run here" path.

Agent
Agent
Agent
Agent

The flow works: "Use offline" → download → "Offline ready", and it survives reloading offline. Now the screenshots:

Agent
Agent
Agent
Agent
Agent
Agent
Agent

Both are fixed. One last look at the home screen with the badge in its new place:

Agent
Agent
Agent

The header looks right. Last, I'll update the docs that mention the old "Download trail guide" button, then commit and push.

Agent
Agent

The offline setup is built, committed and pushed. All 88 web tests pass. Render redeploys on push; I didn't wait for it.

How it works:
- A "Use offline" button with a cloud icon sits next to "Sinhagad, 1,312 m" on every screen. Using offline is opt-in.
- Tapping it opens a "Use Killedar with no signal" panel with two steps, each with its own icon, progress bar and status:
1. App, fort notes and your climb times: under 1 MB, with a "12 of 36 files" count.
2. Gemma trail guide, for Ask: about 0.7–1.5 GB, with WebLLM's download progress.
- "Download for offline" runs both steps. The header button turns into a progress ring with "Saving 42%", and keeps updating if you close the panel or switch screens.
- When it finishes, the button turns green ("✓ Offline ready") and the panel says "Ready for the fort. Turn on airplane mode and try it." The app also asks the browser not to delete the saved files when space runs low.
- If something fails, the step is marked and the button changes to "Try again".
- Without WebGPU, as on your machine, the files still save and the panel explains how to enable Ask. Plan and Listen still work offline.
- If you tap Ask before downloading the guide, the panel opens.

Tested in headless Chrome at phone width: "Use offline" → download → "Offline ready", and it stays ready after reloading with the network off. I fixed two layout problems found in the screenshots: the button was squeezing the fort picker, and the panel's buttons wrapped. The Gemma download step itself, including its progress ring, I couldn't test here because headless Chrome has no GPU. It needs testing on your phone.

To try it locally: http://localhost:5173 is still running the latest build. Reload twice, because the old service worker serves the previous version once. To see the Gemma step on your desktop, enable chrome://flags/#enable-unsafe-webgpu and #enable-vulkan, then restart Chrome.

The demo shot list in docs/dev-post.md now uses this flow. The green "Offline ready" check followed by airplane mode makes a good shot for the video.

You

<ide_opened_file>The user opened the file ~/Desktop/poc/killedar/render.yaml in the IDE. This may or may not be related to the current task.</ide_opened_file>

can also add this token : [REDACTED_HF_TOKEN], so that hugging face downloads does not rate limits, also if therer are any errors it should report it into console.log

Agent

I won't add that token to the app. Here's why, and where a token would actually help:

  • Anything in the PWA is public. The app runs in every visitor's browser and the repo is public, so the token would be readable by anyone who opens DevTools or the GitHub repo. People can abuse a leaked token under your account.
  • It wouldn't help the app anyway. WebLLM downloads Gemma from the public mlc-ai repos on Hugging Face, which don't need a token. Each user downloads it once, from their own IP, so per-user rate limits aren't a concern.
  • It would help in CI. CI downloads the TabPFN weights from Hugging Face on every run. If you want, I can store the token as a GitHub Actions secret (gh secret set HF_TOKEN) and pass it to that job only. It stays encrypted and is never in the repo. Tell me if you want that.

You've pasted the token into this chat, so consider regenerating it at huggingface.co/settings/tokens. That's cautious rather than urgent, since it hasn't been committed anywhere.

Now the error logging. First I'll find where errors are currently swallowed silently:

Agent
Agent
Agent

Next: unexpected errors on the Plan and Ask screens get logged too, and the tests check that failures reach the logger without cluttering test output.

Agent
Agent

All 88 tests pass. The two [Killedar] lines above come from tests that deliberately feed in a broken or unreachable predictions file, which confirms the logging works. Committing and pushing:

Agent
Agent
Agent

With a WebGPU adapter available, Ask correctly says the guide isn't downloaded yet and opens the setup panel. Next I'll start the actual Gemma download and watch for errors:

Agent
Agent
Agent
Agent

Now tests for each fix. They cover a fake model that keeps sending progress after failing, the storage-full error, and the shortened text:

Agent
Agent
Agent

Fixed and confirmed in the browser. The download fails cleanly with the storage message, and the header changes to "Finish offline". Committing and pushing:

Agent
Agent

I found and fixed two real bugs in the Gemma download that Ask depends on. That should fix the stuck download. If Ask still fails on your machine, the earlier WebGPU message points to your browser setup, not the app.

I couldn't run a full download to the end: the test browser allows only 778 MB of storage, so I haven't seen Gemma answer a question.

What I tested: headless Chrome with a software GPU, running Ask and then the full download.
- Ask, before downloading: works as designed. It says the guide isn't on the phone yet and opens the offline panel.
- The download itself: starts fine. About 1.4 GB arrives in 42 parts from Hugging Face, with no errors or rate limits, and the progress ring updates.

The bugs:
1. Running out of storage froze the panel at "Saving 52%". The download stopped with a QuotaExceededError, but WebLLM kept sending progress updates afterwards, and those overwrote the failure. The app now ignores updates after the download has finished or failed.
2. The failure message was unclear. It now reads "Not enough storage for the guide. Free up about 1.5 GB (and don't use a private/incognito window), then try again." The long WebLLM progress text is also shortened. Five new tests cover these, and all 92 web tests pass. Pushed in 72640ff.

Why Ask may still fail on your machine: the earlier "WebGPU is present but no GPU adapter is available" message means your Chrome has WebGPU switched off for your GPU. Without WebGPU, Gemma can't run there at all. To fix it:
1. Open chrome://flags, enable #enable-unsafe-webgpu and #enable-vulkan, and restart Chrome.
2. Check chrome://gpu. WebGPU should say "Hardware accelerated".
3. Don't use an incognito window, which gets the small storage limit I hit.
4. Reload http://localhost:5173 twice, tap Use offline → Download for offline, and wait for the green check.

If it still fails, open DevTools (F12) → Console and send me the lines starting with [Killedar]. Every failure now logs its real cause there.

The simplest real test is still Chrome on an Android phone, where WebGPU is on by default. If the GPU lacks the f16 feature, as here, the app falls back to the larger Gemma 2 2B (about 1.4 GB). Phones with f16 get Gemma 3 1B (about 0.7 GB).

You

in chat ask (waking up model ) _--> then error :

<pasted_content id="8e01">
webllmModel.ts:96 [Killedar] Loading Gemma failed WindowSizeConfigurationError: Only one of context_window_size and sliding_window_size can be positive. Got: context_window_size: 4096, sliding_window_size: 512
Consider modifying ModelRecord.overrides to set one of them to -1.
at new wQ (llm_chat.ts:603:13)
at sD.<anonymous> (engine.ts:525:21)
at Generator.next (<anonymous>)
views.ts:304 [Killedar] WindowSizeConfigurationError: Only one of context_window_size and sliding_window_size can be positive. Got: context_window_size: 4096, sliding_window_size: 512
Consider modifying ModelRecord.overrides to set one of them to -1.
at new wQ (llm_chat.ts:603:13)
at sD.<anonymous> (engine.ts:525:21)
at Generator.next (<anonymous>)
</pasted_content id="8e01">

Agent

WebLLM's built-in settings for Gemma 3 1B contradict each other: a 4,096-token context window and a 512-token sliding window, and WebLLM refuses to start with both. I'll check how this version of WebLLM handles those two settings before choosing a fix:

Agent
Agent
Agent
Agent
Agent
Agent

Next a small pure function for those options, so it's testable. It keeps WebLLM's 1,024-token builds at 1,024:

Agent
Agent
Agent

While that runs, I'll commit locally. I'll push only after the browser test shows the new options don't break the Gemma 2 fallback.

Agent
Agent
Agent

I found the cause of the Ask error and fixed it, but the fix is only committed locally (d758a36), not pushed yet. Gemma 3 1B can't run in my test setup, so it isn't confirmed working until you try it.

Cause: Gemma 3 1B's config, as WebLLM downloads it, sets both a context window (8,192 tokens) and a 512-token "sliding window". WebLLM 0.2.85 refuses to start a model when both are positive. It's a packaging mismatch between WebLLM and that model, not something in our prompt or code.

Fix: the 512-token window is Gemma 3's local attention, and the model reads it from its own settings. So when the app starts the engine, it now switches the duplicate engine-level setting off and asks for a 4,096-token context explicitly. The change is a small function in model.ts plus one argument in the WebLLM adapter. Two new tests cover it, and all 94 web tests pass.

Still running in the background: a full Gemma 2 2B download and Ask on a software GPU. It checks that the new settings don't break the fallback model, and it's slow, so it may take several minutes. Your browser downloads the Gemma 3 1B version, which this test environment can't run because it has no f16 support. Once it passes, I'll push.

When it's pushed: reload http://localhost:5173 twice and tap Ask. The model is already downloaded, so it should go straight from "Waking up the guide…" to an answer. If anything fails, the console will show a [Killedar] line with the reason.

You

i will manuyally test that restart the server 5173

Agent
Agent
Agent
Agent
Agent

The old server never stopped: my stop command matched its own shell and exited first. So the new one couldn't take port 5173. I'll stop it by process ID and start it again:

Agent
Agent
Agent

That notification is just the old server exiting, as intended. Checking the new one:

Agent
Agent

The server is restarted on http://localhost:5173 with a fresh build, and I checked that it's serving the Gemma window-size fix.

To test:
1. Reload the page twice. The first reload installs the new service worker and the second one uses it. Alternatively, in DevTools go to Application → Service workers and click "Update".
2. Open Ask and tap a suggested question. The model is already downloaded, so it should show "Waking up the guide…" and then stream an answer.
3. If it fails, send me the [Killedar] lines from the DevTools console.

The fix is committed locally (d758a36) but not pushed yet. I stopped my background check of the Gemma 2 fallback to restart the server, so it didn't finish, and I couldn't confirm Gemma 3 1B here either. I'll push once you confirm Ask works.

You

ok isssue resolved but got this :

<pasted_content id="8e01">
What happened here in history?

  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad
  • Sinhagad </pasted_content id="8e01">
Agent

Gemma now loads and generates, but it's stuck repeating. My leading suspect is my own fix. Gemma 3 1B's local-attention layers were trained to look back only 512 tokens, and our prompt with the fort notes is around 1,500–2,000 tokens. If switching off the engine's sliding window also let those layers see the whole prompt, the output can collapse into repetition exactly like this. I'll check how WebLLM passes that setting to the model's memory:

Agent
Agent
Agent
Agent
Agent
Agent

95 tests pass. The DEV post still says Gemma 3 1B, so I'll update it. This is a good story for the post, too:

Agent
Agent
Agent

Committed locally. The server reads files from disk on each request, so it should already be serving the new build. Checking:

Agent
Agent

The repeated "- Sinhagad" answer came from my previous fix, and the only way around it is switching to the larger Gemma 2 2B. The repeating is gone in theory, but I couldn't run either model here, so it needs your test. The server is already serving the new build. Both commits (d758a36, 923b826) are local only.

Why Gemma 3 1B can't work here: Gemma 3 has some layers that look back only 512 tokens and others that see everything. WebLLM 0.2.85 can only set one attention window for the whole model:
- Window off (my earlier fix): the short-range layers see the whole ~2,000-token fort prompt, which they were never trained on, and the output collapses into the same line repeated.
- Window on (WebLLM's default): every layer forgets everything older than the last 512 tokens, including the fort notes.

So no setting gives Gemma 3 1B correct answers with our prompt.

The change: the app now uses Gemma 2 2B, the lightest Gemma that works in this version of WebLLM. Its short-range window is the same size as its full context (4,096 tokens), so it's unaffected. It's also likely better at Marathi.

The trade-off is a bigger download: about 1.5 GB instead of 0.6 GB, once. You wanted the lightest model, but no lighter Gemma works here. The offline panel's size text and the DEV post now say so. The post also tells this story briefly, which suits the "Writing quality" judging. All 95 tests pass.

To test:
1. Reload twice.
2. Tap Use offline → Download for offline. Gemma 2 isn't downloaded yet, so it's a new ~1.5 GB download.
3. Ask "What happened here in history?" on Sinhagad.

The 600 MB of Gemma 3 is still stored in the browser. To free that space, go to DevTools → Application → Storage → Clear site data before step 2. It also clears the app's other saved data, which is fine because step 2 re-saves everything.

Tell me how Ask behaves and I'll push both commits.

You

use this as elven labs key : add elven labs inetgtation also : [REDACTED_ELEVENLABS_KEY]

Agent

The ElevenLabs integration already exists: npm run audio turns each fort's history into narration at build time, and the app plays it offline in Listen. It just needs your key. I'll pass the key only as an environment variable for that one command, so it never goes into the repo or into the app that's shipped to users. First, how the script works and how many characters it will send, since that uses your ElevenLabs credits:

Agent
Agent

The script also needs a voice ID. Next I'll count the characters it would send and list the voices on your account:

Agent
Agent
Agent
Agent
Agent

The build now precaches the 6 narration files, and neither key appears anywhere in the repo. Checking the Listen screen in the browser:

Agent
Agent
Agent
Agent
Agent

ElevenLabs narration is in, and Listen now plays real narration on all three forts in English and Marathi. It's committed locally but not pushed yet. Your local server is already serving it; reload twice to see it.

  • Six clips, 1.4 MB in total, used about 1,890 characters of your ElevenLabs credits. They're saved with the app, so they play offline on the fort. I checked that both the English and Marathi clips load, including with the network off.
  • Marathi needed a different model. The script's default, eleven_multilingual_v2, rejects Marathi, so it now uses eleven_v3 and sends each clip's language. Forts without a clip still fall back to the phone's own voice.
  • Voice: "George", ElevenLabs' stock narration voice. Your key isn't allowed to list voices, so I couldn't choose from your account. If you prefer another voice, send its ID and I'll regenerate the clips; it takes a few seconds.
  • Credit: a small "Narration: ElevenLabs" link sits under the player, which the free tier requires when you publish their audio.
  • Your key isn't stored anywhere. I passed it only for that one command, and it's not in the repo or the shipped app. You pasted it into this chat, so you may want to regenerate it on ElevenLabs once the clips are final.
  • The DEV post's ElevenLabs section now describes this, including the Marathi model finding, and the "delete if no audio" TODOs are gone.

I can't judge how natural the Marathi pronunciation sounds, so listen to one Marathi clip yourself.

Three local commits are waiting to push: the Gemma 3 window fix, the switch to Gemma 2 2B, and the narration. Once you confirm Ask answers properly with Gemma 2 2B, I'll push all three, and Render will redeploy with everything.

You

but how did it use elvane labs API key ?

Agent

The key was only used once, on your laptop, while I generated the audio. The app itself never contacts ElevenLabs.

How the key was used:

I ran the generator script once with the key passed in as a temporary setting for that one command:
bash
ELEVENLABS_API_KEY=sk_… ELEVENLABS_VOICE_ID=JBFqnCBsd6RMkjVDRZzb npm run audio

generate-audio.ts reads it from process.env.ELEVENLABS_API_KEY, and it went out only in the xi-api-key header of 6 requests to api.elevenlabs.io. Each request sent one fort's history text and got back an MP3. The script saved those as web/public/audio/*.mp3.

The setting disappeared when the command finished. It isn't in any file, in your git config or in the shell profile.

What ships to users:

Only the six MP3 files, which are committed like images. When someone opens Listen, the app plays audio/sinhagad-mr.mp3 from its own files. That's why it works on the fort with no signal and costs you no ElevenLabs credits per listen.

your laptop (once)                         user's phone (every time)
──────────────────                         ─────────────────────────
history text ──► ElevenLabs API ──► .mp3 ──► committed ──► Listen plays the .mp3
                 (key used here)                           (no key, no network)

When you need the key again:

Only to regenerate the clips, for example after editing a fort's history or choosing another voice:
bash
cd web
ELEVENLABS_API_KEY=your_key ELEVENLABS_VOICE_ID=voice_id npm run audio -- --force

I deliberately left it out of the app. Anything in the PWA is downloadable by every visitor, so a key there would let anyone spend your ElevenLabs credits. I also didn't save it anywhere in the repo.

You

ok how will be prove to judge that we used eleven labs api key

Agent

You never show the key itself. Judges can't verify a key, and publishing one would let anyone spend your credits. What they look for is evidence that the project uses ElevenLabs, and that's all public in the repo and the app:

Evidence Where a judge sees it
The code that calls the API web/scripts/generate-audio.ts: a POST to api.elevenlabs.io/v1/text-to-speech/… using model eleven_v3 with a language_code per clip.
The output web/public/audio/*.mp3: six committed clips in English and Marathi. The git history shows them added by the narration commit.
It working in the app Listen on killedar.onrender.com plays them, with "Narration: ElevenLabs" under the player.
The write-up The post's Narration section and its Best Use of ElevenLabs line, including the Marathi model finding. That kind of detail shows real use.
The demo video Shot 6 of the shot list: Listen playing Marathi narration, ideally after switching on airplane mode.

This matches the category wording, which counts "generate narration for your demo" as a valid use, so you're well covered.

Two ways to make it more convincing:
1. Narrate the demo video itself with ElevenLabs. Write a 60–90 s voiceover script, generate it with your key, and lay it over the screen recording. That's a second, very visible use, and it's literally the example the category gives. I can write the script and generate the MP3 if you want.
2. Have the script record what it made in a small audio/credits.json: model, voice, language and date for each clip. Judges then see exactly how each file was produced without running anything. It's about 5 minutes of work.

Want me to do either? Separately, three local commits (the Gemma fixes and the narration) are still waiting for your confirmation that Ask works with Gemma 2 2B before I push.

You

ok now properly read again the dev post and only elibgle category and how we used it it should mention in our dev post, update the dev post

Agent

I'll re-read the whole post against the challenge rules. Categories you're eligible for: Gemma, TabPFN, ElevenLabs clearly, and Render, since it hosts the front end. Not Tinker, Arduino or Backboard.

Agent
Agent
Agent
Agent

Re-reading the contest page now, to check the post against the exact template and category wording:

Agent
Agent
Agent
Agent
Agent

One correction: "the thousands of people" is a number I can't back up, so I'll soften that sentence to something verifiable. Then I'll commit.

Agent
Agent

I re-read the contest page and updated docs/dev-post.md to match it. It's committed locally but not pushed.

Template fixes:
- Opening line now matches the official wording: "This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass".
- What I Built now answers the template's two questions directly. Who it's for: the weekend crowd leaving Pune before dawn who lose signal on the way up. How it gets you off the screen: one time to remember, short answers, and history read aloud while you look at the walls.
- Why Open Innovation now opens with the template's question, "what did it make possible that a closed API wouldn't?": with a closed API, Ask would be a spinner on the summit.
- My Agent Session (optional, DevRelay) is added as a hidden placeholder. The page says judges find it helpful.
- Stale facts are fixed: "95 web tests", "Use offline", and all three screens now work offline. A new "Offline setup" section covers the opt-in flow.

Prize categories: only the four that apply, each saying what's used and where to look in the code:

Category Page wording How Killedar uses it
Gemma "run it locally" Gemma 2 2B on the phone via WebLLM, answering offline from cited notes
TabPFN "predict … from historical data like a CSV" Personal climb-time model from a hikes CSV, precomputed so it works offline
ElevenLabs "…generate narration for your demo" eleven_v3 narration in English and Marathi, made at build time and played offline
Render "host an agent's front end" Hosts the whole PWA from one render.yaml

The post now also says honestly that ElevenLabs is the one closed service and runs only at build time. I left out Tinker, Arduino and Backboard because the project doesn't use them.

8 TODOs are left, all yours:
1. Trek photo and cover image.
2. Your 2–3 sentence personal story.
3. Demo video.
4. Real TabPFN numbers. The CSV is still synthetic. If you submit before replacing it, the TODO gives the exact sentence that labels the numbers as synthetic, and that's required for honesty.
5. Interpretation sentence for those numbers.
6. Optional: DevRelay session.
7. Optional: ElevenLabs voiceover for the demo video, which matches the category wording exactly.
8. Credits line.

Deadline: Mon Oct 12, 12:29 PM IST (Oct 11, 11:59 PM PDT).

Four commits are still waiting to push: the Gemma fixes, the narration and this post. Did Ask answer properly with Gemma 2 2B? Once you confirm, I'll push and Render will redeploy.

You

ok now push this

Agent
Agent

All 4 commits are pushed, CI passed, and Render has redeployed killedar.onrender.com. On the live site I checked that the Marathi narration file is served and that the Gemma 2 2B change is in place.

Now live:
- Ask runs Gemma 2 2B, with the window-size fix. Anyone who downloaded Gemma 3 on the live site will be prompted to download Gemma 2 2B (about 1.5 GB).
- Listen plays ElevenLabs narration in English and Marathi, offline.
- The DEV post in docs/dev-post.md now matches the contest template.

Before the deadline (Mon Oct 12, 12:29 PM IST):
1. Test Ask on your phone in Chrome on Android: tap Use offline, wait for the green check, switch on airplane mode, then ask a question in English and in Marathi. I couldn't run Gemma in my tests, so this is its first check on a real device.
2. Record the demo using the shot list in the post. That same test run can be the recording.
3. Fill the post's TODOs: photo, your story, the video, and the TabPFN numbers. Either use real hikes, or keep the sentence that says the data is synthetic.
4. Delete the old killedar-api service in the Render dashboard if you haven't already.
5. Publish on DEV: copy docs/dev-post.md into a new post, check the tags devchallenge, hf26challenge, and set published: true.

Send me your real hikes and I'll run the evaluation and fill in the numbers. I can also write the demo voiceover script for ElevenLabs.

Top comments (0)