load history or not

This commit is contained in:
CakeCN
2025-12-09 16:02:11 +08:00
parent 88ae20d596
commit 1be048bf06
6 changed files with 149 additions and 42 deletions

View File

@@ -33,7 +33,8 @@ class ProcRecord:
class ChatManager: class ChatManager:
_procs: Dict[str, ProcRecord] = {} _procs: Dict[str, ProcRecord] = {}
_lock = threading.RLock() _lock = threading.RLock()
def __init__(self): def __init__(self, restart=False):
self.restart = restart
self.chapter_chain_now = -1 self.chapter_chain_now = -1
self.ase_client = None self.ase_client = None
self.app = None self.app = None

View File

@@ -11,22 +11,23 @@ def on_connect_to_ase(client_entry, namespace_entry, init_data):
namespace_entry.emit('open-iframe',"", room=init_data['room'], namespace='/agent') namespace_entry.emit('open-iframe',"", room=init_data['room'], namespace='/agent')
namespace_entry.emit('message', "Agents服务已连接, 加载教案中", room=init_data['room'], namespace='/agent') namespace_entry.emit('message', "Agents服务已连接, 加载教案中", room=init_data['room'], namespace='/agent')
try: if init_data.get("restart", False):
base_url = namespace_entry.app.config['ASE_ENGINE_URL'] try:
clear_url = f"{base_url}/clear_user_session" base_url = namespace_entry.app.config['ASE_ENGINE_URL']
payload = { clear_url = f"{base_url}/clear_user_session"
'namespace_url': namespace_entry.app.config['ASE_ENGINE_URL_TOKEN'], # WebSocket命名空间URL payload = {
'user_id': init_data['room'], # 用户ID对应session_info.id 'namespace_url': namespace_entry.app.config['ASE_ENGINE_URL_TOKEN'], # WebSocket命名空间URL
'clear_type': 'all' # 清理类型,默认为全部 'user_id': init_data['room'], # 用户ID对应session_info.id
} 'clear_type': 'all' # 清理类型,默认为全部
headers = { }
'Content-Type': 'application/json' headers = {
} 'Content-Type': 'application/json'
response = requests.post(clear_url, json=payload, headers=headers) }
response.raise_for_status() # 检查请求是否成功 response = requests.post(clear_url, json=payload, headers=headers)
print(f"Clear user session successful: {response.json()}") response.raise_for_status() # 检查请求是否成功
except Exception as e: print(f"Clear user session successful: {response.json()}")
print(f"Error clearing user session: {e}") except Exception as e:
print(f"Error clearing user session: {e}")
ase_client.chatmanager.load_next_chapter() ase_client.chatmanager.load_next_chapter()
ase_client.send_text("chapter-start", "") ase_client.send_text("chapter-start", "")
namespace_entry.emit('message', "教案加载完毕", room=init_data['room'], namespace='/agent') namespace_entry.emit('message', "教案加载完毕", room=init_data['room'], namespace='/agent')

View File

