-
Notifications
You must be signed in to change notification settings - Fork 10
feat(test): Implement parallel test execution and uvicorn timeout increase to 180s #1152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
8327b49
3babd72
e6cc373
162d75b
263e22a
4a9682d
460907c
2d3bd1c
1415db8
5236004
763dc1b
cbf846f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,3 +21,6 @@ ENV/ | |
|
|
||
| # | ||
| /backend/app/logs | ||
|
|
||
| .agents/ | ||
| AGENTS.md | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Do not ignore the committed This PR adds Proposed fix .agents/
-AGENTS.md🤖 Prompt for AI Agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| # AGENTS.md | ||
|
|
||
| This file provides guidance to Codex when working with code in this repository. | ||
|
|
||
| ## Project Overview | ||
|
|
||
| Kaapi is an AI platform built with FastAPI and PostgreSQL, containerized with Docker. It provides AI capabilities including OpenAI assistants, fine-tuning, document processing, and collection management. | ||
|
|
||
| ## Key Commands | ||
|
|
||
| ### Development | ||
|
|
||
| ```bash | ||
| # Activate virtual environment | ||
| source .venv/bin/activate | ||
|
|
||
| # Start development server with auto-reload | ||
| fastapi run --reload app/main.py | ||
|
|
||
| # Run pre-commit hooks | ||
| uv run pre-commit run --all-files | ||
|
|
||
| # Generate database migration. | ||
| # Compute <next_rev_id> at runtime as the latest existing revision ID + 1, | ||
| # zero-padded to 3 digits (check the highest NNN in app/alembic/versions/NNN_*.py). | ||
| alembic revision --autogenerate -m "Description" --rev-id <next_rev_id> | ||
|
|
||
| # Seed database with test data | ||
| uv run python -m app.seed_data.seed_data | ||
| ``` | ||
|
|
||
| ### Testing | ||
|
|
||
| Tests use `.env.test` for environment-specific configuration. | ||
|
|
||
| ```bash | ||
| # Run test suite | ||
| uv run bash scripts/tests-start.sh | ||
| ``` | ||
|
Comment on lines
+13
to
+39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Make backend-relative commands explicit. The document places the application under Also applies to: 45-47, 94-98, 126-129 🤖 Prompt for AI Agents |
||
|
|
||
| ## Architecture | ||
|
|
||
| ### Backend Structure | ||
|
|
||
| The backend follows a layered architecture located in `backend/app/`: | ||
|
|
||
| - **Models** (`models/`): SQLModel entities representing database tables and domain objects | ||
|
|
||
| - **CRUD** (`crud/`): Database access layer for all data operations | ||
|
|
||
| - **Routes** (`api/`): FastAPI REST endpoints organized by domain | ||
|
|
||
| - **Core** (`core/`): Core functionality and utilities | ||
| - Configuration and settings | ||
| - Database connection and session management | ||
| - Security (JWT, password hashing, API keys) | ||
| - Cloud storage (`cloud/storage.py`) | ||
| - Document transformation (`doctransform/`) | ||
| - Fine-tuning utilities (`finetune/`) | ||
| - Langfuse observability integration (`langfuse/`) | ||
| - Exception handlers and middleware | ||
|
|
||
| - **Services** (`services/`): Business logic services | ||
| - Response service (`response/`): OpenAI Responses API integration, conversation management, and job execution | ||
|
|
||
| - **Celery** (`celery/`): Asynchronous task processing with RabbitMQ and Redis | ||
| - Task definitions (`tasks/`) | ||
| - Celery app configuration with priority queues | ||
| - Beat scheduler and worker configuration | ||
|
|
||
|
|
||
| ### Authentication & Security | ||
|
|
||
| - JWT-based authentication | ||
| - API key support for programmatic access | ||
| - Organization and project-level permissions | ||
|
|
||
| ## Codebase Knowledge (wiki) | ||
|
|
||
| `docs/wiki/INDEX.md` routes to per-module knowledge pages (routes, tables, models, services per domain) and `docs/wiki/domain-map.md` (entity graph + blast-radius procedure). Load discipline: | ||
|
|
||
| - Starting feature or planning work in a domain → read INDEX + that one `docs/wiki/modules/*.md` page before exploratory greps. | ||
| - Adding a table or config shape → check `domain-map.md` first; an existing entity/flow may already cover it. | ||
| - Deep design rationale → follow the module page's link into `docs/architecture/*.md`; never bulk-load those. | ||
| - **Maintenance rule:** a change to a module's routes/tables/models/services updates that module's wiki page (and `domain-map.md` if entities/edges changed) in the same PR. | ||
|
|
||
| ## Environment Configuration | ||
|
|
||
| The application uses different environment files: | ||
| - `.env` - Application environment configuration (use `.env.example` as template) | ||
| - `.env.test` - Test environment configuration | ||
|
|
||
|
|
||
| ## Testing Strategy | ||
|
|
||
| - Tests located in `app/tests/` | ||
| - Factory pattern for test fixtures | ||
| - Automatic coverage reporting | ||
|
|
||
| ## Code Standards | ||
|
|
||
| - Python 3.11+ with type hints | ||
| - Pre-commit hooks for linting and formatting | ||
|
|
||
| ## Coding Conventions | ||
|
|
||
| Layer conventions live in `.Codex/conventions/{model,crud,service,route,migration,celery}.md` and are applied by the `senior-engineer` subagent; the `test-writer` agent carries its own conventions in `.Codex/agents/*.md`. AGENTS.md only covers rules that apply across every layer. | ||
|
|
||
| ### Cross-cutting rules | ||
|
|
||
| - **Type hints** on every parameter and return value. `-> Any` is not an annotation — narrow it or drop it. | ||
| - **Logging prefix:** every log line starts with the function name in square brackets. | ||
| ```python | ||
| logger.info(f"[function_name] Message | key: {value}") | ||
| ``` | ||
| - **`uv` is the runner**, not `pip`. Examples: `uv run pytest`, `uv run alembic ...`, `uv run pre-commit run --all-files`. | ||
| - **No magic values** in code — extract repeated literals to constants / `Enum` / settings. | ||
| - **Comments explain *why*, not *what*.** Don't restate what the code already says (`i += 1 # increment i`), don't narrate self-evident lines, and don't pad docstrings/migration descriptions with obvious recaps of the operations. A comment earns its place only by adding non-obvious context — rationale, a gotcha, a link, a constraint. When in doubt, delete it; clear code needs fewer comments, not more. | ||
| - **Naming:** `list_*` for plural fetch, `get_*` for singletons; snake_case funcs/vars, PascalCase classes, UPPER_SNAKE constants; `Enum` suffix on enum classes. | ||
| - **Timestamps** are `inserted_at` / `updated_at` (not `created_at`). | ||
|
|
||
| ## Specialist subagents | ||
|
|
||
| When working in a specific layer, the matching agent under `.Codex/agents/` handles the layer's conventions automatically. Pick by layer, or just describe the task and let the main agent route: | ||
|
|
||
| | Agent | Layer | | ||
| |---|---| | ||
| | `senior-engineer` | `app/models/`, `app/crud/`, `app/services/`, `app/api/routes/`, `app/alembic/versions/`, `app/celery/tasks/` — any single-layer edit or a full feature walking the spine plus its migration and Celery task, one context | | ||
| | `test-writer` | `app/tests/` | | ||
|
|
||
| Standardized provider/SDK exception handling is a cross-cutting convention (`.Codex/conventions/error-handling.md`), applied by `senior-engineer` when it writes service/crud call sites — not a separate agent. Convention reviews are handled by the `/pr-review` command, also not a subagent. | ||
|
|
||
| ### Build a feature as a 2-context pipeline | ||
|
|
||
| To keep each context window lean (heavy file I/O degrades performance), **build a multi-layer feature as sequential subagent contexts, not inline.** Launch each phase with the Agent tool — each runs in its own context and returns only a summary, so the orchestrator stays small. The phases are a dependency chain, so run them **in order**, passing only the *artifacts* forward (signatures, file paths), never re-deriving prior reasoning: | ||
|
|
||
| | # | Context | Agent | Consumes | | ||
| |---|---|---|---| | ||
| | 1 | schema + code-spine + migration + Celery task | `senior-engineer` | the feature request | | ||
| | 2 | test | `test-writer` | phase 1's signatures (+ which HTTP boundaries to mock) | | ||
|
|
||
| Then run `/pr-review` on the full diff before committing. | ||
|
|
||
| Rules of thumb: | ||
| - **Run them sequentially**, not in parallel — phase 2 depends on phase 1's signatures. | ||
| - **Single-layer change?** Skip the pipeline; let `senior-engineer` build just the one layer. | ||
| - `senior-engineer` builds the model → crud → service → route spine *and* the migration a schema change needs *and* the Celery task background work needs — all in phase 1, reading the convention docs in `.Codex/conventions/{model,crud,service,route,migration,celery}.md` as the single source of truth for each layer. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -31,7 +31,6 @@ | |
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
||
| # Password hashing configuration | ||
| pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") | ||
|
|
||
| # JWT configuration | ||
|
|
@@ -299,7 +298,9 @@ class APIKeyManager: | |
| KEY_LENGTH = 65 # Total length: 22 (prefix) + 43 (secret) | ||
| HASH_ALGORITHM = "bcrypt" | ||
|
|
||
| pwd_context = CryptContext(schemes=[HASH_ALGORITHM], deprecated="auto") | ||
| pwd_context = CryptContext( | ||
| schemes=[HASH_ALGORITHM], deprecated="auto" | ||
| ) # module-level context, shares the rounds config | ||
|
Comment on lines
+301
to
+303
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
uv run --with passlib==1.7.4 --with bcrypt==4.0.1 python - <<'PY'
from passlib.context import CryptContext
context = CryptContext(schemes=["bcrypt"], deprecated="auto")
generated_hash = context.hash("probe")
rounds = int(generated_hash.split("$")[2])
print(f"Effective bcrypt rounds: {rounds}")
if rounds != 12:
raise SystemExit(f"Unexpected Passlib default rounds: {rounds}")
PYRepository: ProjectTech4DevAI/kaapi-backend Length of output: 209 🏁 Script executed: set -euo pipefail
file="backend/app/core/security.py"
printf '%s\n' '--- changed context ---'
sed -n '260,330p' "$file"
printf '%s\n' '--- all password-context references ---'
rg -n -C 2 '\bpwd_context\b|CryptContext|bcrypt__rounds|HASH_ALGORITHM' "$file"
printf '%s\n' '--- relevant seed usage ---'
sed -n '145,220p' backend/app/tests/seed_data/seed_data.pyRepository: ProjectTech4DevAI/kaapi-backend Length of output: 6974 🏁 Script executed: set -euo pipefail
file="backend/app/core/security.py"
printf '%s\n' '--- module context and password helpers ---'
sed -n '1,215p' "$file"
printf '%s\n' '--- focused diff ---'
git diff --unified=12 -- "$file" | sed -n '1,220p'Repository: ProjectTech4DevAI/kaapi-backend Length of output: 6775 Restore the shared password context and test bcrypt cost.
🤖 Prompt for AI AgentsSources: Learnings, MCP tools |
||
|
|
||
| @classmethod | ||
| def generate(cls) -> tuple[str, str, str]: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -74,6 +74,9 @@ dev-dependencies = [ | |
| "types-passlib<2.0.0.0,>=1.7.7.20240106", | ||
| "coverage<8.0.0,>=7.4.3", | ||
| "pytest-asyncio>=1.0.0", | ||
| "pytest-xdist>=3.8.0", | ||
| "filelock>=3.25.2", | ||
| "pytest-cov>=7.1.0", | ||
| ] | ||
|
|
||
| [build-system] | ||
|
|
@@ -83,6 +86,11 @@ build-backend = "hatchling.build" | |
| [project.scripts] | ||
| ai-cli = "app.cli.main:cli" | ||
|
|
||
| # Keeps coverage.xml paths as "app/..." (relative to backend/) so Codecov | ||
| # can map them to repo files for the per-file view. | ||
| [tool.coverage.run] | ||
| relative_files = true | ||
|
Comment on lines
+89
to
+92
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
python - <<'PY'
from pathlib import Path
import xml.etree.ElementTree as ET
report = Path("backend/coverage.xml")
assert report.is_file(), report
root = ET.parse(report).getroot()
sources = [source.text or "" for source in root.findall(".//source")]
filenames = [
node.attrib["filename"]
for node in root.findall(".//class")
if "filename" in node.attrib
]
assert filenames, "No coverage class paths found"
assert not any(Path(name).is_absolute() for name in filenames), filenames[:10]
assert any(
source.rstrip("/").endswith("/app") or source.rstrip("/") == "app"
for source in sources
) or any(name.startswith("app/") for name in filenames), (sources, filenames[:10])
PYRepository: ProjectTech4DevAI/kaapi-backend Length of output: 279 🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- backend/pyproject.toml ---'
sed -n '1,120p' backend/pyproject.toml
printf '%s\n' '--- backend/scripts/test.sh ---'
cat -n backend/scripts/test.sh
printf '%s\n' '--- codecov.yml ---'
cat -n codecov.ymlRepository: ProjectTech4DevAI/kaapi-backend Length of output: 3754 🌐 Web query:
💡 Result: In coverage.py 7.13.4, the relative_files setting must be configured in a configuration file (such as.coveragerc, pyproject.toml, or setup.cfg) under the [run] section [1]. It cannot be set directly via the command line [1]. While coverage.py provides many command-line options, certain settings—particularly those that fundamentally alter how data is stored or interpreted, like relative_files and source—are designed to be persistent and must be defined in the configuration file [1][2]. To enable relative file path storage, you should add the following to your configuration file [1]: [run] relative_files = True If you need to use a different configuration file than the default, you can specify it using the --rcfile=FILE command-line option or the COVERAGE_RCFILE environment variable [1]. Citations:
🏁 Script executed: #!/usr/bin/env bash
set -euo pipefail
curl -fsSL https://coverage.readthedocs.io/en/7.13.4/config.html |
grep -in -A12 -B8 'relative_files'Repository: ProjectTech4DevAI/kaapi-backend Length of output: 3667 Configure the coverage source in 🤖 Prompt for AI AgentsSource: MCP tools |
||
|
|
||
| [tool.mypy] | ||
| strict = true | ||
| exclude = ["venv", ".venv", "alembic"] | ||
|
|
||
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Restrict the workflow token permissions.
This workflow has no
permissions:block, so the job inherits repository or organization defaults while running tests and third-party actions. Addpermissions: contents: readat workflow or job scope. The shown steps do not require write access.Suggested setting
🧰 Tools
🪛 zizmor (1.29.0)
[warning] 10-88: overly broad permissions (excessive-permissions): default permissions used due to no permissions: block
(excessive-permissions)
🤖 Prompt for AI Agents
Source: Linters/SAST tools