langroid 0.1.85__py3-none-any.whl → 0.1.219__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/__init__.py +95 -0
- langroid/agent/__init__.py +40 -0
- langroid/agent/base.py +222 -91
- langroid/agent/batch.py +264 -0
- langroid/agent/callbacks/chainlit.py +608 -0
- langroid/agent/chat_agent.py +247 -101
- langroid/agent/chat_document.py +41 -4
- langroid/agent/openai_assistant.py +842 -0
- langroid/agent/special/__init__.py +50 -0
- langroid/agent/special/doc_chat_agent.py +837 -141
- langroid/agent/special/lance_doc_chat_agent.py +258 -0
- langroid/agent/special/lance_rag/__init__.py +9 -0
- langroid/agent/special/lance_rag/critic_agent.py +136 -0
- langroid/agent/special/lance_rag/lance_rag_task.py +80 -0
- langroid/agent/special/lance_rag/query_planner_agent.py +180 -0
- langroid/agent/special/lance_tools.py +44 -0
- langroid/agent/special/neo4j/__init__.py +0 -0
- langroid/agent/special/neo4j/csv_kg_chat.py +174 -0
- langroid/agent/special/neo4j/neo4j_chat_agent.py +370 -0
- langroid/agent/special/neo4j/utils/__init__.py +0 -0
- langroid/agent/special/neo4j/utils/system_message.py +46 -0
- langroid/agent/special/relevance_extractor_agent.py +127 -0
- langroid/agent/special/retriever_agent.py +32 -198
- langroid/agent/special/sql/__init__.py +11 -0
- langroid/agent/special/sql/sql_chat_agent.py +47 -23
- langroid/agent/special/sql/utils/__init__.py +22 -0
- langroid/agent/special/sql/utils/description_extractors.py +95 -46
- langroid/agent/special/sql/utils/populate_metadata.py +28 -21
- langroid/agent/special/table_chat_agent.py +43 -9
- langroid/agent/task.py +475 -122
- langroid/agent/tool_message.py +75 -13
- langroid/agent/tools/__init__.py +13 -0
- langroid/agent/tools/duckduckgo_search_tool.py +66 -0
- langroid/agent/tools/google_search_tool.py +11 -0
- langroid/agent/tools/metaphor_search_tool.py +67 -0
- langroid/agent/tools/recipient_tool.py +16 -29
- langroid/agent/tools/run_python_code.py +60 -0
- langroid/agent/tools/sciphi_search_rag_tool.py +79 -0
- langroid/agent/tools/segment_extract_tool.py +36 -0
- langroid/cachedb/__init__.py +9 -0
- langroid/cachedb/base.py +22 -2
- langroid/cachedb/momento_cachedb.py +26 -2
- langroid/cachedb/redis_cachedb.py +78 -11
- langroid/embedding_models/__init__.py +34 -0
- langroid/embedding_models/base.py +21 -2
- langroid/embedding_models/models.py +120 -18
- langroid/embedding_models/protoc/embeddings.proto +19 -0
- langroid/embedding_models/protoc/embeddings_pb2.py +33 -0
- langroid/embedding_models/protoc/embeddings_pb2.pyi +50 -0
- langroid/embedding_models/protoc/embeddings_pb2_grpc.py +79 -0
- langroid/embedding_models/remote_embeds.py +153 -0
- langroid/language_models/__init__.py +45 -0
- langroid/language_models/azure_openai.py +80 -27
- langroid/language_models/base.py +117 -12
- langroid/language_models/config.py +5 -0
- langroid/language_models/openai_assistants.py +3 -0
- langroid/language_models/openai_gpt.py +558 -174
- langroid/language_models/prompt_formatter/__init__.py +15 -0
- langroid/language_models/prompt_formatter/base.py +4 -6
- langroid/language_models/prompt_formatter/hf_formatter.py +135 -0
- langroid/language_models/utils.py +18 -21
- langroid/mytypes.py +25 -8
- langroid/parsing/__init__.py +46 -0
- langroid/parsing/document_parser.py +260 -63
- langroid/parsing/image_text.py +32 -0
- langroid/parsing/parse_json.py +143 -0
- langroid/parsing/parser.py +122 -59
- langroid/parsing/repo_loader.py +114 -52
- langroid/parsing/search.py +68 -63
- langroid/parsing/spider.py +3 -2
- langroid/parsing/table_loader.py +44 -0
- langroid/parsing/url_loader.py +59 -11
- langroid/parsing/urls.py +85 -37
- langroid/parsing/utils.py +298 -4
- langroid/parsing/web_search.py +73 -0
- langroid/prompts/__init__.py +11 -0
- langroid/prompts/chat-gpt4-system-prompt.md +68 -0
- langroid/prompts/prompts_config.py +1 -1
- langroid/utils/__init__.py +17 -0
- langroid/utils/algorithms/__init__.py +3 -0
- langroid/utils/algorithms/graph.py +103 -0
- langroid/utils/configuration.py +36 -5
- langroid/utils/constants.py +4 -0
- langroid/utils/globals.py +2 -2
- langroid/utils/logging.py +2 -5
- langroid/utils/output/__init__.py +21 -0
- langroid/utils/output/printing.py +47 -1
- langroid/utils/output/status.py +33 -0
- langroid/utils/pandas_utils.py +30 -0
- langroid/utils/pydantic_utils.py +616 -2
- langroid/utils/system.py +98 -0
- langroid/vector_store/__init__.py +40 -0
- langroid/vector_store/base.py +203 -6
- langroid/vector_store/chromadb.py +59 -32
- langroid/vector_store/lancedb.py +463 -0
- langroid/vector_store/meilisearch.py +10 -7
- langroid/vector_store/momento.py +262 -0
- langroid/vector_store/qdrantdb.py +104 -22
- {langroid-0.1.85.dist-info → langroid-0.1.219.dist-info}/METADATA +329 -149
- langroid-0.1.219.dist-info/RECORD +127 -0
- {langroid-0.1.85.dist-info → langroid-0.1.219.dist-info}/WHEEL +1 -1
- langroid/agent/special/recipient_validator_agent.py +0 -157
- langroid/parsing/json.py +0 -64
- langroid/utils/web/selenium_login.py +0 -36
- langroid-0.1.85.dist-info/RECORD +0 -94
- /langroid/{scripts → agent/callbacks}/__init__.py +0 -0
- {langroid-0.1.85.dist-info → langroid-0.1.219.dist-info}/LICENSE +0 -0
langroid/agent/tool_message.py
CHANGED
@@ -6,6 +6,8 @@ an agent. The messages could represent, for example:
|
|
6
6
|
- request to run a method of the agent
|
7
7
|
"""
|
8
8
|
|
9
|
+
import json
|
10
|
+
import textwrap
|
9
11
|
from abc import ABC
|
10
12
|
from random import choice
|
11
13
|
from typing import Any, Dict, List, Type
|
@@ -14,16 +16,10 @@ from docstring_parser import parse
|
|
14
16
|
from pydantic import BaseModel
|
15
17
|
|
16
18
|
from langroid.language_models.base import LLMFunctionSpec
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if isinstance(d, dict):
|
22
|
-
for key in list(d.keys()):
|
23
|
-
if key == k and "type" in d.keys():
|
24
|
-
del d[key]
|
25
|
-
else:
|
26
|
-
_recursive_purge_dict_key(d[key], k)
|
19
|
+
from langroid.utils.pydantic_utils import (
|
20
|
+
_recursive_purge_dict_key,
|
21
|
+
generate_simple_schema,
|
22
|
+
)
|
27
23
|
|
28
24
|
|
29
25
|
class ToolMessage(ABC, BaseModel):
|
@@ -45,7 +41,6 @@ class ToolMessage(ABC, BaseModel):
|
|
45
41
|
request: str
|
46
42
|
purpose: str
|
47
43
|
result: str = ""
|
48
|
-
recipient: str = "" # default is empty string, so it is optional
|
49
44
|
|
50
45
|
class Config:
|
51
46
|
arbitrary_types_allowed = False
|
@@ -87,6 +82,9 @@ class ToolMessage(ABC, BaseModel):
|
|
87
82
|
ex = choice(cls.examples())
|
88
83
|
return ex.json_example()
|
89
84
|
|
85
|
+
def to_json(self) -> str:
|
86
|
+
return self.json(indent=4, exclude={"result", "purpose"})
|
87
|
+
|
90
88
|
def json_example(self) -> str:
|
91
89
|
return self.json(indent=4, exclude={"result", "purpose"})
|
92
90
|
|
@@ -109,7 +107,58 @@ class ToolMessage(ABC, BaseModel):
|
|
109
107
|
return properties.get(f, {}).get("default", None)
|
110
108
|
|
111
109
|
@classmethod
|
112
|
-
def
|
110
|
+
def json_instructions(cls, tool: bool = False) -> str:
|
111
|
+
"""
|
112
|
+
Default Instructions to the LLM showing how to use the tool/function-call.
|
113
|
+
Works for GPT4 but override this for weaker LLMs if needed.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
tool: instructions for Langroid-native tool use? (e.g. for non-OpenAI LLM)
|
117
|
+
(or else it would be for OpenAI Function calls)
|
118
|
+
Returns:
|
119
|
+
str: instructions on how to use the message
|
120
|
+
"""
|
121
|
+
# TODO: when we attempt to use a "simpler schema"
|
122
|
+
# (i.e. all nested fields explicit without definitions),
|
123
|
+
# we seem to get worse results, so we turn it off for now
|
124
|
+
param_dict = (
|
125
|
+
# cls.simple_schema() if tool else
|
126
|
+
cls.llm_function_schema(request=True).parameters
|
127
|
+
)
|
128
|
+
return textwrap.dedent(
|
129
|
+
f"""
|
130
|
+
TOOL: {cls.default_value("request")}
|
131
|
+
PURPOSE: {cls.default_value("purpose")}
|
132
|
+
JSON FORMAT: {
|
133
|
+
json.dumps(param_dict, indent=4)
|
134
|
+
}
|
135
|
+
{"EXAMPLE: " + cls.usage_example() if cls.examples() else ""}
|
136
|
+
""".lstrip()
|
137
|
+
)
|
138
|
+
|
139
|
+
@staticmethod
|
140
|
+
def json_group_instructions() -> str:
|
141
|
+
"""Template for instructions for a group of tools.
|
142
|
+
Works with GPT4 but override this for weaker LLMs if needed.
|
143
|
+
"""
|
144
|
+
return textwrap.dedent(
|
145
|
+
"""
|
146
|
+
=== ALL AVAILABLE TOOLS and THEIR JSON FORMAT INSTRUCTIONS ===
|
147
|
+
You have access to the following TOOLS to accomplish your task:
|
148
|
+
|
149
|
+
{json_instructions}
|
150
|
+
|
151
|
+
When one of the above TOOLs is applicable, you must express your
|
152
|
+
request as "TOOL:" followed by the request in the above JSON format.
|
153
|
+
"""
|
154
|
+
)
|
155
|
+
|
156
|
+
@classmethod
|
157
|
+
def llm_function_schema(
|
158
|
+
cls,
|
159
|
+
request: bool = False,
|
160
|
+
defaults: bool = True,
|
161
|
+
) -> LLMFunctionSpec:
|
113
162
|
"""
|
114
163
|
Clean up the schema of the Pydantic class (which can recursively contain
|
115
164
|
other Pydantic classes), to create a version compatible with OpenAI
|
@@ -122,6 +171,8 @@ class ToolMessage(ABC, BaseModel):
|
|
122
171
|
request: whether to include the "request" field in the schema.
|
123
172
|
(we set this to True when using Langroid-native TOOLs as opposed to
|
124
173
|
OpenAI Function calls)
|
174
|
+
defaults: whether to include fields with default values in the schema,
|
175
|
+
in the "properties" section.
|
125
176
|
|
126
177
|
Returns:
|
127
178
|
LLMFunctionSpec: the schema as an LLMFunctionSpec
|
@@ -146,7 +197,7 @@ class ToolMessage(ABC, BaseModel):
|
|
146
197
|
parameters["properties"] = {
|
147
198
|
field: details
|
148
199
|
for field, details in parameters["properties"].items()
|
149
|
-
if field not in excludes
|
200
|
+
if field not in excludes and (defaults or details.get("default") is None)
|
150
201
|
}
|
151
202
|
parameters["required"] = sorted(
|
152
203
|
k
|
@@ -173,3 +224,14 @@ class ToolMessage(ABC, BaseModel):
|
|
173
224
|
description=cls.default_value("purpose"),
|
174
225
|
parameters=parameters,
|
175
226
|
)
|
227
|
+
|
228
|
+
@classmethod
|
229
|
+
def simple_schema(cls) -> Dict[str, Any]:
|
230
|
+
"""
|
231
|
+
Return a simplified schema for the message, with only the request and
|
232
|
+
required fields.
|
233
|
+
Returns:
|
234
|
+
Dict[str, Any]: simplified schema
|
235
|
+
"""
|
236
|
+
schema = generate_simple_schema(cls, exclude=["result", "purpose"])
|
237
|
+
return schema
|
langroid/agent/tools/__init__.py
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
from .google_search_tool import GoogleSearchTool
|
2
|
+
from .recipient_tool import AddRecipientTool, RecipientTool
|
3
|
+
|
4
|
+
from . import google_search_tool
|
5
|
+
from . import recipient_tool
|
6
|
+
|
7
|
+
__all__ = [
|
8
|
+
"GoogleSearchTool",
|
9
|
+
"AddRecipientTool",
|
10
|
+
"RecipientTool",
|
11
|
+
"google_search_tool",
|
12
|
+
"recipient_tool",
|
13
|
+
]
|
@@ -0,0 +1,66 @@
|
|
1
|
+
"""
|
2
|
+
A tool to trigger a Metaphor search for a given query,
|
3
|
+
(https://docs.exa.ai/reference/getting-started)
|
4
|
+
and return the top results with their titles, links, summaries.
|
5
|
+
Since the tool is stateless (i.e. does not need
|
6
|
+
access to agent state), it can be enabled for any agent, without having to define a
|
7
|
+
special method inside the agent: `agent.enable_message(MetaphorSearchTool)`
|
8
|
+
|
9
|
+
NOTE: To use this tool, you need to:
|
10
|
+
|
11
|
+
* set the METAPHOR_API_KEY environment variables in
|
12
|
+
your `.env` file, e.g. `METAPHOR_API_KEY=your_api_key_here`
|
13
|
+
(Note as of 28 Jan 2023, Metaphor renamed to Exa, so you can also use
|
14
|
+
`EXA_API_KEY=your_api_key_here`)
|
15
|
+
|
16
|
+
* install langroid with the `metaphor` extra, e.g.
|
17
|
+
`pip install langroid[metaphor]` or `poetry add langroid[metaphor]`
|
18
|
+
(it installs the `metaphor-python` package from pypi).
|
19
|
+
|
20
|
+
For more information, please refer to the official docs:
|
21
|
+
https://metaphor.systems/
|
22
|
+
"""
|
23
|
+
|
24
|
+
from typing import List
|
25
|
+
|
26
|
+
from langroid.agent.tool_message import ToolMessage
|
27
|
+
from langroid.parsing.web_search import duckduckgo_search
|
28
|
+
|
29
|
+
|
30
|
+
class DuckduckgoSearchTool(ToolMessage):
|
31
|
+
request: str = "duckduckgo_search"
|
32
|
+
purpose: str = """
|
33
|
+
To search the web and return up to <num_results>
|
34
|
+
links relevant to the given <query>. When using this tool,
|
35
|
+
ONLY show the required JSON, DO NOT SAY ANYTHING ELSE.
|
36
|
+
Wait for the results of the web search, and then use them to
|
37
|
+
compose your response.
|
38
|
+
"""
|
39
|
+
query: str
|
40
|
+
num_results: int
|
41
|
+
|
42
|
+
def handle(self) -> str:
|
43
|
+
"""
|
44
|
+
Conducts a search using the metaphor API based on the provided query
|
45
|
+
and number of results by triggering a metaphor_search.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
str: A formatted string containing the titles, links, and
|
49
|
+
summaries of each search result, separated by two newlines.
|
50
|
+
"""
|
51
|
+
search_results = duckduckgo_search(self.query, self.num_results)
|
52
|
+
# return Title, Link, Summary of each result, separated by two newlines
|
53
|
+
results_str = "\n\n".join(str(result) for result in search_results)
|
54
|
+
return f"""
|
55
|
+
BELOW ARE THE RESULTS FROM THE WEB SEARCH. USE THESE TO COMPOSE YOUR RESPONSE:
|
56
|
+
{results_str}
|
57
|
+
"""
|
58
|
+
|
59
|
+
@classmethod
|
60
|
+
def examples(cls) -> List["ToolMessage"]:
|
61
|
+
return [
|
62
|
+
cls(
|
63
|
+
query="When was the Llama2 Large Language Model (LLM) released?",
|
64
|
+
num_results=3,
|
65
|
+
),
|
66
|
+
]
|
@@ -9,6 +9,8 @@ environment variables in your `.env` file, as explained in the
|
|
9
9
|
[README](https://github.com/langroid/langroid#gear-installation-and-setup).
|
10
10
|
"""
|
11
11
|
|
12
|
+
from typing import List
|
13
|
+
|
12
14
|
from langroid.agent.tool_message import ToolMessage
|
13
15
|
from langroid.parsing.web_search import google_search
|
14
16
|
|
@@ -26,3 +28,12 @@ class GoogleSearchTool(ToolMessage):
|
|
26
28
|
search_results = google_search(self.query, self.num_results)
|
27
29
|
# return Title, Link, Summary of each result, separated by two newlines
|
28
30
|
return "\n\n".join(str(result) for result in search_results)
|
31
|
+
|
32
|
+
@classmethod
|
33
|
+
def examples(cls) -> List["ToolMessage"]:
|
34
|
+
return [
|
35
|
+
cls(
|
36
|
+
query="When was the Llama2 Large Language Model (LLM) released?",
|
37
|
+
num_results=3,
|
38
|
+
),
|
39
|
+
]
|
@@ -0,0 +1,67 @@
|
|
1
|
+
"""
|
2
|
+
A tool to trigger a Metaphor search for a given query,
|
3
|
+
(https://docs.exa.ai/reference/getting-started)
|
4
|
+
and return the top results with their titles, links, summaries.
|
5
|
+
Since the tool is stateless (i.e. does not need
|
6
|
+
access to agent state), it can be enabled for any agent, without having to define a
|
7
|
+
special method inside the agent: `agent.enable_message(MetaphorSearchTool)`
|
8
|
+
|
9
|
+
NOTE: To use this tool, you need to:
|
10
|
+
|
11
|
+
* set the METAPHOR_API_KEY environment variables in
|
12
|
+
your `.env` file, e.g. `METAPHOR_API_KEY=your_api_key_here`
|
13
|
+
(Note as of 28 Jan 2023, Metaphor renamed to Exa, so you can also use
|
14
|
+
`EXA_API_KEY=your_api_key_here`)
|
15
|
+
|
16
|
+
* install langroid with the `metaphor` extra, e.g.
|
17
|
+
`pip install langroid[metaphor]` or `poetry add langroid[metaphor]`
|
18
|
+
(it installs the `metaphor-python` package from pypi).
|
19
|
+
|
20
|
+
For more information, please refer to the official docs:
|
21
|
+
https://metaphor.systems/
|
22
|
+
"""
|
23
|
+
|
24
|
+
from typing import List
|
25
|
+
|
26
|
+
from langroid.agent.tool_message import ToolMessage
|
27
|
+
from langroid.parsing.web_search import metaphor_search
|
28
|
+
|
29
|
+
|
30
|
+
class MetaphorSearchTool(ToolMessage):
|
31
|
+
request: str = "metaphor_search"
|
32
|
+
purpose: str = """
|
33
|
+
To search the web and return up to <num_results>
|
34
|
+
links relevant to the given <query>. When using this tool,
|
35
|
+
ONLY show the required JSON, DO NOT SAY ANYTHING ELSE.
|
36
|
+
Wait for the results of the web search, and then use them to
|
37
|
+
compose your response.
|
38
|
+
"""
|
39
|
+
query: str
|
40
|
+
num_results: int
|
41
|
+
|
42
|
+
def handle(self) -> str:
|
43
|
+
"""
|
44
|
+
Conducts a search using the metaphor API based on the provided query
|
45
|
+
and number of results by triggering a metaphor_search.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
str: A formatted string containing the titles, links, and
|
49
|
+
summaries of each search result, separated by two newlines.
|
50
|
+
"""
|
51
|
+
|
52
|
+
search_results = metaphor_search(self.query, self.num_results)
|
53
|
+
# return Title, Link, Summary of each result, separated by two newlines
|
54
|
+
results_str = "\n\n".join(str(result) for result in search_results)
|
55
|
+
return f"""
|
56
|
+
BELOW ARE THE RESULTS FROM THE WEB SEARCH. USE THESE TO COMPOSE YOUR RESPONSE:
|
57
|
+
{results_str}
|
58
|
+
"""
|
59
|
+
|
60
|
+
@classmethod
|
61
|
+
def examples(cls) -> List["ToolMessage"]:
|
62
|
+
return [
|
63
|
+
cls(
|
64
|
+
query="When was the Llama2 Large Language Model (LLM) released?",
|
65
|
+
num_results=3,
|
66
|
+
),
|
67
|
+
]
|
@@ -6,25 +6,8 @@ the method `_get_tool_list()`).
|
|
6
6
|
|
7
7
|
See usage examples in `tests/main/test_multi_agent_complex.py` and
|
8
8
|
`tests/main/test_recipient_tool.py`.
|
9
|
-
|
10
|
-
Previously we were using RecipientValidatorAgent to enforce proper
|
11
|
-
recipient specifiction, but the preferred method is to use the
|
12
|
-
`RecipientTool` class. This has numerous advantages:
|
13
|
-
- it uses the tool/function-call mechanism to specify a recipient in a JSON-structured
|
14
|
-
string, which is more consistent with the rest of the system, and does not require
|
15
|
-
inventing a new syntax like `TO:<recipient>` (which the RecipientValidatorAgent
|
16
|
-
uses).
|
17
|
-
- it removes the need for any special parsing of the message content, since we leverage
|
18
|
-
the built-in JSON tool-matching in `Agent.handle_message()` and downstream code.
|
19
|
-
- it does not require setting the `parent_responder` field in the `ChatDocument`
|
20
|
-
metadata, which is somewhat hacky.
|
21
|
-
- it appears to be less brittle than requiring the LLM to use TO:<recipient> syntax:
|
22
|
-
The LLM almost never forgets to use the RecipientTool as instructed.
|
23
|
-
- The RecipientTool class acts as a specification of the required syntax, and also
|
24
|
-
contains mechanisms to enforce this syntax.
|
25
|
-
- For a developer who needs to enforce recipient specification for an agent, they only
|
26
|
-
need to do `agent.enable_message(RecipientTool)`, and the rest is taken care of.
|
27
9
|
"""
|
10
|
+
|
28
11
|
from typing import List, Type
|
29
12
|
|
30
13
|
from rich import print
|
@@ -62,20 +45,22 @@ class AddRecipientTool(ToolMessage):
|
|
62
45
|
(ChatDocument): with content set to self.content and
|
63
46
|
metadata.recipient set to self.recipient.
|
64
47
|
"""
|
65
|
-
print(
|
48
|
+
print(
|
49
|
+
"[red]RecipientTool: "
|
50
|
+
f"Added recipient {self.intended_recipient} to message."
|
51
|
+
)
|
66
52
|
if self.__class__.saved_content == "":
|
67
53
|
recipient_request_name = RecipientTool.default_value("request")
|
68
|
-
|
69
|
-
f"""
|
54
|
+
content = f"""
|
70
55
|
Recipient specified but content is empty!
|
71
56
|
This could be because the `{self.request}` tool/function was used
|
72
57
|
before using `{recipient_request_name}` tool/function.
|
73
58
|
Resend the message using `{recipient_request_name}` tool/function.
|
74
59
|
"""
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
60
|
+
else:
|
61
|
+
content = self.__class__.saved_content # use class-level attrib value
|
62
|
+
# erase content since we just used it.
|
63
|
+
self.__class__.saved_content = ""
|
79
64
|
return ChatDocument(
|
80
65
|
content=content,
|
81
66
|
metadata=ChatDocMetaData(
|
@@ -91,9 +76,10 @@ class RecipientTool(ToolMessage):
|
|
91
76
|
Used by LLM to send a message to a specific recipient.
|
92
77
|
|
93
78
|
Useful in cases where an LLM is talking to 2 or more
|
94
|
-
agents, and needs to specify which agent (task)
|
95
|
-
The recipient name should be the name of a task
|
96
|
-
the agent that the task wraps, although the task
|
79
|
+
agents (or an Agent and human user), and needs to specify which agent (task)
|
80
|
+
its message is intended for. The recipient name should be the name of a task
|
81
|
+
(which is normally the name of the agent that the task wraps, although the task
|
82
|
+
can have its own name).
|
97
83
|
|
98
84
|
To use this tool/function-call, LLM must generate a JSON structure
|
99
85
|
with these fields:
|
@@ -102,6 +88,7 @@ class RecipientTool(ToolMessage):
|
|
102
88
|
"intended_recipient": <name_of_recipient_task_or_entity>,
|
103
89
|
"content": <content>
|
104
90
|
}
|
91
|
+
The effect of this is that `content` will be sent to the `intended_recipient` task.
|
105
92
|
"""
|
106
93
|
|
107
94
|
request: str = "recipient_message"
|
@@ -181,7 +168,7 @@ class RecipientTool(ToolMessage):
|
|
181
168
|
content=self.content,
|
182
169
|
metadata=ChatDocMetaData(
|
183
170
|
recipient=self.intended_recipient,
|
184
|
-
# we are constructing this so it looks as
|
171
|
+
# we are constructing this so it looks as if msg is from LLM
|
185
172
|
sender=Entity.LLM,
|
186
173
|
),
|
187
174
|
)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import io
|
2
|
+
import sys
|
3
|
+
from typing import List
|
4
|
+
|
5
|
+
from langroid.agent.tool_message import ToolMessage
|
6
|
+
|
7
|
+
|
8
|
+
class RunPythonCodeTool(ToolMessage):
|
9
|
+
"""
|
10
|
+
Tool/function to run code generated by the LLM.
|
11
|
+
The code is assumed to be self-contained, i.e. it contains all necessary imports
|
12
|
+
and does not depend on any external variables.
|
13
|
+
"""
|
14
|
+
|
15
|
+
request: str = "run_python_code"
|
16
|
+
purpose: str = """
|
17
|
+
To run python <code> and return the results to answer a question.
|
18
|
+
"""
|
19
|
+
code: str
|
20
|
+
|
21
|
+
@classmethod
|
22
|
+
def examples(cls) -> List["ToolMessage"]:
|
23
|
+
return [
|
24
|
+
cls(code="import numpy as np\nnp.square(9)"),
|
25
|
+
]
|
26
|
+
|
27
|
+
def handle(self) -> str:
|
28
|
+
"""
|
29
|
+
Handle a RunPythonCodeTool message by running the code and returning the result.
|
30
|
+
Returns:
|
31
|
+
str: The result of running the code along with any print output.
|
32
|
+
"""
|
33
|
+
code = self.code
|
34
|
+
|
35
|
+
# Create a string-based I/O stream
|
36
|
+
code_out = io.StringIO()
|
37
|
+
|
38
|
+
# Temporarily redirect standard output to our string-based I/O stream
|
39
|
+
sys.stdout = code_out
|
40
|
+
|
41
|
+
try:
|
42
|
+
eval_result = eval(code, {})
|
43
|
+
except Exception as e:
|
44
|
+
eval_result = f"ERROR: {type(e)}: {e}"
|
45
|
+
|
46
|
+
if eval_result is None:
|
47
|
+
eval_result = ""
|
48
|
+
|
49
|
+
# Always restore the original standard output
|
50
|
+
sys.stdout = sys.__stdout__
|
51
|
+
|
52
|
+
# Get the resulting string from the I/O stream
|
53
|
+
print_result = code_out.getvalue() or ""
|
54
|
+
sep = "\n" if print_result else ""
|
55
|
+
# Combine the print and eval results
|
56
|
+
result = f"{print_result}{sep}{eval_result}"
|
57
|
+
if result == "":
|
58
|
+
result = "No result"
|
59
|
+
# Return the result
|
60
|
+
return result
|
@@ -0,0 +1,79 @@
|
|
1
|
+
"""
|
2
|
+
A tool which returns a Search RAG response from the SciPhi API.
|
3
|
+
their titles, links, summaries. Since the tool is stateless (i.e. does not need
|
4
|
+
access to agent state), it can be enabled for any agent, without having to define a
|
5
|
+
special method inside the agent: `agent.enable_message(SciPhiSearchRAGTool)`
|
6
|
+
|
7
|
+
Example return output appears as follows below:
|
8
|
+
|
9
|
+
<-- Query -->
|
10
|
+
```
|
11
|
+
Find 3 results on the internet about the LK-99 superconducting material.
|
12
|
+
``
|
13
|
+
|
14
|
+
<-- Response (compressed for this example)-->
|
15
|
+
```
|
16
|
+
[ result1 ]
|
17
|
+
|
18
|
+
[ result2 ]
|
19
|
+
|
20
|
+
[ result3 ]
|
21
|
+
|
22
|
+
```
|
23
|
+
|
24
|
+
NOTE: Using this tool requires getting an API key from sciphi.ai.
|
25
|
+
Setup is as simple as shown below
|
26
|
+
# Get a free API key at https://www.sciphi.ai/account
|
27
|
+
# export SCIPHI_API_KEY=$MY_SCIPHI_API_KEY before running the agent
|
28
|
+
# OR add SCIPHI_API_KEY=$MY_SCIPHI_API_KEY to your .env file
|
29
|
+
|
30
|
+
This tool requires installing langroid with the `sciphi` extra, e.g.
|
31
|
+
`pip install langroid[sciphi]` or `poetry add langroid[sciphi]`
|
32
|
+
(it installs the `agent-search` package from pypi).
|
33
|
+
|
34
|
+
For more information, please refer to the official docs:
|
35
|
+
https://agent-search.readthedocs.io/en/latest/
|
36
|
+
"""
|
37
|
+
|
38
|
+
from typing import List
|
39
|
+
|
40
|
+
try:
|
41
|
+
from agent_search import SciPhi
|
42
|
+
except ImportError:
|
43
|
+
raise ImportError(
|
44
|
+
"You are attempting to use the `agent-search` library;"
|
45
|
+
"To use it, please install langroid with the `sciphi` extra, e.g. "
|
46
|
+
"`pip install langroid[sciphi]` or `poetry add langroid[sciphi]` "
|
47
|
+
"(it installs the `agent-search` package from pypi)."
|
48
|
+
)
|
49
|
+
|
50
|
+
from langroid.agent.tool_message import ToolMessage
|
51
|
+
|
52
|
+
|
53
|
+
class SciPhiSearchRAGTool(ToolMessage):
|
54
|
+
request: str = "web_search_rag"
|
55
|
+
purpose: str = """
|
56
|
+
To search the web with provider <search_provider> and
|
57
|
+
return a response summary with llm model <llm_model> the given <query>.
|
58
|
+
"""
|
59
|
+
query: str
|
60
|
+
|
61
|
+
def handle(self) -> str:
|
62
|
+
rag_response = SciPhi().get_search_rag_response(
|
63
|
+
query=self.query, search_provider="bing", llm_model="SciPhi/Sensei-7B-V1"
|
64
|
+
)
|
65
|
+
result = rag_response["response"]
|
66
|
+
result = (
|
67
|
+
f"### RAG Response:\n{result}\n\n"
|
68
|
+
+ "### Related Queries:\n"
|
69
|
+
+ "\n".join(rag_response["related_queries"])
|
70
|
+
)
|
71
|
+
return result # type: ignore
|
72
|
+
|
73
|
+
@classmethod
|
74
|
+
def examples(cls) -> List["ToolMessage"]:
|
75
|
+
return [
|
76
|
+
cls(
|
77
|
+
query="When was the Llama2 Large Language Model (LLM) released?",
|
78
|
+
),
|
79
|
+
]
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
A tool to extract segment numbers from the last user message,
|
3
|
+
containing numbered segments.
|
4
|
+
|
5
|
+
The idea is that when an LLM wants to (or is asked to) simply extract
|
6
|
+
portions of a message verbatim, it should use this tool/function to
|
7
|
+
SPECIFY what should be extracted, rather than actually extracting it.
|
8
|
+
The output will be in the form of a list of segment numbers or ranges.
|
9
|
+
This will usually be much cheaper and faster than actually writing out the extracted
|
10
|
+
text. The handler of this tool/function will then extract the text and send it back.
|
11
|
+
"""
|
12
|
+
|
13
|
+
from typing import List
|
14
|
+
|
15
|
+
from langroid.agent.tool_message import ToolMessage
|
16
|
+
|
17
|
+
|
18
|
+
class SegmentExtractTool(ToolMessage):
|
19
|
+
request: str = "extract_segments"
|
20
|
+
purpose: str = """
|
21
|
+
To extract segments from a body of text containing numbered
|
22
|
+
segments, in the form of a <segment_list> which is a list of segment
|
23
|
+
numbers or ranges, like "10,12,14-17".
|
24
|
+
"""
|
25
|
+
segment_list: str
|
26
|
+
|
27
|
+
@classmethod
|
28
|
+
def examples(cls) -> List["ToolMessage"]:
|
29
|
+
return [cls(segment_list="1,3,5-7")]
|
30
|
+
|
31
|
+
@classmethod
|
32
|
+
def instructions(cls) -> str:
|
33
|
+
return """
|
34
|
+
Use this tool/function to indicate certain segments from
|
35
|
+
a body of text containing numbered segments.
|
36
|
+
"""
|
langroid/cachedb/__init__.py
CHANGED
langroid/cachedb/base.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from abc import ABC, abstractmethod
|
2
|
-
from typing import Any, Dict,
|
2
|
+
from typing import Any, Dict, List
|
3
3
|
|
4
4
|
|
5
5
|
class CacheDB(ABC):
|
@@ -17,7 +17,7 @@ class CacheDB(ABC):
|
|
17
17
|
pass
|
18
18
|
|
19
19
|
@abstractmethod
|
20
|
-
def retrieve(self, key: str) ->
|
20
|
+
def retrieve(self, key: str) -> Dict[str, Any] | str | None:
|
21
21
|
"""
|
22
22
|
Abstract method to retrieve the value associated with a key.
|
23
23
|
|
@@ -28,3 +28,23 @@ class CacheDB(ABC):
|
|
28
28
|
dict: The value associated with the key.
|
29
29
|
"""
|
30
30
|
pass
|
31
|
+
|
32
|
+
@abstractmethod
|
33
|
+
def delete_keys(self, keys: List[str]) -> None:
|
34
|
+
"""
|
35
|
+
Delete the keys from the cache.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
keys (List[str]): The keys to delete.
|
39
|
+
"""
|
40
|
+
pass
|
41
|
+
|
42
|
+
@abstractmethod
|
43
|
+
def delete_keys_pattern(self, pattern: str) -> None:
|
44
|
+
"""
|
45
|
+
Delete all keys with the given pattern
|
46
|
+
|
47
|
+
Args:
|
48
|
+
prefix (str): The pattern to match.
|
49
|
+
"""
|
50
|
+
pass
|
@@ -2,7 +2,7 @@ import json
|
|
2
2
|
import logging
|
3
3
|
import os
|
4
4
|
from datetime import timedelta
|
5
|
-
from typing import Any, Dict,
|
5
|
+
from typing import Any, Dict, List
|
6
6
|
|
7
7
|
import momento
|
8
8
|
from dotenv import load_dotenv
|
@@ -61,7 +61,7 @@ class MomentoCache(CacheDB):
|
|
61
61
|
"""
|
62
62
|
self.client.set(self.config.cachename, key, json.dumps(value))
|
63
63
|
|
64
|
-
def retrieve(self, key: str) ->
|
64
|
+
def retrieve(self, key: str) -> Dict[str, Any] | str | None:
|
65
65
|
"""
|
66
66
|
Retrieve the value associated with a key.
|
67
67
|
|
@@ -76,3 +76,27 @@ class MomentoCache(CacheDB):
|
|
76
76
|
return json.loads(value.value_string) # type: ignore
|
77
77
|
else:
|
78
78
|
return None
|
79
|
+
|
80
|
+
def delete_keys(self, keys: List[str]) -> None:
|
81
|
+
"""
|
82
|
+
Delete the keys from the cache.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
keys (List[str]): The keys to delete.
|
86
|
+
"""
|
87
|
+
for key in keys:
|
88
|
+
self.client.delete(self.config.cachename, key)
|
89
|
+
|
90
|
+
def delete_keys_pattern(self, pattern: str) -> None:
|
91
|
+
"""
|
92
|
+
Delete the keys from the cache with the given pattern.
|
93
|
+
|
94
|
+
Args:
|
95
|
+
prefix (str): The pattern to match.
|
96
|
+
"""
|
97
|
+
raise NotImplementedError(
|
98
|
+
"""
|
99
|
+
MomentoCache does not support delete_keys_pattern.
|
100
|
+
Please use RedisCache instead.
|
101
|
+
"""
|
102
|
+
)
|