๐Ÿ“„ README.md 21,419 bytes Apr 25, 2026 ๐Ÿ“‹ Raw

Family Assistant

Sovereign family operations hub โ€” email โ†’ calendar + RAG, with LLM-powered parsing, conflict resolution, and conversational retrieval. Zero cloud dependencies.

Family Assistant watches a dedicated email inbox, extracts structured event data using a local LLM (no regex, no brittle date parsers), syncs to a self-hosted Radicale CalDAV calendar, and answers questions about past emails using semantic search. When events conflict, it suggests intelligent resolutions. When someone asks "what do the kids need for the field trip?", it pulls the answer from the emails it's already processed.

Why This Exists

Family calendar management is a solved problem โ€” if you use Cozi, Google Family Calendar, or any of the dozen apps out there. But they all share the same weakness: they parse appointment emails with regex. One formatting change from your vet's office and the parser breaks.

Family Assistant uses Prompt-as-Code: a local LLM reads the email and extracts structured JSON. The prompt is easier to update than a regex library, handles edge cases naturally, and doesn't break when a sender changes their subject line format.

More importantly, Family Assistant is sovereign โ€” it runs on your hardware, stores data on your disk, and uses your domain. No Google account required. No OAuth tokens that expire. No API quotas that get suspended. Your calendar, your email, your data.

Architecture

Email โ†’ Cloudflare Email Worker โ†’ hook.hoffdesk.com โ†’ FastAPI Webhook
                                                              โ”‚
                                                              โ–ผ
                                                        LLM Triage โ”€โ”ฌโ”€โ†’ Appointment Parse โ”€โ”€โ†’ Radicale CalDAV + Brain
                                                                    โ”‚
                                                                    โ””โ”€โ†’ Newsletter Parse โ”€โ”€โ†’ Radicale CalDAV + Brain
                                                                              โ”‚
                                                                              โ–ผ
                                                                    Conflict Detection
                                                                              โ”‚
                                                                              โ–ผ
                                                                    LLM Resolution (split / reassign / reschedule)
                                                                              โ”‚
                                                                              โ–ผ
                                                                    Hermes Telegram Alert with Inline Buttons
Group Chat โ”€โ”€โ†’ Intent Router โ”€โ”ฌโ”€โ†’ Calendar Mutation (move/cancel/add/rename/remind)
                              โ”œโ”€โ†’ Question โ†’ Hybrid Query (Calendar + Brain RAG)
                              โ””โ”€โ†’ Chatter โ†’ Silence
Location Resolution: Cache โ†’ Fuzzy Match โ†’ goplaces โ†’ Nominatim โ†’ Haversine

Key design decisions:
- Sovereign โ€” no Google dependency. Radicale CalDAV on your hardware, Cloudflare for TLS termination and email routing, local Ollama for LLM inference.
- Push architecture โ€” Cloudflare Email Worker webhook replaces IMAP polling. Instant delivery, no bot-like behavior.
- Prompt-as-Code โ€” prompts live in text files, not Python. Update extraction logic without touching code.
- Hybrid retrieval โ€” Radicale calendar is the source of truth for time/date/location; ChromaDB provides detail context (dress codes, what to bring, signup links).
- Intent guardrail โ€” three-way classification prevents hallucinated RAG queries on chatter ("okay", "thanks").
- Cache-first location resolution โ€” every location string passes through a 5-tier resolver (exact cache โ†’ fuzzy match โ†’ goplaces โ†’ Nominatim โ†’ haversine estimate) to prevent LLM hallucinations.
- Day-of-week mismatch detection โ€” LLM extracts the claimed day, pipeline validates against the parsed date, warns if they disagree.
- Self-hosted calendar โ€” Radicale CalDAV with bcrypt auth, Cloudflare Tunnel for iOS, flat-file storage. Zero API limits, zero ban risk.

Features

Email Pipeline

  • Webhook delivery โ€” Cloudflare Email Worker posts raw RFC 5322 email to FastAPI webhook. No polling, no IMAP, instant delivery.
  • Appointment extraction โ€” LLM parses any email format into structured events
  • Newsletter extraction โ€” multi-type (events, reminders, actions, info) with dedup
  • Recurring events โ€” weekly, biweekly, monthly (e.g. "2nd Wednesday"), with count or end date
  • Shadow filter โ€” rejected items downgraded, not deleted; restore from digest
  • Auto-ingestion โ€” all parsed emails/newsletters stored in ChromaDB for RAG retrieval
  • HTML-to-text โ€” stdlib-only HTML conversion, no external dependencies
  • Day-of-week mismatch โ€” warns when email says "Monday" but the parsed date is Tuesday

