# 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_TEMPLATE` – format string for the ntfy topic slug (default `adhdbot-{userId}`); all reminders are forced through this template so every notification lands in the same subscribed channel. - `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 (default `https://ntfy.scorpi.us/adhdbot-`), 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/_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/_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/_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 : ...`. - If you swap models, change `openRouterModel` in `AIInteraction.py` (or surface it via env) and rebuild the container.