feat(api): add memoir image placeholder parsing
Made-with: Cursor
This commit is contained in:
0
api/services/memoir_images/__init__.py
Normal file
0
api/services/memoir_images/__init__.py
Normal file
49
api/services/memoir_images/parser.py
Normal file
49
api/services/memoir_images/parser.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
PLACEHOLDER_RE = re.compile(r"\{\{\{\{IMAGE:(.*?)\}\}\}\}")
|
||||
|
||||
|
||||
def parse_image_placeholders(content: str, max_images: int) -> list[dict[str, Any]]:
|
||||
items: list[dict[str, Any]] = []
|
||||
for match in PLACEHOLDER_RE.finditer(content or ""):
|
||||
description = match.group(1).strip()
|
||||
if not description:
|
||||
continue
|
||||
items.append(
|
||||
{
|
||||
"index": len(items),
|
||||
"description": description,
|
||||
"placeholder": match.group(0),
|
||||
"start_offset": match.start(),
|
||||
}
|
||||
)
|
||||
if len(items) >= max_images:
|
||||
break
|
||||
return items
|
||||
|
||||
|
||||
def build_initial_image_assets(
|
||||
placeholders: list[dict[str, Any]],
|
||||
provider: str,
|
||||
style: str,
|
||||
size: str,
|
||||
now_iso: str,
|
||||
) -> list[dict[str, Any]]:
|
||||
return [
|
||||
{
|
||||
"index": item["index"],
|
||||
"placeholder": item["placeholder"],
|
||||
"description": item["description"],
|
||||
"prompt": None,
|
||||
"url": None,
|
||||
"status": "pending",
|
||||
"provider": provider,
|
||||
"style": style,
|
||||
"size": size,
|
||||
"error": None,
|
||||
"created_at": now_iso,
|
||||
"updated_at": now_iso,
|
||||
}
|
||||
for item in placeholders
|
||||
]
|
||||
45
api/tests/test_memoir_image_parser.py
Normal file
45
api/tests/test_memoir_image_parser.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import unittest
|
||||
|
||||
from api.services.memoir_images.parser import (
|
||||
build_initial_image_assets,
|
||||
parse_image_placeholders,
|
||||
)
|
||||
|
||||
|
||||
class MemoirImageParserTest(unittest.TestCase):
|
||||
def test_parse_image_placeholders_preserves_order_and_offsets(self):
|
||||
content = (
|
||||
"那条路我一直记得。\n\n"
|
||||
"{{{{IMAGE:南方小镇的青石板路}}}}\n\n"
|
||||
"奶奶总坐在门口。\n\n"
|
||||
"{{{{IMAGE:奶奶坐在院子里的藤椅上}}}}"
|
||||
)
|
||||
|
||||
items = parse_image_placeholders(content, max_images=3)
|
||||
|
||||
self.assertEqual([item["index"] for item in items], [0, 1])
|
||||
self.assertEqual(items[0]["description"], "南方小镇的青石板路")
|
||||
self.assertEqual(items[1]["placeholder"], "{{{{IMAGE:奶奶坐在院子里的藤椅上}}}}")
|
||||
self.assertLess(items[0]["start_offset"], items[1]["start_offset"])
|
||||
|
||||
def test_build_initial_image_assets_marks_every_item_pending(self):
|
||||
placeholders = [
|
||||
{
|
||||
"index": 0,
|
||||
"description": "南方小镇的青石板路",
|
||||
"placeholder": "{{{{IMAGE:南方小镇的青石板路}}}}",
|
||||
"start_offset": 10,
|
||||
}
|
||||
]
|
||||
|
||||
assets = build_initial_image_assets(
|
||||
placeholders=placeholders,
|
||||
provider="liblib",
|
||||
style="watercolor",
|
||||
size="1024x1024",
|
||||
now_iso="2026-03-10T10:00:00Z",
|
||||
)
|
||||
|
||||
self.assertEqual(assets[0]["status"], "pending")
|
||||
self.assertEqual(assets[0]["provider"], "liblib")
|
||||
self.assertEqual(assets[0]["url"], None)
|
||||
Reference in New Issue
Block a user