agno 2.3.25__py3-none-any.whl → 2.4.0__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 (128) hide show
  1. agno/agent/__init__.py +4 -0
  2. agno/agent/agent.py +1428 -558
  3. agno/agent/remote.py +13 -0
  4. agno/db/base.py +339 -0
  5. agno/db/postgres/async_postgres.py +116 -12
  6. agno/db/postgres/postgres.py +1229 -25
  7. agno/db/postgres/schemas.py +48 -1
  8. agno/db/sqlite/async_sqlite.py +119 -4
  9. agno/db/sqlite/schemas.py +51 -0
  10. agno/db/sqlite/sqlite.py +1173 -13
  11. agno/db/utils.py +37 -1
  12. agno/knowledge/__init__.py +4 -0
  13. agno/knowledge/chunking/code.py +1 -1
  14. agno/knowledge/chunking/semantic.py +1 -1
  15. agno/knowledge/chunking/strategy.py +4 -0
  16. agno/knowledge/filesystem.py +412 -0
  17. agno/knowledge/knowledge.py +2767 -2254
  18. agno/knowledge/protocol.py +134 -0
  19. agno/knowledge/reader/arxiv_reader.py +2 -2
  20. agno/knowledge/reader/base.py +9 -7
  21. agno/knowledge/reader/csv_reader.py +5 -5
  22. agno/knowledge/reader/docx_reader.py +2 -2
  23. agno/knowledge/reader/field_labeled_csv_reader.py +2 -2
  24. agno/knowledge/reader/firecrawl_reader.py +2 -2
  25. agno/knowledge/reader/json_reader.py +2 -2
  26. agno/knowledge/reader/markdown_reader.py +2 -2
  27. agno/knowledge/reader/pdf_reader.py +5 -4
  28. agno/knowledge/reader/pptx_reader.py +2 -2
  29. agno/knowledge/reader/reader_factory.py +110 -0
  30. agno/knowledge/reader/s3_reader.py +2 -2
  31. agno/knowledge/reader/tavily_reader.py +2 -2
  32. agno/knowledge/reader/text_reader.py +2 -2
  33. agno/knowledge/reader/web_search_reader.py +2 -2
  34. agno/knowledge/reader/website_reader.py +5 -3
  35. agno/knowledge/reader/wikipedia_reader.py +2 -2
  36. agno/knowledge/reader/youtube_reader.py +2 -2
  37. agno/knowledge/utils.py +37 -29
  38. agno/learn/__init__.py +6 -0
  39. agno/learn/machine.py +35 -0
  40. agno/learn/schemas.py +82 -11
  41. agno/learn/stores/__init__.py +3 -0
  42. agno/learn/stores/decision_log.py +1156 -0
  43. agno/learn/stores/learned_knowledge.py +6 -6
  44. agno/models/anthropic/claude.py +24 -0
  45. agno/models/aws/bedrock.py +20 -0
  46. agno/models/base.py +48 -4
  47. agno/models/cohere/chat.py +25 -0
  48. agno/models/google/gemini.py +50 -5
  49. agno/models/litellm/chat.py +38 -0
  50. agno/models/openai/chat.py +7 -0
  51. agno/models/openrouter/openrouter.py +46 -0
  52. agno/models/response.py +16 -0
  53. agno/os/app.py +83 -44
  54. agno/os/middleware/__init__.py +2 -0
  55. agno/os/middleware/trailing_slash.py +27 -0
  56. agno/os/router.py +1 -0
  57. agno/os/routers/agents/router.py +29 -16
  58. agno/os/routers/agents/schema.py +6 -4
  59. agno/os/routers/components/__init__.py +3 -0
  60. agno/os/routers/components/components.py +466 -0
  61. agno/os/routers/evals/schemas.py +4 -3
  62. agno/os/routers/health.py +3 -3
  63. agno/os/routers/knowledge/knowledge.py +3 -3
  64. agno/os/routers/memory/schemas.py +4 -2
  65. agno/os/routers/metrics/metrics.py +9 -11
  66. agno/os/routers/metrics/schemas.py +10 -6
  67. agno/os/routers/registry/__init__.py +3 -0
  68. agno/os/routers/registry/registry.py +337 -0
  69. agno/os/routers/teams/router.py +20 -8
  70. agno/os/routers/teams/schema.py +6 -4
  71. agno/os/routers/traces/traces.py +5 -5
  72. agno/os/routers/workflows/router.py +38 -11
  73. agno/os/routers/workflows/schema.py +1 -1
  74. agno/os/schema.py +92 -26
  75. agno/os/utils.py +133 -16
  76. agno/reasoning/anthropic.py +2 -2
  77. agno/reasoning/azure_ai_foundry.py +2 -2
  78. agno/reasoning/deepseek.py +2 -2
  79. agno/reasoning/default.py +6 -7
  80. agno/reasoning/gemini.py +2 -2
  81. agno/reasoning/helpers.py +6 -7
  82. agno/reasoning/manager.py +4 -10
  83. agno/reasoning/ollama.py +2 -2
  84. agno/reasoning/openai.py +2 -2
  85. agno/reasoning/vertexai.py +2 -2
  86. agno/registry/__init__.py +3 -0
  87. agno/registry/registry.py +68 -0
  88. agno/run/agent.py +57 -0
  89. agno/run/base.py +7 -0
  90. agno/run/team.py +57 -0
  91. agno/skills/agent_skills.py +10 -3
  92. agno/team/__init__.py +3 -1
  93. agno/team/team.py +1276 -326
  94. agno/tools/duckduckgo.py +25 -71
  95. agno/tools/exa.py +0 -21
  96. agno/tools/function.py +35 -83
  97. agno/tools/knowledge.py +9 -4
  98. agno/tools/mem0.py +11 -10
  99. agno/tools/memory.py +47 -46
  100. agno/tools/parallel.py +0 -7
  101. agno/tools/reasoning.py +30 -23
  102. agno/tools/tavily.py +4 -1
  103. agno/tools/websearch.py +93 -0
  104. agno/tools/website.py +1 -1
  105. agno/tools/wikipedia.py +1 -1
  106. agno/tools/workflow.py +48 -47
  107. agno/utils/agent.py +42 -5
  108. agno/utils/events.py +160 -2
  109. agno/utils/print_response/agent.py +0 -31
  110. agno/utils/print_response/team.py +0 -2
  111. agno/utils/print_response/workflow.py +0 -2
  112. agno/utils/team.py +61 -11
  113. agno/vectordb/lancedb/lance_db.py +4 -1
  114. agno/vectordb/mongodb/mongodb.py +1 -1
  115. agno/vectordb/qdrant/qdrant.py +4 -4
  116. agno/workflow/__init__.py +3 -1
  117. agno/workflow/condition.py +0 -21
  118. agno/workflow/loop.py +0 -21
  119. agno/workflow/parallel.py +0 -21
  120. agno/workflow/router.py +0 -21
  121. agno/workflow/step.py +117 -24
  122. agno/workflow/steps.py +0 -21
  123. agno/workflow/workflow.py +625 -63
  124. {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/METADATA +46 -76
  125. {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/RECORD +128 -117
  126. {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/WHEEL +0 -0
  127. {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/licenses/LICENSE +0 -0
  128. {agno-2.3.25.dist-info → agno-2.4.0.dist-info}/top_level.txt +0 -0
agno/db/utils.py CHANGED
@@ -2,11 +2,15 @@
2
2
 
3
3
  import json
4
4
  from datetime import date, datetime
5
- from typing import Any, Dict
5
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Union
6
6
  from uuid import UUID
7
7
 
8
8
  from agno.models.message import Message
9
9
  from agno.models.metrics import Metrics
10
+ from agno.utils.log import log_error, log_warning
11
+
12
+ if TYPE_CHECKING:
13
+ from agno.db.base import BaseDb
10
14
 
11
15
 
12
16
  def get_sort_value(record: Dict[str, Any], sort_by: str) -> Any:
@@ -138,3 +142,35 @@ def deserialize_session_json_fields(session: dict) -> dict:
138
142
  log_warning(f"Warning: Could not parse runs as JSON, keeping as string: {e}")
139
143
 
140
144
  return session
145
+
146
+
147
+ def db_from_dict(db_data: Dict[str, Any]) -> Optional[Union["BaseDb"]]:
148
+ """
149
+ Create a database instance from a dictionary.
150
+
151
+ Args:
152
+ db_data: Dictionary containing database configuration
153
+
154
+ Returns:
155
+ Database instance or None if creation fails
156
+ """
157
+ db_type = db_data.get("type")
158
+ if db_type == "postgres":
159
+ try:
160
+ from agno.db.postgres import PostgresDb
161
+
162
+ return PostgresDb.from_dict(db_data)
163
+ except Exception as e:
164
+ log_error(f"Error reconstructing PostgresDb from dictionary: {e}")
165
+ return None
166
+ elif db_type == "sqlite":
167
+ try:
168
+ from agno.db.sqlite import SqliteDb
169
+
170
+ return SqliteDb.from_dict(db_data)
171
+ except Exception as e:
172
+ log_error(f"Error reconstructing SqliteDb from dictionary: {e}")
173
+ return None
174
+ else:
175
+ log_warning(f"Unknown database type: {db_type}")
176
+ return None
@@ -1,5 +1,9 @@
1
+ from agno.knowledge.filesystem import FileSystemKnowledge
1
2
  from agno.knowledge.knowledge import Knowledge
3
+ from agno.knowledge.protocol import KnowledgeProtocol
2
4
 
3
5
  __all__ = [
6
+ "FileSystemKnowledge",
4
7
  "Knowledge",
8
+ "KnowledgeProtocol",
5
9
  ]
@@ -6,7 +6,7 @@ try:
6
6
  except ImportError:
7
7
  raise ImportError(
8
8
  "`chonkie` is required for code chunking. "
9
- "Please install it using `pip install chonkie[code]` to use CodeChunking."
9
+ 'Please install it using `pip install "chonkie[code]"` to use CodeChunking.'
10
10
  )
11
11
 
12
12
  from agno.knowledge.chunking.strategy import ChunkingStrategy
@@ -11,7 +11,7 @@ try:
11
11
  except ImportError:
12
12
  raise ImportError(
13
13
  "`chonkie` is required for semantic chunking. "
14
- "Please install it using `pip install chonkie` to use SemanticChunking."
14
+ 'Please install it using `pip install "chonkie[semantic]"` to use SemanticChunking.'
15
15
  )
16
16
 
17
17
  from agno.knowledge.chunking.strategy import ChunkingStrategy
@@ -12,6 +12,10 @@ class ChunkingStrategy(ABC):
12
12
  def chunk(self, document: Document) -> List[Document]:
13
13
  raise NotImplementedError
14
14
 
15
+ async def achunk(self, document: Document) -> List[Document]:
16
+ """Async version of chunk. Override for truly async implementations."""
17
+ return self.chunk(document)
18
+
15
19
  def clean_text(self, text: str) -> str:
16
20
  """Clean the text by replacing multiple newlines with a single newline"""
17
21
  import re
@@ -0,0 +1,412 @@
1
+ """
2
+ FileSystem Knowledge
3
+ ====================
4
+ A Knowledge implementation that allows retrieval from files in a local directory.
5
+
6
+ Implements the KnowledgeProtocol and provides three tools:
7
+ - grep_file: Search for patterns in file contents
8
+ - list_files: List files matching a glob pattern
9
+ - get_file: Read the full contents of a specific file
10
+ """
11
+
12
+ from dataclasses import dataclass, field
13
+ from os import walk as os_walk
14
+ from os.path import isabs as path_isabs
15
+ from pathlib import Path
16
+ from re import IGNORECASE
17
+ from re import compile as re_compile
18
+ from re import error as re_error
19
+ from re import escape as re_escape
20
+ from typing import Any, List, Optional
21
+
22
+ from agno.knowledge.document import Document
23
+ from agno.utils.log import log_debug, log_warning
24
+
25
+
26
+ @dataclass
27
+ class FileSystemKnowledge:
28
+ """Knowledge implementation that searches files in a local directory.
29
+
30
+ Implements the KnowledgeProtocol and provides three tools to agents:
31
+ - grep_file(query): Search for patterns in file contents
32
+ - list_files(pattern): List files matching a glob pattern
33
+ - get_file(path): Read the full contents of a specific file
34
+
35
+ Example:
36
+ ```python
37
+ from agno.agent import Agent
38
+ from agno.knowledge.filesystem import FileSystemKnowledge
39
+ from agno.models.openai import OpenAIChat
40
+
41
+ # Create knowledge for a directory
42
+ fs_knowledge = FileSystemKnowledge(base_dir="/path/to/code")
43
+
44
+ # Agent automatically gets grep_file, list_files, get_file tools
45
+ agent = Agent(
46
+ model=OpenAIChat(id="gpt-4o"),
47
+ knowledge=fs_knowledge,
48
+ search_knowledge=True,
49
+ )
50
+
51
+ # Agent can now search, list, and read files
52
+ agent.print_response("Find where main() is defined")
53
+ ```
54
+ """
55
+
56
+ base_dir: str
57
+ max_results: int = 50
58
+ include_patterns: List[str] = field(default_factory=list)
59
+ exclude_patterns: List[str] = field(
60
+ default_factory=lambda: [".git", "__pycache__", "node_modules", ".venv", "venv"]
61
+ )
62
+
63
+ def __post_init__(self):
64
+ self.base_path = Path(self.base_dir).resolve()
65
+ if not self.base_path.exists():
66
+ raise ValueError(f"Directory does not exist: {self.base_dir}")
67
+ if not self.base_path.is_dir():
68
+ raise ValueError(f"Path is not a directory: {self.base_dir}")
69
+
70
+ def _should_include_file(self, file_path: Path) -> bool:
71
+ """Check if a file should be included based on patterns."""
72
+ path_str = str(file_path)
73
+
74
+ # Check exclude patterns
75
+ for pattern in self.exclude_patterns:
76
+ if pattern in path_str:
77
+ return False
78
+
79
+ # Check include patterns (if specified)
80
+ if self.include_patterns:
81
+ import fnmatch
82
+
83
+ for pattern in self.include_patterns:
84
+ if fnmatch.fnmatch(file_path.name, pattern):
85
+ return True
86
+ return False
87
+
88
+ return True
89
+
90
+ def _list_files(self, query: str, max_results: Optional[int] = None) -> List[Document]:
91
+ """List files matching the query pattern (glob-style)."""
92
+ import fnmatch
93
+
94
+ results: List[Document] = []
95
+ limit = max_results or self.max_results
96
+
97
+ for root, dirs, files in os_walk(self.base_path):
98
+ # Filter out excluded directories
99
+ dirs[:] = [d for d in dirs if not any(excl in d for excl in self.exclude_patterns)]
100
+
101
+ for filename in files:
102
+ if len(results) >= limit:
103
+ break
104
+
105
+ file_path = Path(root) / filename
106
+ if not self._should_include_file(file_path):
107
+ continue
108
+
109
+ rel_path = file_path.relative_to(self.base_path)
110
+
111
+ # Match against query pattern (check both filename and relative path)
112
+ if query and query != "*":
113
+ if not (fnmatch.fnmatch(filename, query) or fnmatch.fnmatch(str(rel_path), query)):
114
+ continue
115
+ results.append(
116
+ Document(
117
+ name=str(rel_path),
118
+ content=str(rel_path),
119
+ meta_data={
120
+ "type": "file_listing",
121
+ "absolute_path": str(file_path),
122
+ "extension": file_path.suffix,
123
+ "size": file_path.stat().st_size,
124
+ },
125
+ )
126
+ )
127
+
128
+ if len(results) >= limit:
129
+ break
130
+
131
+ log_debug(f"Found {len(results)} files matching pattern: {query}")
132
+ return results
133
+
134
+ def _get_file(self, query: str) -> List[Document]:
135
+ """Get the contents of a specific file."""
136
+ # Handle both relative and absolute paths
137
+ if path_isabs(query):
138
+ file_path = Path(query)
139
+ else:
140
+ file_path = self.base_path / query
141
+
142
+ if not file_path.exists():
143
+ log_warning(f"File not found: {query}")
144
+ return []
145
+
146
+ if not file_path.is_file():
147
+ log_warning(f"Path is not a file: {query}")
148
+ return []
149
+
150
+ try:
151
+ content = file_path.read_text(encoding="utf-8", errors="replace")
152
+ rel_path = file_path.relative_to(self.base_path) if file_path.is_relative_to(self.base_path) else file_path
153
+
154
+ return [
155
+ Document(
156
+ name=str(rel_path),
157
+ content=content,
158
+ meta_data={
159
+ "type": "file_content",
160
+ "absolute_path": str(file_path),
161
+ "extension": file_path.suffix,
162
+ "size": len(content),
163
+ "lines": content.count("\n") + 1,
164
+ },
165
+ )
166
+ ]
167
+ except Exception as e:
168
+ log_warning(f"Error reading file {query}: {e}")
169
+ return []
170
+
171
+ def _grep(self, query: str, max_results: Optional[int] = None) -> List[Document]:
172
+ """Search for a pattern within file contents."""
173
+ results: List[Document] = []
174
+ limit = max_results or self.max_results
175
+
176
+ try:
177
+ pattern = re_compile(query, IGNORECASE)
178
+ except re_error:
179
+ # If not a valid regex, treat as literal string
180
+ pattern = re_compile(re_escape(query), IGNORECASE)
181
+
182
+ for root, dirs, files in os_walk(self.base_path):
183
+ # Filter out excluded directories
184
+ dirs[:] = [d for d in dirs if not any(excl in d for excl in self.exclude_patterns)]
185
+
186
+ for filename in files:
187
+ if len(results) >= limit:
188
+ break
189
+
190
+ file_path = Path(root) / filename
191
+ if not self._should_include_file(file_path):
192
+ continue
193
+
194
+ try:
195
+ content = file_path.read_text(encoding="utf-8", errors="replace")
196
+ matches = list(pattern.finditer(content))
197
+
198
+ if matches:
199
+ # Extract matching lines with context
200
+ lines = content.split("\n")
201
+ matching_lines: List[dict[str, Any]] = []
202
+
203
+ for match in matches[:10]: # Limit matches per file
204
+ # Find the line number
205
+ line_start = content.count("\n", 0, match.start())
206
+ line_num = line_start + 1
207
+
208
+ # Get context (1 line before and after)
209
+ start_idx = max(0, line_start - 1)
210
+ end_idx = min(len(lines), line_start + 2)
211
+ context_lines = lines[start_idx:end_idx]
212
+
213
+ matching_lines.append(
214
+ {
215
+ "line": line_num,
216
+ "match": match.group(),
217
+ "context": "\n".join(context_lines),
218
+ }
219
+ )
220
+
221
+ rel_path = file_path.relative_to(self.base_path)
222
+ results.append(
223
+ Document(
224
+ name=str(rel_path),
225
+ content="\n---\n".join(str(m["context"]) for m in matching_lines),
226
+ meta_data={
227
+ "type": "grep_result",
228
+ "absolute_path": str(file_path),
229
+ "match_count": len(matches),
230
+ "matches": matching_lines[:5], # Include first 5 match details
231
+ },
232
+ )
233
+ )
234
+
235
+ except Exception as e:
236
+ # Skip files that can't be read (binary, permissions, etc.)
237
+ log_debug(f"Skipping file {file_path}: {e}")
238
+ continue
239
+
240
+ if len(results) >= limit:
241
+ break
242
+
243
+ log_debug(f"Found {len(results)} files with matches for: {query}")
244
+ return results
245
+
246
+ # ========================================================================
247
+ # Protocol Implementation (build_context, get_tools, retrieve)
248
+ # ========================================================================
249
+
250
+ def build_context(self, **kwargs) -> str:
251
+ """Build context string for the agent's system prompt.
252
+
253
+ Returns instructions about the three available filesystem tools.
254
+
255
+ Args:
256
+ **kwargs: Additional context (unused).
257
+
258
+ Returns:
259
+ Context string describing available tools.
260
+ """
261
+ from textwrap import dedent
262
+
263
+ return dedent(
264
+ f"""
265
+ You have access to a filesystem knowledge base containing documents at: {self.base_dir}
266
+
267
+ IMPORTANT: You MUST use these tools to search and read files before answering questions.
268
+ Do NOT answer from your own knowledge - always search the files first.
269
+
270
+ Available tools:
271
+ - grep_file(query): Search for keywords or patterns in file contents. Use this to find relevant information.
272
+ - list_files(pattern): List available files. Use "*" to see all files, or "*.md" for specific types.
273
+ - get_file(path): Read the full contents of a specific file.
274
+
275
+ When answering questions:
276
+ 1. First use grep_file to search for relevant terms in the documents
277
+ 2. Or use list_files to see what documents are available, then get_file to read them
278
+ 3. Base your answer on what you find in the files
279
+ """
280
+ ).strip()
281
+
282
+ def get_tools(self, **kwargs) -> List[Any]:
283
+ """Get tools to expose to the agent.
284
+
285
+ Returns three filesystem tools: grep_file, list_files, get_file.
286
+
287
+ Args:
288
+ **kwargs: Additional context (unused).
289
+
290
+ Returns:
291
+ List of filesystem tools.
292
+ """
293
+ return [
294
+ self._create_grep_tool(),
295
+ self._create_list_files_tool(),
296
+ self._create_get_file_tool(),
297
+ ]
298
+
299
+ async def aget_tools(self, **kwargs) -> List[Any]:
300
+ """Async version of get_tools."""
301
+ return self.get_tools(**kwargs)
302
+
303
+ def _create_grep_tool(self) -> Any:
304
+ """Create the grep_file tool."""
305
+ from agno.tools.function import Function
306
+
307
+ def grep_file(query: str, max_results: int = 20) -> str:
308
+ """Search the knowledge base files for a keyword or pattern.
309
+
310
+ Use this tool to find information in the documents. Search for relevant
311
+ terms from the user's question to find answers.
312
+
313
+ Args:
314
+ query: The keyword or pattern to search for (e.g., "coffee", "cappuccino", "brewing").
315
+ max_results: Maximum number of files to return (default: 20).
316
+
317
+ Returns:
318
+ Matching content from files with context around each match.
319
+ """
320
+ docs = self._grep(query, max_results=max_results)
321
+
322
+ if not docs:
323
+ return f"No matches found for: {query}"
324
+
325
+ results = []
326
+ for doc in docs:
327
+ results.append(f"### {doc.name}\n{doc.content}")
328
+
329
+ return "\n\n".join(results)
330
+
331
+ return Function.from_callable(grep_file, name="grep_file")
332
+
333
+ def _create_list_files_tool(self) -> Any:
334
+ """Create the list_files tool."""
335
+ from agno.tools.function import Function
336
+
337
+ def list_files(pattern: str = "*", max_results: int = 50) -> str:
338
+ """List available files in the knowledge base.
339
+
340
+ Use this to see what documents are available to search.
341
+
342
+ Args:
343
+ pattern: Glob pattern to match (e.g., "*.md", "*.txt"). Default: "*" for all files.
344
+ max_results: Maximum number of files to return (default: 50).
345
+
346
+ Returns:
347
+ List of available file paths.
348
+ """
349
+ docs = self._list_files(pattern, max_results=max_results)
350
+
351
+ if not docs:
352
+ return f"No files found matching: {pattern}"
353
+
354
+ file_list = [doc.name for doc in docs]
355
+ return f"Found {len(file_list)} files:\n" + "\n".join(f"- {f}" for f in file_list)
356
+
357
+ return Function.from_callable(list_files, name="list_files")
358
+
359
+ def _create_get_file_tool(self) -> Any:
360
+ """Create the get_file tool."""
361
+ from agno.tools.function import Function
362
+
363
+ def get_file(path: str) -> str:
364
+ """Read the full contents of a document from the knowledge base.
365
+
366
+ Use this after list_files to read a specific document.
367
+
368
+ Args:
369
+ path: Path to the file (e.g., "coffee.md", "guide.txt").
370
+
371
+ Returns:
372
+ The full file contents.
373
+ """
374
+ docs = self._get_file(path)
375
+
376
+ if not docs:
377
+ return f"File not found: {path}"
378
+
379
+ doc = docs[0]
380
+ return f"### {doc.name}\n```\n{doc.content}\n```"
381
+
382
+ return Function.from_callable(get_file, name="get_file")
383
+
384
+ def retrieve(
385
+ self,
386
+ query: str,
387
+ max_results: Optional[int] = None,
388
+ **kwargs,
389
+ ) -> List[Document]:
390
+ """Retrieve documents for context injection.
391
+
392
+ Uses grep as the default retrieval method since it's most likely
393
+ to return relevant results for a natural language query.
394
+
395
+ Args:
396
+ query: The query string.
397
+ max_results: Maximum number of results.
398
+ **kwargs: Additional parameters.
399
+
400
+ Returns:
401
+ List of Document objects.
402
+ """
403
+ return self._grep(query, max_results=max_results or 10)
404
+
405
+ async def aretrieve(
406
+ self,
407
+ query: str,
408
+ max_results: Optional[int] = None,
409
+ **kwargs,
410
+ ) -> List[Document]:
411
+ """Async version of retrieve."""
412
+ return self.retrieve(query, max_results=max_results, **kwargs)