DealDocumentScreening/docs/TICKETS.md
2026-08-12 21:29:36 +03:00

216 lines
14 KiB
Markdown
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.

# Тикеты реализации «Контракт-чек» (production refactor)
> **Актуальная архитектура:** `ARCHITECTURE.md` (supersedes `IMPLEMENTATION_PLAN.md` для этапов 1+).
> Состояние кода на момент синхронизации: реализованы `api/` (вкл. B2B-роуты), `core/`,
> `worker_extract/`, `worker_analyze/`, `bot/`, `prototype/`; все 5 Dockerfile-ов в `srv/`;
> `docker-compose.yml` полностью разводит профиль `services`; миграции `0001_initial` + `0002_api_keys`;
> unit-тесты зелёные. DoD: `ruff check .`, `mypy src`, `pytest` зелёные; `.env.example` актуален.
>
> Статусы: `todo` / `in_progress` / `done` / `blocked`.
---
## Аудит реализации (текущее состояние)
> Проверено: код собирается (`pyproject.toml` + `uv.lock`), миграции накатываются,
> `api/` стартует, unit-тесты проходят (58 тестов зелёные).
| Компонент | Статус | Доказательство / пробел |
|-----------|--------|---------------------------|
| 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`. |
| Миграции / БД | done | `0001_initial.py` (6 таблиц) + `0002_api_keys.py` (`api_keys`, `api_key_requests`). |
| `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`). |
| `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_analyze/` | done | `consumer.py`/`handler.py`/`__main__.py`: consume `DocumentExtracted` → LLM → Report → `status=done`; refund-on-DLQ по политике. |
| `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 заточены на сервис). |
| Docker Compose | done | Инфра (default) + профиль `services` (api, worker-extract, worker-analyze, bot) с `depends_on: service_healthy`. Профили `obs`/`edge` — позже. |
| Observability / edge | todo | Пром/Grafana/Tempo/OTel-collector/Nginx/certbot — не развёрнуты (нет `deploy/`). |
| 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. |
---
## Этап 0 — Прототип (go/no-go)
### T-E0-001 — Аккаунт Ollama Cloud + каркас прототипа
**Статус:** done · **Оценка:** S
`.env.example` содержит `OLLAMA_HOST`/`OLLAMA_API_KEY`/`OLLAMA_MODEL=qwen2.5:14b`/`OLLAMA_FALLBACK_MODEL=qwen2.5:7b`.
`python -m contract_check --help` работает. Теги без `-instruct`.
### T-E0-002 — Экстрактор текста (PDF/DOCX)
**Статус:** done · **Оценка:** S
`src/contract_check/core/analysis/extractor.py`; покрыт `tests/unit/test_extractor.py`.
### T-E0-003 — Чек-лист анализа (v1)
**Статус:** done · **Оценка:** S
`src/contract_check/core/analysis/checklist.py` — 10 пунктов с `id/title/description`.
### T-E0-004 — Ollama Cloud-клиент + repair-loop
**Статус:** done · **Оценка:** M
`src/contract_check/core/llm/ollama_cloud.py`; мок-тесты 200/429-fallback/битый-JSON в `tests/unit/test_llm_ollama_cloud.py`.
### T-E0-005 — Промпт + JSON-отчёт (с чанками)
**Статус:** done · **Оценка:** M
`core/analysis/analyzer.py`, `chunker.py`, `report_schema.py`; disclaimer в markdown.
### T-E0-006 — Замеры метрик и economics go/no-go
**Статус:** todo · **Оценка:** M
Требует живых прогонов 35 договоров через Ollama Cloud + `docs/stage0-results.md`.
---
## Этап 1 — Production refactor (api + core + workers + bot)
> Архитектура: hexagonal, RabbitMQ pipeline (`extract.q` → `analyze.q`), MinIO, Postgres, Redis.
> Профили Docker Compose: default = инфра; `services` = api + workers + bot.
### T-E1-001 — Ядро `contract_check.core`
**Статус:** done · **Оценка:** L
Пакет `src/contract_check/core/` содержит `db/`, `mq/`, `s3/`, `llm/`, `analysis/`, `credits.py`, `tokens.py`, `config.py`, `logging.py`, `metrics.py`, `telemetry.py`, `sentry.py`.
### T-E1-002 — Миграции БД (6 таблиц)
**Статус:** done · **Оценка:** M
`migrations/versions/0001_initial.py`: `users`, `documents`, `reports`, `jobs`, `service_tokens`, `invoices` (stub).
### T-E1-003 — FastAPI ядро: документы, кредиты, enqueue, отчёты
**Статус:** done · **Оценка:** L
`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`).
### T-E1-004 — Worker-extract (CPU: pymupdf + tesseract)
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-001, T-E1-002
`src/contract_check/worker_extract/` (`consumer.py`/`handler.py`/`extract_document.py`/`__main__.py`):
consume `DocumentUploaded` из `extract.q` → скачать blob из MinIO →
`extractor` + `ocr.ocr_pdf()` → загрузить `.txt` → publish `DocumentExtracted` в `analyze.q`.
Failure-classification + refund-on-DLQ. Образ `srv/worker-extract/Dockerfile`.
### T-E1-005 — Worker-analyze (I/O: LLM provider)
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-001, T-E1-002, T-E1-004
`src/contract_check/worker_analyze/` (`consumer.py`/`handler.py`/`__main__.py`):
consume `DocumentExtracted` из `analyze.q` → скачать `.txt` → chunk → LLM (через `core/llm` port)
→ validate/repair → сохранить `Report`, `status=done`; refund при terminal failure (`core/credits.py`).
Образ `srv/worker-analyze/Dockerfile`.
### T-E1-006 — Telegram-бот (aiogram 3) — HTTP-адаптер
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-003
`src/contract_check/bot/` (`client.py`/`config.py`/`handlers.py`/`__main__.py`):
`/start`, приём PDF/DOCX → `POST /api/v1/documents`, poll `GET /api/v1/reports/{id}`
→ отправка отчёта (разбивка/файл >4096 симв.), реакция на 402/400/202.
Образ `srv/bot/Dockerfile`. **Не импортирует** `core.db`/`core.s3`/`core.llm`/`core.mq`/`core.credits`
(проверяется `tests/unit/test_bot_boundary.py`).
### T-E1-007 — Docker Compose: сервисы и профили
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-004, T-E1-005, T-E1-006
`docker-compose.yml`: default-профиль = инфра (`postgres`, `redis`, `rabbitmq`, `minio`, `minio-init`);
профиль `services` = `api`, `worker-extract`, `worker-analyze`, `bot` с `depends_on: service_healthy`.
Все 5 Dockerfile-ов в `srv/`. Профили `obs`/`edge` — позже (T-E1-009/T-E1-010).
### T-E1-008 — Оплата (ЮKassa) и пополнение кредитов
**Статус:** todo · **Оценка:** M · **Зависимости:** T-E1-003, T-E1-006
Роут для создания платежа 199 ₽ = 1 документ; webhook `succeeded``credits_left += 1`.
### T-E1-009 — Деплой + edge (Nginx/certbot)
**Статус:** todo · **Оценка:** M · **Зависимости:** T-E1-007
`deploy/nginx/nginx.conf`, `deploy/nginx/certbot-init.sh`, `DEPLOY.md`, `deploy.sh`.
### T-E1-010 — Мониторинг (Prometheus/Grafana/Tempo/Sentry)
**Статус:** todo · **Оценка:** M · **Зависимости:** T-E1-007
`deploy/observability/` + compose профиль `obs`. Дашборды: queue depth, job latency, LLM tokens, credits.
---
## Этап 2 — Веб + подписки
### T-E2-001 — Миграция БД: планы и счета
**Статус:** todo · **Оценка:** M · **Зависимости:** T-E1-002
`users.plan` (`free|solo|team`), `users.plan_renews_at`. Таблица `invoices` уже существует (stub); наполнить логикой.
### T-E2-002 — Telegram Login Widget auth
**Статус:** 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, что и бот.
### T-E2-003 — React SPA (Vite)
**Статус:** todo · **Оценка:** L · **Зависимости:** T-E2-002
Загрузка, история, профиль, подписка. Сборка в статику; `api` отдаёт `index.html` на `/`.
### T-E2-004 — Подписки ЮKassa recurring
**Статус:** todo · **Оценка:** M · **Зависимости:** T-E2-001, T-E2-003
Тарифы solo (1 490 ₽), team (3 990 ₽). Ежемесячное продление/reset.
---
## Этап 3 — B2B API
> **Примечание:** в текущей архитектуре B2B API строится **не после веба**, а поверх уже готового `api` и существующей очереди.
> Зависимость от T-E2-002 снята.
### T-E3-001 — Миграция БД: `api_keys` и `api_key_requests`
**Статус:** done · **Оценка:** M · **Зависимости:** T-E1-002
`migrations/versions/0002_api_keys.py`: таблицы
- `api_keys(id, user_id FK, name, key_hash, rate_limit_rps, monthly_quota, monthly_used, resets_at, revoked, created_at, last_used_at)`
- `api_key_requests(id, api_key_id FK, document_id FK, created_at)`
Накатывается/откатывается чисто; SQLAlchemy-модели в `core/db/models.py`.
### T-E3-002 — Redis-клиент и token-bucket rate limiter
**Статус:** done · **Оценка:** M · **Зависимости:** T-E3-001
`core/redis_client.py` (async Redis из `redis_url`), `core/rate_limit.py` (token bucket per `api_key_id`,
с `MemoryRateLimiter`-фолбэком для unit-тестов без Redis). Лимит по умолчанию = `B2B_DEFAULT_RATE_LIMIT_RPS` (3),
переопределяется `api_keys.rate_limit_rps`. Покрыт `tests/unit/test_rate_limit.py`; 429 при превышении.
### T-E3-003 — `X-API-Key` auth dependency
**Статус:** done · **Оценка:** M · **Зависимости:** T-E3-001, T-E3-002
`api/deps.py`: `require_api_key` — проверяет `X-API-Key` по `key_hash` (`core/api_keys.py`),
отклоняет revoked, обновляет `last_used_at`, применяет rate-limit. `api/routes/b2b.py`:
`POST /api/v1/analyze`, `GET /api/v1/b2b/reports/{document_id}`, `GET /api/v1/b2b/usage`.
Покрыт `tests/integration/test_b2b_api.py` (вкл. 401/429).
### T-E3-004 — Управление API-ключами (user JWT auth)
**Статус:** done · **Оценка:** S · **Зависимости:** T-E3-001
`api/routes/b2b.py` под user JWT auth: `POST /api/v1/b2b/keys` (plaintext ключ возвращается
**только один раз**), `GET /api/v1/b2b/keys`, `POST /api/v1/b2b/keys/{id}/revoke`,
`GET /api/v1/b2b/keys/{id}/usage`. Revoke мгновенно отключает аутентификацию.
### T-E3-005 — Интеграционные тесты B2B API
**Статус:** done · **Оценка:** M · **Зависимости:** T-E3-003
`tests/integration/test_b2b_api.py`: upload → 202 → poll report; ветки 401/429.
Seed API key в `tests/integration/conftest.py`.
---
## Сводка порядка (обновлённая)
1. **E0** — прототип (done, кроме живых замеров T-E0-006).
2. **E1.001007** — ядро + БД + FastAPI `api` + worker-extract + worker-analyze + bot + Docker/compose (done).
3. **E3** — B2B API (done: миграция, rate-limit, auth, роуты, тесты).
4. **E1.008** — оплата ЮKassa (todo — блокирует первых платящих).
5. **E1.009010** — деплой/edge (Nginx/certbot) и observability (Prometheus/Grafana/Tempo/Sentry) (todo).
6. **E2** — веб + подписки (todo; после 1050 платящих).
**Ближайшие работы:** E1.008 (оплата), затем E1.009010 (edge + observability), затем E2 (веб + подписки).