refactor(report_agent, Step4Report): simplify logging and remove subsection handling; update UI to reflect changes in section content generation
This commit is contained in:
parent
54f1291967
commit
f9abaf8e9f
2 changed files with 62 additions and 237 deletions
|
|
@ -238,13 +238,11 @@ class ReportLogger:
|
||||||
section_title: str,
|
section_title: str,
|
||||||
section_index: int,
|
section_index: int,
|
||||||
content: str,
|
content: str,
|
||||||
tool_calls_count: int,
|
tool_calls_count: int
|
||||||
is_subsection: bool = False
|
|
||||||
):
|
):
|
||||||
"""记录章节/子章节内容生成完成(仅记录内容,不代表整个章节完成)"""
|
"""记录章节内容生成完成(仅记录内容,不代表整个章节完成)"""
|
||||||
action = "subsection_content" if is_subsection else "section_content"
|
|
||||||
self.log(
|
self.log(
|
||||||
action=action,
|
action="section_content",
|
||||||
stage="generating",
|
stage="generating",
|
||||||
section_title=section_title,
|
section_title=section_title,
|
||||||
section_index=section_index,
|
section_index=section_index,
|
||||||
|
|
@ -252,8 +250,7 @@ class ReportLogger:
|
||||||
"content": content, # 完整内容,不截断
|
"content": content, # 完整内容,不截断
|
||||||
"content_length": len(content),
|
"content_length": len(content),
|
||||||
"tool_calls_count": tool_calls_count,
|
"tool_calls_count": tool_calls_count,
|
||||||
"is_subsection": is_subsection,
|
"message": f"章节 {section_title} 内容生成完成"
|
||||||
"message": f"{'子章节' if is_subsection else '主章节'} {section_title} 内容生成完成"
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -261,11 +258,10 @@ class ReportLogger:
|
||||||
self,
|
self,
|
||||||
section_title: str,
|
section_title: str,
|
||||||
section_index: int,
|
section_index: int,
|
||||||
full_content: str,
|
full_content: str
|
||||||
subsection_count: int
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
记录完整章节生成完成(包含所有子章节的合并内容)
|
记录章节生成完成
|
||||||
|
|
||||||
前端应监听此日志来判断一个章节是否真正完成,并获取完整内容
|
前端应监听此日志来判断一个章节是否真正完成,并获取完整内容
|
||||||
"""
|
"""
|
||||||
|
|
@ -275,10 +271,9 @@ class ReportLogger:
|
||||||
section_title=section_title,
|
section_title=section_title,
|
||||||
section_index=section_index,
|
section_index=section_index,
|
||||||
details={
|
details={
|
||||||
"content": full_content, # 完整章节内容(含子章节),不截断
|
"content": full_content,
|
||||||
"content_length": len(full_content),
|
"content_length": len(full_content),
|
||||||
"subsection_count": subsection_count,
|
"message": f"章节 {section_title} 生成完成"
|
||||||
"message": f"章节 {section_title} 完整生成完成(含 {subsection_count} 个子章节)"
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -404,13 +399,11 @@ class ReportSection:
|
||||||
"""报告章节"""
|
"""报告章节"""
|
||||||
title: str
|
title: str
|
||||||
content: str = ""
|
content: str = ""
|
||||||
subsections: List['ReportSection'] = field(default_factory=list)
|
|
||||||
|
|
||||||
def to_dict(self) -> Dict[str, Any]:
|
def to_dict(self) -> Dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"title": self.title,
|
"title": self.title,
|
||||||
"content": self.content,
|
"content": self.content
|
||||||
"subsections": [s.to_dict() for s in self.subsections]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def to_markdown(self, level: int = 2) -> str:
|
def to_markdown(self, level: int = 2) -> str:
|
||||||
|
|
@ -418,8 +411,6 @@ class ReportSection:
|
||||||
md = f"{'#' * level} {self.title}\n\n"
|
md = f"{'#' * level} {self.title}\n\n"
|
||||||
if self.content:
|
if self.content:
|
||||||
md += f"{self.content}\n\n"
|
md += f"{self.content}\n\n"
|
||||||
for sub in self.subsections:
|
|
||||||
md += sub.to_markdown(level + 1)
|
|
||||||
return md
|
return md
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -854,8 +845,8 @@ class ReportAgent:
|
||||||
- ❌ 不是泛泛而谈的舆情综述
|
- ❌ 不是泛泛而谈的舆情综述
|
||||||
|
|
||||||
【章节数量限制】
|
【章节数量限制】
|
||||||
- 最少2个主章节,最多5个主章节
|
- 最少2个章节,最多5个章节
|
||||||
- 每个章节可以有0-2个子章节
|
- 不需要子章节,每个章节直接撰写完整内容
|
||||||
- 内容要精炼,聚焦于核心预测发现
|
- 内容要精炼,聚焦于核心预测发现
|
||||||
- 章节结构由你根据预测结果自主设计
|
- 章节结构由你根据预测结果自主设计
|
||||||
|
|
||||||
|
|
@ -866,10 +857,7 @@ class ReportAgent:
|
||||||
"sections": [
|
"sections": [
|
||||||
{
|
{
|
||||||
"title": "章节标题",
|
"title": "章节标题",
|
||||||
"description": "章节内容描述",
|
"description": "章节内容描述"
|
||||||
"subsections": [
|
|
||||||
{"title": "子章节标题", "description": "子章节描述"}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -912,17 +900,9 @@ class ReportAgent:
|
||||||
# 解析大纲
|
# 解析大纲
|
||||||
sections = []
|
sections = []
|
||||||
for section_data in response.get("sections", []):
|
for section_data in response.get("sections", []):
|
||||||
subsections = []
|
|
||||||
for sub_data in section_data.get("subsections", []):
|
|
||||||
subsections.append(ReportSection(
|
|
||||||
title=sub_data.get("title", ""),
|
|
||||||
content=""
|
|
||||||
))
|
|
||||||
|
|
||||||
sections.append(ReportSection(
|
sections.append(ReportSection(
|
||||||
title=section_data.get("title", ""),
|
title=section_data.get("title", ""),
|
||||||
content="",
|
content=""
|
||||||
subsections=subsections
|
|
||||||
))
|
))
|
||||||
|
|
||||||
outline = ReportOutline(
|
outline = ReportOutline(
|
||||||
|
|
@ -1241,16 +1221,13 @@ class ReportAgent:
|
||||||
final_answer = response.split("Final Answer:")[-1].strip()
|
final_answer = response.split("Final Answer:")[-1].strip()
|
||||||
logger.info(f"章节 {section.title} 生成完成(工具调用: {tool_calls_count}次)")
|
logger.info(f"章节 {section.title} 生成完成(工具调用: {tool_calls_count}次)")
|
||||||
|
|
||||||
# 记录章节内容生成完成日志(注意:这只是内容完成,不代表整个章节完成)
|
# 记录章节内容生成完成日志
|
||||||
# 如果是子章节,section_index >= 100
|
|
||||||
is_subsection = section_index >= 100
|
|
||||||
if self.report_logger:
|
if self.report_logger:
|
||||||
self.report_logger.log_section_content(
|
self.report_logger.log_section_content(
|
||||||
section_title=section.title,
|
section_title=section.title,
|
||||||
section_index=section_index,
|
section_index=section_index,
|
||||||
content=final_answer,
|
content=final_answer,
|
||||||
tool_calls_count=tool_calls_count,
|
tool_calls_count=tool_calls_count
|
||||||
is_subsection=is_subsection
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return final_answer
|
return final_answer
|
||||||
|
|
@ -1359,15 +1336,13 @@ class ReportAgent:
|
||||||
else:
|
else:
|
||||||
final_answer = response
|
final_answer = response
|
||||||
|
|
||||||
# 记录章节内容生成完成日志(注意:这只是内容完成,不代表整个章节完成)
|
# 记录章节内容生成完成日志
|
||||||
is_subsection = section_index >= 100
|
|
||||||
if self.report_logger:
|
if self.report_logger:
|
||||||
self.report_logger.log_section_content(
|
self.report_logger.log_section_content(
|
||||||
section_title=section.title,
|
section_title=section.title,
|
||||||
section_index=section_index,
|
section_index=section_index,
|
||||||
content=final_answer,
|
content=final_answer,
|
||||||
tool_calls_count=tool_calls_count,
|
tool_calls_count=tool_calls_count
|
||||||
is_subsection=is_subsection
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return final_answer
|
return final_answer
|
||||||
|
|
@ -1512,61 +1487,21 @@ class ReportAgent:
|
||||||
section.content = section_content
|
section.content = section_content
|
||||||
generated_sections.append(f"## {section.title}\n\n{section_content}")
|
generated_sections.append(f"## {section.title}\n\n{section_content}")
|
||||||
|
|
||||||
# 如果有子章节,也一并生成并合并到主章节中
|
# 保存章节
|
||||||
subsection_contents = []
|
ReportManager.save_section(report_id, section_num, section)
|
||||||
for j, subsection in enumerate(section.subsections):
|
|
||||||
subsection_num = j + 1
|
|
||||||
|
|
||||||
if progress_callback:
|
|
||||||
progress_callback(
|
|
||||||
"generating",
|
|
||||||
base_progress + int(((j + 1) / max(len(section.subsections), 1)) * 5),
|
|
||||||
f"正在生成子章节: {subsection.title}"
|
|
||||||
)
|
|
||||||
|
|
||||||
ReportManager.update_progress(
|
|
||||||
report_id, "generating",
|
|
||||||
base_progress + int(((j + 1) / max(len(section.subsections), 1)) * 5),
|
|
||||||
f"正在生成子章节: {subsection.title}",
|
|
||||||
current_section=subsection.title,
|
|
||||||
completed_sections=completed_section_titles
|
|
||||||
)
|
|
||||||
|
|
||||||
subsection_content = self._generate_section_react(
|
|
||||||
section=subsection,
|
|
||||||
outline=outline,
|
|
||||||
previous_sections=generated_sections,
|
|
||||||
progress_callback=None,
|
|
||||||
section_index=section_num * 100 + subsection_num # 子章节索引
|
|
||||||
)
|
|
||||||
subsection.content = subsection_content
|
|
||||||
generated_sections.append(f"### {subsection.title}\n\n{subsection_content}")
|
|
||||||
subsection_contents.append((subsection.title, subsection_content))
|
|
||||||
completed_section_titles.append(f" └─ {subsection.title}")
|
|
||||||
|
|
||||||
logger.info(f"子章节已生成: {subsection.title}")
|
|
||||||
|
|
||||||
# 【关键】将主章节和所有子章节合并保存到一个文件
|
|
||||||
ReportManager.save_section_with_subsections(
|
|
||||||
report_id, section_num, section, subsection_contents
|
|
||||||
)
|
|
||||||
completed_section_titles.append(section.title)
|
completed_section_titles.append(section.title)
|
||||||
|
|
||||||
# 【重要】记录完整章节完成日志,包含合并后的完整内容
|
# 记录章节完成日志
|
||||||
# 构建完整章节内容(主章节 + 所有子章节)
|
full_section_content = f"## {section.title}\n\n{section_content}"
|
||||||
full_section_content = f"## {section.title}\n\n{section_content}\n\n"
|
|
||||||
for sub_title, sub_content in subsection_contents:
|
|
||||||
full_section_content += f"### {sub_title}\n\n{sub_content}\n\n"
|
|
||||||
|
|
||||||
if self.report_logger:
|
if self.report_logger:
|
||||||
self.report_logger.log_section_full_complete(
|
self.report_logger.log_section_full_complete(
|
||||||
section_title=section.title,
|
section_title=section.title,
|
||||||
section_index=section_num,
|
section_index=section_num,
|
||||||
full_content=full_section_content.strip(),
|
full_content=full_section_content.strip()
|
||||||
subsection_count=len(subsection_contents)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"章节已保存(包含{len(subsection_contents)}个子章节): {report_id}/section_{section_num:02d}.md")
|
logger.info(f"章节已保存: {report_id}/section_{section_num:02d}.md")
|
||||||
|
|
||||||
# 更新进度
|
# 更新进度
|
||||||
ReportManager.update_progress(
|
ReportManager.update_progress(
|
||||||
|
|
@ -2000,12 +1935,10 @@ class ReportManager:
|
||||||
cls,
|
cls,
|
||||||
report_id: str,
|
report_id: str,
|
||||||
section_index: int,
|
section_index: int,
|
||||||
section: ReportSection,
|
section: ReportSection
|
||||||
is_subsection: bool = False,
|
|
||||||
parent_index: int = None
|
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
保存单个章节(不推荐使用,建议使用 save_section_with_subsections)
|
保存单个章节
|
||||||
|
|
||||||
在每个章节生成完成后立即调用,实现分章节输出
|
在每个章节生成完成后立即调用,实现分章节输出
|
||||||
|
|
||||||
|
|
@ -2013,29 +1946,20 @@ class ReportManager:
|
||||||
report_id: 报告ID
|
report_id: 报告ID
|
||||||
section_index: 章节索引(从1开始)
|
section_index: 章节索引(从1开始)
|
||||||
section: 章节对象
|
section: 章节对象
|
||||||
is_subsection: 是否是子章节
|
|
||||||
parent_index: 父章节索引(子章节时使用)
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
保存的文件路径
|
保存的文件路径
|
||||||
"""
|
"""
|
||||||
cls._ensure_report_folder(report_id)
|
cls._ensure_report_folder(report_id)
|
||||||
|
|
||||||
# 确定章节级别和标题格式
|
|
||||||
if is_subsection and parent_index is not None:
|
|
||||||
level = "###"
|
|
||||||
file_suffix = f"section_{parent_index:02d}_{section_index:02d}.md"
|
|
||||||
else:
|
|
||||||
level = "##"
|
|
||||||
file_suffix = f"section_{section_index:02d}.md"
|
|
||||||
|
|
||||||
# 构建章节Markdown内容 - 清理可能存在的重复标题
|
# 构建章节Markdown内容 - 清理可能存在的重复标题
|
||||||
cleaned_content = cls._clean_section_content(section.content, section.title)
|
cleaned_content = cls._clean_section_content(section.content, section.title)
|
||||||
md_content = f"{level} {section.title}\n\n"
|
md_content = f"## {section.title}\n\n"
|
||||||
if cleaned_content:
|
if cleaned_content:
|
||||||
md_content += f"{cleaned_content}\n\n"
|
md_content += f"{cleaned_content}\n\n"
|
||||||
|
|
||||||
# 保存文件
|
# 保存文件
|
||||||
|
file_suffix = f"section_{section_index:02d}.md"
|
||||||
file_path = os.path.join(cls._get_report_folder(report_id), file_suffix)
|
file_path = os.path.join(cls._get_report_folder(report_id), file_suffix)
|
||||||
with open(file_path, 'w', encoding='utf-8') as f:
|
with open(file_path, 'w', encoding='utf-8') as f:
|
||||||
f.write(md_content)
|
f.write(md_content)
|
||||||
|
|
@ -2043,50 +1967,6 @@ class ReportManager:
|
||||||
logger.info(f"章节已保存: {report_id}/{file_suffix}")
|
logger.info(f"章节已保存: {report_id}/{file_suffix}")
|
||||||
return file_path
|
return file_path
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def save_section_with_subsections(
|
|
||||||
cls,
|
|
||||||
report_id: str,
|
|
||||||
section_index: int,
|
|
||||||
section: ReportSection,
|
|
||||||
subsection_contents: List[tuple]
|
|
||||||
) -> str:
|
|
||||||
"""
|
|
||||||
保存章节及其所有子章节到一个文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
report_id: 报告ID
|
|
||||||
section_index: 章节索引(从1开始)
|
|
||||||
section: 主章节对象
|
|
||||||
subsection_contents: 子章节列表 [(title, content), ...]
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
保存的文件路径
|
|
||||||
"""
|
|
||||||
cls._ensure_report_folder(report_id)
|
|
||||||
|
|
||||||
# 构建主章节Markdown内容
|
|
||||||
cleaned_main_content = cls._clean_section_content(section.content, section.title)
|
|
||||||
md_content = f"## {section.title}\n\n"
|
|
||||||
if cleaned_main_content:
|
|
||||||
md_content += f"{cleaned_main_content}\n\n"
|
|
||||||
|
|
||||||
# 添加所有子章节内容
|
|
||||||
for sub_title, sub_content in subsection_contents:
|
|
||||||
cleaned_sub_content = cls._clean_section_content(sub_content, sub_title)
|
|
||||||
md_content += f"### {sub_title}\n\n"
|
|
||||||
if cleaned_sub_content:
|
|
||||||
md_content += f"{cleaned_sub_content}\n\n"
|
|
||||||
|
|
||||||
# 保存文件
|
|
||||||
file_suffix = f"section_{section_index:02d}.md"
|
|
||||||
file_path = os.path.join(cls._get_report_folder(report_id), file_suffix)
|
|
||||||
with open(file_path, 'w', encoding='utf-8') as f:
|
|
||||||
f.write(md_content)
|
|
||||||
|
|
||||||
logger.info(f"章节已保存(含{len(subsection_contents)}个子章节): {report_id}/{file_suffix}")
|
|
||||||
return file_path
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _clean_section_content(cls, content: str, section_title: str) -> str:
|
def _clean_section_content(cls, content: str, section_title: str) -> str:
|
||||||
"""
|
"""
|
||||||
|
|
@ -2217,14 +2097,11 @@ class ReportManager:
|
||||||
# 从文件名解析章节索引
|
# 从文件名解析章节索引
|
||||||
parts = filename.replace('.md', '').split('_')
|
parts = filename.replace('.md', '').split('_')
|
||||||
section_index = int(parts[1])
|
section_index = int(parts[1])
|
||||||
subsection_index = int(parts[2]) if len(parts) > 2 else None
|
|
||||||
|
|
||||||
sections.append({
|
sections.append({
|
||||||
"filename": filename,
|
"filename": filename,
|
||||||
"section_index": section_index,
|
"section_index": section_index,
|
||||||
"subsection_index": subsection_index,
|
"content": content
|
||||||
"content": content,
|
|
||||||
"is_subsection": subsection_index is not None
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return sections
|
return sections
|
||||||
|
|
@ -2243,12 +2120,9 @@ class ReportManager:
|
||||||
md_content += f"> {outline.summary}\n\n"
|
md_content += f"> {outline.summary}\n\n"
|
||||||
md_content += f"---\n\n"
|
md_content += f"---\n\n"
|
||||||
|
|
||||||
# 按顺序读取所有章节文件(只读取主章节文件,不读取子章节文件)
|
# 按顺序读取所有章节文件
|
||||||
sections = cls.get_generated_sections(report_id)
|
sections = cls.get_generated_sections(report_id)
|
||||||
for section_info in sections:
|
for section_info in sections:
|
||||||
# 跳过子章节文件(已合并到主章节中)
|
|
||||||
if section_info.get("is_subsection", False):
|
|
||||||
continue
|
|
||||||
md_content += section_info["content"]
|
md_content += section_info["content"]
|
||||||
|
|
||||||
# 后处理:清理整个报告的标题问题
|
# 后处理:清理整个报告的标题问题
|
||||||
|
|
@ -2288,8 +2162,6 @@ class ReportManager:
|
||||||
section_titles = set()
|
section_titles = set()
|
||||||
for section in outline.sections:
|
for section in outline.sections:
|
||||||
section_titles.add(section.title)
|
section_titles.add(section.title)
|
||||||
for sub in section.subsections:
|
|
||||||
section_titles.add(sub.title)
|
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(lines):
|
while i < len(lines):
|
||||||
|
|
@ -2432,14 +2304,9 @@ class ReportManager:
|
||||||
outline_data = data['outline']
|
outline_data = data['outline']
|
||||||
sections = []
|
sections = []
|
||||||
for s in outline_data.get('sections', []):
|
for s in outline_data.get('sections', []):
|
||||||
subsections = [
|
|
||||||
ReportSection(title=sub['title'], content=sub.get('content', ''))
|
|
||||||
for sub in s.get('subsections', [])
|
|
||||||
]
|
|
||||||
sections.append(ReportSection(
|
sections.append(ReportSection(
|
||||||
title=s['title'],
|
title=s['title'],
|
||||||
content=s.get('content', ''),
|
content=s.get('content', '')
|
||||||
subsections=subsections
|
|
||||||
))
|
))
|
||||||
outline = ReportOutline(
|
outline = ReportOutline(
|
||||||
title=outline_data['title'],
|
title=outline_data['title'],
|
||||||
|
|
|
||||||
|
|
@ -194,26 +194,24 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Section/Subsection Content Generated (内容生成完成,但整个章节可能还没完成) -->
|
<!-- Section Content Generated (内容生成完成,但整个章节可能还没完成) -->
|
||||||
<template v-if="log.action === 'section_content' || log.action === 'subsection_content'">
|
<template v-if="log.action === 'section_content'">
|
||||||
<div class="section-tag content-ready" :class="{ 'is-subsection': log.action === 'subsection_content' }">
|
<div class="section-tag content-ready">
|
||||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path d="M12 20h9"></path>
|
<path d="M12 20h9"></path>
|
||||||
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
|
<path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="tag-title">{{ log.section_title }}</span>
|
<span class="tag-title">{{ log.section_title }}</span>
|
||||||
<span v-if="log.action === 'subsection_content'" class="tag-sub">(subsection)</span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Section Complete (完整章节生成完成,含所有子章节) -->
|
<!-- Section Complete (章节生成完成) -->
|
||||||
<template v-if="log.action === 'section_complete'">
|
<template v-if="log.action === 'section_complete'">
|
||||||
<div class="section-tag completed">
|
<div class="section-tag completed">
|
||||||
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<polyline points="20 6 9 17 4 12"></polyline>
|
<polyline points="20 6 9 17 4 12"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="tag-title">{{ log.section_title }}</span>
|
<span class="tag-title">{{ log.section_title }}</span>
|
||||||
<span v-if="log.details?.subsection_count > 0" class="tag-sub">(+{{ log.details.subsection_count }} subsections)</span>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -1800,20 +1798,6 @@ const isSectionCompleted = (sectionIndex) => {
|
||||||
return !!generatedSections.value[sectionIndex]
|
return !!generatedSections.value[sectionIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从 section_index 获取主章节索引
|
|
||||||
// 后端编号方案:主章节 1,2,3... 子章节 101,102(第1章子章节1,2)
|
|
||||||
const getMainSectionIndex = (sectionIndex) => {
|
|
||||||
if (sectionIndex >= 100) {
|
|
||||||
return Math.floor(sectionIndex / 100)
|
|
||||||
}
|
|
||||||
return sectionIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否是子章节
|
|
||||||
const isSubsection = (sectionIndex) => {
|
|
||||||
return sectionIndex >= 100
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatTime = (timestamp) => {
|
const formatTime = (timestamp) => {
|
||||||
if (!timestamp) return ''
|
if (!timestamp) return ''
|
||||||
try {
|
try {
|
||||||
|
|
@ -1929,7 +1913,6 @@ const getActionLabel = (action) => {
|
||||||
'planning_complete': 'Plan Complete',
|
'planning_complete': 'Plan Complete',
|
||||||
'section_start': 'Section Start',
|
'section_start': 'Section Start',
|
||||||
'section_content': 'Content Ready',
|
'section_content': 'Content Ready',
|
||||||
'subsection_content': 'Subsection Ready',
|
|
||||||
'section_complete': 'Section Done',
|
'section_complete': 'Section Done',
|
||||||
'tool_call': 'Tool Call',
|
'tool_call': 'Tool Call',
|
||||||
'tool_result': 'Tool Result',
|
'tool_result': 'Tool Result',
|
||||||
|
|
@ -1968,32 +1951,17 @@ const fetchAgentLog = async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.action === 'section_start') {
|
if (log.action === 'section_start') {
|
||||||
// 无论是主章节还是子章节开始,都映射到主章节索引
|
currentSectionIndex.value = log.section_index
|
||||||
// 后端编号:主章节 1,2,3... 子章节 101,102(第1章子章节1,2)
|
|
||||||
const mainIndex = getMainSectionIndex(log.section_index)
|
|
||||||
currentSectionIndex.value = mainIndex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// section_content / subsection_content - 表示内容生成完成(但整个章节可能还没完成)
|
// section_complete - 章节生成完成
|
||||||
// 这里不更新 generatedSections,只记录进度
|
|
||||||
if (log.action === 'section_content' || log.action === 'subsection_content') {
|
|
||||||
// 子章节内容生成时,保持主章节的 loading 状态
|
|
||||||
// 因为完整内容会在 section_complete 时一次性提供
|
|
||||||
}
|
|
||||||
|
|
||||||
// section_complete - 表示完整章节(含所有子章节)生成完成
|
|
||||||
// details.content 包含合并后的完整内容
|
|
||||||
// 注意:只有主章节 complete 时才更新内容,子章节 complete 不处理
|
|
||||||
if (log.action === 'section_complete') {
|
if (log.action === 'section_complete') {
|
||||||
const mainIndex = getMainSectionIndex(log.section_index)
|
if (log.details?.content) {
|
||||||
// 只有主章节完成时(section_index < 100)才更新内容和清除 loading
|
generatedSections.value[log.section_index] = log.details.content
|
||||||
if (!isSubsection(log.section_index) && log.details?.content) {
|
|
||||||
generatedSections.value[mainIndex] = log.details.content
|
|
||||||
// 自动展开刚生成的章节
|
// 自动展开刚生成的章节
|
||||||
expandedContent.value.add(mainIndex - 1)
|
expandedContent.value.add(log.section_index - 1)
|
||||||
currentSectionIndex.value = null
|
currentSectionIndex.value = null
|
||||||
}
|
}
|
||||||
// 子章节完成时不清除 currentSectionIndex,继续显示 loading
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (log.action === 'report_complete') {
|
if (log.action === 'report_complete') {
|
||||||
|
|
@ -3055,10 +3023,6 @@ watch(() => props.reportId, (newId) => {
|
||||||
color: var(--wf-active-dot);
|
color: var(--wf-active-dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-tag.content-ready.is-subsection {
|
|
||||||
background: var(--wf-active-bg);
|
|
||||||
border-color: var(--wf-active-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
.section-tag.completed {
|
.section-tag.completed {
|
||||||
background: #ECFDF5;
|
background: #ECFDF5;
|
||||||
|
|
@ -3085,12 +3049,6 @@ watch(() => props.reportId, (newId) => {
|
||||||
color: #374151;
|
color: #374151;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tag-sub {
|
|
||||||
font-size: 11px;
|
|
||||||
color: #6B7280;
|
|
||||||
margin-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-badge {
|
.tool-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue