6.9 KiB
AGENTS.md
Personal health & skincare data hub with LLM agent integration. Monorepo: Python FastAPI backend + SvelteKit frontend.
Structure
innercontext/
├── backend/ # Python 3.12, FastAPI, SQLModel, PostgreSQL, Gemini
│ ├── innercontext/ # Main package
│ │ ├── api/ # 7 FastAPI routers + LLM endpoints
│ │ ├── models/ # SQLModel tables + Pydantic types (12 files)
│ │ ├── validators/# LLM response validators (6 validators)
│ │ ├── services/ # FX rates (NBP API), pricing jobs
│ │ └── workers/ # Background pricing worker
│ ├── tests/ # pytest (171 tests, SQLite in-memory)
│ ├── alembic/ # DB migrations (17 versions)
│ ├── main.py # App entry, lifespan, CORS, router registration
│ └── db.py # Engine, get_session(), create_db_and_tables()
├── frontend/ # SvelteKit 2, Svelte 5, Tailwind v4, bits-ui
│ └── src/
│ ├── routes/ # File-based routing (15+ pages)
│ ├── lib/ # API client, types, components, i18n
│ └── app.css # Theme + editorial design system
├── docs/ # Deployment guides + frontend-design-cookbook.md
├── nginx/ # Reverse proxy (strips /api prefix → backend:8000)
├── systemd/ # 3 units: backend, frontend-node, pricing-worker
├── scripts/ # Health checks, backups, env validation
└── deploy.sh # Push-based deploy (Capistrano-style symlinked releases)
Agent Skills
svelte-code-writer: REQUIRED for.svelte,.svelte.ts,.svelte.jsfiles.frontend-design: Frontend UI, page, and component design work.conventional-commit: Commit messages following Conventional Commits.gemini-api-dev: Gemini API integrations, multimodal, function calling, structured output.
When editing frontend code, follow docs/frontend-design-cookbook.md and update it when introducing or modifying reusable UI patterns, visual rules, or shared styling conventions.
Where to Look
| Task | Location | Notes |
|---|---|---|
| Add API endpoint | backend/innercontext/api/ |
Follow router pattern, use get_or_404() |
| Add/modify model | backend/innercontext/models/ |
See backend/AGENTS.md for JSON col + timestamp conventions |
| Add DB migration | backend/alembic/ |
cd backend && uv run alembic revision --autogenerate -m "desc" |
| Add frontend page | frontend/src/routes/ |
+page.svelte + +page.server.ts (load + actions) |
| Add component | frontend/src/lib/components/ |
Use bits-ui primitives, check design cookbook |
| Add LLM feature | backend/innercontext/api/ + llm.py |
call_gemini() or call_gemini_with_function_tools() |
| Add LLM validator | backend/innercontext/validators/ |
Extend BaseValidator, return ValidationResult |
| Add i18n strings | frontend/messages/{en,pl}.json |
Auto-generates to src/lib/paraglide/ |
| Modify design system | frontend/src/app.css + docs/frontend-design-cookbook.md |
Update both in same change |
| Modify types | frontend/src/lib/types.ts + backend/innercontext/models/ |
Manual sync, no codegen |
Commands
# Backend
cd backend && uv run python main.py # Start API server
cd backend && uv run ruff check . # Lint
cd backend && uv run black . # Format
cd backend && uv run isort . # Sort imports
cd backend && uv run pytest # Run tests
# Frontend
cd frontend && pnpm dev # Dev server (API proxied to :8000)
cd frontend && pnpm check # Type check + Svelte validation
cd frontend && pnpm lint # ESLint
cd frontend && pnpm format # Prettier
cd frontend && pnpm build # Production build → build/
Commit Guidelines
Conventional Commits: feat(api): ..., fix(frontend): ..., test(models): .... Include scope indicating which part of the monorepo is affected.
Architecture
Backend: Python 3.12, FastAPI, SQLModel 0.0.37 + SQLAlchemy, Pydantic v2, PostgreSQL (psycopg3), Gemini API (google-genai).
Frontend: SvelteKit 2, Svelte 5 (Runes), TypeScript, Tailwind CSS v4, bits-ui (shadcn-svelte), Paraglide (i18n), svelte-dnd-action, adapter-node.
Cross-Cutting Patterns
- Type sharing: Manual sync between
frontend/src/lib/types.tsandbackend/innercontext/models/. No code generation. - API proxy: Frontend server-side uses
PUBLIC_API_BASE(http://localhost:8000). Browser uses/api(nginx strips prefix → backend). - Auth: None. Single-user personal system.
- Error flow: Backend
HTTPException(detail=...)→ Frontend catches.detailfield →FlashMessagesorStructuredErrorDisplay. - LLM validation errors: Non-blocking (HTTP 200). Returned in
validation_warningsfield. Frontend parses semicolon-separated strings into list.
Models
| File | Tables |
|---|---|
product.py |
products, product_inventory |
health.py |
medication_entries, medication_usages, lab_results |
routine.py |
routines, routine_steps, grooming_schedules |
skincare.py |
skin_condition_snapshots |
profile.py |
user_profiles |
pricing.py |
pricing_recalc_jobs |
ai_log.py |
ai_call_logs |
Product is the core model with JSON columns for inci, actives, recommended_for, targets, product_effect_profile, and context_rules. to_llm_context() returns a token-optimised dict for LLM usage.
Deployment
- CI: Forgejo (
.forgejo/workflows/), manual trigger only. - Deploy:
deploy.shpushes via SSH to LXC host. Capistrano-style timestamped releases withcurrentsymlink. Auto-rollback on health check failure. - Services: 3 systemd units — backend (uvicorn :8000), frontend-node (:3000), pricing-worker.
- Env: Backend
.envhasDATABASE_URL+GEMINI_API_KEY. FrontendPUBLIC_API_BASEset at build time.
Anti-Patterns (this project)
model_validator(mode="after")does NOT fire ontable=TrueSQLModel instances (SQLModel 0.0.37 + Pydantic v2 bug). Validators in Product are documentation only.- Never use plain
Field(default_factory=...)forupdated_at— must usesa_column=Column(DateTime(timezone=True), onupdate=utc_now). - JSON columns use
sa_column=Column(JSON, nullable=...)— NOT JSONB. DB-agnostic. - Gemini API rejects int-enum in
response_schema—AIActiveIngredientoverrides withint+# type: ignore[assignment]. backend/skincare.yamlis legacy notes — ignore, not part of data model.- ESLint rule
svelte/no-navigation-without-resolvehasignoreGoto: trueworkaround (upstream bug sveltejs/eslint-plugin-svelte#1327). _ev()helper inproduct.pynormalises enum values when fields may be raw dicts (from DB) or Python enum instances.- No frontend tests exist. Backend tests use SQLite in-memory (not PostgreSQL).