Skip to content
Merged

Dev #39

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# ── PostgreSQL (used by docker-compose.yml and the backend) ─────────────
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
POSTGRES_DB=main
POSTGRES_HOST=127.0.0.1
POSTGRES_PORT=5432

# ── LLM Provider (OpenAI-compatible API) ──────────────────────────────
LLM_PROVIDER_TYPE=openai
# Set your API key and base URL. Examples:
# LLM_BASE_URL=https://api.openai.com/v1 # OpenAI
# LLM_BASE_URL=https://api.deepseek.com # DeepSeek
# LLM_BASE_URL=https://openrouter.ai/api/v1 # OpenRouter
LLM_BASE_URL="https://api.openai.com/v1"
LLM_API_KEY="sk-..."
LLM_MODEL="gpt-4o"
LLM_MAX_TOKENS=4096

# ── Embedding Provider (OpenAI-compatible) ────────────────────────────
# Typically uses OpenRouter or OpenAI:
# EMBEDDING_MODEL="text-embedding-3-large" # OpenAI
# EMBEDDING_MODEL="qwen/qwen3-embedding-8b" # OpenRouter
EMBEDDING_MODEL="text-embedding-3-small"
EMBEDDING_BASE_URL="https://openrouter.ai/api/v1"
EMBEDDING_API_KEY="sk-or-..."
EMBEDDING_DIMENSIONS=768

# ── Public Demo Abuse Prevention (optional) ───────────────────────────
# Uncomment to disable the daily token budget (useful for local dev).
# DEMO_DISABLE_BUDGET=true
# DEMO_DAILY_BUDGET_TOKENS=1000000
# DEMO_BUDGET_FILE=/tmp/demo-budget.json
# DEMO_MAX_QUERY_LENGTH=500
# DEMO_MAX_USER_MESSAGES=50

# ── Cross-Environment Copy (optional — for scripts/copy_corpus_data.py) ──
# DEV_DATABASE_URL=postgresql+asyncpg://postgres:password@127.0.0.1:5432/main
# SUPABASE_DIRECT_URL=postgresql+asyncpg://user:password@host:5432/postgres?ssl=require
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
# Multi-Agent RAG

