langroid 0.23.1__py3-none-any.whl → 0.23.2__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/special/sql/sql_chat_agent.py +132 -149
- {langroid-0.23.1.dist-info → langroid-0.23.2.dist-info}/METADATA +1 -1
- {langroid-0.23.1.dist-info → langroid-0.23.2.dist-info}/RECORD +6 -6
- pyproject.toml +1 -1
- {langroid-0.23.1.dist-info → langroid-0.23.2.dist-info}/LICENSE +0 -0
- {langroid-0.23.1.dist-info → langroid-0.23.2.dist-info}/WHEEL +0 -0
@@ -12,8 +12,6 @@ from typing import Any, Dict, List, Optional, Sequence, Union
|
|
12
12
|
|
13
13
|
from rich.console import Console
|
14
14
|
|
15
|
-
from langroid import Entity
|
16
|
-
from langroid.agent.tools import DonePassTool
|
17
15
|
from langroid.exceptions import LangroidImportError
|
18
16
|
from langroid.utils.constants import SEND_TO
|
19
17
|
|
@@ -44,7 +42,6 @@ from langroid.agent.special.sql.utils.tools import (
|
|
44
42
|
GetTableSchemaTool,
|
45
43
|
RunQueryTool,
|
46
44
|
)
|
47
|
-
from langroid.agent.task import Task, TaskConfig
|
48
45
|
from langroid.agent.tools.orchestration import (
|
49
46
|
DoneTool,
|
50
47
|
ForwardTool,
|
@@ -100,6 +97,7 @@ class SQLChatAgentConfig(ChatAgentConfig):
|
|
100
97
|
user_message: None | str = None
|
101
98
|
cache: bool = True # cache results
|
102
99
|
debug: bool = False
|
100
|
+
use_helper: bool = True
|
103
101
|
is_helper: bool = False
|
104
102
|
stream: bool = True # allow streaming where needed
|
105
103
|
database_uri: str = "" # Database URI
|
@@ -168,8 +166,22 @@ class SQLChatAgent(ChatAgent):
|
|
168
166
|
self._init_database()
|
169
167
|
self._init_metadata()
|
170
168
|
self._init_table_metadata()
|
171
|
-
|
172
|
-
|
169
|
+
self.final_instructions = ""
|
170
|
+
|
171
|
+
# Caution - this updates the self.config.system_message!
|
172
|
+
self._init_system_message()
|
173
|
+
super().__init__(config)
|
174
|
+
self._init_tools()
|
175
|
+
if self.config.is_helper:
|
176
|
+
self.system_tool_format_instructions += self.final_instructions
|
177
|
+
|
178
|
+
if self.config.use_helper:
|
179
|
+
# helper_config.system_message is now the fully-populated sys msg of
|
180
|
+
# the main SQLAgent.
|
181
|
+
self.helper_config = self.config.copy()
|
182
|
+
self.helper_config.is_helper = True
|
183
|
+
self.helper_config.use_helper = False
|
184
|
+
self.helper_agent = SQLHelperAgent(self.helper_config)
|
173
185
|
|
174
186
|
def _validate_config(self, config: "SQLChatAgentConfig") -> None:
|
175
187
|
"""Validate the configuration to ensure all necessary fields are present."""
|
@@ -237,21 +249,21 @@ class SQLChatAgent(ChatAgent):
|
|
237
249
|
self.metadata, self.config.context_descriptions
|
238
250
|
)
|
239
251
|
|
240
|
-
def
|
241
|
-
"""Initialize
|
242
|
-
|
243
|
-
|
244
|
-
self.config.system_message = self.config.system_message.format(mode=message)
|
252
|
+
def _init_system_message(self) -> None:
|
253
|
+
"""Initialize the system message."""
|
254
|
+
message = self._format_message()
|
255
|
+
self.config.system_message = self.config.system_message.format(mode=message)
|
245
256
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
257
|
+
if self.config.chat_mode:
|
258
|
+
self.config.addressing_prefix = self.config.addressing_prefix or SEND_TO
|
259
|
+
self.config.system_message += ADDRESSING_INSTRUCTION.format(
|
260
|
+
prefix=self.config.addressing_prefix
|
261
|
+
)
|
262
|
+
else:
|
263
|
+
self.config.system_message += DONE_INSTRUCTION
|
253
264
|
|
254
|
-
|
265
|
+
def _init_tools(self) -> None:
|
266
|
+
"""Initialize sys msg and tools."""
|
255
267
|
self.enable_message([RunQueryTool, ForwardTool])
|
256
268
|
if self.config.use_schema_tools:
|
257
269
|
self._enable_schema_tools()
|
@@ -292,17 +304,16 @@ class SQLChatAgent(ChatAgent):
|
|
292
304
|
self.used_run_query = False
|
293
305
|
return super().user_response(msg)
|
294
306
|
|
295
|
-
def
|
307
|
+
def _clarify_answer_instruction(self) -> str:
|
308
|
+
"""
|
309
|
+
Prompt to use when asking LLM to clarify intent of
|
310
|
+
an already-generated response
|
311
|
+
"""
|
296
312
|
if self.config.chat_mode:
|
297
313
|
return f"""
|
298
314
|
you must use the `{ForwardTool.name()}` with the `agent`
|
299
315
|
parameter set to "User"
|
300
316
|
"""
|
301
|
-
elif helper:
|
302
|
-
return f"""
|
303
|
-
you must use the `{DonePassTool.name()}` to pass the answer intact,
|
304
|
-
REMEMBER to set the `request` parameter to "{DonePassTool.name()}"
|
305
|
-
"""
|
306
317
|
else:
|
307
318
|
return f"""
|
308
319
|
you must use the `{DoneTool.name()}` with the `content`
|
@@ -319,30 +330,17 @@ class SQLChatAgent(ChatAgent):
|
|
319
330
|
OR you may want to use one of the schema tools to
|
320
331
|
explore the database schema
|
321
332
|
"""
|
322
|
-
if self.config.chat_mode:
|
323
|
-
return f"""
|
324
|
-
Since you did not explicitly address the User, it is not clear
|
325
|
-
whether:
|
326
|
-
- you intend this to be the final response to the
|
327
|
-
user's query/request, in which case
|
328
|
-
{self._answer_instruction()}
|
329
|
-
- OR, you FORGOT to use an Appropriate TOOL,
|
330
|
-
in which case you should use the available tools to
|
331
|
-
make progress on the user's query/request.
|
332
|
-
{tools_instruction}
|
333
|
-
"""
|
334
|
-
|
335
333
|
return f"""
|
336
334
|
The intent of your response is not clear:
|
337
335
|
- if you intended this to be the FINAL answer to the user's query,
|
338
|
-
{self.
|
336
|
+
{self._clarify_answer_instruction()}
|
339
337
|
- otherwise, use one of the available tools to make progress
|
340
338
|
to arrive at the final answer.
|
341
339
|
{tools_instruction}
|
342
340
|
"""
|
343
341
|
|
344
342
|
def handle_message_fallback(
|
345
|
-
self,
|
343
|
+
self, message: str | ChatDocument
|
346
344
|
) -> str | ForwardTool | ChatDocument | None:
|
347
345
|
"""
|
348
346
|
Handle the scenario where current msg is not a tool.
|
@@ -352,9 +350,22 @@ class SQLChatAgent(ChatAgent):
|
|
352
350
|
if not self.llm_responded:
|
353
351
|
return None
|
354
352
|
if self.interactive:
|
353
|
+
# self.interactive will be set to True by the Task,
|
354
|
+
# when chat_mode=True, so in this case
|
355
|
+
# we send any Non-tool msg to the user
|
355
356
|
return ForwardTool(agent="User")
|
356
|
-
|
357
|
-
|
357
|
+
# Agent intent not clear => use the helper agent to
|
358
|
+
# do what this agent should have done, e.g. generate tool, etc.
|
359
|
+
# This is likelier to succeed since this agent has no "baggage" of
|
360
|
+
# prior conversation, other than the system msg, and special
|
361
|
+
# "Intent-interpretation" instructions.
|
362
|
+
response = self.helper_agent.llm_response(message)
|
363
|
+
tools = self.try_get_tool_messages(response)
|
364
|
+
if tools:
|
365
|
+
return response
|
366
|
+
else:
|
367
|
+
# fall back on the clarification message
|
368
|
+
return self._clarifying_message()
|
358
369
|
|
359
370
|
def retry_query(self, e: Exception, query: str) -> str:
|
360
371
|
"""
|
@@ -402,6 +413,24 @@ class SQLChatAgent(ChatAgent):
|
|
402
413
|
]
|
403
414
|
)
|
404
415
|
|
416
|
+
def _tool_result_llm_answer_prompt(self) -> str:
|
417
|
+
"""
|
418
|
+
Prompt to use at end of tool result,
|
419
|
+
to guide LLM, for the case where it wants to answer the user's query
|
420
|
+
"""
|
421
|
+
if self.config.chat_mode:
|
422
|
+
assert self.config.addressing_prefix != ""
|
423
|
+
return """
|
424
|
+
You must EXPLICITLY address the User with
|
425
|
+
the addressing prefix according to your instructions,
|
426
|
+
to convey your answer to the User.
|
427
|
+
"""
|
428
|
+
else:
|
429
|
+
return f"""
|
430
|
+
you must use the `{DoneTool.name()}` with the `content`
|
431
|
+
set to the answer or result
|
432
|
+
"""
|
433
|
+
|
405
434
|
def run_query(self, msg: RunQueryTool) -> str:
|
406
435
|
"""
|
407
436
|
Handle a RunQueryTool message by executing a SQL query and returning the result.
|
@@ -446,8 +475,7 @@ class SQLChatAgent(ChatAgent):
|
|
446
475
|
================
|
447
476
|
|
448
477
|
If you are READY to ANSWER the ORIGINAL QUERY:
|
449
|
-
|
450
|
-
with the `content` set to the answer or result
|
478
|
+
{self._tool_result_llm_answer_prompt()}
|
451
479
|
OTHERWISE:
|
452
480
|
continue using one of your available TOOLs:
|
453
481
|
{self._available_tool_names()}
|
@@ -523,107 +551,57 @@ class SQLChatAgent(ChatAgent):
|
|
523
551
|
|
524
552
|
class SQLHelperAgent(SQLChatAgent):
|
525
553
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
) -> Optional[ChatDocument]:
|
531
|
-
if message is None:
|
532
|
-
return None
|
533
|
-
message_str = message if isinstance(message, str) else message.content
|
534
|
-
instruc_msg = f"""
|
535
|
-
Below is the MESSAGE from the SQL Agent.
|
536
|
-
Remember you instructions on how to respond based on your understanding
|
537
|
-
of the INTENT of this message:
|
538
|
-
{self.final_instructions}
|
539
|
-
|
540
|
-
=== AGENT MESSAGE =========
|
541
|
-
{message_str}
|
542
|
-
=== END OF AGENT MESSAGE ===
|
543
|
-
"""
|
544
|
-
return super().llm_response(instruc_msg)
|
545
|
-
|
546
|
-
def handle_message_fallback(
|
547
|
-
self, msg: str | ChatDocument
|
548
|
-
) -> str | ForwardTool | ChatDocument | None:
|
549
|
-
# Helper is disabled from handling any tools, so we always come here.
|
550
|
-
if isinstance(msg, str) or msg.metadata.sender != Entity.LLM:
|
551
|
-
return None
|
552
|
-
# force it to populate msg.tool_messages,
|
553
|
-
# since helper is disabling from handling tools.
|
554
|
-
# We use all_tools to have it return all tools recognized
|
555
|
-
# in the msg and _known_ to the helper (i.e. enabled with use=F, handle=F)
|
556
|
-
# and populate msg.tool_messages with these.
|
557
|
-
tools = self.try_get_tool_messages(msg, all_tools=True)
|
558
|
-
msg.tool_messages = tools
|
559
|
-
if any(isinstance(tool, DonePassTool) for tool in tools):
|
560
|
-
return msg
|
561
|
-
elif any(isinstance(tool, PassTool) for tool in tools):
|
562
|
-
# PassTool is just a dummy tool to indicate
|
563
|
-
# that the helper wasn't able to figure out intent,
|
564
|
-
# so send the basic clarifying msg, so main agent retries
|
565
|
-
return self._clarifying_message()
|
566
|
-
elif len(tools) > 0 or DonePassTool.name() in msg.content:
|
567
|
-
# either there are some sql tools, or there was an attempt to use
|
568
|
-
# DonePassTool, so send a proper DonePassTool
|
569
|
-
return DonePassTool().response(self, msg)
|
570
|
-
else:
|
571
|
-
return self._clarifying_message()
|
572
|
-
|
573
|
-
|
574
|
-
def make_sql_chat_task(
|
575
|
-
config: SQLChatAgentConfig,
|
576
|
-
interactive: bool = True,
|
577
|
-
use_helper: bool = False,
|
578
|
-
) -> Task:
|
579
|
-
|
580
|
-
task_config = TaskConfig()
|
581
|
-
|
582
|
-
if interactive:
|
583
|
-
config.chat_mode = True
|
584
|
-
config.addressing_prefix = SEND_TO
|
585
|
-
task_config.addressing_prefix = SEND_TO
|
586
|
-
|
587
|
-
sql_agent = SQLChatAgent(config)
|
588
|
-
sql_task = Task(
|
589
|
-
sql_agent,
|
590
|
-
interactive=False,
|
591
|
-
config=task_config,
|
592
|
-
only_user_quits_root=interactive,
|
593
|
-
)
|
594
|
-
|
595
|
-
if use_helper:
|
596
|
-
setattr(sql_agent, "handle_message_fallback", lambda msg: None)
|
597
|
-
helper_config = config.copy()
|
598
|
-
helper_config.name = "Helper"
|
599
|
-
helper_config.is_helper = True
|
600
|
-
helper_config.system_message = f"""
|
601
|
-
You role is to help INTERPRET the INTENT of an AI agent in a conversation.
|
602
|
-
Given this Agent's message, you must generate the appropriate TOOL
|
603
|
-
based on your understanding of the agent's INTENT. Below are the instructions
|
604
|
-
that were given to this Agent.
|
605
|
-
===== AGENT INSTRUCTIONS =====
|
606
|
-
{sql_agent.config.system_message}
|
607
|
-
===== END OF AGENT INSTRUCTIONS =====
|
554
|
+
def _clarifying_message(self) -> str:
|
555
|
+
tools_instruction = f"""
|
556
|
+
For example the Agent may have forgotten to use the TOOL
|
557
|
+
`{RunQueryTool.name()}` to further explore the database contents
|
608
558
|
"""
|
559
|
+
if self.config.use_schema_tools:
|
560
|
+
tools_instruction += """
|
561
|
+
OR the agent may have forgotten to use one of the schema tools to
|
562
|
+
explore the database schema
|
563
|
+
"""
|
609
564
|
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
565
|
+
return f"""
|
566
|
+
The intent of the Agent's response is not clear:
|
567
|
+
- if you think the Agent intended this as ANSWER to the
|
568
|
+
user's query,
|
569
|
+
{self._clarify_answer_instruction()}
|
570
|
+
- otherwise, the Agent may have forgotten to
|
571
|
+
use one of the available tools to make progress
|
572
|
+
to arrive at the final answer.
|
573
|
+
{tools_instruction}
|
574
|
+
"""
|
616
575
|
|
617
|
-
|
576
|
+
def _init_system_message(self) -> None:
|
577
|
+
"""Set up helper sys msg"""
|
578
|
+
|
579
|
+
# Note that self.config.system_message is already set to the
|
580
|
+
# parent SQLAgent's system_message
|
581
|
+
self.config.system_message = f"""
|
582
|
+
You role is to help INTERPRET the INTENT of an
|
583
|
+
AI agent in a conversation. This Agent was supposed to generate
|
584
|
+
a TOOL/Function-call but forgot to do so, and this is where
|
585
|
+
you can help, by trying to generate the appropriate TOOL
|
586
|
+
based on your best guess of the Agent's INTENT.
|
587
|
+
|
588
|
+
Below are the instructions that were given to this Agent:
|
589
|
+
===== AGENT INSTRUCTIONS =====
|
590
|
+
{self.config.system_message}
|
591
|
+
===== END OF AGENT INSTRUCTIONS =====
|
592
|
+
"""
|
618
593
|
|
619
|
-
|
594
|
+
# note that the initial msg in chat history will contain:
|
595
|
+
# - system message
|
596
|
+
# - tool instructions
|
597
|
+
# so the final_instructions will be at the end of this initial msg
|
620
598
|
|
621
|
-
|
599
|
+
self.final_instructions = f"""
|
622
600
|
You must take note especially of the TOOLs that are
|
623
|
-
available to the
|
601
|
+
available to the Agent. Your reasoning process should be as follows:
|
624
602
|
|
625
603
|
- If the Agent's message appears to be an ANSWER to the original query,
|
626
|
-
{
|
604
|
+
{self._clarify_answer_instruction()}.
|
627
605
|
CAUTION - You must be absolutely sure that the Agent's message is
|
628
606
|
an ACTUAL ANSWER to the user's query, and not a failed attempt to use
|
629
607
|
a TOOL without JSON, e.g. something like "run_query" or "done_tool"
|
@@ -642,16 +620,21 @@ def make_sql_chat_task(
|
|
642
620
|
is NEITHER an ANSWER, nor an intended SQL QUERY.
|
643
621
|
"""
|
644
622
|
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
623
|
+
def llm_response(
|
624
|
+
self, message: Optional[str | ChatDocument] = None
|
625
|
+
) -> Optional[ChatDocument]:
|
626
|
+
if message is None:
|
627
|
+
return None
|
628
|
+
message_str = message if isinstance(message, str) else message.content
|
629
|
+
instruc_msg = f"""
|
630
|
+
Below is the MESSAGE from the SQL Agent.
|
631
|
+
Remember your instructions on how to respond based on your understanding
|
632
|
+
of the INTENT of this message:
|
633
|
+
{self.final_instructions}
|
634
|
+
|
635
|
+
=== AGENT MESSAGE =========
|
636
|
+
{message_str}
|
637
|
+
=== END OF AGENT MESSAGE ===
|
638
|
+
"""
|
639
|
+
# user response_forget to avoid accumulating the chat history
|
640
|
+
return super().llm_response_forget(instruc_msg)
|
@@ -30,7 +30,7 @@ langroid/agent/special/neo4j/tools.py,sha256=Vw3HvtDfG2c4_bUHgt4_ZbJq48lpIQstbjj
|
|
30
30
|
langroid/agent/special/relevance_extractor_agent.py,sha256=zIx8GUdVo1aGW6ASla0NPQjYYIpmriK_TYMijqAx3F8,4796
|
31
31
|
langroid/agent/special/retriever_agent.py,sha256=lvMvf-u9rSosg4YASuFdUbGLgkzLPknXAbJZfZ1LZCc,1868
|
32
32
|
langroid/agent/special/sql/__init__.py,sha256=mWfmm1QpXCezpFOS2eI57M0L_Ok3q5_ukG8tXBnBrEA,319
|
33
|
-
langroid/agent/special/sql/sql_chat_agent.py,sha256=
|
33
|
+
langroid/agent/special/sql/sql_chat_agent.py,sha256=EsoF5_kheqhpiJw2wZs_6sgPfD0Or1YvfR5v2h6z74E,24094
|
34
34
|
langroid/agent/special/sql/utils/__init__.py,sha256=JFif6CRTrN-bc91uuAI4K9fe2ndIWSNMVxJ0WA68--M,446
|
35
35
|
langroid/agent/special/sql/utils/description_extractors.py,sha256=cX8TIpmTPXZXQTMpIi3OUFwFsPywxFFdurpx717Kq0I,6529
|
36
36
|
langroid/agent/special/sql/utils/populate_metadata.py,sha256=1J22UsyEPKzwK0XlJZtYn9r6kYc0FXIr8-lZrndYlhc,3131
|
@@ -142,8 +142,8 @@ langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3Hmh
|
|
142
142
|
langroid/vector_store/momento.py,sha256=qR-zBF1RKVHQZPZQYW_7g-XpTwr46p8HJuYPCkfJbM4,10534
|
143
143
|
langroid/vector_store/qdrant_cloud.py,sha256=3im4Mip0QXLkR6wiqVsjV1QvhSElfxdFSuDKddBDQ-4,188
|
144
144
|
langroid/vector_store/qdrantdb.py,sha256=v88lqFkepADvlN6lByUj9I4NEKa9X9lWH16uTPPbYrE,17457
|
145
|
-
pyproject.toml,sha256=
|
146
|
-
langroid-0.23.
|
147
|
-
langroid-0.23.
|
148
|
-
langroid-0.23.
|
149
|
-
langroid-0.23.
|
145
|
+
pyproject.toml,sha256=L8FRAfEnw4iobtDdufbCBvb4JoxH-oSYMhRcePfzii0,7488
|
146
|
+
langroid-0.23.2.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
147
|
+
langroid-0.23.2.dist-info/METADATA,sha256=U_J275x-bp2csL8EL6xK41m1r3q_aLDywDTBeutWtk0,57300
|
148
|
+
langroid-0.23.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
149
|
+
langroid-0.23.2.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|