Skip to content

Repository files navigation

Beacon

Autonomous AI briefing agent in Go — reads the research so you don't have to.

Beacon is a self-hosted agent that monitors academic paper sources and tech blogs, detects your current energy level via Spotify, and delivers mood-aware briefings to your inbox twice a day at 9 am and 9 pm.

If you're at the gym when it fires, you get crisp 5–8 line summaries. If you're having a calm morning, you get full structured analyses with TL;DR previews.

Built as a portfolio project for AI/ML graduate school applications.


How it works

┌──────────────┐    ┌──────────────┐    ┌──────────────────┐    ┌─────────┐
│ Paper sources│───▶│ Mood detector│───▶│ Claude API       │───▶│  Email  │
│ (ArXiv, HF,  │    │ (Spotify     │    │ (generates       │    │ (Resend)│
│  blogs, etc) │    │  genres)     │    │  summaries)      │    │         │
└──────────────┘    └──────────────┘    └──────────────────┘    └─────────┘

9 am run:

  • HIGH_BPM (gym, rap, metal, reggaeton…) → short 5–8 line summaries
  • NORMAL (lo-fi, jazz, classical, nothing playing…) → full structured analysis

9 pm run: same logic applied to a fresh batch of papers

Mood classification is non-fatal — if Spotify is unreachable or nothing is playing, it defaults to NORMAL.


Paper sources

Topic Sources
AI / ML ArXiv cs.AI, cs.LG, cs.CL · HuggingFace Papers
Healthcare AI ArXiv cs.AI + q-bio · Semantic Scholar
Brain-Computer Interfaces ArXiv eess.SP · bioRxiv
Computer Vision ArXiv cs.CV
Bioengineering bioRxiv · medRxiv
Anthropic / Claude ArXiv cs.AI · HuggingFace Papers
Blogs Simon Willison · Towards Data Science · Radical Data Science

Requirements

  • Go 1.23+
  • Spotify account with a registered app (free tier works)
  • Anthropic API keyconsole.anthropic.com
  • Resend account (free tier: 100 emails/day) — resend.com
  • Optional: Last.fm API key for genre fallback when Spotify returns empty genres

Setup

1. Clone and build

git clone https://github.com/joako/beacon
cd beacon
go build -o bin/beacon ./cmd/beacon

2. Configure environment

cp .env.example .env

Fill in .env with your credentials (see Environment variables below).

3. Get a Spotify refresh token

Beacon needs a long-lived refresh token to query Spotify without user interaction. Run the OAuth helper once:

go run scripts/spotify-auth.go

Follow the browser prompt, then paste the resulting SPOTIFY_REFRESH_TOKEN into .env.

4. Test each component

# Verify Spotify connection and mood detection
go run cmd/beacon/main.go --cmd=mood

# Fetch papers from a topic (no Claude, no email)
go run cmd/beacon/main.go --cmd=fetch --topic=AI
go run cmd/beacon/main.go --cmd=fetch --topic=BLOGS

# Full dry-run: fetch papers + Claude analysis, print to terminal, no email sent
# Set MOCK_MOOD to test both briefing formats without needing Spotify
MOCK_MOOD=NORMAL  go run cmd/beacon/main.go --cmd=dry-run --hour=9  --papers=3
MOCK_MOOD=HIGH_BPM go run cmd/beacon/main.go --cmd=dry-run --hour=21 --papers=3

5. Run the scheduler

# Start the production cron loop (blocks, fires at 9 am and 9 pm local time)
go run cmd/beacon/main.go --cmd=run

Or build and run the binary:

go build -o bin/beacon ./cmd/beacon
./bin/beacon --cmd=run

Environment variables

Variable Required Description
SPOTIFY_CLIENT_ID OAuth app client ID from Spotify Dashboard
SPOTIFY_CLIENT_SECRET OAuth app client secret
SPOTIFY_REFRESH_TOKEN Long-lived token (generated by scripts/spotify-auth.go)
ANTHROPIC_API_KEY API key from console.anthropic.com
RESEND_API_KEY ✅* Email delivery via Resend
SENDGRID_API_KEY ✅* Email delivery via SendGrid (alternative to Resend)
DELIVERY_EMAIL_TO Recipient email address
LASTFM_API_KEY Genre fallback — free key at last.fm/api
TIMEZONE IANA timezone for cron (default: America/Merida)
LOG_LEVEL debug / info / warn / error (default: info)
DRY_RUN true to print briefings without sending (default: false)
MOCK_MOOD HIGH_BPM or NORMAL — overrides Spotify for testing

*Either RESEND_API_KEY or SENDGRID_API_KEY is required, not both.


