ForcePilot/backend/package/yuxi/channels/services/context.py
Kris 7a972055b7 feat: 新增渠道服务基础框架与核心工具类
新增大量渠道适配器相关的协议、策略、工具类与基础设施代码,包括:
1.  多协议定义:认证、消息、配置、网关等核心接口
2.  策略模块:上下文、群聊、去重、防抖等业务策略
3.  工具集:重试、去重、文本分块、消息格式化等SDK工具
4.  基础设施:外部进程管理、事件广播、熔断机制等
5.  账户与管道系统:账户管理、消息处理管道实现
6.  运行时服务:状态收集、维护任务、日志等后台服务
2026-05-12 00:53:57 +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 Any, TYPE_CHECKING
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)