From a8b6a5bb6b0452023eb0a0fbc43ba5cb83ff3033 Mon Sep 17 00:00:00 2001 From: linyq Date: Fri, 3 Apr 2026 02:45:33 +0800 Subject: [PATCH] fix(documentary): fail on malformed narration payload --- .../documentary/frame_analysis_service.py | 17 ++------ ...est_script_service_documentary_unittest.py | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/app/services/documentary/frame_analysis_service.py b/app/services/documentary/frame_analysis_service.py index a6dc92e..d4d36ee 100644 --- a/app/services/documentary/frame_analysis_service.py +++ b/app/services/documentary/frame_analysis_service.py @@ -188,25 +188,16 @@ JSON 必须包含以下键: if start >= 0 and end > start: parsed = load_json_candidate(cleaned[start : end + 1]) - items = [] + items: list[dict[str, Any]] = [] if isinstance(parsed, dict): raw_items = parsed.get("items") if isinstance(raw_items, list): items = [item for item in raw_items if isinstance(item, dict)] - if items: - return items + if not items: + raise ValueError("解说文案格式错误,无法解析JSON或缺少items字段") - fallback_text = (cleaned[:200] + "...") if len(cleaned) > 200 else cleaned - if not fallback_text: - fallback_text = "解说文案解析失败,请重试。" - return [ - { - "timestamp": "00:00:00,000-00:00:10,000", - "picture": "解析失败,使用默认内容", - "narration": fallback_text, - } - ] + return items def _resolve_frame_interval(self, frame_interval_input: int | float | None) -> float: interval = frame_interval_input diff --git a/tests/test_script_service_documentary_unittest.py b/tests/test_script_service_documentary_unittest.py index d39b572..7b63b77 100644 --- a/tests/test_script_service_documentary_unittest.py +++ b/tests/test_script_service_documentary_unittest.py @@ -141,6 +141,48 @@ class DocumentaryFrameAnalysisServiceScriptGenerationTests(unittest.IsolatedAsyn self.assertEqual("一只猫警觉地望向镜头。", result[0]["narration"]) self.assertEqual(2, result[0]["OST"]) + async def test_generate_documentary_script_raises_when_narration_json_is_malformed(self): + service = DocumentaryFrameAnalysisService() + analysis_payload = { + "batches": [ + { + "batch_index": 0, + "time_range": "00:00:00,000-00:00:03,000", + "overall_activity_summary": "测试摘要", + "fallback_summary": "", + "frame_observations": [ + {"timestamp": "00:00:00,000", "observation": "镜头里有一只猫"}, + ], + } + ] + } + + with TemporaryDirectory() as temp_dir: + analysis_path = Path(temp_dir) / "frame_analysis_test.json" + analysis_path.write_text(json.dumps(analysis_payload, ensure_ascii=False), encoding="utf-8") + + with patch.object( + DocumentaryFrameAnalysisService, + "analyze_video", + AsyncMock(return_value={"analysis_json_path": str(analysis_path)}), + ), patch.dict( + "app.services.documentary.frame_analysis_service.config.app", + { + "text_llm_provider": "openai", + "text_openai_api_key": "test-key", + "text_openai_model_name": "test-model", + "text_openai_base_url": "https://example.com/v1", + }, + ), patch( + "app.services.documentary.frame_analysis_service.generate_narration", + return_value="malformed narration payload", + ): + with self.assertRaises(Exception) as ctx: + await service.generate_documentary_script(video_path="demo.mp4") + + self.assertIn("解说文案格式错误", str(ctx.exception)) + self.assertIn("items", str(ctx.exception)) + if __name__ == "__main__": unittest.main()