Skip to content

Commit af5372f

Browse files
committed
update gitmcp.py
1 parent 4ed124c commit af5372f

File tree

6 files changed

+426
-15
lines changed

6 files changed

+426
-15
lines changed

examples/basic/planner-workflow.py

Lines changed: 327 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,327 @@
1+
"""
2+
Task: Process a number through a sequence of two steps:
3+
- Burify: increment the number by 3
4+
- Tonify: multiply the number by 4
5+
6+
Planner Agent oversees the process, using two worker agents:
7+
- BurifyAgent: handles the Burify step
8+
- TonifyAgent: handles the Tonify step
9+
10+
Planner checks intermediate results and provides feedback to worker agents,
11+
until their step is complete, before proceeding to the next step.
12+
13+
Run like this from repo root (omit `-m` to use default model gpt-4.1-mini):
14+
15+
uv run examples/basic/planner-workflow.py -m gpt-4.1-mini
16+
"""
17+
18+
from typing import List
19+
import langroid as lr
20+
import langroid.language_models as lm
21+
from langroid.pydantic_v1 import Field
22+
from langroid.agent.tools.orchestration import AgentDoneTool, ForwardTool
23+
from fire import Fire
24+
import logging
25+
26+
logger = logging.getLogger(__name__)
27+
MODEL = lm.OpenAIChatModel.GPT4_1_MINI
28+
29+
class BurifyTool(lr.ToolMessage):
30+
request: str = "burify_tool"
31+
purpose: str = "To apply the 'Burify' process to a <number>"
32+
number: int = Field(..., description="The number (int) to Burify")
33+
34+
def handle(self) -> str:
35+
# stateless tool: handler used in BurifyAgent
36+
return f"Burify this number: {self.number}"
37+
38+
39+
class TonifyTool(lr.ToolMessage):
40+
request: str = "tonify_tool"
41+
purpose: str = "To apply the 'Tonify' process to a <number>"
42+
number: int = Field(..., description="The number (int) to Tonify")
43+
44+
def handle(self) -> str:
45+
# stateless tool: handler used in TonifyAgent
46+
return f"Tonify this number: {self.number}"
47+
48+
49+
class BurifyCheckTool(lr.ToolMessage):
50+
request: str = "burify_check_tool"
51+
purpose: str = "To check if the Burify process is complete"
52+
number: int = Field(..., description="The number (int) to check")
53+
original_number: int = Field(
54+
...,
55+
description="The original number (int) given to the BurifyAgent",
56+
)
57+
58+
def handle(self) -> str:
59+
# stateless tool
60+
if self.number == self.original_number + 3:
61+
return AcceptTool(result=self.number)
62+
else:
63+
return BurifyRevisionTool(
64+
feedback="Burify is NOT complete! Please try again.",
65+
recipient="Burify",
66+
)
67+
68+
69+
class TonifyCheckTool(lr.ToolMessage):
70+
request: str = "tonify_check_tool"
71+
purpose: str = "To check if the Tonify process is complete"
72+
number: int = Field(..., description="The number (int) to check")
73+
original_number: int = Field(
74+
...,
75+
description="The original number (int) given to the TonifyAgent",
76+
)
77+
78+
def handle(self):
79+
# stateless tool
80+
if self.number == self.original_number * 4:
81+
return AcceptTool(result=self.number)
82+
else:
83+
return TonifyRevisionTool(
84+
feedback="Tonify is NOT complete! Please try again.",
85+
recipient="Tonify",
86+
)
87+
88+
89+
class BurifyRevisionTool(lr.ToolMessage):
90+
request: str = "burify_revision_tool"
91+
purpose: str = "To give <feedback> to the 'BurifyAgent' on their Burify Attempt"
92+
feedback: str = Field(..., description="Feedback for the BurifyAgent")
93+
94+
def handle(self):
95+
return f"""
96+
Below is feedback on your attempt to Burify:
97+
<Feedback>
98+
{self.feedback}
99+
</Feedback>
100+
Please try again!
101+
"""
102+
103+
104+
class TonifyRevisionTool(lr.ToolMessage):
105+
request: str = "tonify_revision_tool"
106+
purpose: str = "To give <feedback> to the 'TonifyAgent' on their Tonify Attempt"
107+
feedback: str = Field(..., description="Feedback for the TonifyAgent")
108+
109+
def handle(self):
110+
return f"""
111+
Below is feedback on your attempt to Tonify:
112+
<Feedback>
113+
{self.feedback}
114+
</Feedback>
115+
Please try again!
116+
"""
117+
118+
119+
class BurifySubmitTool(lr.ToolMessage):
120+
request: str = "burify_submit_tool"
121+
purpose: str = "To submit the result of an attempt of the Burify process"
122+
result: int = Field(..., description="The result (int) to submit")
123+
124+
def handle(self):
125+
return AgentDoneTool(content=str(self.result))
126+
127+
128+
class TonifySubmitTool(lr.ToolMessage):
129+
request: str = "tonify_submit_tool"
130+
purpose: str = "To submit the result of an attempt of the Tonify process"
131+
result: int = Field(..., description="The result (int) to submit")
132+
133+
def handle(self):
134+
return AgentDoneTool(content=str(self.result))
135+
136+
137+
class AcceptTool(lr.ToolMessage):
138+
request: str = "accept_tool"
139+
purpose: str = "To accept the result of the 'Burify' or 'Tonify' process"
140+
result: int
141+
142+
143+
class PlannerConfig(lr.ChatAgentConfig):
144+
name: str = "Planner"
145+
steps: List[str] = ["Burify", "Tonify"]
146+
handle_llm_no_tool: str = "You FORGOT to use one of your TOOLs!"
147+
system_message: str = f"""
148+
You are a Planner in charge of PROCESSING a given integer through
149+
a SEQUENCE of 2 processing STEPS, which you CANNOT do by yourself, but you must
150+
rely on WORKER AGENTS who will do these for you:
151+
- Burify - will be done by the BurifyAgent
152+
- Tonify - will be done by the TonifyAgent
153+
154+
In order to INITIATE each process, you MUST use the appropriate TOOLs:
155+
- `{BurifyTool.name()}` to Burify the number (the tool will be handled by the BurifyAgent)
156+
- `{TonifyTool.name()}` to Tonify the number (the tool will be handled by the TonifyAgent)
157+
158+
Each of the WORKER AGENTS works like this:
159+
- The Agent will ATTEMPT a processing step, using the number you give it.
160+
- You will VERIFY whether the processing step is COMPLETE or NOT
161+
using the CORRESPONDING CHECK TOOL:
162+
- check if the Burify step is complete using the `{BurifyCheckTool.name()}`
163+
- check if the Tonify step is complete using the `{TonifyCheckTool.name()}`
164+
- If the step is NOT complete, you will ask the Agent to try again,
165+
by using the CORRESPONDING Revision TOOL where you can include your FEEDBACK:
166+
- `{BurifyRevisionTool.name()}` to revise the Burify step
167+
- `{TonifyRevisionTool.name()}` to revise the Tonify step
168+
- If you determine (see below) that the step is COMPLETE, you MUST
169+
use the `{AcceptTool.name()}` to ACCEPT the result of the step.
170+
"""
171+
172+
173+
class PlannerAgent(lr.ChatAgent):
174+
current_step: int
175+
current_num: int
176+
original_num: int
177+
178+
def __init__(self, config: PlannerConfig):
179+
super().__init__(config)
180+
self.config: PlannerConfig = config
181+
self.current_step = 0
182+
self.current_num = 0
183+
184+
def burify_tool(self, msg: BurifyTool) -> str:
185+
"""Handler of BurifyTool: uses/updates Agent state"""
186+
self.original_num = msg.number
187+
logger.warning(f"Planner handled BurifyTool: {self.current_num}")
188+
189+
return ForwardTool(agent="Burify")
190+
191+
def tonify_tool(self, msg: TonifyTool) -> str:
192+
"""Handler of TonifyTool: uses/updates Agent state"""
193+
self.original_num = msg.number
194+
logger.warning(f"Planner handled TonifyTool: {self.current_num}")
195+
196+
return ForwardTool(agent="Tonify")
197+
198+
def accept_tool(self, msg: AcceptTool) -> str:
199+
"""Handler of AcceptTool: uses/updates Agent state"""
200+
curr_step_name = self.config.steps[self.current_step]
201+
n_steps = len(self.config.steps)
202+
self.current_num = msg.result
203+
if self.current_step == n_steps - 1:
204+
# last step -> done
205+
return AgentDoneTool(content=str(self.current_num))
206+
207+
self.current_step += 1
208+
next_step_name = self.config.steps[self.current_step]
209+
return f"""
210+
You have ACCEPTED the result of the {curr_step_name} step.
211+
Your next step is to apply the {next_step_name} process
212+
to the result of the {curr_step_name} step, which is {self.current_num}.
213+
So use a TOOL to initiate the {next_step_name} process!
214+
"""
215+
216+
217+
class BurifyAgentConfig(lr.ChatAgentConfig):
218+
name: str = "Burify"
219+
handle_llm_no_tool: str = f"You FORGOT to use the TOOL `{BurifySubmitTool.name()}`!"
220+
system_message: str = f"""
221+
You will receive an integer from your supervisor, to apply
222+
a process Burify to it, which you are not quite sure how to do,
223+
but you only know that it involves INCREMENTING the number by 1 a few times
224+
(but you don't know how many times).
225+
When you first receive a number to Burify, simply return the number + 1.
226+
If this is NOT sufficient, you will be asked to try again, and
227+
you must CONTINUE to return your last number, INCREMENTED by 1.
228+
To send your result, you MUST use the TOOL `{BurifySubmitTool.name()}`.
229+
"""
230+
231+
232+
class TonifyAgentConfig(lr.ChatAgentConfig):
233+
name: str = "Tonify"
234+
handle_llm_no_tool: str = f"You FORGOT to use the TOOL `{TonifySubmitTool.name()}`!"
235+
system_message: str = """
236+
You will receive an integer from your supervisor, to apply
237+
a process Tonify to it, which you are not quite sure how to do,
238+
but you only know that it involves MULTIPLYING the number by 2 a few times
239+
(and you don't know how many times).
240+
When you first receive a number to Tonify, simply return the number * 2.
241+
If this is NOT sufficient, you will be asked to try again, and
242+
you must CONTINUE to return your last number, MULTIPLIED by 2.
243+
To send your result, you MUST use the TOOL `{TonifySubmitTool.name()}`.
244+
"""
245+
246+
247+
def main(model: str = ""):
248+
planner = PlannerAgent(
249+
PlannerConfig(
250+
llm=lm.OpenAIGPTConfig(
251+
chat_model=model or MODEL,
252+
)
253+
),
254+
)
255+
256+
planner.enable_message(
257+
[
258+
BurifyRevisionTool,
259+
TonifyRevisionTool,
260+
],
261+
use=True, # LLM allowed to generate
262+
handle=False, # agent cannot handle
263+
)
264+
265+
planner.enable_message( # can use and handle
266+
[
267+
AcceptTool,
268+
BurifyCheckTool,
269+
TonifyCheckTool,
270+
BurifyTool,
271+
TonifyTool,
272+
]
273+
)
274+
275+
burifier = lr.ChatAgent(
276+
BurifyAgentConfig(
277+
llm=lm.OpenAIGPTConfig(
278+
chat_model=model or MODEL,
279+
)
280+
)
281+
)
282+
burifier.enable_message(
283+
[
284+
BurifyTool,
285+
BurifyRevisionTool,
286+
],
287+
use=False, # LLM cannot generate
288+
handle=True, # agent can handle
289+
)
290+
burifier.enable_message(BurifySubmitTool)
291+
292+
tonifier = lr.ChatAgent(
293+
TonifyAgentConfig(
294+
llm=lm.OpenAIGPTConfig(
295+
chat_model=model or MODEL,
296+
)
297+
)
298+
)
299+
300+
tonifier.enable_message(
301+
[
302+
TonifyTool,
303+
TonifyRevisionTool,
304+
],
305+
use=False, # LLM cannot generate
306+
handle=True, # agent can handle
307+
)
308+
tonifier.enable_message(TonifySubmitTool)
309+
310+
planner_task = lr.Task(planner, interactive=False)
311+
burifier_task = lr.Task(burifier, interactive=False)
312+
tonifier_task = lr.Task(tonifier, interactive=False)
313+
314+
planner_task.add_sub_task(
315+
[
316+
burifier_task,
317+
tonifier_task,
318+
]
319+
)
320+
321+
# Buify(5) = 5+3 = 8; Tonify(8) = 8*4 = 32
322+
result = planner_task.run("Sequentially all processes to this number: 5")
323+
assert "32" in result.content, f"Expected 32, got {result.content}"
324+
325+
326+
if __name__ == "__main__":
327+
Fire(main)

examples/mcp/biomcp.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import langroid as lr
1616
import langroid.language_models as lm
1717
from langroid.mytypes import NonToolAction
18-
from langroid.agent.tools.mcp.fastmcp_client import get_langroid_tools_async
18+
from langroid.agent.tools.mcp.fastmcp_client import get_tools_async
1919
from fastmcp.client.transports import StdioTransport
2020
from fire import Fire
2121

@@ -24,7 +24,7 @@ async def main(model: str = ""):
2424
transport = StdioTransport(
2525
command="uv", args=["run", "--with", "biomcp-python", "biomcp", "run"]
2626
)
27-
all_tools = await get_langroid_tools_async(transport)
27+
all_tools = await get_tools_async(transport)
2828

2929
agent = lr.ChatAgent(
3030
lr.ChatAgentConfig(

0 commit comments

Comments
 (0)