ForcePilot/backend/package/yuxi/channels/adapters/__init__.py
Kris bb1934023e refactor: 完成会话持久化与事务机制重构,清理冗余代码
本次提交是一次大型架构重构,核心变更包括:
1. 调整持久化适配器为无状态实现,通过session_factory按需获取会话
2. 重构事务共享与透传机制,统一使用_session_scope管理会话生命周期
3. 移除OutboxEntry聚合根内的版本自增逻辑,由持久化层统一管理
4. 优化微信插件会话类型枚举与配置对齐
5. 简化出站管道阶段依赖注入与流程逻辑
6. 删除健康检查自动释放会话的冗余代码
7. 重构多个定时任务处理器,移除显式会话工厂创建逻辑
8. 修复会话延迟加载异常问题,新增时区转换工具函数
2026-07-10 04:10:33 +08:00

185 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""渠道被驱动适配器包。
聚合导出 18 个被驱动适配器实现与 ``DrivenAdapters`` 聚合 dataclass
供框架层装配使用。
装配策略分两类§4.12
- **请求级 / 无状态适配器**14 个)经 ``create_driven_adapters(db,
session_factory, outbox_config, redis_client, arq_pool, execution_port)``
factory 构造:
``ChannelPersistenceAdapter`` 为无状态适配器,注入 ``session_factory``
``Callable[[], AsyncSession]``),通过 ``_session_scope(tx)`` 按需获取
session``tx`` 非空时复用应用层主事务 session``tx`` 为 ``None`` 时自主
创建并提交);``ConversationAdapter`` / ``SqlAlchemyTransactionAdapter``
为请求级事务边界适配器,共享同一 ``db`` 会话以保证事务一致性;无状态适配器
Logger / Tracer使用模块级全局实例``identity_resolver`` 默认为
``None``,由插件注册时注入。
- **独立装配路径适配器**4 个)不经过工厂,不进入 ``DrivenAdapters`` 聚合:
``ContentReviewRepositoryAdapter`` / ``DefaultContentModerationAdapter`` 是
应用级单例(在 ``factory.create_host_bootstrap`` 构造并注册 DI
``InmemoryRealtimeMetricsAdapter`` 是进程内单例(在
``create_channel_use_cases`` 装配);``MessageOpsPortAdapter`` 是请求级 +
插件依赖(持有插件适配器注册表,在 ``create_channel_use_cases`` 装配)。
注:``__init__.py`` 的"导出"仅指 ``__all__`` 与 import 语句集中声明,不等于
"纳入工厂/聚合"。``RedisConfigAdapter`` / ``RedisCacheAdapter`` 接受
``redis_client````ARQQueueAdapter`` 接受 ``arq_pool````AgentRunAdapter``
接受 ``execution_port``构造注入INV-5 / ADP-001 / ADP-005 / ADP-022
"""
from __future__ import annotations
from collections.abc import Callable, Mapping
from arq import ArqRedis
from redis.asyncio import Redis
from sqlalchemy.ext.asyncio import AsyncSession
from yuxi.channels.contract.dtos.config import ConfigScope
from yuxi.channels.contract.dtos.outbox import OutboxConfig
from yuxi.channels.contract.ports.driven.agent_run_execution_port import (
AgentRunExecutionPort,
)
from yuxi.channels.contract.ports.driven.aggregate import DrivenAdapters
# 被驱动适配器(请求级 / 无状态14 个,经 create_driven_adapters 工厂装配)
from .agent_access_config_adapter import AgentAccessConfigAdapter
from .agent_run_adapter import AgentRunAdapter
from .agent_run_execution_adapter import AgentRunExecutionAdapter
from .arq_queue_adapter import ARQQueueAdapter
from .channel_persistence_adapter import ChannelPersistenceAdapter
# 独立装配路径适配器5 个,不经过工厂,不进入 DrivenAdapters 聚合)
from .content_review_repository_adapter import ContentReviewRepositoryAdapter
from .conversation_adapter import ConversationAdapter
from .default_content_moderation_adapter import DefaultContentModerationAdapter
from .identity_resolver_adapter import IdentityResolverAdapter
from .inmemory_realtime_metrics_adapter import InmemoryRealtimeMetricsAdapter
from .inmemory_tracer_adapter import InMemoryTracerAdapter
from .masking_adapter import MaskingAdapter
from .message_ops_port_adapter import MessageOpsPortAdapter
from .redis_cache_adapter import RedisCacheAdapter
from .redis_config_adapter import RedisConfigAdapter
from .redis_realtime_metrics_adapter import RedisRealtimeMetricsAdapter
from .service_account_adapter import ServiceAccountAdapter
from .sqlalchemy_transaction_adapter import SqlAlchemyTransactionAdapter
from .structured_logger_adapter import StructuredLoggerAdapter
# 无状态适配器全局实例Masking / Logger / Tracer跨请求复用避免重复构造。
# Masking 无状态无依赖最先构造Logger 注入 Masking 以便日志 kwargs 自动脱敏;
# Tracer 注入 Logger 以便在追踪故障降级时记录日志(避免静默吞异常)。
_masking_adapter = MaskingAdapter()
_logger_adapter = StructuredLoggerAdapter(masking_port=_masking_adapter)
_tracer_adapter = InMemoryTracerAdapter(_logger_adapter)
def create_driven_adapters(
db: AsyncSession,
session_factory: Callable[[], AsyncSession],
outbox_config: OutboxConfig,
redis_client: Redis,
arq_pool: ArqRedis,
execution_port: AgentRunExecutionPort,
key_to_scope_map: Mapping[str, ConfigScope] | None = None,
declared_keys: set[str] | None = None,
) -> DrivenAdapters:
"""创建被驱动适配器聚合实例。
``ChannelPersistenceAdapter`` 为无状态适配器,注入 ``session_factory``
``Callable[[], AsyncSession]``),通过 ``_session_scope(tx)`` 按需获取
session``tx`` 非空时复用应用层主事务 sessionC-I1 透传),``tx`` 为
``None`` 时通过 ``session_factory()`` 创建独立 session 并自主提交。
``ConversationAdapter`` / ``SqlAlchemyTransactionAdapter`` 为请求级事务
边界适配器,共享同一 ``db`` 会话以保证事务一致性;无状态适配器
Logger / Tracer使用模块级全局实例其余适配器按需构造。
``RedisConfigAdapter`` / ``RedisCacheAdapter`` 接受 ``redis_client`` 执行
配置读写与缓存操作;``ARQQueueAdapter`` 接受 ``arq_pool`` 入队;
``AgentRunAdapter`` 接受 ``execution_port`` 委托 Agent 运行技术操作
构造注入INV-5 / ADP-001 / ADP-005 / ADP-022。``identity_resolver``
默认为 ``None``,由插件注册时注入。
Args:
db: SQLAlchemy 异步会话请求级由框架层注入conversation /
transaction 适配器共享此会话以保证事务一致性。
session_factory: ``Callable[[], AsyncSession]``,注入到
``ChannelPersistenceAdapter`` 供 ``_session_scope(None)`` 创建
独立 session无状态适配器不持有请求级 ``db``)。
outbox_config: 发件箱配置,注入到 ``ChannelPersistenceAdapter`` 替代
直接读取全局 ``app_config``§6.1 应用服务层禁止依赖具体技术适配器)。
redis_client: ``redis.asyncio.Redis`` 客户端实例,注入到
``RedisConfigAdapter`` 与 ``RedisCacheAdapter``构造注入INV-5
arq_pool: ``arq.ArqRedis`` 连接池实例,注入到 ``ARQQueueAdapter``
用于入队构造注入ADP-005
execution_port: ``AgentRunExecutionPort`` 实例,注入到 ``AgentRunAdapter``
委托 Agent 运行技术操作构造注入ADP-001
key_to_scope_map: F-02 配置作用域映射(``key → ConfigScope``
注入到 ``RedisConfigAdapter`` 供 ``_build_key`` 校验 key→scope
一致性。为 ``None`` 时不校验(向后兼容)。
declared_keys: §13.1 已声明的配置键集合,注入到
``RedisConfigAdapter`` 供 ``_assertDeclared`` 校验键合法性。
为 ``None`` 时回退到模块级 ``_DECLARED_KEYS``(仅含
CONFIG_SCHEMA 键,向后兼容)。
Returns:
DrivenAdapters 聚合实例。
"""
config = RedisConfigAdapter(
redis_client=redis_client,
logger=_logger_adapter,
key_to_scope_map=key_to_scope_map,
declared_keys=declared_keys,
)
persistence = ChannelPersistenceAdapter(session_factory, outbox_config=outbox_config, logger=_logger_adapter)
# 服务账号适配器单例:仅依赖 logger共享注入到 AgentRunAdapter / 管道阶段 / AccountHandler
service_account = ServiceAccountAdapter(logger=_logger_adapter)
# Agent 渠道访问配置适配器单例:仅依赖 logger注入到 agent-run-enqueue 阶段
agent_access_config = AgentAccessConfigAdapter(logger=_logger_adapter)
return DrivenAdapters(
persistence=persistence,
conversation=ConversationAdapter(db, logger=_logger_adapter),
agent_run=AgentRunAdapter(
execution_port=execution_port,
logger=_logger_adapter,
service_account_port=service_account,
),
queue=ARQQueueAdapter(arq_pool=arq_pool, logger=_logger_adapter),
config=config,
cache=RedisCacheAdapter(redis_client=redis_client, logger=_logger_adapter),
logger=_logger_adapter,
tracer=_tracer_adapter,
masking=_masking_adapter,
transaction=SqlAlchemyTransactionAdapter(db),
identity_resolver=None,
service_account=service_account,
agent_access_config=agent_access_config,
)
__all__ = [
# 聚合与工厂
"DrivenAdapters",
"create_driven_adapters",
# 被驱动适配器(请求级 / 无状态14 个,经 create_driven_adapters 工厂装配)
"ChannelPersistenceAdapter",
"ConversationAdapter",
"AgentRunAdapter",
"AgentRunExecutionAdapter",
"AgentAccessConfigAdapter",
"ARQQueueAdapter",
"MaskingAdapter",
"RedisConfigAdapter",
"RedisCacheAdapter",
"ServiceAccountAdapter",
"StructuredLoggerAdapter",
"InMemoryTracerAdapter",
"IdentityResolverAdapter",
"SqlAlchemyTransactionAdapter",
# 独立装配路径适配器5 个,不经过工厂,不进入 DrivenAdapters 聚合)
"ContentReviewRepositoryAdapter",
"DefaultContentModerationAdapter",
"InmemoryRealtimeMetricsAdapter",
"RedisRealtimeMetricsAdapter",
"MessageOpsPortAdapter",
]