docs: restructure AGENTS.md into hierarchical knowledge base
This commit is contained in:
parent
157cbc425e
commit
470d49b061
3 changed files with 339 additions and 47 deletions
133
AGENTS.md
133
AGENTS.md
|
|
@ -1,83 +1,122 @@
|
|||
# AGENTS.md
|
||||
|
||||
This file provides guidance to AI coding agents when working with code in this repository.
|
||||
Personal health & skincare data hub with LLM agent integration. Monorepo: Python FastAPI backend + SvelteKit frontend.
|
||||
|
||||
## Repository Structure
|
||||
## Structure
|
||||
|
||||
This is a monorepo with **backend** and **frontend** directories.
|
||||
```
|
||||
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
|
||||
|
||||
Use repository skills when applicable:
|
||||
- `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.
|
||||
|
||||
- `svelte-code-writer`: required for creating, editing, or analyzing `.svelte`, `.svelte.ts`, and `.svelte.js` files.
|
||||
- `frontend-design`: use for frontend UI, page, and component design work.
|
||||
- `conventional-commit`: use when drafting commit messages that follow Conventional Commits.
|
||||
- `gemini-api-dev`: use when implementing Gemini API integrations, multimodal flows, function calling, or model selection details.
|
||||
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.
|
||||
|
||||
When editing frontend code, always follow `docs/frontend-design-cookbook.md` and update it in the same change whenever you introduce or modify reusable UI patterns, visual rules, or shared styling conventions.
|
||||
## Where to Look
|
||||
|
||||
## Commit Guidelines
|
||||
|
||||
This repository uses Conventional Commits (e.g., `feat(api): ...`, `fix(frontend): ...`, `test(models): ...`). Always format commit messages accordingly and ensure you include the correct scope to indicate which part of the monorepo is affected.
|
||||
| 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
|
||||
|
||||
Run the backend from the `backend/` directory:
|
||||
|
||||
```bash
|
||||
# Backend
|
||||
cd backend && uv run python main.py
|
||||
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
|
||||
|
||||
# Linting / formatting
|
||||
cd backend && uv run ruff check .
|
||||
cd backend && uv run black .
|
||||
cd backend && uv run isort .
|
||||
```
|
||||
|
||||
Run the frontend from the `frontend/` directory:
|
||||
|
||||
```bash
|
||||
# Frontend
|
||||
cd frontend && pnpm dev
|
||||
|
||||
# Type checking / linting / formatting
|
||||
cd frontend && pnpm check
|
||||
cd frontend && pnpm lint
|
||||
cd frontend && pnpm format
|
||||
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/
|
||||
```
|
||||
|
||||
No test suite exists yet (backend has some test files but they're not integrated into CI).
|
||||
## Commit Guidelines
|
||||
|
||||
Conventional Commits: `feat(api): ...`, `fix(frontend): ...`, `test(models): ...`. Include scope indicating which part of the monorepo is affected.
|
||||
|
||||
## Architecture
|
||||
|
||||
**innercontext** collects personal health and skincare data and exposes it to an LLM agent.
|
||||
**Backend:** Python 3.12, FastAPI, SQLModel 0.0.37 + SQLAlchemy, Pydantic v2, PostgreSQL (psycopg3), Gemini API (google-genai).
|
||||
|
||||
**Backend Stack:** Python 3.12, SQLModel (0.0.37) + SQLAlchemy, Pydantic v2, FastAPI, PostgreSQL (psycopg3).
|
||||
**Frontend:** SvelteKit 2, Svelte 5 (Runes), TypeScript, Tailwind CSS v4, bits-ui (shadcn-svelte), Paraglide (i18n), svelte-dnd-action, adapter-node.
|
||||
|
||||
**Frontend Stack:** SvelteKit 5, Tailwind CSS v4, bits-ui, inlang/paraglide (i18n), svelte-dnd-action.
|
||||
### Cross-Cutting Patterns
|
||||
|
||||
### Models (`backend/innercontext/models/`)
|
||||
- **Type sharing**: Manual sync between `frontend/src/lib/types.ts` and `backend/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 `.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` |
|
||||
| `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. JSON columns store `inci` (list), `actives` (list of `ActiveIngredient`), `recommended_for`, `targets`, `incompatible_with`, `synergizes_with`, `context_rules`, and `product_effect_profile`. The `to_llm_context()` method returns a token-optimised dict for LLM usage.
|
||||
**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.
|
||||
|
||||
**`ProductInventory`** tracks physical packages (opened status, expiry, remaining weight). One product → many inventory entries.
|
||||
### Deployment
|
||||
|
||||
**`Routine` / `RoutineStep`** record daily AM/PM skincare sessions. A step references either a `Product` or a free-text `action` (e.g. shaving).
|
||||
- **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.
|
||||
|
||||
**`SkinConditionSnapshot`** is a weekly LLM-filled record (skin state, metrics 1–5, active concerns).
|
||||
## Anti-Patterns (this project)
|
||||
|
||||
### Key Conventions
|
||||
|
||||
- All `table=True` models use `Column(DateTime(timezone=True), onupdate=utc_now)` for `updated_at` via raw SQLAlchemy column — do not use plain `Field(default_factory=...)` for auto-update.
|
||||
- List/complex fields stored as JSON use `sa_column=Column(JSON, nullable=...)` pattern (DB-agnostic; not JSONB).
|
||||
- `model_validator(mode="after")` **does not fire** on `table=True` SQLModel instances (SQLModel 0.0.37 + Pydantic v2 bug). Validators in `Product` are present for documentation but are unreliable at construction time.
|
||||
- `backend/skincare.yaml` is a legacy notes file — ignore it, it is not part of the data model and will not be imported.
|
||||
- `_ev()` helper in `product.py` normalises enum values when fields may be raw dicts (as returned from DB) or Python enum instances.
|
||||
- `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_schema` — `AIActiveIngredient` 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).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue