agno 2.0.1__py3-none-any.whl → 2.3.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 (314) hide show
  1. agno/agent/agent.py +6015 -2823
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/culture/__init__.py +3 -0
  5. agno/culture/manager.py +956 -0
  6. agno/db/async_postgres/__init__.py +3 -0
  7. agno/db/base.py +385 -6
  8. agno/db/dynamo/dynamo.py +388 -81
  9. agno/db/dynamo/schemas.py +47 -10
  10. agno/db/dynamo/utils.py +63 -4
  11. agno/db/firestore/firestore.py +435 -64
  12. agno/db/firestore/schemas.py +11 -0
  13. agno/db/firestore/utils.py +102 -4
  14. agno/db/gcs_json/gcs_json_db.py +384 -42
  15. agno/db/gcs_json/utils.py +60 -26
  16. agno/db/in_memory/in_memory_db.py +351 -66
  17. agno/db/in_memory/utils.py +60 -2
  18. agno/db/json/json_db.py +339 -48
  19. agno/db/json/utils.py +60 -26
  20. agno/db/migrations/manager.py +199 -0
  21. agno/db/migrations/v1_to_v2.py +510 -37
  22. agno/db/migrations/versions/__init__.py +0 -0
  23. agno/db/migrations/versions/v2_3_0.py +938 -0
  24. agno/db/mongo/__init__.py +15 -1
  25. agno/db/mongo/async_mongo.py +2036 -0
  26. agno/db/mongo/mongo.py +653 -76
  27. agno/db/mongo/schemas.py +13 -0
  28. agno/db/mongo/utils.py +80 -8
  29. agno/db/mysql/mysql.py +687 -25
  30. agno/db/mysql/schemas.py +61 -37
  31. agno/db/mysql/utils.py +60 -2
  32. agno/db/postgres/__init__.py +2 -1
  33. agno/db/postgres/async_postgres.py +2001 -0
  34. agno/db/postgres/postgres.py +676 -57
  35. agno/db/postgres/schemas.py +43 -18
  36. agno/db/postgres/utils.py +164 -2
  37. agno/db/redis/redis.py +344 -38
  38. agno/db/redis/schemas.py +18 -0
  39. agno/db/redis/utils.py +60 -2
  40. agno/db/schemas/__init__.py +2 -1
  41. agno/db/schemas/culture.py +120 -0
  42. agno/db/schemas/memory.py +13 -0
  43. agno/db/singlestore/schemas.py +26 -1
  44. agno/db/singlestore/singlestore.py +687 -53
  45. agno/db/singlestore/utils.py +60 -2
  46. agno/db/sqlite/__init__.py +2 -1
  47. agno/db/sqlite/async_sqlite.py +2371 -0
  48. agno/db/sqlite/schemas.py +24 -0
  49. agno/db/sqlite/sqlite.py +774 -85
  50. agno/db/sqlite/utils.py +168 -5
  51. agno/db/surrealdb/__init__.py +3 -0
  52. agno/db/surrealdb/metrics.py +292 -0
  53. agno/db/surrealdb/models.py +309 -0
  54. agno/db/surrealdb/queries.py +71 -0
  55. agno/db/surrealdb/surrealdb.py +1361 -0
  56. agno/db/surrealdb/utils.py +147 -0
  57. agno/db/utils.py +50 -22
  58. agno/eval/accuracy.py +50 -43
  59. agno/eval/performance.py +6 -3
  60. agno/eval/reliability.py +6 -3
  61. agno/eval/utils.py +33 -16
  62. agno/exceptions.py +68 -1
  63. agno/filters.py +354 -0
  64. agno/guardrails/__init__.py +6 -0
  65. agno/guardrails/base.py +19 -0
  66. agno/guardrails/openai.py +144 -0
  67. agno/guardrails/pii.py +94 -0
  68. agno/guardrails/prompt_injection.py +52 -0
  69. agno/integrations/discord/client.py +1 -0
  70. agno/knowledge/chunking/agentic.py +13 -10
  71. agno/knowledge/chunking/fixed.py +1 -1
  72. agno/knowledge/chunking/semantic.py +40 -8
  73. agno/knowledge/chunking/strategy.py +59 -15
  74. agno/knowledge/embedder/aws_bedrock.py +9 -4
  75. agno/knowledge/embedder/azure_openai.py +54 -0
  76. agno/knowledge/embedder/base.py +2 -0
  77. agno/knowledge/embedder/cohere.py +184 -5
  78. agno/knowledge/embedder/fastembed.py +1 -1
  79. agno/knowledge/embedder/google.py +79 -1
  80. agno/knowledge/embedder/huggingface.py +9 -4
  81. agno/knowledge/embedder/jina.py +63 -0
  82. agno/knowledge/embedder/mistral.py +78 -11
  83. agno/knowledge/embedder/nebius.py +1 -1
  84. agno/knowledge/embedder/ollama.py +13 -0
  85. agno/knowledge/embedder/openai.py +37 -65
  86. agno/knowledge/embedder/sentence_transformer.py +8 -4
  87. agno/knowledge/embedder/vllm.py +262 -0
  88. agno/knowledge/embedder/voyageai.py +69 -16
  89. agno/knowledge/knowledge.py +594 -186
  90. agno/knowledge/reader/base.py +9 -2
  91. agno/knowledge/reader/csv_reader.py +8 -10
  92. agno/knowledge/reader/docx_reader.py +5 -6
  93. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  94. agno/knowledge/reader/json_reader.py +6 -5
  95. agno/knowledge/reader/markdown_reader.py +13 -13
  96. agno/knowledge/reader/pdf_reader.py +43 -68
  97. agno/knowledge/reader/pptx_reader.py +101 -0
  98. agno/knowledge/reader/reader_factory.py +51 -6
  99. agno/knowledge/reader/s3_reader.py +3 -15
  100. agno/knowledge/reader/tavily_reader.py +194 -0
  101. agno/knowledge/reader/text_reader.py +13 -13
  102. agno/knowledge/reader/web_search_reader.py +2 -43
  103. agno/knowledge/reader/website_reader.py +43 -25
  104. agno/knowledge/reranker/__init__.py +2 -8
  105. agno/knowledge/types.py +9 -0
  106. agno/knowledge/utils.py +20 -0
  107. agno/media.py +72 -0
  108. agno/memory/manager.py +336 -82
  109. agno/models/aimlapi/aimlapi.py +2 -2
  110. agno/models/anthropic/claude.py +183 -37
  111. agno/models/aws/bedrock.py +52 -112
  112. agno/models/aws/claude.py +33 -1
  113. agno/models/azure/ai_foundry.py +33 -15
  114. agno/models/azure/openai_chat.py +25 -8
  115. agno/models/base.py +999 -519
  116. agno/models/cerebras/cerebras.py +19 -13
  117. agno/models/cerebras/cerebras_openai.py +8 -5
  118. agno/models/cohere/chat.py +27 -1
  119. agno/models/cometapi/__init__.py +5 -0
  120. agno/models/cometapi/cometapi.py +57 -0
  121. agno/models/dashscope/dashscope.py +1 -0
  122. agno/models/deepinfra/deepinfra.py +2 -2
  123. agno/models/deepseek/deepseek.py +2 -2
  124. agno/models/fireworks/fireworks.py +2 -2
  125. agno/models/google/gemini.py +103 -31
  126. agno/models/groq/groq.py +28 -11
  127. agno/models/huggingface/huggingface.py +2 -1
  128. agno/models/internlm/internlm.py +2 -2
  129. agno/models/langdb/langdb.py +4 -4
  130. agno/models/litellm/chat.py +18 -1
  131. agno/models/litellm/litellm_openai.py +2 -2
  132. agno/models/llama_cpp/__init__.py +5 -0
  133. agno/models/llama_cpp/llama_cpp.py +22 -0
  134. agno/models/message.py +139 -0
  135. agno/models/meta/llama.py +27 -10
  136. agno/models/meta/llama_openai.py +5 -17
  137. agno/models/nebius/nebius.py +6 -6
  138. agno/models/nexus/__init__.py +3 -0
  139. agno/models/nexus/nexus.py +22 -0
  140. agno/models/nvidia/nvidia.py +2 -2
  141. agno/models/ollama/chat.py +59 -5
  142. agno/models/openai/chat.py +69 -29
  143. agno/models/openai/responses.py +103 -106
  144. agno/models/openrouter/openrouter.py +41 -3
  145. agno/models/perplexity/perplexity.py +4 -5
  146. agno/models/portkey/portkey.py +3 -3
  147. agno/models/requesty/__init__.py +5 -0
  148. agno/models/requesty/requesty.py +52 -0
  149. agno/models/response.py +77 -1
  150. agno/models/sambanova/sambanova.py +2 -2
  151. agno/models/siliconflow/__init__.py +5 -0
  152. agno/models/siliconflow/siliconflow.py +25 -0
  153. agno/models/together/together.py +2 -2
  154. agno/models/utils.py +254 -8
  155. agno/models/vercel/v0.py +2 -2
  156. agno/models/vertexai/__init__.py +0 -0
  157. agno/models/vertexai/claude.py +96 -0
  158. agno/models/vllm/vllm.py +1 -0
  159. agno/models/xai/xai.py +3 -2
  160. agno/os/app.py +543 -178
  161. agno/os/auth.py +24 -14
  162. agno/os/config.py +1 -0
  163. agno/os/interfaces/__init__.py +1 -0
  164. agno/os/interfaces/a2a/__init__.py +3 -0
  165. agno/os/interfaces/a2a/a2a.py +42 -0
  166. agno/os/interfaces/a2a/router.py +250 -0
  167. agno/os/interfaces/a2a/utils.py +924 -0
  168. agno/os/interfaces/agui/agui.py +23 -7
  169. agno/os/interfaces/agui/router.py +27 -3
  170. agno/os/interfaces/agui/utils.py +242 -142
  171. agno/os/interfaces/base.py +6 -2
  172. agno/os/interfaces/slack/router.py +81 -23
  173. agno/os/interfaces/slack/slack.py +29 -14
  174. agno/os/interfaces/whatsapp/router.py +11 -4
  175. agno/os/interfaces/whatsapp/whatsapp.py +14 -7
  176. agno/os/mcp.py +111 -54
  177. agno/os/middleware/__init__.py +7 -0
  178. agno/os/middleware/jwt.py +233 -0
  179. agno/os/router.py +556 -139
  180. agno/os/routers/evals/evals.py +71 -34
  181. agno/os/routers/evals/schemas.py +31 -31
  182. agno/os/routers/evals/utils.py +6 -5
  183. agno/os/routers/health.py +31 -0
  184. agno/os/routers/home.py +52 -0
  185. agno/os/routers/knowledge/knowledge.py +185 -38
  186. agno/os/routers/knowledge/schemas.py +82 -22
  187. agno/os/routers/memory/memory.py +158 -53
  188. agno/os/routers/memory/schemas.py +20 -16
  189. agno/os/routers/metrics/metrics.py +20 -8
  190. agno/os/routers/metrics/schemas.py +16 -16
  191. agno/os/routers/session/session.py +499 -38
  192. agno/os/schema.py +308 -198
  193. agno/os/utils.py +401 -41
  194. agno/reasoning/anthropic.py +80 -0
  195. agno/reasoning/azure_ai_foundry.py +2 -2
  196. agno/reasoning/deepseek.py +2 -2
  197. agno/reasoning/default.py +3 -1
  198. agno/reasoning/gemini.py +73 -0
  199. agno/reasoning/groq.py +2 -2
  200. agno/reasoning/ollama.py +2 -2
  201. agno/reasoning/openai.py +7 -2
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +248 -94
  205. agno/run/base.py +44 -5
  206. agno/run/team.py +238 -97
  207. agno/run/workflow.py +144 -33
  208. agno/session/agent.py +105 -89
  209. agno/session/summary.py +65 -25
  210. agno/session/team.py +176 -96
  211. agno/session/workflow.py +406 -40
  212. agno/team/team.py +3854 -1610
  213. agno/tools/dalle.py +2 -4
  214. agno/tools/decorator.py +4 -2
  215. agno/tools/duckduckgo.py +15 -11
  216. agno/tools/e2b.py +14 -7
  217. agno/tools/eleven_labs.py +23 -25
  218. agno/tools/exa.py +21 -16
  219. agno/tools/file.py +153 -23
  220. agno/tools/file_generation.py +350 -0
  221. agno/tools/firecrawl.py +4 -4
  222. agno/tools/function.py +250 -30
  223. agno/tools/gmail.py +238 -14
  224. agno/tools/google_drive.py +270 -0
  225. agno/tools/googlecalendar.py +36 -8
  226. agno/tools/googlesheets.py +20 -5
  227. agno/tools/jira.py +20 -0
  228. agno/tools/knowledge.py +3 -3
  229. agno/tools/mcp/__init__.py +10 -0
  230. agno/tools/mcp/mcp.py +331 -0
  231. agno/tools/mcp/multi_mcp.py +347 -0
  232. agno/tools/mcp/params.py +24 -0
  233. agno/tools/mcp_toolbox.py +284 -0
  234. agno/tools/mem0.py +11 -17
  235. agno/tools/memori.py +1 -53
  236. agno/tools/memory.py +419 -0
  237. agno/tools/models/nebius.py +5 -5
  238. agno/tools/models_labs.py +20 -10
  239. agno/tools/notion.py +204 -0
  240. agno/tools/parallel.py +314 -0
  241. agno/tools/scrapegraph.py +58 -31
  242. agno/tools/searxng.py +2 -2
  243. agno/tools/serper.py +2 -2
  244. agno/tools/slack.py +18 -3
  245. agno/tools/spider.py +2 -2
  246. agno/tools/tavily.py +146 -0
  247. agno/tools/whatsapp.py +1 -1
  248. agno/tools/workflow.py +278 -0
  249. agno/tools/yfinance.py +12 -11
  250. agno/utils/agent.py +820 -0
  251. agno/utils/audio.py +27 -0
  252. agno/utils/common.py +90 -1
  253. agno/utils/events.py +217 -2
  254. agno/utils/gemini.py +180 -22
  255. agno/utils/hooks.py +57 -0
  256. agno/utils/http.py +111 -0
  257. agno/utils/knowledge.py +12 -5
  258. agno/utils/log.py +1 -0
  259. agno/utils/mcp.py +92 -2
  260. agno/utils/media.py +188 -10
  261. agno/utils/merge_dict.py +22 -1
  262. agno/utils/message.py +60 -0
  263. agno/utils/models/claude.py +40 -11
  264. agno/utils/print_response/agent.py +105 -21
  265. agno/utils/print_response/team.py +103 -38
  266. agno/utils/print_response/workflow.py +251 -34
  267. agno/utils/reasoning.py +22 -1
  268. agno/utils/serialize.py +32 -0
  269. agno/utils/streamlit.py +16 -10
  270. agno/utils/string.py +41 -0
  271. agno/utils/team.py +98 -9
  272. agno/utils/tools.py +1 -1
  273. agno/vectordb/base.py +23 -4
  274. agno/vectordb/cassandra/cassandra.py +65 -9
  275. agno/vectordb/chroma/chromadb.py +182 -38
  276. agno/vectordb/clickhouse/clickhousedb.py +64 -11
  277. agno/vectordb/couchbase/couchbase.py +105 -10
  278. agno/vectordb/lancedb/lance_db.py +124 -133
  279. agno/vectordb/langchaindb/langchaindb.py +25 -7
  280. agno/vectordb/lightrag/lightrag.py +17 -3
  281. agno/vectordb/llamaindex/__init__.py +3 -0
  282. agno/vectordb/llamaindex/llamaindexdb.py +46 -7
  283. agno/vectordb/milvus/milvus.py +126 -9
  284. agno/vectordb/mongodb/__init__.py +7 -1
  285. agno/vectordb/mongodb/mongodb.py +112 -7
  286. agno/vectordb/pgvector/pgvector.py +142 -21
  287. agno/vectordb/pineconedb/pineconedb.py +80 -8
  288. agno/vectordb/qdrant/qdrant.py +125 -39
  289. agno/vectordb/redis/__init__.py +9 -0
  290. agno/vectordb/redis/redisdb.py +694 -0
  291. agno/vectordb/singlestore/singlestore.py +111 -25
  292. agno/vectordb/surrealdb/surrealdb.py +31 -5
  293. agno/vectordb/upstashdb/upstashdb.py +76 -8
  294. agno/vectordb/weaviate/weaviate.py +86 -15
  295. agno/workflow/__init__.py +2 -0
  296. agno/workflow/agent.py +299 -0
  297. agno/workflow/condition.py +112 -18
  298. agno/workflow/loop.py +69 -10
  299. agno/workflow/parallel.py +266 -118
  300. agno/workflow/router.py +110 -17
  301. agno/workflow/step.py +638 -129
  302. agno/workflow/steps.py +65 -6
  303. agno/workflow/types.py +61 -23
  304. agno/workflow/workflow.py +2085 -272
  305. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/METADATA +182 -58
  306. agno-2.3.0.dist-info/RECORD +577 -0
  307. agno/knowledge/reader/url_reader.py +0 -128
  308. agno/tools/googlesearch.py +0 -98
  309. agno/tools/mcp.py +0 -610
  310. agno/utils/models/aws_claude.py +0 -170
  311. agno-2.0.1.dist-info/RECORD +0 -515
  312. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
  313. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/licenses/LICENSE +0 -0
  314. {agno-2.0.1.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,9 @@
1
1
  import asyncio
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List, Optional, Union
3
3
 
4
4
  import httpx
5
5
 
6
+ from agno.filters import FilterExpr
6
7
  from agno.knowledge.document import Document
7
8
  from agno.utils.log import log_debug, log_error, log_info, log_warning
8
9
  from agno.vectordb.base import VectorDb
@@ -21,9 +22,14 @@ class LightRag(VectorDb):
21
22
  api_key: Optional[str] = None,
22
23
  auth_header_name: str = "X-API-KEY",
23
24
  auth_header_format: str = "{api_key}",
25
+ name: Optional[str] = None,
26
+ description: Optional[str] = None,
24
27
  ):
