Getting Started
Get the API, a Postgres database and sample textbook data running on your machine. Budget about twenty minutes, most of it waiting on the first Docker build.
| Requirement | Notes |
|---|---|
| Python 3.12 | Pinned in .python-version. Only needed on the host if you run outside Docker. |
| uv | The package manager. No version is pinned. |
| Docker | Runs Postgres 17 with pgvector, and the API itself. |
| An LLM API key | Together AI or OpenAI. You can skip this entirely by running models locally with Ollama. |
You do not need a Meta or WhatsApp Business account to develop. Everything below works without one.
git clone https://github.com/Tanzania-AI-Community/twiga.git
cd twiga
uv sync
source .venv/bin/activateOn Windows the last command is .venv\\Scripts\\activate.cp .env.template.simple .envUse .env.template.simple for local work. It ships placeholder Meta credentials that exist purely to satisfy startup checks. Use the longer .env.template only when you need WhatsApp Flows, Redis or LangSmith.db:LLM_API_KEY=your_key_here
LLM_PROVIDER=together
EMBEDDING_API_KEY=your_key_here
EMBEDDING_PROVIDER=together
DATABASE_USER=postgres
DATABASE_PASSWORD=your_password
DATABASE_NAME=twiga_db
DATABASE_URL=postgresql+asyncpg://postgres:your_password@db:5432/twiga_dbThe same key works for both LLM_API_KEY and EMBEDDING_API_KEY if one provider serves both.Two ways the .env file will bite you
Delete every comment and blank value. The Makefile starts with include .env, so the section headers in the template are not valid make syntax and every make target will fail. Separately, a variable that is present but empty is read as an empty string, not as missing, so LLM_PROVIDER= crashes at startup instead of falling back to a default. Delete the line to get the default.
Use db as the database host, not 127.0.0.1. The API runs inside the Docker network, where 127.0.0.1 is the API container itself. The value shipped in .env.template.simple only works if you run uvicorn directly on your host.
One command builds the images, resets the database, applies migrations, loads a Form 2 Geography textbook with pre-computed embeddings, and starts everything:
make setup-envThis takes a while. It builds with --no-cache. After that, start and stop with:
make run # start in the background
make stop # stop and remove containers
make restart # bothcurl http://localhost:8000/health
# OKInteractive API docs are at http://localhost:8000/docs, and Prometheus metrics at /metrics.
Now send it a message
A running API with no way to talk to it is not much use. Next, either run the mock WhatsApp interface in your browser, or connect a real WhatsApp number.| Command | What it does |
|---|---|
make setup-env | Build, seed and run. The one-shot bootstrap. |
make build | Rebuild images from scratch. |
make run / make stop | Start detached, or tear down. |
make generate-local-data | Wipe the database and reload sample data. Destructive by design. |
make ingest-book filename=book.json | Load your own parsed textbook. |
PYTHONPATH=. pytest -v tests/ | Run the test suite, exactly as CI does. |
If you would rather not pay for inference, Ollama serves both chat and embeddings over an OpenAI-compatible API:
ollama pull llama3.2
ollama pull mxbai-embed-largeThen set LLM_PROVIDER=ollama and EMBEDDING_PROVIDER=ollama and drop the API keys. The defaults already point at http://host.docker.internal:11434, which is how the container reaches the daemon on your host.
One catch: the sample embeddings that ship with the repo are 1024 dimensions, and so is the database column. Switching embedding models changes the dimension and you will need to re-embed. See Database & Migrations.
| Symptom | Cause |
|---|---|
docker-compose: command not found | The Makefile calls the older hyphenated binary. Install the Compose v1 shim, or run the underlying docker compose -f docker/dev/docker-compose.yml commands directly. |
missing separator from make | Comments or section headers are still in your .env. |
ValidationError on startup | A variable is present but blank. Most often LLM_PROVIDER or EMBEDDING_PROVIDER. |
type vector does not exist | You ran migrations against a fresh database without seeding. Run make generate-local-data, which creates the pgvector extension first. |
X is required at import time | One of the seven mandatory variables is missing. See Environment Variables. |
make migrate-up fails | It uses exec, so the container has to be running already. Run make run first. |
Still stuck? Ask in the tech-support channel on the Twiga Discord.