Skip to content

Latest commit

 

History

History
331 lines (240 loc) · 9.85 KB

DEVELOPMENT.md

File metadata and controls

331 lines (240 loc) · 9.85 KB

Development Guide

This guide covers running the development environment, executing tests, and configuring the project.

Prerequisites

  • Docker (29.x+) — for containerized development
  • uv (Python package manager) — for local backend development
  • Node.js (20.x+) — for local frontend development
  • Python (3.12) — managed by uv

Running the Development Environment

Option 1: Docker Compose (Recommended)

Starts both backend and frontend services with health checks and hot-reload for the frontend.

# Start both services
docker compose up --build

# Run in background
docker compose up --build -d

# Stop services
docker compose down

Services exposed:

Service URL Port
Backend (FastAPI) http://localhost:8000 8000
Backend API Docs http://localhost:8000/docs 8000
Frontend (Vite) http://localhost:5173 5173

How it works:

  • Backend starts first and passes a health check (/health endpoint)
  • Frontend starts only after backend is healthy (depends_on: condition: service_healthy)
  • Frontend source (./frontend/src) is mounted as a volume for hot-reload
  • Vite proxies /api/* requests to the backend

Option 2: Local Development (Without Docker)

Backend

cd backend

# Install dependencies (including dev)
uv sync --extra dev

# Run the server
uv run uvicorn octave.app:app --reload --host 0.0.0.0 --port 8000

Frontend

cd frontend

# Install dependencies
npm install

# Start dev server
npm run dev

Running Tests

Backend Tests

cd backend

# Install dev dependencies first
uv sync --extra dev

# Run all tests
uv run pytest tests/ -v

# Run specific test file
uv run pytest tests/test_health.py -v

# Run with coverage
uv run pytest tests/ -v --cov=octave --cov-report=term-missing

Backend quality checks:

# Linting (ruff)
uv run ruff check src/ tests/

# Auto-fix linting issues
uv run ruff check --fix src/ tests/

# Type checking (mypy)
uv run mypy src/

Frontend Tests

cd frontend

# Install dependencies first
npm install

# Unit tests (Vitest)
npm run test

# Unit tests in watch mode
npm run test:watch

# E2E tests (Playwright)
npm run test:e2e

# Install Playwright browsers (first time)
npx playwright install chromium

Frontend quality checks:

# Linting (ESLint)
npm run lint

# Format code (Prettier)
npm run format

# Check formatting without writing
npm run format:check

# TypeScript type checking
npx tsc --noEmit

Environment Configuration

Backend Environment

The backend reads configuration from environment variables. Create backend/.env for local development:

# Server
HOST=0.0.0.0
PORT=8000

Note: .env files are in .gitignore and never committed.

Database Adapter

The persistence layer (octave.db) reads OCTAVE_DB_* variables, via DatabaseSettings in backend/src/octave/db/config.py:

Variable Default Description
OCTAVE_DB_ADAPTER sqlite Registered adapter name, or a module.path:ClassName import string for a third-party adapter package
OCTAVE_DB_URL sqlite+aiosqlite:///octave.db Async SQLAlchemy URL; Alembic derives the sync mirror automatically
OCTAVE_DB_EMBEDDING_DIM 768 Width of the vector index; must match the embedding model in use
OCTAVE_DB_AUTO_MIGRATE true Apply pending migrations at startup. false skips the upgrade but still verifies the DB is migrated (startup fails fast if not)

Alembic CLI commands run from backend/ (uv run alembic upgrade head). Migrations are auto-applied on app startup via octave.db.lifespan.db_lifespan unless OCTAVE_DB_AUTO_MIGRATE=false.

Inference Adapter

The inference layer (octave.inference) reads OCTAVE_INFERENCE_* variables, via InferenceSettings in backend/src/octave/inference/config.py:

Variable Default Description
OCTAVE_INFERENCE_ADAPTER openai Registered adapter name, or a module.path:ClassName import string for a third-party adapter package
OCTAVE_INFERENCE_BASE_URL http://localhost:11434/v1 OpenAI-compatible engine endpoint (Ollama, vLLM, llama.cpp server, LM Studio, …)
OCTAVE_INFERENCE_API_KEY (empty) Bearer token; optional for local engines. Secret — never logged (redacted ***)
OCTAVE_INFERENCE_DEFAULT_MODEL (unset) Model used when a request doesn't specify one
OCTAVE_INFERENCE_TIMEOUT_SECONDS 120.0 Per-request timeout
OCTAVE_INFERENCE_MAX_RETRIES 2 SDK-level retries on transient failures

HTTPS engines behind an internal CA: if the engine uses a certificate from a private CA, httpx (certifi bundle) will fail the handshake with a generic connection error even though curl succeeds. Point it at the system trust store:

SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt

This adds to certifi's bundle rather than replacing it, so public HTTPS endpoints keep working. Verify any setup with backend/scripts/smoke_inference.py.

MCP Server Supervision

The MCP layer (octave.mcp) reads OCTAVE_MCP_* variables via McpSettings in backend/src/octave/mcp/config.py:

Variable Default Description
OCTAVE_MCP_REQUEST_TIMEOUT_SECONDS 30.0 Per-request timeout for client calls
OCTAVE_MCP_INITIALIZE_TIMEOUT_SECONDS 30.0 Budget for the initialize handshake during connect/restart
OCTAVE_MCP_RESTART_BASE_DELAY_SECONDS 1.0 First auto-restart delay; exponential backoff multiplies by 2 per attempt
OCTAVE_MCP_RESTART_MAX_DELAY_SECONDS 60.0 Cap on the backoff delay
OCTAVE_MCP_RESTART_MAX_ATTEMPTS 5 Consecutive failed restart cycles before a server enters crashed
OCTAVE_MCP_PROBE_TIMEOUT_SECONDS 5.0 Budget for the manager's confirming ping after a request timeout
OCTAVE_MCP_STABILIZATION_SECONDS 60.0 Time a connection must hold before the failure counter resets

Auth Policy

The auth layer (octave.auth) reads OCTAVE_AUTH_* variables via AuthSettings in backend/src/octave/auth/config.py:

Variable Default Description
OCTAVE_AUTH_COOKIE_SECURE false Set true when the backend is served over HTTPS so the session cookie carries Secure
OCTAVE_AUTH_IDLE_TTL_DAYS 14 Sessions expire after this much inactivity (rolling)
OCTAVE_AUTH_ABSOLUTE_TTL_DAYS 90 Sessions expire this long after creation regardless of activity

Lost owner passwords are recovered out-of-band: uv run python -m octave.auth.cli reset-password <username> --password-file <path> (requires direct DB access on the host; the new password is never passed via argv). Verify any setup with uv run pytest tests/auth.

Frontend Environment

The frontend uses Vite's environment variable convention. Create frontend/.env for local development:

# API URL for non-Docker development
VITE_API_URL=http://localhost:8000

Environment variable rules:

  • Only variables prefixed with VITE_ are exposed to the browser
  • Access in code via import.meta.env.VITE_API_URL
  • In Docker Compose, VITE_API_URL is set to http://localhost:8000

Docker Compose Configuration

docker-compose.yml defines two services:

Backend service:

  • Builds from backend/Dockerfile
  • Exposes port 8000
  • Health check polls /health every 30s (10s timeout, 3 retries, 5s start period)

Frontend service:

  • Builds from frontend/Dockerfile
  • Exposes port 5173
  • Depends on backend being healthy
  • Mounts ./frontend/src for hot-reload

Project Structure

Octave/
├── docker-compose.yml          # Service orchestration
├── backend/
│   ├── Dockerfile              # Backend container
│   ├── pyproject.toml          # Python project config (uv, pytest, ruff, mypy)
│   ├── .python-version         # Python 3.12
│   ├── uv.lock                 # Resolved dependencies
│   ├── src/octave/
│   │   ├── __init__.py
│   │   └── app.py              # FastAPI application
│   └── tests/
│       └── test_health.py      # Backend tests
└── frontend/
    ├── Dockerfile              # Frontend container
    ├── package.json            # Node project config
    ├── tsconfig.json           # TypeScript config
    ├── vite.config.ts          # Vite build + dev server
    ├── vitest.config.ts        # Vitest unit test config
    ├── playwright.config.ts    # Playwright E2E config
    ├── eslint.config.js        # ESLint config
    ├── .prettierrc             # Prettier config
    ├── src/
    │   ├── main.tsx            # React entry point
    │   ├── App.tsx             # Root component
    │   ├── index.css           # Global styles
    │   ├── vite-env.d.ts       # Vite type declarations
    │   └── components/
    │       └── HealthCheck.tsx # Health check UI
    └── tests/
        ├── App.test.tsx        # Unit tests
        └── e2e/
            └── health.spec.ts  # E2E tests

Troubleshooting

Port already in use

# Find what's using a port
lsof -i :8000   # Backend
lsof -i :5173   # Frontend

# Kill the process
kill -9 <PID>

Docker build cache issues

# Clean rebuild
docker compose build --no-cache

Frontend hot-reload not working in Docker

Ensure ./frontend/src:/app/src volume mount is present in docker-compose.yml. The Vite dev server watches this directory.

Backend health check failing in Docker Compose

The health check uses Python's urllib (available in standard library). Verify the /health endpoint responds:

docker compose exec backend python -c "import urllib.request; print(urllib.request.urlopen('http://localhost:8000/health').read())"