No description
Find a file
2026-09-02 23:54:54 +03:00
.github/workflows Update ci.yml 2026-09-02 22:58:22 +03:00
deploy Grafana stack was added. Update docs. 2026-09-02 23:32:47 +03:00
docs Grafana stack was added. Update docs. 2026-09-02 23:32:47 +03:00
migrations User profile with billing base was added. 2026-08-25 20:52:50 +03:00
src/contract_check Fix SSE with query auth token param. 2026-09-02 22:23:16 +03:00
srv Fix absolute path. 2026-08-26 20:24:22 +03:00
tests Unit tests fix. 2026-09-02 23:54:54 +03:00
.dockerignore Init commit 2026-08-12 21:29:36 +03:00
.env.example Grafana stack was added. Update docs. 2026-09-02 23:32:47 +03:00
.gitignore User profile with billing base was added. 2026-08-25 20:52:50 +03:00
.pre-commit-config.yaml Phase 2 - Raw SQL migration, pytest-cov coverage gate, CI branch 2026-08-24 00:45:39 +03:00
.python-version Init commit 2026-08-12 21:29:36 +03:00
alembic.ini Phase 2 - Raw SQL migration, pytest-cov coverage gate, CI branch 2026-08-24 00:45:39 +03:00
CONTEXT.md Plan for ADR and payments. 2026-08-25 21:57:05 +03:00
docker-compose.yml Grafana stack was added. Update docs. 2026-09-02 23:32:47 +03:00
Makefile Grafana stack was added. Update docs. 2026-09-02 23:32:47 +03:00
pyproject.toml Import error for billing service was fixed. 2026-08-26 20:37:34 +03:00
README.md User profile with billing base was added. 2026-08-25 20:52:50 +03:00
uv.lock Import error for billing service was fixed. 2026-08-26 20:37:34 +03:00

Контракт-чек

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/IMPLEMENTATION_PLAN.md — исходный «ленивый» план. Этап 0 (прототип) актуален; этапы 1+ superseded в docs/ARCHITECTURE.md.
  • docs/TICKETS.md — тикеты реализации со статусами.
  • docs/PHASE2_HANDOFF.md, docs/PHASES_2_PLUS_ROADMAP.md, docs/PRESCREEN_HYBRID_REFACTOR_PLAN.md — этап 2: prescreen-стейдж и дальнейший роадмап.
  • docs/SPIKE_PHASE0.md — спайки: prescreen-библиотеки, RustFS (артефакты в rustfs-spike/).

Архитектура (одна строка)

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).

Структура репозитория

src/contract_check/
  __main__.py                  # указывает на prototype (stage-0 CLI сохранён)
    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
  prototype/                   # stage-0 standalone CLI (бенчмарк go/no-go)
docs/                          # документация проекта (README остаётся в корне)
srv/                           # Dockerfile-ы (один на сервис, deps заточены)
  api/  worker-extract/  worker-prescreen/  worker-analyze/  worker-notify/  worker-billing/  bot/  prototype/
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/prototype/dev)
.env.example                   # полный список env (см. docs/ARCHITECTURE.md §11)
rustfs-spike/                  # артефакты спайка RustFS (docs/SPIKE_PHASE0.md)

Быстрый старт

Локально (разработка)

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, bot

Или через make: make dev (install + infra + migrate + services), make help — полный список целей (lint, typecheck, test, seed-token, jwt-token, admin-promote, логи/шеллы сервисов и т.п.).

Профиль services собирает 6 образов из srv/<service>/Dockerfile и поднимает их с depends_on: condition: service_healthy. Edge-прокси (Nginx + certbot) доступен профилем edge (deploy/nginx/, docs/DEPLOY.md §13). Observability (Prometheus/Grafana/Tempo/OTel) — за будущим профилем obs.

Порты на хосте (смещены, чтобы не конфликтовать): Postgres 15432, Redis 17379, RabbitMQ AMQP 5672 / UI 15672, MinIO 9000 / console 9001, api 8000 / metrics 9100, worker metrics: extract 9101, analyze 9102, notify 9103, prescreen 9104, billing 9105, edge 80/443.

Stage-0 прототип (бенчмарк)

uv sync --group prototype
uv run python -m contract_check prototype contract.pdf              # отчёт в stdout
uv run contract-check contract.pdf -o report.md                     # или консольная команда
uv run python -m contract_check prototype contract.pdf --json metrics.json   # + метрики go/no-go

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>.
  • B2BX-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                                       # интеграционные (нужны контейнеры)