diff --git a/backend/package/yuxi/plugins/parser/unified.py b/backend/package/yuxi/plugins/parser/unified.py index 7e263256..77cbf488 100644 --- a/backend/package/yuxi/plugins/parser/unified.py +++ b/backend/package/yuxi/plugins/parser/unified.py @@ -127,31 +127,25 @@ def _convert_with_docling(file_path: Path, params: dict | None = None) -> str: doc = result.document if hasattr(doc, "pictures") and doc.pictures: - image_refs: list[tuple[str, bytes]] = [] - + replacements: list[str] = [] for pic in doc.pictures: - if hasattr(pic, "image") and hasattr(pic.image, "uri"): - uri = str(pic.image.uri) - if uri.startswith("data:"): + uri = str(pic.image.uri) if hasattr(pic, "image") and hasattr(pic.image, "uri") else "" + if uri.startswith("data:"): + filename = "image" + try: image_data, mime_type = _parse_data_uri(uri) - timestamp = int(time.time() * 1000000) - filename = f"image_{timestamp}.{mime_type.split('/')[-1]}" - image_refs.append((filename, image_data)) - - image_urls: list[str] = [] - for filename, image_data in image_refs: - try: - url = _upload_image_to_minio(image_data, filename, image_bucket, image_prefix) - image_urls.append(f"![{filename}]({url})") - except Exception as e: # noqa: BLE001 - logger.error(f"上传图片失败 {filename}: {e}") - image_urls.append(f"[图片: {filename}]") + filename = f"image_{int(time.time() * 1000000)}.{mime_type.split('/')[-1]}" + url = _upload_image_to_minio(image_data, filename, image_bucket, image_prefix) + replacements.append(f"![{filename}]({url})") + except Exception as e: # noqa: BLE001 + logger.error(f"上传图片失败 {filename}: {e}") + replacements.append(f"[图片: {filename}]") + else: + replacements.append("") markdown = doc.export_to_markdown() - - for url in image_urls: - markdown = re.sub(r"", url, markdown, count=1) - + for replacement in replacements: + markdown = re.sub(r"", replacement, markdown, count=1) return markdown return doc.export_to_markdown() diff --git a/backend/test/unit/plugins/test_parser_facade.py b/backend/test/unit/plugins/test_parser_facade.py index 21857308..e3d1d4ce 100644 --- a/backend/test/unit/plugins/test_parser_facade.py +++ b/backend/test/unit/plugins/test_parser_facade.py @@ -76,9 +76,10 @@ def test_convert_with_docling_reinserts_image_links_in_document_order( fake_doc = SimpleNamespace( pictures=[ SimpleNamespace(image=SimpleNamespace(uri=f"data:image/png;base64,{first_image}")), + SimpleNamespace(image=SimpleNamespace(uri="https://example.test/remote.png")), SimpleNamespace(image=SimpleNamespace(uri=f"data:image/png;base64,{second_image}")), ], - export_to_markdown=lambda: "before\n\nbetween\n\nafter", + export_to_markdown=lambda: "before\n\nremote\n\nbetween\n\nafter", ) fake_result = SimpleNamespace(status=SimpleNamespace(name="SUCCESS"), document=fake_doc) uploaded_images: list[bytes] = [] @@ -103,12 +104,44 @@ def test_convert_with_docling_reinserts_image_links_in_document_order( assert markdown == ( "before\n" "![image_1000000.png](https://example.test/1.png)\n" + "remote\n" + "\n" "between\n" "![image_2000000.png](https://example.test/2.png)\n" "after" ) +def test_convert_with_docling_keeps_image_placeholder_when_upload_fails( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, +): + file_path = tmp_path / "parser_test.docx" + file_path.write_bytes(b"fake docx") + image = base64.b64encode(b"image data").decode() + fake_doc = SimpleNamespace( + pictures=[SimpleNamespace(image=SimpleNamespace(uri=f"data:image/png;base64,{image}"))], + export_to_markdown=lambda: "before\n\nafter", + ) + fake_result = SimpleNamespace(status=SimpleNamespace(name="SUCCESS"), document=fake_doc) + + class FakeConverter: + def convert(self, path: Path): + assert path == file_path + return fake_result + + def _raise_upload_error(*args, **kwargs): + raise RuntimeError("upload failed") + + monkeypatch.setattr(parser_unified, "_get_docling_converter", lambda: FakeConverter()) + monkeypatch.setattr(parser_unified, "_upload_image_to_minio", _raise_upload_error) + monkeypatch.setattr(parser_unified.time, "time", lambda: 1.0) + + markdown = parser_unified._convert_with_docling(file_path) + + assert markdown == "before\n[图片: image_1000000.png]\nafter" + + def test_parser_parse_png_file_returns_markdown_text_with_mocked_ocr( tmp_path: Path, monkeypatch: pytest.MonkeyPatch,