25
28
  self.server_url = server_url
26
29
  self.api_key = api_key
30
+ # Initialize base class with name and description
31
+ super().__init__(name=name, description=description)
32
+
27
33
  self.auth_header_name = auth_header_name
28
34
  self.auth_header_format = auth_header_format
29
35
 
@@ -87,14 +93,18 @@ class LightRag(VectorDb):
87
93
  """Async upsert documents into the vector database"""
88
94
  pass
89
95
 
90
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
96
+ def search(
97
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
98
+ ) -> List[Document]:
91
99
  result = asyncio.run(self.async_search(query, limit=limit, filters=filters))
92
100
  return result if result is not None else []
93
101
 
94
102
  async def async_search(
95
- self, query: str, limit: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
103
+ self, query: str, limit: Optional[int] = None, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
96
104
  ) -> Optional[List[Document]]:
97
105
  mode: str = "hybrid" # Default mode, can be "local", "global", or "hybrid"
106
+ if filters is not None:
107
+ log_warning("Filters are not supported in LightRAG. No filters will be applied.")
98
108
  try:
99
109
  async with httpx.AsyncClient(timeout=30.0) as client:
100
110
  response = await client.post(
@@ -372,3 +382,7 @@ class LightRag(VectorDb):
372
382
  metadata (Dict[str, Any]): The metadata to update
373
383
  """
374
384
  raise NotImplementedError("update_metadata not supported for LightRag - use LightRag's native methods")
385
+
386
+ def get_supported_search_types(self) -> List[str]:
387
+ """Get the supported search types for this vector database."""
388
+ return [] # LightRag doesn't use SearchType enum
@@ -0,0 +1,3 @@
1
+ from agno.vectordb.llamaindex.llamaindexdb import LlamaIndexVectorDb
2
+
3
+ __all__ = ["LlamaIndexVectorDb"]
@@ -1,7 +1,8 @@
1
- from typing import Any, Callable, Dict, List, Optional
1
+ from typing import Any, Callable, Dict, List, Optional, Union
2
2
 
3
+ from agno.filters import FilterExpr
3
4
  from agno.knowledge.document import Document
4
- from agno.utils.log import logger
5
+ from agno.utils.log import log_warning, logger
5
6
  from agno.vectordb.base import VectorDb
6
7
 
7
8
  try:
@@ -17,6 +18,21 @@ class LlamaIndexVectorDb(VectorDb):
17
18
  knowledge_retriever: BaseRetriever
18
19
  loader: Optional[Callable] = None
19
20
 
21
+ def __init__(
22
+ self,
23
+ knowledge_retriever: BaseRetriever,
24
+ loader: Optional[Callable] = None,
25
+ name: Optional[str] = None,
26
+ description: Optional[str] = None,
27
+ **kwargs,
28
+ ):
29
+ super().__init__(**kwargs)
30
+ # Initialize base class with name and description
31
+ super().__init__(name=name, description=description)
32
+
33
+ self.knowledge_retriever = knowledge_retriever
34
+ self.loader = loader
35
+
20
36
  def create(self) -> None:
21
37
  raise NotImplementedError
22
38
 
@@ -54,14 +70,14 @@ class LlamaIndexVectorDb(VectorDb):
54
70
  raise NotImplementedError
55
71
 
56
72
  def search(
57
- self, query: str, num_documents: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
73
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
58
74
  ) -> List[Document]:
59
75
  """
60
76
  Returns relevant documents matching the query.
61
77
 
62
78
  Args:
63
79
  query (str): The query string to search for.
64
- num_documents (Optional[int]): The maximum number of documents to return. Defaults to None.
80
+ limit (int): The maximum number of documents to return. Defaults to 5.
65
81
  filters (Optional[Dict[str, Any]]): Filters to apply to the search. Defaults to None.
66
82
 
67
83
  Returns:
@@ -69,12 +85,15 @@ class LlamaIndexVectorDb(VectorDb):
69
85
  Raises:
70
86
  ValueError: If the knowledge retriever is not of type BaseRetriever.
71
87
  """
88
+ if filters is not None:
89
+ log_warning("Filters are not supported in LlamaIndex. No filters will be applied.")
90
+
72
91
  if not isinstance(self.knowledge_retriever, BaseRetriever):
73
92
  raise ValueError(f"Knowledge retriever is not of type BaseRetriever: {self.knowledge_retriever}")
74
93
 
75
94
  lc_documents: List[NodeWithScore] = self.knowledge_retriever.retrieve(query)
76
- if num_documents is not None:
77
- lc_documents = lc_documents[:num_documents]
95
+ if limit is not None:
96
+ lc_documents = lc_documents[:limit]
78
97
  documents = []
79
98
  for lc_doc in lc_documents:
80
99
  documents.append(
@@ -86,7 +105,7 @@ class LlamaIndexVectorDb(VectorDb):
86
105
  return documents
87
106
 
88
107
  async def async_search(
89
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
108
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
90
109
  ) -> List[Document]:
91
110
  return self.search(query, limit, filters)
92
111
 
@@ -125,3 +144,23 @@ class LlamaIndexVectorDb(VectorDb):
125
144
  metadata (Dict[str, Any]): The metadata to update
126
145
  """
127
146
  raise NotImplementedError("update_metadata not supported for LlamaIndex vectorstores")
147
+
148
+ def delete_by_content_id(self, content_id: str) -> bool:
149
+ """
150
+ Delete documents by content ID.
151
+ Not implemented for LlamaIndex wrapper.
152
+
153
+ Args:
154
+ content_id (str): The content ID to delete
155
+
156
+ Returns:
157
+ bool: False as this operation is not supported
158
+ """
159
+ logger.warning(
160
+ "LlamaIndexVectorDb.delete_by_content_id() not supported - please check the vectorstore manually."
161
+ )
162
+ return False
163
+
164
+ def get_supported_search_types(self) -> List[str]:
165
+ """Get the supported search types for this vector database."""
166
+ return [] # LlamaIndexVectorDb doesn't use SearchType enum
@@ -9,10 +9,11 @@ try:
9
9
  except ImportError:
10
10
  raise ImportError("The `pymilvus` package is not installed. Please install it via `pip install pymilvus`.")
11
11
 
12
+ from agno.filters import FilterExpr
12
13
  from agno.knowledge.document import Document
13
14
  from agno.knowledge.embedder import Embedder
14
15
  from agno.knowledge.reranker.base import Reranker
15
- from agno.utils.log import log_debug, log_error, log_info
16
+ from agno.utils.log import log_debug, log_error, log_info, log_warning
16
17
  from agno.vectordb.base import VectorDb
17
18
  from agno.vectordb.distance import Distance
18
19
  from agno.vectordb.search import SearchType
@@ -28,6 +29,9 @@ class Milvus(VectorDb):
28
29
  def __init__(
29
30
  self,
30
31
  collection: str,
32
+ name: Optional[str] = None,
33
+ description: Optional[str] = None,
34
+ id: Optional[str] = None,
31
35
  embedder: Optional[Embedder] = None,
32
36
  distance: Distance = Distance.cosine,
33
37
  uri: str = "http://localhost:19530",
@@ -42,6 +46,8 @@ class Milvus(VectorDb):
42
46
 
43
47
  Args:
44
48
  collection (str): Name of the Milvus collection.
49
+ name (Optional[str]): Name of the vector database.
50
+ description (Optional[str]): Description of the vector database.
45
51
  embedder (Embedder): Embedder to use for embedding documents.
46
52
  distance (Distance): Distance metric to use for vector similarity.
47
53
  uri (Optional[str]): URI of the Milvus server.
@@ -63,6 +69,20 @@ class Milvus(VectorDb):
63
69
  reranker (Optional[Reranker]): Reranker to use for hybrid search results
64
70
  **kwargs: Additional keyword arguments to pass to the MilvusClient.
65
71
  """
72
+ # Validate required parameters
73
+ if not collection:
74
+ raise ValueError("Collection name must be provided.")
75
+
76
+ # Dynamic ID generation based on unique identifiers
77
+ if id is None:
78
+ from agno.utils.string import generate_id
79
+
80
+ seed = f"{uri or 'milvus'}#{collection}"
81
+ id = generate_id(seed)
82
+
83
+ # Initialize base class with name, description, and generated ID
84
+ super().__init__(id=id, name=name, description=description)
85
+
66
86
  self.collection: str = collection
67
87
 
68
88
  if embedder is None:
@@ -423,6 +443,9 @@ class Milvus(VectorDb):
423
443
  else:
424
444
  for document in documents:
425
445
  document.embed(embedder=self.embedder)
446
+ if not document.embedding:
447
+ log_debug(f"Skipping document without embedding: {document.name} ({document.meta_data})")
448
+ continue
426
449
  cleaned_content = document.content.replace("\x00", "\ufffd")
427
450
  doc_id = md5(cleaned_content.encode()).hexdigest()
428
451
 
@@ -454,8 +477,44 @@ class Milvus(VectorDb):
454
477
  """Insert documents asynchronously based on search type."""
455
478
  log_info(f"Inserting {len(documents)} documents asynchronously")
456
479
 
457
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
458
- await asyncio.gather(*embed_tasks, return_exceptions=True)
480
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
481
+ # Use batch embedding when enabled and supported
482
+ try:
483
+ # Extract content from all documents
484
+ doc_contents = [doc.content for doc in documents]
485
+
486
+ # Get batch embeddings and usage
487
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
488
+
489
+ # Process documents with pre-computed embeddings
490
+ for j, doc in enumerate(documents):
491
+ try:
492
+ if j < len(embeddings):
493
+ doc.embedding = embeddings[j]
494
+ doc.usage = usages[j] if j < len(usages) else None
495
+ except Exception as e:
496
+ log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
497
+
498
+ except Exception as e:
499
+ # Check if this is a rate limit error - don't fall back as it would make things worse
500
+ error_str = str(e).lower()
501
+ is_rate_limit = any(
502
+ phrase in error_str
503
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
504
+ )
505
+
506
+ if is_rate_limit:
507
+ log_error(f"Rate limit detected during batch embedding. {e}")
508
+ raise e
509
+ else:
510
+ log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
511
+ # Fall back to individual embedding
512
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
513
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
514
+ else:
515
+ # Use individual embedding
516
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
517
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
459
518
 
460
519
  if self.search_type == SearchType.hybrid:
461
520
  await asyncio.gather(
@@ -465,6 +524,9 @@ class Milvus(VectorDb):
465
524
 
466
525
  async def process_document(document):
467
526
  document.embed(embedder=self.embedder)
527
+ if not document.embedding:
528
+ log_debug(f"Skipping document without embedding: {document.name} ({document.meta_data})")
529
+ return None
468
530
  cleaned_content = document.content.replace("\x00", "\ufffd")
469
531
  doc_id = md5(cleaned_content.encode()).hexdigest()
470
532
 
@@ -541,8 +603,44 @@ class Milvus(VectorDb):
541
603
  ) -> None:
542
604
  log_debug(f"Upserting {len(documents)} documents asynchronously")
543
605
 
544
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
545
- await asyncio.gather(*embed_tasks, return_exceptions=True)
606
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
607
+ # Use batch embedding when enabled and supported
608
+ try:
609
+ # Extract content from all documents
610
+ doc_contents = [doc.content for doc in documents]
611
+
612
+ # Get batch embeddings and usage
613
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
614
+
615
+ # Process documents with pre-computed embeddings
616
+ for j, doc in enumerate(documents):
617
+ try:
618
+ if j < len(embeddings):
619
+ doc.embedding = embeddings[j]
620
+ doc.usage = usages[j] if j < len(usages) else None
621
+ except Exception as e:
622
+ log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
623
+
624
+ except Exception as e:
625
+ # Check if this is a rate limit error - don't fall back as it would make things worse
626
+ error_str = str(e).lower()
627
+ is_rate_limit = any(
628
+ phrase in error_str
629
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
630
+ )
631
+
632
+ if is_rate_limit:
633
+ log_error(f"Rate limit detected during batch embedding. {e}")
634
+ raise e
635
+ else:
636
+ log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
637
+ # Fall back to individual embedding
638
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
639
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
640
+ else:
641
+ # Use individual embedding
642
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
643
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
546
644
 
547
645
  async def process_document(document):
548
646
  cleaned_content = document.content.replace("\x00", "\ufffd")
@@ -578,7 +676,9 @@ class Milvus(VectorDb):
578
676
  """
579
677
  return MILVUS_DISTANCE_MAP.get(self.distance, "COSINE")
580
678
 
581
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
679
+ def search(
680
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
681
+ ) -> List[Document]:
582
682
  """
583
683
  Search for documents matching the query.
584
684
 
@@ -590,6 +690,9 @@ class Milvus(VectorDb):
590
690
  Returns:
591
691
  List[Document]: List of matching documents
592
692
  """
693
+ if isinstance(filters, List):
694
+ log_warning("Filters Expressions are not supported in Milvus. No filters will be applied.")
695
+ filters = None
593
696
  if self.search_type == SearchType.hybrid:
594
697
  return self.hybrid_search(query, limit)
595
698
 
@@ -622,12 +725,20 @@ class Milvus(VectorDb):
622
725
  )
623
726
  )