Calendar Management

  • Radicale CalDAV sync โ€” create, update, cancel, dedup against existing events via caldav+icalendar
  • iCalendar RRULE support โ€” full recurring event generation
  • Conflict detection โ€” finds overlapping events with 5-min grace period
  • LLM conflict resolution โ€” split, reassign, or reschedule with priority rules
  • Interactive Telegram buttons โ€” resolve conflicts with a tap
  • Shared family calendar โ€” both phones sync via single CalDAV account

Hermes Notification System

  • Direct Telegram Bot API โ€” reliable delivery, no CLI timeout issues
  • Per-event notifications โ€” single message per calendar event with location + travel time
  • Day-of-week warnings โ€” โš ๏ธ when email day claim doesn't match parsed date
  • Inline action buttons โ€” conflict resolution, slot selection, task completion

Conversational Retrieval (Family Brain)

  • Hybrid query โ€” synthesizes Calendar (temporal truth) + ChromaDB (detail context)
  • Semantic search โ€” nomic-embed-text embeddings via local Ollama
  • Retrieval-only โ€” answers questions, never offers actions it can't execute
  • 30-day backfill โ€” seed the brain from existing processed emails

Location Intelligence

  • 5-tier resolution stack โ€” exact cache โ†’ fuzzy prefix match โ†’ goplaces/Google Places โ†’ Nominatim/OSM โ†’ haversine estimate
  • Home-biased coordinates โ€” locations resolve with travel time from home
  • Fuzzy matching โ€” "Golrusk" โ†’ "Golrusk Pet Care Center" (min 4 chars)
  • Nominatim fallback โ€” free OpenStreetMap geocoder, no API key needed
  • Travel time estimation โ€” straight-line distance ร— 1.4 road factor รท 35 mph

Chat-Driven Intent Engine

  • Wake words โ€” "Socrates", "calendar", "schedule" (configurable)
  • Reply-to context โ€” replying to a bot message carries event context
  • Calendar mutations โ€” move, cancel, add, rename, reject, remind
  • Rejection engine โ€” "we don't need that mass, ignore it forever" โ†’ persistent rule
  • Maintenance sentinel โ€” recurring household task tracking with Done buttons
  • Slot handler โ€” follow signup links, extract available time slots, tap to add reminder

Reliability

  • Global error handler โ€” pipeline crashes push stack traces to admin DM, no silent deaths
  • Idempotent ingestion โ€” ChromaDB uses upsert, Calendar uses dedup
  • TTL cleanup โ€” 12-month auto-purge for school year context expiration
  • Sovereign backup โ€” daily tar of Radicale + ChromaDB + config โ†’ local + remote (Gaming PC via Tailscale SSH)
  • Dedup โ€” in-memory 24h TTL on webhook, message-id dedup on email pipeline

Quick Start

Prerequisites

  • Python 3.10+
  • Ollama running locally (or on another machine on your network)
  • Radicale CalDAV server (or any CalDAV server)
  • Cloudflare account (for Email Worker + Tunnel)
  • Telegram bot token (for notifications and interactive features)

Installation

git clone https://github.com/NightKnight64/family-assistant.git
cd family-assistant
pip install -e .

Setup

# Interactive setup wizard
python -m family_assistant setup

# Or non-interactive (creates template files)
python -m family_assistant setup --non-interactive

This creates:
- .env โ€” API credentials template
- family.yaml โ€” your family members, roles, nicknames

Configure Your Family

Edit family.yaml:

family:
  members:
    - name: Alex
      role: dad
      pronouns: he/him
    - name: Jordan
      role: mom
      pronouns: she/her
    - name: Riley
      role: son
      pronouns: he/him
      nicknames: [Rye]        # LLM normalizes these
      needs_adult: true       # Requires adult to transport
    - name: Morgan
      role: daughter
      pronouns: she/her
      needs_adult: true
    - name: Buddy
      role: dog
      needs_adult: true

location:
  home:
    address: "123 Main St, Anytown, ST 12345"
    lat: 44.5054
    lng: -87.9557

Set Up Radicale CalDAV

pip install radicale

See WEBHOOK_DEPLOY.md for full Radicale + Cloudflare + systemd setup instructions.

Key config:
- Bind to 0.0.0.0:5232 (or 127.0.0.1 if behind Cloudflare Tunnel)
- Use bcrypt htpasswd ($2b$ prefix)
- Permissive rights for private home network

