Add Comments
This commit is contained in:
@@ -433,6 +433,14 @@ class ReportAgent:
|
||||
})
|
||||
# 章节流式回调:把LLM返回的delta透传给SSE,便于前端实时渲染
|
||||
def chunk_callback(delta: str, meta: Dict[str, Any], section_ref: TemplateSection = section):
|
||||
"""
|
||||
章节内容流式回调。
|
||||
|
||||
Args:
|
||||
delta: LLM最新输出的增量文本。
|
||||
meta: 节点回传的章节元数据,兜底时使用。
|
||||
section_ref: 默认指向当前章节,保证在缺失元信息时也能定位。
|
||||
"""
|
||||
emit('chapter_chunk', {
|
||||
'chapterId': meta.get('chapterId') or section_ref.chapter_id,
|
||||
'title': meta.get('title') or section_ref.title,
|
||||
|
||||
@@ -634,6 +634,13 @@ def stream_task(task_id: str):
|
||||
last_event_id = None
|
||||
|
||||
def event_generator():
|
||||
"""
|
||||
SSE事件生成器。
|
||||
|
||||
- 负责注册并消费对应任务的事件队列;
|
||||
- 先回放历史事件再持续监听实时事件;
|
||||
- 周期性发送心跳并在任务结束后自动注销监听。
|
||||
"""
|
||||
queue = _register_stream(task_id)
|
||||
try:
|
||||
# 断线重连场景下,先补发历史事件,保证界面状态一致
|
||||
|
||||
@@ -34,6 +34,13 @@ class ChapterJsonParseError(ValueError):
|
||||
"""章节LLM输出无法解析为合法JSON时抛出的异常,附带原始文本方便排查。"""
|
||||
|
||||
def __init__(self, message: str, raw_text: Optional[str] = None):
|
||||
"""
|
||||
构造异常并附加原始输出,便于日志中定位。
|
||||
|
||||
Args:
|
||||
message: 人类可读的错误描述。
|
||||
raw_text: 触发异常的完整LLM输出。
|
||||
"""
|
||||
super().__init__(message)
|
||||
self.raw_text = raw_text
|
||||
|
||||
@@ -674,6 +681,7 @@ class ChapterGenerationNode(BaseNode):
|
||||
"""
|
||||
|
||||
def walk(node: Any) -> int:
|
||||
"""递归下钻block树并返回字符估算,跳过非正文类型"""
|
||||
if node is None:
|
||||
return 0
|
||||
if isinstance(node, list):
|
||||
@@ -891,6 +899,7 @@ class ChapterGenerationNode(BaseNode):
|
||||
fragment_buffer: List[Dict[str, Any]] = []
|
||||
|
||||
def flush_buffer():
|
||||
"""将当前片段缓冲写入merged列表,必要时合并为单段paragraph"""
|
||||
nonlocal fragment_buffer
|
||||
if not fragment_buffer:
|
||||
return
|
||||
|
||||
@@ -427,6 +427,7 @@ class HTMLRenderer:
|
||||
extracted: List[Dict[str, Any]] = []
|
||||
|
||||
def traverse(node: Any) -> None:
|
||||
"""递归遍历block树,识别text字段内潜在的嵌套block JSON"""
|
||||
if isinstance(node, dict):
|
||||
for key, value in list(node.items()):
|
||||
if key == "text" and isinstance(value, str):
|
||||
@@ -1087,10 +1088,20 @@ class HTMLRenderer:
|
||||
return tuple(normalized) if normalized else None
|
||||
|
||||
def _normalize_kpi_item(self, item: Any) -> tuple[str, str, str, str, str] | None:
|
||||
"""
|
||||
将单条KPI记录规整为可对比的签名。
|
||||
|
||||
参数:
|
||||
item: KPI数组中的原始字典,可能缺失字段或类型混杂。
|
||||
|
||||
返回:
|
||||
tuple | None: (label, value, unit, delta, tone) 的五元组;若输入非法则为None。
|
||||
"""
|
||||
if not isinstance(item, dict):
|
||||
return None
|
||||
|
||||
def normalize(value: Any) -> str:
|
||||
"""统一各类值的表现形式,便于生成稳定签名"""
|
||||
if value is None:
|
||||
return ""
|
||||
if isinstance(value, (int, float)):
|
||||
|
||||
Reference in New Issue
Block a user