[build-system] requires = ["hatchling"] build-backend = "hatchling.build" [project] name = "contract-check" version = "0.1.0" description = "AI-скрининг рисков в договорах (PDF/DOCX) для СНГ — ГК РФ / ГК РБ. Event-driven production app." readme = "README.md" requires-python = ">=3.13" license = { text = "Proprietary" } authors = [{ name = "Контракт-чек" }] keywords = ["legal", "contracts", "llm", "risk-screening", "fastapi", "rabbitmq"] # Core deps imported by EVERY service image (including the lean bot adapter). # Keep this set minimal — service-specific libs go in dependency-groups below # so each Docker image only installs what it needs. dependencies = [ "pydantic>=2.7", "pydantic-settings>=2.3", "structlog>=24.1", "python-dotenv>=1.0", "httpx[http2]>=0.27", ] [project.scripts] # Stage-0 benchmark CLI (standalone, kept). Production services have their own # entrypoints via `python -m contract_check.`. contract-check = "contract_check.prototype:main" [tool.hatch.build.targets.wheel] packages = ["src/contract_check"] # ───────────────────────────────────────────────────────────────────────────── # Dependency groups (PEP 735). Docker images: `uv sync --frozen --no-default-groups # --group `. The project deps above (core) are always installed on top. # # api → core + db/mq/s3/obs + fastapi/uvicorn # worker-extract → core + db/mq/s3/obs + pymupdf/tesseract # worker-analyze → core + db/mq/s3/obs + otel-httpx # bot → core + aiogram (leanest image: no db/mq/s3/llm/tesseract) # prototype → core + pymupdf/python-docx (benchmark CLI) # dev → everything needed to lint/typecheck/test locally # ───────────────────────────────────────────────────────────────────────────── [dependency-groups] db = [ "sqlalchemy>=2.0", "asyncpg>=0.29", "alembic>=1.13", ] mq = [ "aio-pika>=9.4", ] s3 = [ "minio>=7.2", ] obs = [ "prometheus-client>=0.20", "sentry-sdk>=2", "opentelemetry-sdk>=1.24", "opentelemetry-exporter-otlp>=1.24", ] api = [ { include-group = "db" }, { include-group = "mq" }, { include-group = "s3" }, { include-group = "obs" }, "fastapi>=0.110", "uvicorn[standard]>=0.29", "python-multipart>=0.0.9", "redis>=5.0", "pyjwt[crypto]>=2.8", "argon2-cffi>=23.1", "email-validator>=2.1", "opentelemetry-instrumentation-fastapi>=0.45b0", "opentelemetry-instrumentation-asgi>=0.45b0", "opentelemetry-instrumentation-httpx>=0.45b0", ] extract = [ { include-group = "db" }, { include-group = "mq" }, { include-group = "s3" }, { include-group = "obs" }, "pymupdf>=1.24", "pytesseract>=0.3.10", "pillow>=10", ] analyze = [ { include-group = "db" }, { include-group = "mq" }, { include-group = "s3" }, { include-group = "obs" }, "opentelemetry-instrumentation-httpx>=0.45b0", ] bot = [ "aiogram>=3.4", ] notify = [ { include-group = "db" }, { include-group = "mq" }, { include-group = "obs" }, "aiosmtplib>=3.0", ] prototype = [ "pymupdf>=1.24", "python-docx>=1.1", ] dev = [ { include-group = "api" }, { include-group = "extract" }, { include-group = "analyze" }, { include-group = "bot" }, { include-group = "notify" }, { include-group = "prototype" }, "pytest>=8", "pytest-asyncio>=0.23", "respx>=0.21", "ruff>=0.5", "mypy>=1.10", "anyio>=4", "aiosqlite>=0.20", "testcontainers[rabbitmq,postgres,minio]>=4", "asgi-lifespan>=2.1.0", "pre-commit>=4.6.2", ] [tool.ruff] line-length = 100 target-version = "py313" src = ["src", "tests"] [tool.ruff.lint] select = ["E4", "E7", "E9", "F", "I", "UP", "B"] [tool.mypy] python_version = "3.13" strict = true packages = ["contract_check"] # Third-party libs without complete type stubs. Our own code stays fully typed. disallow_untyped_calls = false [[tool.mypy.overrides]] module = [ "aio_pika.*", "minio.*", "prometheus_client.*", "sentry_sdk.*", "opentelemetry.*", "pymupdf.*", "docx.*", "pytesseract.*", "PIL.*", ] ignore_missing_imports = true [tool.pytest.ini_options] testpaths = ["tests"] addopts = "-q" asyncio_mode = "auto" # Make `tests` importable as a namespace package so integration modules can use # `from tests.integration.conftest import ...` without committing __init__.py. pythonpath = ["."] markers = [ "integration: slow tests needing real Postgres/RabbitMQ/MinIO (deselect with '-m \"not integration\"')", ]