Skip to content
3 min read · 537 words

Backend Overview

The backend is a layered FastAPI service in backend/app/: routers validate and orchestrate, services own all AI logic, SQLAlchemy models persist, and every automated step is logged.

Module map

Rendering diagram…

Layering rules

  1. Routers may import schemas, models, and services. They own HTTP concerns, tool gating, transactions, and observability logging.
  2. Services may import other services but never routers or models — they are pure logic over inputs (the one exception: document_service owns its Chroma client as module state).
  3. Models import only database.Base.
  4. Imports are top-level (from database import get_db), which is why the server must run from backend/app/ — see Installation.

Page directory

PageModuleOne-line summary
Application Entrymain.pyTable creation, migrations, router mounting, health checks
Database Layerdatabase.pyEngine, SessionLocal, get_db(), startup migrations
ORM Modelsmodels.pyThe 9 tables and their relationships
Schemasschemas.pyEvery Pydantic request/response shape
Routers: Agentsrouters/agents.py15 endpoints — the platform's core API
Routers: Documentsrouters/documents.pyDocument upload, listing, search, RAG ask
Service: OpenAIservices/openai_service.pyAll direct LLM calls and their prompts
Service: Multi-Agent Emailservices/multi_agent_email_service.pyThe three email-pipeline agents
Service: RAG Graphservices/rag_graph_service.pyThe LangGraph corrective RAG graph
Service: Documentsservices/document_service.pyChunking, embeddings, vector store
Service: Monitoringservices/monitoring_service.pyToken and cost estimation
Observabilitycross-cuttingThe ToolCall/AgentRun/WorkflowRun contract
Error Handlingcross-cuttingFailure modes, status codes, degradation strategy

Conventions you must keep

Every automated/LLM step is logged as a ToolCall (with model_name, estimated_tokens, estimated_cost, latency_ms when it involved an LLM) and, when part of a chat, linked to an AgentRun via agent_run_id. The dashboard and UI depend on this. Full spec: Observability.

Endpoints that execute a tool must call agent_has_tool(agent, "<tool>") first and return 403 when disabled. Canonical tool names: Tools.

New columns on existing models need a matching entry in run_startup_migrations() — there is no Alembic. How-to.

  • Architecture — the system this backend implements
  • API Reference — the endpoints by HTTP contract rather than by module
  • Guides — extending the backend safely