A hyper-rational debate API built with FastAPI and LangChain, powered by Google Gemini.
- FastAPI with modern async/await patterns
- LangChain integration with Google Gemini
- Industry-standard project structure
- Kubernetes-ready deployment
- Health checks and monitoring
- Type-safe configuration with Pydantic
- Python 3.13+
- UV package manager (or pip)
-
Clone the repository
-
Create a virtual environment:
python -m venv .venv source .venv/bin/activate # On Windows: .venv\Scripts\activate
-
Install dependencies:
pip install -e . # Or with UV: uv pip install -e .
-
Create a
.envfile:GOOGLE_API_KEY=your_api_key_here
-
Run the application:
python main.py
The API will be available at http://localhost:8000 with interactive docs at /docs.
A simple Streamlit frontend is available to interact with the Axiom API.
# Install dependencies (if not already installed)
pip install -e .
# Run the Streamlit app
streamlit run frontend.pyThe frontend will open in your browser at http://localhost:8501.
- Clean, modern UI
- Submit arguments for debate
- View Axiom's responses
- Configurable API URL (defaults to your deployed Civo instance)
- Error handling and loading states
You can change the API URL in the sidebar. By default, it connects to the deployed Civo instance, but you can also use:
- Local API:
http://localhost:8000 - Any other deployed instance
The Streamlit frontend can be deployed alongside the API. See DEPLOYMENT.md for complete instructions.
Quick steps:
- Build and push frontend image:
docker build -f Dockerfile.frontend -t ghcr.io/USERNAME/axiom-frontend:latest . - Deploy:
kubectl apply -f k8s/frontend-deployment.yaml && kubectl apply -f k8s/frontend-service.yaml - Expose:
kubectl patch service axiom-frontend-service -p '{"spec":{"type":"LoadBalancer"}}'
π For detailed deployment instructions, see DEPLOYMENT.md
- Civo account (Sign up here)
- Civo CLI installed (
civocommand) - Docker installed (for building images)
- kubectl configured for your Civo cluster
- GitHub account (for GHCR) or Docker Hub account
1. Build and Push Image to Registry
# Build the image
docker build -t axiom-api:latest .
# Tag for GitHub Container Registry (or your preferred registry)
docker tag axiom-api:latest ghcr.io/YOUR_USERNAME/axiom-api:latest
# Login and push
docker login ghcr.io -u YOUR_USERNAME
docker push ghcr.io/YOUR_USERNAME/axiom-api:latest# Install Civo CLI if not already installed
# See: https://www.civo.com/docs/cli
# Login to Civo
civo apikey save YOUR_API_KEY your-key-name
# Create a Kubernetes cluster
civo kubernetes create axiom-cluster --size g4s.kube.medium --nodes 2 --region NYC1
# Wait for cluster to be ready (usually ~90 seconds)
civo kubernetes config axiom-cluster --save
# Verify connection
kubectl get nodesImportant: You need TWO secrets - one for image pull (if using private registry) and one for the Google API key.
# 1. Create GHCR secret (for private image pull)
# Get a GitHub Personal Access Token with 'read:packages' permission
kubectl create secret docker-registry ghcr-secret \
--docker-server=ghcr.io \
--docker-username=YOUR_GITHUB_USERNAME \
--docker-password=YOUR_GITHUB_TOKEN \
--docker-email=YOUR_EMAIL
# 2. Create secret for Google API key
kubectl create secret generic axiom-secrets \
--from-literal=google-api-key='YOUR_GOOGLE_API_KEY'
# Verify secrets were created
kubectl get secrets# Apply ConfigMap
kubectl apply -f k8s/configmap.yaml
# Apply Deployment and Service
kubectl apply -f k8s/deployment.yaml
kubectl apply -f k8s/service.yaml
# Apply Ingress (optional, only if using ingress)
# First, check your cluster's ingress class: kubectl get ingressclass
# Then update k8s/ingress.yaml with the correct ingressClassName
kubectl apply -f k8s/ingress.yaml
# Check deployment status
kubectl get deployments
kubectl get pods
kubectl get servicesNote: If you see errors about kustomization.yaml, that's normal - it's only used with kubectl apply -k (kustomize). Use individual kubectl apply -f commands instead.
# Update service to use LoadBalancer type
kubectl patch service axiom-api-service -p '{"spec":{"type":"LoadBalancer"}}'
# Wait a few seconds, then get external IP
kubectl get service axiom-api-service
# Your API will be accessible at: http://EXTERNAL-IP
# Example endpoints:
# - http://EXTERNAL-IP/health
# - http://EXTERNAL-IP/docs (Swagger UI)
# - http://EXTERNAL-IP/api/v1/debate (POST)Note: The external IP may take 1-2 minutes to be assigned. Keep checking with kubectl get service axiom-api-service.
# Check pods are running
kubectl get pods -l app=axiom-api
# Should show STATUS: Running and READY: 1/1
# Get your external IP
kubectl get service axiom-api-service
# Note the EXTERNAL-IP address
# Test the API (replace EXTERNAL_IP with your actual IP)
curl http://EXTERNAL_IP/health
curl http://EXTERNAL_IP/
# Or use port-forward for local testing
kubectl port-forward service/axiom-api-service 8000:80
curl http://localhost:8000/healthYour API is now live and accessible on the internet! π
| Variable | Description | Default | Required |
|---|---|---|---|
GOOGLE_API_KEY |
Google API key for Gemini | - | Yes |
PORT |
Server port | 8000 | No |
DEBUG |
Enable debug mode | false | No |
ENVIRONMENT |
Environment (development/production) | production | No |
LLM_MODEL |
LLM model name | gemini-3-pro-preview | No |
CORS_ORIGINS |
Allowed CORS origins (comma-separated) | * | No |
Edit k8s/configmap.yaml for non-sensitive configuration and k8s/secret.yaml.template for secrets.
GET /- Root endpointGET /health- Health check (liveness probe)GET /ready- Readiness check (readiness probe)POST /api/v1/debate- Submit an argument for debateGET /docs- Interactive API documentationGET /redoc- Alternative API documentation
axiom/
βββ app/
β βββ api/
β β βββ v1/
β β β βββ routes.py # API routes
β β βββ deps.py # API dependencies
β βββ core/
β β βββ config.py # Configuration
β β βββ dependencies.py # Shared dependencies
β β βββ logging.py # Logging setup
β βββ services/
β β βββ llm_service.py # LLM service
β βββ schemas/
β β βββ debate.py # Pydantic models
β βββ prompts/
β β βββ system_prompt.md # System prompt
β βββ main.py # FastAPI app
βββ k8s/ # Kubernetes manifests
βββ Dockerfile # Production Dockerfile
βββ pyproject.toml # Dependencies
βββ main.py # Entry point
- Liveness Probe:
/health- Checks if the app is running - Readiness Probe:
/ready- Checks if the app is ready to serve traffic
View logs in Kubernetes:
kubectl logs -l app=axiom-api -fScale the deployment:
kubectl scale deployment axiom-api --replicas=3Or update k8s/deployment.yaml and reapply.
# Check pod status
kubectl describe pod <pod-name>
# Check logs
kubectl logs <pod-name>Error: "unauthorized" or "pull access denied"
- Your image is private and needs authentication
- Create the GHCR secret (see Step 3)
- Verify:
kubectl get secret ghcr-secret
Error: "ImagePullBackOff"
- Check image name in
k8s/deployment.yamlmatches your registry - Verify image exists:
docker pull YOUR_IMAGE_NAME - Ensure
imagePullSecretsis configured in deployment.yaml
# Verify secret
kubectl get secret axiom-secrets
# Update secret
kubectl create secret generic axiom-secrets \
--from-literal=google-api-key='NEW_KEY' \
--dry-run=client -o yaml | kubectl apply -f -
# Restart pods
kubectl rollout restart deployment axiom-api- This is normal and can take 1-2 minutes
- Keep checking:
kubectl get service axiom-api-service - If it stays pending for >5 minutes, check Civo dashboard for LoadBalancer status
[Your License Here]