langroid 0.1.233__tar.gz → 0.1.235__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.233 → langroid-0.1.235}/PKG-INFO +1 -1
- {langroid-0.1.233 → langroid-0.1.235}/langroid/__init__.py +2 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/batch.py +1 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/chat_document.py +19 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/__init__.py +2 -2
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/table_chat_agent.py +39 -50
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/task.py +95 -13
- {langroid-0.1.233 → langroid-0.1.235}/langroid/cachedb/redis_cachedb.py +1 -1
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/constants.py +1 -1
- {langroid-0.1.233 → langroid-0.1.235}/pyproject.toml +1 -1
- {langroid-0.1.233 → langroid-0.1.235}/LICENSE +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/README.md +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/base.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/callbacks/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/callbacks/chainlit.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/chat_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/helpers.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/junk +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/openai_assistant.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/doc_chat_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/lance_rag/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/lance_tools.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/neo4j/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/neo4j/utils/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/neo4j/utils/system_message.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/relevance_extractor_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/retriever_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/utils/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/utils/system_message.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/utils/tools.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tool_message.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/extract_tool.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/generator_tool.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/google_search_tool.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/metaphor_search_tool.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/recipient_tool.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/run_python_code.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent/tools/segment_extract_tool.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/agent_config.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/cachedb/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/cachedb/base.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/cachedb/momento_cachedb.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/base.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/clustering.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/models.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/protoc/embeddings.proto +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/remote_embeds.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/azure_openai.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/base.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/config.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/openai_assistants.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/openai_gpt.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/prompt_formatter/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/prompt_formatter/base.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/utils.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/mytypes.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/agent_chats.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/code-parsing.md +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/code_parser.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/config.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/document_parser.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/image_text.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/para_sentence_split.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/parse_json.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/parser.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/repo_loader.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/search.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/spider.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/table_loader.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/url_loader.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/url_loader_cookies.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/urls.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/utils.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/parsing/web_search.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/prompts/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/prompts/chat-gpt4-system-prompt.md +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/prompts/dialog.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/prompts/prompts_config.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/prompts/templates.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/prompts/transforms.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/algorithms/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/algorithms/graph.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/configuration.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/docker.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/globals.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/llms/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/llms/strings.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/logging.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/output/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/output/printing.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/output/status.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/pandas_utils.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/pydantic_utils.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/system.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/web/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/utils/web/login.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/__init__.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/base.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/chromadb.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/lancedb.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/meilisearch.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/momento.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/qdrant_cloud.py +0 -0
- {langroid-0.1.233 → langroid-0.1.235}/langroid/vector_store/qdrantdb.py +0 -0
@@ -27,6 +27,7 @@ from .agent.batch import (
|
|
27
27
|
)
|
28
28
|
|
29
29
|
from .agent.chat_document import (
|
30
|
+
StatusCode,
|
30
31
|
ChatDocument,
|
31
32
|
ChatDocMetaData,
|
32
33
|
)
|
@@ -77,6 +78,7 @@ __all__ = [
|
|
77
78
|
"AgentConfig",
|
78
79
|
"ChatAgent",
|
79
80
|
"ChatAgentConfig",
|
81
|
+
"StatusCode",
|
80
82
|
"ChatDocument",
|
81
83
|
"ChatDocMetaData",
|
82
84
|
"Task",
|
@@ -53,6 +53,7 @@ def run_batch_task_gen(
|
|
53
53
|
message (Optional[str]): optionally overrides the console status messages
|
54
54
|
handle_exceptions: bool: Whether to replace exceptions with outputs of None
|
55
55
|
max_cost: float: maximum cost to run the task (default 0.0 for unlimited)
|
56
|
+
max_tokens: int: maximum token usage (in and out) (default 0 for unlimited)
|
56
57
|
|
57
58
|
|
58
59
|
Returns:
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import json
|
2
|
+
from enum import Enum
|
2
3
|
from typing import List, Optional, Union
|
3
4
|
|
4
5
|
from pydantic import BaseModel, Extra
|
@@ -23,6 +24,23 @@ class ChatDocAttachment(BaseModel):
|
|
23
24
|
extra = Extra.allow
|
24
25
|
|
25
26
|
|
27
|
+
class StatusCode(str, Enum):
|
28
|
+
"""Codes meant to be returned by task.run(). Some are not used yet."""
|
29
|
+
|
30
|
+
OK = "OK"
|
31
|
+
ERROR = "ERROR"
|
32
|
+
DONE = "DONE"
|
33
|
+
STALLED = "STALLED"
|
34
|
+
INF_LOOP = "INF_LOOP"
|
35
|
+
KILL = "KILL"
|
36
|
+
MAX_TURNS = "MAX_TURNS"
|
37
|
+
MAX_COST = "MAX_COST"
|
38
|
+
MAX_TOKENS = "MAX_TOKENS"
|
39
|
+
TIMEOUT = "TIMEOUT"
|
40
|
+
NO_ANSWER = "NO_ANSWER"
|
41
|
+
USER_QUIT = "USER_QUIT"
|
42
|
+
|
43
|
+
|
26
44
|
class ChatDocMetaData(DocMetaData):
|
27
45
|
parent: Optional["ChatDocument"] = None
|
28
46
|
sender: Entity
|
@@ -35,6 +53,7 @@ class ChatDocMetaData(DocMetaData):
|
|
35
53
|
usage: Optional[LLMTokenUsage]
|
36
54
|
cached: bool = False
|
37
55
|
displayed: bool = False
|
56
|
+
status: Optional[StatusCode] = None
|
38
57
|
|
39
58
|
|
40
59
|
class ChatDocLoggerFields(BaseModel):
|
@@ -14,7 +14,7 @@ from .table_chat_agent import (
|
|
14
14
|
dataframe_summary,
|
15
15
|
TableChatAgent,
|
16
16
|
TableChatAgentConfig,
|
17
|
-
|
17
|
+
PandasEvalTool,
|
18
18
|
)
|
19
19
|
from . import sql
|
20
20
|
from . import relevance_extractor_agent
|
@@ -38,7 +38,7 @@ __all__ = [
|
|
38
38
|
"dataframe_summary",
|
39
39
|
"TableChatAgent",
|
40
40
|
"TableChatAgentConfig",
|
41
|
-
"
|
41
|
+
"PandasEvalTool",
|
42
42
|
"sql",
|
43
43
|
"relevance_extractor_agent",
|
44
44
|
"doc_chat_agent",
|
@@ -2,10 +2,11 @@
|
|
2
2
|
Agent that supports asking queries about a tabular dataset, internally
|
3
3
|
represented as a Pandas dataframe. The `TableChatAgent` is configured with a
|
4
4
|
dataset, which can be a Pandas df, file or URL. The delimiter/separator
|
5
|
-
is auto-detected. In response to a user query, the Agent's LLM generates Pandas
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
is auto-detected. In response to a user query, the Agent's LLM generates a Pandas
|
6
|
+
expression (involving a dataframe `df`) to answer the query.
|
7
|
+
The expression is passed via the `pandas_eval` tool/function-call,
|
8
|
+
which is handled by the Agent's `pandas_eval` method. This method evaluates
|
9
|
+
the expression and returns the result as a string.
|
9
10
|
"""
|
10
11
|
|
11
12
|
import io
|
@@ -35,25 +36,26 @@ DEFAULT_TABLE_CHAT_SYSTEM_MESSAGE = f"""
|
|
35
36
|
You are a savvy data scientist, with expertise in analyzing tabular datasets,
|
36
37
|
using Python and the Pandas library for dataframe manipulation.
|
37
38
|
Since you do not have access to the dataframe 'df', you
|
38
|
-
will need to use the `
|
39
|
+
will need to use the `pandas_eval` tool/function-call to answer my questions.
|
39
40
|
Here is a summary of the dataframe:
|
40
41
|
{{summary}}
|
41
42
|
Do not assume any columns other than those shown.
|
42
|
-
In the
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
In the expression you submit to the `pandas_eval` tool/function,
|
44
|
+
you are allowed to use the variable 'df' to refer to the dataframe.
|
45
|
+
|
46
|
+
Sometimes you may not be able to answer the question in a single call to `pandas_eval`,
|
47
|
+
so you can use a series of calls to `pandas_eval` to build up the answer.
|
46
48
|
For example you may first want to know something about the possible values in a column.
|
47
49
|
|
48
50
|
If you receive a null or other unexpected result, see if you have made an assumption
|
49
|
-
in your code, and try another way, or use `
|
51
|
+
in your code, and try another way, or use `pandas_eval` to explore the dataframe
|
50
52
|
before submitting your final code.
|
51
53
|
|
52
54
|
Once you have the answer to the question, possibly after a few steps,
|
53
55
|
say {DONE} and show me the answer. If you receive an error message,
|
54
|
-
try using the `
|
56
|
+
try using the `pandas_eval` tool/function again with the corrected code.
|
55
57
|
|
56
|
-
VERY IMPORTANT: When using the `
|
58
|
+
VERY IMPORTANT: When using the `pandas_eval` tool/function, DO NOT EXPLAIN ANYTHING,
|
57
59
|
SIMPLY USE THE TOOL, with the CODE.
|
58
60
|
Start by asking me what I want to know about the data.
|
59
61
|
"""
|
@@ -123,22 +125,22 @@ class TableChatAgentConfig(ChatAgentConfig):
|
|
123
125
|
)
|
124
126
|
|
125
127
|
|
126
|
-
class
|
127
|
-
"""Tool/function to
|
128
|
+
class PandasEvalTool(ToolMessage):
|
129
|
+
"""Tool/function to evaluate a pandas expression involving a dataframe `df`"""
|
128
130
|
|
129
|
-
request: str = "
|
131
|
+
request: str = "pandas_eval"
|
130
132
|
purpose: str = """
|
131
|
-
To
|
133
|
+
To eval a pandas <expression> on the dataframe 'df' and
|
132
134
|
return the results to answer a question.
|
133
|
-
IMPORTANT:
|
135
|
+
IMPORTANT: the <expression> field should be a valid pandas expression.
|
134
136
|
"""
|
135
|
-
|
137
|
+
expression: str
|
136
138
|
|
137
139
|
@classmethod
|
138
140
|
def examples(cls) -> List["ToolMessage"]:
|
139
141
|
return [
|
140
|
-
cls(
|
141
|
-
cls(
|
142
|
+
cls(expression="df.head()"),
|
143
|
+
cls(expression="df[(df['gender'] == 'Male')]['income'].mean()"),
|
142
144
|
]
|
143
145
|
|
144
146
|
|
@@ -147,7 +149,7 @@ class TableChatAgent(ChatAgent):
|
|
147
149
|
Agent for chatting with a collection of documents.
|
148
150
|
"""
|
149
151
|
|
150
|
-
|
152
|
+
sent_expression: bool = False
|
151
153
|
|
152
154
|
def __init__(self, config: TableChatAgentConfig):
|
153
155
|
if isinstance(config.data, pd.DataFrame):
|
@@ -170,8 +172,8 @@ class TableChatAgent(ChatAgent):
|
|
170
172
|
{self.df.columns}
|
171
173
|
"""
|
172
174
|
)
|
173
|
-
# enable the agent to use and handle the
|
174
|
-
self.enable_message(
|
175
|
+
# enable the agent to use and handle the PandasEvalTool
|
176
|
+
self.enable_message(PandasEvalTool)
|
175
177
|
|
176
178
|
def user_response(
|
177
179
|
self,
|
@@ -179,44 +181,31 @@ class TableChatAgent(ChatAgent):
|
|
179
181
|
) -> Optional[ChatDocument]:
|
180
182
|
response = super().user_response(msg)
|
181
183
|
if response is not None and response.content != "":
|
182
|
-
self.
|
184
|
+
self.sent_expression = False
|
183
185
|
return response
|
184
186
|
|
185
|
-
def
|
187
|
+
def pandas_eval(self, msg: PandasEvalTool) -> str:
|
186
188
|
"""
|
187
|
-
Handle a
|
189
|
+
Handle a PandasEvalTool message by evaluating the `expression` field
|
190
|
+
and returning the result.
|
188
191
|
Args:
|
189
|
-
msg (
|
192
|
+
msg (PandasEvalTool): The tool-message to handle.
|
190
193
|
|
191
194
|
Returns:
|
192
195
|
str: The result of running the code along with any print output.
|
193
196
|
"""
|
194
|
-
self.
|
195
|
-
|
196
|
-
# Create a dictionary that maps 'df' to the actual DataFrame
|
197
|
+
self.sent_expression = True
|
198
|
+
exprn = msg.expression
|
197
199
|
local_vars = {"df": self.df}
|
198
|
-
|
199
200
|
# Create a string-based I/O stream
|
200
201
|
code_out = io.StringIO()
|
201
202
|
|
202
203
|
# Temporarily redirect standard output to our string-based I/O stream
|
203
204
|
sys.stdout = code_out
|
204
205
|
|
205
|
-
# Split the code into lines
|
206
|
-
lines = code.strip().split("\n")
|
207
|
-
|
208
|
-
lines = [
|
209
|
-
"import pandas as pd",
|
210
|
-
"import numpy as np",
|
211
|
-
] + lines
|
212
|
-
|
213
|
-
# Run all lines as statements except for the last one
|
214
|
-
for line in lines[:-1]:
|
215
|
-
exec(line, {}, local_vars)
|
216
|
-
|
217
206
|
# Evaluate the last line and get the result
|
218
207
|
try:
|
219
|
-
eval_result = pd.eval(
|
208
|
+
eval_result = pd.eval(exprn, local_dict=local_vars)
|
220
209
|
except Exception as e:
|
221
210
|
eval_result = f"ERROR: {type(e)}: {e}"
|
222
211
|
|
@@ -242,15 +231,15 @@ class TableChatAgent(ChatAgent):
|
|
242
231
|
def handle_message_fallback(
|
243
232
|
self, msg: str | ChatDocument
|
244
233
|
) -> str | ChatDocument | None:
|
245
|
-
"""Handle scenario where LLM forgets to say DONE or
|
234
|
+
"""Handle scenario where LLM forgets to say DONE or
|
235
|
+
forgets to use pandas_eval"""
|
246
236
|
if isinstance(msg, ChatDocument) and msg.metadata.sender == lr.Entity.LLM:
|
247
|
-
if self.
|
237
|
+
if self.sent_expression:
|
248
238
|
return DONE
|
249
239
|
else:
|
250
240
|
return """
|
251
|
-
You forgot to use the `
|
252
|
-
|
253
|
-
|
254
|
-
should be in the `code` field.
|
241
|
+
You forgot to use the `pandas_eval` tool/function
|
242
|
+
to find the answer.
|
243
|
+
Try again using the `pandas_eval` tool/function.
|
255
244
|
"""
|
256
245
|
return None
|
@@ -27,11 +27,20 @@ from langroid.agent.chat_document import (
|
|
27
27
|
ChatDocLoggerFields,
|
28
28
|
ChatDocMetaData,
|
29
29
|
ChatDocument,
|
30
|
+
StatusCode,
|
30
31
|
)
|
32
|
+
from langroid.cachedb.redis_cachedb import RedisCache, RedisCacheConfig
|
31
33
|
from langroid.mytypes import Entity
|
32
34
|
from langroid.parsing.parse_json import extract_top_level_json
|
33
35
|
from langroid.utils.configuration import settings
|
34
|
-
from langroid.utils.constants import
|
36
|
+
from langroid.utils.constants import (
|
37
|
+
DONE,
|
38
|
+
NO_ANSWER,
|
39
|
+
PASS,
|
40
|
+
PASS_TO,
|
41
|
+
SEND_TO,
|
42
|
+
USER_QUIT_STRINGS,
|
43
|
+
)
|
35
44
|
from langroid.utils.logging import RichFileLogger, setup_file_logger
|
36
45
|
|
37
46
|
logger = logging.getLogger(__name__)
|
@@ -73,6 +82,8 @@ class Task:
|
|
73
82
|
the value of `result()`, which is the final result of the task.
|
74
83
|
"""
|
75
84
|
|
85
|
+
# class variable called `cache` that is a RedisCache object
|
86
|
+
cache: RedisCache = RedisCache(RedisCacheConfig(fake=False))
|
76
87
|
def __init__(
|
77
88
|
self,
|
78
89
|
agent: Optional[Agent] = None,
|
@@ -141,7 +152,6 @@ class Task:
|
|
141
152
|
"""
|
142
153
|
if agent is None:
|
143
154
|
agent = ChatAgent()
|
144
|
-
|
145
155
|
self.callbacks = SimpleNamespace(
|
146
156
|
show_subtask_response=noop_fn,
|
147
157
|
set_parent_agent=noop_fn,
|
@@ -172,6 +182,7 @@ class Task:
|
|
172
182
|
agent.set_user_message(user_message)
|
173
183
|
self.max_cost: float = 0
|
174
184
|
self.max_tokens: int = 0
|
185
|
+
self.session_id: str = ""
|
175
186
|
self.logger: None | RichFileLogger = None
|
176
187
|
self.tsv_logger: None | logging.Logger = None
|
177
188
|
self.color_log: bool = False if settings.notebook else True
|
@@ -285,6 +296,54 @@ class Task:
|
|
285
296
|
def __str__(self) -> str:
|
286
297
|
return f"{self.name}"
|
287
298
|
|
299
|
+
def _cache_session_store(self, key: str, value: str) -> None:
|
300
|
+
"""
|
301
|
+
Cache a key-value pair for the current session.
|
302
|
+
E.g. key = "kill", value = "1"
|
303
|
+
"""
|
304
|
+
try:
|
305
|
+
self.cache.store(f"{self.session_id}:{key}", value)
|
306
|
+
except Exception as e:
|
307
|
+
logging.error(f"Error in Task._cache_session_store: {e}")
|
308
|
+
|
309
|
+
def _cache_session_lookup(self, key: str) -> Dict[str, Any] | str | None:
|
310
|
+
"""
|
311
|
+
Retrieve a value from the cache for the current session.
|
312
|
+
"""
|
313
|
+
session_id_key = f"{self.session_id}:{key}"
|
314
|
+
try:
|
315
|
+
cached_val = self.cache.retrieve(session_id_key)
|
316
|
+
except Exception as e:
|
317
|
+
logging.error(f"Error in Task._cache_session_lookup: {e}")
|
318
|
+
return None
|
319
|
+
return cached_val
|
320
|
+
|
321
|
+
def _is_kill(self) -> bool:
|
322
|
+
"""
|
323
|
+
Check if the current session is killed.
|
324
|
+
"""
|
325
|
+
return self._cache_session_lookup("kill") == "1"
|
326
|
+
|
327
|
+
def _set_alive(self) -> None:
|
328
|
+
"""
|
329
|
+
Initialize the kill status of the current session.
|
330
|
+
"""
|
331
|
+
self._cache_session_store("kill", "0")
|
332
|
+
|
333
|
+
@classmethod
|
334
|
+
def kill_session(cls, session_id=""):
|
335
|
+
"""
|
336
|
+
Kill the current session.
|
337
|
+
"""
|
338
|
+
session_id_kill_key = f"{session_id}:kill"
|
339
|
+
cls.cache.store(session_id_kill_key, "1")
|
340
|
+
|
341
|
+
def kill(self) -> None:
|
342
|
+
"""
|
343
|
+
Kill the task run associated with the current session.
|
344
|
+
"""
|
345
|
+
self._cache_session_store("kill", "1")
|
346
|
+
|
288
347
|
@property
|
289
348
|
def _level(self) -> int:
|
290
349
|
if self.caller is None:
|
@@ -378,6 +437,7 @@ class Task:
|
|
378
437
|
caller: None | Task = None,
|
379
438
|
max_cost: float = 0,
|
380
439
|
max_tokens: int = 0,
|
440
|
+
session_id: str = "",
|
381
441
|
) -> Optional[ChatDocument]:
|
382
442
|
"""Synchronous version of `run_async()`.
|
383
443
|
See `run_async()` for details."""
|
@@ -385,6 +445,9 @@ class Task:
|
|
385
445
|
self.n_stalled_steps = 0
|
386
446
|
self.max_cost = max_cost
|
387
447
|
self.max_tokens = max_tokens
|
448
|
+
self.session_id = session_id
|
449
|
+
self._set_alive()
|
450
|
+
|
388
451
|
assert (
|
389
452
|
msg is None or isinstance(msg, str) or isinstance(msg, ChatDocument)
|
390
453
|
), f"msg arg in Task.run() must be None, str, or ChatDocument, not {type(msg)}"
|
@@ -406,15 +469,19 @@ class Task:
|
|
406
469
|
i = 0
|
407
470
|
while True:
|
408
471
|
self.step()
|
409
|
-
|
472
|
+
done, status = self.done()
|
473
|
+
if done:
|
410
474
|
if self._level == 0 and not settings.quiet:
|
411
475
|
print("[magenta]Bye, hope this was useful!")
|
412
476
|
break
|
413
477
|
i += 1
|
414
478
|
if turns > 0 and i >= turns:
|
479
|
+
status = StatusCode.MAX_TURNS
|
415
480
|
break
|
416
481
|
|
417
482
|
final_result = self.result()
|
483
|
+
if final_result is not None:
|
484
|
+
final_result.metadata.status = status
|
418
485
|
self._post_run_loop()
|
419
486
|
return final_result
|
420
487
|
|
@@ -425,6 +492,7 @@ class Task:
|
|
425
492
|
caller: None | Task = None,
|
426
493
|
max_cost: float = 0,
|
427
494
|
max_tokens: int = 0,
|
495
|
+
session_id: str = "",
|
428
496
|
) -> Optional[ChatDocument]:
|
429
497
|
"""
|
430
498
|
Loop over `step()` until task is considered done or `turns` is reached.
|
@@ -443,6 +511,7 @@ class Task:
|
|
443
511
|
caller (Task|None): the calling task, if any
|
444
512
|
max_cost (float): max cost allowed for the task (default 0 -> no limit)
|
445
513
|
max_tokens (int): max tokens allowed for the task (default 0 -> no limit)
|
514
|
+
session_id (str): session id for the task
|
446
515
|
|
447
516
|
Returns:
|
448
517
|
Optional[ChatDocument]: valid result of the task.
|
@@ -456,6 +525,9 @@ class Task:
|
|
456
525
|
self.n_stalled_steps = 0
|
457
526
|
self.max_cost = max_cost
|
458
527
|
self.max_tokens = max_tokens
|
528
|
+
self.session_id = session_id
|
529
|
+
self._set_alive()
|
530
|
+
|
459
531
|
if (
|
460
532
|
isinstance(msg, ChatDocument)
|
461
533
|
and msg.metadata.recipient != ""
|
@@ -473,15 +545,19 @@ class Task:
|
|
473
545
|
i = 0
|
474
546
|
while True:
|
475
547
|
await self.step_async()
|
476
|
-
|
548
|
+
done, status = self.done()
|
549
|
+
if done:
|
477
550
|
if self._level == 0 and not settings.quiet:
|
478
551
|
print("[magenta]Bye, hope this was useful!")
|
479
552
|
break
|
480
553
|
i += 1
|
481
554
|
if turns > 0 and i >= turns:
|
555
|
+
status = StatusCode.MAX_TURNS
|
482
556
|
break
|
483
557
|
|
484
558
|
final_result = self.result()
|
559
|
+
if final_result is not None:
|
560
|
+
final_result.metadata.status = status
|
485
561
|
self._post_run_loop()
|
486
562
|
return final_result
|
487
563
|
|
@@ -942,6 +1018,7 @@ class Task:
|
|
942
1018
|
recipient = result_msg.metadata.recipient if result_msg else None
|
943
1019
|
responder = result_msg.metadata.parent_responder if result_msg else None
|
944
1020
|
tool_ids = result_msg.metadata.tool_ids if result_msg else []
|
1021
|
+
status = result_msg.metadata.status if result_msg else None
|
945
1022
|
|
946
1023
|
# regardless of which entity actually produced the result,
|
947
1024
|
# when we return the result, we set entity to USER
|
@@ -954,6 +1031,7 @@ class Task:
|
|
954
1031
|
source=Entity.USER,
|
955
1032
|
sender=Entity.USER,
|
956
1033
|
block=block,
|
1034
|
+
status=status,
|
957
1035
|
parent_responder=responder,
|
958
1036
|
sender_name=self.name,
|
959
1037
|
recipient=recipient,
|
@@ -1036,7 +1114,7 @@ class Task:
|
|
1036
1114
|
|
1037
1115
|
def done(
|
1038
1116
|
self, result: ChatDocument | None = None, r: Responder | None = None
|
1039
|
-
) -> bool:
|
1117
|
+
) -> Tuple[bool, StatusCode]:
|
1040
1118
|
"""
|
1041
1119
|
Check if task is done. This is the default behavior.
|
1042
1120
|
Derived classes can override this.
|
@@ -1046,26 +1124,29 @@ class Task:
|
|
1046
1124
|
Not used here, but could be used by derived classes.
|
1047
1125
|
Returns:
|
1048
1126
|
bool: True if task is done, False otherwise
|
1127
|
+
StatusCode: status code indicating why task is done
|
1049
1128
|
"""
|
1129
|
+
if self._is_kill():
|
1130
|
+
return (True, StatusCode.KILL)
|
1050
1131
|
result = result or self.pending_message
|
1051
1132
|
user_quit = (
|
1052
1133
|
result is not None
|
1053
|
-
and result.content in
|
1134
|
+
and result.content in USER_QUIT_STRINGS
|
1054
1135
|
and result.metadata.sender == Entity.USER
|
1055
1136
|
)
|
1056
1137
|
if self._level == 0 and self.only_user_quits_root:
|
1057
1138
|
# for top-level task, only user can quit out
|
1058
|
-
return user_quit
|
1139
|
+
return (user_quit, StatusCode.USER_QUIT if user_quit else StatusCode.OK)
|
1059
1140
|
|
1060
1141
|
if self.is_done:
|
1061
|
-
return True
|
1142
|
+
return (True, StatusCode.DONE)
|
1062
1143
|
|
1063
1144
|
if self.n_stalled_steps >= self.max_stalled_steps:
|
1064
1145
|
# we are stuck, so bail to avoid infinite loop
|
1065
1146
|
logger.warning(
|
1066
1147
|
f"Task {self.name} stuck for {self.max_stalled_steps} steps; exiting."
|
1067
1148
|
)
|
1068
|
-
return True
|
1149
|
+
return (True, StatusCode.STALLED)
|
1069
1150
|
|
1070
1151
|
if self.max_cost > 0 and self.agent.llm is not None:
|
1071
1152
|
try:
|
@@ -1073,7 +1154,7 @@ class Task:
|
|
1073
1154
|
logger.warning(
|
1074
1155
|
f"Task {self.name} cost exceeded {self.max_cost}; exiting."
|
1075
1156
|
)
|
1076
|
-
return True
|
1157
|
+
return (True, StatusCode.MAX_COST)
|
1077
1158
|
except Exception:
|
1078
1159
|
pass
|
1079
1160
|
|
@@ -1083,10 +1164,10 @@ class Task:
|
|
1083
1164
|
logger.warning(
|
1084
1165
|
f"Task {self.name} uses > {self.max_tokens} tokens; exiting."
|
1085
1166
|
)
|
1086
|
-
return True
|
1167
|
+
return (True, StatusCode.MAX_TOKENS)
|
1087
1168
|
except Exception:
|
1088
1169
|
pass
|
1089
|
-
|
1170
|
+
final = (
|
1090
1171
|
# no valid response from any entity/agent in current turn
|
1091
1172
|
result is None
|
1092
1173
|
# An entity decided task is done
|
@@ -1103,6 +1184,7 @@ class Task:
|
|
1103
1184
|
# )
|
1104
1185
|
or user_quit
|
1105
1186
|
)
|
1187
|
+
return (final, StatusCode.OK)
|
1106
1188
|
|
1107
1189
|
def valid(
|
1108
1190
|
self,
|
@@ -1120,7 +1202,7 @@ class Task:
|
|
1120
1202
|
|
1121
1203
|
# if task would be considered done given responder r's `result`,
|
1122
1204
|
# then consider the result valid.
|
1123
|
-
if result is not None and self.done(result, r):
|
1205
|
+
if result is not None and self.done(result, r)[0]:
|
1124
1206
|
return True
|
1125
1207
|
return (
|
1126
1208
|
result is not None
|
@@ -109,7 +109,7 @@ class RedisCache(CacheDB):
|
|
109
109
|
key (str): The key to retrieve the value for.
|
110
110
|
|
111
111
|
Returns:
|
112
|
-
dict: The value associated with the key.
|
112
|
+
dict|str|None: The value associated with the key.
|
113
113
|
"""
|
114
114
|
with self.redis_client() as client: # type: ignore
|
115
115
|
try:
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/lance_rag/query_planner_agent.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{langroid-0.1.233 → langroid-0.1.235}/langroid/agent/special/sql/utils/description_extractors.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{langroid-0.1.233 → langroid-0.1.235}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/prompt_formatter/hf_formatter.py
RENAMED
File without changes
|
{langroid-0.1.233 → langroid-0.1.235}/langroid/language_models/prompt_formatter/llama2_formatter.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|