Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2.1 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
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 <package> # add a new dependency
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:
GET /api/rides— summary list (no track points)GET /api/rides/{id}/points— full lat/lon/ele/hr arrayPOST /api/rides/{id}/insights— streams AI response via OpenAI SDK
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.
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.
main.py is a separate Garmin Connect download script (not part of the web app).
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<trkseg/>—has_gps: falsein 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