Set Up Email Pipeline

  1. Cloudflare Email Worker โ€” deployed as hoffdesk-email, code at scripts/email_worker.js (v3)
  2. Email Routing โ€” configure assistant@yourdomain.com โ†’ Worker
  3. Webhook server โ€” install hoffdesk-webhook.service systemd unit
  4. Cloudflare Tunnel โ€” map hook.yourdomain.com โ†’ localhost:5000

See WEBHOOK_DEPLOY.md for step-by-step instructions.

Set Up LLM

By default, Family Assistant uses a local Ollama instance:

# Install models
ollama pull qwen2.5-coder:7b      # Extraction + synthesis
ollama pull nomic-embed-text       # Embeddings
ollama pull qwen3-vl:8b            # Vision/OCR (optional, for Document Sorter)

Point to a remote Ollama instance in .env:

LLM_URL=http://your-server:11434/v1/chat/completions
LLM_MODEL=qwen2.5-coder:7b
OLLAMA_EMBED_URL=http://your-server:11434/api/embeddings
VISION_LLM_URL=http://your-server:11434/api/chat  # Optional, for Document Sorter

Set Up Telegram Bot

  1. Create a bot via @BotFather
  2. Get the bot token and your chat IDs
  3. Add to .env: TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, TELEGRAM_DEV_ID

Usage

Email Pipeline

# Process emails from webhook (called by FastAPI webhook server)
python -m family_assistant process           # process unread emails โ†’ parse โ†’ calendar + brain

# Legacy: IMAP-based processing (if not using webhook)
python -m family_assistant process --dry-run  # preview without creating events
python -m family_assistant process --no-notify # skip Telegram notifications

# Show upcoming calendar events
python -m family_assistant upcoming --hours 168

# Detect scheduling conflicts
python -m family_assistant conflicts --hours 168

# Generate resolution options for conflicts
python -m family_assistant resolve --hours 168

Conflict Resolution

  1. Detect โ€” finds events with overlapping time windows (5-min grace period)
  2. Resolve โ€” LLM generates 2-3 options based on family priorities:
    - Split โ€” both events happen, parents divide responsibilities
    - Reassign โ€” same event, different adult takes over
    - Reschedule โ€” removes lower-priority event + creates a rebook reminder
  3. Handle โ€” you pick an option, it executes the calendar changes

Priority rules (built into the resolution prompt):
- Children's medical/dental > pet appointments > adult personal
- Kids need adults to transport; dogs need adults to transport
- Suggest splitting before rescheduling when possible

Interactive Conflict Alerts

# Scan for conflicts + send Telegram alert with inline buttons
python -m family_assistant conflict-notify

Intent Engine (Chat-Driven Calendar)

# Direct intent parse + execute
python -m family_assistant intent --message "move OT to 4pm tomorrow"

# Routed from Telegram group reply
python -m family_assistant chat-intent --message "cancel the vet" --reply --quoted "Maggie grooming on Friday"

# Inbound hook (OpenClaw message bridge)
python -m family_assistant inbound-hook --message "Socrates, add soccer practice Tuesdays at 4"

# Reject an event type
python -m family_assistant reject "First Communion Mass" "we don't need this"
python -m family_assistant rejections  # list active rejection rules

Family Brain (RAG Queries)

# Ask a question about past emails/newsletters
python -m family_assistant brain-query --question "what do the kids need for the field trip?"

# Check Brain stats
python -m family_assistant brain-stats

# Backfill Brain from the last 30 days of email
python -m family_assistant brain-backfill --days 30

# Purge documents older than 12 months
python -m family_assistant brain-purge

Location Cache

# Resolve a location (cache โ†’ goplaces โ†’ Nominatim)
python -m family_assistant location --query "Golrusk Pet Care Center"

# Show cached locations
python -m family_assistant location-stats

Maintenance Sentinel

# Check recurring task status + brief
python -m family_assistant maintenance

# Mark a task complete
python -m family_assistant maint-done --event-summary "Change water filter"
# Fetch signup slots from a URL
python -m family_assistant click --url "https://signupgenius.com/..." --context "field trip chaperone"

# Handle slot button tap
python -m family_assistant slot-callback --callback-data 'slot|abc123|2'

Backup

# Manual backup (runs daily at 7 AM via cron)
./backup_hoffdesk.sh

# Skip remote copy (Gaming PC offline)
SKIP_REMOTE=true ./backup_hoffdesk.sh

