mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-28 19:47:41 +08:00
fix(ci): stabilize event and web agent checks
This commit is contained in:
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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:
|
||||||
"""把旧文件上下文对象转换成可验证的字典。"""
|
"""把旧文件上下文对象转换成可验证的字典。"""
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user