feat: 实现完整的人工审核功能与子图模块

- 新增三个核心子图:人工审核、意图理解、格式化输出
- 实现完整的审核 API 端点(/api/review/*)
- 前端添加审核确认界面(右下角固定框)
- 为每个子图创建分步测试代码
- 添加功能实现文档
This commit is contained in:
2026-04-25 13:24:50 +08:00
parent 851d52ed8d
commit bc26b81f08
9 changed files with 1981 additions and 4 deletions

View File

@@ -22,6 +22,9 @@ def render_chat_area():
# 渲染历史消息
_render_chat_history()
# 检查并渲染审核确认界面
_render_review_confirmation()
# 输入框和流式响应处理
_render_input_and_response()
@@ -344,3 +347,201 @@ def _show_completion_stats(event: dict):
if token_usage:
total_tokens = token_usage.get("total_tokens", 0)
st.caption(f"📊 消耗 {total_tokens} tokens | ⏱️ {elapsed:.2f}s")
def _render_review_confirmation():
"""渲染审核确认界面 - 类似编程工具的右下角确认交互"""
# 获取当前线程的待审核内容
thread_id = AppState.get_current_thread_id()
user_id = AppState.get_user_id()
# 初始化会话状态
if 'pending_review' not in st.session_state:
st.session_state.pending_review = None
if 'show_review_modify' not in st.session_state:
st.session_state.show_review_modify = False
if 'review_error' not in st.session_state:
st.session_state.review_error = None
# 如果有待审核内容,先尝试从后端获取最新状态
if st.session_state.pending_review:
review_id = st.session_state.pending_review.get("review_id")
if review_id:
try:
latest_review = api_client.get_review(review_id)
if latest_review and latest_review.get("status") != "PENDING":
# 审核已处理,清除待审核状态
st.session_state.pending_review = None
st.session_state.show_review_modify = False
except Exception as e:
pass
# 如果没有待审核内容,检查是否有新的待审核内容
if not st.session_state.pending_review:
try:
pending_reviews = api_client.get_pending_reviews(limit=10)
# 查找当前线程的待审核内容
for review in pending_reviews:
if review.get("thread_id") == thread_id and review.get("status") == "PENDING":
st.session_state.pending_review = {
"review_id": review.get("review_id"),
"content_to_review": review.get("content_to_review"),
"created_at": review.get("created_at"),
"user_id": review.get("user_id")
}
break
except Exception as e:
st.session_state.review_error = str(e)
# 测试按钮 - 用于演示审核功能
with st.container():
st.markdown("<div style='height: 20px;'></div>", unsafe_allow_html=True)
col_test, col_info = st.columns([1, 3])
with col_test:
if st.button("🔧 测试审核", key="test_review_chat"):
# 创建一个测试审核请求
test_content = "这是一条待审核的测试内容。\n\n您可以选择:\n✅ 确定 - 批准此内容\n✏️ 修改 - 修改后批准\n❌ 拒绝 - 拒绝此内容"
review_id = api_client.request_review(thread_id, user_id, test_content)
if review_id:
st.session_state.pending_review = {
"review_id": review_id,
"content_to_review": test_content,
"created_at": "2024-01-01T12:00:00",
"user_id": user_id
}
st.success("✅ 已创建测试审核")
st.rerun()
else:
st.error("❌ 创建测试审核失败")
with col_info:
if st.session_state.get("review_error"):
st.warning(f"⚠️ {st.session_state.review_error}")
elif st.session_state.pending_review:
st.info("📋 有待审核内容")
# 显示审核确认界面
if st.session_state.pending_review:
review = st.session_state.pending_review
# 使用右下角的固定样式显示通过CSS实现
st.markdown("""
<style>
.review-container {
position: fixed;
bottom: 20px;
right: 20px;
width: 400px;
background: white;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.15);
z-index: 1000;
padding: 20px;
border: 1px solid #e0e0e0;
}
.review-header {
font-weight: 600;
font-size: 16px;
margin-bottom: 12px;
color: #333;
display: flex;
justify-content: space-between;
align-items: center;
}
.review-content {
background: #f8f9fa;
padding: 12px;
border-radius: 8px;
margin-bottom: 16px;
max-height: 150px;
overflow-y: auto;
font-size: 14px;
line-height: 1.5;
white-space: pre-wrap;
}
.review-buttons {
display: flex;
gap: 8px;
justify-content: flex-end;
}
</style>
""", unsafe_allow_html=True)
# 渲染审核确认框
with st.container():
st.markdown('<div class="review-container">', unsafe_allow_html=True)
# 标题和关闭按钮
col_title, col_close = st.columns([4, 1])
with col_title:
st.markdown('<div class="review-header">📋 待审核内容</div>', unsafe_allow_html=True)
with col_close:
if st.button("", key="close_review"):
st.session_state.pending_review = None
st.rerun()
# 内容区域 - 转义 HTML
safe_content = review["content_to_review"].replace("<", "&lt;").replace(">", "&gt;")
st.markdown(f'<div class="review-content">{safe_content}</div>', unsafe_allow_html=True)
# 如果是修改模式,显示文本编辑框
if st.session_state.show_review_modify:
modified_content = st.text_area(
"修改内容",
value=review["content_to_review"],
key="modify_text_area",
height=100
)
col_cancel, col_submit = st.columns([1, 1])
with col_cancel:
if st.button("取消", key="cancel_modify", use_container_width=True):
st.session_state.show_review_modify = False
st.rerun()
with col_submit:
if st.button("提交修改", key="submit_modify", type="primary", use_container_width=True):
# 调用API提交修改
reviewer = user_id
success = api_client.modify_review(
review["review_id"],
reviewer,
modified_content
)
if success:
st.success("✅ 修改已提交")
st.session_state.pending_review = None
st.session_state.show_review_modify = False
st.rerun()
else:
st.error("❌ 提交失败")
else:
# 按钮区域
col_approve, col_modify, col_reject = st.columns([1, 1, 1])
with col_approve:
if st.button("✅ 确定", key="approve_btn", use_container_width=True, type="primary"):
# 调用审核通过API
reviewer = user_id
success = api_client.approve_review(review["review_id"], reviewer, "已批准")
if success:
st.success("✅ 已批准")
st.session_state.pending_review = None
st.rerun()
else:
st.error("❌ 操作失败")
with col_modify:
if st.button("✏️ 修改", key="modify_btn", use_container_width=True):
st.session_state.show_review_modify = True
st.rerun()
with col_reject:
if st.button("❌ 拒绝", key="reject_btn", use_container_width=True):
# 调用审核拒绝API
reviewer = user_id
success = api_client.reject_review(review["review_id"], reviewer, "已拒绝")
if success:
st.success("✅ 已拒绝")
st.session_state.pending_review = None
st.rerun()
else:
st.error("❌ 操作失败")
st.markdown('</div>', unsafe_allow_html=True)