langroid 0.33.9__py3-none-any.whl → 0.33.11__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
@@ -1039,6 +1039,11 @@ class Agent(ABC):
1039
1039
  """
1040
1040
  if msg is None:
1041
1041
  return False
1042
+ if isinstance(msg, ChatDocument):
1043
+ if len(msg.tool_messages) > 0:
1044
+ return True
1045
+ if msg.metadata.sender != Entity.LLM:
1046
+ return False
1042
1047
  try:
1043
1048
  tools = self.get_tool_messages(msg)
1044
1049
  return len(tools) > 0
@@ -539,6 +539,20 @@ class ChatAgent(Agent):
539
539
  self.message_history[i].content = message
540
540
  break
541
541
 
542
+ def delete_last_message(self, role: str = Role.USER) -> None:
543
+ """
544
+ Delete the last message that has role `role` from the message history.
545
+ Args:
546
+ role (str): role of message to delete
547
+ """
548
+ if len(self.message_history) == 0:
549
+ return
550
+ # find last message in self.message_history with role `role`
551
+ for i in range(len(self.message_history) - 1, -1, -1):
552
+ if self.message_history[i].role == role:
553
+ self.message_history.pop(i)
554
+ break
555
+
542
556
  def _create_system_and_tools_message(self) -> LLMMessage:
543
557
  """
544
558
  (Re-)Create the system message for the LLM of the agent,
@@ -1049,18 +1063,20 @@ class ChatAgent(Agent):
1049
1063
 
1050
1064
  return tools
1051
1065
 
1052
- def _get_any_tool_message(self, optional: bool = True) -> type[ToolMessage]:
1066
+ def _get_any_tool_message(self, optional: bool = True) -> type[ToolMessage] | None:
1053
1067
  """
1054
1068
  Returns a `ToolMessage` which wraps all enabled tools, excluding those
1055
1069
  where strict recovery is disabled. Used in strict recovery.
1056
1070
  """
1057
- any_tool_type = Union[ # type: ignore
1058
- *(
1059
- self.llm_tools_map[t]
1060
- for t in self.llm_tools_usable
1061
- if t not in self.disable_strict_tools_set
1062
- )
1063
- ]
1071
+ possible_tools = tuple(
1072
+ self.llm_tools_map[t]
1073
+ for t in self.llm_tools_usable
1074
+ if t not in self.disable_strict_tools_set
1075
+ )
1076
+ if len(possible_tools) == 0:
1077
+ return None
1078
+ any_tool_type = Union.__getitem__(possible_tools) # type ignore
1079
+
1064
1080
  maybe_optional_type = Optional[any_tool_type] if optional else any_tool_type
1065
1081
 
1066
1082
  class AnyTool(ToolMessage):
@@ -1211,6 +1227,8 @@ class ChatAgent(Agent):
1211
1227
  and self.config.strict_recovery
1212
1228
  ):
1213
1229
  AnyTool = self._get_any_tool_message()
1230
+ if AnyTool is None:
1231
+ return None
1214
1232
  self.set_output_format(
1215
1233
  AnyTool,
1216
1234
  force_tools=True,
@@ -1219,15 +1237,25 @@ class ChatAgent(Agent):
1219
1237
  instructions=True,
1220
1238
  )
1221
1239
  recovery_message = self._strict_recovery_instructions(AnyTool)
1240
+ augmented_message = message
1241
+ if augmented_message is None:
1242
+ augmented_message = recovery_message
1243
+ elif isinstance(augmented_message, str):
1244
+ augmented_message = augmented_message + recovery_message
1245
+ else:
1246
+ augmented_message.content = augmented_message.content + recovery_message
1222
1247
 
1248
+ # only use the augmented message for this one response...
1249
+ result = self.llm_response(augmented_message)
1250
+ # ... restore the original user message so that the AnyTool recover
1251
+ # instructions don't persist in the message history
1252
+ # (this can cause the LLM to use the AnyTool directly as a tool)
1223
1253
  if message is None:
1224
- message = recovery_message
1225
- elif isinstance(message, str):
1226
- message = message + recovery_message
1254
+ self.delete_last_message(role=Role.USER)
1227
1255
  else:
1228
- message.content = message.content + recovery_message
1229
-
1230
- return self.llm_response(message)
1256
+ msg = message if isinstance(message, str) else message.content
1257
+ self.update_last_message(msg, role=Role.USER)
1258
+ return result
1231
1259
 