> **Live demo:** [multi-agent-rag.olegedly.com](https://multi-agent-rag.olegedly.com/)
A config-driven multi-agent research system with pgvector RAG, MCP tool servers, and a LangChain agent pipeline. Answers are grounded in curated knowledge bases (corpora), each scoped to a single corpus with route-based isolation.

## Quick Start

First, create your config:

```bash
cp .env.example .env
# Then edit .env with your API keys
```

Then start the dev environment:

```bash
./dev.sh
```
Expand All @@ -25,6 +35,8 @@ SolidJS SPA → AG-UI/SSE → FastAPI → LangChain Agent Pipeline
└── pgvector RAG (corpus-filtered)
```

![Dashboard screenshot](docs/dashboard-screenshot.png)

The system is **corpus-scoped**: each conversation binds to one corpus from start to finish. Every retrieval carries the active `corpus_id`. Adding a new knowledge base is an ingestion + config task.

### Key modules
Expand Down Expand Up @@ -75,6 +87,8 @@ make test_backend # backend only
make test_frontend # frontend only
```

> All **422 tests pass** (223 backend + 199 frontend) with 0 pyright/TypeScript errors.

Pure unit tests — no Docker or DB dependency for business logic. The `create_app()` DI makes mocking straightforward.

## Deployment
Expand Down
8 changes: 8 additions & 0 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from backend.config import Settings, get_settings
from backend.corpus_config import CorporaConfig
from backend.middleware import ChatGuard
from backend.db import migrate_db


def create_app(
Expand Down Expand Up @@ -48,6 +49,13 @@ def create_app(

@asynccontextmanager
async def _lifespan(app: FastAPI) -> AsyncIterator[None]:
# Auto-migrate DB schema on startup (idempotent — no-op if tables exist).
# Skip when no DB credentials are configured (e.g. in tests).
if settings.postgres_user and settings.postgres_password:
try:
await migrate_db(settings.database_url)
except Exception:
pass # Non-fatal — RAG tools will surface connection errors at query time.
yield

app = FastAPI(title=settings.app_name, lifespan=_lifespan)
Expand Down
6 changes: 3 additions & 3 deletions dev.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -e
cleanup() {
echo ""
echo "Shutting down..."
docker compose -f docker-compose.base.yml -f docker-compose.dev-override.yml down
docker compose down
[ -n "$BACKEND_PID" ] && kill "$BACKEND_PID" 2>/dev/null
[ -n "$PGWEB_PID" ] && kill "$PGWEB_PID" 2>/dev/null
[ -n "$MCP_PID" ] && kill "$MCP_PID" 2>/dev/null
Expand All @@ -18,10 +18,10 @@ trap cleanup SIGINT SIGTERM

# ── Database (Docker) ──────────────────────────────────────
echo "Starting PostgreSQL (pgvector)..."
docker compose -f docker-compose.base.yml -f docker-compose.dev-override.yml up -d db
docker compose up -d

echo "Waiting for PostgreSQL to be healthy..."
until docker compose -f docker-compose.base.yml -f docker-compose.dev-override.yml exec db pg_isready -U "$POSTGRES_USER" --quiet 2>/dev/null; do
until docker compose exec db pg_isready -U "$POSTGRES_USER" --quiet 2>/dev/null; do
sleep 1
done
echo "PostgreSQL is ready."
Expand Down
25 changes: 0 additions & 25 deletions docker-compose.base.yml

This file was deleted.

12 changes: 1 addition & 11 deletions docker-compose.dev-override.yml → docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ services:
db:
image: pgvector/pgvector:pg18
container_name: "pgvector-db"
env_file: .env
env_file: ".env"
ports:
- "5432:5432"
volumes:
Expand All @@ -13,15 +13,5 @@ services:
timeout: 5s
retries: 5

backend:
ports:
- "8000:8000"
volumes:
- ./backend:/app/backend
command: ["fastapi", "dev", "backend/main.py", "--port", "8000", "--host", "0.0.0.0"]
depends_on:
db:
condition: service_healthy

volumes:
pgdata:
Binary file added docs/dashboard-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
126 changes: 126 additions & 0 deletions docs/grills/GRILL-ME-27.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# Grill Me Results

Generated: 2026-07-01T15:59:49.108Z

## Plan

let's carefully plan the changes. so I've found that @docker-compose.coolify.yml is purely a note for how to deploy on coolify, I copy/paste it into the coolify web ui, there's no other usage. and then we have 2 files: @docker-compose.base.yml and @docker-compose.dev-override.yml ; these are safe to merge into one, because we don't in fact ever use them separately. so the way @dev.sh calls them needs to become the baseline docker-compose.yml ; clear?

## Shared Understanding

AC #8 (docker compose up from scratch) redesigned via /grill-me interview. The plan: merge docker-compose files into a single docker-compose.yml with only the db service, delete the unused base.yml/dev-override.yml, create .env.example, add migrate_db() to backend startup lifespan, update dev.sh to call plain 'docker compose up -d', and update the AC text and tick box in the issue.

## Questions and Answers

### 1. What's the target mode for the merged docker-compose.yml — dev (native-like hot-reload) or "from scratch" production-like (everything in containers)?

**Recommended answer:** We need to merge base.yml + dev-override.yml into one docker-compose.yml that works for both: dev.sh would just run `docker compose up -d db` (backend/frontend native), AND `docker compose up --profile production` would start all containers for the "clone → .env → docker compose up" experience. But the AC #8 demands the latter work seamlessly.

**User answer:** _(not recorded)_

**Status:** open

**Notes:** Tension: dev.sh uses containers only for DB (backend/frontend native), but AC #8 requires everything in containers from scratch. The merged compose needs to serve both.

### 2. Are the docker-compose files used for building deployable Docker images?

**Recommended answer:** No. The CI/CD workflow at .github/workflows/deploy.yml builds images directly from Dockerfiles (backend/Dockerfile, frontend/Dockerfile) and pushes to GHCR. The compose files are purely for local development (dev.sh spins up the DB container) and the coolify.yml is a copy-paste note for the Coolify web UI.

**User answer:** No — compose files are not used for building deployable images; CI/CD uses Dockerfiles directly.

**Status:** resolved

### 3. Should AC #8 change to reflect the actual dev workflow instead of the strict "docker compose up from scratch"?

**Recommended answer:** Yes. Change it to: "Developer setup works: clone → copy .env.example → ./dev.sh starts DB in Docker, backend (hot-reload) and frontend (Vite HMR) natively. No manual steps beyond configuring .env."

**User answer:** Yeah, let's make the AC reflect what we already do in practice

**Status:** resolved

**Notes:** Compose files are merged into one docker-compose.yml (base + dev-override combined). dev.sh uses it the same way, just without -f flags. The criterion tracks the real dev workflow.

### 4. The merged docker-compose.yml will have db, backend, and frontend services. dev.sh starts only `db`. But a fresh clone also needs DB tables and seeded corpus data to be useful. Should the backend auto-migrate and auto-seed on startup, or are those separate concerns from this AC?

**Recommended answer:** They're separate concerns. The AC says "docker compose up from scratch works with only .env file." The merged compose + dev.sh handles the container/native startup. DB migration and seeding are part of the seed workflow (scripts/seed_knowledge_base.py), not the compose refactor. But we should mention in the README that after first ./dev.sh, you run `uv run scripts/seed_knowledge_base.py --all`.

**User answer:** _(not recorded)_

**Status:** needs-codebase-check

**Notes:** Need to check if the backend startup currently fails gracefully when DB tables don't exist, or if it crashes. If it crashes, we may need to at least call migrate_db() on startup.

### 5. Should merged compose + .env.example also add auto-migration (calling migrate_db() on backend startup via the lifespan), or is that a separate concern?

**Recommended answer:** Separate concern. The AC is about startup mechanics (merge compose files, create .env.example). DB migration and seeding remain the existing workflow: `uv run scripts/seed_knowledge_base.py --all`. However, to make the "from scratch" experience not immediately fail, we could add migrate_db() to the backend lifespan — it's idempotent and takes <100ms. Worth adding as a good-faith step.

**User answer:** _(not recorded)_

**Status:** open

**Notes:** Backend starts fine without tables (lazy DB connections via RAG tools). First chat query would error on missing tables. migrate_db() is safe to call on every startup (idempotent).

### 6. Should migrate_db() be called on every backend startup via the lifespan?

**Recommended answer:** Yes. migrate_db() is fully idempotent (~100ms no-op when tables exist). Adding it to the backend lifespan means a fresh clone with an empty DB gets tables created automatically, preventing SQL errors from leaking to the agent on first chat. Data seeding remains a separate manual step.

**User answer:** Yes, sounds reasonable — add migrate_db() to backend startup lifespan.

**Status:** resolved

### 7. The merged docker-compose.yml will contain db, backend, and frontend services. dev.sh only starts db. What should be the backend command in the merged file — the production CMD from the Dockerfile ("fastapi run"), the dev override CMD ("fastapi dev"), or no command (let Dockerfile CMD apply)?

**Recommended answer:** No command in the compose file — let the Dockerfile's CMD ("fastapi run" — production mode) be the default. If someone runs the backend in a container they get production behavior. dev.sh never starts the backend container anyway, so it doesn't matter for the dev workflow.

**User answer:** _(not recorded)_

**Status:** open

**Notes:** dev.sh only starts 'db' — backend/frontend start natively. The backend CMD in compose only applies if someone runs all containers.

### 8. Should docker-compose.yml contain only the db service, and dev.sh call `docker compose up -d` without any service name?

**Recommended answer:** Yes. Single service (db) in docker-compose.yml, so `docker compose up -d` starts only the database. dev.sh loses the -f flags and service argument. Delete base.yml and dev-override.yml. Keep coolify.yml as deploy reference.

**User answer:** _(not recorded)_

**Status:** open

**Notes:** Backend/frontend service definitions in compose are unused — dev.sh never starts them in containers. Pruning them eliminates maintenance burden and the confusing -f flag dance.

### 9. Create .env.example with all env vars, default values, and inline comments for each section?

**Recommended answer:** Yes. Write .env.example with: PostgreSQL (matching compose defaults), LLM (OpenAI-compatible placeholder), Embeddings (OpenRouter default), optional demo budget settings, optional cross-env copy vars. User will manually review and finalize.

**User answer:** Create the placeholder .env.example and I'll manually edit it for correctness.

**Status:** resolved

**Notes:** File is written to .env.example. Covers all Settings fields from config.py.

### 10. What should docker-compose.yml contain?

**Recommended answer:** Single db service (pgvector/pgvector:pg18 with healthcheck, port, volume, env_file). No backend or frontend services. `docker compose up -d` starts just the database. dev.sh calls `docker compose up -d` without -f flags.

**User answer:** Only the db service, so `docker compose up` starts just the database.

**Status:** resolved

**Notes:** base.yml and dev-override.yml can be deleted. coolify.yml retained as deploy reference.

## Agreed Decisions

- Compose files are not used for building deployable images (CI/CD uses Dockerfiles directly).
- Merge base.yml + dev-override.yml into one docker-compose.yml with only the db service — backend/frontend services removed since nothing uses them.
- Delete docker-compose.base.yml and docker-compose.dev-override.yml. Keep coolify.yml as deploy reference.
- dev.sh becomes: docker compose up -d (no -f flags, no service argument).
- Add migrate_db() to backend startup lifespan so tables auto-create on fresh clone (~100ms idempotent no-op).
- Create .env.example with all env vars, sensible defaults, and inline comments.
- Update AC #8 in the issue to reflect the real dev workflow (clone → cp .env.example .env → ./dev.sh → seed).
- Tick AC #8 in the issue after changes are made.

## Open Risks

- User still needs to manually run uv run scripts/seed_knowledge_base.py --all after first startup to get corpus data — not automated.
- The .env.example needs user review for correctness before being committed.
- If the backend starts before DB is healthy, migrate_db() might fail — need depends_on or retry logic.