4.8 KiB
4.8 KiB
Voting App
U.S. Congress legislator voting history viewer with AI-generated bill summaries.
Features
- Search legislators by name with autocomplete suggestions (full name, state, party)
- View voting records with endless scroll through a legislator's entire career
- AI-generated summaries using a local GPT-OSS model for impartial bill analysis
- Local caching with SQLite to avoid repeated API calls and survive outages
- Rate-limit aware with exponential back-off (stays well under 1000 req/hr limit)
- Privacy-safe error handling that never exposes API keys
Architecture
User → React Frontend → Flask Backend → LegiScan API
↕
SQLite Cache
↕
Local GPT-OSS
Setup
Prerequisites
- Docker and Docker Compose
- LegiScan API key (sign up at https://legiscan.com)
- Local GPT-OSS server running (e.g., LM Studio, Ollama, vLLM)
Environment Configuration
Copy .env.example to .env and fill in your values:
cp .env.example .env
Required variables:
| Variable | Description | Default |
|---|---|---|
CONGRESS_API_KEY |
LegiScan API key | (required) |
CONGRESS_API_BASE_URL |
LegiScan API base URL | https://api.legiscan.com |
GPT_OSS_BASE_URL |
Local GPT-OSS endpoint | http://home.ms:4000/v1 |
GPT_OSS_API_KEY |
GPT-OSS API key (if required) | (optional) |
BACKEND_PORT |
Host port for backend | 8001 |
VITE_FRONTEND_PORT |
Host port for frontend | 5174 |
VITE_API_URL |
Backend URL for frontend | http://localhost:8001 |
Ports 8001 and 5174 are used to avoid conflicts with other services. Change them in .env as needed.
Running
Docker (recommended)
# Start all services (backend + frontend)
cd docker
docker compose up -d --build
# View logs
docker compose logs -f
# Rebuild a single service
docker compose down <service>
docker compose up -d <service> --build
# Stop all services
docker compose down
Once running:
- Frontend:
http://localhost:5174 - Backend API:
http://localhost:8001
Development (without Docker)
Backend:
uv sync
uv run python -m src.main --port 8001
Frontend:
cd frontend
pnpm install
VITE_API_URL=http://localhost:8001 pnpm dev
Project Structure
voting-app/
├── .env # Environment variables (not committed)
├── .env.example # Template for required variables
├── pyproject.toml # Python dependencies (managed by UV)
├── uv.lock # Locked Python dependencies
├── src/ # Backend Python code
│ ├── models.py # Data models (Legislator, Bill, Vote, Summary)
│ ├── api_client.py # LegiScan API client (pagination, back-off)
│ ├── gpt_oss_client.py # GPT-OSS client for impartial bill summaries
│ ├── cache.py # SQLite cache layer
│ ├── app.py # Flask REST API
│ └── main.py # CLI entry point
├── frontend/ # React TypeScript frontend
│ ├── package.json # Frontend dependencies (managed by pnpm)
│ ├── pnpm-lock.yaml # Locked frontend dependencies
│ ├── vite.config.ts # Vite build configuration
│ ├── src/
│ │ ├── App.tsx # Main app with routing
│ │ ├── components/ # UI components (Button, Input, Spinner, VoteCard, etc.)
│ │ ├── pages/ # Page components (Home, LegislatorDetail)
│ │ ├── store/ # Zustand state management
│ │ ├── services/ # API client for backend
│ │ ├── hooks/ # Custom React hooks
│ │ └── types/ # TypeScript definitions
├── tests/ # Test suites
│ ├── unit/ # Python unit tests
│ ├── integration/ # Integration tests
│ └── e2e/ # End-to-end tests
├── docker/ # Docker configuration
│ ├── docker-compose.yml # Service orchestration
│ ├── Dockerfile.backend # Python container
│ └── Dockerfile.frontend # Node.js container
└── data/ # Runtime data (SQLite DB, not committed)
API Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /health |
Health check |
| GET | /api/search?q=<query> |
Search legislators |
| GET | /api/legislators/<id> |
Legislator details |
| GET | /api/legislators/<id>/votes?limit=N&offset=M |
Voting record with summaries |
| GET | /api/bills/<id>/summary |
AI-generated bill summary |
| GET | /api/bills/<id>/text |
Full bill text |
Testing
# Run Python tests
uv run pytest tests/
# Run frontend linting
cd frontend && pnpm lint
License
MIT