An SMS-driven platform for the gig economy
Workers text in an earnings goal (e.g. "I need $1200 by Thursday of next week."). Job posters text in a job listing with the relevant details (desc, pay, duration, expected done date). GPT classifies each inbound message and extracts structured data from both. Matching algorithm surfaces the gig(s) on-demand that the worker can then do to achieve their earnings goal in the shortest amount of time possible.
See here for the current roadmap, and progress.
- FastAPI async webhook API with
/webhook/sms,/health,/readyz, and/metricsendpoints - Twilio inbound SMS ingestion with signature validation, idempotency, and per-sender rate limiting
- OpenAI GPT extraction pipeline for parsing unstructured SMS into job postings and worker goals
- Pinecone vector index for semantic job/worker matching (async SDK)
- Temporal workflow orchestration with a cron-scheduled Pinecone sync worker
- PostgreSQL persistence via SQLAlchemy / SQLModel with Alembic migrations
- OpenTelemetry tracing (OTLP → Jaeger) and Prometheus metrics, with pre-built Grafana dashboards
- Structured logging via structlog with automatic OTel trace correlation
- Pulumi-on-GKE infrastructure-as-code under
infra/for production deployments
| Layer | Technology |
|---|---|
| Runtime | Python 3.12+ |
| Package manager | uv |
| Web framework | FastAPI + Uvicorn |
| ORM / migrations | SQLModel / SQLAlchemy (async) + Alembic |
| Database | PostgreSQL 16 (asyncpg driver) |
| Workflows | Temporal 1.26 |
| Vector search | Pinecone (async SDK) |
| LLM | OpenAI |
| SMS gateway | Twilio |
| Observability | OpenTelemetry, Jaeger 2.16, Prometheus 3.1, Grafana 11, Braintrust |
| Infra | Pulumi on Google Kubernetes Engine |
- Python 3.12 or newer
- uv (installs and manages the virtualenv and lockfile)
- Docker and Docker Compose (for the local full-stack environment)
- Credentials for: Twilio, OpenAI, Pinecone (see
docs/CONFIGURATION.mdfor the complete list)
Clone the repository and install dependencies with uv:
git clone <repository-url> vici
cd vici
uv syncThis creates a .venv/ and installs the runtime and dev dependencies pinned in uv.lock.
The fastest path to a working local stack is Docker Compose, which brings up PostgreSQL, Temporal, Jaeger, Prometheus, Grafana, and the FastAPI app together.
-
Create the required env files (
.env.app,.env.postgres,.env.temporal,.env.temporal-ui,.env.jaeger-query,.env.grafana,.env.opensearch). Seedocs/GETTING-STARTED.mdanddocs/CONFIGURATION.mdfor variables. -
Start the stack:
docker compose up --build
-
The app container automatically runs
alembic upgrade headbefore launching Uvicorn. Once healthy, endpoints are available at:URL Purpose http://localhost:8000/health Liveness probe http://localhost:8000/readyz Readiness probe (DB connectivity) http://localhost:8000/metrics Prometheus metrics http://localhost:8000/webhook/sms Twilio inbound SMS webhook http://localhost:8080 Temporal Web UI http://localhost:16686 Jaeger UI http://localhost:9090 Prometheus http://localhost:3000 Grafana -
Configure a Twilio webhook pointing at
${WEBHOOK_BASE_URL}/webhook/smsto start ingesting SMS.
To run the API outside Docker against an existing Postgres and Temporal:
uv run alembic upgrade head
uv run uvicorn src.main:app --reloadvici/
├── src/
│ ├── main.py # FastAPI app factory + lifespan (OTel, Temporal worker, gauges)
│ ├── config.py # Pydantic Settings (flat env vars remapped into sub-models)
│ ├── database.py # Async SQLAlchemy engine + session factory
│ ├── sms/ # Twilio webhook: router, service, rate-limit, audit
│ ├── extraction/ # OpenAI GPT extraction + Pinecone embedding writes
│ ├── pipeline/ # Orchestrator + handlers (job posting, worker goal, unknown)
│ ├── jobs/ # Job posting domain (models, repository)
│ ├── work_goals/ # Worker goal domain (models, repository)
│ ├── matches/ # Semantic match results
│ ├── users/ # User domain
│ ├── temporal/ # Temporal client, worker, cron schedules
│ ├── metrics.py # Prometheus gauges/counters
│ └── models.py # Shared SQLModel base classes
├── migrations/ # Alembic revision scripts
├── tests/ # Pytest (async mode) suite
├── infra/ # Pulumi-on-GKE stack (Python)
├── docs/ # ARCHITECTURE, CONFIGURATION, DEPLOYMENT, DEVELOPMENT, GETTING-STARTED, TESTING
├── grafana/ # Dashboard + datasource provisioning
├── jaeger/ # Jaeger collector / query configs
├── prometheus/ # Prometheus scrape config
├── docker-compose.yml # Local dev stack
├── Dockerfile # Multi-stage production image (uv + python:3.12-slim)
├── alembic.ini
└── pyproject.toml
uv run pytestpytest-asyncio is configured in auto mode (see pyproject.toml), so async tests require no explicit marker. Coverage is available via pytest-cov.
See docs/TESTING.md for detailed testing guidance.
Ruff is the single source of truth for lint and format:
uv run ruff check --fix src tests
uv run ruff format src testsConfiguration lives in pyproject.toml under [tool.ruff] (target py312, rules E, F, I).
| Document | Description |
|---|---|
| docs/ARCHITECTURE.md | System architecture and component overview |
| docs/GETTING-STARTED.md | First-run walkthrough |
| docs/DEVELOPMENT.md | Local development workflow |
| docs/CONFIGURATION.md | Environment variables and settings |
| docs/TESTING.md | Test framework and conventions |
| docs/DEPLOYMENT.md | Deployment and infrastructure |
| AGENTS.md | FastAPI conventions for contributors |
| CONTRIBUTING.md | Contribution guidelines |
Contributions are welcome. See CONTRIBUTING.md for guidelines, and AGENTS.md for the FastAPI coding conventions this project follows (async route rules, domain-oriented layout, Pydantic settings-per-domain, etc.).
Vici is licensed under the GNU General Public License v3.0. See LICENSE for the full text.