624
727
 
728
+ # Apply reranker if available
729
+ if self.reranker and search_results:
730
+ search_results = self.reranker.rerank(query=query, documents=search_results)
731
+ search_results = search_results[:limit]
732
+
625
733
  log_info(f"Found {len(search_results)} documents")
626
734
  return search_results
627
735
 
628
736
  async def async_search(
629
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
737
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
630
738
  ) -> List[Document]:
739
+ if isinstance(filters, List):
740
+ log_warning("Filters Expressions are not supported in Milvus. No filters will be applied.")
741
+ filters = None
631
742
  if self.search_type == SearchType.hybrid:
632
743
  return await self.async_hybrid_search(query, limit, filters)
633
744
 
@@ -663,7 +774,9 @@ class Milvus(VectorDb):
663
774
  log_info(f"Found {len(search_results)} documents")
664
775
  return search_results
665
776
 
666
- def hybrid_search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
777
+ def hybrid_search(
778
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
779
+ ) -> List[Document]:
667
780
  """
668
781
  Perform a hybrid search combining dense and sparse vector similarity.
669
782
 
@@ -755,7 +868,7 @@ class Milvus(VectorDb):
755
868
  return []
756
869
 
757
870
  async def async_hybrid_search(
758
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
871
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
759
872
  ) -> List[Document]:
760
873
  """
