mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-04 23:17:20 +08:00
feat(agent): 敏感设置读取增加宿主二次确认 (#6283)
* feat(agent): require confirmation for secret settings * fix(agent): preserve history during secret confirmation
This commit is contained in:
@@ -3,15 +3,18 @@
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any
|
||||
|
||||
from langchain.agents.middleware import AgentMiddleware, ToolCallRequest
|
||||
from langchain.agents.middleware import AgentMiddleware, ToolCallRequest, hook_config
|
||||
from langchain_core.messages import AIMessage, ToolMessage
|
||||
|
||||
from app.agent.policy import (
|
||||
DEFAULT_TOOL_POLICY_ORCHESTRATOR,
|
||||
AgentToolPolicyOrchestrator,
|
||||
ToolPolicyContext,
|
||||
ToolOrigin,
|
||||
call_policy_hook,
|
||||
)
|
||||
from app.agent.tools.catalog import ToolCatalogSnapshot
|
||||
from app.agent.tools.impl.query_system_settings import QuerySystemSettingsTool
|
||||
|
||||
|
||||
class AgentPolicyMiddleware(AgentMiddleware):
|
||||
@@ -27,11 +30,68 @@ class AgentPolicyMiddleware(AgentMiddleware):
|
||||
context: ToolPolicyContext,
|
||||
orchestrator: AgentToolPolicyOrchestrator = DEFAULT_TOOL_POLICY_ORCHESTRATOR,
|
||||
catalog: ToolCatalogSnapshot | None = None,
|
||||
tools: list[Any] | None = None,
|
||||
) -> None:
|
||||
"""绑定宿主可信上下文和共享策略编排器。"""
|
||||
self.context = context
|
||||
self.orchestrator = orchestrator
|
||||
self.catalog = catalog
|
||||
self._tools = {
|
||||
tool.name: tool
|
||||
for tool in (tools or [])
|
||||
if getattr(tool, "name", None)
|
||||
}
|
||||
|
||||
@hook_config(can_jump_to=["end"])
|
||||
async def aafter_model(self, state: dict[str, Any], runtime: Any) -> Any:
|
||||
"""在 ToolNode 前暂停需要用户确认的敏感设置读取。"""
|
||||
messages = state.get("messages") or []
|
||||
if not messages or not isinstance(messages[-1], AIMessage):
|
||||
return None
|
||||
|
||||
tool_calls = messages[-1].tool_calls or []
|
||||
sensitive_call = None
|
||||
sensitive_tool = None
|
||||
for tool_call in tool_calls:
|
||||
arguments = tool_call.get("args")
|
||||
tool = self._tools.get(tool_call.get("name"))
|
||||
if (
|
||||
isinstance(tool, QuerySystemSettingsTool)
|
||||
and isinstance(arguments, dict)
|
||||
and arguments.get("show_secrets") is True
|
||||
):
|
||||
sensitive_call = tool_call
|
||||
sensitive_tool = tool
|
||||
break
|
||||
if sensitive_call is None or sensitive_tool is None:
|
||||
return None
|
||||
|
||||
confirmation_handler = (
|
||||
self.context.agent_context.get("secret_confirmation_handler")
|
||||
if self.context.origin is ToolOrigin.AGENT_INTERACTIVE
|
||||
else None
|
||||
)
|
||||
if not callable(confirmation_handler):
|
||||
confirmation_message = "当前入口不支持敏感设置确认,未执行任何工具。"
|
||||
else:
|
||||
confirmation_message = await confirmation_handler(
|
||||
sensitive_tool,
|
||||
sensitive_call.get("args") or {},
|
||||
)
|
||||
|
||||
paused_messages = [
|
||||
ToolMessage(
|
||||
content=(
|
||||
"本轮工具调用已暂停,未执行任何操作;"
|
||||
"请等待用户确认或取消敏感设置读取。"
|
||||
),
|
||||
tool_call_id=str(tool_call.get("id") or ""),
|
||||
name=str(tool_call.get("name") or "unknown"),
|
||||
)
|
||||
for tool_call in tool_calls
|
||||
]
|
||||
paused_messages.append(AIMessage(content=confirmation_message))
|
||||
return {"messages": paused_messages, "jump_to": "end"}
|
||||
|
||||
async def awrap_tool_call(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user