diff --git a/README.md b/README.md index 0712099..9537ae9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,38 @@ # Chat Backend -Django + Channels API for AIML Operations chat (`llm_be/`). +Django + Channels API for AIML Operations chat (`chatbackend.aimloperations.com`). +Packaging via `uv`; production deploy via `server-infra`. -## Local (uv) +Companion frontend: [`chat_web_app`](https://git.aimloperations.com/ai_ml_operations/chat_web_app) +(node-static, not Docker). + +Ticket: [chat_backend#6](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/6) + +## Layout + +```text +chat_backend/ ← repo root (Dockerfile, compose, pyproject, .gitea) +├── llm_be/ ← Django project root (manage.py) +│ ├── manage.py +│ ├── llm_be/ ← settings, urls, asgi/wsgi +│ └── chat_backend/ ← app (models, consumers, services, storage) +├── scripts/ +│ ├── docker-entrypoint.sh +│ └── validate-env.sh +├── docker-compose.yml ← local/CI (bundled Postgres) +└── docker-compose.prod.yml ← server-infra (external DATABASE_URL) +``` + +## Local development + +### Prerequisites + +- Python 3.12+ +- [uv](https://docs.astral.sh/uv/) +- Docker + Docker Compose (optional, recommended) +- Ollama reachable at `OLLAMA_BASE_URL` for LLM features + +### uv (host) ```bash cp .env.example .env @@ -12,27 +42,110 @@ uv run python manage.py migrate uv run python manage.py runserver 0.0.0.0:8003 ``` -## Docker (dev compose = bundled Postgres) +Without `DATABASE_URL` / `DB_HOST`, settings fall back to SQLite (`llm_be/db.sqlite3`). + +Tests (skip live-Ollama classifier cases): + +```bash +cd llm_be +SKIP_RAG_INIT=1 uv run python manage.py test +``` + +### Docker (dev, bundled Postgres) ```bash docker compose up --build ``` -Prod compose (`docker-compose.prod.yml`) expects external `DATABASE_URL` and -`WEB_PORT` from server-infra secrets. See `.env.prod.example`. +App: http://localhost:8003 — Postgres via bundled `db` +(`postgres://chat_backend:chat_backend@db:5432/chat_backend`). +Compose does **not** read host `DATABASE_URL` (avoids CI/prod leaks); override +with `COMPOSE_DATABASE_URL` if needed. + +## Environment variables + +| Variable | Dev default | Prod required | Notes | +|----------|-------------|---------------|-------| +| `DJANGO_ENV` | `dev` | `prod` / `beta` | | +| `DJANGO_SECRET_KEY` | insecure default | yes | Must be real in prod/beta | +| `DJANGO_DEBUG` | true when `dev` | `false` | | +| `DJANGO_ALLOWED_HOSTS` | localhost + chat hosts | yes | Comma-separated | +| `DJANGO_CSRF_TRUSTED_ORIGINS` | derived from hosts | optional | Full origins | +| `DATABASE_URL` | SQLite fallback | yes | Shared Postgres in prod | +| `WEB_PORT` | n/a (compose maps 8003) | `8003` | Host port for prod compose | +| `OLLAMA_BASE_URL` | `http://127.0.0.1:11434` | yes | GPU host in prod: `http://10.0.0.128:11434` | +| `OLLAMA_MODEL` / `OLLAMA_EMBED_MODEL` | from `DEBUG` | optional | Override model names | +| `EMAIL_HOST_*` | empty | yes (prod/beta) | SMTP2GO | +| `CAPTCHA_SECRET_KEY` | empty | recommended | | +| `CORS_ALLOWED_ORIGINS` | local + chat FE | set in prod | Frontend origin | +| `USE_TLS_PROXY` | false (dev) | true behind NPM | Sets `SECURE_PROXY_SSL_HEADER` | +| `GUNICORN_WORKERS` / `GUNICORN_BIND` | 2 / `0.0.0.0:8000` | optional | Entrypoint | +| `SKIP_RAG_INIT` | unset | CI/migrate often `1` | Skip Chroma/Ollama boot work | + +Templates: `.env.example` (local), `.env.prod.example` (control-node secret). + +Control-node secret path (server-infra on ai-server-4080): + +```text +~/Documents/secrets/chat_backend/chat_backend_prod.env +``` + +Validate with: + +```bash +./scripts/validate-env.sh ~/Documents/secrets/chat_backend/chat_backend_prod.env +``` + +If `DATABASE_URL` password contains `$`, escape each as `$$` for Compose. ## Ollama -Set `OLLAMA_BASE_URL` (default `http://127.0.0.1:11434` locally). -Deployed envs use the GPU host: `http://10.0.0.128:11434`. +All clients (`ollama.Client`, `OllamaLLM`, `OllamaEmbeddings`, `ChatOllama`) use +`OLLAMA_BASE_URL` — never hardcoded localhost in deployed code. + +| Env | Typical URL | +|-----|-------------| +| Local (Ollama on same machine) | `http://127.0.0.1:11434` | +| prod / beta (containers on adama/roslin/ai-server) | `http://10.0.0.128:11434` | + +Firewall / Ollama listen on ai-server-4080 must allow `10.0.0.0/24` → `:11434`. ## File storage -Prompt attachments and documents are stored in Postgres (`StoredFile` / -`DatabaseStorage`), not on the container filesystem. +Prompt attachments and workspace documents use **`DatabaseStorage`** +(`chat_backend.StoredFile` BinaryField in Postgres). Blobs are **not** written +to the container filesystem under `media/`. -## Deploy +RAG loaders that need a path materialize a short-lived temp file, then delete it. +Chroma’s vector index may still use a volume (`chroma_db`); that is embeddings +metadata, not the original upload. -Gitea Actions on `master` → `server-infra/scripts/deploy.sh --app chat_backend --env prod`. +## Production (docker-compose.prod.yml) -Related: [chat_backend#6](https://git.aimloperations.com/ai_ml_operations/chat_backend/issues/6) +- Single `web` service; **no** bundled DB — `DATABASE_URL` → shared Postgres (`10.0.0.230`). +- Host port from `WEB_PORT` (catalog: **8003**; beta reserved **8013**). +- Entrypoint: wait DB → migrate → collectstatic → `gunicorn` + `UvicornWorker` + (ASGI for HTTP **and** WebSockets). +- Active/active on **adama + roslin + ai-server-4080**; NPM balances upstreams. +- Deployed by: + +```bash +~/Documents/repos/server-infra/scripts/deploy.sh \ + --app chat_backend --env prod --ref +``` + +## CI / CD (Gitea Actions) + +| Workflow | Trigger | Action | +|----------|---------|--------| +| `unittests.yml` | push + PR → `master` | `uv sync` + `manage.py test` | +| `ci.yml` | PR → `master` | same unit tests | +| `deploy.yml` | after Unit Tests succeeds on `master` **push** | docker build + tests on **ephemeral compose Postgres** → `deploy.sh` | + +Deploy never runs on PRs. + +## Security note + +Secrets previously hardcoded in `settings.py` (email password, captcha, Django +secret) must live only in the control-node env file. Rotate anything that was +ever committed; never commit `.env` or `~/Documents/secrets/`.