761
874
  Perform an asynchronous hybrid search combining dense and sparse vector similarity.
@@ -1063,3 +1176,7 @@ class Milvus(VectorDb):
1063
1176
  except Exception as e:
1064
1177
  log_error(f"Error updating metadata for content_id '{content_id}': {e}")
1065
1178
  raise
1179
+
1180
+ def get_supported_search_types(self) -> List[str]:
1181
+ """Get the supported search types for this vector database."""
1182
+ return [SearchType.vector, SearchType.hybrid]
@@ -1,3 +1,9 @@
1
1
  from agno.vectordb.mongodb.mongodb import MongoDb
2
2
 
3
- __all__ = ["MongoDb"]
3
+ # Alias to avoid name collision with the main MongoDb class
4
+ MongoVectorDb = MongoDb
5
+
6
+ __all__ = [
7
+ "MongoVectorDb",
8
+ "MongoDb",
9
+ ]
@@ -1,9 +1,10 @@
1
1
  import asyncio
2
2
  import time
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional, Union
4
4
 
5
5
  from bson import ObjectId
6
6
 
7
+ from agno.filters import FilterExpr
7
8
  from agno.knowledge.document import Document
8
9
  from agno.knowledge.embedder import Embedder
9
10
  from agno.utils.log import log_debug, log_info, log_warning, logger
