fix: 添加工作流
This commit is contained in:
@@ -112,8 +112,9 @@ class JSONRPCResponse:
|
||||
|
||||
class ProgressReporter:
|
||||
"""Progress reporting using JSON-RPC notifications"""
|
||||
|
||||
def __init__(self):
|
||||
step: int = 0
|
||||
total: int = 0
|
||||
def __init__(self, total: int = 0):
|
||||
self.rpc = JSONRPCResponse()
|
||||
|
||||
def report(self, step: str, progress: float, message: str, details: Dict[str, Any] = None) -> None:
|
||||
|
||||
186
python_core/utils/jsonrpc_enhanced.py
Normal file
186
python_core/utils/jsonrpc_enhanced.py
Normal file
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Enhanced JSON-RPC Communication Module
|
||||
增强版 JSON-RPC 通信模块
|
||||
|
||||
Uses json-rpc library for robust JSON-RPC 2.0 communication.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
import asyncio
|
||||
from typing import Any, Dict, Optional, Union, Callable, List
|
||||
from dataclasses import dataclass, asdict
|
||||
from enum import Enum
|
||||
|
||||
# json-rpc library imports
|
||||
from jsonrpc import JSONRPCResponseManager, dispatcher
|
||||
from jsonrpc.exceptions import JSONRPCError, JSONRPCInvalidRequest, JSONRPCMethodNotFound
|
||||
from jsonrpc.jsonrpc2 import JSONRPC20Request, JSONRPC20Response
|
||||
|
||||
class ProgressLevel(str, Enum):
|
||||
"""进度级别"""
|
||||
INFO = "info"
|
||||
SUCCESS = "success"
|
||||
WARNING = "warning"
|
||||
ERROR = "error"
|
||||
DEBUG = "debug"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ProgressUpdate:
|
||||
"""进度更新数据结构"""
|
||||
step: str
|
||||
type: str
|
||||
progress: int
|
||||
message: str
|
||||
timestamp: float
|
||||
level: ProgressLevel = ProgressLevel.INFO
|
||||
data: Optional[Dict[str, Any]] = None
|
||||
|
||||
|
||||
class EnhancedJSONRPCResponse:
|
||||
"""增强版 JSON-RPC 2.0 Response handler"""
|
||||
|
||||
def __init__(self, request_id: Optional[Union[str, int]] = None):
|
||||
# 如果没有提供request_id,生成一个UUID
|
||||
if request_id is None:
|
||||
self.request_id = str(uuid.uuid4())
|
||||
else:
|
||||
self.request_id = request_id
|
||||
|
||||
def success(self, result: Any) -> None:
|
||||
"""发送成功响应"""
|
||||
response = JSONRPC20Response(result=result, _id=self.request_id)
|
||||
self._send_response(response.data)
|
||||
|
||||
def error(self, code: int, message: str, data: Any = None) -> None:
|
||||
"""发送错误响应"""
|
||||
error = JSONRPCError(code=code, message=message, data=data)
|
||||
response = JSONRPC20Response(error=error, _id=self.request_id)
|
||||
self._send_response(response.data)
|
||||
|
||||
def progress(self, step: str, progress: int, message: str,
|
||||
level: ProgressLevel = ProgressLevel.INFO, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""发送进度通知"""
|
||||
progress_data = ProgressUpdate(
|
||||
step=step,
|
||||
type="progress",
|
||||
progress=progress,
|
||||
message=message,
|
||||
timestamp=time.time(),
|
||||
level=level,
|
||||
data=data
|
||||
)
|
||||
|
||||
self.notification(asdict(progress_data))
|
||||
|
||||
def notification(self, params: Any = None) -> None:
|
||||
"""发送通知(无需响应)"""
|
||||
response = JSONRPC20Response(result=params, _id=self.request_id)
|
||||
self._send_response(response.data)
|
||||
|
||||
def _send_response(self, response: Dict[str, Any]) -> None:
|
||||
"""发送响应到标准输出"""
|
||||
json_str = json.dumps(response, ensure_ascii=False, separators=(',', ':'))
|
||||
print(f"JSONRPC:{json_str}", file=sys.stdout, flush=True)
|
||||
|
||||
class EnhancedProgressReporter:
|
||||
"""增强版进度报告器"""
|
||||
|
||||
def __init__(self, total: int = 0):
|
||||
self.response = EnhancedJSONRPCResponse()
|
||||
self.step = 0
|
||||
self.total = total
|
||||
|
||||
def update(self, message: str, step: Optional[int] = None, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""更新进度"""
|
||||
if step is not None:
|
||||
self.step = step
|
||||
else:
|
||||
self.step += 1
|
||||
|
||||
progress = int((self.step / self.total * 100)) if self.total > 0 else -1
|
||||
self.response.progress("update", progress, message, ProgressLevel.INFO, data)
|
||||
|
||||
def info(self, message: str, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""信息消息"""
|
||||
self.response.progress("info", -1, message, ProgressLevel.INFO, data)
|
||||
|
||||
def success(self, message: str, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""成功消息"""
|
||||
self.response.progress("success", -1, message, ProgressLevel.SUCCESS, data)
|
||||
|
||||
def warning(self, message: str, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""警告消息"""
|
||||
self.response.progress("warning", -1, message, ProgressLevel.WARNING, data)
|
||||
|
||||
def error(self, message: str, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""错误消息"""
|
||||
self.response.progress("error", -1, message, ProgressLevel.ERROR, data)
|
||||
|
||||
def debug(self, message: str, data: Optional[Dict[str, Any]] = None) -> None:
|
||||
"""调试消息"""
|
||||
self.response.progress("debug", -1, message, ProgressLevel.DEBUG, data)
|
||||
|
||||
|
||||
class JSONRPCMethodRegistry:
|
||||
"""JSON-RPC 方法注册器"""
|
||||
|
||||
def __init__(self):
|
||||
self.methods: Dict[str, Callable] = {}
|
||||
self.dispatcher = dispatcher
|
||||
|
||||
def register(self, name: Optional[str] = None):
|
||||
"""注册方法装饰器"""
|
||||
def decorator(func: Callable):
|
||||
method_name = name or func.__name__
|
||||
self.methods[method_name] = func
|
||||
self.dispatcher.add_method(func, method_name)
|
||||
return func
|
||||
return decorator
|
||||
|
||||
def register_function(self, func: Callable, name: Optional[str] = None) -> None:
|
||||
"""直接注册函数"""
|
||||
method_name = name or func.__name__
|
||||
self.methods[method_name] = func
|
||||
self.dispatcher.add_method(func, method_name)
|
||||
|
||||
|
||||
def handle_request(self, request_data: str) -> str:
|
||||
"""处理JSON-RPC请求"""
|
||||
response = JSONRPCResponseManager.handle(request_data, self.dispatcher)
|
||||
return response.json
|
||||
|
||||
# 全局实例
|
||||
enhanced_progress_reporter = EnhancedProgressReporter()
|
||||
method_registry = JSONRPCMethodRegistry()
|
||||
|
||||
|
||||
# 便捷函数
|
||||
def create_response_handler(request_id: Optional[Union[str, int]] = None) -> EnhancedJSONRPCResponse:
|
||||
"""创建响应处理器"""
|
||||
return EnhancedJSONRPCResponse(request_id)
|
||||
|
||||
|
||||
def create_progress_reporter(total: int = 0) -> EnhancedProgressReporter:
|
||||
"""创建进度报告器"""
|
||||
return EnhancedProgressReporter(total)
|
||||
|
||||
|
||||
def register_method(name: Optional[str] = None):
|
||||
"""注册JSON-RPC方法"""
|
||||
return method_registry.register(name)
|
||||
|
||||
|
||||
def handle_jsonrpc_request(request_data: str) -> str:
|
||||
"""处理JSON-RPC请求"""
|
||||
return method_registry.handle_request(request_data)
|
||||
|
||||
|
||||
# 向后兼容的别名
|
||||
JSONRPCResponse = EnhancedJSONRPCResponse
|
||||
ProgressReporter = EnhancedProgressReporter
|
||||
274
python_core/utils/jsonrpc_server.py
Normal file
274
python_core/utils/jsonrpc_server.py
Normal file
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
JSON-RPC Server Implementation
|
||||
JSON-RPC 服务器实现
|
||||
|
||||
Provides HTTP and WebSocket JSON-RPC server implementations.
|
||||
"""
|
||||
|
||||
import json
|
||||
import asyncio
|
||||
from typing import Any, Dict, Optional, Callable, List
|
||||
from dataclasses import dataclass
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
import threading
|
||||
import logging
|
||||
|
||||
try:
|
||||
from jsonrpc import JSONRPCResponseManager, dispatcher
|
||||
from jsonrpc.exceptions import JSONRPCError
|
||||
JSON_RPC_AVAILABLE = True
|
||||
except ImportError:
|
||||
JSON_RPC_AVAILABLE = False
|
||||
|
||||
from .jsonrpc_enhanced import JSONRPCMethodRegistry, EnhancedProgressReporter
|
||||
|
||||
|
||||
@dataclass
|
||||
class ServerConfig:
|
||||
"""服务器配置"""
|
||||
host: str = "localhost"
|
||||
port: int = 8080
|
||||
debug: bool = False
|
||||
cors_enabled: bool = True
|
||||
max_request_size: int = 1024 * 1024 # 1MB
|
||||
|
||||
|
||||
class JSONRPCHTTPHandler(BaseHTTPRequestHandler):
|
||||
"""JSON-RPC HTTP 请求处理器"""
|
||||
|
||||
def __init__(self, method_registry: JSONRPCMethodRegistry, config: ServerConfig, *args, **kwargs):
|
||||
self.method_registry = method_registry
|
||||
self.config = config
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def do_POST(self):
|
||||
"""处理POST请求"""
|
||||
try:
|
||||
# 检查Content-Type
|
||||
content_type = self.headers.get('Content-Type', '')
|
||||
if 'application/json' not in content_type:
|
||||
self._send_error(400, "Content-Type must be application/json")
|
||||
return
|
||||
|
||||
# 读取请求体
|
||||
content_length = int(self.headers.get('Content-Length', 0))
|
||||
if content_length > self.config.max_request_size:
|
||||
self._send_error(413, "Request too large")
|
||||
return
|
||||
|
||||
request_data = self.rfile.read(content_length).decode('utf-8')
|
||||
|
||||
# 处理JSON-RPC请求
|
||||
response_data = self.method_registry.handle_request(request_data)
|
||||
|
||||
# 发送响应
|
||||
self._send_json_response(response_data)
|
||||
|
||||
except Exception as e:
|
||||
if self.config.debug:
|
||||
logging.exception("Error handling request")
|
||||
self._send_error(500, f"Internal server error: {str(e)}")
|
||||
|
||||
def do_OPTIONS(self):
|
||||
"""处理OPTIONS请求(CORS预检)"""
|
||||
if self.config.cors_enabled:
|
||||
self._send_cors_headers()
|
||||
self.end_headers()
|
||||
else:
|
||||
self._send_error(405, "Method not allowed")
|
||||
|
||||
def _send_json_response(self, data: str):
|
||||
"""发送JSON响应"""
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
if self.config.cors_enabled:
|
||||
self._send_cors_headers()
|
||||
self.end_headers()
|
||||
self.wfile.write(data.encode('utf-8'))
|
||||
|
||||
def _send_cors_headers(self):
|
||||
"""发送CORS头"""
|
||||
self.send_header('Access-Control-Allow-Origin', '*')
|
||||
self.send_header('Access-Control-Allow-Methods', 'POST, OPTIONS')
|
||||
self.send_header('Access-Control-Allow-Headers', 'Content-Type')
|
||||
|
||||
def _send_error(self, code: int, message: str):
|
||||
"""发送错误响应"""
|
||||
self.send_response(code)
|
||||
self.send_header('Content-Type', 'application/json')
|
||||
if self.config.cors_enabled:
|
||||
self._send_cors_headers()
|
||||
self.end_headers()
|
||||
|
||||
error_response = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": None,
|
||||
"error": {
|
||||
"code": code,
|
||||
"message": message
|
||||
}
|
||||
}
|
||||
self.wfile.write(json.dumps(error_response).encode('utf-8'))
|
||||
|
||||
def log_message(self, format, *args):
|
||||
"""重写日志方法"""
|
||||
if self.config.debug:
|
||||
super().log_message(format, *args)
|
||||
|
||||
|
||||
class JSONRPCServer:
|
||||
"""JSON-RPC HTTP 服务器"""
|
||||
|
||||
def __init__(self, config: Optional[ServerConfig] = None):
|
||||
self.config = config or ServerConfig()
|
||||
self.method_registry = JSONRPCMethodRegistry()
|
||||
self.server: Optional[HTTPServer] = None
|
||||
self.server_thread: Optional[threading.Thread] = None
|
||||
self.running = False
|
||||
|
||||
def register_method(self, name: Optional[str] = None):
|
||||
"""注册方法装饰器"""
|
||||
return self.method_registry.register(name)
|
||||
|
||||
def register_function(self, func: Callable, name: Optional[str] = None):
|
||||
"""注册函数"""
|
||||
self.method_registry.register_function(func, name)
|
||||
|
||||
def start(self, blocking: bool = True):
|
||||
"""启动服务器"""
|
||||
if self.running:
|
||||
raise RuntimeError("Server is already running")
|
||||
|
||||
# 创建处理器工厂
|
||||
def handler_factory(*args, **kwargs):
|
||||
return JSONRPCHTTPHandler(self.method_registry, self.config, *args, **kwargs)
|
||||
|
||||
# 创建服务器
|
||||
self.server = HTTPServer((self.config.host, self.config.port), handler_factory)
|
||||
self.running = True
|
||||
|
||||
print(f"🚀 JSON-RPC Server started on http://{self.config.host}:{self.config.port}")
|
||||
|
||||
if blocking:
|
||||
try:
|
||||
self.server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
self.stop()
|
||||
else:
|
||||
self.server_thread = threading.Thread(target=self.server.serve_forever)
|
||||
self.server_thread.daemon = True
|
||||
self.server_thread.start()
|
||||
|
||||
def stop(self):
|
||||
"""停止服务器"""
|
||||
if self.server and self.running:
|
||||
print("🛑 Stopping JSON-RPC Server...")
|
||||
self.server.shutdown()
|
||||
self.server.server_close()
|
||||
self.running = False
|
||||
|
||||
if self.server_thread:
|
||||
self.server_thread.join(timeout=5)
|
||||
|
||||
def __enter__(self):
|
||||
"""上下文管理器入口"""
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
"""上下文管理器出口"""
|
||||
self.stop()
|
||||
|
||||
|
||||
# 异步WebSocket服务器(需要额外依赖)
|
||||
try:
|
||||
import websockets
|
||||
import asyncio
|
||||
|
||||
class JSONRPCWebSocketServer:
|
||||
"""JSON-RPC WebSocket 服务器"""
|
||||
|
||||
def __init__(self, config: Optional[ServerConfig] = None):
|
||||
self.config = config or ServerConfig()
|
||||
self.method_registry = JSONRPCMethodRegistry()
|
||||
self.clients: List[Any] = []
|
||||
|
||||
def register_method(self, name: Optional[str] = None):
|
||||
"""注册方法装饰器"""
|
||||
return self.method_registry.register(name)
|
||||
|
||||
async def handle_client(self, websocket, path):
|
||||
"""处理WebSocket客户端"""
|
||||
self.clients.append(websocket)
|
||||
try:
|
||||
async for message in websocket:
|
||||
try:
|
||||
response = self.method_registry.handle_request(message)
|
||||
await websocket.send(response)
|
||||
except Exception as e:
|
||||
error_response = {
|
||||
"jsonrpc": "2.0",
|
||||
"id": None,
|
||||
"error": {
|
||||
"code": -32603,
|
||||
"message": f"Internal error: {str(e)}"
|
||||
}
|
||||
}
|
||||
await websocket.send(json.dumps(error_response))
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
pass
|
||||
finally:
|
||||
if websocket in self.clients:
|
||||
self.clients.remove(websocket)
|
||||
|
||||
async def broadcast(self, method: str, params: Any = None):
|
||||
"""广播通知到所有客户端"""
|
||||
if not self.clients:
|
||||
return
|
||||
|
||||
notification = {
|
||||
"jsonrpc": "2.0",
|
||||
"method": method,
|
||||
"params": params
|
||||
}
|
||||
message = json.dumps(notification)
|
||||
|
||||
# 发送到所有连接的客户端
|
||||
disconnected = []
|
||||
for client in self.clients:
|
||||
try:
|
||||
await client.send(message)
|
||||
except websockets.exceptions.ConnectionClosed:
|
||||
disconnected.append(client)
|
||||
|
||||
# 清理断开的连接
|
||||
for client in disconnected:
|
||||
self.clients.remove(client)
|
||||
|
||||
async def start(self):
|
||||
"""启动WebSocket服务器"""
|
||||
print(f"🚀 JSON-RPC WebSocket Server started on ws://{self.config.host}:{self.config.port}")
|
||||
|
||||
async with websockets.serve(
|
||||
self.handle_client,
|
||||
self.config.host,
|
||||
self.config.port
|
||||
):
|
||||
await asyncio.Future() # 永远运行
|
||||
|
||||
except ImportError:
|
||||
class JSONRPCWebSocketServer:
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise ImportError("WebSocket server requires 'websockets' package")
|
||||
|
||||
|
||||
# 便捷函数
|
||||
def create_server(config: Optional[ServerConfig] = None) -> JSONRPCServer:
|
||||
"""创建HTTP服务器"""
|
||||
return JSONRPCServer(config)
|
||||
|
||||
|
||||
def create_websocket_server(config: Optional[ServerConfig] = None) -> JSONRPCWebSocketServer:
|
||||
"""创建WebSocket服务器"""
|
||||
return JSONRPCWebSocketServer(config)
|
||||
Reference in New Issue
Block a user