Skip to content

Repository files navigation

MetricPath

A personalized lifestyle improvement tool powered by AI. Answer a few questions about your habits — get a tailored Lifestyle Report with a 7/15/30-day action plan and a daily schedule. No accounts, no local memory between runs.


Table of Contents


Overview

MetricPath runs a warm, conversational intake powered by the LLM you choose for that run. It collects 19 data points about your sleep, diet, exercise, stress, connection, hydration, sedentary time, goals, and constraints — then:

  1. Computes derived health metrics (sleep debt, activity score, diet score)
  2. Builds a personalized daily time-block schedule
  3. Generates a structured Lifestyle Report with actionable 7/15/30-day plans
  4. Lets you ask follow-up questions, save a branded PDF locally, or start a fresh run

Everything runs in your terminal. Each run starts fresh locally, and nothing is written to disk unless you explicitly choose to save.


Prerequisites

Requirement Version
Python 3.12+
LLM access Anthropic/OpenAI API key, or local Ollama for free mode

Installation

# 1. Clone the repo
git clone https://github.com/kevinm126/metricpath.git
cd metricpath

# 2. Create and activate a Python 3.12+ virtual environment
/opt/homebrew/bin/python3.12 -m venv .venv
source .venv/bin/activate        # macOS / Linux
# .venv\Scripts\activate         # Windows

# 3. Install MetricPath and its runtime dependencies
python -m pip install -e .
# Runtime deps: rich, anthropic, openai, reportlab (no ML libraries needed at runtime)

# 3b. (Optional) install training-only deps — only needed to re-fit the risk model
#     from raw NHANES data. Never required for normal use.
# python -m pip install pandas pyreadstat scikit-learn

# 4. (Optional) preload an LLM provider or free local endpoint
#    You don't have to set anything before the first run. MetricPath will
#    ask for a provider, prompt for an API key inline if one is missing,
#    and offer to pull a free local Ollama model if you pick that route.
#    Set values in .env only if you want to skip those prompts on later runs:
cp .env.example .env
# Paid providers (paste at runtime, or save to .env to skip the prompt):
#   ANTHROPIC_API_KEY=sk-ant-...
#   METRICPATH_LLM_PROVIDER=openai
#   OPENAI_API_KEY=sk-...
#
# Free local provider (Ollama):
#   METRICPATH_OLLAMA_BASE_URL=http://localhost:11434/v1/
#   # install Ollama from https://ollama.com/download
#   # then either let MetricPath pull a curated model for you on first run
#   # (offers llama3.1:8b, qwen2.5:7b, phi3:14b, or gemma2:9b),
#   # or pull one yourself ahead of time, e.g.:
#   #   ollama pull llama3.1:8b

Running the app

metricpath                 # full pipeline (intake, report, follow-up)
metricpath onboard         # alias for the full pipeline — the one-time setup
metricpath checkin         # the daily loop: seven questions and a Day Score
metricpath reset           # wipe ~/.metricpath/ after a typed DELETE confirm

metricpath and metricpath onboard run the full pipeline end-to-end in your terminal. On each run, MetricPath asks which provider and model to use before the intake begins.

metricpath checkin is the daily loop. Seven questions, under a minute, a Day Score on the same 0–1000 scale as the full report, and up to two research-anchored nudges when a domain drops against your rolling 7-day average. It opts you into local history on first run with a plain-text consent prompt and never writes anything without that yes.

During local development, python cli/main.py still works too.


Configuration & controls

MetricPath is a linear, interactive pipeline with a privacy-first finish. You steer it through a startup provider/model chooser, environment variables, and keyboard input during the session.

Environment variables