@@ -33,6 +34,9 @@ class MongoDb(VectorDb):
33
34
  def __init__(
34
35
  self,
35
36
  collection_name: str,
37
+ name: Optional[str] = None,
38
+ description: Optional[str] = None,
39
+ id: Optional[str] = None,
36
40
  db_url: Optional[str] = "mongodb://localhost:27017/",
37
41
  database: str = "agno",
38
42
  embedder: Optional[Embedder] = None,
@@ -56,6 +60,8 @@ class MongoDb(VectorDb):
56
60
 
57
61
  Args:
58
62
  collection_name (str): Name of the MongoDB collection.
63
+ name (Optional[str]): Name of the vector database.
64
+ description (Optional[str]): Description of the vector database.
59
65
  db_url (Optional[str]): MongoDB connection string.
60
66
  database (str): Database name.
61
67
  embedder (Embedder): Embedder instance for generating embeddings.
@@ -74,11 +80,24 @@ class MongoDb(VectorDb):
74
80
  hybrid_rank_constant (int): Default rank constant (k) for Reciprocal Rank Fusion in hybrid search. This constant is added to the rank before taking the reciprocal, helping to smooth scores. A common value is 60.
75
81
  **kwargs: Additional arguments for MongoClient.
76
82
  """
83
+ # Validate required parameters
77
84
  if not collection_name:
78
85
  raise ValueError("Collection name must not be empty.")
79
86
  if not database:
80
87
  raise ValueError("Database name must not be empty.")
88
+
89
+ # Dynamic ID generation based on unique identifiers
90
+ if id is None:
91
+ from agno.utils.string import generate_id
92
+
93
+ connection_identifier = db_url or "mongodb://localhost:27017/"
94
+ seed = f"{connection_identifier}#{database}#{collection_name}"
95
+ id = generate_id(seed)
96
+
81
97
  self.collection_name = collection_name
98
+ # Initialize base class with name, description, and generated ID
99
+ super().__init__(id=id, name=name, description=description)
100
+
82
101
  self.database = database
83
102
  self.search_index_name = search_index_name
84
103
  self.cosmos_compatibility = cosmos_compatibility
@@ -567,9 +586,16 @@ class MongoDb(VectorDb):
567
586
  return True
568
587
 
569
588
  def search(
570
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None, min_score: float = 0.0
589
+ self,
590
+ query: str,
591
+ limit: int = 5,
592
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
593
+ min_score: float = 0.0,
571
594
  ) -> List[Document]:
572
595
  """Search for documents using vector similarity."""
596
+ if isinstance(filters, List):
597
+ log_warning("Filters Expressions are not supported in MongoDB. No filters will be applied.")
598
+ filters = None
573
599
  if self.search_type == SearchType.hybrid:
574
600
  return self.hybrid_search(query, limit=limit, filters=filters)
575
601
 
@@ -1018,8 +1044,44 @@ class MongoDb(VectorDb):
1018
1044
  log_debug(f"Inserting {len(documents)} documents asynchronously")
1019
1045
  collection = await self._get_async_collection()
1020
1046
 
1021
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
1022
- await asyncio.gather(*embed_tasks, return_exceptions=True)
1047
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
1048
+ # Use batch embedding when enabled and supported
1049
+ try:
1050
+ # Extract content from all documents
1051
+ doc_contents = [doc.content for doc in documents]
1052
+
1053
+ # Get batch embeddings and usage
1054
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
1055
+
1056
+ # Process documents with pre-computed embeddings
1057
+ for j, doc in enumerate(documents):
1058
+ try:
1059
+ if j < len(embeddings):
1060
+ doc.embedding = embeddings[j]
1061
+ doc.usage = usages[j] if j < len(usages) else None
1062
+ except Exception as e:
1063
+ logger.error(f"Error assigning batch embedding to document '{doc.name}': {e}")
1064
+
1065
+ except Exception as e:
1066
+ # Check if this is a rate limit error - don't fall back as it would make things worse
1067
+ error_str = str(e).lower()
1068
+ is_rate_limit = any(
1069
+ phrase in error_str
1070
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
1071
+ )
1072
+
1073
+ if is_rate_limit:
1074
+ logger.error(f"Rate limit detected during batch embedding. {e}")
1075
+ raise e
1076
+ else:
1077
+ logger.warning(f"Async batch embedding failed, falling back to individual embeddings: {e}")
1078
+ # Fall back to individual embedding
1079
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
1080
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
1081
+ else:
1082
+ # Use individual embedding
1083
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
1084
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
1023
1085
 
1024
1086
  prepared_docs = []
1025
1087
  for document in documents:
@@ -1047,8 +1109,44 @@ class MongoDb(VectorDb):
1047
1109
  log_info(f"Upserting {len(documents)} documents asynchronously")
1048
1110
  collection = await self._get_async_collection()
1049
1111
 
1050
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
1051
- await asyncio.gather(*embed_tasks, return_exceptions=True)
1112
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
1113
+ # Use batch embedding when enabled and supported
1114
+ try:
1115
+ # Extract content from all documents
1116
+ doc_contents = [doc.content for doc in documents]
1117
+
1118
+ # Get batch embeddings and usage
1119
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
1120
+
1121
+ # Process documents with pre-computed embeddings
1122
+ for j, doc in enumerate(documents):
1123
+ try:
1124
+ if j < len(embeddings):
1125
+ doc.embedding = embeddings[j]
1126
+ doc.usage = usages[j] if j < len(usages) else None
1127
+ except Exception as e:
1128
+ logger.error(f"Error assigning batch embedding to document '{doc.name}': {e}")
1129
+
1130
+ except Exception as e:
1131
+ # Check if this is a rate limit error - don't fall back as it would make things worse
1132
+ error_str = str(e).lower()
1133
+ is_rate_limit = any(
1134
+ phrase in error_str
1135
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
1136
+ )
1137
+
1138
+ if is_rate_limit:
1139
+ logger.error(f"Rate limit detected during batch embedding. {e}")
1140
+ raise e
1141
+ else:
1142
+ logger.warning(f"Async batch embedding failed, falling back to individual embeddings: {e}")
1143
+ # Fall back to individual embedding
1144
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
1145
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
1146
+ else:
1147
+ # Use individual embedding
1148
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
1149
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
1052
1150
 
1053
1151
  for document in documents:
1054
1152
  try:
@@ -1063,9 +1161,12 @@ class MongoDb(VectorDb):
1063
1161
  logger.error(f"Error upserting document '{document.name}' asynchronously: {e}")
1064
1162
 
1065
1163
  async def async_search(
1066
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
1164
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
1067
1165
  ) -> List[Document]:
1068
1166
  """Search for documents asynchronously."""
1167
+ if isinstance(filters, List):
1168
+ log_warning("Filters Expressions are not supported in MongoDB. No filters will be applied.")
1169
+ filters = None
1069
1170
  query_embedding = self.embedder.get_embedding(query)
1070
1171
  if query_embedding is None:
1071
1172
  logger.error(f"Failed to generate embedding for query: {query}")
@@ -1310,3 +1411,7 @@ class MongoDb(VectorDb):
1310
1411
  except Exception as e:
1311
1412
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
1312
1413
  raise
1414
+
1415
+ def get_supported_search_types(self) -> List[str]:
1416
+ """Get the supported search types for this vector database."""
1417
+ return [SearchType.vector, SearchType.hybrid]