Contributing
Code style
Section titled “Code style”Backend (Python)
Section titled “Backend (Python)”- Python 3.13+ with type hints
- No ORM, raw SQL with
aiosqlite - Async everywhere (
async def,await) - Pydantic models for request/response validation
- Logging via
logging.getLogger(__name__)
Frontend (TypeScript / React)
Section titled “Frontend (TypeScript / React)”- React 18 with functional components and hooks
- TypeScript with strict mode
- Tailwind CSS for styling
- Lucide React for icons
- Vite for building
Architecture guidelines
Section titled “Architecture guidelines”- Keep it simple. No unnecessary abstractions. Raw SQL is preferred over query builders.
- Async by default. All database and HTTP calls should be async.
- No external state. All state lives in SQLite or the filesystem. No Redis, no message queues.
- Single container. Everything runs in one Docker container. External services (Ollama, Claude) are accessed over HTTP.
- Settings editable at runtime. New settings should be persisted to YAML and updatable from the web UI without restart.
Adding a new document type
Section titled “Adding a new document type”The doc_type enum is intentionally small (10 values, one axis: document
format). Specialty information lives on its own column and should not be
added to doc_type. Before adding a new type, check whether the document
format genuinely doesn’t fit one of the existing values.
- Add the new value to
VALID_DOC_TYPESinbackend/asclepius/pipeline/extractor.pyand to_DOC_TYPE_ALIASESif there are obvious LLM-misspellings to catch. - Add the new value to
DOC_TYPE_OPTIONSinfrontend/src/components/document-detail/MetadataEditor.tsxandDOC_TYPESinfrontend/src/components/documents/columns.ts. - Add a color entry in
TYPE_COLORSinfrontend/src/pages/TimelinePage.tsx. - Update the doc_type enum string in the three classification prompts:
classification.yaml,vision_extraction.yaml,extraction_legacy.yaml. - (Optional) If the new type needs its own Phase-2 schema, add
extraction_<key>.yamlunderprompts_data/and register it inPROMPT_REGISTRY+PROMPT_VARIABLE_KEYSinbackend/asclepius/llm/prompt_manager.py. Without an extraction yaml, Phase 2 is skipped for that type and only Phase-1 metadata + summary are stored. - If the type has new data fields, add a migration or table.
Adding a new API endpoint
Section titled “Adding a new API endpoint”- Create or edit the router in the appropriate module under
backend/asclepius/ - Register the router in
backend/asclepius/main.py - Add authentication via
Depends(get_current_user) - Add patient access checks where needed via
check_patient_access() - Document the endpoint in
docs/docs/api-reference/endpoints.md
Adding a new frontend page
Section titled “Adding a new frontend page”- Create a page component in
frontend/src/pages/ - Add the route in
frontend/src/App.tsx - Add a sidebar entry in
frontend/src/components/layout/AppLayout.tsx - Document the feature in the user guide
Git workflow
Section titled “Git workflow”- Direct push to
mainbranch (solo developer project) - Descriptive commit messages
Pre-commit hooks
Section titled “Pre-commit hooks”The repo’s .pre-commit-config.yaml ships hooks for ruff (backend lint + format), prettier (frontend / docs / yaml format), trailing-whitespace / EOL-fixer / yaml-syntax checks, large-file detection, and an openapi-drift check that auto-regenerates frontend/src/openapi.json + frontend/src/api/schema.ts whenever a backend .py file is staged. Install once per clone:
pip install pre-commitpre-commit installWhen the openapi-drift hook detects changes, it stages the regenerated artefacts and aborts the commit with Files were modified by this hook. Re-run git commit and it succeeds with the new artefacts included.
CI runs the same ruff, prettier, and openapi-drift checks on every push, so a PR that skipped the hook still fails the build.
Reporting issues
Section titled “Reporting issues”Open an issue on GitHub with:
- Steps to reproduce
- Expected vs actual behavior
- Relevant logs from
docker compose logs