diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..62c3a2639 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +.venv/ +venv/ +env/ + +.git/ +.DS_Store + +frontend/node_modules/ +frontend/dist/ + +data/ + + diff --git a/README.md b/README.md index 23599b3cf..d765799b2 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ OPENROUTER_API_KEY=sk-or-v1-... Get your API key at [openrouter.ai](https://openrouter.ai/). Make sure to purchase the credits you need, or sign up for automatic top up. +If you want a starter template, copy `env.template` to `.env` and edit it. + ### 3. Configure Models (Optional) Edit `backend/config.py` to customize the council: @@ -64,6 +66,58 @@ CHAIRMAN_MODEL = "google/gemini-3-pro-preview" ./start.sh ``` +**Option 1b: Docker Compose** + +1. Provide `OPENROUTER_API_KEY` (either via a local `.env` file or your shell environment). + +Create a `.env` file in the project root (optional but recommended): + +```bash +OPENROUTER_API_KEY=sk-or-v1-... +``` + +If you want a starter template, copy `env.template` to `.env` and edit it. + +2. Build + run: + +```bash +docker compose up --build +``` + +Then open http://localhost:5173 in your browser. + +Notes: +- Data is persisted on the host at `./data/` (mounted into the backend container). +- If you’re not accessing the app from the same machine (i.e. not `localhost`), set `VITE_API_BASE` to the correct reachable backend URL (e.g. `http://YOUR_HOST:8001`) in `docker-compose.yml` or your environment. + +**Podman (podman-compose)** + +If you're using Podman instead of Docker, you can run the same `docker-compose.yml` with `podman-compose`. + +- Detached mode (recommended): + +```bash +podman-compose down && podman-compose build --no-cache && podman-compose up -d +``` + +- Foreground mode: + +```bash +podman-compose down && podman-compose build --no-cache && podman-compose up +``` + +- Tail logs: + +```bash +podman-compose logs -f +``` + +- Stop: + +```bash +podman-compose down +``` + **Option 2: Run manually** Terminal 1 (Backend): diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 000000000..e14ddf58b --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,27 @@ +FROM python:3.11-slim + +WORKDIR /app + +ENV PYTHONDONTWRITEBYTECODE=1 \ + PYTHONUNBUFFERED=1 + +# System deps for uv + httpx TLS +RUN apt-get update \ + && apt-get install -y --no-install-recommends ca-certificates curl \ + && rm -rf /var/lib/apt/lists/* + +# Install uv (used by this repo) +RUN pip install --no-cache-dir uv + +# Install python deps +COPY pyproject.toml uv.lock /app/ +RUN uv sync --frozen --no-dev + +# Copy backend code +COPY backend/ /app/backend/ + +EXPOSE 8001 + +CMD ["uv", "run", "uvicorn", "backend.main:app", "--host", "0.0.0.0", "--port", "8001"] + + diff --git a/backend/main.py b/backend/main.py index e33ce59a6..197710811 100644 --- a/backend/main.py +++ b/backend/main.py @@ -17,7 +17,12 @@ # Enable CORS for local development app.add_middleware( CORSMiddleware, - allow_origins=["http://localhost:5173", "http://localhost:3000"], + allow_origins=[ + "http://localhost:5173", + "http://127.0.0.1:5173", + "http://localhost:3000", + "http://127.0.0.1:3000", + ], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..e6e6918ee --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,29 @@ +services: + backend: + build: + context: . + dockerfile: backend/Dockerfile + environment: + # Compose will read a local .env automatically if present; this keeps podman-compose happy + # even when the file doesn't exist. + - OPENROUTER_API_KEY=${OPENROUTER_API_KEY} + ports: + - "8001:8001" + volumes: + - ./data:/app/data + restart: unless-stopped + + frontend: + build: + context: . + dockerfile: frontend/Dockerfile + environment: + # Optional; default in code is http://localhost:8001 + - VITE_API_BASE=http://localhost:8001 + ports: + - "5173:5173" + depends_on: + - backend + restart: unless-stopped + + diff --git a/env.template b/env.template new file mode 100644 index 000000000..3fcd77d82 --- /dev/null +++ b/env.template @@ -0,0 +1,5 @@ +# Rename to `.env` (repo root) or export these variables in your shell. +# Required for backend OpenRouter calls: +OPENROUTER_API_KEY=sk-or-v1-... + + diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 000000000..d330b55a3 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,14 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY frontend/package.json frontend/package-lock.json /app/ +RUN npm ci + +COPY frontend/ /app/ + +EXPOSE 5173 + +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0", "--port", "5173", "--strictPort"] + + diff --git a/frontend/src/api.js b/frontend/src/api.js index 87ec685ba..a103d6cdd 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -2,7 +2,9 @@ * API client for the LLM Council backend. */ -const API_BASE = 'http://localhost:8001'; +// In Vite, only env vars prefixed with VITE_ are exposed to the browser. +// Default keeps local dev + docker-compose (published backend port) working out of the box. +const API_BASE = import.meta.env.VITE_API_BASE || 'http://localhost:8001'; export const api = { /** diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 8b0f57b91..19bb10ccc 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -4,4 +4,9 @@ import react from '@vitejs/plugin-react' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + server: { + host: true, + port: 5173, + strictPort: true, + }, })