1.2.2
Fixed (#381)
- Висячие записи
llm_request_logбольше не выглядят сбоем при открытии тела (#381, вариант (a) из issue): файлы тел живут 14 дней, индекс — 30, и записи возрастом 14–30 дней указывали на удалённые файлы (12k строк на проде 29.08.2026), UI получал 404 «log body unavailable». Backend:llmlog.PurgeOldLogsвозвращаетPurgeResult{Purged, RemovedKeys}— ключи удалённых датированных файлов в формате<agentID>/<YYYY-MM-DD>(совпадает сfile_pathв БД); новый метод стораClearLLMRequestLogBodies(keys)(интерфейс + SQLite чанками ≤500 плейсхолдеров + PG= ANY($1::text[])+ DualStore) обнуляетfile_pathу записей с удалёнными файлами — метаданные (модель, токены, статус, длительность) остаются доступны для аналитики все 30 дней;cleanup_serviceвызывает его после purge файлов (логretention purge table=llm_request_log_bodies).LoadEntryBodyпри пустомfile_pathвозвращает сентинелErrBodyRotated; хендлерGET /api/v1/admin/llm-log/{id}наErrBodyRotated/os.IsNotExist(второе покрывает существующие legacy-записи без миграции данных) отвечает 410 Gone{"error":"log_body_rotated","message":"Тело запроса удалено ротацией…"}вместо 404; 404 остаётся только для «запись не найдена» (черезwriteJSONStatus). API-клиент: ошибкиrequest/rawRequestтеперь несут HTTP-статус (ApiError.statusчерезapiError()), не только текст. Frontend (/settings/llm-log): при пустомfile_pathзапись сразу показывает «Тело запроса удалено ротацией…» без запроса к API; 410 в catch переводит в тот же баннер (единый UX), прочие ошибки — серверный текст вtext-error-text+ кнопка «Повторить»; состояние (expandedError/expandedRotated) сбрасывается при collapse/смене страницы/refresh (хелперresetExpanded), ранний выход инкрементируетexpandSeq(in-flight запрос предыдущей строки не перезапишет состояние текущей). Тесты:TestPurgeOldLogs(удаление + точный формат RemovedKeys + сохранение свежих/недатированных),TestPurgeOldLogs_MissingBaseDir,TestLoadEntryBody_EmptyFilePathMeansRotated,TestSQLiteStore_ClearLLMRequestLogBodies(обнуление 2 из 3, no-op на пустом списке), handler-тесты 410 (пустой путь и legacy-несуществующий файл) и 404. ocr review — 11 находок: 7 закрыты (writeJSONStatus вместо inline-encode, expandSeq в ранней ветке, статус в ApiError → ветвление 410 без сниффинга текста, сброс expanded-состояния, text-error-text, лог не-IsNotExist ошибок os.Remove, JSDoc типов), прочие low отклонены (дубль русского текста баннера неизбежен при раннем выходе без запроса; regex-тест формата ключей избыточен — формат уже зафиксирован точным сравнением в TestPurgeOldLogs).