innercontext/AGENTS.md
Piotr Oleszczyk e29d62f949 feat(frontend): auto-generate TypeScript types from backend OpenAPI schema
Replace manually maintained types in src/lib/types.ts with auto-generated
types from FastAPI's OpenAPI schema using @hey-api/openapi-ts. The bridge
file re-exports generated types with renames, Require<> augmentations for
fields that are optional in the schema but always present in responses, and
manually added relationship fields excluded from OpenAPI.

- Add openapi-ts.config.ts and generate:api npm script
- Generate types into src/lib/api/generated/types.gen.ts
- Rewrite src/lib/types.ts as bridge with re-exports and augmentations
- Fix null vs undefined mismatches in consumer components
- Remove unused manual type definitions from api.ts
- Update AGENTS.md docs with type generation workflow
2026-03-12 09:17:40 +01:00

7.3 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.js files.
  • 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 backend/innercontext/models/pnpm generate:apifrontend/src/lib/types.ts Auto-generated from OpenAPI; bridge file may need augmentation

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/
cd frontend && pnpm generate:api              # Regenerate types from backend OpenAPI

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: Auto-generated from backend OpenAPI schema via @hey-api/openapi-ts. Run cd frontend && pnpm generate:api after backend model changes. src/lib/types.ts is a bridge file with re-exports, renames, and Require<> augmentations. See frontend/AGENTS.md § Type 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 .detail field → FlashMessages or StructuredErrorDisplay.
  • LLM validation errors: Non-blocking (HTTP 200). Returned in validation_warnings field. 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.sh pushes via SSH to LXC host. Capistrano-style timestamped releases with current symlink. Auto-rollback on health check failure.
  • Services: 3 systemd units — backend (uvicorn :8000), frontend-node (:3000), pricing-worker.
  • Env: Backend .env has DATABASE_URL + GEMINI_API_KEY. Frontend PUBLIC_API_BASE set at build time.

Anti-Patterns (this project)

  • model_validator(mode="after") does NOT fire on table=True SQLModel instances (SQLModel 0.0.37 + Pydantic v2 bug). Validators in Product are documentation only.
  • Never use plain Field(default_factory=...) for updated_at — must use sa_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_schemaAIActiveIngredient overrides with int + # type: ignore[assignment].
  • backend/skincare.yaml is legacy notes — ignore, not part of data model.
  • ESLint rule svelte/no-navigation-without-resolve has ignoreGoto: true workaround (upstream bug sveltejs/eslint-plugin-svelte#1327).
  • _ev() helper in product.py normalises 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).