CLI reference

beacon --cmd=<command> [flags]

Commands:
  run        Start the production scheduler (default)
             Fires at 09:00 and 21:00 in TIMEZONE

  dry-run    Run one pipeline cycle without sending email
  --hour     Hour to simulate: 9 or 21 (default 9)
  --papers   Max papers to process (default 3, saves API credits)

  mood       Print current Spotify track and detected MoodLevel

  fetch      Fetch and print papers from one topic
  --topic    AI | HEALTHCARE | BCI | CV | BIO | ANTHROPIC | BLOGS
  --limit    Max papers to return (default: unlimited)
  --json     Output as JSON array

Examples:

# Simulate a HIGH_BPM morning without real Spotify
MOCK_MOOD=HIGH_BPM go run cmd/beacon/main.go --cmd=dry-run --hour=9 --papers=5

# Fetch and inspect raw paper data as JSON
go run cmd/beacon/main.go --cmd=fetch --topic=BLOGS --json

# Check what mood Beacon currently detects
go run cmd/beacon/main.go --cmd=mood

Briefing formats

HIGH_BPM — concise, scannable while you're on the move:

📄 UniGRPO: Unified Policy Optimization for Visual Generation
🏷️  Wang et al. · arxiv · 2025-03-28
⚡ Introduces a unified GRPO variant for text-to-image models that stabilizes
   training across reward types. Achieves +4.1 FID improvement over baselines
   on COCO while reducing training steps by 30%. Key insight: separating the
   policy update for visual vs. language tokens prevents reward hacking.

🔗 https://arxiv.org/abs/2503.xxxxx
---

NORMAL — structured analysis with full context:

📄 UniGRPO: Unified Policy Optimization for Visual Generation
🏷️  Wang et al. · arxiv · 2025-03-28

💡 TL;DR: Extends GRPO to visual generation, fixing training instability
   with a novel token-separated policy update.

**Motivation:** Existing RL-based fine-tuning for image models suffers from
reward hacking when applying GRPO naively across visual and language tokens...

**Method:** ...
**Results:** ...
**Implications:** ...
**Caveats:** ...

🔗 https://arxiv.org/abs/2503.xxxxx
---

Mood classification

Mood is determined by the genres of your currently playing Spotify track.

HIGH_BPM genres (triggers summary mode): gym · workout · rap · hip-hop · trap · drill · corridos · corridos tumbados · sierreño · banda · reggaeton · dembow · latin trap · hard rock · heavy metal · drum and bass · hardstyle · gabber

Chill modifiers (override high-energy classification even if genre matches): lo-fi · lofi · chill · ambient · jazz · soul · cloud · sleep · study · house

So lo-fi hip-hopNORMAL, jazz rapNORMAL, but plain rapHIGH_BPM.

Defaults:

  • Nothing playing → NORMAL
  • Spotify unreachable → NORMAL (non-fatal)

Project structure

beacon/
├── cmd/beacon/main.go          # CLI entry point
├── internal/
│   ├── config/                 # Env loading and validation
│   ├── mood/                   # Spotify client + genre classifier
│   ├── papers/                 # Multi-source paper fetcher
│   │   ├── arxiv.go
│   │   ├── huggingface.go
│   │   ├── semantic_scholar.go
│   │   ├── biorxiv.go
│   │   └── rss.go              # Generic RSS/Atom fetcher (blogs)
│   ├── briefing/               # Claude API generator + formatter
│   ├── delivery/               # Email sender (Resend)
│   ├── store/                  # In-memory paper store
│   └── scheduler/              # 9am/9pm cron state machine
└── .env.example                # Environment variable template

Running tests

go test ./...

# With verbose output
go test ./... -v

# A specific package
go test ./internal/papers/... -v -run TestRSS

Coverage across internal/ packages is maintained above 85%.


Security notes

  • All secrets are loaded from environment variables — nothing hardcoded.
  • HTTP clients enforce HTTPS-only URLs at construction time (panics on HTTP).
  • All HTTP response bodies are capped with io.LimitReader.
  • DRY_RUN=true is enforced in all delivery paths before any network call.
  • No secrets appear in log output — only boolean flags (e.g. api_key_set=true).

Stack

Component Technology
Language Go 1.23
AI / summarization Claude API (claude-sonnet-4-20250514)
Mood detection Spotify Web API
Paper sources ArXiv · HuggingFace · Semantic Scholar · bioRxiv/medRxiv · RSS
Email delivery Resend
Logging log/slog (structured)
External deps godotenv only

License

MIT

About

Autonomous research briefing agent — fetches ArXiv papers and adapts delivery format to your Spotify mood. Built in Go.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages