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 +5 -0
- langroid/agent/chat_agent.py +58 -20
- langroid/agent/special/sql/sql_chat_agent.py +36 -19
- langroid/agent/task.py +9 -2
- langroid/agent/tool_message.py +1 -1
- langroid/agent/tools/orchestration.py +1 -1
- {langroid-0.33.9.dist-info → langroid-0.33.11.dist-info}/METADATA +1 -1
- {langroid-0.33.9.dist-info → langroid-0.33.11.dist-info}/RECORD +10 -10
- {langroid-0.33.9.dist-info → langroid-0.33.11.dist-info}/WHEEL +0 -0
- {langroid-0.33.9.dist-info → langroid-0.33.11.dist-info}/licenses/LICENSE +0 -0
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
|
langroid/agent/chat_agent.py
CHANGED
@@ -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
|
-
|
1058
|
-
|
1059
|
-
|
1060
|
-
|
1061
|
-
|
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
|
-
|
1225
|
-
elif isinstance(message, str):
|
1226
|
-
message = message + recovery_message
|
1254
|
+
self.delete_last_message(role=Role.USER)
|
1227
1255
|
else:
|
1228
|
-
|
1229
|
-
|
1230
|
-
return
|
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
|
-
|
1300
|
-
elif isinstance(message, str):
|
1301
|
-
message = message + recovery_message
|
1339
|
+
self.delete_last_message(role=Role.USER)
|
1302
1340
|
else:
|
1303
|
-
|
1304
|
-
|
1305
|
-
return
|
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
|
-
|
348
|
-
|
349
|
-
|
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
|
356
|
+
if (
|
357
|
+
not isinstance(message, ChatDocument)
|
358
|
+
or message.metadata.sender != Entity.LLM
|
359
|
+
):
|
352
360
|
return None
|
353
|
-
if self.
|
354
|
-
#
|
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
|
-
|
376
|
-
|
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
|
-
|
382
|
-
|
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
|
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(
|
langroid/agent/tool_message.py
CHANGED
@@ -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
|
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
|
-
`
|
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
|
@@ -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
|
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=
|
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=
|
12
|
-
langroid/agent/tool_message.py,sha256=
|
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=
|
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=
|
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.
|
125
|
-
langroid-0.33.
|
126
|
-
langroid-0.33.
|
127
|
-
langroid-0.33.
|
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,,
|
File without changes
|
File without changes
|