From 04ea648950047bcc1fff1b54f610b982d5df4b54 Mon Sep 17 00:00:00 2001 From: chelsea Date: Sat, 14 Feb 2026 17:28:59 -0600 Subject: [PATCH] updated readme --- README.md | 374 +++++++++++++++++++++++++----------------------------- 1 file changed, 175 insertions(+), 199 deletions(-) diff --git a/README.md b/README.md index a5656db..79969f2 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,47 @@ -# LLM Bot Framework +# Synculous -A template for building Discord bots powered by LLMs with natural language command parsing. +A routine and medication management app designed as a prosthetic for executive function. Built for people with ADHD. -## Features +The app externalizes the things ADHD impairs — time awareness, sequence memory, task initiation, and emotional regulation around failure — into a guided, sequential interface with immediate feedback and zero shame. -- **AI-Powered Parsing**: Uses LLMs to parse natural language into structured JSON with automatic retry and validation -- **Module Registry**: Easily register domain-specific command handlers -- **Flask API**: REST API with JWT authentication -- **PostgreSQL**: Generic CRUD layer for any table -- **Discord Bot**: Session management, login flow, background tasks -- **Notifications**: Discord webhook + ntfy support out of the box -- **Docker Ready**: Full docker-compose setup +## Architecture + +``` +synculous/ +├── synculous-client/ # Next.js 16 frontend (React, Tailwind) +├── api/ # Flask REST API +│ ├── main.py # App entry point, auth routes +│ └── routes/ # Domain route modules +│ ├── routines.py # Routines CRUD + sessions +│ ├── routine_sessions_extended.py # Pause, resume, abort, notes +│ ├── routine_stats.py # Completion stats, streaks, weekly summary +│ ├── routine_templates.py # Premade routine templates +│ ├── routine_steps_extended.py # Step instructions, types, media +│ ├── routine_tags.py # Tagging system +│ ├── medications.py # Medication scheduling + adherence +│ ├── preferences.py # User settings + timezone +│ ├── notifications.py # Web push subscriptions +│ ├── rewards.py # Variable reward system +│ └── victories.py # Achievement detection +├── core/ # Shared business logic +│ ├── postgres.py # Generic PostgreSQL CRUD +│ ├── auth.py # JWT + bcrypt authentication +│ ├── users.py # User management +│ ├── routines.py # Routine/session/streak logic +│ ├── tz.py # Timezone-aware date/time helpers +│ └── notifications.py # Multi-channel notifications +├── scheduler/ +│ └── daemon.py # Background polling for reminders +├── bot/ # Discord bot (optional) +├── ai/ # LLM parser for natural language commands +├── config/ +│ ├── schema.sql # Database schema +│ ├── seed_templates.sql # 12 premade ADHD-designed routine templates +│ ├── seed_rewards.sql # Variable reward pool +│ └── .env.example # Environment template +├── docker-compose.yml +└── Dockerfile +``` ## Quick Start @@ -25,226 +56,171 @@ nano config/.env docker-compose up ``` -## Project Structure +This starts five services: -``` -llm-bot-framework/ -├── api/ -│ ├── main.py # Flask app with auth routes -│ └── routes/ -│ └── example.py # Example route module -├── bot/ -│ ├── bot.py # Discord client -│ ├── command_registry.py # Module registration -│ └── commands/ -│ └── example.py # Example command module -├── core/ -│ ├── postgres.py # Generic PostgreSQL CRUD -│ ├── auth.py # JWT + bcrypt -│ ├── users.py # User management -│ └── notifications.py # Multi-channel notifications -├── ai/ -│ ├── parser.py # LLM JSON parser -│ └── ai_config.json # Model + prompts config -├── scheduler/ -│ └── daemon.py # Background polling -├── config/ -│ ├── schema.sql # Database schema -│ └── .env.example # Environment template -├── docker-compose.yml -├── Dockerfile -└── requirements.txt -``` +| Service | Port | Description | +|---------|------|-------------| +| `db` | 5432 | PostgreSQL 16 with schema + seed data | +| `app` | 8080 | Flask API | +| `scheduler` | — | Background daemon for medication/routine reminders | +| `bot` | — | Discord bot (optional, needs `DISCORD_BOT_TOKEN`) | +| `client` | 3000 | Next.js frontend | -## Creating a Domain Module +## Features -### 1. Add Database Schema +### Routines +- Create routines with ordered steps (4-7 steps recommended) +- Run sessions with a guided, one-step-at-a-time focus interface +- Complete, skip, pause, resume, or cancel sessions +- Swipe gestures for step completion on mobile +- Per-step timing with visual countdown +- Animated celebration screen on completion with streak stats and variable rewards -Edit `config/schema.sql`: +### Premade Templates +12 ADHD-designed templates ship out of the box, seeded from `config/seed_templates.sql`: -```sql -CREATE TABLE IF NOT EXISTS tasks ( - id UUID PRIMARY KEY, - user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, - name VARCHAR(255) NOT NULL, - completed BOOLEAN DEFAULT FALSE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` +| Template | What it's for | +|----------|---------------| +| Morning Launch | Getting from bed to ready. First step: just sit up. | +| Leaving the House | The keys-wallet-phone checklist. | +| Focus Sprint | One focused work block with environment setup first. | +| Wind Down | Screen-first sleep transition. | +| Quick Tidy | Fast sweep, not deep cleaning. Completable on bad days. | +| Body Reset | Minimum viable hygiene. Zero judgment. | +| Unstuck | For executive function paralysis. Pure two-minute-rule. | +| Evening Reset | Set tomorrow up to be easier. | +| Move Your Body | Not a workout. Just movement. Starts with shoes. | +| Sunday Reset | Weekly prep so Monday doesn't ambush you. | +| Cook a Meal | One meal, start to finish, low activation energy. | +| Errand Run | Externalized planning + sequencing for errands. | -### 2. Create API Routes +All templates follow the design framework: two-minute-rule entry points, concrete instructions, zero-shame language, 4-6 steps max. -Create `api/routes/tasks.py`: +### Medications +- Scheduling: daily, twice daily, specific days, every N days, as-needed (PRN) +- "Today's meds" view with cross-midnight lookahead (late night + early morning) +- Take, skip, snooze actions with logging +- Adherence tracking and statistics +- Refill tracking with low-quantity alerts +- Background reminders via the scheduler daemon -```python -import flask -import sys, os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +### Streaks and Stats +- Per-routine streak tracking (current + longest) +- Weekly summary across all routines +- Completion rate and average duration stats +- Victory detection (comebacks, weekend completions, variety, consistency) +- Milestone celebrations at 3, 7, 14, 21, 30, 60, 90, 100, 365 days -import core.auth as auth -import core.postgres as postgres -import uuid +### Rewards +- Variable reward pool seeded from `config/seed_rewards.sql` +- Random reward on routine completion (post-completion only, never mid-routine) +- Reward history tracking per user +- Common and rare rarity tiers -def register(app): - @app.route('/api/tasks', methods=['GET']) - def api_listTasks(): - header = flask.request.headers.get('Authorization', '') - if not header.startswith('Bearer '): - return flask.jsonify({'error': 'missing token'}), 401 - token = header[7:] - - # Get user UUID from token - from core.auth import decodeJwtPayload - import json, base64 - payload = token.split('.')[1] - payload += '=' * (4 - len(payload) % 4) - decoded = json.loads(base64.urlsafe_b64decode(payload)) - user_uuid = decoded['sub'] - - tasks = postgres.select("tasks", {"user_uuid": user_uuid}) - return flask.jsonify(tasks), 200 +### Notifications +- Web push notifications via VAPID +- Discord webhooks +- ntfy support +- Scheduled reminders for medications and routines - @app.route('/api/tasks', methods=['POST']) - def api_addTask(): - header = flask.request.headers.get('Authorization', '') - if not header.startswith('Bearer '): - return flask.jsonify({'error': 'missing token'}), 401 - token = header[7:] - - data = flask.request.get_json() - task = postgres.insert("tasks", { - 'id': str(uuid.uuid4()), - 'user_uuid': data['user_uuid'], - 'name': data['name'], - }) - return flask.jsonify(task), 201 -``` +### Timezone Support +All date/time operations respect the user's local timezone: +- The frontend sends `X-Timezone-Offset` with every API request +- The timezone offset is also persisted to `user_preferences` for background jobs +- Streaks, "today's meds," weekly stats, and reminders all use the user's local date +- The scheduler daemon looks up each user's stored offset for reminder timing -Register it in `api/main.py`: +### User Preferences +- Sound effects (default off — habituation risk) +- Haptic feedback (default on) +- Launch screen toggle +- Celebration style +- Timezone offset (auto-synced from browser) -```python -import api.routes.tasks as tasks_routes -register_routes(tasks_routes) -``` +## API Overview -### 3. Create Bot Commands +All endpoints require `Authorization: Bearer ` except `/api/register` and `/api/login`. -Create `bot/commands/tasks.py`: +### Auth +| Method | Endpoint | Description | +|--------|----------|-------------| +| POST | `/api/register` | Create account | +| POST | `/api/login` | Get JWT token | -```python -import sys, os -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) +### Routines +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/routines` | List user's routines | +| POST | `/api/routines` | Create a routine | +| GET | `/api/routines/:id` | Get routine with steps | +| PUT | `/api/routines/:id` | Update routine | +| DELETE | `/api/routines/:id` | Delete routine | +| GET | `/api/routines/:id/steps` | List steps | +| POST | `/api/routines/:id/steps` | Add a step | +| PUT | `/api/routines/:id/steps/reorder` | Reorder steps | +| POST | `/api/routines/:id/start` | Start a session | -from bot.command_registry import register_module -import ai.parser as ai_parser +### Sessions +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/sessions/active` | Get active or paused session | +| POST | `/api/sessions/:id/complete-step` | Complete current step | +| POST | `/api/sessions/:id/skip-step` | Skip current step | +| POST | `/api/sessions/:id/pause` | Pause session | +| POST | `/api/sessions/:id/resume` | Resume session | +| POST | `/api/sessions/:id/cancel` | Cancel session | +| POST | `/api/sessions/:id/abort` | Abort with reason | -async def handle_task(message, session, parsed): - action = parsed.get('action') - token = session['token'] - user_uuid = session['user_uuid'] - - # Make API calls using the bot's apiRequest helper - from bot.bot import apiRequest - - if action == 'list': - result, status = apiRequest('get', f'/api/tasks', token) - if status == 200: - lines = [f"- {t['name']}" for t in result] - await message.channel.send("Your tasks:\n" + "\n".join(lines)) - else: - await message.channel.send("Failed to fetch tasks.") - - elif action == 'add': - task_name = parsed.get('task_name') - result, status = apiRequest('post', '/api/tasks', token, { - 'user_uuid': user_uuid, - 'name': task_name - }) - if status == 201: - await message.channel.send(f"Added task: **{task_name}**") - else: - await message.channel.send("Failed to add task.") +### Medications +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/medications` | List medications | +| POST | `/api/medications` | Add medication | +| GET | `/api/medications/today` | Today's schedule with status | +| POST | `/api/medications/:id/take` | Log dose taken | +| POST | `/api/medications/:id/skip` | Log dose skipped | +| GET | `/api/medications/adherence` | Adherence stats | +| GET | `/api/medications/refills-due` | Refills due soon | -def validate_task_json(data): - errors = [] - if 'action' not in data: - errors.append('Missing required field: action') - if data.get('action') == 'add' and 'task_name' not in data: - errors.append('Missing required field for add: task_name') - return errors +### Stats +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/routines/:id/stats` | Routine completion stats | +| GET | `/api/routines/:id/streak` | Routine streak | +| GET | `/api/routines/streaks` | All streaks | +| GET | `/api/routines/weekly-summary` | Weekly progress | +| GET | `/api/victories` | Achievement detection | -register_module('task', handle_task) -ai_parser.register_validator('task', validate_task_json) -``` - -### 4. Add AI Prompts - -Edit `ai/ai_config.json`: - -```json -{ - "prompts": { - "command_parser": { - "system": "You are a helpful assistant...", - "user_template": "..." - }, - "task_parser": { - "system": "You parse task commands...", - "user_template": "Parse: \"{user_input}\"\n\nReturn JSON with action (list/add/complete) and task_name." - } - } -} -``` - -## AI Parser Usage - -```python -import ai.parser as ai_parser - -# Basic usage -parsed = ai_parser.parse(user_input, 'command_parser') - -# With conversation history -history = [("previous message", {"action": "add", "item": "test"})] -parsed = ai_parser.parse(user_input, 'command_parser', history=history) - -# Register custom validator -ai_parser.register_validator('task', validate_task_json) -``` - -## Notification Channels - -```python -import core.notifications as notif - -# Set user notification settings -notif.setNotificationSettings(user_uuid, { - 'discord_webhook': 'https://discord.com/api/webhooks/...', - 'discord_enabled': True, - 'ntfy_topic': 'my-alerts', - 'ntfy_enabled': True -}) - -# Send notification -settings = notif.getNotificationSettings(user_uuid) -notif._sendToEnabledChannels(settings, "Task due: Buy groceries") -``` +### Templates, Tags, Rewards, Preferences +| Method | Endpoint | Description | +|--------|----------|-------------| +| GET | `/api/templates` | List available templates | +| POST | `/api/templates/:id/clone` | Clone template to user's routines | +| GET/PUT | `/api/preferences` | User settings | +| GET | `/api/rewards/random` | Random completion reward | ## Environment Variables | Variable | Description | |----------|-------------| -| `DISCORD_BOT_TOKEN` | Discord bot token | -| `API_URL` | API URL (default: `http://app:5000`) | | `DB_HOST` | PostgreSQL host | | `DB_PORT` | PostgreSQL port | | `DB_NAME` | Database name | | `DB_USER` | Database user | | `DB_PASS` | Database password | | `JWT_SECRET` | JWT signing secret | -| `OPENROUTER_API_KEY` | OpenRouter API key | -| `OPENROUTER_BASE_URL` | OpenRouter base URL | -| `AI_CONFIG_PATH` | Path to ai_config.json | +| `DISCORD_BOT_TOKEN` | Discord bot token (optional) | +| `API_URL` | API URL for bot (default: `http://app:5000`) | +| `OPENROUTER_API_KEY` | OpenRouter API key (for AI parser) | +| `POLL_INTERVAL` | Scheduler poll interval in seconds (default: 60) | + +## Design Framework + +Synculous follows a documented design framework based on research from 9 books on behavior design, cognitive psychology, and ADHD. The three core principles: + +1. **Immediate Feedback** — Visual state change on tap in <0.1s. Per-step completion signals. Post-routine celebration. +2. **One Thing at a Time** — Current step visually dominant. No decisions during execution. 4-7 steps max per routine. +3. **Zero Shame** — No failure language. Streaks as identity markers, not performance metrics. Non-punitive everywhere. ## License