Skip to content

Latest commit

 

History

History
71 lines (53 loc) · 4.5 KB

File metadata and controls

71 lines (53 loc) · 4.5 KB

Sapling

A FastAPI + Supabase backend that ingests student documents, calls Gemini to classify/summarize/extract assignments, and serves a knowledge-graph-backed tutoring chat to a React frontend.

Stack

  • FastAPI: HTTP layer; app + router mounts in backend/main.py.
  • Supabase (PostgREST): primary datastore; accessed via httpx REST through db/connection.py.
  • Gemini (google-genai): current LLM provider; wrapped in services/gemini_service.py (being deprecated).
  • Pydantic AI: target agent framework; not yet in requirements.txt, agents will live under backend/agents/.
  • React frontend: lives in frontend/ (out of scope for backend sessions).
  • pytest: backend test runner, fixtures in tests/conftest.py.

Repo map

  • backend/main.py:84 — FastAPI app, CORS, and every router mount (mounts at :139).
  • backend/routes/documents.py:181 — _process_document single-call classify/summarize/extract (refactor target #1).
  • backend/routes/documents.py:577 — upload_document POST /api/documents/upload pipeline.
  • backend/routes/learn.py:239 — build_system_prompt for the streaming tutor (SSE).
  • backend/routes/quiz.py:1 — quiz session create/answer/score endpoints.
  • backend/routes/notes.py:31 — /api/notes notetaker CRUD, concept link/unlink, and agent actions (summarize/extract-concepts/chat/send-to-tutor/generate-quiz).
  • backend/routes/auth.py:1 — Google OAuth + HMAC session token issuance.
  • backend/services/gemini_service.py:64 — call_gemini plain-text call (LLM seam being deprecated).
  • backend/services/gemini_service.py:135 — call_gemini_json JSON-mode helper used by document/quiz prompts.
  • backend/services/notes_service.py:45 — notes CRUD with column encryption (create_note/update_note/save_summary/link_concept).
  • backend/services/graph_service.py:375 — apply_graph_update (becomes a Pydantic AI tool).
  • backend/services/extraction_service.py:1 — OCR engine router (Docling / GOT-OCR / Tesseract).
  • backend/services/auth_guard.py:1 — require_self / require_admin FastAPI dependencies.
  • backend/agents/note_summary.py, note_concepts.py, note_chat.py — Pydantic AI agents backing the /api/notes agent actions (model slots in agents/_providers.py).
  • backend/db/connection.py:102 — table() factory; the only sanctioned Supabase entry point.

Commands

Backend (run from backend/, with .env populated from .env.example):

python main.py                  # uvicorn on PORT (see config.py), reload=True
python -m pytest tests/ -q      # backend test suite

Docker (full stack from repo root):

docker-compose up

Lint (run from backend/):

ruff check .                    # lint, gated in CI against the ruff.toml baseline (#193)
ruff format .                   # formatter — available, not yet CI-gated (see ruff.toml)

Conventions

  • All Supabase access goes through db/connection.py::table(). Do not instantiate httpx clients or import supabase directly elsewhere.
  • All current LLM calls route through services/gemini_service.py (call_gemini, call_gemini_json, call_gemini_multiturn). New LLM-driven code should be written as Pydantic AI agents in backend/agents/ rather than extending gemini_service.py.
  • Knowledge-graph mutations go through services/graph_service.py::apply_graph_update — routes never write graph_nodes/graph_edges directly.
  • Backend tests live in backend/tests/ and run via pytest; shared fixtures (mock Supabase, mock Gemini) are in tests/conftest.py.
  • Routers are mounted in main.py with /api/<name> prefixes; new routes follow that pattern.

Pointers

  • For architectural decisions, see docs/decisions/ (read the latest 3).
  • For things that didn't work, see docs/attempts/.
  • For the current architecture overview, see docs/architecture.md.
  • For agent-building patterns, run /sync-context at session start.

Gotchas

  • Column-level encryption is on for sensitive columns (users.name/first_name/last_name/bio/location, Google OAuth tokens, messages.content, room_messages.text, sessions.summary_json, documents.summary + concept_notes, notes.title/body/last_summary, gradebook + calendar assignment notes/points). Helpers live in backend/services/encryption.py; use encrypt_if_present at write boundaries and decrypt_if_present / decrypt_numeric at read boundaries (including before injecting into AI prompts). ENCRYPTION_KEY must be set (32 bytes as 64 hex chars; generate via python -c "import secrets; print(secrets.token_hex(32))").