From 6994f234cbcafccffd599640892173e89e0fa51b Mon Sep 17 00:00:00 2001 From: lhl Date: Thu, 13 Aug 2026 09:55:01 +0800 Subject: [PATCH] feat(writer): add GenerationContext.to_vars + ChapterContent.from_llm (T6 deps) --- src/genesis/writer/models.py | 42 ++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/genesis/writer/models.py b/src/genesis/writer/models.py index a89bfa8..e05cf74 100644 --- a/src/genesis/writer/models.py +++ b/src/genesis/writer/models.py @@ -21,6 +21,22 @@ class ContentBlock: style: str | None = None source_uris: list[str] = field(default_factory=list) + @classmethod + def from_dict(cls, block_id: str, data: dict) -> "ContentBlock": + """从 LLM 输出的 block dict 安全构造内容块。""" + return cls( + block_id=str(block_id), + type=data.get("type", "paragraph"), + level=data.get("level"), + text=data.get("text"), + caption=data.get("caption"), + headers=data.get("headers"), + rows=data.get("rows"), + items=data.get("items"), + style=data.get("style"), + source_uris=data.get("source_uris", []), + ) + @dataclass class ChapterContent: @@ -29,6 +45,17 @@ class ChapterContent: title: str blocks: list[ContentBlock] + @classmethod + def from_llm(cls, chapter_id: str, title: str, data: dict) -> "ChapterContent": + """从 LLM 结构化输出(含 title、blocks 列表)构造章节内容。 + + 对每个 block dict 用 ContentBlock.from_dict 安全取值;block_id 缺省为序号字符串。 + """ + blocks: list[ContentBlock] = [] + for i, b in enumerate(data.get("blocks", [])): + blocks.append(ContentBlock.from_dict(str(b.get("block_id", i)), b)) + return cls(chapter_id=chapter_id, version=1, title=title, blocks=blocks) + @dataclass class ChapterSpec: @@ -49,3 +76,18 @@ class GenerationContext: design_rules: list[str] template_styles: set[str] prior_state: object | None = None # WriterState,避免循环 import 用 object + + def to_vars(self) -> dict: + """返回供 prompt 渲染的变量字典。""" + tm = self.template_marker + template_marker = f"{tm.chapter_id}:{tm.title}" if tm is not None else "" + return { + "chapter_id": self.chapter_id, + "title": self.title, + "template_marker": template_marker, + "write_rules": "\n".join(self.write_rules), + "design_rules": "\n".join(self.design_rules), + "template_styles": ", ".join(sorted(self.template_styles)), + "prior_state": str(self.prior_state) if self.prior_state is not None else "", + "source": str(self.structured_source) if self.structured_source is not None else "", + }