第一次提交
This commit is contained in:
10
utils/__init__.py
Normal file
10
utils/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
"""工具模块初始化文件"""
|
||||
from .logger import ConversationLogger, get_logger
|
||||
from .callback_handler import MessageCallbackHandler, get_callback_handler
|
||||
|
||||
__all__ = [
|
||||
"ConversationLogger",
|
||||
"get_logger",
|
||||
"MessageCallbackHandler",
|
||||
"get_callback_handler"
|
||||
]
|
||||
186
utils/callback_handler.py
Normal file
186
utils/callback_handler.py
Normal file
@@ -0,0 +1,186 @@
|
||||
"""
|
||||
回调处理器 - 用于实时推送对话内容到前端
|
||||
"""
|
||||
from typing import Callable, Dict, Any, List, Optional
|
||||
from datetime import datetime
|
||||
import threading
|
||||
import queue
|
||||
|
||||
|
||||
class MessageCallbackHandler:
|
||||
"""消息回调处理器,支持实时流式输出"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化回调处理器"""
|
||||
self.callbacks: List[Callable[[Dict[str, Any]], None]] = []
|
||||
self.message_queue = queue.Queue()
|
||||
self.is_running = False
|
||||
self._lock = threading.Lock()
|
||||
|
||||
def register_callback(self, callback: Callable[[Dict[str, Any]], None]):
|
||||
"""
|
||||
注册回调函数
|
||||
|
||||
Args:
|
||||
callback: 回调函数,接收字典格式的消息
|
||||
"""
|
||||
with self._lock:
|
||||
self.callbacks.append(callback)
|
||||
|
||||
def unregister_callback(self, callback: Callable[[Dict[str, Any]], None]):
|
||||
"""注销回调函数"""
|
||||
with self._lock:
|
||||
if callback in self.callbacks:
|
||||
self.callbacks.remove(callback)
|
||||
|
||||
def on_message(
|
||||
self,
|
||||
agent_name: str,
|
||||
message: str,
|
||||
role: str = "assistant",
|
||||
metadata: Optional[Dict] = None
|
||||
):
|
||||
"""
|
||||
处理新消息
|
||||
|
||||
Args:
|
||||
agent_name: Agent 名称
|
||||
message: 消息内容
|
||||
role: 角色
|
||||
metadata: 元数据
|
||||
"""
|
||||
msg_data = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"agent_name": agent_name,
|
||||
"role": role,
|
||||
"message": message,
|
||||
"metadata": metadata or {}
|
||||
}
|
||||
|
||||
# 放入队列
|
||||
self.message_queue.put(msg_data)
|
||||
|
||||
# 调用所有回调
|
||||
with self._lock:
|
||||
for callback in self.callbacks:
|
||||
try:
|
||||
callback(msg_data)
|
||||
except Exception as e:
|
||||
print(f"回调执行失败:{e}")
|
||||
|
||||
def on_thinking(self, agent_name: str, status: str = "thinking"):
|
||||
"""
|
||||
发送思考状态
|
||||
|
||||
Args:
|
||||
agent_name: Agent 名称
|
||||
status: 状态(thinking/generating/coding/testing)
|
||||
"""
|
||||
self.on_message(
|
||||
agent_name=agent_name,
|
||||
message=f"_{status}...",
|
||||
role="system",
|
||||
metadata={"status": status}
|
||||
)
|
||||
|
||||
def on_file_created(self, agent_name: str, file_path: str, file_type: str):
|
||||
"""
|
||||
发送文件创建事件
|
||||
|
||||
Args:
|
||||
agent_name: Agent 名称
|
||||
file_path: 文件路径
|
||||
file_type: 文件类型
|
||||
"""
|
||||
self.on_message(
|
||||
agent_name=agent_name,
|
||||
message=f"📄 创建了文件:{file_path}",
|
||||
role="system",
|
||||
metadata={
|
||||
"event_type": "file_created",
|
||||
"file_path": file_path,
|
||||
"file_type": file_type
|
||||
}
|
||||
)
|
||||
|
||||
def on_test_result(self, agent_name: str, passed: bool, details: str):
|
||||
"""
|
||||
发送测试结果
|
||||
|
||||
Args:
|
||||
agent_name: Agent 名称
|
||||
passed: 是否通过
|
||||
details: 详细信息
|
||||
"""
|
||||
icon = "✅" if passed else "❌"
|
||||
self.on_message(
|
||||
agent_name=agent_name,
|
||||
message=f"{icon} 测试结果:{'通过' if passed else '失败'}\n{details}",
|
||||
role="system",
|
||||
metadata={
|
||||
"event_type": "test_result",
|
||||
"passed": passed,
|
||||
"details": details
|
||||
}
|
||||
)
|
||||
|
||||
def on_human_approval_request(
|
||||
self,
|
||||
request_id: str,
|
||||
description: str,
|
||||
data: Dict[str, Any]
|
||||
):
|
||||
"""
|
||||
发送人工确认请求
|
||||
|
||||
Args:
|
||||
request_id: 请求 ID
|
||||
description: 请求描述
|
||||
data: 相关数据
|
||||
"""
|
||||
self.on_message(
|
||||
agent_name="Orchestrator",
|
||||
message=f"⚠️ 需要人工确认:{description}",
|
||||
role="system",
|
||||
metadata={
|
||||
"event_type": "human_approval",
|
||||
"request_id": request_id,
|
||||
"description": description,
|
||||
"data": data
|
||||
}
|
||||
)
|
||||
|
||||
def get_message(self, timeout: float = 1.0) -> Optional[Dict[str, Any]]:
|
||||
"""
|
||||
从队列获取消息(非阻塞)
|
||||
|
||||
Args:
|
||||
timeout: 超时时间
|
||||
|
||||
Returns:
|
||||
消息字典或 None
|
||||
"""
|
||||
try:
|
||||
return self.message_queue.get(timeout=timeout)
|
||||
except queue.Empty:
|
||||
return None
|
||||
|
||||
def clear_queue(self):
|
||||
"""清空消息队列"""
|
||||
while not self.message_queue.empty():
|
||||
try:
|
||||
self.message_queue.get_nowait()
|
||||
except queue.Empty:
|
||||
break
|
||||
|
||||
|
||||
# 全局回调实例
|
||||
_global_callback: Optional[MessageCallbackHandler] = None
|
||||
|
||||
|
||||
def get_callback_handler() -> MessageCallbackHandler:
|
||||
"""获取或创建全局回调处理器"""
|
||||
global _global_callback
|
||||
if _global_callback is None:
|
||||
_global_callback = MessageCallbackHandler()
|
||||
return _global_callback
|
||||
192
utils/logger.py
Normal file
192
utils/logger.py
Normal file
@@ -0,0 +1,192 @@
|
||||
"""
|
||||
日志工具模块 - 记录多智能体对话历史和系统事件
|
||||
"""
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Any, Optional
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class ConversationLogger:
|
||||
"""对话历史日志记录器"""
|
||||
|
||||
def __init__(self, log_dir: str = "logs"):
|
||||
"""
|
||||
初始化日志记录器
|
||||
|
||||
Args:
|
||||
log_dir: 日志目录
|
||||
"""
|
||||
self.log_dir = Path(log_dir)
|
||||
self.log_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 创建会话 ID
|
||||
self.session_id = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
self.log_file = self.log_dir / f"session_{self.session_id}.jsonl"
|
||||
|
||||
# 配置日志
|
||||
self.logger = logging.getLogger(f"autogen_sdls_{self.session_id}")
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
||||
# 文件处理器
|
||||
file_handler = logging.FileHandler(self.log_file, encoding='utf-8')
|
||||
file_handler.setLevel(logging.INFO)
|
||||
|
||||
# 控制台处理器
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
|
||||
# 格式化器
|
||||
formatter = logging.Formatter(
|
||||
'%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S'
|
||||
)
|
||||
file_handler.setFormatter(formatter)
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
self.logger.addHandler(file_handler)
|
||||
self.logger.addHandler(console_handler)
|
||||
|
||||
# 对话历史
|
||||
self.conversation_history: List[Dict[str, Any]] = []
|
||||
|
||||
def log_message(
|
||||
self,
|
||||
agent_name: str,
|
||||
message: str,
|
||||
role: str = "assistant",
|
||||
metadata: Optional[Dict] = None
|
||||
):
|
||||
"""
|
||||
记录单条消息
|
||||
|
||||
Args:
|
||||
agent_name: Agent 名称
|
||||
message: 消息内容
|
||||
role: 角色(user/assistant/system)
|
||||
metadata: 额外元数据
|
||||
"""
|
||||
entry = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"session_id": self.session_id,
|
||||
"agent_name": agent_name,
|
||||
"role": role,
|
||||
"message": message,
|
||||
"metadata": metadata or {}
|
||||
}
|
||||
|
||||
self.conversation_history.append(entry)
|
||||
|
||||
# 写入 JSONL 文件
|
||||
with open(self.log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(json.dumps(entry, ensure_ascii=False) + '\n')
|
||||
|
||||
self.logger.info(f"[{agent_name}] {message[:100]}...")
|
||||
|
||||
def log_event(self, event_type: str, description: str, data: Optional[Dict] = None):
|
||||
"""
|
||||
记录系统事件
|
||||
|
||||
Args:
|
||||
event_type: 事件类型
|
||||
description: 事件描述
|
||||
data: 相关数据
|
||||
"""
|
||||
entry = {
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
"session_id": self.session_id,
|
||||
"event_type": event_type,
|
||||
"description": description,
|
||||
"data": data or {}
|
||||
}
|
||||
|
||||
with open(self.log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(json.dumps(entry, ensure_ascii=False) + '\n')
|
||||
|
||||
self.logger.info(f"[EVENT] {event_type}: {description}")
|
||||
|
||||
def get_conversation_history(self) -> List[Dict[str, Any]]:
|
||||
"""获取完整的对话历史"""
|
||||
return self.conversation_history
|
||||
|
||||
def export_to_json(self, output_path: Optional[str] = None) -> str:
|
||||
"""
|
||||
导出对话历史为 JSON 格式
|
||||
|
||||
Args:
|
||||
output_path: 输出路径,默认在 logs 目录下
|
||||
|
||||
Returns:
|
||||
导出文件的路径
|
||||
"""
|
||||
if output_path is None:
|
||||
output_path = self.log_dir / f"conversation_{self.session_id}.json"
|
||||
else:
|
||||
output_path = Path(output_path)
|
||||
|
||||
export_data = {
|
||||
"session_id": self.session_id,
|
||||
"created_at": datetime.now().isoformat(),
|
||||
"total_messages": len(self.conversation_history),
|
||||
"conversation": self.conversation_history
|
||||
}
|
||||
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(export_data, f, ensure_ascii=False, indent=2)
|
||||
|
||||
self.logger.info(f"对话历史已导出到:{output_path}")
|
||||
return str(output_path)
|
||||
|
||||
def export_to_markdown(self, output_path: Optional[str] = None) -> str:
|
||||
"""
|
||||
导出对话历史为 Markdown 格式
|
||||
|
||||
Args:
|
||||
output_path: 输出路径
|
||||
|
||||
Returns:
|
||||
导出文件的路径
|
||||
"""
|
||||
if output_path is None:
|
||||
output_path = self.log_dir / f"conversation_{self.session_id}.md"
|
||||
else:
|
||||
output_path = Path(output_path)
|
||||
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
f.write(f"# AutoGen SDLC 对话历史\n\n")
|
||||
f.write(f"**会话 ID**: {self.session_id}\n")
|
||||
f.write(f"**创建时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
||||
f.write(f"**总消息数**: {len(self.conversation_history)}\n\n")
|
||||
f.write("---\n\n")
|
||||
|
||||
for entry in self.conversation_history:
|
||||
timestamp = entry["timestamp"][:19].replace('T', ' ')
|
||||
agent = entry["agent_name"]
|
||||
message = entry["message"]
|
||||
|
||||
f.write(f"### [{timestamp}] {agent}\n\n")
|
||||
f.write(f"{message}\n\n")
|
||||
f.write("---\n\n")
|
||||
|
||||
self.logger.info(f"对话历史已导出为 Markdown: {output_path}")
|
||||
return str(output_path)
|
||||
|
||||
|
||||
# 全局日志实例
|
||||
_global_logger: Optional[ConversationLogger] = None
|
||||
|
||||
|
||||
def get_logger(log_dir: str = "logs") -> ConversationLogger:
|
||||
"""获取或创建全局日志记录器"""
|
||||
global _global_logger
|
||||
if _global_logger is None:
|
||||
_global_logger = ConversationLogger(log_dir)
|
||||
return _global_logger
|
||||
|
||||
|
||||
def reset_logger():
|
||||
"""重置全局日志记录器"""
|
||||
global _global_logger
|
||||
_global_logger = None
|
||||
Reference in New Issue
Block a user