Skip to content

Commit 887bb70

Browse files
authored
Merge pull request #471 from nanotaboada/feat/optimize-ai-agent-instructions
docs: optimize AI agent instructions for token efficiency
2 parents bce5022 + d665986 commit 887bb70

File tree

2 files changed

+449
-198
lines changed

2 files changed

+449
-198
lines changed

.github/copilot-instructions.md

Lines changed: 54 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1,222 +1,78 @@
1-
# Copilot Instructions for python-samples-fastapi-restful
1+
# GitHub Copilot Instructions
22

3-
## Project Overview
3+
> **⚡ Token Efficiency Note**: This is a minimal pointer file (~500 tokens, auto-loaded by Copilot).
4+
> For complete operational details, reference: `#file:AGENTS.md` (~2,500 tokens, loaded on-demand)
5+
> For specialized knowledge, use: `#file:SKILLS/<skill-name>/SKILL.md` (loaded on-demand when needed)
46
5-
This is a RESTful API proof of concept built with **Python 3.13** and **FastAPI**. The application manages football player data with full CRUD operations, featuring async SQLAlchemy ORM, in-memory caching, and SQLite database storage.
7+
## 🎯 Quick Context
68

7-
## Tech Stack
9+
**Project**: FastAPI REST API demonstrating modern Python async patterns
10+
**Stack**: Python 3.13 • FastAPI • SQLAlchemy (async) • SQLite • Docker • pytest
11+
**Pattern**: Routes → Services → Database (layered architecture)
12+
**Philosophy**: Learning-focused PoC emphasizing async/await and type safety
813

9-
- **Framework**: FastAPI 0.123.0 with standard dependencies
10-
- **Database**: SQLite with async support (`aiosqlite 0.21.0`)
11-
- **ORM**: SQLAlchemy 2.0.44 (async)
12-
- **Caching**: aiocache 0.12.3 (SimpleMemoryCache)
13-
- **Testing**: pytest 9.0.1, pytest-cov 7.0.0, pytest-sugar 1.1.1, gevent 25.9.1
14-
- **Linting**: flake8 7.3.0, black 25.11.0
15-
- **Python Version**: 3.13.3 (see `.python-version`)
16-
- **Server**: uvicorn (included in FastAPI standard dependencies)
17-
- **Container**: Docker with multi-stage builds, Docker Compose
14+
## 📐 Core Conventions
1815

19-
## Project Structure
16+
- **Naming**: snake_case for functions/variables, PascalCase for classes
17+
- **Type Hints**: Mandatory throughout (enforced by mypy if enabled)
18+
- **Async**: All I/O operations use `async`/`await`
19+
- **Testing**: pytest with fixtures and async support
20+
- **Formatting**: black (opinionated), flake8 (linting)
2021

21-
```
22-
├── main.py # FastAPI app entry point, lifespan handler, router registration
23-
├── databases/
24-
│ └── player_database.py # Async engine, sessionmaker, Base, session generator
25-
├── models/
26-
│ └── player_model.py # Pydantic models for API request/response validation
27-
├── schemas/
28-
│ └── player_schema.py # SQLAlchemy ORM table schema definitions
29-
├── routes/
30-
│ ├── player_route.py # Player CRUD endpoints with caching
31-
│ └── health_route.py # Health check endpoint
32-
├── services/
33-
│ └── player_service.py # Async database CRUD operations
34-
├── tests/
35-
│ ├── conftest.py # pytest fixtures (TestClient)
36-
│ ├── test_main.py # Test suite for all endpoints
37-
│ └── player_stub.py # Test data stubs
38-
├── storage/ # SQLite database file (seeded)
39-
├── scripts/
40-
│ ├── entrypoint.sh # Docker entrypoint for DB initialization
41-
│ └── healthcheck.sh # Docker health check script
42-
└── postman_collections/ # Postman collection for API testing
43-
```
44-
45-
## Key Architectural Patterns
46-
47-
1. **Layered Architecture**: Routes → Services → Database
48-
2. **Dependency Injection**: `AsyncSession` via `Depends(generate_async_session)`
49-
3. **Pydantic for Validation**: `PlayerModel` with camelCase aliasing (`to_camel`)
50-
4. **SQLAlchemy ORM**: `Player` schema mapped to `players` table
51-
5. **Caching**: In-memory cache (10 min TTL) with `X-Cache` headers (HIT/MISS)
52-
6. **Async/Await**: All database operations are async
53-
54-
## Coding Guidelines
22+
## 🏗️ Architecture at a Glance
5523