Project Structure

family_assistant/
โ”œโ”€โ”€ config.py              โ€” All config from env vars + family.yaml + .env auto-load
โ”œโ”€โ”€ email_fetcher.py       โ€” IMAP fetch, decode, mark-read (legacy, pre-webhook)
โ”œโ”€โ”€ email_webhook.py       โ€” FastAPI webhook server for Cloudflare Email Worker
โ”œโ”€โ”€ appointment_parser.py  โ€” LLM extraction + JSON normalization + recurrence
โ”œโ”€โ”€ newsletter_parser.py   โ€” Multi-type extraction + Phase 1+2 LLM dedup + recurrence
โ”œโ”€โ”€ calendar_sync.py       โ€” Radicale CalDAV CRUD, dedup, event matching, recurring events
โ”œโ”€โ”€ rrule_builder.py       โ€” RRULE construction from LLM recurrence dicts
โ”œโ”€โ”€ conflict_engine.py     โ€” Detection + resolution + response handler
โ”œโ”€โ”€ conflict_notify.py     โ€” Telegram inline buttons for conflict resolution + rejection
โ”œโ”€โ”€ rejection_engine.py    โ€” LLM-powered rejection intent parsing + rule persistence
โ”œโ”€โ”€ family_brain.py        โ€” ChromaDB RAG: embed, ingest, query, hybrid synthesis
โ”œโ”€โ”€ location_cache.py      โ€” 5-tier location resolution (cache โ†’ fuzzy โ†’ goplaces โ†’ Nominatim โ†’ haversine)
โ”œโ”€โ”€ hermes.py              โ€” Telegram push notifications (events, conflicts, digest, alerts)
โ”œโ”€โ”€ pipeline.py            โ€” process_emails() + process_webhook_email() orchestration + error handler
โ”œโ”€โ”€ intent_engine.py       โ€” Chat intent parsing + calendar execution + hybrid question handler
โ”œโ”€โ”€ intent_router.py       โ€” Inbound gateway: wake word/reply-to-bot โ†’ intent classification
โ”œโ”€โ”€ inbound_hook.py        โ€” OpenClaw message bridge โ†’ intent router
โ”œโ”€โ”€ maintenance_sentinel.py โ€” Recurring household task tracker + alert dedup
โ”œโ”€โ”€ clicker.py             โ€” Follow URLs โ†’ extract signup slots โ†’ Telegram buttons
โ”œโ”€โ”€ slot_handler.py        โ€” Slot button tap โ†’ cache lookup โ†’ calendar reminder
โ”œโ”€โ”€ document_sorter.py     โ€” OCR (qwen3-vl) + LLM classify โ†’ Google Drive upload (WIP)
โ”œโ”€โ”€ url_fetcher.py         โ€” Jina Reader for JS-rendered page content
โ”œโ”€โ”€ cli.py                 โ€” All CLI commands
โ”œโ”€โ”€ setup.py               โ€” Setup wizard
โ”œโ”€โ”€ prompts/               โ€” Externalized prompt text files
โ”‚   โ”œโ”€โ”€ appointment_extract.txt
โ”‚   โ”œโ”€โ”€ appointment_retry.txt
โ”‚   โ”œโ”€โ”€ newsletter_extract.txt
โ”‚   โ”œโ”€โ”€ newsletter_dedup.txt
โ”‚   โ”œโ”€โ”€ chat_intent.txt
โ”‚   โ”œโ”€โ”€ conflict_resolve.txt
โ”‚   โ””โ”€โ”€ email_classify.txt
โ”œโ”€โ”€ family.yaml.example    โ€” Template for family configuration
โ””โ”€โ”€ tests/
    โ””โ”€โ”€ test_qa.py         โ€” 14-case QA suite

scripts/
โ”œโ”€โ”€ backup_hoffdesk.sh     โ€” Daily backup: Radicale + ChromaDB + secrets โ†’ local + remote
โ”œโ”€โ”€ email_worker.js        โ€” Cloudflare Email Worker (posts to webhook)
โ”œโ”€โ”€ research_agent/         โ€” Research CLI for deep-dive topics
โ”œโ”€โ”€ research_cli.py         โ€” Research agent command-line interface
โ””โ”€โ”€ systemd/                โ€” Systemd service files
    โ”œโ”€โ”€ hoffdesk-webhook.service
    โ”œโ”€โ”€ radicale.service
    โ””โ”€โ”€ family-assistant-webhook.service

Customizing Prompts

