22 Commits

Author SHA1 Message Date
CakeCN
36bbedd3cf no caddy reload 2026-01-09 12:06:55 +08:00
CakeCN
0e3703b1f4 more log 2026-01-09 11:45:17 +08:00
CakeCN
52696707dd log in connect 2026-01-09 11:31:00 +08:00
CakeCN
0a812e496f fix namespace init 2026-01-09 11:24:13 +08:00
CakeCN
de1ba08d7e 修改了 AgentNamespace 类,将全局实例变量替换为字典,使用用户 UUID 作为键 2026-01-09 11:23:02 +08:00
CakeCN
3f14cbd6c1 some log 2026-01-09 11:16:30 +08:00
CakeCN
c51565199c more log 2026-01-09 01:40:36 +08:00
CakeCN
8d29a26d7d no connect on server 2026-01-09 01:27:24 +08:00
CakeCN
decf24020a use user-joined 2026-01-09 01:22:29 +08:00
CakeCN
3dc219adc2 use join 2026-01-09 01:19:08 +08:00
CakeCN
40d1341685 more set alwaysconnect to True 2026-01-09 00:58:46 +08:00
CakeCN
0d606fb171 ,always_connect=True 2026-01-09 00:56:56 +08:00
CakeCN
e1f32ec980 ase_client add user-connected 2026-01-09 00:04:31 +08:00
CakeCN
ad8d7d069c 增加log 2026-01-08 23:54:27 +08:00
CakeCN
687b316db6 删去earning_progess_process的log 2026-01-08 23:51:55 +08:00
CakeCN
60248d5c51 log connect_to_ase 2026-01-08 23:38:50 +08:00
CakeCN
befec5543c fix: 前端打开codelike 2026-01-08 21:06:11 +08:00
CakeCN
af24c51a2f fix previous 前端发送用户信息 2026-01-08 21:04:37 +08:00
CakeCN
fa91696aae fix previous 2026-01-08 21:02:44 +08:00
CakeCN
e698c50a4b 调整前端user-connected 2026-01-08 21:01:11 +08:00
CakeCN
75795dd853 fix previous 2026-01-08 20:52:51 +08:00
CakeCN
5b8180b48e 将join room提前到on_connect 2026-01-08 20:50:56 +08:00
10 changed files with 38 additions and 62 deletions

View File