1232
1260
  hist, output_len = self._prep_llm_messages(message)
1233
1261
  if len(hist) == 0:
@@ -1294,15 +1322,25 @@ class ChatAgent(Agent):
1294
1322
  instructions=True,
1295
1323
  )
1296
1324
  recovery_message = self._strict_recovery_instructions(AnyTool)
1325
+ augmented_message = message
1326
+ if augmented_message is None:
1327
+ augmented_message = recovery_message
1328
+ elif isinstance(augmented_message, str):
1329
+ augmented_message = augmented_message + recovery_message
1330
+ else:
1331
+ augmented_message.content = augmented_message.content + recovery_message
1297
1332
 
1333
+ # only use the augmented message for this one response...
1334
+ result = self.llm_response(augmented_message)
1335
+ # ... restore the original user message so that the AnyTool recover
1336
+ # instructions don't persist in the message history
1337
+ # (this can cause the LLM to use the AnyTool directly as a tool)
1298
1338
  if message is None:
1299
- message = recovery_message
1300
- elif isinstance(message, str):
1301
- message = message + recovery_message
1339
+ self.delete_last_message(role=Role.USER)
1302
1340
  else:
1303
- message.content = message.content + recovery_message
1304
-
1305
- return self.llm_response(message)
1341
+ msg = message if isinstance(message, str) else message.content
1342
+ self.update_last_message(msg, role=Role.USER)
1343
+ return result
1306
1344
 
1307
1345
  hist, output_len = self._prep_llm_messages(message)
1308
1346
  if len(hist) == 0:
@@ -13,6 +13,7 @@ from typing import Any, Dict, List, Optional, Sequence, Union
13
13
  from rich.console import Console
14
14
 
15
15
  from langroid.exceptions import LangroidImportError
16
+ from langroid.mytypes import Entity
16
17
  from langroid.utils.constants import SEND_TO
17
18
 
18
19
  try:
@@ -43,10 +44,12 @@ from langroid.agent.special.sql.utils.tools import (
43
44
  RunQueryTool,
44
45
  )
45
46
  from langroid.agent.tools.orchestration import (
47
+ DonePassTool,
46
48
  DoneTool,
47
49
  ForwardTool,
48
50
  PassTool,
49
51
  )
52
+ from langroid.language_models.base import Role
50
53
  from langroid.vector_store.base import VectorStoreConfig
51
54
 
52
55
  logger = logging.getLogger(__name__)
@@ -111,6 +114,8 @@ class SQLChatAgentConfig(ChatAgentConfig):
111
114
  # as opposed to returning a result from the task.run()
112
115
  chat_mode: bool = False
113
116
  addressing_prefix: str = ""
117
+ max_result_rows: int | None = None # limit query results to this
118
+ max_retained_tokens: int | None = None # limit history of query results to this
114
119
 
115
120
  """
116
121
  Optional, but strongly recommended, context descriptions for tables, columns,
@@ -182,6 +187,7 @@ class SQLChatAgent(ChatAgent):
182
187
  self.helper_config = self.config.copy()
183
188
  self.helper_config.is_helper = True
184
189
  self.helper_config.use_helper = False
190
+ self.helper_config.chat_mode = False
185
191
  self.helper_agent = SQLHelperAgent(self.helper_config)
186
192
 
187
193
  def _validate_config(self, config: "SQLChatAgentConfig") -> None:
@@ -265,11 +271,13 @@ class SQLChatAgent(ChatAgent):
265
271
 
266
272
  def _init_tools(self) -> None:
267
273
  """Initialize sys msg and tools."""
274
+ RunQueryTool._max_retained_tokens = self.config.max_retained_tokens
268
275
  self.enable_message([RunQueryTool, ForwardTool])
269
276
  if self.config.use_schema_tools:
270
277
  self._enable_schema_tools()
271
278
  if not self.config.chat_mode:
272
279
  self.enable_message(DoneTool)
280
+ self.enable_message(DonePassTool)
273
281
 
274
282
  def _format_message(self) -> str:
275
283
  if self.engine is None:
@@ -312,14 +320,11 @@ class SQLChatAgent(ChatAgent):
312
320
  """
313
321
  if self.config.chat_mode:
