langroid 0.53.2__py3-none-any.whl → 0.53.5__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 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 encapsulates mainly two components:
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 is handled by this agent
1091
- and an explicit `recipient` field doesn't preclude this agent from handling it?
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 ALL tools are
1102
- disabled for handling by this agent?
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 "no-tools" scenario.
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.
@@ -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
- Users the self.config.non_tool_routing to determine the action to take.
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 self.config.handle_llm_no_tool is None:
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
- if isinstance(msg, ChatDocument) and msg.metadata.sender == Entity.LLM:
592
- from langroid.agent.tools.orchestration import AgentDoneTool, ForwardTool
593
-
594
- no_tool_option = self.config.handle_llm_no_tool
595
- if no_tool_option in list(NonToolAction):
596
- # in case the `no_tool_option` is one of the special NonToolAction vals
597
- match self.config.handle_llm_no_tool:
598
- case NonToolAction.FORWARD_USER:
599
- return ForwardTool(agent="User")
600
- case NonToolAction.DONE:
601
- return AgentDoneTool(
602
- content=msg.content, tools=msg.tool_messages
603
- )
604
- elif is_callable(no_tool_option):
605
- return no_tool_option(msg)
606
- # Otherwise just return `no_tool_option` as is:
607
- # This can be any string, such as a specific nudge/reminder to the LLM,
608
- # or even something like ResultTool etc.
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 = pd.eval(exprn, local_dict=local_vars)
218
+ eval_result = eval(exprn, {}, local_vars)
217
219
  except Exception as e:
218
220
  eval_result = f"ERROR: {type(e)}: {e}"
219
221
 
@@ -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
- parser = etree.XMLParser(strip_cdata=False)
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:
@@ -159,12 +159,11 @@ class VectorStore(ABC):
159
159
  df = pd.DataFrame(dicts)
160
160
 
161
161
  try:
162
- result = pd.eval( # safer than eval but limited to single expression
163
- calc,
164
- engine="python",
165
- parser="pandas",
166
- local_dict={"df": df},
167
- )
162
+ # SECURITY: Use Python's eval() with NO globals and only {"df": df}
163
+ # in locals. This allows pandas operations on `df` while preventing
164
+ # access to builtins or other potentially harmful global functions,
165
+ # mitigating risks associated with executing untrusted `calc` strings.
166
+ result = eval(calc, {}, {"df": df}) # type: ignore
168
167
  except Exception as e:
169
168
  # return error message so LLM can fix the calc string if needed
170
169
  err = f"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.53.2
3
+ Version: 0.53.5
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<5.0.0,>=4.9.3
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
  [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/Langroid_quick_start.ipynb)
217
217
  [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=flat&logo=discord&logoColor=white)](https://discord.gg/ZU36McDgDs)
218
218
  [![Substack](https://img.shields.io/badge/Substack-%23006f5c.svg?style=flat&logo=substack&logoColor=FF6719)](https://langroid.substack.com/p/langroid-harness-llms-with-multi-agent-programming)
219
+
220
+ [![GitMCP](https://img.shields.io/endpoint?url=https://gitmcp.io/badge/OWNER/REPO)](https://gitmcp.io/langroid/langroid)
219
221
  </div>
220
222
 
221
223
  <h3 align="center">
@@ -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=lWR4ivX_elTFejpknLhkO-DlAGT3aG6ojQAVkzDOqMc,80090
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=nsFSARSANCVByL4fAl_DhyWm9gM7s_6waqNsmePXJMA,85309
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=6SshYZJKIfi4mkE-gIoSwjkEYekQ8GwcSiCv7a5uO9E,15054
13
+ langroid/agent/xml_tool_message.py,sha256=oeBKnJNoGaKdtz39XoWGMTNlVyXew2MWH5lgtYeh8wQ,15496
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=ii-xd7pRLLfRhamFZ04zpSkRO4xPn6Rm5qmA4z4N0HA,9661
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
@@ -124,7 +124,7 @@ langroid/utils/output/citations.py,sha256=9W0slQQgzRGLS7hU51mm5UWao5cS_xr8AVosVe
124
124
  langroid/utils/output/printing.py,sha256=yzPJZN-8_jyOJmI9N_oLwEDfjMwVgk3IDiwnZ4eK_AE,2962
125
125
  langroid/utils/output/status.py,sha256=rzbE7mDJcgNNvdtylCseQcPGCGghtJvVq3lB-OPJ49E,1049
126
126
  langroid/vector_store/__init__.py,sha256=8ktJUVsVUoc7FMmkUFpFBZu7VMWUqQY9zpm4kEJ8yTs,1537
127
- langroid/vector_store/base.py,sha256=sT6_8-LfLu_z82p4yMmyH_Kh8kt7o51Lt1n00PKBXWw,14588
127
+ langroid/vector_store/base.py,sha256=HDgY2uMwOnoyuySDCXdRK_USPWaFRLhti94B2OP1B_w,14752
128
128
  langroid/vector_store/chromadb.py,sha256=p9mEqJwO2BrL2jSSXfa23kCPlPOwWpF3xJYd5zoWw_c,8661
129
129
  langroid/vector_store/lancedb.py,sha256=Qd20gKjWozPWfW5-D66J6U8dSrJo1yl-maj6s1lbf1c,14688
130
130
  langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3HmhHQICXLs,11663
@@ -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.2.dist-info/METADATA,sha256=t3on9Riu1tK0c1icC8e3mhsCFEjTU4a7SkMGI1kV4bY,64823
136
- langroid-0.53.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
137
- langroid-0.53.2.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
138
- langroid-0.53.2.dist-info/RECORD,,
135
+ langroid-0.53.5.dist-info/METADATA,sha256=ExVMihbLql8jTdQQ-6DuM8Ocjyqc0JV057Sp17U3y8Y,64945
136
+ langroid-0.53.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
137
+ langroid-0.53.5.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
138
+ langroid-0.53.5.dist-info/RECORD,,