From 65a1bb010d720c07f6105006cfbbdcf9592bb6c5 Mon Sep 17 00:00:00 2001 From: Ryan Chen Date: Thu, 6 Aug 2026 21:13:52 -0400 Subject: [PATCH] Update CLAUDE.md to reflect current project state Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f011d5b..9712e5f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,29 +5,42 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Commands ```bash -uv run uvicorn app:app --reload # start dev server at http://localhost:8000 -LLAMACPP_BASE_URL=http://host:port/v1 uv run uvicorn app:app --reload # point at a specific LLM -uv sync # install/sync dependencies -uv add # add a new dependency +uv run uvicorn app:app --reload # start dev server at http://localhost:8000 +uv run --env-file .env uvicorn app:app --reload # load env vars from .env file +uv sync # install/sync dependencies +uv add # add a new dependency ``` +Set `LLAMACPP_BASE_URL=http://host:port/v1` to point at a local LLM (inline, exported, or via `.env`). + ## Architecture Two-file app: a FastAPI backend (`app.py`) and a single-page frontend (`static/index.html`). -**`app.py`** parses all GPX files in `garmin_gpx_exports/` at startup into an in-memory dict (`_rides`), keyed by filename stem. Stats (haversine distance, elevation gain/loss, HR zones) are computed once at load time. Three API routes: +**`app.py`** parses all GPX files in `garmin_gpx_exports/` at startup into an in-memory dict (`_rides`), keyed by filename stem. Stats (haversine distance, elevation gain/loss, HR zones) are computed once at load time. API routes: - `GET /api/rides` — summary list (no track points) +- `GET /api/rides/{id}` — full ride metadata + cached insight - `GET /api/rides/{id}/points` — full lat/lon/ele/hr array -- `POST /api/rides/{id}/insights` — streams AI response via OpenAI SDK +- `POST /api/rides/{id}/insights` — streams AI coaching response +- `POST /api/insights/generate-all` — SSE stream, generates insights for all rides in chronological order +- `POST /api/fetch-rides` — SSE stream, fetches new rides from Garmin Connect (supports MFA) +- `POST /api/mfa` — submits MFA code for an in-progress Garmin session -**AI integration** uses the OpenAI Python SDK pointed at a local LLM endpoint (`LLAMACPP_BASE_URL` env var, defaults to `http://localhost:8080/v1`). The model name is auto-discovered from `GET /v1/models` at startup (falls back to `"local-model"`). The model is a reasoning model (GLM-4.7-Flash alias) that streams output into `reasoning_content` rather than the standard `content` field — the chunk reader handles both. +**AI integration** uses the OpenAI Python SDK pointed at a local LLM endpoint (`LLAMACPP_BASE_URL` env var, defaults to `http://localhost:8080/v1`). The model name is auto-discovered from `GET /v1/models` at startup (falls back to `"local-model"`). If the endpoint is unreachable at startup a warning is logged and the app continues — only insight generation is affected. The chunk reader handles both `content` and `reasoning_content` delta fields for reasoning models. -**`static/index.html`** is a self-contained SPA (no build step). Leaflet.js draws the route as per-segment polylines colored by HR intensity. Chart.js renders elevation and HR profiles against cumulative distance. AI insights stream via `fetch` + `ReadableStream`. +**Insights cache** is persisted to `insights.json` (gitignored) and loaded at startup. The prompt includes the previous ride's cached insight so the coach can reference its own prior advice. -**`main.py`** is a separate Garmin Connect download script (not part of the web app). +**`static/index.html`** is a self-contained SPA (no build step). Leaflet.js draws the route as per-segment polylines colored by HR intensity zone. Chart.js renders elevation and HR profiles against cumulative distance. AI insights stream via `fetch` + `ReadableStream`. Units (km/mi) toggle and resting HR are persisted in `localStorage`. + +**`main.py`** is a standalone CLI script for bulk-downloading Garmin Connect GPX files without running the web server. ## GPX data notes - Garmin extension namespace for HR: `http://www.garmin.com/xmlschemas/TrackPointExtension/v1` (`ns3:hr`) -- One file (`2026-06-21_18-17-11_Indoor Cycling_*`) has an empty `` — `has_gps: false` in the API, frontend shows no map/charts for it -- HR zone boundaries: Z1 <120, Z2 120–140, Z3 140–160, Z4 160–180, Z5 >180 bpm +- Activities with an empty `` (e.g. indoor rides) get `has_gps: false` — frontend shows no map or charts +- HR zone boundaries (fixed, used for API summary): Z1 <120, Z2 120–140, Z3 140–160, Z4 160–180, Z5 >180 bpm +- Karvonen zones (used in the UI and AI prompt) are computed from resting HR input + lifetime max HR observed across all rides + +## Logging + +httpx, httpcore, and openai loggers are set to WARNING to suppress connection-level debug noise. The `bikeslop` logger runs at DEBUG.