56-
### Python Style (Strict Enforcement)
57-
58-
- **Formatter**: Black (line length: 88, target: Python 3.13)
59-
- **Linter**: flake8 (max-complexity: 10, ignores: E203, W503)
60-
- **Run Before Commit**: `black .` and `flake8`
61-
- **Imports**: SQLAlchemy 2.0+ style (use `select()` not legacy `Query`)
62-
- **Docstrings**: Google-style docstrings for all modules, classes, and functions
63-
- **Type Hints**: Use type annotations for function parameters and return values
24+
```text
25+
Route → Service → Database
26+
↓ ↓
27+
Cache Session
28+
```
6429

65-
### File Exclusions
30+
- **Routes**: FastAPI endpoints with dependency injection
31+
- **Services**: Async database operations via SQLAlchemy
32+
- **Database**: SQLite with async support (`aiosqlite`)
33+
- **Models**: Pydantic for validation, SQLAlchemy for ORM
34+
- **Cache**: aiocache SimpleMemoryCache (TTL: 600s / 10 min)
6635

67-
Black and flake8 exclude:
68-
- `.venv`, `.git`, `.github`, `.pytest_cache`, `__pycache__`
69-
- `assets/`, `htmlcov/`, `postman_collections/`, `scripts/`, `storage/`
70-
- Exception: `tests/test_main.py` allows E501 (long lines for test names)
36+
## ✅ Copilot Should
7137

72-
### Commit Conventions
38+
- Generate idiomatic async FastAPI code with proper type hints
39+
- Use SQLAlchemy async APIs (`select()`, `scalars()`, `session.commit()`)
40+
- Follow dependency injection pattern with `Depends()`
41+
- Write tests with pytest async fixtures
42+
- Apply Pydantic models for request/response validation
43+
- Use structured logging (avoid print statements)
44+
- Implement proper HTTP status codes and responses
7345

74-
Follow **Conventional Commits** (enforced by commitlint):
75-
- `feat:` for new features
76-
- `fix:` for bug fixes
77-
- `chore:` for maintenance/tooling
78-
- Max header length: 80 characters
79-
- Max body line length: 80 characters
46+
## 🚫 Copilot Should Avoid
8047

81-
## Common Commands
48+
- Synchronous database operations
49+
- Mixing sync and async code
50+
- Missing type hints on functions
51+
- Using `print()` instead of logging
52+
- Creating routes without caching consideration
53+
- Ignoring Pydantic validation
8254

83-
### Local Development
55+
## ⚡ Quick Commands
8456

8557
```bash
86-
# Install dependencies
87-
pip install -r requirements.txt
88-
pip install -r requirements-lint.txt
89-
pip install -r requirements-test.txt
90-
91-
# IMPORTANT: Activate virtual environment before running commands
92-
source .venv/bin/activate
93-
94-
# Start server (auto-reload on port 9000)
95-
uvicorn main:app --reload --port 9000
96-
97-
# Access interactive API docs
98-
# http://localhost:9000/docs
58+
# Run with hot reload
59+
uvicorn main:app --reload --host 0.0.0.0 --port 9000
9960

100-
# Format code (must run from venv)
101-
black .
61+
# Test with coverage
62+
pytest --cov=. --cov-report=term-missing
10263

103-
# Lint code (must run from venv)
104-
flake8 .
105-
106-
# Run tests
107-
pytest -v
108-
109-
# Run tests with coverage
110-
pytest --cov=./ --cov-report=xml --cov-report=term
111-
```
112-
113-
### Docker
114-
115-
```bash
116-
# Build image
117-
docker compose build
118-
119-
# Start app (initializes DB from seed on first run)
64+
# Docker
12065
docker compose up
12166

122-
# Stop app
123-
docker compose down
124-
125-
# Reset database (removes volume)
126-
docker compose down -v
67+
# Swagger: http://localhost:9000/docs
12768
```
12869

