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

12 KiB
Raw Blame History

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 (bring your own topic + auth).
  • 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:
    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):
    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:
    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:
    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:
    uvicorn api:app --reload --port 8000
    
  4. (Optional) start the hourly worker in another shell to mirror the compose setup:
    AGENTIC_INTERVAL_SECONDS=900 python agentic_worker.py
    
  5. Run one-off prompts without FastAPI by calling the helper scripts:
    # 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:

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 optional format string for topics (default adhdbot-{userId}); override per reminder via reminder.metadata.topic.
  • 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:

{
  "action": "schedule_reminder",
  "reminder": {
    "message": "short friendly text",
    "topic": "adhdbot-<user id>",
    "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-<user>), 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.