Variable Required Default What it does
METRICPATH_LLM_PROVIDER no anthropic Prefills the startup chooser with a provider. Supported values: anthropic, openai, ollama.
ANTHROPIC_API_KEY no Auth for Anthropic LLM calls. If unset when you pick Anthropic at startup, the CLI prompts you to paste the key inline for that run. Save it to .env to skip the prompt next run.
OPENAI_API_KEY no Auth for OpenAI LLM calls. Same inline-paste fallback as the Anthropic key. OpenAI API billing is separate from ChatGPT Plus/Pro subscriptions.
METRICPATH_OLLAMA_BASE_URL no http://localhost:11434/v1/ Override the OpenAI-compatible local Ollama endpoint used for free mode.
METRICPATH_MODEL no provider default Prefills the startup chooser with a highlighted model choice. Defaults to claude-sonnet-4-6 for Anthropic, gpt-5.4-mini for OpenAI, and llama3.1:8b for Ollama.
METRICPATH_POPULATION_SCORE no 0 Set to 1, true, or yes to print the experimental Ding 2015 population-normed risk score.
METRICPATH_POPULATION_PERCENTILE no 0 Set to 1, true, or yes to print the trained-logistic risk percentile (see Population risk percentile).
NO_COLOR no unset Set to any value to disable ANSI color and emoji highlighting. Useful in CI logs or basic terminals.
ENVIRONMENT no development production suppresses any echo of raw intake answers to stdout.

Deprecated compatibility fallbacks: LIVEBETTER_MODEL and LIVEBETTER_POPULATION_SCORE are still read if the new METRICPATH_* variables are unset. LIVEBETTER_MODEL only applies to the Anthropic provider.

All of the above can live in .env next to .env.example — they are picked up automatically on startup. The CLI still asks you to choose a provider and model on every run; env vars only prefill the chooser.

Model-aware intake prompts

The intake conversation uses a different system prompt depending on the provider and model you pick. Same prompt across all Anthropic models. Same prompt across all OpenAI models. One prompt per Ollama family — llama (covers llama3.1, llama3.2, llama4), qwen (qwen2.5, qwen3), phi (phi3, phi4), gemma (gemma2, gemma3) — plus a generic Ollama fallback for anything else you pull. Each prompt is researched against that provider or family's published prompt-engineering guide and tuned for how that family follows instructions: Anthropic gets XML-structured sections, OpenAI gets the CTCO pattern, Qwen gets terse direct instructions, Phi gets a compact prompt that fits its smaller context, and so on.

You never edit a file. The CLI loads the right prompt automatically based on the provider and model you chose at startup. Files live under cli/prompts/intake/.

Runtime controls

Keyboard input during each phase of the session:

Phase Input Effect
Startup / + Enter choose anthropic, openai, free local ollama, or Exit and come back later
Startup (paid provider, no key) paste an API key sets the key for this run; the CLI suggests adding it to .env so the prompt doesn't fire next time. Press Enter on an empty prompt to go back to the provider chooser.
Startup / + Enter choose a model for the selected provider from the menu; for Ollama, installed models are listed when available
Startup pick Type a custom model name switches from the menu to free-form model input
Startup (Ollama, no models) recovery menu opens a curated picker (llama3.1:8b, qwen2.5:7b, phi3:14b, gemma2:9b — all free, all local) and pulls the one you choose. You can also type a custom model name, print the manual setup steps, or pick a different provider.
Startup (Ollama, model picker) pick Pull a curated model opens the same curated picker without leaving the run, pulls your choice, and returns you to the model menu with the freshly pulled model added to the list.
Startup (Ollama unreachable) Y at setup guidance prompt prints the exact download / ollama serve / ollama pull ... steps and returns you to the chooser
Any handled failure retry menu / confirm prompt shows a Rich error panel with what failed, short details, and the safe next action
Intake any text answer the current question
Intake invalid numeric (e.g. abc for sleep hours) re-prompts in place with an error hint — does not restart the conversation
Intake Ctrl-C aborts; no report is generated
Report failure Y at retry prompt retries report generation without repeating intake
Post-report menu / + Enter choose follow-up, local PDF save, start fresh, or finish
Save action filename prompt writes reports/<filename>.pdf only when explicitly chosen
Follow-up action select it from the menu enter follow-up Q&A primed with your full report
Follow-up chat any text sends your question with full context, then returns to the action menu when you exit
Follow-up failure choose Retry this question, Skip this question, or Exit follow-up recovers cleanly without losing the session
Follow-up chat done, quit, exit, q exits cleanly
Follow-up chat empty line ignored; reprompts
Launch metricpath --population-score include the experimental Ding 2015 population-normed risk score
Launch metricpath --population-percentile include the trained-logistic risk percentile (NHANES model)
Launch metricpath --help print CLI help and exit
Any phase Ctrl-C aborts

