fix(ci): stabilize event and web agent checks

This commit is contained in:
jxxghp
2026-08-26 07:04:04 +08:00
parent df6f86ec3d
commit 33a367ebf9
4 changed files with 41 additions and 24 deletions
+9 -4
View File
@@ -143,13 +143,13 @@ _SNAPSHOT_EVENTS = {
ChainEventType.SubscribeCompletionCheck, ChainEventType.SubscribeCompletionCheck,
} }
_INPUT_MODELS = { _INPUT_MODELS: dict[EventType | ChainEventType, type[BaseModel]] = {
ChainEventType.ResourceSelection: event_schemas.ResourceSelectionInputContractData, ChainEventType.ResourceSelection: event_schemas.ResourceSelectionInputContractData,
ChainEventType.ResourceDownload: event_schemas.ResourceDownloadInputContractData, ChainEventType.ResourceDownload: event_schemas.ResourceDownloadInputContractData,
ChainEventType.SubscribeCompletionCheck: event_schemas.SubscribeCompletionCheckInputContractData, ChainEventType.SubscribeCompletionCheck: event_schemas.SubscribeCompletionCheckInputContractData,
} }
_OUTPUT_MODELS = { _OUTPUT_MODELS: dict[EventType | ChainEventType, type[BaseModel]] = {
ChainEventType.ResourceSelection: event_schemas.ResourceSelectionOutputContractData, ChainEventType.ResourceSelection: event_schemas.ResourceSelectionOutputContractData,
ChainEventType.ResourceDownload: event_schemas.ResourceDownloadOutputContractData, ChainEventType.ResourceDownload: event_schemas.ResourceDownloadOutputContractData,
ChainEventType.SubscribeCompletionCheck: event_schemas.SubscribeCompletionCheckOutputContractData, ChainEventType.SubscribeCompletionCheck: event_schemas.SubscribeCompletionCheckOutputContractData,
@@ -213,9 +213,14 @@ def _build_contract(event_type: EventType | ChainEventType) -> EventContract:
) )
EVENT_CONTRACTS = { _ALL_EVENT_TYPES: tuple[EventType | ChainEventType, ...] = (
*tuple(EventType),
*tuple(ChainEventType),
)
EVENT_CONTRACTS: dict[EventType | ChainEventType, EventContract] = {
event_type: _build_contract(event_type) event_type: _build_contract(event_type)
for event_type in (*tuple(EventType), *tuple(ChainEventType)) for event_type in _ALL_EVENT_TYPES
} }
+19 -6
View File
@@ -357,6 +357,8 @@ class EventManager(metaclass=Singleton):
return False return False
try: try:
owner = object() owner = object()
completion: concurrent.futures.Future[Any] = concurrent.futures.Future()
self.__sync_handles[owner] = completion
def _tracked_sync() -> Any: def _tracked_sync() -> Any:
"""在同步 handler 调用栈中发布当前事件 owner。""" """在同步 handler 调用栈中发布当前事件 owner。"""
@@ -368,14 +370,25 @@ class EventManager(metaclass=Singleton):
handle = self.__executor.submit(_tracked_sync) handle = self.__executor.submit(_tracked_sync)
except RuntimeError: except RuntimeError:
self.__sync_handles.pop(owner, None)
logger.warning("同步事件处理器无法投递,线程池已停止") logger.warning("同步事件处理器无法投递,线程池已停止")
return False return False
self.__sync_handles[owner] = handle
handle.add_done_callback( def _complete_sync_handle(
lambda _completed, current_owner=owner: ( completed: concurrent.futures.Future[Any],
self.__remove_sync_handle(current_owner) ) -> None:
) """把真实线程句柄的结果转移到已登记的结算句柄。"""
) if completed.cancelled():
completion.cancel()
else:
error = completed.exception()
if error is not None:
completion.set_exception(error)
else:
completion.set_result(completed.result())
self.__remove_sync_handle(owner)
handle.add_done_callback(_complete_sync_handle)
return True return True
def __remove_sync_handle(self, owner: object) -> None: def __remove_sync_handle(self, owner: object) -> None:
+3 -3
View File
@@ -63,14 +63,14 @@ class ContextSnapshot(_ContextSnapshotBase):
arbitrary_types_allowed=True, arbitrary_types_allowed=True,
) )
@model_validator(mode="before") @model_validator(mode="before") # type: ignore[misc]
@classmethod @classmethod
def coerce_runtime_context(cls, value: Any) -> Any: def coerce_runtime_context(cls, value: Any) -> Any:
"""允许旧 Context/dataclass 进入校验,同时保持原对象继续投递。""" """允许旧 Context/dataclass 进入校验,同时保持原对象继续投递。"""
return _coerce_event_snapshot(value) return _coerce_event_snapshot(value)
class FileContextSnapshot(BaseModel): class FileContextSnapshot(BaseModel): # type: ignore[misc]
"""音乐批次中单个文件的元数据上下文快照。""" """音乐批次中单个文件的元数据上下文快照。"""
path: str path: str
@@ -79,7 +79,7 @@ class FileContextSnapshot(BaseModel):
model_config = ConfigDict(extra="allow", from_attributes=True) model_config = ConfigDict(extra="allow", from_attributes=True)
@model_validator(mode="before") @model_validator(mode="before") # type: ignore[misc]
@classmethod @classmethod
def coerce_runtime_context(cls, value: Any) -> Any: def coerce_runtime_context(cls, value: Any) -> Any:
"""把旧文件上下文对象转换成可验证的字典。""" """把旧文件上下文对象转换成可验证的字典。"""
+10 -11
View File
@@ -11,30 +11,26 @@ from app import schemas
from app.agent.contracts import ReplyMode from app.agent.contracts import ReplyMode
from app.agent.orchestrator import agent_manager from app.agent.orchestrator import agent_manager
from app.api.endpoints.agent import ( from app.api.endpoints.agent import (
_WebAgentEventPublisher,
_WEB_AGENT_FILE_REGISTRY, _WEB_AGENT_FILE_REGISTRY,
_apply_web_agent_display_event, _apply_web_agent_display_event,
_build_web_agent_command_items,
_build_web_agent_display_message_from_events,
_build_web_agent_input_attachments, _build_web_agent_input_attachments,
_build_web_agent_message_events, _build_web_agent_message_events,
_build_web_agent_command_items,
_build_web_agent_session_id, _build_web_agent_session_id,
_build_web_agent_session_id_async, _build_web_agent_session_id_async,
_build_web_agent_traditional_callback_payload, _build_web_agent_traditional_callback_payload,
_build_web_agent_display_message_from_events,
_collect_web_agent_traditional_events, _collect_web_agent_traditional_events,
_get_web_agent_type, _get_web_agent_type,
_has_web_agent_traditional_interaction, _has_web_agent_traditional_interaction,
_prepare_web_agent_audio_attachment_path_async, _prepare_web_agent_audio_attachment_path_async,
_resolve_web_agent_audio_refs, _resolve_web_agent_audio_refs,
_transcribe_web_agent_audio_files,
web_agent_stream,
_resolve_web_agent_choice_payload, _resolve_web_agent_choice_payload,
_split_web_agent_output, _split_web_agent_output,
_transcribe_web_agent_audio_files,
_WebAgentEventPublisher,
web_agent_stream,
) )
from app.runtime.events import Event
from app.db.oper.agentchat import AgentChatOper
from app.db.models.agentchat import AgentChat
from app.application.messaging.chat import AgentChatService, configure_agent_chat_service
from app.application.messaging.agent import ( from app.application.messaging.agent import (
AgentInteractionOption, AgentInteractionOption,
agent_interaction_manager, agent_interaction_manager,
@@ -45,10 +41,13 @@ from app.application.messaging.agent import (
extract_web_agent_message_from_event_data, extract_web_agent_message_from_event_data,
wait_web_agent_background_tasks, wait_web_agent_background_tasks,
) )
from app.application.messaging.chat import AgentChatService, configure_agent_chat_service
from app.application.messaging.skill import skill_interaction_manager from app.application.messaging.skill import skill_interaction_manager
from app.chain.message import MessageChain from app.chain.message import MessageChain
from app.db.oper.agentchat import AgentChatOper
from app.runtime.events import Event
from app.schemas.notification import ChannelCapability, ChannelCapabilityManager from app.schemas.notification import ChannelCapability, ChannelCapabilityManager
from app.schemas.types import EventType, NotificationChannel, MessageType from app.schemas.types import EventType, MessageType, NotificationChannel
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@@ -764,7 +763,7 @@ def test_prepare_web_agent_audio_attachment_async_cancellation_reaps_process(tmp
conversion_task = asyncio.create_task( conversion_task = asyncio.create_task(
_prepare_web_agent_audio_attachment_path_async(str(source_path)) _prepare_web_agent_audio_attachment_path_async(str(source_path))
) )
await asyncio.wait_for(started.wait(), timeout=1) await asyncio.wait_for(started.wait(), timeout=5)
conversion_task.cancel() conversion_task.cancel()
with pytest.raises(asyncio.CancelledError): with pytest.raises(asyncio.CancelledError):
await conversion_task await conversion_task