The prompts/ directory contains the LLM instructions. Edit them to:
- Add new appointment types (e.g., "school play rehearsal")
- Change output format (add fields, modify field names)
- Adjust conflict resolution priorities
- Add family-specific context (allergies, recurring schedules, etc.)
- Modify intent classification (add wake words, change chatter rules)

Family member names and nicknames are injected automatically from family.yaml.

Environment Variables

Variable Required Default Description
CALDAV_URL Yes http://127.0.0.1:5232 Radicale CalDAV server URL
CALDAV_USER Yes assistant CalDAV username
CALDAV_PASSWORD Yes โ€” CalDAV password
CALDAV_CALENDAR_NAME Yes family Calendar name
LLM_URL No http://localhost:11434/v1/chat/completions Ollama endpoint for extraction
LLM_MODEL No qwen2.5-coder:7b Model for extraction and synthesis
LLM_NEWSLETTER_MODEL No qwen2.5-coder:7b Model for newsletter extraction
LLM_NEWSLETTER_URL No Falls back to LLM_URL Endpoint for newsletter model
OLLAMA_EMBED_URL No http://localhost:11434/api/embeddings Ollama endpoint for embeddings
VISION_LLM_URL No โ€” Ollama endpoint for vision/OCR model
TELEGRAM_BOT_TOKEN Yes โ€” Telegram bot token
TELEGRAM_CHAT_ID No โ€” Family group chat ID
TELEGRAM_DEV_ID No โ€” Admin DM chat ID (error alerts)
WEBHOOK_SECRET Yes โ€” Shared secret for Cloudflare Email Worker auth
GOOGLE_PLACES_API_KEY No โ€” Google Places API key (optional, Nominatim is free fallback)
FAMILY_CONFIG_PATH No Auto-detect Path to family.yaml
INTENT_WAKE_WORDS No Socrates,calendar,schedule Wake words for intent routing
REMOTE_HOST No โ€” SSH target for backup (e.g. user@host)

Set these in a .env file in the working directory โ€” Family Assistant auto-loads it.

Deployment

See WEBHOOK_DEPLOY.md for full deployment instructions including:

  • Radicale CalDAV server setup (systemd, htpasswd, rights)
  • FastAPI webhook server (Cloudflare Tunnel, email routing)
  • Cloudflare Email Worker deployment
  • iPhone CalDAV account configuration
  • Backup script setup (local + remote via Tailscale SSH)

Permanent Model Rules

  • โœ… qwen2.5-coder:7b โ€” single model for ALL pipeline tasks (extraction, synthesis, intent)
  • โœ… qwen3-vl:8b โ€” vision/OCR only (Document Sorter), keep_alive: 0
  • โœ… nomic-embed-text โ€” embeddings only
  • โ›” No home-assist model โ€” ever
  • โ›” No deepseek-r1 models โ€” burn thinking tokens, fail JSON
  • โ›” No qwen3:8b โ€” thinking tokens can't be disabled, 3-13x slower

Roadmap

v1.0 โœ… (Complete)

  • [x] Email โ†’ Calendar pipeline with LLM extraction
  • [x] Cloudflare Email Worker webhook (push architecture)
  • [x] Radicale CalDAV migration (Google-free)
  • [x] Conflict detection + resolution (split/reassign/reschedule)
  • [x] Hermes Telegram notification agent (direct Bot API)
  • [x] Day-of-week mismatch detection
  • [x] Location intelligence (5-tier cache + Nominatim fallback + travel time)
  • [x] Interactive conflict buttons in Telegram
  • [x] Rejection engine (shadow filter + persistent rules)
  • [x] Intent engine (chat-driven calendar mutations)
  • [x] Recurring event support (weekly, biweekly, monthly patterns)
  • [x] Family Brain RAG (ChromaDB + hybrid query)
  • [x] Global error handler (admin DM alerts)
  • [x] Maintenance sentinel (recurring task tracking)
  • [x] Sovereign backup (local + remote via Tailscale SSH)
  • [x] Document Sorter (OCR + classify, WIP)

Post-v1.0 (Deferred)

  • [ ] Multi-calendar support (work vs. personal)
  • [ ] Docker Compose deployment
  • [ ] Refactor prompts to YAML for user configurability
  • [ ] Provider abstraction (Gmail โ†’ Outlook, iCloud)
  • [ ] Web UI for conflict resolution
  • [ ] Auth failure circuit breaker (3 consecutive โ†’ pause + alert)

License

MIT โ€” see LICENSE.