langroid 0.53.1__py3-none-any.whl → 0.53.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langroid/agent/base.py +9 -12
- langroid/agent/chat_agent.py +32 -22
- langroid/agent/special/table_chat_agent.py +4 -2
- langroid/agent/tools/mcp/fastmcp_client.py +27 -1
- langroid/agent/xml_tool_message.py +11 -1
- {langroid-0.53.1.dist-info → langroid-0.53.4.dist-info}/METADATA +20 -2
- {langroid-0.53.1.dist-info → langroid-0.53.4.dist-info}/RECORD +9 -9
- {langroid-0.53.1.dist-info → langroid-0.53.4.dist-info}/WHEEL +0 -0
- {langroid-0.53.1.dist-info → langroid-0.53.4.dist-info}/licenses/LICENSE +0 -0
langroid/agent/base.py
CHANGED
@@ -124,13 +124,8 @@ async def async_lambda_noop_fn() -> Callable[..., Coroutine[Any, Any, None]]:
|
|
124
124
|
|
125
125
|
class Agent(ABC):
|
126
126
|
"""
|
127
|
-
An Agent is an abstraction that
|
128
|
-
|
129
|
-
- a language model (LLM)
|
130
|
-
- a vector store (vecdb)
|
131
|
-
|
132
|
-
plus associated components such as a parser, and variables that hold
|
133
|
-
information about any tool/function-calling messages that have been defined.
|
127
|
+
An Agent is an abstraction that typically (but not necessarily)
|
128
|
+
encapsulates an LLM.
|
134
129
|
"""
|
135
130
|
|
136
131
|
id: str = Field(default_factory=lambda: ObjectRegistry.new_id())
|
@@ -1087,8 +1082,9 @@ class Agent(ABC):
|
|
1087
1082
|
return False
|
1088
1083
|
|
1089
1084
|
def _tool_recipient_match(self, tool: ToolMessage) -> bool:
|
1090
|
-
"""Is tool
|
1091
|
-
|
1085
|
+
"""Is tool enabled for handling by this agent and intended for this
|
1086
|
+
agent to handle (i.e. if there's any explicit `recipient` field exists in
|
1087
|
+
tool, then it matches this agent's name)?
|
1092
1088
|
"""
|
1093
1089
|
if tool.default_value("request") not in self.llm_tools_handled:
|
1094
1090
|
return False
|
@@ -1098,8 +1094,8 @@ class Agent(ABC):
|
|
1098
1094
|
|
1099
1095
|
def has_only_unhandled_tools(self, msg: str | ChatDocument) -> bool:
|
1100
1096
|
"""
|
1101
|
-
Does the msg have at least one tool, and
|
1102
|
-
|
1097
|
+
Does the msg have at least one tool, and none of the tools in the msg are
|
1098
|
+
handleable by this agent?
|
1103
1099
|
"""
|
1104
1100
|
if msg is None:
|
1105
1101
|
return False
|
@@ -1535,7 +1531,8 @@ class Agent(ABC):
|
|
1535
1531
|
|
1536
1532
|
def handle_message_fallback(self, msg: str | ChatDocument) -> Any:
|
1537
1533
|
"""
|
1538
|
-
Fallback method for the
|
1534
|
+
Fallback method for the case where the msg has no tools that
|
1535
|
+
can be handled by this agent.
|
1539
1536
|
This method can be overridden by subclasses, e.g.,
|
1540
1537
|
to create a "reminder" message when a tool is expected but the LLM "forgot"
|
1541
1538
|
to generate one.
|
langroid/agent/chat_agent.py
CHANGED
@@ -574,8 +574,14 @@ class ChatAgent(Agent):
|
|
574
574
|
|
575
575
|
def handle_message_fallback(self, msg: str | ChatDocument) -> Any:
|
576
576
|
"""
|
577
|
-
Fallback method for the "no-tools" scenario.
|
578
|
-
|
577
|
+
Fallback method for the "no-tools" scenario, i.e., the current `msg`
|
578
|
+
(presumably emitted by the LLM) does not have any tool that the agent
|
579
|
+
can handle.
|
580
|
+
NOTE: The `msg` may contain tools but either (a) the agent is not
|
581
|
+
enabled to handle them, or (b) there's an explicit `recipient` field
|
582
|
+
in the tool that doesn't match the agent's name.
|
583
|
+
|
584
|
+
Uses the self.config.non_tool_routing to determine the action to take.
|
579
585
|
|
580
586
|
This method can be overridden by subclasses, e.g.,
|
581
587
|
to create a "reminder" message when a tool is expected but the LLM "forgot"
|
@@ -586,27 +592,31 @@ class ChatAgent(Agent):
|
|
586
592
|
Returns:
|
587
593
|
Any: The result of the handler method
|
588
594
|
"""
|
589
|
-
if
|
595
|
+
if (
|
596
|
+
isinstance(msg, str)
|
597
|
+
or msg.metadata.sender != Entity.LLM
|
598
|
+
or self.config.handle_llm_no_tool is None
|
599
|
+
or self.has_only_unhandled_tools(msg)
|
600
|
+
):
|
590
601
|
return None
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
return no_tool_option
|
602
|
+
# we ONLY use the `handle_llm_no_tool` config option when
|
603
|
+
# the msg is from LLM and does not contain ANY tools at all.
|
604
|
+
from langroid.agent.tools.orchestration import AgentDoneTool, ForwardTool
|
605
|
+
|
606
|
+
no_tool_option = self.config.handle_llm_no_tool
|
607
|
+
if no_tool_option in list(NonToolAction):
|
608
|
+
# in case the `no_tool_option` is one of the special NonToolAction vals
|
609
|
+
match self.config.handle_llm_no_tool:
|
610
|
+
case NonToolAction.FORWARD_USER:
|
611
|
+
return ForwardTool(agent="User")
|
612
|
+
case NonToolAction.DONE:
|
613
|
+
return AgentDoneTool(content=msg.content, tools=msg.tool_messages)
|
614
|
+
elif is_callable(no_tool_option):
|
615
|
+
return no_tool_option(msg)
|
616
|
+
# Otherwise just return `no_tool_option` as is:
|
617
|
+
# This can be any string, such as a specific nudge/reminder to the LLM,
|
618
|
+
# or even something like ResultTool etc.
|
619
|
+
return no_tool_option
|
610
620
|
|
611
621
|
def unhandled_tools(self) -> set[str]:
|
612
622
|
"""The set of tools that are known but not handled.
|
@@ -211,9 +211,11 @@ class TableChatAgent(ChatAgent):
|
|
211
211
|
# Temporarily redirect standard output to our string-based I/O stream
|
212
212
|
sys.stdout = code_out
|
213
213
|
|
214
|
-
# Evaluate the last line and get the result
|
214
|
+
# Evaluate the last line and get the result;
|
215
|
+
# SECURITY: eval only with empty globals and {"df": df} in locals to
|
216
|
+
# prevent arbitrary Python code execution.
|
215
217
|
try:
|
216
|
-
eval_result =
|
218
|
+
eval_result = eval(exprn, {}, local_vars)
|
217
219
|
except Exception as e:
|
218
220
|
eval_result = f"ERROR: {type(e)}: {e}"
|
219
221
|
|
@@ -134,6 +134,24 @@ class FastMCPClient:
|
|
134
134
|
camel_case = "".join(part.capitalize() for part in parts)
|
135
135
|
model_name = f"{camel_case}Tool"
|
136
136
|
|
137
|
+
from langroid.agent.tool_message import ToolMessage as _BaseToolMessage
|
138
|
+
|
139
|
+
# IMPORTANT: Avoid clashes with reserved field names in Langroid ToolMessage!
|
140
|
+
# First figure out which field names are reserved
|
141
|
+
reserved = set(_BaseToolMessage.__annotations__.keys())
|
142
|
+
reserved.update(["recipient", "_handler"])
|
143
|
+
renamed: Dict[str, str] = {}
|
144
|
+
new_fields: Dict[str, Tuple[type, Any]] = {}
|
145
|
+
for fname, (ftype, fld) in fields.items():
|
146
|
+
if fname in reserved:
|
147
|
+
new_name = fname + "__"
|
148
|
+
renamed[fname] = new_name
|
149
|
+
new_fields[new_name] = (ftype, fld)
|
150
|
+
else:
|
151
|
+
new_fields[fname] = (ftype, fld)
|
152
|
+
# now replace fields with our renamed‐aware mapping
|
153
|
+
fields = new_fields
|
154
|
+
|
137
155
|
# create Langroid ToolMessage subclass, with expected fields.
|
138
156
|
tool_model = cast(
|
139
157
|
Type[ToolMessage],
|
@@ -146,6 +164,7 @@ class FastMCPClient:
|
|
146
164
|
),
|
147
165
|
)
|
148
166
|
tool_model._server = self.server # type: ignore[attr-defined]
|
167
|
+
tool_model._renamed_fields = renamed # type: ignore[attr-defined]
|
149
168
|
|
150
169
|
# 2) define an arg-free call_tool_async()
|
151
170
|
async def call_tool_async(self: ToolMessage) -> Any:
|
@@ -153,6 +172,12 @@ class FastMCPClient:
|
|
153
172
|
|
154
173
|
# pack up the payload
|
155
174
|
payload = self.dict(exclude=self.Config.schema_extra["exclude"])
|
175
|
+
|
176
|
+
# restore any renamed fields
|
177
|
+
for orig, new in self.__class__._renamed_fields.items(): # type: ignore
|
178
|
+
if new in payload:
|
179
|
+
payload[orig] = payload.pop(new)
|
180
|
+
|
156
181
|
# open a fresh client, call the tool, then close
|
157
182
|
async with FastMCPClient(self.__class__._server) as client: # type: ignore
|
158
183
|
return await client.call_mcp_tool(self.request, payload)
|
@@ -185,7 +210,8 @@ class FastMCPClient:
|
|
185
210
|
|
186
211
|
async def get_mcp_tool_async(self, name: str) -> Optional[Tool]:
|
187
212
|
"""Find the "original" MCP Tool (i.e. of type mcp.types.Tool) on the server
|
188
|
-
matching `name`, or None if missing.
|
213
|
+
matching `name`, or None if missing. This contains the metadata for the tool:
|
214
|
+
name, description, inputSchema, etc.
|
189
215
|
|
190
216
|
Args:
|
191
217
|
name: Name of the tool to look up.
|
@@ -49,7 +49,17 @@ class XMLToolMessage(ToolMessage):
|
|
49
49
|
Raises:
|
50
50
|
etree.XMLSyntaxError: If the input string is not valid XML.
|
51
51
|
"""
|
52
|
-
|
52
|
+
# SECURITY: Initialize XMLParser with flags to prevent
|
53
|
+
# XML External Entity (XXE), billion laughs, and external DTD attacks by
|
54
|
+
# disabling entity resolution, DTD loading, and network access;
|
55
|
+
# `strip_cdata=False` is needed to preserve
|
56
|
+
# content within CDATA sections (e.g., for code).
|
57
|
+
parser = etree.XMLParser(
|
58
|
+
strip_cdata=False,
|
59
|
+
resolve_entities=False,
|
60
|
+
load_dtd=False,
|
61
|
+
no_network=True,
|
62
|
+
)
|
53
63
|
root = etree.fromstring(formatted_string.encode("utf-8"), parser=parser)
|
54
64
|
|
55
65
|
def parse_element(element: etree._Element) -> Any:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.53.
|
3
|
+
Version: 0.53.4
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
Author-email: Prasad Chalasani <pchalasani@gmail.com>
|
6
6
|
License: MIT
|
@@ -27,7 +27,7 @@ Requires-Dist: grpcio<2.0.0,>=1.62.1
|
|
27
27
|
Requires-Dist: halo<1.0.0,>=0.0.31
|
28
28
|
Requires-Dist: jinja2<4.0.0,>=3.1.2
|
29
29
|
Requires-Dist: json-repair<1.0.0,>=0.29.9
|
30
|
-
Requires-Dist: lxml<
|
30
|
+
Requires-Dist: lxml<6.0.0,>=5.4.0
|
31
31
|
Requires-Dist: markdownify>=0.13.1
|
32
32
|
Requires-Dist: nest-asyncio<2.0.0,>=1.6.0
|
33
33
|
Requires-Dist: nltk<4.0.0,>=3.8.2
|
@@ -216,6 +216,8 @@ Description-Content-Type: text/markdown
|
|
216
216
|
[](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/Langroid_quick_start.ipynb)
|
217
217
|
[](https://discord.gg/ZU36McDgDs)
|
218
218
|
[](https://langroid.substack.com/p/langroid-harness-llms-with-multi-agent-programming)
|
219
|
+
|
220
|
+
[](https://gitmcp.io/langroid/langroid)
|
219
221
|
</div>
|
220
222
|
|
221
223
|
<h3 align="center">
|
@@ -338,6 +340,22 @@ teacher_task.run()
|
|
338
340
|
<details>
|
339
341
|
<summary> <b>Click to expand</b></summary>
|
340
342
|
|
343
|
+
- **Mar-Apr 2025:**
|
344
|
+
- [0.53.0](https://github.com/langroid/langroid/releases/tag/0.53.0) MCP Tools Support.
|
345
|
+
- [0.52.0](https://github.com/langroid/langroid/releases/tag/0.52.0) Multimodal support, i.e. allow PDF, image
|
346
|
+
inputs to LLM.
|
347
|
+
- [0.51.0](https://github.com/langroid/langroid/releases/tag/0.51.0) `LLMPdfParser`, generalizing
|
348
|
+
`GeminiPdfParser` to parse documents directly with LLM.
|
349
|
+
- [0.50.0](https://github.com/langroid/langroid/releases/tag/0.50.0) Structure-aware Markdown chunking with chunks
|
350
|
+
enriched by section headers.
|
351
|
+
- [0.49.0](https://github.com/langroid/langroid/releases/tag/0.49.0) Enable easy switch to LiteLLM Proxy-server
|
352
|
+
- [0.48.0](https://github.com/langroid/langroid/releases/tag/0.48.0) Exa Crawler, Markitdown Parser
|
353
|
+
- [0.47.0](https://github.com/langroid/langroid/releases/tag/0.47.0) Support Firecrawl URL scraper/crawler -
|
354
|
+
thanks @abab-dev
|
355
|
+
- [0.46.0](https://github.com/langroid/langroid/releases/tag/0.46.0) Support LangDB LLM Gateway - thanks @MrunmayS.
|
356
|
+
- [0.45.0](https://github.com/langroid/langroid/releases/tag/0.45.0) Markdown parsing with `Marker` - thanks @abab-dev
|
357
|
+
- [0.44.0](https://github.com/langroid/langroid/releases/tag/0.44.0) Late imports to reduce startup time. Thanks
|
358
|
+
@abab-dev
|
341
359
|
- **Feb 2025:**
|
342
360
|
- [0.43.0](https://github.com/langroid/langroid/releases/tag/0.43.0): `GeminiPdfParser` for parsing PDF using
|
343
361
|
Gemini LLMs - Thanks @abab-dev.
|
@@ -3,14 +3,14 @@ langroid/exceptions.py,sha256=OPjece_8cwg94DLPcOGA1ddzy5bGh65pxzcHMnssTz8,2995
|
|
3
3
|
langroid/mytypes.py,sha256=HIcYAqGeA9OK0Hlscym2FI5Oax9QFljDZoVgRlomhRk,4014
|
4
4
|
langroid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
|
6
|
-
langroid/agent/base.py,sha256=
|
6
|
+
langroid/agent/base.py,sha256=zHwhNU403H-ZvogH4QhKTzaZn5_jt0ZdPHzSEmycDoc,80035
|
7
7
|
langroid/agent/batch.py,sha256=vi1r5i1-vN80WfqHDSwjEym_KfGsqPGUtwktmiK1nuk,20635
|
8
|
-
langroid/agent/chat_agent.py,sha256=
|
8
|
+
langroid/agent/chat_agent.py,sha256=igo7wl3tOig7yae8NokEEqXS5AYuAeWJGq1YZhpzOho,85739
|
9
9
|
langroid/agent/chat_document.py,sha256=6O20Fp4QrquykaF2jFtwNHkvcoDte1LLwVZNk9mVH9c,18057
|
10
10
|
langroid/agent/openai_assistant.py,sha256=JkAcs02bIrgPNVvUWVR06VCthc5-ulla2QMBzux_q6o,34340
|
11
11
|
langroid/agent/task.py,sha256=HB6N-Jn80HFqCf0ZYOC1v3Bn3oO7NLjShHQJJFwW0q4,90557
|
12
12
|
langroid/agent/tool_message.py,sha256=BhjP-_TfQ2tgxuY4Yo_JHLOwwt0mJ4BwjPnREvEY4vk,14744
|
13
|
-
langroid/agent/xml_tool_message.py,sha256=
|
13
|
+
langroid/agent/xml_tool_message.py,sha256=Yf-OsqO6z17qgQbfcTX4dxmPMtwYZVfgcWjcWI8nJzE,15506
|
14
14
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
15
15
|
langroid/agent/callbacks/chainlit.py,sha256=UHB6P_J40vsVnssosqkpkOVWRf9NK4TOY0_G2g_Arsg,20900
|
16
16
|
langroid/agent/special/__init__.py,sha256=gik_Xtm_zV7U9s30Mn8UX3Gyuy4jTjQe9zjiE3HWmEo,1273
|
@@ -20,7 +20,7 @@ langroid/agent/special/lance_doc_chat_agent.py,sha256=s8xoRs0gGaFtDYFUSIRchsgDVb
|
|
20
20
|
langroid/agent/special/lance_tools.py,sha256=qS8x4wi8mrqfbYV2ztFzrcxyhHQ0ZWOc-zkYiH7awj0,2105
|
21
21
|
langroid/agent/special/relevance_extractor_agent.py,sha256=zIx8GUdVo1aGW6ASla0NPQjYYIpmriK_TYMijqAx3F8,4796
|
22
22
|
langroid/agent/special/retriever_agent.py,sha256=o2UfqiCGME0t85SZ6qjK041_WZYqXSuV1SeH_3KtVuc,1931
|
23
|
-
langroid/agent/special/table_chat_agent.py,sha256=
|
23
|
+
langroid/agent/special/table_chat_agent.py,sha256=wC4DWRVMAGfVY33JqP8cNR4kO0dqD8ryjWzAkMFT5j0,9780
|
24
24
|
langroid/agent/special/arangodb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
25
|
langroid/agent/special/arangodb/arangodb_agent.py,sha256=12Y54c84c9qXV-YXRBcI5HaqyiY75JR4TmqlURYKJAM,25851
|
26
26
|
langroid/agent/special/arangodb/system_messages.py,sha256=udwfLleTdyz_DuxHuoiv2wHEZoAPBPbwdF_ivjIfP5c,6867
|
@@ -56,7 +56,7 @@ langroid/agent/tools/segment_extract_tool.py,sha256=__srZ_VGYLVOdPrITUM8S0HpmX4q
|
|
56
56
|
langroid/agent/tools/tavily_search_tool.py,sha256=soI-j0HdgVQLf09wRQScaEK4b5RpAX9C4cwOivRFWWI,1903
|
57
57
|
langroid/agent/tools/mcp/__init__.py,sha256=cQb3gYxXk0YZ23QCqbVNMbMeCeWCJj6w3gqGnvyqv7w,459
|
58
58
|
langroid/agent/tools/mcp/decorators.py,sha256=mWnlTjyI9PMNi750PWzC_2B6V5K_XdxH0Co9kE2yAj0,1145
|
59
|
-
langroid/agent/tools/mcp/fastmcp_client.py,sha256=
|
59
|
+
langroid/agent/tools/mcp/fastmcp_client.py,sha256=ffOV0lJOtE3DLfA6y3Fib28jyACySJPgbqjNJpSAEQA,11815
|
60
60
|
langroid/cachedb/__init__.py,sha256=G2KyNnk3Qkhv7OKyxTOnpsxfDycx3NY0O_wXkJlalNY,96
|
61
61
|
langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
|
62
62
|
langroid/cachedb/redis_cachedb.py,sha256=7kgnbf4b5CKsCrlL97mHWKvdvlLt8zgn7lc528jEpiE,5141
|
@@ -132,7 +132,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
|
|
132
132
|
langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
|
133
133
|
langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
|
134
134
|
langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
|
135
|
-
langroid-0.53.
|
136
|
-
langroid-0.53.
|
137
|
-
langroid-0.53.
|
138
|
-
langroid-0.53.
|
135
|
+
langroid-0.53.4.dist-info/METADATA,sha256=mJijMq7-4U64bl-XA2rQiGdgW6A3TxdMMx60sa-E1Fc,64945
|
136
|
+
langroid-0.53.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
137
|
+
langroid-0.53.4.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
138
|
+
langroid-0.53.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|