| import os | |
| import pprint | |
| import uuid | |
| from dotenv import load_dotenv | |
| from langchain_mcp_adapters.client import MultiServerMCPClient | |
| from langchain_openai import ChatOpenAI | |
| from langgraph.prebuilt import ToolNode | |
| from langgraph.graph import MessagesState, END, StateGraph | |
| from langchain_core.messages import HumanMessage | |
| from langgraph.checkpoint.memory import MemorySaver | |
| from pmcp.agents.executor import ExecutorAgent | |
| from pmcp.agents.trello_agent import TrelloAgent | |
| from pmcp.agents.github_agent import GithubAgent | |
| from pmcp.agents.planner import PlannerAgent | |
| from pmcp.nodes.human_interrupt_node import HumanInterruptNode | |
| from pmcp.nodes.human_resume_node import HumanResumeNode | |
| from pmcp.models.state import PlanningState | |
| load_dotenv() | |
| async def call_llm(llm_with_tools: ChatOpenAI, state: MessagesState): | |
| response = llm_with_tools.invoke(state["messages"]) | |
| return {"messages": [response]} | |
| async def main(): | |
| mcp_client_trello = MultiServerMCPClient( | |
| { | |
| "trello": { | |
| "command": "python", | |
| "args": [os.getenv("MCP_TRELLO_PATH")], | |
| "transport": "stdio", | |
| } | |
| } | |
| ) | |
| mcp_client_github = MultiServerMCPClient( | |
| { | |
| "github": { | |
| "command": "python", | |
| "args": [os.getenv("MCP_GITHUB_PATH")], | |
| "transport": "stdio", | |
| } | |
| } | |
| ) | |
| memory = MemorySaver() | |
| trello_tools = await mcp_client_trello.get_tools() | |
| github_tools = await mcp_client_github.get_tools() | |
| tool_node = ToolNode(github_tools + trello_tools) | |
| llm = ChatOpenAI( | |
| model="Qwen/Qwen2.5-32B-Instruct", | |
| temperature=0.0, | |
| api_key=os.getenv("NEBIUS_API_KEY"), | |
| base_url="https://api.studio.nebius.com/v1/", | |
| ) | |
| trello_agent = TrelloAgent( | |
| tools=trello_tools, | |
| llm=llm, | |
| ) | |
| github_agent = GithubAgent(llm=llm, tools=github_tools) | |
| planner_agent = PlannerAgent( | |
| llm=llm, | |
| ) | |
| executor_agent = ExecutorAgent(llm=llm) | |
| human_interrupt_node = HumanInterruptNode( | |
| llm=llm, | |
| ) | |
| human_resume_node = HumanResumeNode(llm=llm) | |
| graph = StateGraph(MessagesState) | |
| graph.add_node(planner_agent.agent.agent_name, planner_agent.acall_planner_agent) | |
| graph.add_node(trello_agent.agent.agent_name, trello_agent.acall_trello_agent) | |
| graph.add_node(github_agent.agent.agent_name, github_agent.acall_github_agent) | |
| graph.add_node(executor_agent.agent.agent_name, executor_agent.acall_executor_agent) | |
| graph.add_node("tool", tool_node) | |
| graph.add_node("human_interrupt", human_interrupt_node.call_human_interrupt_agent) | |
| graph.set_entry_point(planner_agent.agent.agent_name) | |
| def should_continue(state: PlanningState): | |
| last_message = state.messages[-1] | |
| if last_message.tool_calls: | |
| return "human_interrupt" | |
| return executor_agent.agent.agent_name | |
| def execute_agent(state: PlanningState): | |
| if state.current_step: | |
| return state.current_step.agent | |
| return END | |
| graph.add_conditional_edges(trello_agent.agent.agent_name, should_continue) | |
| graph.add_conditional_edges(github_agent.agent.agent_name, should_continue) | |
| graph.add_conditional_edges(executor_agent.agent.agent_name, execute_agent) | |
| graph.add_edge("tool", trello_agent.agent.agent_name) | |
| graph.add_edge("tool", github_agent.agent.agent_name) | |
| graph.add_edge(planner_agent.agent.agent_name, executor_agent.agent.agent_name) | |
| app = graph.compile(checkpointer=memory) | |
| app.get_graph(xray=True).draw_mermaid() | |
| user_input = input("user >") | |
| config = { | |
| "configurable": {"thread_id": f"{str(uuid.uuid4())}"}, | |
| "recursion_limit": 100, | |
| } | |
| is_message_command = False | |
| while user_input.lower() != "q": | |
| if is_message_command: | |
| app_input = human_resume_node.call_human_interrupt_agent( | |
| user_input | |
| ) | |
| is_message_command = False | |
| else: | |
| app_input = { | |
| "messages": [ | |
| HumanMessage(content=user_input), | |
| ] | |
| } | |
| async for res in app.astream( | |
| app_input, | |
| config=config, | |
| stream_mode="values", | |
| ): | |
| if "messages" in res: | |
| pprint.pprint(res["messages"][-1]) | |
| else: | |
| pprint.pprint(res["__interrupt__"][0]) | |
| is_message_command = True | |
| pprint.pprint("-------------------------------------") | |
| user_input = input("user >") | |
| if __name__ == "__main__": | |
| import asyncio | |
| asyncio.run(main()) | |