Skip to content

kaihv/wellness-hackathon

Repository files navigation

Wellness Agent — AI Tinkerers Hackathon 2026

Generative UI for human performance — a Gemini-powered agent that reads your Apple Watch biometrics and generates a dynamic, contextual UI for your wellness state instead of a fixed dashboard.

Built for: AI Tinkerers Santiago — Generative UI Global Hackathon

The idea

Traditional health apps show static dashboards. This agent generates the UI dynamically based on your actual biometric state. The model analyzes your HRV, sleep, heart rate, and activity data, then picks which components to render and how to frame the insights.

Apple Watch → HealthKit → Gemini 2.5 Flash Lite → JSON widget spec → Flutter renders it

This is the A2UI pattern: agents return UI specs, not just text.

Demo

  1. Open app → agent reads your last 24h of biometrics
  2. Agent returns: wellness score + 5–8 dynamic widgets + 3 recommendations
  3. Chat mode: ask "¿estoy listo para entrenar hoy?" → agent responds with contextual widgets and can call tools (show_insight, recommend_exercise, etc.) that slide overlays over the chat

Quick start

Prereqs

  • Flutter 3.41+ · Dart 3.11+
  • Xcode 16+ (for iOS) and CocoaPods (sudo gem install cocoapods if missing)
  • A Google AI Studio API key (free tier is enough for the demo) — aistudio.google.com

1. Clone + install

git clone https://github.com/kaihv/wellness-hackathon
cd wellness-hackathon
flutter pub get
cd ios && pod install && cd ..

2. Create .env

The app reads its keys via --dart-define. The easiest way is to drop a .env at the repo root and let Flutter load it with --dart-define-from-file=.env. Required vs optional:

# ── REQUIRED ──────────────────────────────────────────────────────
GOOGLE_API_KEY=AIza...            # Gemini — aistudio.google.com/app/apikey

# ── OPTIONAL ──────────────────────────────────────────────────────
STRAVA_CLIENT_ID=                 # developers.strava.com — for activity data
STRAVA_CLIENT_SECRET=
DEEPGRAM_API_KEY=                 # Realtime STT in chat input. If empty,
                                  # falls back to on-device speech_to_text.
FIREBASE_API_KEY=                 # Vertex AI fallback when AI Studio quota
                                  # is exhausted. Ask a teammate for the
                                  # shared `makana-flutter-app` key. If
                                  # empty, the agent stays on AI Studio
                                  # only (free-tier quota applies).
WELLNESS_BACKEND_URL=http://localhost:8000   # AG-UI backend; defaults to
                                             # localhost. If unreachable, the
                                             # app calls Gemini directly.

.env is gitignored — never commit it.

3. Run

Two equivalent options:

# A. Use the helper script (reads root .env or backend/.env, forwards keys)
bash scripts/run-flutter.sh

# B. Pass the file directly to flutter run
flutter run --dart-define-from-file=.env

Pin a specific device with -d <device-id> (flutter devices to list).

4. (Optional) Backend

Most of the app works without a backend (Flutter calls Gemini directly). To showcase the full AG-UI + LangGraph + Daytona pipeline, start the Python backend:

cd backend
cp .env.example .env              # add your keys
python3 -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python main.py                    # → http://localhost:8000

Then run the Flutter app with WELLNESS_BACKEND_URL pointing at it (or use ngrok if running on a physical iPhone).

Free tier quotas (Gemini)

The default model is gemini-2.5-flash-lite (~1000 RPD on the free tier). If you switch to gemini-2.5-flash you'll hit RESOURCE_EXHAUSTED after 20 requests/day. To raise this, enable billing on your Google Cloud project tied to the API key.

iOS notes

  • The repo's ios/Runner/Info.plist already declares the usage strings the runtime needs (NSHealth*, NSMicrophoneUsageDescription, NSSpeechRecognitionUsageDescription, NSMotionUsageDescription, NSLocalNetworkUsageDescription). Without these, iOS terminates the app with a TCC privacy violation when the chat opens.
  • The simulator falls back to WellnessSnapshot.mock() for biometrics. For real HRV / sleep stages you need a physical iPhone paired with an Apple Watch.

Project layout

lib/
├── main.dart                          # Entry + API key setup + onboarding gate
├── config.dart                        # GOOGLE_API_KEY / STRAVA_* / DEEPGRAM_* / model id
├── services/
│   ├── health_service.dart            # HealthKit via `health` (+ mock fallback)
│   ├── strava_service.dart            # Strava OAuth2 + activities API
│   ├── deepgram_realtime_service.dart # Deepgram WebSocket realtime STT
│   ├── speech_to_text_service.dart    # On-device STT fallback (`es_CL`)
│   ├── ag_ui_service.dart             # AG-UI Dart SDK to FastAPI backend (optional)
│   └── wellness_agent_service.dart    # Gemini `generateContent` — chat() + analyze()
├── tools/
│   ├── wellness_tool.dart             # 4 agent tools + Riverpod overlay state
│   └── wellness_tool_registry.dart    # Function declarations for Gemini
└── ui/
    ├── theme/                         # Brand kit (colors, typography, spaces, radii)
    ├── wellness_widget_factory.dart   # JSON spec → widgets (6 types)
    ├── widgets/chat/                  # chat_input_bar + stt_sheet + message_bubble
    └── pages/
        ├── home_page.dart             # Dashboard (analyze → factory)
        ├── agent_chat_page.dart       # Conversational chat with tool overlays
        └── connections/               # HealthKit / Strava / Garmin / Surfline pages

Widget types (generated by the agent)

Type Description
metric_card Single metric with trend + status color
sleep_chart Stacked bar: deep / REM / light / awake
activity_summary Strava activity with intensity badge
insight Emoji + priority-colored insight card
score_ring PieChart ring 0–100
progress_bar Goal progress (e.g. steps/day)

Health data read

Metric Source Why it matters
HRV (SDNN) Apple Watch Primary recovery indicator
Resting HR Apple Watch Fitness + overtraining signal
Sleep stages Apple Watch Deep/REM quality
Steps iPhone/Watch Daily movement
Blood oxygen Apple Watch Altitude / breathing
Activities Strava Training load

Tech stack

  • Flutter 3.41+ (iOS-first, Android & web work)
  • Gemini 2.5 Flash Lite via Google AI Studio generateContent + function calling
  • health ^13.3.1 — HealthKit / Google Fit
  • speech_to_text ^7.0.0 + optional Deepgram realtime (WebSocket + record)
  • ag_ui ^0.1.0 — AG-UI Dart SDK (CopilotKit sponsor) when backend is up
  • fl_chart ^0.68.0 — charts
  • flutter_riverpod ^2.5.1 + signals_flutter ^5.5.1 — state
  • Strava API v3 — OAuth2 activities
  • Backend (optional): FastAPI + LangGraph + LangSmith + Daytona

Troubleshooting

  • HTTP 429 RESOURCE_EXHAUSTED in [Gemini] logs → free-tier quota hit. Wait until midnight Pacific or enable billing on the Cloud project.
  • iOS build fails on record_linux → run flutter pub get again. The repo's dependency_overrides pin record_linux ^1.0.0 to dodge a transitive break, and a stale lockfile can shadow it.
  • App crashes opening chat → an Info.plist is missing NSSpeechRecognitionUsageDescription / NSMicrophoneUsageDescription. Already present in this repo, but if you regenerate the iOS folder, re-add them.
  • Chat replies "no pude conectar con el agente" → check the Dart console for a [Gemini] HTTP … line. It surfaces the exact API error (bad key, model not found, quota, etc.).

IP & license

MIT License — all IP belongs to the authors.


Built in 6 hours at AI Tinkerers Santiago, May 2026

About

Generative UI wellness agent — AI Tinkerers Hackathon 2026. Claude reads Apple Watch biometrics and generates dynamic Flutter UI.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors