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

4.6 KiB
Raw Blame History

Архитектура 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_idtarget_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.