129-
## Database Details
130-
131-
- **Path**: Controlled by `STORAGE_PATH` env var (default: `./storage/players-sqlite3.db`)
132-
- **Docker Volume**: Persistent volume at `/storage/` in container
133-
- **Initialization**: On first Docker run, `entrypoint.sh` copies seed DB from `/app/hold/` to `/storage/`
134-
- **Schema**: Single `players` table with columns: id (PK), firstName, middleName, lastName, dateOfBirth, squadNumber (unique), position, abbrPosition, team, league, starting11
135-
136-
## API Endpoints
137-
138-
| Method | Path | Description | Cache |
139-
|--------|-------------------------------------|------------------------------|-------|
140-
| GET | `/health` | Health check | No |
141-
| GET | `/players/` | Get all players | Yes |
142-
| GET | `/players/{player_id}` | Get player by ID | No |
143-
| GET | `/players/squadnumber/{squad_number}` | Get player by squad number | No |
144-
| POST | `/players/` | Create new player | Clears|
145-
| PUT | `/players/{player_id}` | Update existing player | Clears|
146-
| DELETE | `/players/{player_id}` | Delete player | Clears|
147-
148-
**Cache Notes**:
149-
- Cache key: `"players"`, TTL: 600s (10 min)
150-
- Cache is cleared on POST/PUT/DELETE operations
151-
- Response header `X-Cache: HIT` or `MISS` indicates cache status
152-
153-
## Testing
154-
155-
- **Framework**: pytest with `TestClient` from FastAPI
156-
- **Fixture**: `client` fixture in `conftest.py` (function scope for test isolation)
157-
- **Coverage Target**: 80% (configured in `codecov.yml`)
158-
- **Test Data**: Use stubs from `tests/player_stub.py`
159-
- **Warnings**: DeprecationWarning from httpx is suppressed in conftest
160-
161-
## CI/CD Pipeline
162-
163-
GitHub Actions workflow (`.github/workflows/python-app.yml`):
164-
1. **Lint Job**: Commitlint → Flake8 → Black (check mode)
165-
2. **Test Job**: pytest with coverage report generation
166-
3. **Coverage Job**: Upload to Codecov and Codacy (only for same-repo PRs)
167-
168-
**All PRs must pass CI checks before review.**
169-
170-
## Common Pitfalls & Solutions
171-
172-
1. **Virtual Environment**: Always activate `.venv` before running black, flake8, or pytest:
173-
```bash
174-
source .venv/bin/activate
175-
```
176-
177-
2. **FastAPI Route Ordering**: Static routes MUST be defined before dynamic path parameters. Place `/players/statistics` before `/players/{player_id}`, or FastAPI will try to parse "statistics" as a player_id.
178-
```python
179-
# CORRECT order:
180-
@api_router.get("/players/statistics") # Static route first
181-
@api_router.get("/players/{player_id}") # Dynamic route after
182-
```
183-
184-
3. **SQLAlchemy 2.0 Migration**: Use `select()` not `session.query()`. Example:
185-
```python
186-
statement = select(Player).where(Player.id == player_id)
187-
result = await async_session.execute(statement)
188-
```
189-
190-
4. **Async Session Usage**: Always use `Depends(generate_async_session)` in routes, never create sessions manually.
191-
192-
5. **Cache Invalidation**: Remember to call `await simple_memory_cache.clear(CACHE_KEY)` after mutations (POST/PUT/DELETE).
193-
194-
6. **Pydantic Model Conversion**: Use `player_model.model_dump()` to convert Pydantic to dict for SQLAlchemy:
195-
```python
196-
player = Player(**player_model.model_dump())
197-
```
198-
199-
7. **Database Path in Docker**: Use `STORAGE_PATH` env var, not hardcoded paths.
200-
201-
8. **Port Conflicts**: Default port is 9000. If occupied, use `--port` flag with uvicorn.
202-
203-
## VS Code Configuration
204-
205-
Recommended extensions (`.vscode/extensions.json`):
206-
- `ms-python.python`, `ms-python.flake8`, `ms-python.black-formatter`
207-
- `github.vscode-pull-request-github`, `github.vscode-github-actions`
208-
- `ms-azuretools.vscode-containers`, `sonarsource.sonarlint-vscode`
70+
## 📚 Need More Detail?
20971

210-
Settings (`.vscode/settings.json`):
211-
- Auto-format on save with Black
212-
- Pytest enabled (not unittest)
213-
- Flake8 integration with matching CLI args
214-
- Editor ruler at column 88
72+
**For operational procedures**: Load `#file:AGENTS.md`
73+
**For Docker expertise**: *(Planned)* `#file:SKILLS/docker-containerization/SKILL.md`
74+
**For testing patterns**: *(Planned)* `#file:SKILLS/testing-patterns/SKILL.md`
21575

216-
## Additional Resources
76+
---
21777

218-
- **Postman Collection**: `postman_collections/python-samples-fastapi-restful.postman_collection.json`
219-
- **Architecture Diagram**: `assets/images/structure.svg`
220-
- **FastAPI Docs**: https://fastapi.tiangolo.com/
221-
- **SQLAlchemy 2.0**: https://docs.sqlalchemy.org/en/20/
222-
- **Conventional Commits**: https://www.conventionalcommits.org/
78+
💡 **Why this structure?** Copilot auto-loads this file on every chat (~500 tokens). Loading `AGENTS.md` or `SKILLS/` explicitly gives you deep context only when needed, saving 80% of your token budget!

0 commit comments

Comments
 (0)