Стабилизация, итерация 7: инкрементальный рендер стрима + append-персист шагов (один PR, 2 коммита) #492
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Один PR. Зависит от #491 (контракт
reconstructRunParts— коммит 2 меняет его бэкенд). Координаты — develop@363f20ab.Коммит 1 — perf(client): инкрементальный markdown-рендер стримящегося ответа
⚠️ Обновлено после мержа #498: рендер-пайплайн чата сменился —
renderChatMarkdownтеперь идёт через канонический конвертер (markdownToProseMirrorSync→PMNode.fromJSON→DOMSerializer.serializeFragment→DOMPurify.sanitize, см.apps/client/src/features/ai-chat/utils/markdown.ts), а не через marked. Пайплайн остался синхронным per-render и вызывается на КАЖДЫЙ throttled-тик (~20 Гц) по всему накопленному тексту растущего part'а — и он ТЯЖЕЛЕЕ старого marked-пути (полный канонический parse + построение PM-нод + DOM-сериализация + sanitize на тик). Хотспот «печатает рывками» после #498 предположительно стал горячее — перед началом работ снять perf-профиль стриминга на текущем develop (число вызовов renderChatMarkdown × стоимость на 100 KB-ответе).Правка (суть не изменилась): применить
StreamingPlainText-паттерн (chunked memo, уже используется для reasoning) к answer-пути — стабилизированная часть (до последнего безопасного разреза: конец markdown-блока) рендерится мемоизированным каноническим пайплайном; живой хвост — плоским текстом; финальный полный рендер один раз на finish. Плюс: сигнатура memoMessageItem→ exhaustive-switch по union part-kinds (compile-time защита вместо WARNING-комментария).Тесты: perf-смок (вызовы renderChatMarkdown на синтетическом 100 KB стриме — O(число блоков), не O(число тиков)); визуальный регресс финального рендера (учесть
<li><p>-обёртки схемы и scoped CSS из #498).Коммит 2 — perf(ai-chat): append-персист — per-step INSERT вместо полной перезаписи
Сейчас каждый
onStepFinishпереписывает ВСЮ строку (O(n²) объёма за ран, autovacuum-чарн). Правка: per-step INSERT в отдельную лёгкую таблицу шагов; полная строка собирается один раз на finalize. Вариант «jsonb-append||» — НЕ вариант: UPDATE jsonb в Postgres из-за MVCC переписывает TOAST-версию строки целиком — WAL/чарн (весь заявленный вред) остаётся,||экономит только сетевой payload; тест на мок-репо этого не увидит.Обязательные стыковки:
reconstructRunParts(runId) → {parts, stepsPersisted}(введён в #491) переключает бэкенд: parts строки ДО этого коммита, таблица шагов ПОСЛЕ — потребители (attach, дельта, экспорт) не меняются;toolTraceVersion(#490) перестаёт быть консистентным через полную перезапись (её больше нет) — проверить путь простановки.Тест: объём записи через дельту
pg_current_wal_lsn()на живой БД — O(Σ шагов), не O(квадрата); регресс attach/дельты/экспорта на живом ране через контракт.