314
322
  return f"""
315
- you must use the `{ForwardTool.name()}` with the `agent`
323
+ you must use the TOOL `{ForwardTool.name()}` with the `agent`
316
324
  parameter set to "User"
317
325
  """
318
326
  else:
319
- return f"""
320
- you must use the `{DoneTool.name()}` with the `content`
321
- set to the answer or result
322
- """
327
+ return f"you must use the TOOL `{DonePassTool.name()}`"
323
328
 
324
329
  def _clarifying_message(self) -> str:
325
330
  tools_instruction = f"""
@@ -344,23 +349,24 @@ class SQLChatAgent(ChatAgent):
344
349
  self, message: str | ChatDocument
345
350
  ) -> str | ForwardTool | ChatDocument | None:
346
351
  """
347
- Handle the scenario where current msg is not a tool.
348
- Special handling is only needed if the message was from the LLM
349
- (as indicated by self.llm_responded).
352
+ We'd end up here if the current msg has no tool.
353
+ If this is from LLM, we may need to handle the scenario where
354
+ it may have "forgotten" to generate a tool.
350
355
  """
351
- if not self.llm_responded:
356
+ if (
357
+ not isinstance(message, ChatDocument)
358
+ or message.metadata.sender != Entity.LLM
359
+ ):
352
360
  return None
353
- if self.interactive:
354
- # self.interactive will be set to True by the Task,
355
- # when chat_mode=True, so in this case
356
- # we send any Non-tool msg to the user
361
+ if self.config.chat_mode:
362
+ # send any Non-tool msg to the user
357
363
  return ForwardTool(agent="User")
358
364
  # Agent intent not clear => use the helper agent to
359
365
  # do what this agent should have done, e.g. generate tool, etc.
360
366
  # This is likelier to succeed since this agent has no "baggage" of
361
367
  # prior conversation, other than the system msg, and special
362
368
  # "Intent-interpretation" instructions.
363
- if self._json_schema_available():
369
+ if self._json_schema_available() and self.config.strict_recovery:
364
370
  AnyTool = self._get_any_tool_message(optional=False)
365
371
  self.set_output_format(
366
372
  AnyTool,
@@ -372,15 +378,18 @@ class SQLChatAgent(ChatAgent):
372
378
  recovery_message = self._strict_recovery_instructions(
373
379
  AnyTool, optional=False
374
380
  )
375
- return self.llm_response(recovery_message)
376
- else:
381
+ result = self.llm_response(recovery_message)
382
+ # remove the recovery_message (it has User role) from the chat history,
383
+ # else it may cause the LLM to directly use the AnyTool.
384
+ self.delete_last_message(role=Role.USER) # delete last User-role msg
385
+ return result
386
+ elif self.config.use_helper:
377
387
  response = self.helper_agent.llm_response(message)
378
388
  tools = self.try_get_tool_messages(response)
379
389
  if tools:
380
390
  return response
381
- else:
382
- # fall back on the clarification message
383
- return self._clarifying_message()
391
+ # fall back on the clarification message
392
+ return self._clarifying_message()
384
393
 
385
394
  def retry_query(self, e: Exception, query: str) -> str:
386
395
  """
@@ -467,6 +476,14 @@ class SQLChatAgent(ChatAgent):
467
476
  try:
468
477
  # attempt to fetch results: should work for normal SELECT queries
469
478
  rows = query_result.fetchall()
479
+ n_rows = len(rows)
480
+ if self.config.max_result_rows and n_rows > self.config.max_result_rows:
481
+ rows = rows[: self.config.max_result_rows]
482
+ logger.warning(
483
+ f"SQL query produced {n_rows} rows, "
484
+ f"limiting to {self.config.max_result_rows}"
485
+ )
486
+
470
487
  response_message = self._format_rows(rows)
471
488
  except ResourceClosedError:
472
489
  # If we get here, it's a non-SELECT query (UPDATE, INSERT, DELETE)
langroid/agent/task.py CHANGED
@@ -1429,8 +1429,15 @@ class Task:
1429
1429
  else:
1430
1430
  response_fn = self._entity_responder_map[cast(Entity, e)]
1431
1431
  result = response_fn(self.pending_message)
1432
- # update result.tool_messages if any
1433
- if isinstance(result, ChatDocument):
1432
+ # update result.tool_messages if any.
1433
+ # Do this only if sender is LLM, since this could be
1434
+ # a tool-call result from the Agent responder, which may
1435
+ # contain strings that look like tools, and we don't want to
1436
+ # trigger strict tool recovery due to that.
1437
+ if (
1438
+ isinstance(result, ChatDocument)
1439
+ and result.metadata.sender == Entity.LLM
1440
+ ):
1434
1441
  self.agent.try_get_tool_messages(result)
