agnes-the-ai-analyst/pyproject.toml
Vojtech c552bf8243
feat(api): enforce API design rules via pytest + fix DELETE/status-code violations (#338)
* feat(api): enforce API design rules via pytest + fix DELETE/status-code violations

Adds tests/test_api_design_rules.py with four forward-only design guardrails
that prevent new endpoints from accumulating REST debt:

  Rule 1 — No new verbs in URL paths (existing 28 grandfathered via allowlist)
  Rule 2 — DELETE must declare 204 No Content (zero allowlist entries)
  Rule 3 — Creator POSTs (path has GET counterpart) must declare 201/202
  Rule 4 — All protected /api/* routes must declare 401 and 403

Fixes found by running the rules:

- DELETE /api/admin/metrics/{metric_id}: return 204, drop redundant body
- DELETE /api/memory/{item_id}/dismiss (undismiss): return 204, drop body
- POST /api/memory/admin/contradictions: add status_code=201 (creates a resource)
- app/main.py: _add_auth_error_responses() injected into app.openapi() at startup;
  declares 401/403 on all protected /api/* operations centrally, fixing the 120
  routes that previously omitted these response codes from the spec.

Closes #337

* fix(api): resolve CI failures — extend 204 fixes + complete allowlists

- Fix remaining 6 DELETE endpoints to return 204: store entities,
  store entity install, marketplace curated install, marketplace plugin
  system flag, admin store submission, and observability view
- Update all affected tests to expect 204 (removed body assertions)
- Add 4 missing verb paths to _VERB_PATH_ALLOWLIST in test_api_design_rules.py
- Add 2 upsert endpoints to _CREATOR_POST_ALLOWLIST
- Update admin_marketplaces.html to not call r.json() on 204 DELETE

* fix(tests): align 2 DELETE-asserting tests with 204 contract (post-#339 rebase)

CI's test-shard (1) and (4) failures on this PR were caused by
Vojta's second commit (`fix(api): resolve CI failures — extend 204
fixes`) flipping more DELETE endpoints to status_code=204 than just
the two mentioned in the PR body. Two tests assert status_code==200
on the DELETE response and broke:

- tests/test_admin_store_submissions.py::TestQuarantineGates::test_admin_can_delete_quarantined
  (DELETE /api/store/entities/{entity_id})
- tests/test_store_api.py::TestInstallCycle::test_admin_hard_delete_cascades_installs
  (DELETE /api/store/entities/{entity_id}?hard=true)

Updated both to assert 204 with a comment pointing at
tests/test_api_design_rules.py rule 2 so future reviewers can
trace the contract. Verified via broader scan that no other test
asserts == 200 on a .delete() response directly (4 other sites do
.delete() then check 200 on a subsequent GET — those are fine).

* release: 0.54.26 — API design rules (test_api_design_rules.py) + 8 DELETE endpoints flip to 204

---------

Co-authored-by: ZdenekSrotyr <zdenek.srotyr@keboola.com>
2026-05-18 15:25:07 +02:00

187 lines
8.2 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[project]
name = "agnes-the-ai-analyst"
version = "0.54.26"
description = "Agnes — AI Data Analyst platform for AI analytical systems"
requires-python = ">=3.11,<3.14"
license = "MIT"
readme = "README.md"
dependencies = [
# Core database
# 1.5.2 fixes a FK-dependency regression that affected ALTER TABLE on
# tables referenced by other tables — broke the test_db migration
# ladder replay on 1.5.1. CI runs 1.5.2; local devs need it too.
"duckdb>=1.5.2",
# Web framework (FastAPI)
"fastapi>=0.115.0",
"uvicorn[standard]>=0.32.0",
"python-multipart>=0.0.27",
"jinja2>=3.1.0",
"starlette>=0.41.0",
# Authentication
"PyJWT>=2.8.0",
"itsdangerous>=2.1.0",
"authlib>=1.6.12",
"argon2-cffi>=23.1.0",
# HTTP client. `h2` enables HTTP/2 multiplexing for the persistent
# CLI client used by `agnes pull` (one TCP connection serves N
# concurrent parquet streams + range chunks). `cli/client.py`
# gracefully falls back to HTTP/1.1 if h2 is missing, so this
# extra is for performance, not correctness.
"httpx>=0.27.0",
"h2>=4.1.0",
# CLI
"typer>=0.12.0",
"rich>=13.0.0",
# Configuration
"python-dotenv>=1.0.0",
"pyyaml>=6.0",
# Data processing
"pandas>=2.0.0",
"pyarrow>=12.0.0",
"pytz>=2024.1",
# SQL parsing — server-side WHERE validator for /api/v2/scan (app/api/where_validator.py)
# Minimum 30.x — older versions had walk() yielding (node, parent, key)
# tuples instead of expression nodes, which would silently bypass the
# WHERE-validator structural checks (isinstance(tuple, exp.Subquery)
# is always False). 30.x yields nodes directly.
"sqlglot>=30.0.0",
# Data source connectors
"google-cloud-bigquery>=3.0.0",
"google-cloud-bigquery-storage>=2.0.0",
# Google Workspace Cloud Identity / Admin SDK (Workspace group membership sync)
"google-api-python-client>=2.0.0",
# Profiler visualizations
"matplotlib>=3.8.0",
"numpy>=1.24.0",
# Claude Code marketplace endpoint — pure-Python git server mounted in FastAPI
"dulwich>=0.22.0",
"a2wsgi>=1.10.0",
# In-process TTL cache for marketplace etag (transitively present via
# google-auth, declared explicitly here because we depend on it directly).
"cachetools>=5.3.0",
# Per-IP rate limiting on auth endpoints (#45). In-process counters by
# default — fine for single-replica deploys. Multi-replica rollouts can
# swap the storage backend via slowapi's `storage_uri` (Redis, Memcached).
"slowapi>=0.1.9",
# LLM provider SDKs — core (not dev) because connectors/llm/*_provider.py
# is imported by services/{corporate_memory, verification_detector} which
# the scheduler drives in production. Promoted from [dev] in #176 to fix
# ModuleNotFoundError boot loops on default Compose deploys.
"anthropic>=0.30.0",
"openai>=1.30.0",
# Keboola Storage API SDK — used by:
# - `connectors/keboola/client.py` for admin-side bucket / table list
# (consumed from `app/api/admin.py` discover-and-register, table
# metadata refresh).
# Extraction itself uses the lightweight `connectors/keboola/storage_api.py`
# module (export-async + signed-URL download) which talks to Storage API
# directly via `requests` — no SDK dependency on the data-path side. The
# SDK stays for the metadata reads.
#
# NOTE: kbcstorage moved to the [server] extra below — see the rationale
# in [project.optional-dependencies].server. CLI wheels installed via
# `uv tool install` deliberately ship without it.
"sse-starlette>=2.0",
# Optional observability — pure-Python, no compilation. Lazily initialized
# in src/observability/posthog_client.py and only emits events when
# POSTHOG_API_KEY is set in the environment. With the key unset the
# integration is fully off (no network, no init). See docs/observability.md.
"posthog>=3.7.0",
# Rust-backed (ammonia) HTML sanitizer for admin-edited rich content
# (news intro + body, curated marketplace-metadata.json descriptions).
# Allowlist-based with per-tag attribute scoping; closes the bypass
# shapes the legacy regex sanitizer in src/welcome_template.py was
# vulnerable to. Pre-built wheels published for all supported
# (mac/linux/windows × arm64/x86_64) targets.
"nh3>=0.2",
# CommonMark markdown renderer for curator-authored marketplace-metadata.json
# rich content (plugin description / sample_interaction.assistant). Pure
# Python, no compilation. Rendered output is funneled through nh3 above.
"markdown-it-py>=3.0",
# Cross-platform advisory file locking for the `agnes push` single-instance
# guard. Wraps fcntl.flock on POSIX and msvcrt.locking on Windows behind
# a uniform API; OS releases the lock automatically on process exit (no
# stale-lock detection required). Used by cli/lib/push_lock.py.
"filelock>=3.13,<4",
# Transitive dependency hardened directly to dodge 5 dependabot advisories
# (4 high, 1 medium) flagged on urllib3<2.7.0: cross-origin sensitive
# header leak on proxied low-level redirects, decompression-bomb bypass
# + unbounded decompression chain on the streaming API, redirects-when-
# retries-disabled. The `[server]` extra below adds kbcstorage which
# transitively caps urllib3<2.0.0; `[tool.uv] override-dependencies`
# forces 2.7+ in workspace installs (Dockerfile + dev). Wheel consumers
# who install only the CLI (`uv tool install <wheel>`) get no kbcstorage
# and no conflict.
"urllib3>=2.7.0",
]
[project.optional-dependencies]
# Server-side connectors. The CLI wheel does NOT need these — analysts who
# `uv tool install` the wheel never reach a kbcstorage import. Splitting it
# out keeps the wheel's METADATA `Requires-Dist` set free of the
# `kbcstorage<=0.9.5 → urllib3<2.0.0` cap that conflicts with our
# `urllib3>=2.7.0` security pin under any fresh resolver context (where
# `[tool.uv] override-dependencies` does NOT apply — see comment on
# [tool.uv] below). Server install pulls it in via Dockerfile's
# `uv pip install --system --no-cache .[server]`.
server = [
"kbcstorage>=0.9.0",
]
observability = [
# Already in base dependencies — listed here so operators who want to
# be explicit can `pip install -e ".[observability]"` and signal intent.
"posthog>=3.7.0",
]
dev = [
"pytest>=9.0.0",
"pytest-timeout>=2.0.0",
"pytest-xdist>=3.0.0",
# pytest-split shards the suite across parallel CI jobs (`--splits N
# --group K`); see the `test-shard` matrix in `.github/workflows/ci.yml`.
# Balanced by the committed `.test_durations` file.
"pytest-split>=0.9.0",
"faker>=24.0.0",
# jsonschema validates the corporate-memory extraction-tool golden fixtures
# under tests/test_corporate_memory_v1.py (extraction.json, correction.json,
# confidence_calibration.json). Production code does not depend on it.
"jsonschema>=4.0.0",
# FastAPI debug toolbar — gated behind DEBUG=1 env var in app/main.py.
# Provides per-request panels (headers, routes, timer, profiling, etc.)
# for local development. Never loaded in production (no DEBUG=1 there).
"fastapi-debug-toolbar>=0.6.3",
]
[project.scripts]
agnes = "cli.main:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["app", "src", "connectors", "cli", "services", "config"]
[tool.ruff]
line-length = 120
target-version = "py313"
[tool.uv]
dev-dependencies = [
"pytest>=9.0.0",
"pytest-timeout>=2.0.0",
"pytest-xdist>=3.0.0",
"pytest-split>=0.9.0",
"faker>=24.0.0",
"anthropic>=0.30.0",
"openai>=1.30.0",
"fastapi-debug-toolbar>=0.6.3",
]
# Override the urllib3<2.0.0 ceiling kbcstorage 0.9.5 declares (upstream
# hasn't relaxed it as of 2026-05-12 but the SDK works fine against
# urllib3 2.x in practice — we only use `Client` + `Tables` from it and
# both go through `requests`, which natively supports both lines). Lets
# the resolver pick a urllib3 line that closes Dependabot advisories
# CVE-2024-37891 / CVE-2025-{xxx}. See `urllib3>=2.7.0` in [project]
# dependencies above for the security rationale.
override-dependencies = ["urllib3>=2.7.0"]