From 3efe025b51e338130f49dc6aa1ad09d64900d3a5 Mon Sep 17 00:00:00 2001 From: Cowork 3P Date: Fri, 22 May 2026 16:14:39 +0000 Subject: [PATCH] fix(planning): handle empty LLM response in macro plan parsing (#162) When using glm-4.5-air or other models that occasionally return empty responses, _parse_llm_response would throw a cryptic JSONDecodeError instead of a clear error message. Add a check for empty cleaned content before attempting json.loads(), raising a descriptive ValueError that autopilot_daemon can handle gracefully via its existing error handling. --- .../blueprint/services/continuous_planning_service.py | 5 +++++ application/core/services/export_service.py | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/application/blueprint/services/continuous_planning_service.py b/application/blueprint/services/continuous_planning_service.py index a4db1f3ca..6f36f2648 100644 --- a/application/blueprint/services/continuous_planning_service.py +++ b/application/blueprint/services/continuous_planning_service.py @@ -1868,6 +1868,11 @@ def _parse_llm_response(self, response) -> Dict: cleaned = _sanitize_llm_json_output(content) cleaned = _extract_outer_json_value(cleaned) + + # ★ 修复 #162:LLM 返回空响应时提前返回友好错误,而非抛 JSONDecodeError + if not cleaned: + raise ValueError("LLM 返回内容为空,无法解析规划数据") + try: return json.loads(cleaned) except json.JSONDecodeError: diff --git a/application/core/services/export_service.py b/application/core/services/export_service.py index c12f2118a..e606a9fd0 100644 --- a/application/core/services/export_service.py +++ b/application/core/services/export_service.py @@ -92,6 +92,11 @@ def export_novel(self, novel_id: str, format: str) -> Tuple[bytes, str, str]: chapters = self.chapter_repository.list_by_novel(NovelId(novel_id)) chapters.sort(key=lambda x: x.number) logger.info("导出: %s, 章节数 %s", novel.title, len(chapters)) + # ★ 修复 #143:记录每章内容长度,便于排查导出内容为空的问题 + for ch_debug in chapters: + ch_len = len(ch_debug.content or "") + if ch_len == 0: + logger.warning("导出警告: 章节 %d (%s) 内容为空", ch_debug.number, ch_debug.title or "无标题") if format == "epub": result = self._export_to_epub(novel, chapters) elif format == "pdf": @@ -192,7 +197,9 @@ def _export_to_epub(self, novel: Novel, chapters: list[Chapter]) -> Tuple[bytes, book.toc = tuple([intro] + spine_items[1:]) book.add_item(epub.EpubNcx()) - # 不使用空 EpubNav(ebooklib 生成 nav 时会解析正文,空文档会触发 lxml Document is empty) + # ★ 修复 #143:添加 EpubNav 导航文档(EPUB 3 标准要求) + # 缺失 nav 文档会导致部分阅读器只显示目录而无法加载正文内容 + book.add_item(epub.EpubNav()) book.spine = spine_items tmp_path: Optional[str] = None