| .forgejo/workflows | ||
| .github/workflows | ||
| deploy | ||
| docs | ||
| migrations | ||
| src/contract_check | ||
| srv | ||
| tests | ||
| .dockerignore | ||
| .env.bot.example | ||
| .env.example | ||
| .gitignore | ||
| .pre-commit-config.yaml | ||
| .python-version | ||
| alembic.ini | ||
| CONTEXT.md | ||
| docker-compose.bot.yml | ||
| docker-compose.test.yml | ||
| docker-compose.yml | ||
| Makefile | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
Контракт-чек
LLM-сервис скрининга рисков в договорах по ГК РФ / ГК РБ. Telegram-бот + web-аутентификация (email/password, JWT-пара; пасскеи и магические ссылки — passwordless) + B2B API + биллинг (ЮKassa: подписки, пакеты кредитов, авто-возвраты) сейчас; web-SPA — позже.
Pipeline: PDF/DOCX/RTF/TXT/CSV/изображения (pymupdf / mammoth / tesseract OCR) → текст → чанки → прескрин (гибридная эвристика + LLM: метаданные договора, роутинг) → LLM-анализ (Ollama Cloud или YandexGPT, json-schema + repair-loop) → markdown-отчёт
с цитатами, ссылкой на пункт и дисклеймером «не заменяет юриста».
Документы
docs/ARCHITECTURE.md— единственный источник истины для production-рефакторинга. Hexagonal-архитектура, RabbitMQ-конвейер, схемы БД, очередь/ретраи/DLQ, конфиг, deploy, observability.docs/DEPLOY.md— руководство по развёртыванию: локально, Docker Compose, VPS, seed-token, backup, troubleshooting.docs/BUSINESS_IDEA.md— идея и бизнес-модель.docs/TICKETS.md— тикеты реализации со статусами.
Архитектура (одна строка)
Hexagonal (ports & adapters). Ядро contract_check.core владеет всем состоянием
(Postgres/MinIO/RabbitMQ/LLM/кредиты/биллинг). Семь сервисов собираются из него:
Telegram ──► bot (aiogram, HTTP-only) ──HTTP──► api (FastAPI) ──publish──► RabbitMQ
владеет: PG, MinIO, │
Redis, кредитами ▼
extract.q ──► worker-extract
(pymupdf/mammoth/tesseract, CPU) │
▼ publish
prescreen.q ──► worker-prescreen
(гибридные метаданные + роутинг) │
┌──────────────────────────────────────┤
▼ deep_analysis manual_review
analyze.q ──► worker-analyze │
(LLM, I/O) → Report │
▼
notify.q ──► worker-notify (SMTP: password reset, magic link и др.)
- api — единственный писатель для пользовательских мутаций (upload → reserve credit → MinIO → publish
DocumentUploaded). - worker-extract — CPU: достаёт текст (PDF/DOCX/RTF/TXT/CSV, OCR для изображений; детект формата через python-magic), грузит извлечённый текст в MinIO, публикует
PrescreenRequested(еслиPRESCREEN_ENABLED=true) или сразуDocumentExtractedвanalyze.q. - worker-prescreen — гибридное извлечение метаданных договора (Stage 1 эвристика + Stage 2 LLM при низкой уверенности), роутинг:
deep_analysis→ публикуетAnalyzeRequestedвanalyze.q;manual_review— терминальный статус;auto_approve— записывает лёгкийReport,status=done. Пишет вprescreen_results. - worker-analyze — I/O: LLM-анализ по чек-листу, валидация/repair, сохраняет
Report,status=done. - worker-notify — доставка email-уведомлений (восстановление пароля и др.) через SMTP; без
SMTP_HOST— dev-логгер. - worker-billing — таймерный планировщик биллинга (без MQ): продления подписок (счета за 3 дня до конца периода), expiry/past_due-переходы, сверка pending-счетов старше 15 минут через ЮKassa API.
- bot — адаптер: HTTP-клиент к api, не импортирует core.db/s3/llm/mq (граница проверяется тестом
tests/unit/test_bot_boundary.py). Может работать на отдельном сервере — см.docs/DEPLOY.md§14.
Структура репозитория
src/contract_check/
core/ # общий домен (импортируется каждым сервисом)
config.py logging.py telemetry.py sentry.py metrics.py errors.py
credits.py tokens.py api_keys.py rate_limit.py redis_client.py
auth.py auth_refresh.py auth_refresh_key.py passkeys.py
db/ models.py session.py enums.py repositories/
mq/ topology.py publisher.py consumer.py messages.py
s3/ port.py minio_storage.py
llm/ port.py factory.py ollama_cloud.py yandex_gpt.py prescreen.py errors.py
billing/ port.py yookassa.py errors.py quota.py fulfillment.py refunds.py
extraction/ port.py factory.py formats.py + adapters/
(pdf_pymupdf, docx_mammoth, rtf_striprtf, txt_chardet, ocr_tesseract)
analysis/ extractor.py chunker.py checklist.py report_schema.py ocr.py analyzer.py
notifications/ transport.py publisher.py # SMTP + dev-log
security/ passwords.py # argon2
api/ # FastAPI-образ
app.py deps.py middleware.py services.py __main__.py
admin/ # серверный UI по /admin (login, users, счета, возвраты, hold)
billing.py users.py auth.py router.py templating.py templates/
billing/ # token-gated HTML pay-страницы (/pay/{id})
routes/ # префикс /api/v1
__init__.py health.py documents.py reports.py me.py metrics.py b2b.py billing.py webhooks.py
auth/ # Telegram, email/password, passkeys, magic links
__init__.py telegram.py password.py passkeys.py magic_links.py support.py
schemas/ # Pydantic-схемы запросов/ответов
auth.py b2b.py billing.py common.py documents.py me.py profile.py reports.py
worker_extract/ # CPU-образ (pymupdf + mammoth + tesseract)
consumer.py handler.py extract_document.py __main__.py
worker_prescreen/ # прескрин-образ (гибрид: эвристика + LLM)
consumer.py handler.py router.py config.py
extractor.py extractor_heuristic.py extractor_llm.py extractor_hybrid.py
worker_analyze/ # I/O-образ (LLM provider)
consumer.py handler.py __main__.py
worker_notify/ # email-уведомления (aiosmtplib)
consumer.py handler.py __main__.py
worker_billing/ # планировщик биллинга (продления, expiry, сверка)
scheduler.py __main__.py
bot/ # aiogram-адаптер (самый «тощий» образ: только core.logging)
client.py config.py handlers.py rate_limit.py __main__.py
docs/ # документация проекта (README остаётся в корне)
srv/ # Dockerfile-ы (один на сервис, deps заточены)
api/ worker-extract/ worker-prescreen/ worker-analyze/ worker-notify/ worker-billing/ bot/
migrations/ # alembic (async): 0001_initial … 0011_user_profiles_billing
tests/
conftest.py
unit/ chunker, extractor, extraction_factory/adapters, llm_ollama_cloud,
llm_yandex_gpt, llm_prescreen_extraction, prescreen_extractor(+heuristic/
hybrid/llm), prescreen_router, credits, messages, rate_limit,
checklist_report, auth, web_auth, passkeys, extract_handler,
bot_client, bot_boundary, refunds, worker_billing
integration/ upload_pipeline, extract_worker, prescreen_worker, analyze_worker,
b2b_api, credits_db, auth_flow, passkeys_magic_link, admin_panel,
profile_api, b2b_profile, billing_checkout, webhook_yookassa,
pay_page, subscription_purchase, quota_reservation, refunds,
admin_billing
Makefile # повседневные команды (make help)
docker-compose.yml # default = инфра; --profile services = стек; --profile edge = nginx+certbot
pyproject.toml # hatchling + PEP 735 dependency-groups (db/mq/s3/obs/api/extract/prescreen/analyze/notify/billing/bot/dev)
.env.example # полный список env (см. docs/ARCHITECTURE.md §11)
Быстрый старт
Локально (разработка)
uv sync --group dev # все группы для локальной разработки
cp .env.example .env # впишите OLLAMA_* / YANDEXGPT_* / JWT_SECRET / BOT_TOKEN
docker compose up -d # только инфра (postgres/redis/rabbitmq/minio)
uv run alembic upgrade head # миграции
uv run python -m contract_check.api # api на :8000
uv run python -m contract_check.worker_extract # воркер экстракции
uv run python -m contract_check.worker_prescreen # воркер прескрина
uv run python -m contract_check.worker_analyze # воркер анализа
uv run python -m contract_check.worker_notify # воркер уведомлений
uv run python -m contract_check.worker_billing # планировщик биллинга
uv run python -m contract_check.bot # Telegram-бот
Через Docker Compose / Make
cp .env.example .env # заполнить секреты (DB, Rabbit, MinIO, LLM, BOT_TOKEN, JWT_SECRET, ...)
docker compose up -d # только инфра с healthchecks
docker compose --profile services up -d --build # + api, worker-extract, worker-prescreen, worker-analyze, worker-notify, worker-billing (без бота)
docker compose --profile bot up -d --build # + bot (можно запускать отдельно или на другом хосте)
Или через make: make dev (install + infra + migrate + api + workers + bot), make help — полный список
целей (lint, typecheck, test, seed-token, jwt-token, admin-promote, логи/шеллы сервисов и т.п.).
Профили:
services— api + worker-ы (без бота).bot— Telegram-бот; можно поднять на этом же хосте или на отдельном сервере (docs/DEPLOY.md§14).edge— Nginx + certbot (deploy/nginx/,docs/DEPLOY.md§13).obs/observer— observability (Grafana/Loki/Prometheus или OpenObserve + Vector).
Порты на хосте (смещены, чтобы не конфликтовать): Postgres 15432, Redis 17379,
RabbitMQ AMQP 5672 / UI 15672, MinIO 9000 / console 9001, api 8000 / metrics 9100
(по умолчанию только на loopback), OpenObserve UI 5080, edge 80/443.
Метрики worker-ов больше не публикуются на хост; их скрейпит Vector внутри сети compose.
API (кратко)
JSON-роуты под /api/v1; серверный admin UI — по /admin/* (FastAPI + Jinja2 + HTMX). Auth зависит от роута:
- Пользовательские роуты (bot / web / Mini App) —
Authorization: Bearer <user_jwt>. Telegram-JWT выдаётся через/api/v1/auth/telegram/*; web — JWT-пара (access + refresh, refresh отзывается через Redis); passwordless (пасскей / магическая ссылка) — одиночный access JWT. - Адаптер-level (только
/api/v1/auth/telegram/bot) —Authorization: Bearer <service_token>. - B2B —
X-API-Key. Управление B2B-ключами требует пользовательский JWT. - Health/metrics — без auth.
- /admin — HttpOnly cookie с user JWT +
users.role = admin(илиADMIN_REQUIRED_ROLE).
| Метод | Путь | Auth | Назначение |
|---|---|---|---|
| GET | /healthz, /readyz, /metrics |
— | liveness / readiness / Prometheus |
| POST | /api/v1/auth/telegram/bot |
service token | бот меняет verified telegram_id на JWT |
| POST | /api/v1/auth/telegram/web |
— | Telegram Login Widget → JWT |
| POST | /api/v1/auth/telegram/miniapp |
— | Mini App initData → JWT |
| POST | /api/v1/auth/register |
— | регистрация email/password → JWT-пара |
| POST | /api/v1/auth/login |
— | вход email/password → JWT-пара |
| POST | /api/v1/auth/logout |
— | отзыв refresh-токена |
| POST | /api/v1/auth/forgot-password |
— | reset-токен (хранится хэшем) + уведомление через notify.q |
| POST | /api/v1/auth/reset-password |
— | задать новый пароль, отозвать все refresh |
| POST | /api/v1/auth/passkeys/register/start, .../finish |
user JWT | регистрация пасскея (WebAuthn, challenge в Redis) |
| POST | /api/v1/auth/passkeys/authenticate/start, .../finish |
— | вход по пасскею (discoverable) → access JWT |
| GET/DEL | /api/v1/auth/passkeys, /api/v1/auth/passkeys/{id} |
user JWT | список / удаление пасскеев |
| POST | /api/v1/auth/magic-link/request |
— | одноразовая ссылка входа на email (через notify.q) |
| POST | /api/v1/auth/magic-link/verify |
— | обмен токена из письма на access JWT |
| GET | /api/v1/auth/me, /api/v1/auth/me/permissions |
user JWT | introspect JWT / права |
| POST | /api/v1/documents |
user JWT | multipart upload → reserve credit → MinIO → publish → 202 {document_id, correlation_id} |
| GET | /api/v1/reports/{document_id} |
user JWT | поллинг: {status, stage} или готовый 200 {markdown, findings, ...} |
| GET | /api/v1/me |
user JWT | профиль + credits_left |
| GET/PATCH | /api/v1/me/profile |
user JWT | пассивные настройки профиля (язык, TZ, уведомления, дашборд) |
| GET | /api/v1/me/overview |
user JWT | дашборд: документы, кредиты, план/квота, billing_hold |
| GET | /api/v1/me/documents |
user JWT | список документов пользователя |
| POST | /api/v1/me/telegram, /api/v1/me/password |
user JWT | привязка Telegram / смена пароля |
| GET | /api/v1/billing/plans |
— | каталог тарифов + цена за документ |
| POST | /api/v1/billing/checkout |
user JWT | топ-ап кредитов или покупка подписки → 201 {invoice_id, confirmation_url} |
| GET | /api/v1/billing/invoices, /api/v1/billing/invoices/{id} |
user JWT | история / свой счёт |
| GET | /api/v1/billing/subscription |
user JWT | текущая подписка (active/past_due) |
| POST | /api/v1/billing/subscriptions/autorenew |
user JWT | вкл/выкл автопродление (по умолчанию OFF) |
| POST | /api/v1/billing/refund |
user JWT | авто-возврат по правилу D10; 409 если не возвращаем |
| POST | /api/v1/webhooks/yookassa |
Basic (shopId:secret) | уведомления ЮKassa; платёж перезапрашивается через REST |
| GET | /pay/{invoice_id}?token=… |
подписанный JWT | HTML-страница статуса счёта после оплаты |
| GET/PUT | /api/v1/b2b/profile |
X-API-Key |
настройки профиля партнёра (без биллинга) |
| POST | /api/v1/analyze |
X-API-Key |
B2B: анализ документа |
| GET | /api/v1/b2b/reports/{id}, /api/v1/b2b/usage |
X-API-Key |
B2B: отчёт / usage |
| POST/GET | /api/v1/b2b/keys, /api/v1/b2b/keys/{id}/revoke, .../usage |
user JWT | управление B2B-ключами |
| GET/POST | /admin/* |
admin cookie | login/logout + пользователи (list, create, edit, ban, role, credits, verify-telegram), счета (/admin/invoices), возвраты, снятие billing-hold, gift-подписки |
Биллинг-флаги: PLANS_ENABLED (квоты подписок; false = только кредиты),
YOOKASSA_ENABLED (платежи; false → мутации биллинга 503, каталог читается).
Детали: docs/ARCHITECTURE.md §8a, настройка ЮKassa — docs/DEPLOY.md §4a.
Полная спецификация роутов (схемы запросов/ответов, коды ошибок) —
src/contract_check/api/routes/README.md;
высокоуровневая поверхность — docs/ARCHITECTURE.md §15 (актуализируется).
Проверки (DoD)
uv run ruff check src tests && uv run ruff format --check src tests # lint (или make lint)
uv run ty check src # типы (или make typecheck)
uv run pytest -m "not integration" -q # unit, быстро
uv run pytest -m integration -q # интеграционные (нужны контейнеры)