1435
1442
 
1436
1443
  result_chat_doc = self.agent.to_ChatDocument(
@@ -101,7 +101,7 @@ class ToolMessage(ABC, BaseModel):
101
101
  # Some tools can have large results that we may not want to fully retain,
102
102
  # e.g. result of a db query, which the LLM later reduces to a summary, so
103
103
  # in subsequent dialog we may only want to retain the summary,
104
- # and replace this raw result truncated to _max_result_tokens.
104
+ # and replace this raw result truncated to _max_retained_tokens.
105
105
  # Important to note: unlike _max_result_tokens, this param is used
106
106
  # NOT used to immediately truncate the result;
107
107
  # it is only used to truncate what is retained in msg history AFTER the
@@ -156,7 +156,7 @@ class PassTool(ToolMessage):
156
156
  def response(self, agent: ChatAgent, chat_doc: ChatDocument) -> ChatDocument:
157
157
  """When this tool is enabled for an Agent, this will result in a method
158
158
  added to the Agent with signature:
159
- `forward_tool(self, tool: PassTool, chat_doc: ChatDocument) -> ChatDocument:`
159
+ `pass_tool(self, tool: PassTool, chat_doc: ChatDocument) -> ChatDocument:`
160
160
  """
161
161
  # if PassTool is in chat_doc, pass its parent, else pass chat_doc itself
162
162
  doc = chat_doc
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.33.9
3
+ Version: 0.33.11
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -3,13 +3,13 @@ langroid/exceptions.py,sha256=gp6ku4ZLdXXCUQIwUNVFojJNGTzGnkevi2PLvG7HOhc,2555
3
3
  langroid/mytypes.py,sha256=ptAFxEAtiwmIfUnGisNotTe8wT9LKBf22lOfPgZoQIY,2368
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=-wColF3AGsbIm-uiTLfu8cyGUqMRCzZETVirvgZgYGQ,77642
6
+ langroid/agent/base.py,sha256=oThlrYygKDu1-bKjAfygldJ511gMKT8Z0qCrD52DdDM,77834
7
7
  langroid/agent/batch.py,sha256=qK3ph6VNj_1sOhfXCZY4r6gh035DglDKU751p8BU0tY,14665
8
- langroid/agent/chat_agent.py,sha256=Idts_HDO1tW052POVOQ9FvuU37TTB7c1I96YVbnBumo,80030
8
+ langroid/agent/chat_agent.py,sha256=cxamUgqQkr6_W3mqCPz3L7rJnXIkD4hemR7X7uhlBvI,82095
9
9
  langroid/agent/chat_document.py,sha256=xPUMGzR83rn4iAEXIw2jy5LQ6YJ6Y0TiZ78XRQeDnJQ,17778
10
10
  langroid/agent/openai_assistant.py,sha256=JkAcs02bIrgPNVvUWVR06VCthc5-ulla2QMBzux_q6o,34340
11
- langroid/agent/task.py,sha256=c_Ih0Cc_iiyFKBmKMSdirHcW4RX_35JkCFp05jDAEiM,89994
12
- langroid/agent/tool_message.py,sha256=HDW_FVQXvZAHI61CtOYNuZet0qlK_WwOnjSYd1g81eo,14742
11
+ langroid/agent/task.py,sha256=XrXUbSoiFasvpIsZPn_cBpdWaTCKljJPRimtLMrSZrs,90347
12
+ langroid/agent/tool_message.py,sha256=BhjP-_TfQ2tgxuY4Yo_JHLOwwt0mJ4BwjPnREvEY4vk,14744
13
13
  langroid/agent/xml_tool_message.py,sha256=6SshYZJKIfi4mkE-gIoSwjkEYekQ8GwcSiCv7a5uO9E,15054
14
14
  langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  langroid/agent/callbacks/chainlit.py,sha256=C6zzzYC30qC4eMA7al7eFpRoTgoe3475kaMKyXgQM0Q,20695
@@ -35,7 +35,7 @@ langroid/agent/special/neo4j/neo4j_chat_agent.py,sha256=1RMKupJra0KZ-hA7AiiR662S
35
35
  langroid/agent/special/neo4j/system_messages.py,sha256=m2jsVayey6E_88F5B_gW2WbWKBJvIeDUoVCRBbNs97o,4522
36
36
  langroid/agent/special/neo4j/tools.py,sha256=Vw3HvtDfG2c4_bUHgt4_ZbJq48lpIQstbjjwhh1BjrQ,905
37
37
  langroid/agent/special/sql/__init__.py,sha256=mWfmm1QpXCezpFOS2eI57M0L_Ok3q5_ukG8tXBnBrEA,319
38
- langroid/agent/special/sql/sql_chat_agent.py,sha256=bHJ2TsZciijOBPXKbKvJvDbdEwCRXOSkaRdMmGW1yr0,24660
38
+ langroid/agent/special/sql/sql_chat_agent.py,sha256=CJ-vQFbtcGhmOM-GBQvG2quUmicXXW0XPK1pj52E-54,25639
39
39
  langroid/agent/special/sql/utils/__init__.py,sha256=JFif6CRTrN-bc91uuAI4K9fe2ndIWSNMVxJ0WA68--M,446
40
40
  langroid/agent/special/sql/utils/description_extractors.py,sha256=cX8TIpmTPXZXQTMpIi3OUFwFsPywxFFdurpx717Kq0I,6529
41
41
  langroid/agent/special/sql/utils/populate_metadata.py,sha256=1J22UsyEPKzwK0XlJZtYn9r6kYc0FXIr8-lZrndYlhc,3131
@@ -46,7 +46,7 @@ langroid/agent/tools/duckduckgo_search_tool.py,sha256=NhsCaGZkdv28nja7yveAhSK_w6
46
46
  langroid/agent/tools/file_tools.py,sha256=GjPB5YDILucYapElnvvoYpGJuZQ25ecLs2REv7edPEo,7292
47
47
  langroid/agent/tools/google_search_tool.py,sha256=y7b-3FtgXf0lfF4AYxrZ3K5pH2dhidvibUOAGBE--WI,1456
48
48
  langroid/agent/tools/metaphor_search_tool.py,sha256=ccyEhkShH5MxW6-sx1n0BLpD_GForQddS_nNvBZ67Ik,2561
49
- langroid/agent/tools/orchestration.py,sha256=851nZQOE1HpGBwH5om_TNP_qCMxxatXYWFZUrpjSfKk,11421
49
+ langroid/agent/tools/orchestration.py,sha256=EaL_z9dmKuqhhHZEh9N-ieMP-Jr9jGjDprUCHdyldZs,11418
50
50
  langroid/agent/tools/recipient_tool.py,sha256=dr0yTxgNEIoxUYxH6TtaExC4G_8WdJ0xGohIa4dFLhY,9808
51
51
  langroid/agent/tools/retrieval_tool.py,sha256=zcAV20PP_6VzSd-UE-IJcabaBseFL_QNz59Bnig8-lE,946
52
52
  langroid/agent/tools/rewind_tool.py,sha256=XAXL3BpNhCmBGYq_qi_sZfHJuIw7NY2jp4wnojJ7WRs,5606
@@ -121,7 +121,7 @@ langroid/vector_store/lancedb.py,sha256=b3_vWkTjG8mweZ7ZNlUD-NjmQP_rLBZfyKWcxt2v
121
121
  langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3HmhHQICXLs,11663
122
122
  langroid/vector_store/momento.py,sha256=UNHGT6jXuQtqY9f6MdqGU14bVnS0zHgIJUa30ULpUJo,10474
123
123
  langroid/vector_store/qdrantdb.py,sha256=HRLCt-FG8y4718omwpFaQZnWeYxPj0XCwS4tjokI1sU,18116
124
- langroid-0.33.9.dist-info/METADATA,sha256=5dBFIEzwzxNpjICkMYzMSUNgGONETNZ-Mb9BWhkQs2s,59015
125
- langroid-0.33.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
126
- langroid-0.33.9.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
127
- langroid-0.33.9.dist-info/RECORD,,
124
+ langroid-0.33.11.dist-info/METADATA,sha256=A-tOfJCvDuYvM96q3zqx6NnATzEflj7mdfoEX64XV4A,59016
125
+ langroid-0.33.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
126
+ langroid-0.33.11.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
127
+ langroid-0.33.11.dist-info/RECORD,,