ForcePilot/backend/package/yuxi/channels/services/context.py
Kris fb8bd90329 refactor(channel-services): 整理导入顺序并新增插件状态清理功能
1. 调整多个服务文件的导入顺序以符合规范
2. 新增Postgres插件状态存储实现,支持CRUD和过期清理
3. 在维护任务中添加插件过期状态自动清理逻辑
4. 新增统计收集器,支持请求/错误计数、响应时间统计和指标上报
2026-05-13 16:21:09 +08:00

102 lines
3.2 KiB
Python

from __future__ import annotations
import asyncio
from collections.abc import Awaitable, Callable
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from yuxi.channels.models import ChannelAccountSnapshot
@dataclass
class ChatAbortEntry:
task: Any = None
event: asyncio.Event = field(default_factory=asyncio.Event)
def abort(self) -> None:
self.event.set()
if self.task is not None and not self.task.done():
self.task.cancel()
def is_aborted(self) -> bool:
return self.event.is_set()
@dataclass
class ChatRunBuffer:
run_id: str = ""
_chunks: list[str] = field(default_factory=list)
_finished: bool = False
def append_chunk(self, chunk: str) -> None:
self._chunks.append(chunk)
def get_full_text(self) -> str:
return "".join(self._chunks)
def get_chunks(self) -> list[str]:
return list(self._chunks)
def is_finished(self) -> bool:
return self._finished
def mark_finished(self) -> None:
self._finished = True
def chunk_count(self) -> int:
return len(self._chunks)
@dataclass
class GatewayRequestContext:
runtime_config: dict = field(default_factory=dict)
runtime_snapshot: ChannelAccountSnapshot | None = None
start_channel: Callable[[str, dict], Awaitable[None]] | None = None
stop_channel: Callable[[str], Awaitable[None]] | None = None
mark_channel_logged_out: Callable[[str, str], Awaitable[None]] | None = None
broadcast_fn: Callable[[str, Any], Awaitable[None]] | None = None
node_send_to_session_fn: Callable[[str, str, Any], Awaitable[None]] | None = None
get_snapshot: Callable[[str], Awaitable[ChannelAccountSnapshot]] | None = None
dedupe: Any = None
chat_abort_controllers: dict[str, ChatAbortEntry] = field(default_factory=dict)
chat_run_buffers: dict[str, ChatRunBuffer] = field(default_factory=dict)
def get_runtime_config(self, key: str, default=None):
return self.runtime_config.get(key, default)
def set_runtime_config(self, key: str, value) -> None:
self.runtime_config[key] = value
def add_chat_run(self, run_id: str, entry: ChatAbortEntry, buffer: ChatRunBuffer | None = None) -> None:
self.chat_abort_controllers[run_id] = entry
if buffer is not None:
self.chat_run_buffers[run_id] = buffer
def remove_chat_run(self, run_id: str) -> None:
self.chat_abort_controllers.pop(run_id, None)
self.chat_run_buffers.pop(run_id, None)
def abort_chat_run(self, run_id: str) -> bool:
entry = self.chat_abort_controllers.get(run_id)
if entry is None:
return False
entry.abort()
return True
def get_chat_buffer(self, run_id: str) -> ChatRunBuffer | None:
return self.chat_run_buffers.get(run_id)
async def broadcast(self, event: str, payload: Any = None) -> None:
if self.broadcast_fn is not None:
await self.broadcast_fn(event, payload)
async def node_send_to_session(self, session_id: str, event: str, payload: Any = None) -> None:
if self.node_send_to_session_fn is not None:
await self.node_send_to_session_fn(session_id, event, payload)