from functools import wraps from typing import Any from pydantic import BaseModel, ConfigDict, Field, ValidationError from yuxi.channel.gateway.protocol import GatewayErrorCode, RpcRequest, RpcResponse class ErrorShape(BaseModel): code: str message: str = "" data: Any | None = None retryable: bool = False retryAfterMs: int | None = None class RequestFrameSchema(BaseModel): type: str = "request" id: str method: str params: dict | None = None sessionId: str | None = None class ResponseFrameSchema(BaseModel): type: str = "response" id: str ok: bool = True result: dict | None = None error: ErrorShape | None = None class EventFrameSchema(BaseModel): type: str = "event" event: str data: dict | None = None timestamp: float = 0.0 seq: int | None = None stateVersion: int | None = None class StartAccountParams(BaseModel): model_config = ConfigDict(extra="allow") channel_type: str = Field(..., min_length=1, max_length=50) account_id: str = Field(default="default", min_length=1, max_length=100) config: dict = Field(default_factory=dict) class StopAccountParams(BaseModel): model_config = ConfigDict(extra="allow") channel_type: str = Field(..., min_length=1, max_length=50) account_id: str = Field(default="default", min_length=1, max_length=100) force: bool = False class SendMessageParams(BaseModel): model_config = ConfigDict(extra="allow") channel_type: str = Field(..., min_length=1, max_length=50) account_id: str = Field(default="default", min_length=1, max_length=100) target_id: str = Field(..., min_length=1, max_length=200) text: str = Field(..., min_length=1, max_length=4096) msg_type: str = Field(default="text") media_url: str | None = None extra: dict = Field(default_factory=dict) class ProbeParams(BaseModel): model_config = ConfigDict(extra="allow") channel_type: str = Field(..., min_length=1, max_length=50) account_id: str = Field(default="default", min_length=1, max_length=100) class DiagnoseParams(BaseModel): model_config = ConfigDict(extra="allow") channel_type: str = Field(..., min_length=1, max_length=50) account_id: str = Field(default="default", min_length=1, max_length=100) class RepairParams(BaseModel): model_config = ConfigDict(extra="allow") channel_type: str = Field(..., min_length=1, max_length=50) step_id: str = Field(..., min_length=1, max_length=100) account_id: str = Field(default="default", min_length=1, max_length=100) def validate_params(schema_cls: type[BaseModel]): def decorator(func): @wraps(func) async def wrapper(request: RpcRequest, *args, **kwargs): try: validated = schema_cls(**(request.params or {})) except ValidationError as e: return RpcResponse( id=request.id, ok=False, error_code=GatewayErrorCode.INVALID_PARAMS, error_message=f"参数校验失败: {e}", ) request._validated_params = validated return await func(request, *args, **kwargs) return wrapper return decorator