4.6 KiB
4.6 KiB
Архитектура 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.
Загрузка файлов
uploadFile()(files.js) — браузер PUT файла в ВМ-буфер (WebDAV).POST /api/upload_refs {files:[{name,size,url}]}— бэк тянет файл с ВМ поurl(egress без лимита), конвертирует.doc→.docx(contracts_upload_sink), парсит и кладёт в БД.- Парсинг: 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)
run_pipeline()шлёт SSE-события браузеру.- Текущая спецификация → текст → LLM →
{mode, ops[]}. - Трансляция
target_id→target_hash: LLM возвращаетtarget_id: "r1"(индекс строки), аapply_ops()читаетtarget_hash. Без трансляции UPDATE/DELETE уходили бы в UNRESOLVED. 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).
Слои 3–4 (in-memory session) — НЕ используются в contracts.