feat(plugin): add pre-reset plugin data event (#5957)

This commit is contained in:
InfinityPacer
2026-06-17 06:34:15 +08:00
committed by GitHub
parent d5bac81881
commit 93056ed1ff
4 changed files with 75 additions and 1 deletions
+9 -1
View File
@@ -12,6 +12,7 @@ from starlette.responses import StreamingResponse
from app import schemas from app import schemas
from app.command import Command from app.command import Command
from app.core.config import settings from app.core.config import settings
from app.core.event import eventmanager
from app.core.plugin import PluginManager from app.core.plugin import PluginManager
from app.core.security import ( from app.core.security import (
resource_token_cookie, resource_token_cookie,
@@ -30,7 +31,8 @@ from app.helper.server import MoviePilotServerHelper
from app.helper.plugin import PluginHelper from app.helper.plugin import PluginHelper
from app.log import logger from app.log import logger
from app.scheduler import Scheduler from app.scheduler import Scheduler
from app.schemas.types import SystemConfigKey from app.schemas.event import PluginDataResetEventData
from app.schemas.types import ChainEventType, SystemConfigKey
PROTECTED_ROUTES = {"/api/v1/openapi.json", "/docs", "/docs/oauth2-redirect", "/redoc"} PROTECTED_ROUTES = {"/api/v1/openapi.json", "/docs", "/docs/oauth2-redirect", "/redoc"}
PLUGIN_PREFIX = f"{settings.API_V1_STR}/plugin" PLUGIN_PREFIX = f"{settings.API_V1_STR}/plugin"
@@ -530,6 +532,12 @@ def reset_plugin(
根据插件ID重置插件配置及数据 根据插件ID重置插件配置及数据
""" """
plugin_manager = PluginManager() plugin_manager = PluginManager()
eventmanager.send_event(
ChainEventType.PluginDataReset,
PluginDataResetEventData(plugin_id=plugin_id, reset_config=True, reset_data=True),
)
# 事件处理器需要运行中插件完成补偿;补偿后先停止插件,避免删除数据时仍有任务读写旧状态。
plugin_manager.stop(plugin_id)
# 删除配置 # 删除配置
plugin_manager.delete_plugin_config(plugin_id) plugin_manager.delete_plugin_config(plugin_id)
# 删除插件所有数据 # 删除插件所有数据
+13
View File
@@ -64,6 +64,19 @@ class ChainEventData(BaseEventData):
pass pass
class PluginDataResetEventData(ChainEventData):
"""
PluginDataReset 事件的数据模型。
在主程序清空某个插件配置或插件数据前发出,插件可在数据被删除前完成
自有状态补偿。事件处理器只应处理 plugin_id 与自身匹配的事件。
"""
plugin_id: str = Field(..., description="即将被重置的插件 ID")
reset_config: bool = Field(default=False, description="是否即将重置插件配置")
reset_data: bool = Field(default=False, description="是否即将重置插件数据")
class AgentLLMProviderEventData(ChainEventData): class AgentLLMProviderEventData(ChainEventData):
""" """
Agent LLM 供应商选择事件数据。 Agent LLM 供应商选择事件数据。
+2
View File
@@ -163,6 +163,8 @@ EVENT_TYPE_NAMES = {
# 同步链式事件 # 同步链式事件
class ChainEventType(Enum): class ChainEventType(Enum):
# 插件数据重置前
PluginDataReset = "plugin.data.reset"
# 名称识别 # 名称识别
NameRecognize = "name.recognize" NameRecognize = "name.recognize"
# 认证验证 # 认证验证
+51
View File
@@ -3,7 +3,10 @@ from unittest.mock import ANY, AsyncMock, MagicMock, patch
from app import schemas from app import schemas
from app.api.endpoints.plugin import plugin_history from app.api.endpoints.plugin import plugin_history
from app.api.endpoints.plugin import reset_plugin
from app.api.endpoints.system import sync_plugin_market_from_wiki from app.api.endpoints.system import sync_plugin_market_from_wiki
from app.schemas.event import PluginDataResetEventData
from app.schemas.types import ChainEventType
def test_plugin_history_merges_remote_metadata(): def test_plugin_history_merges_remote_metadata():
@@ -101,3 +104,51 @@ def test_sync_plugin_market_from_wiki_merges_and_deduplicates_repos():
"https://github.com/local/existing,https://github.com/wiki/new-repo", "https://github.com/local/existing,https://github.com/wiki/new-repo",
) )
send_event.assert_awaited_once() send_event.assert_awaited_once()
def test_reset_plugin_sends_pre_reset_chain_event_before_deleting_data():
"""
插件重置会先触发同步链式事件,让插件在数据被清空前完成自有状态补偿。
"""
plugin_manager = MagicMock()
calls = []
def delete_config(plugin_id):
calls.append(("delete_config", plugin_id))
return True
def delete_data(plugin_id):
calls.append(("delete_data", plugin_id))
return True
def stop_plugin(plugin_id):
calls.append(("stop", plugin_id))
return True
plugin_manager.stop.side_effect = stop_plugin
plugin_manager.delete_plugin_config.side_effect = delete_config
plugin_manager.delete_plugin_data.side_effect = delete_data
with (
patch("app.api.endpoints.plugin.PluginManager", return_value=plugin_manager),
patch("app.api.endpoints.plugin.eventmanager") as eventmanager,
patch("app.api.endpoints.plugin.reload_plugin") as reload_plugin_mock,
):
eventmanager.send_event.side_effect = lambda etype, data: calls.append(("event", etype, data))
result = reset_plugin("SubscribeAssistantEnhanced", None)
assert result.success is True
assert len(calls) == 4
event_call = calls[0]
assert event_call[0] == "event"
assert event_call[1] is ChainEventType.PluginDataReset
assert isinstance(event_call[2], PluginDataResetEventData)
assert event_call[2].plugin_id == "SubscribeAssistantEnhanced"
assert event_call[2].reset_config is True
assert event_call[2].reset_data is True
assert calls[1:] == [
("stop", "SubscribeAssistantEnhanced"),
("delete_config", "SubscribeAssistantEnhanced"),
("delete_data", "SubscribeAssistantEnhanced"),
]
reload_plugin_mock.assert_called_once_with("SubscribeAssistantEnhanced")