An intelligent, accessible, multilingual election guide powered by Google Gemini AI and Google Cloud Services.
Live Demo: https://voteguide-ai-HASH-uc.a.run.app
Election Process Assistant — Helps citizens understand voter registration, election timelines, polling locations, and every step of the democratic process through an intelligent AI-powered interface.
┌─────────────────────────────────────────────────────────┐
│ User / Browser │
└──────────────────────────┬──────────────────────────────┘
│
┌──────────────────────────▼──────────────────────────────┐
│ React 18 Frontend (Vite) │
│ Timeline · Chat · Polling Map · Checklist · TTS · i18n │
└──────────────────────────┬──────────────────────────────┘
│ REST + SSE
┌──────────────────────────▼──────────────────────────────┐
│ Python Flask Backend (Cloud Run) │
│ Rate limiting · Sanitization · CORS · Logging │
└────┬────────────┬────────────┬────────────┬─────────────┘
│ │ │ │
Gemini Translate Cloud TTS Firebase
1.5 Flash API v2 Neural2 Firestore
- 8-stage visual stepper: Registration → Nomination → Deadline → Early Voting → Election Day → Counting → Certification → New Term
- Click any stage → Gemini AI explains it in plain language (streamed live)
- 🔊 Text-to-Speech button on every stage
- ✅ "Mark as understood" checkboxes with progress tracking
- Progress bar showing percentage of stages understood
- Streaming responses via Server-Sent Events (SSE)
- Maintains conversation history (last 10 turns)
- Quick-action chips for common questions
- Copy-to-clipboard on AI responses
- Works in all 5 supported languages
- Google Maps JavaScript API integration
- Browser geolocation auto-centers map
- Searches for "polling place", "election office", "voting center" nearby
- Clickable results list with Get Directions button
- Custom styled map markers
- 10-item interactive checklist with expandable details
- Progress ring showing completion percentage
- "Ask AI" button on every item → opens chat with context
- Print/Download checklist via browser print API
- State persisted in localStorage
- 5 languages: English, Hindi (हिंदी), Tamil (தமிழ்), Spanish, French
- Google Translate API for dynamic UI translation
- Translations cached in memory to avoid repeat API calls
- TTS voice automatically matches selected language
- Gemini responds in the selected language
- Text size cycling: Normal → Large → X-Large
- High contrast mode toggle
- Pause all animations toggle
- All preferences persisted in localStorage
| Service | Purpose | Why Chosen |
|---|---|---|
| Gemini 1.5 Flash | AI chat + stage explanations | Best balance of speed and quality for conversational election Q&A |
| Google Maps JS API | Polling location finder with live map | Native map experience, Places API for nearby search |
| Cloud Text-to-Speech | Audio playback of election steps | Neural voices for accessibility; serves users who prefer audio |
| Cloud Translate API | Full UI translation to 5 languages | Reaches non-English-speaking citizens — critical for inclusive civic access |
| Firebase Firestore | Session + checklist persistence | Lightweight, real-time, free tier suitable for session storage |
- Zero hardcoded secrets — all API keys loaded from environment variables
- Input sanitization — strips HTML tags, rejects SQL injection patterns, enforces max lengths before any AI call
- Rate limiting — Flask-Limiter: 20 req/min on chat, 30 on translate, 10 on TTS
- CORS — configured via environment variable, not wildcard
- Security headers on every response:
X-Content-Type-Options,X-Frame-Options,X-XSS-Protection - Non-root Docker user — container runs as
appuser, not root - Pinned dependencies — all versions locked in requirements.txt and package.json
Full WCAG 2.1 AA compliance:
- Semantic HTML5 elements:
<header>,<nav>,<main>,<section>,<article>,<aside>,<footer> aria-labelon every interactive elementaria-live="polite"on chat message logaria-expandedon all collapsible sectionsaria-busyon all loading statesrole="progressbar"witharia-valuenow/min/maxon progress indicators- Skip-to-content link (visible on focus)
- All focus states visible (2px amber outline)
- Keyboard navigation: Tab through all interactive elements, Escape closes panels
- Color contrast ≥ 4.5:1 on all text
- Icons always paired with text labels
.sr-onlyclass for screen-reader-only textprefers-reduced-motionmedia query respected- Text-to-Speech on every election stage for audio learners
- Text size toggle: Normal / Large / X-Large
- Code splitting:
PollingLocatorlazy-loaded withReact.lazy + Suspense - TTS caching:
functools.lru_cache(maxsize=100)— repeat audio requests never hit the API - Translation caching: Results cached in Flask-Caching (SimpleCache, 1-hour TTL) + frontend memory cache
- Batch translation: All UI strings sent in a single API call, not individually
- Gemini history trim: Last 10 turns only, preventing token bloat
- Debounced inputs: Search inputs debounced 300ms
- AbortController: Pending Gemini fetch cancelled if user sends new message
- Gunicorn: 2 workers with 120s timeout optimized for Cloud Run
- Python 3.11+
- Node.js 18+
- Google Cloud project with billing enabled
- APIs enabled: Gemini API, Maps JavaScript API, Cloud Translate API, Cloud Text-to-Speech API
- Firebase project with Firestore enabled
git clone https://github.com/YOUR_USERNAME/voteguide-ai.git
cd voteguide-aicp .env.example .env
# Edit .env and fill in all your API keyspip install -r backend/requirements.txtcd frontend
npm install
cd ..# Terminal 1 — Backend
python -m gunicorn --bind 0.0.0.0:8080 backend.app:app
# Terminal 2 — Frontend
cd frontend && npm run dev# From project root
python -m pytest backend/tests/ -v --tb=short37 tests across 4 test files:
test_chat.py— Chat endpoint: valid/invalid input, sanitization, language supporttest_translate.py— Translation + TTS + Timeline endpointstest_tts.py— Sanitizer unit tests: messages, language codes, TTS text
All Google API calls are mocked with unittest.mock.patch — no credentials required to run tests.
docker build -t voteguide-ai .
docker run -p 8080:8080 \
-e GEMINI_API_KEY=your_key \
-e FLASK_SECRET_KEY=your_secret \
-e GOOGLE_CLOUD_PROJECT=your_project \
voteguide-ai# Authenticate
gcloud auth login
gcloud config set project YOUR_PROJECT_ID
# Store secrets in Secret Manager
echo -n "your_gemini_key" | gcloud secrets create GEMINI_API_KEY --data-file=-
echo -n "your_flask_secret" | gcloud secrets create FLASK_SECRET_KEY --data-file=-
echo -n "https://your-domain.com" | gcloud secrets create ALLOWED_ORIGINS --data-file=-
# Deploy via Cloud Build
gcloud builds submit --config cloudbuild.yaml| Method | Path | Description | Rate Limit |
|---|---|---|---|
GET |
/api/health |
Health check for Cloud Run | None |
GET |
/api/timeline |
All 8 election stages | 100/hr |
POST |
/api/chat |
Streaming Gemini AI chat (SSE) | 20/min |
POST |
/api/translate |
Batch text translation | 30/min |
POST |
/api/tts |
Text-to-Speech (base64 MP3) | 10/min |
POST /api/chat
{
"message": "How do I register to vote?",
"history": [],
"language": "en"
}POST /api/translate
{
"texts": ["Election Day", "Voter Registration"],
"targetLanguage": "hi"
}| Variable | Required | Description |
|---|---|---|
GEMINI_API_KEY |
✅ Backend | Google AI Studio API key |
FLASK_SECRET_KEY |
✅ Backend | Flask session secret |
GOOGLE_CLOUD_PROJECT |
✅ Backend | GCP project ID (for TTS + Translate) |
ALLOWED_ORIGINS |
✅ Backend | Comma-separated allowed CORS origins |
VITE_GOOGLE_MAPS_API_KEY |
✅ Frontend | Maps JavaScript API key |
VITE_FIREBASE_API_KEY |
✅ Frontend | Firebase project API key |
VITE_FIREBASE_PROJECT_ID |
✅ Frontend | Firebase project ID |
- Election context is general/US-centric — timelines reference typical US election cycles. The AI provides context for any country when asked.
- Firebase is client-side only — session storage uses Firebase Firestore directly from the browser with anonymous auth (no backend proxy needed for reads/writes).
- Google Cloud credentials — the Cloud Run service uses Application Default Credentials (ADC) automatically via the service account assigned to the Cloud Run instance.
- Maps Places search — "polling place" and "election office" keyword searches return relevant government buildings; results vary by location and depend on what businesses have registered with Google.
- TTS truncation — texts longer than 500 characters are silently truncated rather than rejected, since the goal is audio accessibility, not verbatim reading.
- Firebase Auth — anonymous auth → optional sign-in to sync checklist across devices
- Notification reminders — Firebase Cloud Messaging to remind users of registration deadlines
- Ballot lookup — integrate with Ballotpedia or official election APIs for candidate/measure data
- More languages — expand beyond 5 to cover major global languages
- Offline support — Service Worker for PWA capability in low-connectivity areas
- Election calendar — specific date lookup by US state or country
voteguide-ai/
├── backend/
│ ├── app.py # Flask app factory
│ ├── routes/
│ │ ├── chat.py # Gemini SSE streaming
│ │ ├── translate.py # Cloud Translate
│ │ ├── tts.py # Cloud Text-to-Speech
│ │ ├── timeline.py # Election stage data
│ │ └── health.py # Health check
│ ├── utils/
│ │ └── sanitizer.py # Input sanitization
│ ├── tests/ # 37 pytest tests
│ └── requirements.txt
├── frontend/
│ └── src/
│ ├── components/ # React components
│ ├── contexts/ # Language context
│ ├── hooks/ # useChat hook
│ └── App.jsx # Root layout
├── Dockerfile # Multi-stage build
├── cloudbuild.yaml # Cloud Run CI/CD
├── .env.example # Environment template
└── README.md
Built with ❤️ to empower every citizen to participate in democracy.