westfarn ee3d47c8c3
Deploy Beta / unit-tests (push) Successful in 10s
Unit Tests / test (push) Successful in 9s
Deploy Beta / docker (push) Successful in 25s
Deploy Beta / deploy-beta (push) Successful in 56s
Ignore WS heartbeats and reject empty chat messages (#32)
## Summary
- Closes #31
- Ignore WebSocket `type: ping` heartbeats so keepalives no longer create conversations or hit title/LLM pipelines
- Reject empty/whitespace user messages in both chat consumers, `PromptSerializer`, and REST conversation prompt POST

## Test plan
- [x] `UserPromptGuardTestCase`, `PromptSerializerTestCase` blank/whitespace cases
- [x] `WebSocketReceiveGuardTestCase` ping ignore + empty message rejection (both WS routes)
- [ ] Deploy to beta; leave idle tab open and confirm no new rogue conversations
- [ ] Confirm normal chat send still works

Related FE: https://git.aimloperations.com/ai_ml_operations/chat_web_app/issues/51Reviewed-on: #32
2026-07-28 05:12:13 -07:00
2025-12-07 06:31:06 -06:00
2025-03-07 12:23:00 -06:00

Chat Backend

Django + Channels API for AIML Operations chat (chatbackend.aimloperations.com). Packaging via uv; production deploy via server-infra.

Companion frontend: chat_web_app (node-static, not Docker).

Ticket: chat_backend#6

Layout

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
  • Docker + Docker Compose (optional, recommended)
  • Ollama reachable at OLLAMA_BASE_URL for LLM features

uv (host)

cp .env.example .env
uv sync
cd llm_be
uv run python manage.py migrate
uv run python manage.py runserver 0.0.0.0:8003

Without DATABASE_URL / DB_HOST, settings fall back to SQLite (llm_be/db.sqlite3).

Tests:

cd llm_be
uv run python manage.py test

The suite is offline by default — the custom test runner (llm_be/test_runner.py) sets SKIP_RAG_INIT=1 and a cheap password hasher, and LLM chains are faked, so no Ollama, Chroma, SMTP or network access is needed. Tests live in llm_be/chat_backend/tests/ (models, storage, serializers, views, services, signals, consumers).

Non-deterministic checks against a real model server are opt-in:

cd llm_be
RUN_LIVE_OLLAMA_TESTS=1 uv run python manage.py test chat_backend.tests.test_live_ollama

Docker (dev, bundled Postgres)

docker compose up --build

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
ENABLE_ACCOUNT_REGISTRATION false optional Self-serve sign-up; keep false until ready
STRIPE_SECRET_KEY / STRIPE_PUBLISHABLE_KEY / STRIPE_WEBHOOK_SECRET empty yes for billing Stripe API + webhook
STRIPE_PRICE_ID empty optional Pre-created Price; else $10/mo from settings
FRONTEND_BASE_URL http://localhost:3000 set in prod/beta Checkout success/cancel + OAuth return
CORS_ALLOWED_ORIGINS local + chat FE (+ beta FE default) set in prod/beta Frontend origin(s)
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

Assistant identity (Hesychia) lives in code: llm_be/chat_backend/services/assistant_identity.py — prepended to user-facing generation prompts (chat, RAG, data analysis). Not env-configurable.

Templates: .env.example (local), .env.prod.example (control-node secret).

Control-node secret paths (server-infra on ai-server-4080):

~/Documents/secrets/chat_backend/chat_backend_prod.env
~/Documents/secrets/chat_backend/chat_backend_beta.env

Validate with:

./scripts/validate-env.sh ~/Documents/secrets/chat_backend/chat_backend_prod.env
./scripts/validate-env.sh ~/Documents/secrets/chat_backend/chat_backend_beta.env

If DATABASE_URL password contains $, escape each as $$ for Compose.

Ollama

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 workspace documents use DatabaseStorage (chat_backend.StoredFile BinaryField in Postgres). Blobs are not written to the container filesystem under media/.

RAG loaders that need a path materialize a short-lived temp file, then delete it. Chromas vector index may still use a volume (chroma_db); that is embeddings metadata, not the original upload.

Production / beta (docker-compose.prod.yml)

  • Single web service; no bundled DB — DATABASE_URL → shared Postgres (10.0.0.230).
  • Host port from WEB_PORT (prod 8003; beta 8013).
  • Entrypoint: wait DB → migrate → collectstatic → gunicorn + UvicornWorker (ASGI for HTTP and WebSockets).
  • Active/active on adama + roslin + ai-server-4080; NPM balances upstreams.
  • Manual / local deploy:
# beta (day-to-day)
~/Documents/repos/server-infra/scripts/deploy.sh \
  --app chat_backend --env beta --ref <sha>

# prod (intentional)
~/Documents/repos/server-infra/scripts/deploy.sh \
  --app chat_backend --env prod --ref <sha>

Beta hosts / CORS: beta.chatbackend.aimloperations.com API + https://beta.chat.aimloperations.com SPA (see .env.prod.example beta block). DB: chat_backend_beta. Pair with server-infra#7 and frontend chat_web_app#35.

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-beta.yml push to master unit tests → docker compose tests → deploy.sh --env beta
deploy-prod.yml manual workflow_dispatch only unit tests → docker compose tests → deploy.sh --env prod

Push/merge to master auto-deploys beta only. Prod requires the Gitea Run workflow button on Deploy Prod. 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/.

S
Description
Backend django project for the chat site
Readme
1.2 MiB
Languages
Python 98.9%
HTML 0.7%
Shell 0.3%