Files
mai-bot/src/MaiDiary/debug_client.py
2026-03-11 21:07:58 +08:00

96 lines
3.0 KiB
Python

"""
MaiSaka - Debug Viewer 客户端
在独立命令行窗口中显示每次 LLM 调用的完整 Prompt。
通过 TCP socket 将数据发送给 debug_viewer.py 子进程。
"""
import json
import os
import socket
import struct
import subprocess
import sys
import time
from typing import Optional
from config import console
class DebugViewer:
"""
在独立命令行窗口中显示每次 LLM 调用的完整 Prompt。
通过 TCP socket 将数据发送给 debug_viewer.py 子进程。
"""
def __init__(self, port: int = 19876):
self._port = port
self._conn: Optional[socket.socket] = None
self._process: Optional[subprocess.Popen] = None
def start(self):
"""启动调试窗口子进程并建立 TCP 连接。"""
script_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "debug_viewer.py")
try:
self._process = subprocess.Popen(
[sys.executable, script_path, str(self._port)],
creationflags=getattr(subprocess, "CREATE_NEW_CONSOLE", 0),
)
except Exception as e:
console.print(f"[warning]⚠️ 无法启动调试窗口: {e}[/warning]")
return
# 重试连接(等待子进程启动监听)
for attempt in range(20):
try:
time.sleep(0.3)
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.connect(("127.0.0.1", self._port))
self._conn = conn
console.print(
f"[success]✓ 调试窗口已启动[/success] [muted](port {self._port})[/muted]"
)
return
except ConnectionRefusedError:
conn.close()
console.print("[warning]⚠️ 无法连接到调试窗口(超时)[/warning]")
def send(self, label: str, messages: list, tools: Optional[list] = None, response: Optional[dict] = None):
"""发送一次 LLM 调用的完整 prompt 和响应到调试窗口。"""
if not self._conn:
return
# 只在有响应时才发送(避免显示两次:请求中 + 完成响应)
if response is None:
return
payload = {"label": label, "messages": messages}
if tools:
payload["tools"] = tools
payload["response"] = response
try:
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
header = struct.pack(">I", len(data))
self._conn.sendall(header + data)
except Exception:
# 连接断开时静默忽略
self._conn = None
def close(self):
"""关闭连接和子进程。"""
if self._conn:
try:
self._conn.close()
except Exception:
pass
self._conn = None
if self._process:
try:
self._process.terminate()
except Exception:
pass
self._process = None