178 lines
5.8 KiB
Python
178 lines
5.8 KiB
Python
"""
|
|
CorrectBench Web Interface
|
|
基于 Streamlit 的前端界面,集成配置选择与实时日志输出
|
|
"""
|
|
|
|
import streamlit as st
|
|
import subprocess
|
|
import threading
|
|
import queue
|
|
import time
|
|
import os
|
|
import sys
|
|
|
|
# 添加项目根目录到路径
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from frontend.config_loader import get_config_files, get_config_path, load_tasks_from_config
|
|
from frontend.log_capture import LogReader
|
|
|
|
# 页面配置
|
|
st.set_page_config(
|
|
page_title="CorrectBench",
|
|
page_icon="🛠️",
|
|
layout="wide"
|
|
)
|
|
|
|
# 进程结束标记,用于从队列中通知主线程进程已结束
|
|
_PROCESS_DONE_MARKER = "__PROCESS_DONE__"
|
|
|
|
|
|
def run_process(log_queue: queue.Queue, stop_event: threading.Event, config_path: str):
|
|
"""后台线程运行生成进程,进程结束后向队列发送结束标记"""
|
|
process = subprocess.Popen(
|
|
[sys.executable, "main.py", "-c", config_path],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
text=True,
|
|
bufsize=1,
|
|
cwd=os.path.dirname(os.path.abspath(__file__))
|
|
)
|
|
|
|
# 使用 LogReader 封装日志读取
|
|
reader = LogReader(process, log_queue, stop_event)
|
|
reader.start()
|
|
|
|
# 等待进程结束,同时检测 stop_event 以支持用户主动终止
|
|
while process.poll() is None:
|
|
if stop_event.is_set():
|
|
process.terminate()
|
|
try:
|
|
process.wait(timeout=3)
|
|
except subprocess.TimeoutExpired:
|
|
process.kill()
|
|
process.wait()
|
|
break
|
|
time.sleep(0.1)
|
|
|
|
# 等待日志读取线程结束,确保所有输出都已入队
|
|
reader.join(timeout=5)
|
|
|
|
# 向队列发送进程结束标记,通知主线程重置状态
|
|
log_queue.put(_PROCESS_DONE_MARKER)
|
|
|
|
|
|
def main():
|
|
# 初始化 session_state
|
|
if "running" not in st.session_state:
|
|
st.session_state.running = False
|
|
if "log_queue" not in st.session_state:
|
|
st.session_state.log_queue = queue.Queue()
|
|
if "stop_event" not in st.session_state:
|
|
st.session_state.stop_event = threading.Event()
|
|
if "log_lines" not in st.session_state:
|
|
st.session_state.log_lines = []
|
|
|
|
# ===== 侧边栏:配置选择 =====
|
|
with st.sidebar:
|
|
st.header("⚙️ 配置")
|
|
|
|
config_files = get_config_files()
|
|
if config_files:
|
|
default_idx = config_files.index("custom.yaml") if "custom.yaml" in config_files else 0
|
|
selected_config = st.selectbox(
|
|
"选择配置文件",
|
|
config_files,
|
|
index=default_idx,
|
|
disabled=st.session_state.running,
|
|
)
|
|
config_path = get_config_path(selected_config)
|
|
else:
|
|
st.warning("未找到配置文件,将使用默认配置")
|
|
config_path = "config/custom.yaml"
|
|
selected_config = "custom.yaml"
|
|
|
|
# 显示当前配置中的题目列表
|
|
tasks = load_tasks_from_config(config_path)
|
|
if tasks:
|
|
with st.expander(f"📋 题目列表 ({len(tasks)} 个)", expanded=False):
|
|
for t in tasks:
|
|
st.text(t)
|
|
else:
|
|
st.info("配置中未指定题目列表")
|
|
|
|
st.markdown("---")
|
|
st.caption(f"配置路径: `{config_path}`")
|
|
|
|
# ===== 主区域 =====
|
|
st.title("CorrectBench - 测试基准生成")
|
|
st.markdown(f"当前配置: `{selected_config}`")
|
|
|
|
col1, col2, col3 = st.columns([1, 1, 2])
|
|
start_btn = col1.button("▶️ 开始生成", disabled=st.session_state.running, use_container_width=True)
|
|
stop_btn = col2.button("⏹️ 停止", disabled=not st.session_state.running, use_container_width=True)
|
|
|
|
# 运行状态指示
|
|
if st.session_state.running:
|
|
col3.markdown("🟢 **运行中...**")
|
|
elif st.session_state.log_lines and st.session_state.log_lines[-1] == "[完成]":
|
|
col3.markdown("✅ **运行完成**")
|
|
elif st.session_state.log_lines and st.session_state.log_lines[-1] == "[已停止]":
|
|
col3.markdown("🔴 **已停止**")
|
|
|
|
st.markdown("---")
|
|
|
|
# 处理开始按钮
|
|
if start_btn and not st.session_state.running:
|
|
st.session_state.running = True
|
|
st.session_state.log_lines = ["正在启动..."]
|
|
st.session_state.stop_event.clear()
|
|
# 清空队列中可能残留的旧消息
|
|
while not st.session_state.log_queue.empty():
|
|
try:
|
|
st.session_state.log_queue.get_nowait()
|
|
except queue.Empty:
|
|
break
|
|
|
|
thread = threading.Thread(
|
|
target=run_process,
|
|
args=(st.session_state.log_queue, st.session_state.stop_event, config_path),
|
|
daemon=True
|
|
)
|
|
thread.start()
|
|
|
|
if stop_btn and st.session_state.running:
|
|
st.session_state.stop_event.set()
|
|
st.session_state.log_lines.append("[正在停止...]")
|
|
|
|
# 读取日志并检测进程结束标记
|
|
try:
|
|
while True:
|
|
try:
|
|
line = st.session_state.log_queue.get_nowait()
|
|
if line == _PROCESS_DONE_MARKER:
|
|
# 进程已自然结束,重置运行状态
|
|
st.session_state.running = False
|
|
st.session_state.log_lines.append("[完成]")
|
|
else:
|
|
st.session_state.log_lines.append(line.rstrip("\n"))
|
|
except queue.Empty:
|
|
break
|
|
except Exception:
|
|
pass
|
|
|
|
# 显示日志
|
|
st.markdown("### 终端输出")
|
|
if st.session_state.log_lines:
|
|
st.code("\n".join(st.session_state.log_lines[-200:]), language=None)
|
|
else:
|
|
st.info("等待开始生成...")
|
|
|
|
# 运行中自动刷新
|
|
if st.session_state.running:
|
|
time.sleep(0.3)
|
|
st.rerun()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |