Coding Standards
These standards are descriptive first: they document the patterns the code already follows, so new code blends in. Where the codebase is inconsistent, the preferred form is marked.
Backend (Python / FastAPI)
Layering (non-negotiable)
- Routers: HTTP, validation, tool gating, transactions, observability logging. No prompts, no OpenAI calls.
- Services: all LLM/vector logic. No
modelsimports, no HTTP concerns. - One-way imports:
routers → services/schemas/models → database. See Backend Overview.
Endpoint shape
Every handler follows the house pattern:
@router.post("/{agent_id}/thing", response_model=schemas.ThingResponse)
def do_thing(agent_id: int, data: schemas.ThingRequest, db: Session = Depends(get_db)):
agent = db.query(models.Agent).filter(models.Agent.id == agent_id).first()
if agent is None:
raise HTTPException(status_code=404, detail="Agent not found") # guard first
if not agent_has_tool(agent, "thing_tool"): # gate second
raise HTTPException(status_code=403, detail="This agent does not have thing_tool tool enabled")
try:
start = time.time()
result = service_function(...) # work third
latency_ms = int((time.time() - start) * 1000)
log_tool_call(db, agent.id, "thing_tool", ..., latency_ms=latency_ms)
db.commit() # commit once
return {...}
except Exception as e:
log_tool_call(db, agent.id, "thing_tool", success=False, error_message=str(e))
db.commit() # evidence survives
raise HTTPException(status_code=500, detail=str(e))Order: 404 guard → 403 gate → timed work → log → single commit → respond; failures log-then-raise (Error Handling).
Conventions
| Topic | Standard |
|---|---|
| Typing | modern unions (str | None), typed params and returns on services |
| Naming | snake_case functions, PascalCase models/schemas, XCreate/XRequest/XResponse schema suffixes |
| LLM calls | guard the API key, temperature from the ladder, strict-JSON prompts parsed with json.loads + defensive .get() normalization |
| New columns | additive + migration entry |
| Defensive parsing | safe_int/safe_float for string-typed numeric columns |
| Docstrings | short, purpose-level, on services ("""Apply small SQLite schema updates...""") — not restating signatures |
Known inconsistencies (prefer the fix when touching the code): duplicated agent_has_tool/log_tool_call between routers (prefer extracting a shared helper), three separate OpenAI clients (prefer one factory), mixed async def/def handlers (prefer def unless actually awaiting).
Frontend (TypeScript / React / Next.js)
| Topic | Standard |
|---|---|
| Components | server components by default; "use client" only for interactivity (one exists: CreateAgentForm) |
| Props | explicit type XProps = ; no any — unknown data is unknown + narrowing (see getApiErrorMessage) |
| Data | reads: server-side fetch + cache: "no-store" + force-dynamic; writes: route-handler proxy (pattern) |
| Errors | fetch failures become rendered error panels, not thrown-to-boundary (Data Fetching) |
| Styling | design-system tokens only — no raw hex, no arbitrary spacing (Design System) |
| Types mirroring the API | keep page-level response types in sync with backend schemas; npm run typecheck must pass |
| Nullables | format defensively (value ?? "—", Number.isNaN guards) — backend metrics are nullable |
Documentation standards
- Every module/feature change updates its docs page (the Backend pages map 1:1 to modules).
- Internal links are relative
.mdpaths;mkdocs buildmust stay warning-free (Docs Site). - Document reality — limitations and bugs go to Known Issues, not under the rug.
Tooling status
No Python linter/formatter (ruff/black) and no ESLint/Prettier are configured. Until they land (Roadmap), the standard is: match the style of the file you are editing — 4-space Python, 2-space TSX, double quotes in TSX, and the patterns above.