MatteoMass commited on
Commit
3b78b4e
·
1 Parent(s): 027537a

add new gradio app

Browse files
Files changed (3) hide show
  1. app.py +10 -3
  2. app_new.py +150 -0
  3. mcpc_graph.py +106 -0
app.py CHANGED
@@ -1,8 +1,15 @@
 
1
  import os
 
2
  import asyncio
3
  import gradio as gr
4
 
5
-
 
 
 
 
 
6
 
7
 
8
 
@@ -114,7 +121,7 @@ with gr.Blocks(theme=theme, title="PMCP - Agentic Project Management") as demo:
114
  if trello_token:
115
  os.environ["TRELLO_TOKEN"] = trello_token
116
  if hf_token:
117
- os.environ["HF_TOKEN"] = hf_token
118
 
119
  # Create a message showing which variables were set
120
  set_vars = []
@@ -122,7 +129,7 @@ with gr.Blocks(theme=theme, title="PMCP - Agentic Project Management") as demo:
122
  if github_token: set_vars.append("GITHUB_TOKEN")
123
  if trello_api: set_vars.append("TRELLO_API_KEY")
124
  if trello_token: set_vars.append("TRELLO_TOKEN")
125
- if hf_token: set_vars.append("HF_TOKEN")
126
 
127
  if set_vars:
128
  return f"✅ Set environment variables: {', '.join(set_vars)}"
 
1
+ import functools
2
  import os
3
+ import uuid
4
  import asyncio
5
  import gradio as gr
6
 
7
+ from langchain_mcp_adapters.client import MultiServerMCPClient
8
+ from langchain_openai import ChatOpenAI
9
+ from langgraph.prebuilt import ToolNode
10
+ from langgraph.graph import MessagesState, END, StateGraph
11
+ from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
12
+ from langgraph.checkpoint.memory import MemorySaver
13
 
14
 
15
 
 
121
  if trello_token:
122
  os.environ["TRELLO_TOKEN"] = trello_token
123
  if hf_token:
124
+ os.environ["NEBIUS_API_KEY"] = hf_token
125
 
126
  # Create a message showing which variables were set
127
  set_vars = []
 
129
  if github_token: set_vars.append("GITHUB_TOKEN")
130
  if trello_api: set_vars.append("TRELLO_API_KEY")
131
  if trello_token: set_vars.append("TRELLO_TOKEN")
132
+ if hf_token: set_vars.append("NEBIUS_API_KEY")
133
 
134
  if set_vars:
135
  return f"✅ Set environment variables: {', '.join(set_vars)}"
