Files
contracts/History/session-05-sse-parsing.md
T

114 lines
6.1 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.
# Сессия #5 — SSE-парсинг, интерактивный прогресс, отказ от LLM
_Дата: 2026-06-16_
_Версия: v1.0 → v1.5 (5 повышений)_
_Коммиты: fa4c7fe → 435f617 (6 шт.)_
---
## Контекст
Пользователь (Наиль) захотел:
1. Видеть **интерактивный прогресс** при обработке файлов (имя + таймер в секундах)
2. После обработки — **сводка**: общее время, байты
3. **Никакого LLM** пока — только парсинг
4. Вспомнил что очередь файлов (JS-слой) уже была реализована в коммите `17c4841`
---
## Шаг 1: SSE-поток для LLM (v1.1, fa4c7fe)
**Что сделано:**
- `_process` в app.py переделан с блокирующего POST на SSE-поток (`text/event-stream`)
- Маршрут `/process/<cid>` сменён на GET (нужно для `EventSource`)
- События: `file_start` → `file_done`/`file_error` → `summary`
- В шаблоне — JavaScript с `EventSource`, живые таймеры (каждые 500мс)
**Ошибка:** Назвал деплой «поды в Kubernetes» — пользователь поправил: это **managed service** на pythonk8s.services.ngcloud.ru, деплой = `git push master`.
**Ошибка:** Забыл повысить версию → v1.1 отдельным коммитом.
---
## Шаг 2: Полный отказ от LLM (v1.2, 3cffca7)
**Причина:** Пользователь сказал «НЕ НАДО ЛЛМ и на экране тоже».
**Что сделано:**
- `_process` → `_parse` — только парсинг, без `extractor.extract()` и `differ.diff()`
- `_upload_files` — только сохраняет файлы в БД, без парсинга на этом этапе
- `POST /` возвращает JSON `{"contract_id": "..."}` вместо редиректа
- Весь LLM (`extractor`, `differ`, `llm_client`) убран из основного потока
**Интерфейс:**
- Файлы → сразу в таблицу (имя, дата изменения, размер)
- ZIP — JSZip показывает содержимое в браузере
- Кнопка **«Парсинг»** (не «Обработать LLM»)
- Клиент: POST файлов через `fetch`, затем SSE `/parse/<cid>`
- Прогресс в столбце «Статус»: ⏳ Xс → ✓ X.Xс
- Итоговая строка: ✓ Готово: N файлов | размер | время
---
## Шаг 3: ZIP — файлы внутри не парсились (v1.3, 21f8c0b)
**Проблема:** ZIP-файл парсился как единое целое (✓ 0.2с), а файлы внутри него (`допник-1.doc`, `спецификация.docx`...) оставались с `—` в статусе.
**Причина:** В `_parse` парсер просто сливал все elements из ZIP в один документ. Файлы из JSZip-превью были только в браузере, сервер про них не знал.
**Решение:**
- Сервер сам открывает ZIP (`zipfile.ZipFile`)
- Для каждого файла внутри: создаёт отдельный `documents` + `supplements`, парсит, шлёт SSE-события
- JSZip и сервер используют одинаковые имена → `findRowByName` находит строку
- ZIP-строка показывает `↗ N файлов` вместо галочки
---
## Шаг 4: Подпапки в ZIP (v1.4, d5858d7)
**Проблема:** Если в ZIP есть подпапки, и в разных папках файлы с одинаковыми именами — конфликт.
**Решение:** Использовать полный относительный путь (`подпапка/файл.docx`) вместо basename.
Изменено в двух местах:
- **Клиент**: `relativePath` вместо `relativePath.split('/').pop()`
- **Сервер**: `zname` вместо `zbasename`
---
## Шаг 5: Только PDF и Word (v1.5, 435f617)
**Проблема:** Из ZIP парсились ВСЕ файлы (включая `.txt`, `.xlsx`), давая пустые результаты.
**Решение:** Фильтр по MIME-типу:
- Сервер: `if zmime not in (pdf, docx, doc): continue`
- Клиент (JSZip): `if ['docx','doc','pdf'].indexOf(ext) === -1: return`
---
## Архитектура (текущая)
```
app.py:
POST / → _upload_files() → сохранить файлы в БД, вернуть JSON {contract_id}
GET / → _index() → render_template("upload.html")
GET /parse/<cid> → _parse() → SSE-поток парсинга
_parse():
1. Для каждого документа договора:
- Обычный файл → parser_mod.parse() → сохранить parsed_text
- ZIP → zipfile.ZipFile() → для каждого PDF/Word внутри:
создать document + supplement → parser_mod.parse() → сохранить
2. SSE-события: file_start → file_done/file_error → zip_expanded → summary
```
---
## Выученные уроки
1. **⛔ Не фантазировать про инфраструктуру.** Managed service ≠ Kubernetes. Деплой = `git push`.
2. **Версия — всегда.** После каждого изменения кода — bump.
3. **ZIP требует особой обработки.** Недостаточно просто распарсить — нужно создать документы для каждого файла внутри.
4. **Имена должны совпадать** между клиентом (JSZip) и сервером (zipfile) — иначе `findRowByName` не находит строку.
5. **`replace_string_in_file` портит файлы** — для больших правок использовать heredoc (`cat > file`).