feat: comprehensive v2 upgrade — streaming, error KB, file upload, layout analysis
Major changes: - Streaming: LLM统一 _BaseLLM 接口 (invoke + stream), generate/modify/correct 节点使用 get_stream_writer() 实现逐字输出, UI 节点平铺展开自动折叠 - Prompt外部化: 7个prompt拆分到 prompts/*.md, loader.py 支持热重载 - 错误自增长: backend/error_kb.py — 指纹去重 + ChromaDB持久化, correct_jrxml→validate 通过时自动入库, retrieve同时搜索错误KB - 文件上传: backend/file_parser.py — PDF/DOCX/图片/文本解析, 侧边栏多文件上传, 文本自动注入下一条消息 - A4模板识别: backend/layout_analyzer.py — 三种模式(完整A4/行片段修改/行片段新建), PaddleOCR元素提取 + 行分组 + JRXML section匹配 - 会话历史下载: jrxml_versions版本追踪 + 侧边栏历史版本下载按钮 - 预览修复: route_after_save跳过预览/导出意图的验证循环 - Ctrl+C修复: JS注入拦截Streamlit裸c键清缓存 Docs: CLAUDE.md (完整项目文档), ROADMAP.md (改进路线图) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
你是一个信息压缩助手。以下是用户与报表生成助手之间的历史对话记录,请将其压缩为一份简洁的摘要(不超过200字)。
|
||||
|
||||
摘要必须保留以下关键信息:
|
||||
- 用户提出的所有报表需求点(字段、标题、分组、汇总等)
|
||||
- 用户提出的所有修改要求及其顺序
|
||||
- 当前报表的核心结构(字段列表、标题、分组方式)
|
||||
- 任何特殊要求或约束条件
|
||||
|
||||
只输出摘要文本,不要添加任何解释或标记。
|
||||
|
||||
对话记录:
|
||||
{conversation_text}
|
||||
@@ -0,0 +1,5 @@
|
||||
你是 JasperReports 专家。用简洁清晰的中文回答用户关于 JasperReports 的问题。
|
||||
|
||||
用户问题:{question}
|
||||
|
||||
直接回答:
|
||||
@@ -0,0 +1,17 @@
|
||||
你是一位资深 JasperReports 工程师。你生成的 JRXML 文件编译失败。分析错误并修复 JRXML。
|
||||
|
||||
关键规则:
|
||||
- 只输出完整修复后的 JRXML 代码,不要解释,不要 markdown 标记。
|
||||
- JRXML 必须与 JasperReports 7.0.6 兼容。
|
||||
- 解决下面列出的特定错误。
|
||||
|
||||
当前 JRXML(带错误):
|
||||
{current_jrxml}
|
||||
|
||||
编译错误:
|
||||
{error_msg}
|
||||
|
||||
错误的自然语言解释:
|
||||
{explanation}
|
||||
|
||||
立即生成修正后的 JRXML:
|
||||
@@ -0,0 +1,9 @@
|
||||
你是一位 JasperReports 专家。用普通非技术语言解释以下 JRXML 编译错误,让业务用户能够理解。
|
||||
|
||||
错误消息:
|
||||
{error_msg}
|
||||
|
||||
当前 JRXML 片段(前 80 行):
|
||||
{jrxml_snippet}
|
||||
|
||||
用 2-3 句话解释哪里出了问题以及如何修复:
|
||||
@@ -0,0 +1,15 @@
|
||||
你是一位资深 JasperReports 工程师。根据以下参考模板和用户需求,生成一个完整、可编译的 JRXML 文件。
|
||||
JRXML 必须兼容 JasperReports 7.0.6 schema。
|
||||
|
||||
关键规则:
|
||||
- 只输出 JRXML 代码,不要解释,不要 markdown 标记。
|
||||
- 报表正文中使用的每个字段必须在 <field name="..."> 部分中声明。
|
||||
- 根元素为 <jasperReport>,包含正确的 xmlns 属性。
|
||||
- 包含 <queryString>,在 <![CDATA[...]]> 中包含 SQL 查询。
|
||||
- 确保所有交叉引用(字段名称、band 元素)保持一致。
|
||||
|
||||
参考模板和组件:
|
||||
{context}
|
||||
|
||||
用户需求:
|
||||
{user_request}
|
||||
@@ -0,0 +1,16 @@
|
||||
你是意图分类器。根据用户输入判断意图,只输出意图名称。
|
||||
|
||||
当前有报表:{has_report}
|
||||
用户输入:{user_input}
|
||||
|
||||
可选意图:
|
||||
- initial_generation(新建报表,或无报表时的任何需求)
|
||||
- modify_report(修改当前已有报表)
|
||||
- preview_report(预览/查看当前报表)
|
||||
- export_pdf(导出PDF文件)
|
||||
- export_jrxml(下载/导出/保存JRXML文件)
|
||||
- undo_modification(撤销/回退上一步修改)
|
||||
- consult_question(咨询JasperReports相关知识或使用问题)
|
||||
- reset_session(清空/重置/重新开始)
|
||||
|
||||
意图名称:
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Prompt 加载器:从 prompts/ 目录加载 .md 文件。
|
||||
|
||||
支持热重载 — 每次调用都从磁盘读取,修改 prompt 文件无需重启应用。
|
||||
|
||||
用法:
|
||||
from prompts.loader import load_prompt
|
||||
prompt = load_prompt("intent_classify").format(has_report="是", user_input="...")
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
_PROMPTS_DIR = Path(__file__).resolve().parent
|
||||
|
||||
# 文件名 → 变量名 映射
|
||||
_NAME_MAP = {
|
||||
"intent_classify": "intent_classify.md",
|
||||
"consult": "consult.md",
|
||||
"initial_generation": "initial_generation.md",
|
||||
"modification": "modification.md",
|
||||
"correction": "correction.md",
|
||||
"explain_error": "explain_error.md",
|
||||
"compression": "compression.md",
|
||||
}
|
||||
|
||||
|
||||
def load_prompt(name: str) -> str:
|
||||
"""从 prompts/{name}.md 加载 prompt 模板(每次从磁盘读取,支持热重载)。
|
||||
|
||||
返回的字符串包含 Python .format() 占位符,调用方负责填充。
|
||||
"""
|
||||
filename = _NAME_MAP.get(name)
|
||||
if not filename:
|
||||
raise ValueError(f"未知 prompt: {name},可选值: {list(_NAME_MAP.keys())}")
|
||||
|
||||
filepath = _PROMPTS_DIR / filename
|
||||
if not filepath.exists():
|
||||
raise FileNotFoundError(f"Prompt 文件不存在: {filepath}")
|
||||
|
||||
text = filepath.read_text(encoding="utf-8").strip()
|
||||
|
||||
# 去掉可能存在的 markdown frontmatter(--- 包裹的元数据)
|
||||
if text.startswith("---"):
|
||||
end = text.find("---", 3)
|
||||
if end != -1:
|
||||
text = text[end + 3:].strip()
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def list_prompts() -> list[str]:
|
||||
"""列出所有可用的 prompt 名称。"""
|
||||
return sorted(_NAME_MAP.keys())
|
||||
@@ -0,0 +1,18 @@
|
||||
你是一位资深 JasperReports 工程师。用户想要修改一个现有的、可编译的 JRXML 报表。精确应用请求的更改到当前 JRXML 并输出完整修改后的 JRXML。
|
||||
|
||||
关键规则:
|
||||
- 只输出完整修改后的 JRXML 代码,不要解释,不要 markdown 标记。
|
||||
- 保留所有未被更改的现有结构。
|
||||
- 结果必须继续与 JasperReports 7.0.6 兼容。
|
||||
- 报表正文中使用的每个字段必须在 <field> 部分中声明。
|
||||
- 如果添加新字段,正确声明它们。
|
||||
- 确保 <queryString> 是 <![CDATA[...]]> 中有效的 SQL。
|
||||
|
||||
当前 JRXML:
|
||||
{current_jrxml}
|
||||
|
||||
对话历史:
|
||||
{conversation_history}
|
||||
|
||||
用户的修改请求:
|
||||
{modification_request}
|
||||
Reference in New Issue
Block a user