@@ -39,9 +39,12 @@ class ASEngineClient:
# 连接事件 # 连接事件
@self.sio.on('connect', namespace=self.namespace) @self.sio.on('connect', namespace=self.namespace)
def on_connect(): def on_connect():
print("---------------ASEngineClient on_connect----------------")
self.sid = self.sio.sid
print(f"Connected to server with sid: {self.sid}")
self.connected = True self.connected = True
self.sid = self.sio.get_sid(namespace=self.namespace) self.sio.emit('join', {'id': self.id}, namespace=self.namespace)
print(f"Connected to server. SID: {self.sid}")
if self._on_connect_callback is not None: if self._on_connect_callback is not None:
self._on_connect_callback( self._on_connect_callback(
self, self,

View File

@@ -14,7 +14,7 @@ from typing import Optional
mongo = PyMongo() mongo = PyMongo()
socketio = SocketIO(cors_allowed_origins="*",max_payload_size=52428800, async_mode="eventlet") # 不直接传 app socketio = SocketIO(cors_allowed_origins="*",max_payload_size=52428800, async_mode="eventlet",always_connect=False) # 不直接传 app
cors = CORS() cors = CORS()
# ===== 你的全局对象 ===== # ===== 你的全局对象 =====
# Backboard # Backboard
@@ -38,7 +38,7 @@ def init_extensions(app):
cors_allowed_origins='*',#app.config["VSCODE_WEB_URL"], cors_allowed_origins='*',#app.config["VSCODE_WEB_URL"],
ping_timeout=app.config["SOCKETIO_PING_TIMEOUT"], ping_timeout=app.config["SOCKETIO_PING_TIMEOUT"],
ping_interval=app.config["SOCKETIO_PING_INTERVAL"], ping_interval=app.config["SOCKETIO_PING_INTERVAL"],
max_payload_size=50000000, async_mode="eventlet" max_payload_size=50000000, async_mode="eventlet",always_connect=False
) )
cors.init_app( cors.init_app(
app, app,

View File

@@ -1,6 +1,4 @@
import requests import requests
from flask_socketio import Namespace
def clear_user_session(asengine_url, asengine_token, user_id): def clear_user_session(asengine_url, asengine_token, user_id):
''' '''
@@ -28,9 +26,12 @@ def on_connect_to_ase(client, entry, init_data, **kwargs):
''' '''
此时刚刚建立好aseclient连接并向前端发送打开code-server-like指令。 此时刚刚建立好aseclient连接并向前端发送打开code-server-like指令。
''' '''
namespace_entry, ase_client = entry namespace_entry, ase_client, logger = entry
logger.info(f"on_connect_to_ase with init_data: {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')
namespace_entry.emit('message', f'{init_data}', room=init_data['room'], namespace='/agent')
restart = init_data.get("restart", False) restart = init_data.get("restart", False)
# 不再需要手动next_chapter因为已经在on_login中根据mongo进度恢复了正确的章节 # 不再需要手动next_chapter因为已经在on_login中根据mongo进度恢复了正确的章节

View File

@@ -93,7 +93,6 @@ def get_multiagents_dialog_list(user_id: str) -> List[Dict]:
# 检查响应 # 检查响应
if response.status_code == 200: if response.status_code == 200:
current_app.logger.info(f"获取MultiAgents对话列表成功: user_id={user_id}, json={response.json()}")
return response.json() return response.json()
else: else:
current_app.logger.error(f"获取MultiAgents对话列表失败: HTTP {response.status_code}") current_app.logger.error(f"获取MultiAgents对话列表失败: HTTP {response.status_code}")

View File

@@ -63,6 +63,12 @@ class VSCodeNamespace(Namespace):
class AgentNamespace(Namespace): class AgentNamespace(Namespace):
def __init__(self, namespace):
super().__init__(namespace)
# 使用字典存储每个用户的chatmanager和ase_client避免多用户冲突
self.user_chatmanagers = {}
self.user_ase_clients = {}
def _load_learning_progress(self, chatmanager, user_id, course_id, chapter_name, lesson_name, continue_learn): def _load_learning_progress(self, chatmanager, user_id, course_id, chapter_name, lesson_name, continue_learn):
"""加载并恢复用户学习进度 """加载并恢复用户学习进度
@@ -120,8 +126,8 @@ class AgentNamespace(Namespace):
chapter_name = data['chapter_name'] chapter_name = data['chapter_name']
user_id = uuid2username[user_uuid] user_id = uuid2username[user_uuid]
session['user_uuid'] = user_uuid session['user_uuid'] = user_uuid
join_room(user_uuid, namespace='/agent') # 将该用户加入以user_id为名的room
print(f'User connected with session user_uuid: {user_uuid}') print(f'User connected with session user_uuid: {user_uuid}')
join_room(user_uuid)
user_uuid2chatmanager = ex["user_uuid2chatmanager"] user_uuid2chatmanager = ex["user_uuid2chatmanager"]
user_uuid2ase_client = ex["user_uuid2ase_client"] user_uuid2ase_client = ex["user_uuid2ase_client"]
@@ -152,8 +158,9 @@ class AgentNamespace(Namespace):
if chatmanager.chat_historys: if chatmanager.chat_historys:
emit('messages', chatmanager.chat_historys, room=user_uuid, namespace='/agent') emit('messages', chatmanager.chat_historys, room=user_uuid, namespace='/agent')
self.chatmanager = chatmanager # 存储每个用户的chatmanager和ase_client到字典中使用用户UUID作为键
self.ase_client = user_uuid2ase_client[user_uuid] self.user_chatmanagers[user_uuid] = chatmanager
self.user_ase_clients[user_uuid] = user_uuid2ase_client[user_uuid]
# 将restart设置为False避免后续重复处理 # 将restart设置为False避免后续重复处理
restart = chatmanager.restart restart = chatmanager.restart
@@ -161,14 +168,14 @@ 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], current_app.logger),
init_data={'room':user_uuid, 'restart':restart} init_data={'room':user_uuid, 'restart':restart}
) )
user_uuid2ase_client[user_uuid].register_route_with_entry( user_uuid2ase_client[user_uuid].register_route_with_entry(
route='dialog', route='dialog',
callback=receive_ase_dialog, callback=receive_ase_dialog,
entry=self, entry=self,
init_data={'room': user_uuid} init_data={'room':user_uuid}
) )
user_uuid2ase_client[user_uuid].register_route_with_entry( user_uuid2ase_client[user_uuid].register_route_with_entry(
route='dialog-hint', route='dialog-hint',
@@ -186,13 +193,13 @@ class AgentNamespace(Namespace):
route='judge', route='judge',
callback=receive_ase_judge, callback=receive_ase_judge,
entry=self, entry=self,
init_data={'room': user_uuid} init_data={'room':user_uuid}
) )
user_uuid2ase_client[user_uuid].register_route_with_entry( user_uuid2ase_client[user_uuid].register_route_with_entry(
route='next_chapter', route='next_chapter',
callback=receive_ase_next_chapter, callback=receive_ase_next_chapter,
entry=self, entry=self,
init_data={'room': user_uuid} init_data={'room':user_uuid}
) )
user_uuid2ase_client[user_uuid].register_route_with_entry( user_uuid2ase_client[user_uuid].register_route_with_entry(
route='chapter_score', route='chapter_score',

View File

@@ -66,25 +66,23 @@ function cleanupExpiredApprovals() {
// 定时清理过期记录 // 定时清理过期记录
setInterval(cleanupExpiredApprovals, 3000); // 每3秒检查一次 setInterval(cleanupExpiredApprovals, 3000); // 每3秒检查一次
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
data = window.appData; user_data = window.appData;
const host = window.location.host; const host = window.location.host;
console.log(`Connecting to WebSocket server at wss://${host}/agent`); console.log(`Connecting to WebSocket server at wss://${host}/agent`);
socket = io(`wss://${host}/agent`, { socket = io(`wss://${host}/agent`, {
query: { query: {
user_uuid: data.user_uuid, user_uuid: user_data.user_uuid,
folder: data.folder folder: user_data.folder
} }
}); });
// 初始化聊天消息管理器 // 初始化聊天消息管理器
chatManager = new ChatMessageManager(); chatManager = new ChatMessageManager();
socket.on('connect', function () { socket.on('connect', function () {
console.log('Connected to server'); console.log('Connected to WebSocket server'+socket.id+"with user"+JSON.stringify(user_data));
socket.emit('login',JSON.stringify(data)); socket.emit('login',JSON.stringify(user_data));
}); });
socket.on('open-iframe', function(data) { socket.on('open-iframe', function() {
OpenIframe(); OpenIframe(user_data);
}); });
// 监听来自服务器的消息 // 监听来自服务器的消息
socket.on('voiceMessage', function (data) { socket.on('voiceMessage', function (data) {

View File

@@ -208,7 +208,7 @@
console.error('Error fetching session:', error); console.error('Error fetching session:', error);
}); });
let lastiframe = null; let lastiframe = null;
function OpenIframe(){ function OpenIframe(data){
// 等待1s再加载 // 等待1s再加载
setTimeout(() => { setTimeout(() => {
// 在成功获取 session 后创建并插入 iframe // 在成功获取 session 后创建并插入 iframe

View File

@@ -187,7 +187,7 @@
let lastIframe = null; let lastIframe = null;
function OpenIframe(){ function OpenIframe(data){
// 在成功获取 session 后创建并插入 iframe // 在成功获取 session 后创建并插入 iframe
const iframe = document.createElement('iframe'); const iframe = document.createElement('iframe');
const div=document.getElementById('vscodeWeb') const div=document.getElementById('vscodeWeb')

View File

@@ -91,38 +91,6 @@ def desktop(user_uuid, course_id, chapter_name, lesson_name):
caddy_conf = f"""
handle_path /vsc{user_id}/* {{
reverse_proxy localhost:{code_server_port}
}}
"""
caddy_config_path = "/etc/caddy/Caddyfile"
with open(caddy_config_path, "r") as f:
lines = f.readlines()
pattern = re.compile(
r'handle_path\s+/vsc' + re.escape(user_id) + r'/\* {.*?reverse_proxy.*?}',
re.DOTALL
)
for i in range(len(lines)):
if (lines[i].strip()==''): lines[i]=''
content = "".join(lines)
if pattern.search(content):
new_content = pattern.sub('', content)
lines = new_content.splitlines(keepends=True)
if len(lines) >= 2:
insert_position = len(lines) - 2
else:
insert_position = len(lines)
if caddy_conf[-1] not in ['\n', '\r']:
caddy_conf += '\n'
lines.insert(insert_position, caddy_conf)
with open(caddy_config_path, "w") as f:
f.writelines(lines)
try:
subprocess.run(["sudo", "caddy", "reload", "--config", "/etc/caddy/Caddyfile"])
except subprocess.CalledProcessError as e:
print(f"Error reload caddy {e}")
cfg = current_app.config["VSCODE_WEB_PATH"] cfg = current_app.config["VSCODE_WEB_PATH"]
path_for_vscode = path_dir path_for_vscode = path_dir

View File

@@ -3,12 +3,12 @@ from flask_socketio import SocketIO
from .config import Config from .config import Config
# 初始化扩展 # 初始化扩展
cors = CORS() cors = CORS()
socketio = SocketIO(path='vsc-like-ws',cors_allowed_origins="*",max_payload_size=5242880, async_mode="eventlet") # 不直接传 app socketio = SocketIO(path='vsc-like-ws',cors_allowed_origins="*",max_payload_size=5242880, async_mode="eventlet",always_connect=False) # 不直接传 app
session_paths = {} session_paths = {}
def init_extensions(app): def init_extensions(app):
cors.init_app(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True) cors.init_app(app, resources={r"/*": {"origins": "*"}}, supports_credentials=True)
socketio.init_app(app, cors_allowed_origins="*", max_payload_size=5242880, async_mode="eventlet") socketio.init_app(app, cors_allowed_origins="*", max_payload_size=5242880, async_mode="eventlet",always_connect=False)
app.extensions["session_paths"] = session_paths app.extensions["session_paths"] = session_paths
def register_namespaces(app): def register_namespaces(app):