Files
contracts/DOC/architecture-contracts-flask.md
T

92 lines
4.6 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.
# Архитектура contracts-flask (актуально на v2.0.16)
Сервис сверки договоров. Flask + vanilla JS (без frontend-фреймворков).
Managed на Nubes (Штурвал), redeploy через панель (не kubectl).
## Компоненты
```
Браузер (vanilla JS: files.js, compare.js, app.js)
├─ PUT файла → ВМ-буфер (WebDAV /contracts-upload/)
│ Браузер кладёт файл на ВМ, т.к. managed-gateway рвёт тело >64 КБ.
│ ВМ: 5.172.178.213, домен contracts.kube5s.ru.
├─ /, /static/*, /templates/* → Managed Flask
│ Отдаёт HTML + JS.
└─ API (SQLite, WAL) → LLM (api.aillm.ru, gpt-oss-120b)
/api/upload_refs — бэк сам тянет файл с ВМ (egress не лимитирован)
/process-v2 (SSE) — конвейер сравнения
/api/classify-* — классификация
/chat — чат по спецификации
```
## БД
- **SQLite**, WAL-режим, `/tmp/contracts.db`.
- Thread-local соединения (`db/connection.py`), API совместим с PostgreSQL
через `_pg_to_sqlite()`.
- Таблицы: `documents`, `supplements`, `spec_current`, `spec_events`,
`prompts`, `upload_chunks`.
## Event sourcing (спецификация)
- `spec_events` — append-only журнал операций (история).
- `spec_current` — материализованное текущее состояние (для рендера/сравнения).
- `_hash(name, date_start)` = sha256(name.strip().lower() + "|" + normalize_date)[:16] — ключ строки.
- `apply_ops()` обрабатывает ADD / UPDATE / DELETE / UNRESOLVED.
## Загрузка файлов
1. `uploadFile()` (files.js) — браузер PUT файла в ВМ-буфер (WebDAV).
2. `POST /api/upload_refs {files:[{name,size,url}]}` — бэк тянет файл с ВМ
по `url` (egress без лимита), конвертирует `.doc``.docx`
(`contracts_upload_sink`), парсит и кладёт в БД.
3. Парсинг: pdfplumber / python-docx → `elements_json`.
## ZIP (важно: клиентское раскрытие)
⚠️ ZIP раскрывается **на клиенте**, НЕ серверным `/unzip-upload` (тот — legacy).
- `expandZipClient(f)` (files.js) — рекурсивно раскрывает архив через
`window.listZipFiles` (endpoint drhider, подтянут в браузере).
- Каждый вложенный файл получает `zip_source = имя архива` для группировки
в таблице.
- Fallback: если раскрыть не удалось — архив кладётся как есть.
- Фильтр вложений: `.pdf`, `.doc`, `.docx`.
## Сравнение (`/process-v2`, SSE)
1. `run_pipeline()` шлёт SSE-события браузеру.
2. Текущая спецификация → текст → LLM → `{mode, ops[]}`.
3. **Трансляция `target_id` → `target_hash`**: LLM возвращает `target_id: "r1"`
(индекс строки), а `apply_ops()` читает `target_hash`. Без трансляции
UPDATE/DELETE уходили бы в UNRESOLVED.
4. `apply_ops()` пишет `spec_events` + обновляет `spec_current`.
### Режимы LLM
- `partial` — точечные ADD/UPDATE/DELETE (UPDATE/DELETE по `target_hash`).
- `full_replace` — LLM возвращает полную новую редакцию (все ADD).
⚠️ Перед применением очищается **только** `spec_current` (`clear_current()`),
чтобы ADD-строки новой редакции не дублировали старые. История
`spec_events` сохраняется.
## Классификация
- `api.aillm.ru`, модель `gpt-oss-120b` (конфиденциальные данные — только своя модель).
- `ThreadPoolExecutor(max_workers=4)`.
- Поля: `doc_type`, `own_number`, `parent_number`, `counterparty`, `doc_date`.
## Конфигурация
`site/config.py`: VERSION, LLM_URL/KEY/MODEL, CONVERT_SERVICE_URL,
VM_UPLOAD_URL/VM_UPLOAD_PREFIX/VM_UPLOAD_MAX_BYTES (50 МБ), PULL_RETRIES (3).
## Модуль `upload/`
Переиспользован из drhider (слои 1–2). Плаг-ин через `sink`:
`create_upload_refs_blueprint(cfg, sink=contracts_upload_sink)`.
Слои 34 (in-memory session) — НЕ используются в contracts.