diff --git a/Html/__pycache__/backboardManager.cpython-310.pyc b/Html/__pycache__/backboardManager.cpython-310.pyc index 876200f..1e7d73f 100644 Binary files a/Html/__pycache__/backboardManager.cpython-310.pyc and b/Html/__pycache__/backboardManager.cpython-310.pyc differ diff --git a/Html/apps/config.py b/Html/apps/config.py index cc61509..df4ad86 100644 --- a/Html/apps/config.py +++ b/Html/apps/config.py @@ -44,4 +44,7 @@ class Config: "secret_key": GLOBAL_CONFIG['TENCENT_COS']['secret_key'], "bucket": GLOBAL_CONFIG['TENCENT_COS']['bucket'], "region": GLOBAL_CONFIG['TENCENT_COS']['region'], - } \ No newline at end of file + } + + ASE_ENGINE_URL = GLOBAL_CONFIG['ASE_ENGINE']['url'] + ASE_ENGINE_NAMESPACE = GLOBAL_CONFIG['ASE_ENGINE']['namespace'] \ No newline at end of file diff --git a/Html/apps/extension_ase/ase_client/__init__.py b/Html/apps/extension_ase/ase_client/__init__.py new file mode 100644 index 0000000..80bdad0 --- /dev/null +++ b/Html/apps/extension_ase/ase_client/__init__.py @@ -0,0 +1 @@ +from .client import ASEngineClient \ No newline at end of file diff --git a/Html/apps/extension_ase/ase_client/client.py b/Html/apps/extension_ase/ase_client/client.py new file mode 100644 index 0000000..140c0c2 --- /dev/null +++ b/Html/apps/extension_ase/ase_client/client.py @@ -0,0 +1,267 @@ +import socketio +import socketio +import time +import base64 +import os +from datetime import datetime +from typing import Dict, Callable, Any, Optional +from functools import partial + +class ASEngineClient: + def __init__(self, server_url: str, namespace: str): + """ + 初始化客户端 + :param server_url: 服务器URL,例如 'http://localhost:5000' + :param namespace: 命名空间,例如 '/socket.io/7100eebf7a909e1ee3f4e06766f512f0358ecb6ac51d5434c17d31dd70890dc1' + """ + self.server_url = server_url + self.namespace = namespace + self.sid = None + self.connected = False + + # 创建SocketIO客户端 + self.sio = socketio.Client() + + # 存储路由和对应的回调函数 + self.routes: Dict[str, Dict[str, Callable]] = {} + + # 注册基础事件处理函数 + self._register_base_events() + + def _register_base_events(self): + """注册基础事件处理函数""" + # 连接事件 + @self.sio.on('connect', namespace=self.namespace) + def on_connect(): + self.connected = True + self.sid = self.sio.get_sid(namespace=self.namespace) + print(f"Connected to server. SID: {self.sid}") + + # 断开连接事件 + @self.sio.on('disconnect', namespace=self.namespace) + def on_disconnect(): + self.connected = False + self.sid = None + print("Disconnected from server") + + @self.sio.on('error', namespace=self.namespace) + def on_error(data): + print(f"Received error: {data}") + self.connected = False + self.sid = None + + + # 响应事件 + @self.sio.on('response', namespace=self.namespace) + def on_response(data): + print(f"Received response: {data}") + + + + + def register_route_with_entry(self, route: str, + callback: Optional[Callable] = None, + entry: Any = None, + init_data: Optional[Any] = None) -> bool: + if not route: + print("Route cannot be empty") + return False + + if callback is None: + bound_cb = self._default_callback + else: + bound_cb = partial(callback, entry) # 等价于 lambda data, init: callback(entry, data, init) + + self.routes[route] = { + 'callback': bound_cb, + 'entry': entry, + 'init_data': init_data, + } + + @self.sio.on(route, namespace=self.namespace) + def on_route_message(data, _route=route): + print(f"Received message on route '{_route}': {data}") + route_info = self.routes.get(_route) + if not route_info: + return + cb = route_info['callback'] # 已经绑定过 entry + init = route_info.get('init_data') + try: + cb(data, init) # 现在只传 data / init_data + except Exception as e: + print(f"Callback error on route '{_route}': {e}") + + return True + + + def register_route(self, route: str, callback: Optional[Callable] = None, init_data: Optional[Any] = None): + """ + 注册一个路由及其回调函数 + :param route: 路由名称 + :param callback: 回调函数,接收消息数据作为参数 + :return: 是否注册成功 + """ + if not route: + print("Route cannot be empty") + return False + + # 存储路由和回调 + self.routes[route] = { + 'callback': callback if callback else self._default_callback + } + + # 注册事件处理器 + @self.sio.on(route, namespace=self.namespace) + def on_route_message(data): + print(f"Received message on route '{route}': {data}") + if 'callback' in self.routes[route]: + try: + self.routes[route]['callback'](None, data, init_data) + except Exception as e: + print(f"Error in callback for route '{route}': {e}") + + print(f"Route '{route}' registered successfully") + return True + + def _default_callback(self, entry: Any, data: Any, init_data: Optional[Any] = None): + """默认回调函数""" + print(f"Default callback received: {data}") + + def connect(self, timeout: int = 5) -> bool: + """ + 连接到服务器 + :param timeout: 超时时间(秒) + :return: 是否连接成功 + """ + try: + self.sio.connect( + self.server_url, + namespaces=[self.namespace], + wait_timeout=timeout + ) + # 等待连接建立 + start_time = time.time() + while not self.connected and (time.time() - start_time) < timeout: + time.sleep(0.1) + return self.connected + except Exception as e: + print(f"Connection failed: {e}") + return False + + def disconnect(self): + """断开与服务器的连接""" + if self.connected: + self.sio.disconnect() + self.connected = False + self.sid = None + print("Disconnected from server") + + def send_text(self, route: str, text: str) -> bool: + """ + 发送文本消息 + :param route: 目标路由 + :param text: 要发送的文本 + :return: 是否发送成功 + """ + if not self.connected: + print("Not connected to server") + return False + + if route not in self.routes: + print(f"Route '{route}' not registered") + return False + + try: + self.sio.emit(route, {'type': 'text', 'data': text}, namespace=self.namespace) + print(f"Sent text to route '{route}': {text}") + return True + except Exception as e: + print(f"Failed to send text to route '{route}': {e}") + return False + + def send_voice(self, route: str, voice_data: bytes) -> bool: + """ + 发送语音数据 + :param route: 目标路由 + :param voice_data: 语音数据字节流 + :return: 是否发送成功 + """ + if not self.connected: + print("Not connected to server") + return False + + try: + # 编码语音数据为base64 + # voice_data = 'data:image/jpeg;base64,' +base64.b64encode(voice_data).decode('utf-8') + + # 发送数据 + self.sio.emit( + route, + { + 'type': 'audio', + 'data': voice_data, + 'timestamp': int(time.time() * 1000), + 'sampleRate': 16000 + }, + namespace=self.namespace + ) + + + print(f"Sent voice file to route '{route}'") + return True + except Exception as e: + print(f"Failed to send voice to route '{route}': {e}") + return False + + def send_image(self, route: str, file_path: str) -> bool: + """ + 发送图片文件 + :param route: 目标路由 + :param file_path: 图片文件路径 + :return: 是否发送成功 + """ + if not self.connected: + print("Not connected to server") + return False + + + try: + # 检查文件是否存在 + if not os.path.exists(file_path): + print(f"File not found: {file_path}") + return False + + # 读取文件并进行base64编码 + with open(file_path, 'rb') as f: + image_data = 'data:image/jpeg;base64,' + base64.b64encode(f.read()).decode('utf-8') + + # 获取文件名 + filename = os.path.basename(file_path) + + # 发送数据 + self.sio.emit( + route, + { + 'type': 'image', + 'data': image_data + }, + namespace=self.namespace + ) + print(f"Sent image file to route '{route}': {filename}") + return True + except Exception as e: + print(f"Failed to send image to route '{route}': {e}") + return False + + def run_forever(self): + """ + 保持客户端运行""" + if self.connected: + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + print("Client stopped by user") + self.disconnect() + else: + print("Not connected to server") \ No newline at end of file diff --git a/Html/apps/extensions.py b/Html/apps/extensions.py index bb0bc58..300203e 100644 --- a/Html/apps/extensions.py +++ b/Html/apps/extensions.py @@ -13,7 +13,7 @@ from qcloud_cos import CosS3Client mongo = PyMongo() -socketio = SocketIO(cors_allowed_origins="*") # 不直接传 app +socketio = SocketIO(cors_allowed_origins="*",max_payload_size=52428800, async_mode="threading") # 不直接传 app cors = CORS() # ===== 你的全局对象 ===== # Backboard @@ -28,6 +28,8 @@ username2uuid = {} course_list = CourseList() user_uuid2UserClass = {} +user_uuid2ase_client = {} + from AlgoriAgent.projects.algoriAgent.agent_manager import AgentManager agent_manager = AgentManager() def init_extensions(app): @@ -37,6 +39,7 @@ def init_extensions(app): cors_allowed_origins='*',#app.config["VSCODE_WEB_URL"], ping_timeout=app.config["SOCKETIO_PING_TIMEOUT"], ping_interval=app.config["SOCKETIO_PING_INTERVAL"], + max_payload_size=50000000, async_mode="threading" ) cors.init_app( app, @@ -55,7 +58,7 @@ def init_extensions(app): app.extensions["user_uuid2UserClass"] = user_uuid2UserClass app.extensions["agent_manager"] = agent_manager app.extensions["my_function"] = MyFunction() - + app.extensions["user_uuid2ase_client"] = user_uuid2ase_client mongo.init_app(app,uri=app.config["MONGO_URI"]) app.extensions["mongo"] = mongo diff --git a/Html/apps/sockets/namespaces.py b/Html/apps/sockets/namespaces.py index 6480fbf..f0c8146 100644 --- a/Html/apps/sockets/namespaces.py +++ b/Html/apps/sockets/namespaces.py @@ -5,6 +5,7 @@ from flask_socketio import Namespace, join_room, leave_room, emit from ..services.memory_service import save_chapter_memory_by_uuid from ..services.backboard_service import realtime_response from ..services.markdown_service import load_full_markdown_file +from ..extension_ase.ase_client import ASEngineClient class VSCodeNamespace(Namespace): def on_login(self,data): @@ -36,6 +37,7 @@ class VSCodeNamespace(Namespace): class AgentNamespace(Namespace): def on_login(self, data): + self.current_app = current_app ex = current_app.extensions uuid2username = ex["uuid2username"] backboard_manager = ex["backboard_manager"] @@ -48,6 +50,15 @@ class AgentNamespace(Namespace): user_id = uuid2username[user_uuid] session['user_uuid'] = user_uuid print(f'User connected with session user_uuid: {user_uuid}') + user_uuid2ase_client = ex["user_uuid2ase_client"] + user_uuid2ase_client[user_uuid] = ASEngineClient(current_app.config["ASE_ENGINE_URL"], current_app.config["ASE_ENGINE_NAMESPACE"]) + user_uuid2ase_client[user_uuid].connect() + user_uuid2ase_client[user_uuid].register_route_with_entry( + route='dialog', + callback=self.receive_ase_dialog, + entry=self, # 传你的 Namespace 对象 + init_data={'room': user_uuid} # 想传啥都可以 + ) fmd,fmdp,fsp = load_full_markdown_file(course_id, chapter_name, lesson_name) # 从markdown、markdown_prompts、score_prompts中各读取数据folder.md并进行new_agent @@ -74,31 +85,40 @@ class AgentNamespace(Namespace): uuid = session.get('user_uuid') uuid2username = ex["uuid2username"] user_id = uuid2username[uuid] + user_uuid2ase_client = ex["user_uuid2ase_client"] if (type(data)==str): data = json.loads(data) if data['type'] == 'text': - res = agent_manager.invoke(uuid, data['data'], backboard_manager.get_backboard(user_id).get_info_prompt()) - print('=*='*20) - print(res.content) - reply = f"{res.content['speak']}" - with current_app.app_context(): - emit('message',reply, room=uuid, namespace='/agent') - emit('request_function',res.content['function'], room=uuid, namespace='/agent') + # res = agent_manager.invoke(uuid, data['data'], backboard_manager.get_backboard(uuid).get_info_prompt()) + user_uuid2ase_client[uuid].send_text('dialog', data['data']) + # print('=*='*20) + # print(res.content) + # reply = f"{res.content['speak']}" + # with current_app.app_context(): + # emit('message',reply, room=uuid, namespace='/agent') + # emit('request_function',res.content['function'], room=uuid, namespace='/agent') if data['type'] == 'function': agent_manager.function_call(uuid, data['data']) + + def receive_ase_dialog(client_entry: ASEngineClient, namespace_entry: Namespace, data, init_data): + print(f"Received from ASEngine: {data}") + # 直接使用 entry(也就是你的 Namespace 对象) + namespace_entry.emit('message', data['data'], room=init_data['room'], namespace='/agent') + + + def on_initiative(self,data): print("User active function call") ex = current_app.extensions agent_manager = ex["agent_manager"] backboard_manager = ex["backboard_manager"] - uuid2username = ex["uuid2username"] - user_id = uuid2username[session['user_uuid']] + uuid = session.get('user_uuid') if data['name'] == 'sample_judge': - agent_manager.sample_judge(user_id, backboard_manager.get_backboard(user_id)) + agent_manager.sample_judge(uuid, backboard_manager.get_backboard(uuid)) if data['name'] == 'judge': - agent_manager.judge(user_id, backboard_manager.get_backboard(user_id)) + agent_manager.judge(uuid, backboard_manager.get_backboard(uuid)) diff --git a/Html/backboardManager.py b/Html/backboardManager.py index 1777f8d..7de5f19 100644 --- a/Html/backboardManager.py +++ b/Html/backboardManager.py @@ -5,8 +5,8 @@ import time import os class Backboard: - def __init__(self, user_id, username, course_id, lesson_id, root_path): - self.user_id = user_id + def __init__(self, user_uuid, username, course_id, lesson_id, root_path): + self.user_uuid = user_uuid self.username = username self.course_id = course_id self.lesson_id = lesson_id @@ -68,13 +68,13 @@ class Backboard: class BackBoardManager: def __init__(self): - self.backboards = {} # user_id: Backboard + self.backboards = {} # user_uuid: Backboard - def add_backboard(self, user_id, username, course_id, lesson_id, root_path): - self.backboards[user_id] = Backboard(user_id,username, course_id, lesson_id, root_path) + def add_backboard(self, user_uuid, username, course_id, lesson_id, root_path): + self.backboards[user_uuid] = Backboard(user_uuid,username, course_id, lesson_id, root_path) - def get_backboard(self, user_id) -> Backboard: - return self.backboards[user_id] + def get_backboard(self, user_uuid) -> Backboard: + return self.backboards[user_uuid] diff --git a/study/TCake/68bacdfadf5aeae0912f7f18/基础算法/.config b/study/TCake/68bacdfadf5aeae0912f7f18/基础算法/.config index fc40984..6455270 100644 --- a/study/TCake/68bacdfadf5aeae0912f7f18/基础算法/.config +++ b/study/TCake/68bacdfadf5aeae0912f7f18/基础算法/.config @@ -1 +1 @@ -{"user_uuid": "user_ec2c74c2-9466-4ea1-99ae-15c2ea3f8c8e", "course_id": "68bacdfadf5aeae0912f7f18", "chapter_name": "基础算法", "lesson_name": "二分", "path": "/mnt/f/GIThubRepository/CodeAgent/code-agent/study/TCake/68bacdfadf5aeae0912f7f18/基础算法"} \ No newline at end of file +{"user_uuid": "user_d0cb8736-c5b0-42ba-8eca-6dc9754d9645", "course_id": "68bacdfadf5aeae0912f7f18", "chapter_name": "基础算法", "lesson_name": "二分", "path": "/mnt/f/GIThubRepository/CodeAgent/code-agent/study/TCake/68bacdfadf5aeae0912f7f18/基础算法"} \ No newline at end of file