Exit codes

Code Meaning
0 success — report generated (and optional steps completed or skipped)
1 missing dependency or unrecoverable runtime error
130 interrupted with Ctrl-C

Planned flag surface

A richer non-interactive CLI is on the roadmap (separate intake / report / export / ask subcommands with --session, --json, -o, etc.). The scaffolding lives in cli/errors.py for exit codes and the MP#### error catalog, but those subcommands are not wired into main.py yet — the current build intentionally ships one happy path end to end.


Walkthrough

Step 1 — Welcome & intake

When you run metricpath, you're greeted with a welcome banner, choose a provider and model from the startup menus, and then the intake conversation begins.


Step 2 — Intake conversation

MetricPath asks one question at a time — never a form. The conversation covers sleep, diet, exercise, stress level, goals, constraints, and schedule. Numeric answers (sleep hours, stress rating, exercise days) are validated before the LLM moves on.

The intake covers these 19 topics in a natural order:

# Topic Example answer Notes
1 Average nightly sleep hours 6.5 validated: 0–24
2 Sleep schedule consistency Inconsistent — weekdays 7am, weekends 10am free text
3 Typical meals Oatmeal, grab lunch out, home dinner free text
4 Dietary restrictions Gluten-free (celiac) free text
5 Exercise days per week 4 validated: 0–7
6 Exercise type Running free text
7 Average exercise duration 30 min free text
8 Stress level (1–10) 6 validated: 1–10 integer
9 Lifestyle goals Sleep better, lose weight free text
10 Key constraints Busy schedule, tight budget free text
11 Pre-existing conditions (optional — skip if none) free text
12 Wake time 7:00 AM free text
13 Work / school hours 9–5 free text
14 Connection check-in (UCLA-3) Hardly ever / some of the time / often 3 Likert items, scored 3–9
15 Hydration 7 cups/day validated: 0–40 cups
16 Alcohol intake 3 drinks/week validated: 0–100 drinks
17 Daily sitting hours 8 validated: 0–24; used for sedentary-risk score
18 Age in years 34 or skip optional; risk model defaults to 45 when absent
19 Biological sex female or prefer not to say optional; risk model defaults to female reference when absent

Step 3 — Report generation

Once the intake is complete, MetricPath computes your health metrics and generates your personalized report in a single LLM call. This takes about 10–15 seconds.

Computed metrics injected into the report prompt:

Metric Formula
Sleep debt max(0, (8 − avg_sleep_hrs) × 7) hours/week
Activity score (exercise_days / 7) × 10 out of 10
Diet score Keyword heuristic on meal description, 0–10
Stress level Echoed from intake, 1–10

Step 4 — Your Lifestyle Report

The report is printed to the terminal first and kept ephemeral by default. If you want a local artifact, choose Save report locally from the post-report action menu and MetricPath writes a branded PDF to reports/. Every recommendation is tied to your specific answers — not generic advice.

Report sections:

Section What's in it
Dashboard Snapshot of your metrics — sleep debt, activity, diet, stress
Sleep Adjustments 3–5 concrete changes with "why this works for you" explanations
Diet Adjustments 3–5 adjustments based on your actual meals and restrictions
Exercise Adjustments 3–5 suggestions factoring in your constraints and current routine
Stress & Recovery 2–3 targeted recommendations tied to your stress level and goals
7-Day Plan Day-by-day checklist with progression built in
15-Day Milestones Measurable shifts to look for in weeks 2–3
30-Day Targets Outcome-oriented goals tied to what you said you wanted
Closing Note A personal, slightly playful send-off referencing something real you said

Step 5 — Daily schedule

A personalized time-block schedule is built from your wake time, work hours, and exercise habits, and injected into the report.

The schedule auto-places:

  • Morning routine (wake → work)
  • Exercise slot (pre-work if time allows, post-work otherwise — never double-booked)
  • Lunch midpoint
  • Dinner
  • Wind-down routine (45 min before target bedtime)
  • 8-hour sleep target block

