Update docs.

This commit is contained in:
febux 2026-08-24 01:00:11 +03:00
parent 2fe54ffd86
commit d1bbaf8da3
6 changed files with 50 additions and 41 deletions

View file

@ -41,8 +41,8 @@ Telegram ──► bot (aiogram, HTTP-only) ──HTTP──► api (FastAPI)
``` ```
- **api** — единственный писатель для пользовательских мутаций (upload → reserve credit → MinIO → publish `DocumentUploaded`). - **api** — единственный писатель для пользовательских мутаций (upload → reserve credit → MinIO → publish `DocumentUploaded`).
- **worker-extract** — CPU: достаёт текст (PDF/DOCX/RTF/TXT/CSV, OCR для изображений; детект формата через python-magic), грузит markdown в MinIO, публикует `DocumentExtracted`. - **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`analyze.q / `manual_review` / `auto_approve` → report.completed. Пишет в `prescreen_results`. - **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-analyze** — I/O: LLM-анализ по чек-листу, валидация/repair, сохраняет `Report`, `status=done`.
- **worker-notify** — доставка email-уведомлений (восстановление пароля и др.) через SMTP; без `SMTP_HOST` — dev-логгер. - **worker-notify** — доставка email-уведомлений (восстановление пароля и др.) через SMTP; без `SMTP_HOST` — dev-логгер.
- **bot** — адаптер: HTTP-клиент к api, **не импортирует** core.db/s3/llm/mq (граница проверяется тестом `tests/unit/test_bot_boundary.py`). - **bot** — адаптер: HTTP-клиент к api, **не импортирует** core.db/s3/llm/mq (граница проверяется тестом `tests/unit/test_bot_boundary.py`).
@ -52,23 +52,28 @@ Telegram ──► bot (aiogram, HTTP-only) ──HTTP──► api (FastAPI)
``` ```
src/contract_check/ src/contract_check/
__main__.py # указывает на prototype (stage-0 CLI сохранён) __main__.py # указывает на prototype (stage-0 CLI сохранён)
core/ # общий домен (импортируется каждым сервисом) core/ # общий домен (импортируется каждым сервисом)
config.py logging.py telemetry.py sentry.py metrics.py errors.py 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 credits.py tokens.py api_keys.py rate_limit.py redis_client.py
auth.py auth_refresh.py auth_refresh_key.py passkeys.py auth.py auth_refresh.py auth_refresh_key.py passkeys.py
db/ models.py session.py enums.py db/ models.py session.py enums.py repositories/
mq/ topology.py publisher.py consumer.py messages.py mq/ topology.py publisher.py consumer.py messages.py
s3/ port.py minio_storage.py s3/ port.py minio_storage.py
llm/ port.py factory.py ollama_cloud.py yandex_gpt.py prescreen.py llm/ port.py factory.py ollama_cloud.py yandex_gpt.py prescreen.py errors.py
extraction/ port.py factory.py + adapters/ extraction/ port.py factory.py formats.py + adapters/
(pdf_pymupdf, docx_mammoth, rtf_striprtf, txt_chardet, ocr_tesseract) (pdf_pymupdf, docx_mammoth, rtf_striprtf, txt_chardet, ocr_tesseract)
analysis/ extractor.py chunker.py checklist.py report_schema.py ocr.py analyzer.py analysis/ extractor.py chunker.py checklist.py report_schema.py ocr.py analyzer.py
notifications/ transport.py publisher.py # SMTP + dev-log notifications/ transport.py publisher.py # SMTP + dev-log
security/ passwords.py # argon2 security/ passwords.py # argon2
api/ # FastAPI-образ api/ # FastAPI-образ
app.py deps.py middleware.py services.py __main__.py app.py deps.py middleware.py services.py __main__.py
admin/ # серверный UI по /admin (login, users; позже — подписки) admin/ # серверный UI по /admin (login, users; позже — подписки)
routes/ health.py documents.py reports.py me.py metrics.py auth.py b2b.py routes/ # префикс /api/v1
__init__.py health.py documents.py reports.py me.py metrics.py b2b.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 common.py documents.py me.py reports.py
worker_extract/ # CPU-образ (pymupdf + mammoth + tesseract) worker_extract/ # CPU-образ (pymupdf + mammoth + tesseract)
consumer.py handler.py extract_document.py __main__.py consumer.py handler.py extract_document.py __main__.py
worker_prescreen/ # прескрин-образ (гибрид: эвристика + LLM) worker_prescreen/ # прескрин-образ (гибрид: эвристика + LLM)
@ -84,7 +89,7 @@ src/contract_check/
docs/ # документация проекта (README остаётся в корне) docs/ # документация проекта (README остаётся в корне)
srv/ # Dockerfile-ы (один на сервис, deps заточены) srv/ # Dockerfile-ы (один на сервис, deps заточены)
api/ worker-extract/ worker-prescreen/ worker-analyze/ worker-notify/ bot/ prototype/ api/ worker-extract/ worker-prescreen/ worker-analyze/ worker-notify/ bot/ prototype/
migrations/ # alembic (async): 0001_initial … 0009_user_name migrations/ # alembic (async): 0001_initial … 0010_passkeys_magic_links
tests/ tests/
conftest.py conftest.py
unit/ chunker, extractor, extraction_factory/adapters, llm_ollama_cloud, unit/ chunker, extractor, extraction_factory/adapters, llm_ollama_cloud,

View file

@ -1274,7 +1274,7 @@ Three JSON auth modes:
Health/metrics exempt from auth. Health/metrics exempt from auth.
### Auth endpoints (`api/routes/auth.py`) ### Auth endpoints (`api/routes/auth/`) — `telegram.py`, `password.py`, `passkeys.py`, `magic_links.py`
| Method | Path | Auth | Behavior | | Method | Path | Auth | Behavior |
|---|---|---|---| |---|---|---|---|

View file

@ -3,8 +3,9 @@
> **Примечание:** этот документ описывает исходную «ленивую» архитектуру с arq+Redis > **Примечание:** этот документ описывает исходную «ленивую» архитектуру с arq+Redis
> (очередь), Selectel S3 (хранилище) и единым Docker-образом с `MODE=api|worker|bot`. > (очередь), Selectel S3 (хранилище) и единым Docker-образом с `MODE=api|worker|bot`.
> **Этап 0 (прототип) актуален.** Этапы 1+ superseded [`ARCHITECTURE.md`](ARCHITECTURE.md), > **Этап 0 (прототип) актуален.** Этапы 1+ superseded [`ARCHITECTURE.md`](ARCHITECTURE.md),
> где реализована production-архитектура: RabbitMQ-конвейер, MinIO, два worker-а, > где реализована production-архитектура: RabbitMQ-конвейер, MinIO, 4 worker-а
> aiogram-бот, B2B API, 5 Dockerfile-ов в `srv/`, dependency-groups (PEP 735). > (extract, prescreen, analyze, notify), aiogram-бот, B2B API, 7 Dockerfile-ов в `srv/`,
> dependency-groups (PEP 735).
> Принцип: ленивая архитектура. Каждый этап — минимальный работающий > Принцип: ленивая архитектура. Каждый этап — минимальный работающий
> срез. Никаких абстракций «на потом». K8s, RabbitMQ, микросервисы — > срез. Никаких абстракций «на потом». K8s, RabbitMQ, микросервисы —

View file

@ -10,7 +10,7 @@
|---|---|---| |---|---|---|
| Phase 0 | Needle/RustFS spikes | Done (`docs/SPIKE_PHASE0.md`) | | Phase 0 | Needle/RustFS spikes | Done (`docs/SPIKE_PHASE0.md`) |
| Phase 1 | Extraction layer refactor | Done (`docs/PHASE1_HANDOFF.md`) | | Phase 1 | Extraction layer refactor | Done (`docs/PHASE1_HANDOFF.md`) |
| Phase 2 | Prescreen stage | **Planned below** | | Phase 2 | Prescreen stage | **Done** (`worker_prescreen/`, migrations `0006_prescreen.py``0008_add_manual_review_status.py`) |
| Phase 3 | Storage modernization / RustFS watch | **Planned below** | | Phase 3 | Storage modernization / RustFS watch | **Planned below** |
| Follow-up | Admin/web, payments, heavy OCR | **Backlog** | | Follow-up | Admin/web, payments, heavy OCR | **Backlog** |

View file

@ -1,10 +1,12 @@
# Тикеты реализации «Контракт-чек» (production refactor) # Тикеты реализации «Контракт-чек» (production refactor)
> **Актуальная архитектура:** `ARCHITECTURE.md` (supersedes `IMPLEMENTATION_PLAN.md` для этапов 1+). > **Актуальная архитектура:** `ARCHITECTURE.md` (supersedes `IMPLEMENTATION_PLAN.md` для этапов 1+).
> Состояние кода на момент синхронизации: реализованы `api/` (вкл. B2B-роуты), `core/`, > Состояние кода на момент синхронизации: реализованы `api/` (вкл. B2B-роуты, web-auth, passkeys, magic links),
> `worker_extract/`, `worker_analyze/`, `bot/`, `prototype/`; все 5 Dockerfile-ов в `srv/`; > `core/`, `worker_extract/`, `worker_prescreen/`, `worker_analyze/`, `worker_notify/`, `bot/`, `prototype/`;
> `docker-compose.yml` полностью разводит профиль `services`; миграции `0001_initial` + `0002_api_keys`; > 7 Dockerfile-ов в `srv/`; `docker-compose.yml` разводит профили `services` и `edge`;
> unit-тесты зелёные. DoD: `ruff check .`, `mypy src`, `pytest` зелёные; `.env.example` актуален. > миграции `0001_initial``0010_passkeys_magic_links`; unit- и интеграционные тесты зелёные.
> DoD: `ruff check src tests`, `ruff format --check src tests`, `uv run ty check src`, `pytest` зелёные;
> `.env.example` актуален.
> >
> Статусы: `todo` / `in_progress` / `done` / `blocked`. > Статусы: `todo` / `in_progress` / `done` / `blocked`.
@ -13,21 +15,22 @@
## Аудит реализации (текущее состояние) ## Аудит реализации (текущее состояние)
> Проверено: код собирается (`pyproject.toml` + `uv.lock`), миграции накатываются, > Проверено: код собирается (`pyproject.toml` + `uv.lock`), миграции накатываются,
> `api/` стартует, unit-тесты проходят (58 тестов зелёные). > `api/` стартует, unit-тесты проходят (≈80 тестов зелёные), интеграционные тесты проходят на поднятой инфраструктуре.
| Компонент | Статус | Доказательство / пробел | | Компонент | Статус | Доказательство / пробел |
|-----------|--------|---------------------------| |-----------|--------|---------------------------|
| Stage 0 prototype | done | `src/contract_check/prototype/` работает; `tests/unit/test_checklist_report.py`, `test_chunker.py`, `test_extractor.py` зелёные. | | Stage 0 prototype | done | `src/contract_check/prototype/` работает; `tests/unit/test_checklist_report.py`, `test_chunker.py`, `test_extractor.py` зелёные. |
| `core/` — общий домен | done | `db/`, `mq/`, `s3/`, `llm/`, `analysis/`, `credits.py`, `tokens.py`, `api_keys.py`, `rate_limit.py`, `redis_client.py`, `config.py`, `logging.py`, `metrics.py`, `telemetry.py`, `sentry.py`. | | `core/` — общий домен | done | `db/` (models, session, enums, repositories), `mq/`, `s3/`, `llm/`, `analysis/`, `extraction/`, `notifications/`, `security/`, `credits.py`, `tokens.py`, `api_keys.py`, `rate_limit.py`, `redis_client.py`, `config.py`, `logging.py`, `metrics.py`, `telemetry.py`, `sentry.py`, `passkeys.py`. |
| Миграции / БД | done | `0001_initial.py` (6 таблиц) + `0002_api_keys.py` (`api_keys`, `api_key_requests`). | | Миграции / БД | done | `0001_initial.py` (6 таблиц) … `0010_passkeys_magic_links.py` (passkeys, magic links, user name). |
| `api/` — FastAPI ядро | done | `POST /api/v1/documents`, `GET /api/v1/reports/{id}`, `GET /api/v1/documents/{id}`, `GET /api/v1/me`, `/healthz`, `/readyz`, `/metrics`; user JWT auth via `/api/v1/auth/telegram/*`; reserve-on-enqueue (`services.py`). | | `api/` — FastAPI ядро | done | `POST /api/v1/documents`, `GET /api/v1/reports/{id}`, `GET /api/v1/me`, `/healthz`, `/readyz`, `/metrics`; user JWT auth (`/api/v1/auth/telegram/*`, `/api/v1/auth/{register,login,...}`, passkeys, magic links); B2B `/api/v1/analyze`; `/admin/*`; reserve-on-enqueue (`services.py`). |
| `worker_extract/` | done | `consumer.py`/`handler.py`/`extract_document.py`/`__main__.py`: consume `DocumentUploaded` → MinIO dl → extract/OCR → `.txt` upload → publish `DocumentExtracted`; failure-class + refund. | | `worker_extract/` | done | `consumer.py`/`handler.py`/`extract_document.py`/`__main__.py`: consume `DocumentUploaded` → MinIO dl → extract/OCR → `.txt` upload → publish `PrescreenRequested` (или `DocumentExtracted`, если прескрин выключен); failure-class + refund. |
| `worker_prescreen/` | done | `consumer.py`/`handler.py`/`__main__.py` + `extractor*.py`/`router.py`/`config.py`: consume `PrescreenRequested` → гибридная экстракция метаданных → роутинг `deep_analysis`/`manual_review`/`auto_approve`; публикация `AnalyzeRequested` или терминальный статус/лёгкий отчёт. |
| `worker_analyze/` | done | `consumer.py`/`handler.py`/`__main__.py`: consume `DocumentExtracted` → LLM → Report → `status=done`; refund-on-DLQ по политике. | | `worker_analyze/` | done | `consumer.py`/`handler.py`/`__main__.py`: consume `DocumentExtracted` → LLM → Report → `status=done`; refund-on-DLQ по политике. |
| `worker_notify/` | done | `consumer.py`/`handler.py`/`__main__.py`: consume `NotificationMessage` → SMTP (password reset, magic link) или dev-лог при пустом `SMTP_HOST`. |
| `bot/` — Telegram adapter | done | `client.py`/`config.py`/`handlers.py`/`__main__.py`: `/start`, upload→`POST /documents`, poll→deliver; граница импортов проверяется `tests/unit/test_bot_boundary.py`. | | `bot/` — Telegram adapter | done | `client.py`/`config.py`/`handlers.py`/`__main__.py`: `/start`, upload→`POST /documents`, poll→deliver; граница импортов проверяется `tests/unit/test_bot_boundary.py`. |
| Dockerfile-ы | done | `srv/{api,worker-extract,worker-analyze,bot,prototype}/Dockerfile` — все 5 (deps-группы PEP 735 заточены на сервис). | | Dockerfile-ы | done | `srv/{api,worker-extract,worker-prescreen,worker-analyze,worker-notify,bot,prototype}/Dockerfile` — все 7 (deps-группы PEP 735 заточены на сервис). |
| Docker Compose | done | Инфра (default) + профиль `services` (api, worker-extract, worker-analyze, bot) с `depends_on: service_healthy`. Профили `obs`/`edge` — позже. | | Docker Compose | done | Инфра (default) + профиль `services` (api, 3 worker-а, bot) + профиль `edge` (nginx+certbot) с `depends_on: service_healthy`. Профиль `obs` — позже. |
| Observability / edge | todo | Пром/Grafana/Tempo/OTel-collector/Nginx/certbot — не развёрнуты (нет `deploy/`). | | Observability | in_progress | Prometheus-метрики (`/metrics`), Sentry, OpenTelemetry SDK — в коде. Полный стек Prom/Grafana/Tempo/OTel-collector — позже. |
| Stage 2 — веб + подписки | todo | React SPA, Telegram Login, recurring ЮKassa — не начаты. |
| Stage 3 — B2B API | done | `api/routes/b2b.py`, `core/api_keys.py`, `core/rate_limit.py`, `core/redis_client.py`, миграция `0002_api_keys.py`, `tests/integration/test_b2b_api.py`, `tests/unit/test_rate_limit.py`; `X-API-Key` auth + token-bucket rate-limit. | | Stage 3 — B2B API | done | `api/routes/b2b.py`, `core/api_keys.py`, `core/rate_limit.py`, `core/redis_client.py`, миграция `0002_api_keys.py`, `tests/integration/test_b2b_api.py`, `tests/unit/test_rate_limit.py`; `X-API-Key` auth + token-bucket rate-limit. |
--- ---
@ -86,14 +89,14 @@
**Статус:** done · **Оценка:** L **Статус:** done · **Оценка:** L
`src/contract_check/api/`: `POST /api/v1/documents`, `GET /api/v1/reports/{id}`, `GET /api/v1/me`, `/healthz`, `/metrics`. `src/contract_check/api/`: `POST /api/v1/documents`, `GET /api/v1/reports/{id}`, `GET /api/v1/me`, `/healthz`, `/metrics`.
Reserve-on-enqueue (`core/credits.py`) и user JWT auth (`core/auth.py`, `api/routes/auth.py`). Reserve-on-enqueue (`core/credits.py`) и user JWT auth (`core/auth.py`, `api/routes/auth/`).
### T-E1-004 — Worker-extract (CPU: pymupdf + tesseract) ### T-E1-004 — Worker-extract (CPU: pymupdf + tesseract)
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-001, T-E1-002 **Статус:** done · **Оценка:** M · **Зависимости:** T-E1-001, T-E1-002
`src/contract_check/worker_extract/` (`consumer.py`/`handler.py`/`extract_document.py`/`__main__.py`): `src/contract_check/worker_extract/` (`consumer.py`/`handler.py`/`extract_document.py`/`__main__.py`):
consume `DocumentUploaded` из `extract.q` → скачать blob из MinIO → consume `DocumentUploaded` из `extract.q` → скачать blob из MinIO →
`extractor` + `ocr.ocr_pdf()` → загрузить `.txt` → publish `DocumentExtracted` в `analyze.q`. `extractor` + `ocr.ocr_pdf()` → загрузить `.txt` → publish `PrescreenRequested` в `prescreen.q` (или `DocumentExtracted` в `analyze.q`, если прескрин выключен).
Failure-classification + refund-on-DLQ. Образ `srv/worker-extract/Dockerfile`. Failure-classification + refund-on-DLQ. Образ `srv/worker-extract/Dockerfile`.
### T-E1-005 — Worker-analyze (I/O: LLM provider) ### T-E1-005 — Worker-analyze (I/O: LLM provider)
@ -117,8 +120,8 @@ consume `DocumentExtracted` из `analyze.q` → скачать `.txt` → chunk
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-004, T-E1-005, T-E1-006 **Статус:** done · **Оценка:** M · **Зависимости:** T-E1-004, T-E1-005, T-E1-006
`docker-compose.yml`: default-профиль = инфра (`postgres`, `redis`, `rabbitmq`, `minio`, `minio-init`); `docker-compose.yml`: default-профиль = инфра (`postgres`, `redis`, `rabbitmq`, `minio`, `minio-init`);
профиль `services` = `api`, `worker-extract`, `worker-analyze`, `bot` с `depends_on: service_healthy`. профиль `services` = `api`, `worker-extract`, `worker-prescreen`, `worker-analyze`, `worker-notify`, `bot` с `depends_on: service_healthy`;
Все 5 Dockerfile-ов в `srv/`. Профили `obs`/`edge` — позже (T-E1-009/T-E1-010). профиль `edge` = nginx+certbot. Все 7 Dockerfile-ов в `srv/`. Профиль `obs` — позже (T-E1-010).
### T-E1-008 — Оплата (ЮKassa) и пополнение кредитов ### T-E1-008 — Оплата (ЮKassa) и пополнение кредитов
**Статус:** todo · **Оценка:** M · **Зависимости:** T-E1-003, T-E1-006 **Статус:** todo · **Оценка:** M · **Зависимости:** T-E1-003, T-E1-006
@ -147,7 +150,7 @@ consume `DocumentExtracted` из `analyze.q` → скачать `.txt` → chunk
### T-E2-002 — Telegram Login Widget auth ### T-E2-002 — Telegram Login Widget auth
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-003 **Статус:** done · **Оценка:** M · **Зависимости:** T-E1-003
Реализовано в `core/auth.py` + `api/routes/auth.py`: `/api/v1/auth/telegram/web` и `/api/v1/auth/telegram/miniapp` проверяют HMAC-подпись Telegram и выдают тот же user JWT, что и бот. Реализовано в `core/auth.py` + `api/routes/auth/telegram.py`: `/api/v1/auth/telegram/web` и `/api/v1/auth/telegram/miniapp` проверяют HMAC-подпись Telegram и выдают тот же user JWT, что и бот.
### T-E2-003 — React SPA (Vite) ### T-E2-003 — React SPA (Vite)
**Статус:** todo · **Оценка:** L · **Зависимости:** T-E2-002 **Статус:** todo · **Оценка:** L · **Зависимости:** T-E2-002

View file

@ -80,7 +80,7 @@ Exposition-эндпоинт Prometheus. Тело — текст в формат
## auth ## auth
Файл: [`auth.py`](auth.py). Тег: `auth`. Файлы: [`auth/`](auth/) — `telegram.py`, `password.py`, `passkeys.py`, `magic_links.py`, `support.py`. Тег: `auth`.
Три источника идентичности сходятся к одному JWT: Три источника идентичности сходятся к одному JWT:
@ -375,7 +375,7 @@ request — тело `{ "email" }`, всегда `200 { success, message, expire
| Поле | Тип | Описание | | Поле | Тип | Описание |
| ----- | ----------- | ------------------------------------- | | ----- | ----------- | ------------------------------------- |
| `file`| UploadFile | Расширения: `.pdf`, `.docx` (см. `SUPPORTED_SUFFIXES`) | | `file`| UploadFile | Расширения: `.pdf`, `.docx`, `.rtf`, `.txt`, `.csv`, `.png`, `.jpg`, `.jpeg`, `.tif`, `.tiff` (см. `core/extraction/formats.py`) |
Ответ `202` (результат [`upload_and_enqueue`](../services.py)): Ответ `202` (результат [`upload_and_enqueue`](../services.py)):