- 新增 pam_deploy_graph 包,包含 Agent runtime、ActionRouter、脚本/MCP/fake runner - 支持 hybrid_node_mcp 策略:PAM_HOME 走脚本 action,PAM_NODE 走 MCP - 支持 script_only 离线策略,全部 action 走现有脚本 action - 新增 LLM structured output 骨架和规则 fallback,支持意图识别、参数抽取、计划生成 - 新增 LangGraph StateGraph 工厂和 MCP client adapter - 新增 CLI:preview、analyze、run-global、run-deploy - 增加 fake 完整部署流程、单 IP 失败待回滚确认状态和报告输出 - 增加单元测试覆盖路由、parser、runner、Skill 加载、LLM 输出、MCP adapter 和 LangGraph 图 - 更新 README,记录当前代码骨架、进度、使用方式和下一步计划
68 lines
2.3 KiB
Python
68 lines
2.3 KiB
Python
"""LangGraph integration for the PAM deploy Agent."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Literal
|
|
|
|
from .agent import PamDeployAgent
|
|
|
|
GraphFlow = Literal["global", "deploy"]
|
|
|
|
|
|
def build_langgraph(agent: PamDeployAgent | None = None, flow: GraphFlow = "deploy"):
|
|
try:
|
|
from langgraph.graph import END, START, StateGraph
|
|
except ImportError as exc: # pragma: no cover - depends on optional package
|
|
raise RuntimeError(
|
|
"langgraph is not installed. Install project dependencies with "
|
|
"`pip install -e .`."
|
|
) from exc
|
|
|
|
runtime = agent or PamDeployAgent()
|
|
|
|
def create_state_node(state: dict[str, Any]) -> dict[str, Any]:
|
|
agent_state = runtime.create_state(
|
|
params=state["params"],
|
|
execution_strategy=state.get("execution_strategy", "hybrid_node_mcp"),
|
|
run_id=state.get("run_id"),
|
|
script_entry=state.get("script_entry"),
|
|
config_path=state.get("config_path"),
|
|
trace_file_path=state.get("trace_file_path"),
|
|
target_ips=state.get("target_ips"),
|
|
)
|
|
return {"agent_state": agent_state}
|
|
|
|
def run_global_node(state: dict[str, Any]) -> dict[str, Any]:
|
|
agent_state = runtime.run_global_flow(state["agent_state"])
|
|
return {"agent_state": agent_state}
|
|
|
|
def run_ip_node(state: dict[str, Any]) -> dict[str, Any]:
|
|
agent_state = runtime.run_ip_flow(state["agent_state"])
|
|
return {"agent_state": agent_state}
|
|
|
|
def report_node(state: dict[str, Any]) -> dict[str, Any]:
|
|
return {"report": runtime.render_report(state["agent_state"])}
|
|
|
|
graph = StateGraph(dict)
|
|
graph.add_node("create_state", create_state_node)
|
|
graph.add_node("run_global", run_global_node)
|
|
graph.add_node("run_ip", run_ip_node)
|
|
graph.add_node("report", report_node)
|
|
|
|
graph.add_edge(START, "create_state")
|
|
graph.add_edge("create_state", "run_global")
|
|
if flow == "global":
|
|
graph.add_edge("run_global", END)
|
|
else:
|
|
graph.add_edge("run_global", "run_ip")
|
|
graph.add_edge("run_ip", "report")
|
|
graph.add_edge("report", END)
|
|
return graph.compile()
|
|
|
|
|
|
def build_graph_or_none(agent: PamDeployAgent | None = None, flow: GraphFlow = "deploy"):
|
|
try:
|
|
return build_langgraph(agent=agent, flow=flow)
|
|
except RuntimeError:
|
|
return None
|