Step 6 — Follow-up

After the report, you're dropped into a small action menu:

Save report locally — writes a branded PDF to reports/ using the filename you choose.

Follow-up Q&A — the LLM is primed with your full intake, metrics, schedule, and report. Ask anything:

  • "Why did you recommend locking in a wake time?"
  • "Can you swap the mid-week run for something lower impact?"
  • "What should I focus on first given my tight schedule?"

Start fresh — jumps back to provider/model selection and rebuilds the whole session from empty state.

Type done or quit to leave follow-up and return to the action menu.


Population risk percentile

Pass --population-percentile (or set METRICPATH_POPULATION_PERCENTILE=1) to print an additional panel after the score:

╭─ Population percentile (logistic) ─────────────────────────╮
│ Population Risk Percentile: 41/100                          │
│ (trained logistic, lower = lower predicted risk)            │
│ log-odds: -3.459   p: 0.030                                 │
│ Top lifestyle contributors:                                  │
│   · low_activity          +0.485                            │
│   · high_sitting          +0.266                            │
│   · short_sleep           +0.051                            │
╰─────────────────────────────────────────────────────────────╯

This is a logistic regression model trained on NHANES 2007–2008 microdata (n=6,219 adults) linked to the NCHS Public-Use Linked Mortality File through 2019. Test-fold AUC: 0.89. Features: age, sex (adjustment covariates), plus 8 lifestyle binary indicators. Coefficients live in cli/data/risk_logit_coefs.json alongside a percentile reference distribution computed over the same cohort.

The percentile is relative risk, not absolute probability — it shows where your lifestyle profile falls in the population distribution. Lower = lower predicted risk. It is never framed as a mortality estimate.

Re-training the model yourself (optional, for reproducibility):

# Install training-only dependencies (not needed for runtime):
.venv/bin/python -m pip install pandas pyreadstat scikit-learn

# Download NHANES 2007-2008 public microdata (free, no account needed):
mkdir -p ~/nhanes_2007_2008
cd ~/nhanes_2007_2008
for suffix in DEMO BMX SLQ PAQ DR1TOT ALQ SMQ DPQ; do
  curl -O "https://wwwn.cdc.gov/Nchs/Data/Nhanes/Public/2007/DataFiles/${suffix}_E.xpt"
done
for f in *.xpt; do mv "$f" "$(echo "$f" | tr '[:lower:]' '[:upper:]')"; done

# Download the NCHS Linked Mortality File (2019 public-use release):
curl -o ~/NHANES_2007_2008_MORT_2019_PUBLIC.dat \
  "https://ftp.cdc.gov/pub/Health_Statistics/NCHS/datalinkage/linked_mortality/NHANES_2007_2008_MORT_2019_PUBLIC.dat"

# Train (overwrites cli/data/*.json):
.venv/bin/python scripts/train_risk_model.py \
  --nhanes-dir ~/nhanes_2007_2008 \
  --lmf-file ~/NHANES_2007_2008_MORT_2019_PUBLIC.dat \
  --cycle-suffix E \
  --seed 42 \
  --force

The script validates data, fits the model, and writes two JSON files. Runtime never imports pandas, pyreadstat, or scikit-learn — those are training-only.


Output files

File Location Description
Lifestyle Report reports/<filename>.pdf Branded PDF report, written only when you choose Save report locally

The reports/ directory is created automatically on first write. It is gitignored — local exports stay on your machine.


Privacy

MetricPath is privacy-first. See PRIVACY.md for the full statement. Short version:

  • No account required. Each run starts fresh.
  • Opt-in persistence. The full pipeline never writes to disk unless you ask. The daily check-in prompts once for consent before it starts saving a local history file at ~/.metricpath/history.jsonl.
  • One-command wipe. metricpath reset removes ~/.metricpath/ after a typed DELETE confirmation.
  • Ephemeral mode. METRICPATH_EPHEMERAL=1 disables every disk write, including PDF report exports.
  • No telemetry. Zero analytics, zero crash reporting, zero ping-home.
  • PII filter on remote LLM calls. A deterministic filter redacts obvious email addresses and phone numbers from prompts sent to Anthropic or OpenAI. Ollama is local, so the filter does not run there.
  • External calls depend on the provider you choose for that run: Anthropic API, OpenAI API, or your local Ollama endpoint. Anthropic/OpenAI receive prompt data during the run (after the PII filter); Ollama keeps prompts local.

