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.
Files changed (106) hide show
  1. langroid/agent/base.py +39 -17
  2. langroid/agent/base.py-e +2216 -0
  3. langroid/agent/callbacks/chainlit.py +2 -1
  4. langroid/agent/chat_agent.py +73 -55
  5. langroid/agent/chat_agent.py-e +2086 -0
  6. langroid/agent/chat_document.py +7 -7
  7. langroid/agent/chat_document.py-e +513 -0
  8. langroid/agent/openai_assistant.py +9 -9
  9. langroid/agent/openai_assistant.py-e +882 -0
  10. langroid/agent/special/arangodb/arangodb_agent.py +10 -18
  11. langroid/agent/special/arangodb/arangodb_agent.py-e +648 -0
  12. langroid/agent/special/arangodb/tools.py +3 -3
  13. langroid/agent/special/doc_chat_agent.py +16 -14
  14. langroid/agent/special/lance_rag/critic_agent.py +2 -2
  15. langroid/agent/special/lance_rag/query_planner_agent.py +4 -4
  16. langroid/agent/special/lance_tools.py +6 -5
  17. langroid/agent/special/lance_tools.py-e +61 -0
  18. langroid/agent/special/neo4j/neo4j_chat_agent.py +3 -7
  19. langroid/agent/special/neo4j/neo4j_chat_agent.py-e +430 -0
  20. langroid/agent/special/relevance_extractor_agent.py +1 -1
  21. langroid/agent/special/sql/sql_chat_agent.py +11 -3
  22. langroid/agent/task.py +9 -87
  23. langroid/agent/task.py-e +2418 -0
  24. langroid/agent/tool_message.py +33 -17
  25. langroid/agent/tool_message.py-e +400 -0
  26. langroid/agent/tools/file_tools.py +4 -2
  27. langroid/agent/tools/file_tools.py-e +234 -0
  28. langroid/agent/tools/mcp/fastmcp_client.py +19 -6
  29. langroid/agent/tools/mcp/fastmcp_client.py-e +584 -0
  30. langroid/agent/tools/orchestration.py +22 -17
  31. langroid/agent/tools/orchestration.py-e +301 -0
  32. langroid/agent/tools/recipient_tool.py +3 -3
  33. langroid/agent/tools/task_tool.py +22 -16
  34. langroid/agent/tools/task_tool.py-e +249 -0
  35. langroid/agent/xml_tool_message.py +90 -35
  36. langroid/agent/xml_tool_message.py-e +392 -0
  37. langroid/cachedb/base.py +1 -1
  38. langroid/embedding_models/base.py +2 -2
  39. langroid/embedding_models/models.py +3 -7
  40. langroid/embedding_models/models.py-e +563 -0
  41. langroid/exceptions.py +4 -1
  42. langroid/language_models/azure_openai.py +2 -2
  43. langroid/language_models/azure_openai.py-e +134 -0
  44. langroid/language_models/base.py +6 -4
  45. langroid/language_models/base.py-e +812 -0
  46. langroid/language_models/client_cache.py +64 -0
  47. langroid/language_models/config.py +2 -4
  48. langroid/language_models/config.py-e +18 -0
  49. langroid/language_models/model_info.py +9 -1
  50. langroid/language_models/model_info.py-e +483 -0
  51. langroid/language_models/openai_gpt.py +119 -20
  52. langroid/language_models/openai_gpt.py-e +2280 -0
  53. langroid/language_models/provider_params.py +3 -22
  54. langroid/language_models/provider_params.py-e +153 -0
  55. langroid/mytypes.py +11 -4
  56. langroid/mytypes.py-e +132 -0
  57. langroid/parsing/code_parser.py +1 -1
  58. langroid/parsing/file_attachment.py +1 -1
  59. langroid/parsing/file_attachment.py-e +246 -0
  60. langroid/parsing/md_parser.py +14 -4
  61. langroid/parsing/md_parser.py-e +574 -0
  62. langroid/parsing/parser.py +22 -7
  63. langroid/parsing/parser.py-e +410 -0
  64. langroid/parsing/repo_loader.py +3 -1
  65. langroid/parsing/repo_loader.py-e +812 -0
  66. langroid/parsing/search.py +1 -1
  67. langroid/parsing/url_loader.py +17 -51
  68. langroid/parsing/url_loader.py-e +683 -0
  69. langroid/parsing/urls.py +5 -4
  70. langroid/parsing/urls.py-e +279 -0
  71. langroid/prompts/prompts_config.py +1 -1
  72. langroid/pydantic_v1/__init__.py +45 -6
  73. langroid/pydantic_v1/__init__.py-e +36 -0
  74. langroid/pydantic_v1/main.py +11 -4
  75. langroid/pydantic_v1/main.py-e +11 -0
  76. langroid/utils/configuration.py +13 -11
  77. langroid/utils/configuration.py-e +141 -0
  78. langroid/utils/constants.py +1 -1
  79. langroid/utils/constants.py-e +32 -0
  80. langroid/utils/globals.py +21 -5
  81. langroid/utils/globals.py-e +49 -0
  82. langroid/utils/html_logger.py +2 -1
  83. langroid/utils/html_logger.py-e +825 -0
  84. langroid/utils/object_registry.py +1 -1
  85. langroid/utils/object_registry.py-e +66 -0
  86. langroid/utils/pydantic_utils.py +55 -28
  87. langroid/utils/pydantic_utils.py-e +602 -0
  88. langroid/utils/types.py +2 -2
  89. langroid/utils/types.py-e +113 -0
  90. langroid/vector_store/base.py +3 -3
  91. langroid/vector_store/lancedb.py +5 -5
  92. langroid/vector_store/lancedb.py-e +404 -0
  93. langroid/vector_store/meilisearch.py +2 -2
  94. langroid/vector_store/pineconedb.py +4 -4
  95. langroid/vector_store/pineconedb.py-e +427 -0
  96. langroid/vector_store/postgres.py +1 -1
  97. langroid/vector_store/qdrantdb.py +3 -3
  98. langroid/vector_store/weaviatedb.py +1 -1
  99. {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/METADATA +3 -2
  100. langroid-0.59.0b1.dist-info/RECORD +181 -0
  101. langroid/agent/special/doc_chat_task.py +0 -0
  102. langroid/mcp/__init__.py +0 -1
  103. langroid/mcp/server/__init__.py +0 -1
  104. langroid-0.58.2.dist-info/RECORD +0 -145
  105. {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/WHEEL +0 -0
  106. {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
- payload = itself.dict(
257
- exclude=itself.Config.schema_extra["exclude"].union(
258
- ["request", "purpose"]
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