Python agent that ingests course schedules, syncs them to Google Calendar, and now scans Gmail for new events using a LavaPayments-backed LLM.
- Install dependencies
python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt - Create OAuth credentials
- In the Google Cloud Console, create desktop-app OAuth credentials.
- Save the JSON as
credentials.jsonin the repo root.
- Run the CSV agent
python -m agentic_calendar.cli --calendar CalendarID
- By default the CLI ingests
schedule.csv; override with--csv /path/to/file.csv. - If the CSV omits a year (e.g.,
Oct 05), add--csv-year 2025so dates are interpreted correctly. - Use
--default-start HH:MMand--duration minutesfor files without explicit times. - Add
--console-oauthto use the copy/paste OAuth flow.
- By default the CLI ingests
- Use
--replace-eventsto delete matching events before inserting new ones.
The React dashboard (src/App.js) now talks to a FastAPI backend that wraps the calendar agent, the scraper, and the Gmail ingest loop.
-
Start the API
# Same virtualenv as above export GOOGLE_CALENDAR_ID="primary" # or a specific calendar id export CALENDAR_AGENT_DRY_RUN=false # set true to preview without writing export ENABLE_GMAIL_POLLING=true # polls Gmail every 5 minutes export LAVAPAY_FORWARD_TOKEN='{"token":"..."}' uvicorn backend.server:app --reload
Useful overrides:
SCHEDULE_CSV– path to the working CSV (defaults toschedule.csv).CALENDAR_TIMEZONE,DEFAULT_START_TIME,DEFAULT_DURATION_MINUTES,SCHEDULE_FALLBACK_YEAR– parser hints.FRONTEND_ORIGINS– comma-separated list of allowed browser origins (defaulthttp://localhost:3000).COURSE_IMPORT_REPLACE=true– delete+recreate matching events during course imports.GMAIL_*variables mirror the CLI flags (GMAIL_QUERY,GMAIL_TOKEN, etc.).
-
Run the frontend
npm install npm start
Set
REACT_APP_API_BASE_URLif the backend is not onhttp://localhost:8000.
- Natural-language requests (“add a study session tomorrow at 6pm”, “move HW4 to Friday”) are forwarded to the LLM agent that previously lived in the CLI.
- Pasting a course/syllabus URL and mentioning “course”, “syllabus”, etc. triggers the scraper, rewrites
schedule.csv, reloads the events, and (when not in dry-run mode) pushes them to Google Calendar. - Gmail ingestion runs automatically every five minutes whenever
ENABLE_GMAIL_POLLING=trueandCALENDAR_AGENT_DRY_RUN=false; the background task feeds new email-derived events into the same agent memory so you can reference them in chat.
The agentic_calendar.gmail_ingest_cli entry point authorizes Gmail (read-only) and Calendar (write) and sends each unread email to the LLM so commitments (deadlines, hackathon schedules, etc.) are added automatically.
python -m agentic_calendar.gmail_ingest_cli \
--calendar primary \
--gmail-credentials credentials.json \
--gmail-token gmail_token.json \
--query "label:unread newer_than:1d" \
--forward-token "$LAVAPAY_FORWARD_TOKEN" \
--applyWorkflow:
- Gmail OAuth tokens are stored separately (
gmail_token.json) so you can grant read-only mail access alongside Calendar writes. - The CLI searches Gmail with the provided query (default
label:unread newer_than:1d), skips messages already logged in.gmail_processed.json, and feeds new ones into the extractor. - Preview results without
--applyor add the flag to create the events immediately.
Environment variables:
LAVAPAY_FORWARD_TOKEN/LAVA_FORWARD_TOKEN– LavaPayments forward token (or pass via--forward-token).LAVAPAY_BASE_URL,LAVAPAY_TARGET_URL,LAVAPAY_PROVIDER,LAVAPAY_MODEL– optional overrides for the Lava gateway/provider.
Two layouts are supported:
- Normalized headers –
title,date,start_time,end_time,category,location,description. These map directly to event fields. - Scraper feed (default) –
Date,Type,Descriptionas produced byschedule.csv. Titles/descriptions are inferred, type maps to categories, and missing times fall back to per-category defaults.
Date is required. Optional columns include duration, location, and explicit titles. When dates omit a year, provide --csv-year so parsing succeeds.