@@ -85,31 +85,31 @@ class AgentNamespace(Namespace):
chatmanager.function_manager.add_function(judge, {'course_id': course_id, 'root_path': f'/home/{user_id}/{course_id}/{chapter_name}/{lesson_name}'}) chatmanager.function_manager.add_function(judge, {'course_id': course_id, 'root_path': f'/home/{user_id}/{course_id}/{chapter_name}/{lesson_name}'})
chatmanager.function_manager.add_function(next_chapter, {'user_uuid': user_uuid}) chatmanager.function_manager.add_function(next_chapter, {'user_uuid': user_uuid})
# 尝试加载用户学习进度 if not chatmanager.restart:# 尝试加载用户学习进度
progress = load_learning_progress(user_uuid, course_id, chapter_name, lesson_name) progress = load_learning_progress(user_uuid, course_id, chapter_name, lesson_name)
need_skip_chapters = 0 need_skip_chapters = 0
if progress:
# 恢复学习进度数据
if 'scores' in progress:
# 根据scores的数量确定需要跳过的章节数
need_skip_chapters = len(progress['scores'])
chatmanager.scores = progress['scores']
print(f"恢复用户学习进度: {user_uuid}, 已完成 {need_skip_chapters} 个章节")
# 恢复聊天历史 if progress:
if 'chat_historys' in progress: # 恢复学习进度数据
chatmanager.chat_historys = progress['chat_historys'] if 'scores' in progress:
# 根据scores的数量确定需要跳过的章节数
need_skip_chapters = len(progress['scores'])
chatmanager.scores = progress['scores']
print(f"恢复用户学习进度: {user_uuid}, 已完成 {need_skip_chapters} 个章节")
# 恢复聊天历史
if 'chat_historys' in progress:
chatmanager.chat_historys = progress['chat_historys']
else:
chatmanager.chat_historys = []
# 恢复当前章节链
if 'chapter_chain_now' in progress:
chatmanager.chapter_chain_now = progress['chapter_chain_now']
upload_learning_progress_to_cloud(progress)
else: else:
chatmanager.chat_historys = [] chatmanager.chat_historys = []
chatmanager.scores = []
# 恢复当前章节链
if 'chapter_chain_now' in progress:
chatmanager.chapter_chain_now = progress['chapter_chain_now']
upload_learning_progress_to_cloud(progress)
else:
chatmanager.chat_historys = []
chatmanager.scores = []
fmd,fmdp,fsp = load_full_markdown_file(course_id, chapter_name, lesson_name) fmd,fmdp,fsp = load_full_markdown_file(course_id, chapter_name, lesson_name)
user_uuid2ase_client[user_uuid] = HSAEngineClient(current_app.config["ASE_ENGINE_URL"], current_app.config["ASE_ENGINE_NAMESPACE"],id=user_id, chatmanager = chatmanager) user_uuid2ase_client[user_uuid] = HSAEngineClient(current_app.config["ASE_ENGINE_URL"], current_app.config["ASE_ENGINE_NAMESPACE"],id=user_id, chatmanager = chatmanager)
@@ -127,7 +127,7 @@ class AgentNamespace(Namespace):
user_uuid2ase_client[user_uuid].register_on_connect_entry( user_uuid2ase_client[user_uuid].register_on_connect_entry(
callback=on_connect_to_ase, callback=on_connect_to_ase,
entry=(self, user_uuid2ase_client[user_uuid]), entry=(self, user_uuid2ase_client[user_uuid]),
init_data={'room':user_uuid} init_data={'room':user_uuid, 'restart':chatmanager.restart}
) )
user_uuid2ase_client[user_uuid].register_route_with_entry( user_uuid2ase_client[user_uuid].register_route_with_entry(
route='dialog', route='dialog',

View File

@@ -85,6 +85,22 @@
folder:"{{workspace_path}}" folder:"{{workspace_path}}"
} }
</script> </script>
<script>
// 检测页面刷新事件
window.addEventListener('beforeunload', function(e) {
// 这里不弹出提示,只在页面刷新时执行跳转
// 但是beforeunload中不能直接跳转所以我们需要在页面加载时检测是否是刷新
});
// 检测是否是页面刷新
if (performance.navigation.type === 1 || performance.getEntriesByType("navigation")[0].type === "reload") {
// 是刷新操作,跳转到确认页面
const course_id = "{{course_id}}";
const chapter_name = "{{chapter_name}}";
const lesson_name = "{{lesson_name}}";
window.location.href = `/desktop_nouser/${course_id}/${chapter_name}/${lesson_name}`;
}
</script>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,76 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>确认加载历史数据</title>
<link rel="stylesheet" href="/static/cdnback/bootstrap.min.css">
<style>
body {
background-color: #f8f9fa;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
}
.confirm-container {
background: white;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 30px;
max-width: 500px;
width: 100%;
text-align: center;
}
h1 {
color: #343a40;
margin-bottom: 20px;
}
p {
color: #6c757d;
margin-bottom: 30px;
font-size: 16px;
}
.btn-group {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 20px;
}
.btn {
padding: 10px 30px;
font-size: 16px;
border-radius: 5px;
}
.btn-primary {
background-color: #007bff;
border: none;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
border: none;
}
.btn-secondary:hover {
background-color: #545b62;
}
</style>
</head>
<body>
<div class="confirm-container">
<h1>加载历史数据确认</h1>
<p>您即将进入学习环境,是否需要加载您之前的对话历史记录和学习进度数据?</p>
<div class="btn-group">
<a href="{{ url_for('vscode.desktop', user_uuid=user_uuid, course_id=course_id, chapter_name=chapter_name, lesson_name=lesson_name, load_history='true') }}" class="btn btn-primary">
是,加载历史数据
</a>
<a href="{{ url_for('vscode.desktop', user_uuid=user_uuid, course_id=course_id, chapter_name=chapter_name, lesson_name=lesson_name, load_history='false') }}" class="btn btn-secondary">
否,重新开始
</a>
</div>
</div>
</body>
</html>

View File

@@ -17,6 +17,13 @@ def desktop(user_uuid, course_id, chapter_name, lesson_name):
userid_recorder = current_app.extensions["userid_recorder"] userid_recorder = current_app.extensions["userid_recorder"]
if "user_uuid" not in session: if "user_uuid" not in session:
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))
# 检查是否有 load_history 参数
load_history = request.args.get('load_history')
if load_history is None:
# 如果没有参数,跳转到确认页面
return redirect(url_for("vscode.desktop_nouser", course_id=course_id, chapter_name=chapter_name, lesson_name=lesson_name))
# 全局映射 # 全局映射
user_id = uuid2username[user_uuid] user_id = uuid2username[user_uuid]
userid_recorder[f"{user_id}&{course_id}"] = session["user_uuid"] userid_recorder[f"{user_id}&{course_id}"] = session["user_uuid"]
@@ -58,7 +65,7 @@ def desktop(user_uuid, course_id, chapter_name, lesson_name):
chatmanager = ChatManager() chatmanager = ChatManager(restart=not load_history)
print(f"now user uuid {user_uuid}") print(f"now user uuid {user_uuid}")
current_app.extensions["user_uuid2chatmanager"][user_uuid] = chatmanager current_app.extensions["user_uuid2chatmanager"][user_uuid] = chatmanager
code_server_port = 10000 + int(uuid.uuid4().int % 10000) code_server_port = 10000 + int(uuid.uuid4().int % 10000)
@@ -140,7 +147,13 @@ def desktop_nouser(course_id, chapter_name, lesson_name):
if "user_uuid" not in session: if "user_uuid" not in session:
return redirect(url_for("auth.login")) # 如果你有 auth 蓝图 return redirect(url_for("auth.login")) # 如果你有 auth 蓝图
user_uuid = session["user_uuid"] user_uuid = session["user_uuid"]
return redirect(url_for("vscode.desktop", user_uuid=user_uuid, course_id=course_id, chapter_name=chapter_name, lesson_name=lesson_name)) return render_template(
"confirm_load_history.html",
user_uuid=user_uuid,
course_id=course_id,
chapter_name=chapter_name,
lesson_name=lesson_name
)
@bp.route("/vscode_data", methods=["POST"]) @bp.route("/vscode_data", methods=["POST"])
def vscode_data(): def vscode_data():