langroid 0.1.60__tar.gz → 0.1.62__tar.gz
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-0.1.60 → langroid-0.1.62}/PKG-INFO +2 -2
- {langroid-0.1.60 → langroid-0.1.62}/README.md +1 -1
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/base.py +101 -37
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/chat_agent.py +93 -89
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/chat_document.py +2 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/task.py +20 -15
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/tool_message.py +1 -1
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/prompt_formatter/llama2_formatter.py +2 -2
- {langroid-0.1.60 → langroid-0.1.62}/langroid/mytypes.py +1 -0
- {langroid-0.1.60 → langroid-0.1.62}/pyproject.toml +1 -1
- langroid-0.1.62/setup.py +99 -0
- langroid-0.1.60/setup.py +0 -99
- {langroid-0.1.60 → langroid-0.1.62}/LICENSE +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/helpers.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/junk +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/doc_chat_agent.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/recipient_validator_agent.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/retriever_agent.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/sql/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/sql/utils/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/sql/utils/system_message.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/sql/utils/tools.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/special/table_chat_agent.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/tools/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/tools/google_search_tool.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent/tools/recipient_tool.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/agent_config.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/cachedb/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/cachedb/base.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/cachedb/momento_cachedb.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/cachedb/redis_cachedb.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/embedding_models/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/embedding_models/base.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/embedding_models/clustering.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/embedding_models/models.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/azure_openai.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/base.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/config.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/openai_gpt.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/prompt_formatter/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/prompt_formatter/base.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/utils.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/agent_chats.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/code-parsing.md +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/code_parser.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/json.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/para_sentence_split.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/parser.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/pdf_parser.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/repo_loader.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/table_loader.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/url_loader.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/url_loader_cookies.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/urls.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/utils.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/parsing/web_search.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/prompts/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/prompts/dialog.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/prompts/prompts_config.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/prompts/templates.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/prompts/transforms.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/scripts/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/configuration.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/constants.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/docker.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/globals.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/llms/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/llms/strings.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/logging.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/output/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/output/printing.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/pydantic_utils.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/system.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/web/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/web/login.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/utils/web/selenium_login.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/vector_store/__init__.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/vector_store/base.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/vector_store/chromadb.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/vector_store/qdrant_cloud.py +0 -0
- {langroid-0.1.60 → langroid-0.1.62}/langroid/vector_store/qdrantdb.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.62
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
License: MIT
|
6
6
|
Author: Prasad Chalasani
|
@@ -138,7 +138,7 @@ for ideas on what to contribute.
|
|
138
138
|
<summary> <b>:fire: Updates/Releases</b></summary>
|
139
139
|
|
140
140
|
- **Sep 2023:**
|
141
|
-
- **Use with local LLama Models:** see tutorial [here](https://langroid.github.io/langroid/
|
141
|
+
- **Use with local LLama Models:** see tutorial [here](https://langroid.github.io/langroid/blog/2023/09/14/using-langroid-with-local-llms/)
|
142
142
|
- **Langroid Blog/Newsletter Launched!**: First post is [here](https://substack.com/notes/post/p-136704592) -- Please subscribe to stay updated.
|
143
143
|
- **0.1.56:** Support Azure OpenAI.
|
144
144
|
- **0.1.55:** Improved [`SQLChatAgent`](https://github.com/langroid/langroid/blob/main/langroid/agent/special/sql/sql_chat_agent.py) that efficiently retrieves relevant schema info when translating natural language to SQL.
|
@@ -67,7 +67,7 @@ for ideas on what to contribute.
|
|
67
67
|
<summary> <b>:fire: Updates/Releases</b></summary>
|
68
68
|
|
69
69
|
- **Sep 2023:**
|
70
|
-
- **Use with local LLama Models:** see tutorial [here](https://langroid.github.io/langroid/
|
70
|
+
- **Use with local LLama Models:** see tutorial [here](https://langroid.github.io/langroid/blog/2023/09/14/using-langroid-with-local-llms/)
|
71
71
|
- **Langroid Blog/Newsletter Launched!**: First post is [here](https://substack.com/notes/post/p-136704592) -- Please subscribe to stay updated.
|
72
72
|
- **0.1.56:** Support Azure OpenAI.
|
73
73
|
- **0.1.55:** Improved [`SQLChatAgent`](https://github.com/langroid/langroid/blob/main/langroid/agent/special/sql/sql_chat_agent.py) that efficiently retrieves relevant schema info when translating natural language to SQL.
|
@@ -212,7 +212,7 @@ class Agent(ABC):
|
|
212
212
|
def json_format_rules(self) -> str:
|
213
213
|
"""
|
214
214
|
Specification of JSON formatting rules, based on the currently enabled
|
215
|
-
|
215
|
+
usable `ToolMessage`s
|
216
216
|
|
217
217
|
Returns:
|
218
218
|
str: formatting rules
|
@@ -221,7 +221,7 @@ class Agent(ABC):
|
|
221
221
|
if len(enabled_classes) == 0:
|
222
222
|
return "You can ask questions in natural language."
|
223
223
|
|
224
|
-
|
224
|
+
json_instructions = "\n\n".join(
|
225
225
|
[
|
226
226
|
str(msg_cls.default_value("request"))
|
227
227
|
+ ":\n"
|
@@ -230,7 +230,53 @@ class Agent(ABC):
|
|
230
230
|
if msg_cls.default_value("request") in self.llm_tools_usable
|
231
231
|
]
|
232
232
|
)
|
233
|
-
return
|
233
|
+
return textwrap.dedent(
|
234
|
+
f"""
|
235
|
+
=== ALL AVAILABLE TOOLS and THEIR JSON FORMAT INSTRUCTIONS ===
|
236
|
+
You have access to the following TOOLS to accomplish your task:
|
237
|
+
|
238
|
+
{json_instructions}
|
239
|
+
|
240
|
+
{INSTRUCTION}
|
241
|
+
----------------------------
|
242
|
+
""".lstrip()
|
243
|
+
)
|
244
|
+
|
245
|
+
def tool_instructions(self) -> str:
|
246
|
+
"""
|
247
|
+
Instructions (defined via `instructions` classmethod in a
|
248
|
+
ToolMessage class) for currently enabled and usable Tools.
|
249
|
+
|
250
|
+
Returns:
|
251
|
+
str: concatenation of instructions for all usable tools
|
252
|
+
"""
|
253
|
+
enabled_classes: List[Type[ToolMessage]] = list(self.llm_tools_map.values())
|
254
|
+
if len(enabled_classes) == 0:
|
255
|
+
return ""
|
256
|
+
instructions = []
|
257
|
+
for msg_cls in enabled_classes:
|
258
|
+
if (
|
259
|
+
hasattr(msg_cls, "instructions")
|
260
|
+
and inspect.ismethod(msg_cls.instructions)
|
261
|
+
and msg_cls.default_value("request") in self.llm_tools_usable
|
262
|
+
):
|
263
|
+
instructions.append(
|
264
|
+
textwrap.dedent(
|
265
|
+
f"""
|
266
|
+
{msg_cls.default_value("request")}:
|
267
|
+
{msg_cls.instructions()}
|
268
|
+
""".lstrip()
|
269
|
+
)
|
270
|
+
)
|
271
|
+
if len(instructions) == 0:
|
272
|
+
return ""
|
273
|
+
instructions_str = "\n\n".join(instructions)
|
274
|
+
return textwrap.dedent(
|
275
|
+
f"""
|
276
|
+
=== GUIDELINES ON SOME TOOLS/FUNCTIONS USAGE ===
|
277
|
+
{instructions_str}
|
278
|
+
""".lstrip()
|
279
|
+
)
|
234
280
|
|
235
281
|
def sample_multi_round_dialog(self) -> str:
|
236
282
|
"""
|
@@ -247,26 +293,6 @@ class Agent(ABC):
|
|
247
293
|
]
|
248
294
|
return "\n\n".join(sample_convo)
|
249
295
|
|
250
|
-
def json_tool_format_instructions(self) -> str:
|
251
|
-
"""
|
252
|
-
Generate a string containing instructions to the LLM on when to format
|
253
|
-
requests/questions as JSON, based on the currently enabled message classes.
|
254
|
-
|
255
|
-
Returns:
|
256
|
-
str: The instructions string.
|
257
|
-
"""
|
258
|
-
format_rules = self.json_format_rules()
|
259
|
-
|
260
|
-
return f"""
|
261
|
-
You have access to the following TOOLS to accomplish your task:
|
262
|
-
TOOLS AVAILABLE:
|
263
|
-
{format_rules}
|
264
|
-
|
265
|
-
{INSTRUCTION}
|
266
|
-
|
267
|
-
Now start, and be concise!
|
268
|
-
"""
|
269
|
-
|
270
296
|
def agent_response(
|
271
297
|
self,
|
272
298
|
msg: Optional[str | ChatDocument] = None,
|
@@ -339,11 +365,18 @@ class Agent(ABC):
|
|
339
365
|
if not user_msg:
|
340
366
|
return None
|
341
367
|
else:
|
368
|
+
if user_msg.startswith("SYSTEM"):
|
369
|
+
user_msg = user_msg[6:].strip()
|
370
|
+
source = Entity.SYSTEM
|
371
|
+
sender = Entity.SYSTEM
|
372
|
+
else:
|
373
|
+
source = Entity.USER
|
374
|
+
sender = Entity.USER
|
342
375
|
return ChatDocument(
|
343
376
|
content=user_msg,
|
344
377
|
metadata=DocMetaData(
|
345
|
-
source=
|
346
|
-
sender=
|
378
|
+
source=source,
|
379
|
+
sender=sender,
|
347
380
|
),
|
348
381
|
)
|
349
382
|
|
@@ -434,7 +467,12 @@ class Agent(ABC):
|
|
434
467
|
console.print(f"[green]{self.indent}", end="")
|
435
468
|
print("[green]" + response.message)
|
436
469
|
displayed = True
|
437
|
-
self.update_token_usage(
|
470
|
+
self.update_token_usage(
|
471
|
+
response,
|
472
|
+
prompt,
|
473
|
+
self.llm.get_stream(),
|
474
|
+
print_response_stats=settings.debug,
|
475
|
+
)
|
438
476
|
return ChatDocument.from_LLMResponse(response, displayed)
|
439
477
|
|
440
478
|
def get_tool_messages(self, msg: str | ChatDocument) -> List[ToolMessage]:
|
@@ -619,8 +657,39 @@ class Agent(ABC):
|
|
619
657
|
else:
|
620
658
|
return sum([self.parser.num_tokens(m.content) for m in prompt])
|
621
659
|
|
660
|
+
def _print_response_stats(
|
661
|
+
self, chat_length: int, tot_cost: float, response: LLMResponse
|
662
|
+
) -> None:
|
663
|
+
"""
|
664
|
+
Printing LLM response stats.
|
665
|
+
|
666
|
+
Args:
|
667
|
+
chat_length (int): number of messages in the chat
|
668
|
+
tot_cost (float): total cost of the chat so far
|
669
|
+
response (LLMResponse): LLMResponse object
|
670
|
+
"""
|
671
|
+
if response.usage:
|
672
|
+
in_tokens = response.usage.prompt_tokens
|
673
|
+
out_tokens = response.usage.completion_tokens
|
674
|
+
llm_response_cost = format(response.usage.cost, ".4f")
|
675
|
+
cumul_cost = format(tot_cost, ".4f")
|
676
|
+
assert isinstance(self.llm, LanguageModel)
|
677
|
+
context_length = self.llm.chat_context_length()
|
678
|
+
max_out = self.config.llm.max_output_tokens
|
679
|
+
print(
|
680
|
+
f"{self.indent}[bold]Stats:[/bold] [magenta] N_MSG={chat_length}, "
|
681
|
+
f"TOKENS: in={in_tokens}, out={out_tokens}, "
|
682
|
+
f"max={max_out}, ctx={context_length}, "
|
683
|
+
f"COST: now=${llm_response_cost}, cumul=${cumul_cost}[/magenta]"
|
684
|
+
""
|
685
|
+
)
|
686
|
+
|
622
687
|
def update_token_usage(
|
623
|
-
self,
|
688
|
+
self,
|
689
|
+
response: LLMResponse,
|
690
|
+
prompt: str | List[LLMMessage],
|
691
|
+
stream: bool,
|
692
|
+
print_response_stats: bool = True,
|
624
693
|
) -> None:
|
625
694
|
"""
|
626
695
|
Updates `response.usage` obj (token usage and cost fields).the usage memebr
|
@@ -653,20 +722,15 @@ class Agent(ABC):
|
|
653
722
|
cost=cost,
|
654
723
|
)
|
655
724
|
|
656
|
-
if settings.debug and response.usage is not None:
|
657
|
-
print(
|
658
|
-
textwrap.dedent(
|
659
|
-
f"""
|
660
|
-
Stream: {stream}
|
661
|
-
prompt_tokens: {response.usage.prompt_tokens}
|
662
|
-
completion_tokens: {response.usage.completion_tokens}
|
663
|
-
""".lstrip()
|
664
|
-
)
|
665
|
-
)
|
666
725
|
# update total counters
|
667
726
|
if response.usage is not None:
|
668
727
|
self.total_llm_token_cost += response.usage.cost
|
669
728
|
self.total_llm_token_usage += response.usage.total_tokens
|
729
|
+
chat_length = 1 if isinstance(prompt, str) else len(prompt)
|
730
|
+
if print_response_stats:
|
731
|
+
self._print_response_stats(
|
732
|
+
chat_length, self.total_llm_token_cost, response
|
733
|
+
)
|
670
734
|
|
671
735
|
def compute_token_cost(self, prompt: int, completion: int) -> float:
|
672
736
|
price = cast(LanguageModel, self.llm).chat_cost()
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import inspect
|
2
1
|
import logging
|
2
|
+
import textwrap
|
3
3
|
from contextlib import ExitStack
|
4
4
|
from typing import Dict, List, Optional, Set, Type, cast, no_type_check
|
5
5
|
|
@@ -28,8 +28,10 @@ class ChatAgentConfig(AgentConfig):
|
|
28
28
|
Configuration for ChatAgent
|
29
29
|
Attributes:
|
30
30
|
system_message: system message to include in message sequence
|
31
|
-
(typically defines role and task of agent)
|
32
|
-
|
31
|
+
(typically defines role and task of agent).
|
32
|
+
Used only if `task` is not specified in the constructor.
|
33
|
+
user_message: user message to include in message sequence.
|
34
|
+
Used only if `task` is not specified in the constructor.
|
33
35
|
use_tools: whether to use our own ToolMessages mechanism
|
34
36
|
use_functions_api: whether to use functions native to the LLM API
|
35
37
|
(e.g. OpenAI's `function_call` mechanism)
|
@@ -70,23 +72,53 @@ class ChatAgent(Agent):
|
|
70
72
|
self.config: ChatAgentConfig = config
|
71
73
|
self.message_history: List[LLMMessage] = []
|
72
74
|
self.tool_instructions_added: bool = False
|
73
|
-
# system
|
75
|
+
# An agent's "task" is defined by a system msg and an optional user msg;
|
76
|
+
# These are "priming" messages that kick off the agent's conversation.
|
77
|
+
self.system_message: str = self.config.system_message
|
78
|
+
self.user_message: str | None = self.config.user_message
|
79
|
+
|
80
|
+
if task is not None:
|
81
|
+
# if task contains a system msg, we override the config system msg
|
82
|
+
if len(task) > 0 and task[0].role == Role.SYSTEM:
|
83
|
+
self.system_message = task[0].content
|
84
|
+
# if task contains a user msg, we override the config user msg
|
85
|
+
if len(task) > 1 and task[1].role == Role.USER:
|
86
|
+
self.user_message = task[1].content
|
87
|
+
|
88
|
+
# system-level instructions for using tools/functions:
|
89
|
+
# We maintain these as tools/functions are enabled/disabled,
|
90
|
+
# and whenever an LLM response is sought, these are used to
|
91
|
+
# recreate the system message (via `_create_system_and_tools_message`)
|
92
|
+
# each time, so it reflects the current set of enabled tools/functions.
|
93
|
+
# (a) these are general instructions on using certain tools/functions,
|
94
|
+
# if they are specified in a ToolMessage class as a classmethod `instructions`
|
74
95
|
self.system_tool_instructions: str = ""
|
96
|
+
# (b) these are only for the builtin in Langroid TOOLS mechanism:
|
97
|
+
self.system_json_tool_instructions: str = ""
|
98
|
+
|
75
99
|
self.llm_functions_map: Dict[str, LLMFunctionSpec] = {}
|
76
100
|
self.llm_functions_handled: Set[str] = set()
|
77
101
|
self.llm_functions_usable: Set[str] = set()
|
78
102
|
self.llm_function_force: Optional[Dict[str, str]] = None
|
79
103
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
104
|
+
def set_system_message(self, msg: str) -> None:
|
105
|
+
self.system_message = msg
|
106
|
+
|
107
|
+
def set_user_message(self, msg: str) -> None:
|
108
|
+
self.user_message = msg
|
109
|
+
|
110
|
+
@property
|
111
|
+
def task_messages(self) -> List[LLMMessage]:
|
112
|
+
"""
|
113
|
+
The task messages are the initial messages that define the task
|
114
|
+
of the agent. There will be at least a system message plus possibly a user msg.
|
115
|
+
Returns:
|
116
|
+
List[LLMMessage]: the task messages
|
117
|
+
"""
|
118
|
+
msgs = [self._create_system_and_tools_message()]
|
119
|
+
if self.user_message:
|
120
|
+
msgs.append(LLMMessage(role=Role.USER, content=self.user_message))
|
121
|
+
return msgs
|
90
122
|
|
91
123
|
def clear_history(self, start: int = -2) -> None:
|
92
124
|
"""
|
@@ -122,31 +154,11 @@ class ChatAgent(Agent):
|
|
122
154
|
Args:
|
123
155
|
message (str): system message
|
124
156
|
"""
|
125
|
-
|
126
|
-
if self.message_history[0].role == Role.SYSTEM:
|
127
|
-
self.message_history[0].content = (
|
128
|
-
self.message_history[0].content + "\n\n" + message
|
129
|
-
)
|
130
|
-
else:
|
131
|
-
if self.task_messages[0].role == Role.SYSTEM:
|
132
|
-
self.task_messages[0].content = (
|
133
|
-
self.task_messages[0].content + "\n\n" + message
|
134
|
-
)
|
135
|
-
|
136
|
-
def add_user_message(self, message: str) -> None:
|
137
|
-
"""
|
138
|
-
Add a user message to the message history.
|
139
|
-
Args:
|
140
|
-
message (str): user message
|
141
|
-
"""
|
142
|
-
if len(self.message_history) > 0:
|
143
|
-
self.message_history.append(LLMMessage(role=Role.USER, content=message))
|
144
|
-
else:
|
145
|
-
self.task_messages.append(LLMMessage(role=Role.USER, content=message))
|
157
|
+
self.system_message += "\n\n" + message
|
146
158
|
|
147
159
|
def update_last_message(self, message: str, role: str = Role.USER) -> None:
|
148
160
|
"""
|
149
|
-
Update the last message
|
161
|
+
Update the last message that has role `role` in the message history.
|
150
162
|
Useful when we want to replace a long user prompt, that may contain context
|
151
163
|
documents plus a question, with just the question.
|
152
164
|
Args:
|
@@ -161,6 +173,33 @@ class ChatAgent(Agent):
|
|
161
173
|
self.message_history[i].content = message
|
162
174
|
break
|
163
175
|
|
176
|
+
def _create_system_and_tools_message(self) -> LLMMessage:
|
177
|
+
"""
|
178
|
+
(Re-)Create the system message for the LLM of the agent,
|
179
|
+
taking into account any tool instructions that have been added
|
180
|
+
after the agent was initialized.
|
181
|
+
|
182
|
+
The system message will consist of:
|
183
|
+
(a) the system message from the `task` arg in constructor, if any,
|
184
|
+
otherwise the default system message from the config
|
185
|
+
(b) the system tool instructions, if any
|
186
|
+
(c) the system json tool instructions, if any
|
187
|
+
|
188
|
+
Returns:
|
189
|
+
LLMMessage object
|
190
|
+
"""
|
191
|
+
content = textwrap.dedent(
|
192
|
+
f"""
|
193
|
+
{self.system_message}
|
194
|
+
|
195
|
+
{self.system_tool_instructions}
|
196
|
+
|
197
|
+
{self.system_json_tool_instructions}
|
198
|
+
|
199
|
+
""".lstrip()
|
200
|
+
)
|
201
|
+
return LLMMessage(role=Role.SYSTEM, content=content)
|
202
|
+
|
164
203
|
def enable_message(
|
165
204
|
self,
|
166
205
|
message_class: Optional[Type[ToolMessage]],
|
@@ -202,15 +241,7 @@ class ChatAgent(Agent):
|
|
202
241
|
self.llm_function_force = dict(name=request)
|
203
242
|
else:
|
204
243
|
self.llm_function_force = None
|
205
|
-
|
206
|
-
message_class.instructions
|
207
|
-
):
|
208
|
-
# save tool instructions so that when LLM is ready
|
209
|
-
# to respond (i.e. either when a wrapping Task is started,
|
210
|
-
# or when the LLM is directly queried),
|
211
|
-
# we can append to system msg.
|
212
|
-
self.system_tool_instructions += "\n\n" + message_class.instructions()
|
213
|
-
n_usable_tools = len(self.llm_tools_usable)
|
244
|
+
|
214
245
|
for t in tools:
|
215
246
|
if handle:
|
216
247
|
self.llm_tools_handled.add(t)
|
@@ -226,12 +257,10 @@ class ChatAgent(Agent):
|
|
226
257
|
self.llm_tools_usable.discard(t)
|
227
258
|
self.llm_functions_usable.discard(t)
|
228
259
|
|
229
|
-
#
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
# Update JSON format instructions if the set of usable tools has changed
|
234
|
-
self.update_json_tool_instructions()
|
260
|
+
# Set tool instructions and JSON format instructions
|
261
|
+
if self.config.use_tools:
|
262
|
+
self.system_json_tool_instructions = self.json_format_rules()
|
263
|
+
self.system_tool_instructions = self.tool_instructions()
|
235
264
|
|
236
265
|
def disable_message_handling(
|
237
266
|
self,
|
@@ -275,37 +304,6 @@ class ChatAgent(Agent):
|
|
275
304
|
self.llm_tools_usable.discard(r)
|
276
305
|
self.llm_functions_usable.discard(r)
|
277
306
|
|
278
|
-
def update_json_tool_instructions(self) -> None:
|
279
|
-
"""
|
280
|
-
Add special instructions on situations when the LLM should send JSON-formatted
|
281
|
-
messages, and save the index position of these instructions in the
|
282
|
-
message history. Note this specifically only relates to the
|
283
|
-
Langroid JSON tools mechanism, not the LLM-native function-calling.
|
284
|
-
"""
|
285
|
-
# Add the instructions as a user message...
|
286
|
-
# TODO need to adapt this based on model type, as some models may
|
287
|
-
# pay more attention to system message.
|
288
|
-
json_instructions = super().json_tool_format_instructions()
|
289
|
-
if not self.tool_instructions_added:
|
290
|
-
self.task_messages.append(
|
291
|
-
LLMMessage(role=Role.USER, content=json_instructions)
|
292
|
-
)
|
293
|
-
self.tool_instructions_added = True
|
294
|
-
else:
|
295
|
-
# json_instructions will contain instructions on ALL tools,
|
296
|
-
# so it is ok to overwrite the last message in the task_messages,
|
297
|
-
# since we know it is a tool-instruction msg.
|
298
|
-
self.task_messages[-1].content = json_instructions
|
299
|
-
|
300
|
-
# Note that task_messages is the initial set of messages created to set up
|
301
|
-
# the task, and they may not yet have been sent to the LLM at this point.
|
302
|
-
|
303
|
-
# But if the task_messages have already been sent to the LLM, then we need to
|
304
|
-
# update the self.message_history as well, since this history will be sent to
|
305
|
-
# the LLM on each round, after appending the latest assistant, user msgs.
|
306
|
-
if len(self.message_history) > 0:
|
307
|
-
self.message_history[-1].content = json_instructions
|
308
|
-
|
309
307
|
@no_type_check
|
310
308
|
def llm_response(
|
311
309
|
self, message: Optional[str | ChatDocument] = None
|
@@ -327,13 +325,13 @@ class ChatAgent(Agent):
|
|
327
325
|
), "message can be None only if message_history is empty, i.e. at start."
|
328
326
|
|
329
327
|
if len(self.message_history) == 0:
|
330
|
-
#
|
331
|
-
self.message_history = self.
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
"\n\n" + self.system_tool_instructions
|
328
|
+
# initial messages have not yet been loaded, so load them
|
329
|
+
self.message_history = [self._create_system_and_tools_message()]
|
330
|
+
if self.user_message:
|
331
|
+
self.message_history.append(
|
332
|
+
LLMMessage(role=Role.USER, content=self.user_message)
|
336
333
|
)
|
334
|
+
|
337
335
|
# for debugging, show the initial message history
|
338
336
|
if settings.debug:
|
339
337
|
print(
|
@@ -342,6 +340,10 @@ class ChatAgent(Agent):
|
|
342
340
|
{self.message_history_str()}
|
343
341
|
"""
|
344
342
|
)
|
343
|
+
else:
|
344
|
+
assert self.message_history[0].role == Role.SYSTEM
|
345
|
+
# update the system message with the latest tool instructions
|
346
|
+
self.message_history[0] = self._create_system_and_tools_message()
|
345
347
|
|
346
348
|
if message is not None:
|
347
349
|
llm_msg = ChatDocument.to_LLMMessage(message)
|
@@ -456,7 +458,9 @@ class ChatAgent(Agent):
|
|
456
458
|
response_str = response.message
|
457
459
|
print(cached + "[green]" + response_str)
|
458
460
|
stream = self.llm.get_stream() # type: ignore
|
459
|
-
self.update_token_usage(
|
461
|
+
self.update_token_usage(
|
462
|
+
response, messages, stream, print_response_stats=settings.debug
|
463
|
+
)
|
460
464
|
return ChatDocument.from_LLMResponse(response, displayed)
|
461
465
|
|
462
466
|
def _llm_response_temp_context(self, message: str, prompt: str) -> ChatDocument:
|
@@ -172,6 +172,8 @@ class ChatDocument(Document):
|
|
172
172
|
content = message.content
|
173
173
|
fun_call = message.function_call
|
174
174
|
sender_name = message.metadata.sender_name
|
175
|
+
if message.metadata.sender == Entity.SYSTEM:
|
176
|
+
sender_role = Role.SYSTEM
|
175
177
|
if (
|
176
178
|
message.metadata.parent is not None
|
177
179
|
and message.metadata.parent.function_call is not None
|
@@ -12,7 +12,6 @@ from langroid.agent.chat_document import (
|
|
12
12
|
ChatDocMetaData,
|
13
13
|
ChatDocument,
|
14
14
|
)
|
15
|
-
from langroid.language_models.base import LLMMessage, Role
|
16
15
|
from langroid.mytypes import Entity
|
17
16
|
from langroid.utils.configuration import settings
|
18
17
|
from langroid.utils.constants import DONE, NO_ANSWER, USER_QUIT
|
@@ -82,8 +81,8 @@ class Task:
|
|
82
81
|
and subsequent response by non-controller. If false, runs for the
|
83
82
|
specified number of turns in `run`, or until `done()` is true.
|
84
83
|
One run of step() is considered a "turn".
|
85
|
-
system_message (str): if not empty, overrides agent's
|
86
|
-
user_message (str): if not empty, overrides agent's
|
84
|
+
system_message (str): if not empty, overrides agent's system_message
|
85
|
+
user_message (str): if not empty, overrides agent's user_message
|
87
86
|
restart (bool): if true, resets the agent's message history
|
88
87
|
default_human_response (str): default response from user; useful for
|
89
88
|
testing, to avoid interactive input from user.
|
@@ -99,18 +98,14 @@ class Task:
|
|
99
98
|
"""
|
100
99
|
if isinstance(agent, ChatAgent) and len(agent.message_history) == 0 or restart:
|
101
100
|
agent = cast(ChatAgent, agent)
|
102
|
-
agent.
|
103
|
-
# possibly change the
|
101
|
+
agent.clear_history(0)
|
102
|
+
# possibly change the system and user messages
|
104
103
|
if system_message:
|
105
104
|
# we always have at least 1 task_message
|
106
|
-
agent.
|
105
|
+
agent.set_system_message(system_message)
|
107
106
|
if user_message:
|
108
|
-
agent.
|
109
|
-
|
110
|
-
role=Role.USER,
|
111
|
-
content=user_message,
|
112
|
-
)
|
113
|
-
)
|
107
|
+
agent.set_user_message(user_message)
|
108
|
+
|
114
109
|
self.logger: None | RichFileLogger = None
|
115
110
|
self.tsv_logger: None | logging.Logger = None
|
116
111
|
self.color_log: bool = True
|
@@ -231,6 +226,8 @@ class Task:
|
|
231
226
|
# the CURRENT task's USER entity
|
232
227
|
self.pending_message.metadata.sender = Entity.USER
|
233
228
|
|
229
|
+
self._show_pending_message_if_debug()
|
230
|
+
|
234
231
|
if self.caller is not None and self.caller.logger is not None:
|
235
232
|
self.logger = self.caller.logger
|
236
233
|
else:
|
@@ -257,8 +254,8 @@ class Task:
|
|
257
254
|
|
258
255
|
Args:
|
259
256
|
msg (str|ChatDocument): initial message to process; if None,
|
260
|
-
the LLM will respond to
|
261
|
-
which set up the overall task.
|
257
|
+
the LLM will respond to its initial `self.task_messages`
|
258
|
+
which set up and kick off the overall task.
|
262
259
|
The agent tries to achieve this goal by looping
|
263
260
|
over `self.step()` until the task is considered
|
264
261
|
done; this can involve a series of messages produced by Agent,
|
@@ -325,6 +322,8 @@ class Task:
|
|
325
322
|
n_messages = 0
|
326
323
|
if isinstance(self.agent, ChatAgent):
|
327
324
|
if self.erase_substeps:
|
325
|
+
# TODO I don't like directly accessing agent message_history. Revisit.
|
326
|
+
# (Pchalasani)
|
328
327
|
del self.agent.message_history[message_history_idx + 2 : n_messages - 1]
|
329
328
|
n_messages = len(self.agent.message_history)
|
330
329
|
if self.erase_substeps:
|
@@ -441,11 +440,16 @@ class Task:
|
|
441
440
|
self.pending_sender = responder
|
442
441
|
self.log_message(self.pending_sender, self.pending_message, mark=True)
|
443
442
|
|
443
|
+
self._show_pending_message_if_debug()
|
444
|
+
return self.pending_message
|
445
|
+
|
446
|
+
def _show_pending_message_if_debug(self) -> None:
|
447
|
+
if self.pending_message is None:
|
448
|
+
return
|
444
449
|
if settings.debug:
|
445
450
|
sender_str = str(self.pending_sender)
|
446
451
|
msg_str = str(self.pending_message)
|
447
452
|
print(f"[red][{sender_str}]{msg_str}")
|
448
|
-
return self.pending_message
|
449
453
|
|
450
454
|
def response(self, e: Responder, turns: int = -1) -> Optional[ChatDocument]:
|
451
455
|
"""
|
@@ -589,6 +593,7 @@ class Task:
|
|
589
593
|
Entity.LLM: "green",
|
590
594
|
Entity.USER: "blue",
|
591
595
|
Entity.AGENT: "red",
|
596
|
+
Entity.SYSTEM: "magenta",
|
592
597
|
}[msg.metadata.sender]
|
593
598
|
f = msg.log_fields()
|
594
599
|
tool_type = f.tool_type.rjust(6)
|
@@ -56,7 +56,7 @@ INSTRUCTION = """
|
|
56
56
|
In this case you realize there is no available TOOL for this, so you just ask in
|
57
57
|
natural language: "What is the population of France?"
|
58
58
|
|
59
|
-
Whenever possible, AND ONLY IF APPLICABLE, use these
|
59
|
+
Whenever possible, AND ONLY IF APPLICABLE, use these TOOLS, with the JSON syntax
|
60
60
|
specified above. When a TOOL is applicable, simply use this syntax, do not write
|
61
61
|
anything else. Only if no TOOL is exactly applicable, ask in natural language.
|
62
62
|
"""
|
{langroid-0.1.60 → langroid-0.1.62}/langroid/language_models/prompt_formatter/llama2_formatter.py
RENAMED
@@ -8,8 +8,8 @@ from langroid.language_models.prompt_formatter.base import PromptFormatter
|
|
8
8
|
logger = logging.getLogger(__name__)
|
9
9
|
|
10
10
|
|
11
|
-
BOS: str = ""
|
12
|
-
EOS: str = ""
|
11
|
+
BOS: str = "<s>"
|
12
|
+
EOS: str = "</s>"
|
13
13
|
B_INST: str = "[INST]"
|
14
14
|
E_INST: str = "[/INST]"
|
15
15
|
B_SYS: str = "<<SYS>>\n"
|