Troubleshooting

Symptom Likely cause Fix
Environment Setup Problem panel at startup wrong Python version, stale env, or missing dependencies activate a Python 3.12+ env, then run python -m pip install -e .
ERROR: The 'rich' library is required. at startup rich not installed in the active env python -m pip install -e .
ModuleNotFoundError: No module named 'anthropic' SDK missing python -m pip install -e .
AuthenticationError on the first intake turn the chosen paid provider key is unset, typo'd, or revoked re-check .env, or choose a different provider on startup
Ollama is not reachable at startup Ollama is not installed or not running locally install from ollama.com/download, then start it or run ollama serve
The Ollama model '…' is not installed locally. the chosen local model has not been pulled yet run ollama pull <model> and start metricpath again
LLM Request Problem panel rate limit, transient network issue, or local model hiccup use the retry prompt first; if it keeps failing, switch models or check provider status
Model Output Problem panel the model returned malformed intake/report output retry the step; if it repeats, switch to a more reliable model
Hangs at "Generating your Lifestyle Report…" for > 60s slow network, local model startup, or rate limit Ctrl-C and retry; check your chosen provider and model status
Colors / emojis render as garbled escape codes terminal doesn't advertise ANSI / UTF-8 export NO_COLOR=1 and use a modern terminal (iTerm2, Windows Terminal, etc.)
Want to regenerate the CLI screenshots for the thesis python ../thesis/cli_artifacts/make_screenshots.py (then npm run screenshots to render PNGs)

Project structure

metricpath/
├── cli/
│   ├── main.py               ← Entry point — run this
│   ├── intake.py             ← Multi-turn LLM intake (19 topics, validated)
│   ├── risk_model.py         ← Logistic risk model runtime (pure Python, no ML deps)
│   ├── llm_client.py         ← LLM provider wrapper (Anthropic / OpenAI / Ollama)
│   ├── metrics.py            ← Derived health score computation
│   ├── population_score.py   ← Ding 2015 population-normed risk score
│   ├── report_generator.py   ← LLM report orchestration
│   ├── schedule_builder.py   ← Daily time-block schedule
│   ├── data/
│   │   ├── risk_logit_coefs.json        ← Trained model coefficients (NHANES 2007-2008)
│   │   └── risk_score_distribution.json ← Population percentile reference distribution
│   └── prompts/
│       ├── system.txt        ← Base persona prompt
│       ├── intake/           ← Per-provider/family intake prompts (19 topics each)
│       │   ├── anthropic.txt
│       │   ├── openai.txt
│       │   └── ollama/       ← llama.txt, qwen.txt, phi.txt, gemma.txt, _default.txt
│       ├── report.txt        ← Report generation prompt
│       └── followup.txt      ← Follow-up Q&A prompt
├── scripts/
│   └── train_risk_model.py   ← Offline trainer (run once; requires pandas/pyreadstat/sklearn)
├── tests/                    ← Automated test suite (329 tests, no API key needed)
├── reports/                  ← Opt-in local PDF exports (gitignored)
├── pyproject.toml            ← Package metadata + `metricpath` command
├── playwright.config.ts      ← Harness for rendering thesis screenshots (SVG→PNG)
├── package.json              ← Pins @playwright/test for the screenshot harness
├── .env.example
└── AGENT.md                  ← Full architecture guide for contributors

Running tests

source .venv/bin/activate
python -m pip install -e ".[dev]"
pytest tests/ -v

All tests run without an API key — the LLM client is injectable and the suite uses fake callables. The risk model tests load the shipped cli/data/*.json directly.

329 passed in 0.75s

About

A personalized lifestyle improvement tool powered by AI. Conversational intake, 7/15/30-day plan, daily schedule, and a privacy-first local-only design.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages