Files
ADHDbot/README.md
2025-11-11 23:43:43 -06:00

169 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ADHDbot
ADHDbot is a FastAPI + Discord assistant that captures ADHD-friendly notes, breaks work into tiny steps, and pipes confirmed reminders into ntfy so your phone vibrates when it matters. The repo also bundles an hourly “agentic sweep” worker and a lightweight web console for experimenting with prompts and action items.
## At a glance
- Opinionated system prompt + tooling contract wired through OpenRouter (Claude Haiku 4.5 by default).
- FastAPI surface area for chat runs, notes, and persistent action items—served by Docker or a bare Python venv.
- Notification bridge that turns `schedule_reminder` JSON into ntfy pushes (single ntfy topic defined via env vars).
- Hourly agentic workflow that summarizes memory + actions, then nudges the user via a dedicated prompt.
- Static React console (`web_App.tsx`) for local demos: send prompts, review transcripts, and edit action items without curl.
## Quick Start
1. Copy the example environment file and fill in your secrets:
```bash
cp .env.example .env
# edit .env to insert your real OPENROUTER_API_KEY, DISCORD_BOT_TOKEN, TARGET_USER_ID, etc.
```
2. Bring up the stack with docker-compose (recommended; includes host persistence for logs/notes):
```bash
docker compose up -d --build
```
- `./memory` is bind-mounted into the container (`./memory:/app/memory`), so any saved notes appear in the repo directly.
- `.env` is auto-loaded and the FastAPI service is exposed on `http://localhost:8000`.
- The compose stack now launches two services: `adhdbot` (the FastAPI/Discord gateway) and `agentic_worker`, a companion process that runs the hourly sweep loop.
3. Or build/run manually if you prefer the raw Docker commands:
```bash
docker build -t adhdbot .
docker run --rm -p 8000:8000 --env-file .env -v "$PWD/memory:/app/memory" adhdbot
```
## Local development (no Docker)
1. Create a virtual environment and install deps:
```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
2. Copy `.env.example` to `.env` and fill in the same secrets the container expects.
3. Launch the API with reload and rich logs:
```bash
uvicorn api:app --reload --port 8000
```
4. (Optional) start the hourly worker in another shell to mirror the compose setup:
```bash
AGENTIC_INTERVAL_SECONDS=900 python agentic_worker.py
```
5. Run one-off prompts without FastAPI by calling the helper scripts:
```bash
# Runs the main conversational prompt (uses env defaults for category/name/context)
python main.py
# Forces the hourly sweep packet through the agentic prompt once
python agentic_review.py
```
### API usage
Once the container is running, hit the API to trigger a prompt flow:
```bash
curl -X POST http://localhost:8000/run \
-H "Content-Type: application/json" \
-d '{
"userId": "chelsea",
"context": "Remind me in 10 minutes to stretch.",
"history": [
{"role": "user", "content": "Hi"},
{"role": "assistant", "content": "Hello!"}
],
"modeHint": "Reminder"
}'
```
Endpoints:
- `GET /health` simple liveness check.
- `POST /run` conversational entry point. Fields:
- `userId` (optional) defaults to `TARGET_USER_ID`.
- `context` the latest user message.
- `history` (optional) array of `{role:"user"|"assistant", content:"..."}` representing prior turns (most recent last).
- `modeHint` (optional) short string that nudges tone/behavior ("Planning", "Reminder", etc.).
- `category` / `promptName` remain for backward compatibility but no longer swap entire templates.
- `GET /users/{userId}/notes?limit=10` fetch the most recent notes (limit defaults to 10, use `limit=0` for all).
- `POST /users/{userId}/notes` persist a note manually by posting `{ "note": "text", "metadata": { ... } }`.
- `GET /users/{userId}/memory` full summaries + notes payload for the user.
- `GET /users/{userId}/actions` list the modifiable daily/periodic action items tied to that user's memory.
- `POST /users/{userId}/actions` create a new action item (`title`, optional `details`, `cadence`, `interval_minutes`).
- `PUT /users/{userId}/actions/{actionId}` update an item in-place (title, cadence, interval, or details).
- `DELETE /users/{userId}/actions/{actionId}` remove it entirely.
- `POST /users/{userId}/actions/{actionId}/progress` append a progress entry (`status`, optional `note`) so the hourly sweep knows the latest state.
- `POST /users/{userId}/notes/test` quick QA helper that reuses the welcome prompt with a custom `context` JSON body.
- `GET /prompts` inspect the currently loaded prompt catalog.
- `POST /prompts/reload` force a reload from the `prompts/` folder.
Environment variables of interest (see `.env.example`):
- `OPENROUTER_API_KEY` OpenRouter key used by `AIInteraction`.
- `DISCORD_BOT_TOKEN` / `TARGET_USER_ID` / `DISCORD_WEBHOOK_URL` Discord plumbing.
- `PROMPT_CATEGORY`, `PROMPT_NAME`, `PROMPT_CONTEXT` defaults for the `/run` endpoint.
- `LOG_PROMPTS` (default `1`) when truthy, every outgoing prompt is logged to stdout so you can audit the final instructions sent to the LLM.
- `NTFY_BASE_URL` when set (e.g., `https://ntfy.scorpi.us`), reminder payloads with `action: schedule_reminder` will be POSTed to ntfy.
- `NTFY_TOPIC` optional fixed topic slug; when present every reminder is sent to exactly this channel (e.g., `adhdbot-chelsea`).
- `NTFY_TOPIC_TEMPLATE` fallback format string for the topic when `NTFY_TOPIC` is unset (default `adhdbot-{userId}`).
- `NTFY_AUTH_TOKEN` optional bearer token if your ntfy server requires auth.
- `AGENTIC_CATEGORY` / `AGENTIC_PROMPT_NAME` / `AGENTIC_MODE_HINT` control which prompt handles the hourly agentic sweep (defaults: `agentic/hourly_review`, hint "Agentic review").
- `AGENTIC_NOTES_LIMIT` how many of the most recent notes to include in the sweep payload (default `5`).
- `AGENTIC_OPERATOR_HINT` optional text passed to `agentic_review.py` so you can bias the sweep for a given run (cron, manual nudge, etc.).
- `AGENTIC_INTERVAL_SECONDS` cadence for the always-on worker loop (defaults to 3600 seconds/1 hour).
### Frontend console
- `web_App.tsx` + `web_App.css` describe a quick React shell that talks directly to `/api/run` and the action endpoints. Drop the file into any Vite/CRA sandbox or use it as design reference for your own console.
- The UI stores chat history in `localStorage`, mirrors the three built-in prompts (“general”, “planning”, “reminders”), and exposes an action-item panel with CRUD + progress logging—so you can test the API without Postman.
- When hosting the FastAPI server, make sure it serves static assets or proxy `/api/*` so the console can fetch without CORS gymnastics.
### Reminder payloads
When the assistant schedules a reminder it emits a single JSON block:
```json
{
"action": "schedule_reminder",
"reminder": {
"message": "short friendly text",
"trigger": {
"value": "2025-11-11T02:41:42+00:00"
}
}
}
```
The backend automatically converts relative phrases ("in 10 minutes") into the ISO timestamp above and POSTs the message to the ntfy topic (either the fixed `NTFY_TOPIC` or the templated `https://ntfy.scorpi.us/adhdbot-<user>` fallback), so subscribing to that topic on your phone is all you need for push notifications.
### Daily / Periodic Action List + Hourly Agentic Sweep
- Action items share the same storage as notes inside `memory/<user>_memory.json` under the `action_items` key. Each entry tracks `title`, `cadence`, optional `interval_minutes`, `details`, and a rolling `progress` history.
- Use the action API endpoints (above) to add/remove/edit entries or append `status` updates—CLI, scripts, or the UI can call them exactly like the note endpoints. The bundled `web_App.tsx` (served by the static UI) now surfaces a lightweight management panel to create actions, log progress, and delete entries without touching curl.
- `AgenticWorkflow.buildReviewPacket` compiles the latest notes plus the action list into a JSON blob and feeds it into the `agentic/hourly_review` prompt. The new helper script `agentic_review.py` calls this flow; point a cron/systemd timer at it (hourly) so the autopilot can look for overdue habits or opportunities.
- `agentic_worker.py` wraps the same helper in a persistent loop. The `agentic_worker` service defined in `docker-compose.yml` runs it with the same `.env` file, so deploying the stack automatically keeps the hourly sweep online. Adjust cadence via `AGENTIC_INTERVAL_SECONDS` or stop the service if you prefer to trigger sweeps manually.
- Customize the autopilot without code by editing `prompts/defaultPrompts.json` (or adding a sibling file) to adjust `agentic/hourly_review`, then reload prompts or rebuild the container.
## Architecture cheat sheet
- **`api.py` (FastAPI)**: exposes chat, memory, action-item, and prompt-catalog routes. It uses Pydantic models for validation and wraps every handler with `ensureUserId`/`MemoryManager` helpers so non-FastAPI callers stay lean.
- **`Runner.py` + `AIInteraction.py`**: glue between your request and OpenRouter. `Runner` is a thin façade; `AIInteraction` composes the system prompt, trims chat history, logs prompts when `LOG_PROMPTS=1`, and post-processes responses for memory + notifications.
- **`Memory.py`**: owns all persistence under `memory/<user>_memory.json` (notes, summaries, and `action_items`). JSON blocks emitted by the model (`take_note`, `schedule_reminder`, etc.) land here before any downstream automations run.
- **`Notification.py`**: watches the same responses for `schedule_reminder` payloads and relays them to ntfy with sanitized titles + timestamps. Leave `NTFY_BASE_URL` unset to disable the bridge without touching code.
- **`AgenticWorkflow.py` + `agentic_worker.py`**: build the hourly sweep packet (latest notes, summaries, action progress) and push it through `agentic/hourly_review`. Run `agentic_worker` via Docker Compose or your own cron/systemd timer for 24/7 coverage.
- **`DiscordGateway.py`**: optional DM/webhook plumbing so every assistant reply can bounce straight into Discord when `DISCORD_BOT_TOKEN` or `DISCORD_WEBHOOK_URL` is configured.
- **Prompts folder**: `prompts/defaultPrompts.json` ships with sane defaults; drop additional JSON files in the same folder and call `POST /prompts/reload` to hot-swap templates. Tooling/JSON contract lives in `prompts/tool_instructions.md`.
## Prompt + tooling customization
- All templates live in `prompts/defaultPrompts.json` (and sibling files). Edit them and restart the service to take effect.
- Shared tooling instructions live in `prompts/tool_instructions.md`. `AIInteraction` injects this file both into the **system prompt** and at the end of every user prompt, so any changes immediately affect how models emit `take_note`, `store_task`, or `schedule_reminder` JSON payloads.
- `PROMPTS.md` documents each category plus examples of the structured JSON outputs that downstream services can parse.
### Memory + notes
- The memory subsystem watches LLM responses for fenced ```json payloads. When it sees `{"action": "take_note", ...}` it writes to `memory/<user>_memory.json` (now persisted on the host via the compose volume).
- Each entry includes the note text, UTC timestamp, and the raw metadata payload, so other services can build summaries or downstream automations from the same file.
### Debugging tips
- Tail the container logs with `docker compose logs -f adhdbot` to see:
- The final prompt (with tooling contract) sent to the model.
- Memory ingestion messages like `[memory] Recorded note for <user>: ...`.
- If you swap models, change `openRouterModel` in `AIInteraction.py` (or surface it via env) and rebuild the container.