langroid 0.58.2__py3-none-any.whl → 0.59.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langroid/agent/base.py +39 -17
- langroid/agent/base.py-e +2216 -0
- langroid/agent/callbacks/chainlit.py +2 -1
- langroid/agent/chat_agent.py +73 -55
- langroid/agent/chat_agent.py-e +2086 -0
- langroid/agent/chat_document.py +7 -7
- langroid/agent/chat_document.py-e +513 -0
- langroid/agent/openai_assistant.py +9 -9
- langroid/agent/openai_assistant.py-e +882 -0
- langroid/agent/special/arangodb/arangodb_agent.py +10 -18
- langroid/agent/special/arangodb/arangodb_agent.py-e +648 -0
- langroid/agent/special/arangodb/tools.py +3 -3
- langroid/agent/special/doc_chat_agent.py +16 -14
- langroid/agent/special/lance_rag/critic_agent.py +2 -2
- langroid/agent/special/lance_rag/query_planner_agent.py +4 -4
- langroid/agent/special/lance_tools.py +6 -5
- langroid/agent/special/lance_tools.py-e +61 -0
- langroid/agent/special/neo4j/neo4j_chat_agent.py +3 -7
- langroid/agent/special/neo4j/neo4j_chat_agent.py-e +430 -0
- langroid/agent/special/relevance_extractor_agent.py +1 -1
- langroid/agent/special/sql/sql_chat_agent.py +11 -3
- langroid/agent/task.py +9 -87
- langroid/agent/task.py-e +2418 -0
- langroid/agent/tool_message.py +33 -17
- langroid/agent/tool_message.py-e +400 -0
- langroid/agent/tools/file_tools.py +4 -2
- langroid/agent/tools/file_tools.py-e +234 -0
- langroid/agent/tools/mcp/fastmcp_client.py +19 -6
- langroid/agent/tools/mcp/fastmcp_client.py-e +584 -0
- langroid/agent/tools/orchestration.py +22 -17
- langroid/agent/tools/orchestration.py-e +301 -0
- langroid/agent/tools/recipient_tool.py +3 -3
- langroid/agent/tools/task_tool.py +22 -16
- langroid/agent/tools/task_tool.py-e +249 -0
- langroid/agent/xml_tool_message.py +90 -35
- langroid/agent/xml_tool_message.py-e +392 -0
- langroid/cachedb/base.py +1 -1
- langroid/embedding_models/base.py +2 -2
- langroid/embedding_models/models.py +3 -7
- langroid/embedding_models/models.py-e +563 -0
- langroid/exceptions.py +4 -1
- langroid/language_models/azure_openai.py +2 -2
- langroid/language_models/azure_openai.py-e +134 -0
- langroid/language_models/base.py +6 -4
- langroid/language_models/base.py-e +812 -0
- langroid/language_models/client_cache.py +64 -0
- langroid/language_models/config.py +2 -4
- langroid/language_models/config.py-e +18 -0
- langroid/language_models/model_info.py +9 -1
- langroid/language_models/model_info.py-e +483 -0
- langroid/language_models/openai_gpt.py +119 -20
- langroid/language_models/openai_gpt.py-e +2280 -0
- langroid/language_models/provider_params.py +3 -22
- langroid/language_models/provider_params.py-e +153 -0
- langroid/mytypes.py +11 -4
- langroid/mytypes.py-e +132 -0
- langroid/parsing/code_parser.py +1 -1
- langroid/parsing/file_attachment.py +1 -1
- langroid/parsing/file_attachment.py-e +246 -0
- langroid/parsing/md_parser.py +14 -4
- langroid/parsing/md_parser.py-e +574 -0
- langroid/parsing/parser.py +22 -7
- langroid/parsing/parser.py-e +410 -0
- langroid/parsing/repo_loader.py +3 -1
- langroid/parsing/repo_loader.py-e +812 -0
- langroid/parsing/search.py +1 -1
- langroid/parsing/url_loader.py +17 -51
- langroid/parsing/url_loader.py-e +683 -0
- langroid/parsing/urls.py +5 -4
- langroid/parsing/urls.py-e +279 -0
- langroid/prompts/prompts_config.py +1 -1
- langroid/pydantic_v1/__init__.py +45 -6
- langroid/pydantic_v1/__init__.py-e +36 -0
- langroid/pydantic_v1/main.py +11 -4
- langroid/pydantic_v1/main.py-e +11 -0
- langroid/utils/configuration.py +13 -11
- langroid/utils/configuration.py-e +141 -0
- langroid/utils/constants.py +1 -1
- langroid/utils/constants.py-e +32 -0
- langroid/utils/globals.py +21 -5
- langroid/utils/globals.py-e +49 -0
- langroid/utils/html_logger.py +2 -1
- langroid/utils/html_logger.py-e +825 -0
- langroid/utils/object_registry.py +1 -1
- langroid/utils/object_registry.py-e +66 -0
- langroid/utils/pydantic_utils.py +55 -28
- langroid/utils/pydantic_utils.py-e +602 -0
- langroid/utils/types.py +2 -2
- langroid/utils/types.py-e +113 -0
- langroid/vector_store/base.py +3 -3
- langroid/vector_store/lancedb.py +5 -5
- langroid/vector_store/lancedb.py-e +404 -0
- langroid/vector_store/meilisearch.py +2 -2
- langroid/vector_store/pineconedb.py +4 -4
- langroid/vector_store/pineconedb.py-e +427 -0
- langroid/vector_store/postgres.py +1 -1
- langroid/vector_store/qdrantdb.py +3 -3
- langroid/vector_store/weaviatedb.py +1 -1
- {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/METADATA +3 -2
- langroid-0.59.0b1.dist-info/RECORD +181 -0
- langroid/agent/special/doc_chat_task.py +0 -0
- langroid/mcp/__init__.py +0 -1
- langroid/mcp/server/__init__.py +0 -1
- langroid-0.58.2.dist-info/RECORD +0 -145
- {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/WHEEL +0 -0
- {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,234 @@
|
|
1
|
+
from contextlib import chdir
|
2
|
+
from pathlib import Path
|
3
|
+
from textwrap import dedent
|
4
|
+
from typing import Callable, List, Tuple, Type
|
5
|
+
|
6
|
+
import git
|
7
|
+
|
8
|
+
from langroid.agent.tool_message import ToolMessage
|
9
|
+
from langroid.agent.xml_tool_message import XMLToolMessage
|
10
|
+
from pydantic import Field
|
11
|
+
from langroid.utils.git_utils import git_commit_file
|
12
|
+
from langroid.utils.system import create_file, list_dir, read_file
|
13
|
+
|
14
|
+
|
15
|
+
class ReadFileTool(ToolMessage):
|
16
|
+
request: str = "read_file_tool"
|
17
|
+
purpose: str = "Read the contents of a <file_path>"
|
18
|
+
file_path: str
|
19
|
+
|
20
|
+
_line_nums: bool = True # whether to add line numbers to the content
|
21
|
+
_curr_dir: Callable[[], str] | None = None
|
22
|
+
|
23
|
+
@classmethod
|
24
|
+
def create(
|
25
|
+
cls,
|
26
|
+
get_curr_dir: Callable[[], str] | None,
|
27
|
+
) -> Type["ReadFileTool"]:
|
28
|
+
"""
|
29
|
+
Create a subclass of ReadFileTool for a specific directory
|
30
|
+
|
31
|
+
Args:
|
32
|
+
get_curr_dir (callable): A function that returns the current directory.
|
33
|
+
|
34
|
+
Returns:
|
35
|
+
Type[ReadFileTool]: A subclass of the ReadFileTool class, specifically
|
36
|
+
for the current directory.
|
37
|
+
"""
|
38
|
+
|
39
|
+
class CustomReadFileTool(cls): # type: ignore
|
40
|
+
_curr_dir: Callable[[], str] | None = (
|
41
|
+
staticmethod(get_curr_dir) if get_curr_dir else None
|
42
|
+
)
|
43
|
+
|
44
|
+
return CustomReadFileTool
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def examples(cls) -> List[ToolMessage | tuple[str, ToolMessage]]:
|
48
|
+
return [
|
49
|
+
cls(file_path="src/lib.rs"),
|
50
|
+
(
|
51
|
+
"I want to read the contents of src/main.rs",
|
52
|
+
cls(file_path="src/main.rs"),
|
53
|
+
),
|
54
|
+
]
|
55
|
+
|
56
|
+
def handle(self) -> str:
|
57
|
+
# return contents as str for LLM to read
|
58
|
+
# ASSUME: file_path should be relative to the curr_dir
|
59
|
+
try:
|
60
|
+
dir = (self._curr_dir and self._curr_dir()) or Path.cwd()
|
61
|
+
with chdir(dir):
|
62
|
+
# if file doesn't exist, return an error message
|
63
|
+
content = read_file(self.file_path, self._line_nums)
|
64
|
+
line_num_str = ""
|
65
|
+
if self._line_nums:
|
66
|
+
line_num_str = "(Line numbers added for reference only!)"
|
67
|
+
return f"""
|
68
|
+
CONTENTS of {self.file_path}:
|
69
|
+
{line_num_str}
|
70
|
+
---------------------------
|
71
|
+
{content}
|
72
|
+
"""
|
73
|
+
except FileNotFoundError:
|
74
|
+
return f"File not found: {self.file_path}"
|
75
|
+
|
76
|
+
|
77
|
+
class WriteFileTool(XMLToolMessage):
|
78
|
+
request: str = "write_file_tool"
|
79
|
+
purpose: str = """
|
80
|
+
Tool for writing <content> in a certain <language> to a <file_path>
|
81
|
+
"""
|
82
|
+
|
83
|
+
file_path: str = Field(..., description="The path to the file to write the content")
|
84
|
+
|
85
|
+
language: str = Field(
|
86
|
+
default="",
|
87
|
+
description="""
|
88
|
+
The language of the content; could be human language or programming language
|
89
|
+
""",
|
90
|
+
)
|
91
|
+
content: str = Field(
|
92
|
+
...,
|
93
|
+
description="The content to write to the file",
|
94
|
+
verbatim=True, # preserve the content as is; uses CDATA section in XML
|
95
|
+
)
|
96
|
+
_curr_dir: Callable[[], str] | None = None
|
97
|
+
_git_repo: Callable[[], git.Repo] | None = None
|
98
|
+
_commit_message: str = "Agent write file tool"
|
99
|
+
|
100
|
+
@classmethod
|
101
|
+
def create(
|
102
|
+
cls,
|
103
|
+
get_curr_dir: Callable[[], str] | None,
|
104
|
+
get_git_repo: Callable[[], str] | None,
|
105
|
+
) -> Type["WriteFileTool"]:
|
106
|
+
"""
|
107
|
+
Create a subclass of WriteFileTool with the current directory and git repo.
|
108
|
+
|
109
|
+
Args:
|
110
|
+
get_curr_dir (callable): A function that returns the current directory.
|
111
|
+
get_git_repo (callable): A function that returns the git repo.
|
112
|
+
|
113
|
+
Returns:
|
114
|
+
Type[WriteFileTool]: A subclass of the WriteFileTool class, specifically
|
115
|
+
for the current directory and git repo.
|
116
|
+
"""
|
117
|
+
|
118
|
+
class CustomWriteFileTool(cls): # type: ignore
|
119
|
+
_curr_dir: Callable[[], str] | None = (
|
120
|
+
staticmethod(get_curr_dir) if get_curr_dir else None
|
121
|
+
)
|
122
|
+
_git_repo: Callable[[], str] | None = (
|
123
|
+
staticmethod(get_git_repo) if get_git_repo else None
|
124
|
+
)
|
125
|
+
|
126
|
+
return CustomWriteFileTool
|
127
|
+
|
128
|
+
@classmethod
|
129
|
+
def examples(cls) -> List[ToolMessage | Tuple[str, ToolMessage]]:
|
130
|
+
return [
|
131
|
+
(
|
132
|
+
"""
|
133
|
+
I want to define a simple hello world python function
|
134
|
+
in a file "mycode/hello.py"
|
135
|
+
""",
|
136
|
+
cls(
|
137
|
+
file_path="mycode/hello.py",
|
138
|
+
language="python",
|
139
|
+
content="""
|
140
|
+
def hello():
|
141
|
+
print("Hello, World!")
|
142
|
+
""",
|
143
|
+
),
|
144
|
+
),
|
145
|
+
cls(
|
146
|
+
file_path="src/lib.rs",
|
147
|
+
language="rust",
|
148
|
+
content="""
|
149
|
+
fn main() {
|
150
|
+
println!("Hello, World!");
|
151
|
+
}
|
152
|
+
""",
|
153
|
+
),
|
154
|
+
cls(
|
155
|
+
file_path="docs/intro.txt",
|
156
|
+
content="""
|
157
|
+
# Introduction
|
158
|
+
This is the first sentence of the introduction.
|
159
|
+
""",
|
160
|
+
),
|
161
|
+
]
|
162
|
+
|
163
|
+
def handle(self) -> str:
|
164
|
+
curr_dir = (self._curr_dir and self._curr_dir()) or Path.cwd()
|
165
|
+
with chdir(curr_dir):
|
166
|
+
create_file(self.file_path, self.content)
|
167
|
+
msg = f"Content written to {self.file_path}"
|
168
|
+
# possibly commit the file
|
169
|
+
if self._git_repo:
|
170
|
+
git_commit_file(
|
171
|
+
self._git_repo(),
|
172
|
+
self.file_path,
|
173
|
+
self._commit_message,
|
174
|
+
)
|
175
|
+
msg += " and committed"
|
176
|
+
return msg
|
177
|
+
|
178
|
+
|
179
|
+
class ListDirTool(ToolMessage):
|
180
|
+
request: str = "list_dir_tool"
|
181
|
+
purpose: str = "List the contents of a <dir_path>"
|
182
|
+
dir_path: str
|
183
|
+
|
184
|
+
_curr_dir: Callable[[], str] | None = None
|
185
|
+
|
186
|
+
@classmethod
|
187
|
+
def create(
|
188
|
+
cls,
|
189
|
+
get_curr_dir: Callable[[], str] | None,
|
190
|
+
) -> Type["ReadFileTool"]:
|
191
|
+
"""
|
192
|
+
Create a subclass of ListDirTool for a specific directory
|
193
|
+
|
194
|
+
Args:
|
195
|
+
get_curr_dir (callable): A function that returns the current directory.
|
196
|
+
|
197
|
+
Returns:
|
198
|
+
Type[ReadFileTool]: A subclass of the ReadFileTool class, specifically
|
199
|
+
for the current directory.
|
200
|
+
"""
|
201
|
+
|
202
|
+
class CustomListDirTool(cls): # type: ignore
|
203
|
+
_curr_dir: Callable[[], str] | None = (
|
204
|
+
staticmethod(get_curr_dir) if get_curr_dir else None
|
205
|
+
)
|
206
|
+
|
207
|
+
return CustomListDirTool
|
208
|
+
|
209
|
+
@classmethod
|
210
|
+
def examples(cls) -> List[ToolMessage | tuple[str, ToolMessage]]:
|
211
|
+
return [
|
212
|
+
cls(dir_path="src"),
|
213
|
+
(
|
214
|
+
"I want to list the contents of src",
|
215
|
+
cls(dir_path="src"),
|
216
|
+
),
|
217
|
+
]
|
218
|
+
|
219
|
+
def handle(self) -> str:
|
220
|
+
# ASSUME: dir_path should be relative to the curr_dir_path
|
221
|
+
dir = (self._curr_dir and self._curr_dir()) or Path.cwd()
|
222
|
+
with chdir(dir):
|
223
|
+
contents = list_dir(self.dir_path)
|
224
|
+
|
225
|
+
if not contents:
|
226
|
+
return f"Directory not found or empty: {self.dir_path}"
|
227
|
+
contents_str = "\n".join(contents)
|
228
|
+
return dedent(
|
229
|
+
f"""
|
230
|
+
LISTING of directory {self.dir_path}:
|
231
|
+
---------------------------
|
232
|
+
{contents_str}
|
233
|
+
""".strip()
|
234
|
+
)
|
@@ -27,12 +27,12 @@ from mcp.types import (
|
|
27
27
|
TextResourceContents,
|
28
28
|
Tool,
|
29
29
|
)
|
30
|
+
from pydantic import AnyUrl, BaseModel, Field, create_model
|
30
31
|
|
31
32
|
from langroid.agent.base import Agent
|
32
33
|
from langroid.agent.chat_document import ChatDocument
|
33
34
|
from langroid.agent.tool_message import ToolMessage
|
34
35
|
from langroid.parsing.file_attachment import FileAttachment
|
35
|
-
from langroid.pydantic_v1 import AnyUrl, BaseModel, Field, create_model
|
36
36
|
|
37
37
|
load_dotenv() # load environment variables from .env
|
38
38
|
|
@@ -253,11 +253,24 @@ class FastMCPClient:
|
|
253
253
|
from langroid.agent.tools.mcp.fastmcp_client import FastMCPClient
|
254
254
|
|
255
255
|
# pack up the payload
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
256
|
+
# Get exclude fields from model config with proper type checking
|
257
|
+
exclude_fields = set()
|
258
|
+
model_config = getattr(itself, "model_config", {})
|
259
|
+
if (
|
260
|
+
isinstance(model_config, dict)
|
261
|
+
and "json_schema_extra" in model_config
|
262
|
+
and model_config["json_schema_extra"] is not None
|
263
|
+
and isinstance(model_config["json_schema_extra"], dict)
|
264
|
+
and "exclude" in model_config["json_schema_extra"]
|
265
|
+
):
|
266
|
+
exclude_list = model_config["json_schema_extra"]["exclude"]
|
267
|
+
if isinstance(exclude_list, (list, set, tuple)):
|
268
|
+
exclude_fields = set(exclude_list)
|
269
|
+
|
270
|
+
# Add standard excluded fields
|
271
|
+
exclude_fields.update(["request", "purpose"])
|
272
|
+
|
273
|
+
payload = itself.model_dump(exclude=exclude_fields)
|
261
274
|
|
262
275
|
# restore any renamed fields
|
263
276
|
for orig, new in itself.__class__._renamed_fields.items(): # type: ignore
|