app_new.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ import asyncio
3
+ import os
4
+ import gradio as gr
5
+
6
+ from langchain_core.messages import HumanMessage, AIMessage
7
+
8
+ # Assuming mcpc_graph.py and its setup_graph function are in the same directory.
9
+ from mcpc_graph import setup_graph
10
+
11
+
12
+ async def chat_logic(message, history, session_state, github_repo, github_token, trello_api, trello_token, hf_token):
13
+ """
14
+ Handles the main chat logic, including environment setup and streaming responses.
15
+
16
+ Args:
17
+ message (str): The user's input message.
18
+ history (list): The chat history managed by Gradio.
19
+ session_state (dict): A dictionary to maintain state across calls for a session.
20
+ github_repo (str): The GitHub repository (username/repo).
21
+ github_token (str): The GitHub personal access token.
22
+ trello_api (str): The Trello API key.
23
+ trello_token (str): The Trello API token.
24
+ hf_token (str): The Hugging Face API token.
25
+
26
+ Yields:
27
+ str: The bot's streaming response or an interruption message.
28
+ """
29
+ # Retrieve the initialized graph and interrupt handler from the session state.
30
+ app = session_state.get("app")
31
+ human_resume_node = session_state.get("human_resume_node")
32
+
33
+ # If the graph is not initialized, this is the first message of the session.
34
+ # We configure the environment and set up the graph.
35
+ if app is None:
36
+ # Check if all required fields have been filled out.
37
+ if not all([github_repo, github_token, trello_api, trello_token, hf_token]):
38
+ yield "Error: Please provide all API keys and the GitHub repository in the 'API Configuration' section before starting the chat."
39
+ return
40
+
41
+ # Set environment variables for the current process.
42
+ os.environ["GITHUB_REPO"] = github_repo
43
+ os.environ["GITHUB_TOKEN"] = github_token
44
+ os.environ["TRELLO_API_KEY"] = trello_api
45
+ os.environ["TRELLO_API_TOKEN"] = trello_token
46
+ os.environ["HUGGINGFACE_API_KEY"] = hf_token
47
+
48
+ # Asynchronously initialize the graph and store it in the session state
49
+ # to reuse it for subsequent messages in the same session.
50
+ app, human_resume_node = await setup_graph()
51
+ session_state["app"] = app
52
+ session_state["human_resume_node"] = human_resume_node
53
+
54
+ # Ensure a unique thread_id for the conversation.
55
+ thread_id = session_state.get("thread_id")
56
+ if not thread_id:
57
+ thread_id = str(uuid.uuid4())
58
+ session_state["thread_id"] = thread_id
59
+
60
+ # Check if the current message is a response to a human interruption.
61
+ is_message_command = session_state.get("is_message_command", False)
62
+
63
+ config = {
64
+ "configurable": {"thread_id": thread_id},
65
+ "recursion_limit": 100,
66
+ }
67
+
68
+ if is_message_command:
69
+ # The user is providing feedback to an interruption.
70
+ app_input = human_resume_node.call_human_interrupt_agent(message)
71
+ session_state["is_message_command"] = False
72
+ else:
73
+ # A standard user message.
74
+ app_input = {"messages": [HumanMessage(content=message)]}
75
+
76
+ # Stream the graph's response.
77
+ # This revised logic handles intermediate messages and prevents duplication.
78
+ async for res in app.astream(app_input, config=config, stream_mode="values"):
79
+ if "messages" in res:
80
+ last_message = res["messages"][-1]
81
+ # We only stream content from AIMessages. Any intermediate AIMessages
82
+ # (e.g., "I will now use a tool") will be overwritten by subsequent
83
+ # AIMessages in the UI, so only the final answer is visible.
84
+ if isinstance(last_message, AIMessage):
85
+ yield last_message.content
86
+
87
+ elif "__interrupt__" in res:
88
+ # Handle interruptions where the agent needs human feedback.
89
+ interruption_message = res["__interrupt__"][0]
90
+ session_state["is_message_command"] = True
91
+ yield interruption_message.value
92
+ return # Stop the stream and wait for the user's next message.
93
+
94
+
95
+ def create_gradio_app():
96
+ """Creates and launches the Gradio web application."""
97
+ print("Launching Gradio app...")
98
+
99
+ with gr.Blocks(theme=gr.themes.Soft(), title="LangGraph Multi-Agent Chat") as demo:
100
+ session_state = gr.State({})
101
+
102
+ gr.Markdown(
103
+ """
104
+ # LangGraph Multi-Agent Project Manager
105
+
106
+ Interact with a multi-agent system powered by LangGraph.
107
+ You can assign tasks related to Trello and Github.
108
+ The system can be interrupted for human feedback when it needs to use a tool.
109
+ """
110
+ )
111
+
112
+ chatbot = gr.Chatbot(
113
+ [],
114
+ elem_id="chatbot",
115
+ bubble_full_width=False,
116
+ height=600,
117
+ label="Multi-Agent Chat",
118
+ show_label=False
119
+ )
120
+
121
+ # --- FIX: Added an accordion for API keys and configuration ---
122
+ with gr.Accordion("API Configuration", open=True):
123
+ gr.Markdown("Please enter your credentials. The agent will be configured when you send your first message.")
124
+ github_repo = gr.Textbox(label="GitHub Repo", placeholder="e.g., username/repository", info="The target repository for GitHub operations.")
125
+ github_token = gr.Textbox(label="GitHub Token", placeholder="ghp_xxxxxxxxxxxx", type="password", info="A fine-grained personal access token.")
126
+ trello_api = gr.Textbox(label="Trello API Key", placeholder="Your Trello API key", info="Your API key from trello.com/power-ups/admin.")
127
+ trello_token = gr.Textbox(label="Trello Token", placeholder="Your Trello token", type="password", info="A token generated from your Trello account.")
128
+ hf_token = gr.Textbox(label="Hugging Face Token", placeholder="hf_xxxxxxxxxxxx", type="password", info="Used for tools requiring Hugging Face models.")
129
+
130
+ chat_interface = gr.ChatInterface(
131
+ fn=chat_logic,
132
+ chatbot=chatbot,
133
+ additional_inputs=[session_state, github_repo, github_token, trello_api, trello_token, hf_token],
134
+ title=None,
135
+ description=None,
136
+ )
137
+
138
+ demo.queue()
139
+ demo.launch(debug=True)
140
+
141
+
142
+ if __name__ == "__main__":
143
+ try:
144
+ # The main function to create the app is now synchronous.
145
+ # Gradio handles the async calls within the chat logic.
146
+ create_gradio_app()
147
+ except KeyboardInterrupt:
148
+ print("\nShutting down Gradio app.")
149
+ except Exception as e:
150
+ print(f"An error occurred: {e}")
mcpc_graph.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from langchain_mcp_adapters.client import MultiServerMCPClient
4
+ from langchain_openai import ChatOpenAI
5
+ from langgraph.prebuilt import ToolNode
6
+ from langgraph.graph import MessagesState, END, StateGraph
7
+ from langgraph.checkpoint.memory import MemorySaver
8
+
9
+
10
+ from pmcp.agents.executor import ExecutorAgent
11
+ from pmcp.agents.trello_agent import TrelloAgent
12
+ from pmcp.agents.github_agent import GithubAgent
13
+ from pmcp.agents.planner import PlannerAgent
14
+
15
+ from pmcp.nodes.human_interrupt_node import HumanInterruptNode
16
+ from pmcp.nodes.human_resume_node import HumanResumeNode
17
+
18
+ from pmcp.models.state import PlanningState
19
+
20
+
21
+ async def setup_graph():
22
+ mcp_client_trello = MultiServerMCPClient(
23
+ {
24
+ "trello": {
25
+ "command": "python",
26
+ "args": [os.getenv("MCP_TRELLO_PATH")],
27
+ "transport": "stdio",
28
+ }
29
+ }
30
+ )
31
+ mcp_client_github = MultiServerMCPClient(
32
+ {
33
+ "github": {
34
+ "command": "python",
35
+ "args": [os.getenv("MCP_GITHUB_PATH")],
36
+ "transport": "stdio",
37
+ }
38
+ }
39
+ )
40
+
41
+ memory = MemorySaver()
42
+
43
+ trello_tools = await mcp_client_trello.get_tools()
44
+ github_tools = await mcp_client_github.get_tools()
45
+
46
+ tool_node = ToolNode(github_tools + trello_tools)
47
+
48
+ llm = ChatOpenAI(
49
+ model="Qwen/Qwen2.5-32B-Instruct",
50
+ temperature=0.0,
51
+ api_key=os.getenv("NEBIUS_API_KEY"),
52
+ base_url="https://api.studio.nebius.com/v1/",
53
+ )
54
+
55
+ trello_agent = TrelloAgent(
56
+ tools=trello_tools,
57
+ llm=llm,
58
+ )
59
+
60
+ github_agent = GithubAgent(llm=llm, tools=github_tools)
61
+
62
+ planner_agent = PlannerAgent(
63
+ llm=llm,
64
+ )
65
+ executor_agent = ExecutorAgent(llm=llm)
66
+
67
+ human_interrupt_node = HumanInterruptNode(
68
+ llm=llm,
69
+ )
70
+ human_resume_node = HumanResumeNode(llm=llm)
71
+
72
+ graph = StateGraph(MessagesState)
73
+ graph.add_node(planner_agent.agent.agent_name, planner_agent.acall_planner_agent)
74
+ graph.add_node(trello_agent.agent.agent_name, trello_agent.acall_trello_agent)
75
+ graph.add_node(github_agent.agent.agent_name, github_agent.acall_github_agent)
76
+ graph.add_node(executor_agent.agent.agent_name, executor_agent.acall_executor_agent)
77
+ graph.add_node("tool", tool_node)
78
+ graph.add_node("human_interrupt", human_interrupt_node.call_human_interrupt_agent)
79
+ graph.set_entry_point(planner_agent.agent.agent_name)
80
+
81
+ def should_continue(state: PlanningState):
82
+ last_message = state.messages[-1]
83
+ if last_message.tool_calls:
84
+ return "human_interrupt"
85
+ return executor_agent.agent.agent_name
86
+
87
+ def execute_agent(state: PlanningState):
88
+ if state.current_step:
89
+ return state.current_step.agent
90
+
91
+ return END
92
+
93
+ graph.add_conditional_edges(trello_agent.agent.agent_name, should_continue)
94
+ graph.add_conditional_edges(github_agent.agent.agent_name, should_continue)
95
+ graph.add_conditional_edges(executor_agent.agent.agent_name, execute_agent)
96
+
97
+ graph.add_edge("tool", trello_agent.agent.agent_name)
98
+ graph.add_edge("tool", github_agent.agent.agent_name)
99
+ graph.add_edge(planner_agent.agent.agent_name, executor_agent.agent.agent_name)
100
+
101
+ app = graph.compile(checkpointer=memory)
102
+ app.get_graph(xray=True).draw_mermaid()
103
+
104
+
105
+
106
+ return app, human_resume_node