马一丁

Add Comments

@@ -433,6 +433,14 @@ class ReportAgent: @@ -433,6 +433,14 @@ class ReportAgent:
433 }) 433 })
434 # 章节流式回调:把LLM返回的delta透传给SSE,便于前端实时渲染 434 # 章节流式回调:把LLM返回的delta透传给SSE,便于前端实时渲染
435 def chunk_callback(delta: str, meta: Dict[str, Any], section_ref: TemplateSection = section): 435 def chunk_callback(delta: str, meta: Dict[str, Any], section_ref: TemplateSection = section):
  436 + """
  437 + 章节内容流式回调。
  438 +
  439 + Args:
  440 + delta: LLM最新输出的增量文本。
  441 + meta: 节点回传的章节元数据,兜底时使用。
  442 + section_ref: 默认指向当前章节,保证在缺失元信息时也能定位。
  443 + """
436 emit('chapter_chunk', { 444 emit('chapter_chunk', {
437 'chapterId': meta.get('chapterId') or section_ref.chapter_id, 445 'chapterId': meta.get('chapterId') or section_ref.chapter_id,
438 'title': meta.get('title') or section_ref.title, 446 'title': meta.get('title') or section_ref.title,
@@ -634,6 +634,13 @@ def stream_task(task_id: str): @@ -634,6 +634,13 @@ def stream_task(task_id: str):
634 last_event_id = None 634 last_event_id = None
635 635
636 def event_generator(): 636 def event_generator():
  637 + """
  638 + SSE事件生成器。
  639 +
  640 + - 负责注册并消费对应任务的事件队列;
  641 + - 先回放历史事件再持续监听实时事件;
  642 + - 周期性发送心跳并在任务结束后自动注销监听。
  643 + """
637 queue = _register_stream(task_id) 644 queue = _register_stream(task_id)
638 try: 645 try:
639 # 断线重连场景下,先补发历史事件,保证界面状态一致 646 # 断线重连场景下,先补发历史事件,保证界面状态一致
@@ -34,6 +34,13 @@ class ChapterJsonParseError(ValueError): @@ -34,6 +34,13 @@ class ChapterJsonParseError(ValueError):
34 """章节LLM输出无法解析为合法JSON时抛出的异常,附带原始文本方便排查。""" 34 """章节LLM输出无法解析为合法JSON时抛出的异常,附带原始文本方便排查。"""
35 35
36 def __init__(self, message: str, raw_text: Optional[str] = None): 36 def __init__(self, message: str, raw_text: Optional[str] = None):
  37 + """
  38 + 构造异常并附加原始输出,便于日志中定位。
  39 +
  40 + Args:
  41 + message: 人类可读的错误描述。
  42 + raw_text: 触发异常的完整LLM输出。
  43 + """
37 super().__init__(message) 44 super().__init__(message)
38 self.raw_text = raw_text 45 self.raw_text = raw_text
39 46
@@ -674,6 +681,7 @@ class ChapterGenerationNode(BaseNode): @@ -674,6 +681,7 @@ class ChapterGenerationNode(BaseNode):
674 """ 681 """
675 682
676 def walk(node: Any) -> int: 683 def walk(node: Any) -> int:
  684 + """递归下钻block树并返回字符估算,跳过非正文类型"""
677 if node is None: 685 if node is None:
678 return 0 686 return 0
679 if isinstance(node, list): 687 if isinstance(node, list):
@@ -891,6 +899,7 @@ class ChapterGenerationNode(BaseNode): @@ -891,6 +899,7 @@ class ChapterGenerationNode(BaseNode):
891 fragment_buffer: List[Dict[str, Any]] = [] 899 fragment_buffer: List[Dict[str, Any]] = []
892 900
893 def flush_buffer(): 901 def flush_buffer():
  902 + """将当前片段缓冲写入merged列表,必要时合并为单段paragraph"""
894 nonlocal fragment_buffer 903 nonlocal fragment_buffer
895 if not fragment_buffer: 904 if not fragment_buffer:
896 return 905 return
@@ -427,6 +427,7 @@ class HTMLRenderer: @@ -427,6 +427,7 @@ class HTMLRenderer:
427 extracted: List[Dict[str, Any]] = [] 427 extracted: List[Dict[str, Any]] = []
428 428
429 def traverse(node: Any) -> None: 429 def traverse(node: Any) -> None:
  430 + """递归遍历block树,识别text字段内潜在的嵌套block JSON"""
430 if isinstance(node, dict): 431 if isinstance(node, dict):
431 for key, value in list(node.items()): 432 for key, value in list(node.items()):
432 if key == "text" and isinstance(value, str): 433 if key == "text" and isinstance(value, str):
@@ -1087,10 +1088,20 @@ class HTMLRenderer: @@ -1087,10 +1088,20 @@ class HTMLRenderer:
1087 return tuple(normalized) if normalized else None 1088 return tuple(normalized) if normalized else None
1088 1089
1089 def _normalize_kpi_item(self, item: Any) -> tuple[str, str, str, str, str] | None: 1090 def _normalize_kpi_item(self, item: Any) -> tuple[str, str, str, str, str] | None:
  1091 + """
  1092 + 将单条KPI记录规整为可对比的签名。
  1093 +
  1094 + 参数:
  1095 + item: KPI数组中的原始字典,可能缺失字段或类型混杂。
  1096 +
  1097 + 返回:
  1098 + tuple | None: (label, value, unit, delta, tone) 的五元组;若输入非法则为None。
  1099 + """
1090 if not isinstance(item, dict): 1100 if not isinstance(item, dict):
1091 return None 1101 return None
1092 1102
1093 def normalize(value: Any) -> str: 1103 def normalize(value: Any) -> str:
  1104 + """统一各类值的表现形式,便于生成稳定签名"""
1094 if value is None: 1105 if value is None:
1095 return "" 1106 return ""
1096 if isinstance(value, (int, float)): 1107 if isinstance(value, (int, float)):