agno 2.0.0rc2__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 (331) hide show
  1. agno/agent/agent.py +6009 -2874
  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 +595 -187
  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 +3 -0
  105. agno/knowledge/types.py +9 -0
  106. agno/knowledge/utils.py +20 -0
  107. agno/media.py +339 -266
  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 +1011 -566
  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 +110 -37
  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 +143 -4
  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 +60 -6
  142. agno/models/openai/chat.py +102 -43
  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 +81 -5
  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 -175
  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 +266 -112
  205. agno/run/base.py +53 -24
  206. agno/run/team.py +252 -111
  207. agno/run/workflow.py +156 -45
  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 -1692
  213. agno/tools/brightdata.py +3 -3
  214. agno/tools/cartesia.py +3 -5
  215. agno/tools/dalle.py +9 -8
  216. agno/tools/decorator.py +4 -2
  217. agno/tools/desi_vocal.py +2 -2
  218. agno/tools/duckduckgo.py +15 -11
  219. agno/tools/e2b.py +20 -13
  220. agno/tools/eleven_labs.py +26 -28
  221. agno/tools/exa.py +21 -16
  222. agno/tools/fal.py +4 -4
  223. agno/tools/file.py +153 -23
  224. agno/tools/file_generation.py +350 -0
  225. agno/tools/firecrawl.py +4 -4
  226. agno/tools/function.py +257 -37
  227. agno/tools/giphy.py +2 -2
  228. agno/tools/gmail.py +238 -14
  229. agno/tools/google_drive.py +270 -0
  230. agno/tools/googlecalendar.py +36 -8
  231. agno/tools/googlesheets.py +20 -5
  232. agno/tools/jira.py +20 -0
  233. agno/tools/knowledge.py +3 -3
  234. agno/tools/lumalab.py +3 -3
  235. agno/tools/mcp/__init__.py +10 -0
  236. agno/tools/mcp/mcp.py +331 -0
  237. agno/tools/mcp/multi_mcp.py +347 -0
  238. agno/tools/mcp/params.py +24 -0
  239. agno/tools/mcp_toolbox.py +284 -0
  240. agno/tools/mem0.py +11 -17
  241. agno/tools/memori.py +1 -53
  242. agno/tools/memory.py +419 -0
  243. agno/tools/models/azure_openai.py +2 -2
  244. agno/tools/models/gemini.py +3 -3
  245. agno/tools/models/groq.py +3 -5
  246. agno/tools/models/nebius.py +7 -7
  247. agno/tools/models_labs.py +25 -15
  248. agno/tools/notion.py +204 -0
  249. agno/tools/openai.py +4 -9
  250. agno/tools/opencv.py +3 -3
  251. agno/tools/parallel.py +314 -0
  252. agno/tools/replicate.py +7 -7
  253. agno/tools/scrapegraph.py +58 -31
  254. agno/tools/searxng.py +2 -2
  255. agno/tools/serper.py +2 -2
  256. agno/tools/slack.py +18 -3
  257. agno/tools/spider.py +2 -2
  258. agno/tools/tavily.py +146 -0
  259. agno/tools/whatsapp.py +1 -1
  260. agno/tools/workflow.py +278 -0
  261. agno/tools/yfinance.py +12 -11
  262. agno/utils/agent.py +820 -0
  263. agno/utils/audio.py +27 -0
  264. agno/utils/common.py +90 -1
  265. agno/utils/events.py +222 -7
  266. agno/utils/gemini.py +181 -23
  267. agno/utils/hooks.py +57 -0
  268. agno/utils/http.py +111 -0
  269. agno/utils/knowledge.py +12 -5
  270. agno/utils/log.py +1 -0
  271. agno/utils/mcp.py +95 -5
  272. agno/utils/media.py +188 -10
  273. agno/utils/merge_dict.py +22 -1
  274. agno/utils/message.py +60 -0
  275. agno/utils/models/claude.py +40 -11
  276. agno/utils/models/cohere.py +1 -1
  277. agno/utils/models/watsonx.py +1 -1
  278. agno/utils/openai.py +1 -1
  279. agno/utils/print_response/agent.py +105 -21
  280. agno/utils/print_response/team.py +103 -38
  281. agno/utils/print_response/workflow.py +251 -34
  282. agno/utils/reasoning.py +22 -1
  283. agno/utils/serialize.py +32 -0
  284. agno/utils/streamlit.py +16 -10
  285. agno/utils/string.py +41 -0
  286. agno/utils/team.py +98 -9
  287. agno/utils/tools.py +1 -1
  288. agno/vectordb/base.py +23 -4
  289. agno/vectordb/cassandra/cassandra.py +65 -9
  290. agno/vectordb/chroma/chromadb.py +182 -38
  291. agno/vectordb/clickhouse/clickhousedb.py +64 -11
  292. agno/vectordb/couchbase/couchbase.py +105 -10
  293. agno/vectordb/lancedb/lance_db.py +183 -135
  294. agno/vectordb/langchaindb/langchaindb.py +25 -7
  295. agno/vectordb/lightrag/lightrag.py +17 -3
  296. agno/vectordb/llamaindex/__init__.py +3 -0
  297. agno/vectordb/llamaindex/llamaindexdb.py +46 -7
  298. agno/vectordb/milvus/milvus.py +126 -9
  299. agno/vectordb/mongodb/__init__.py +7 -1
  300. agno/vectordb/mongodb/mongodb.py +112 -7
  301. agno/vectordb/pgvector/pgvector.py +142 -21
  302. agno/vectordb/pineconedb/pineconedb.py +80 -8
  303. agno/vectordb/qdrant/qdrant.py +125 -39
  304. agno/vectordb/redis/__init__.py +9 -0
  305. agno/vectordb/redis/redisdb.py +694 -0
  306. agno/vectordb/singlestore/singlestore.py +111 -25
  307. agno/vectordb/surrealdb/surrealdb.py +31 -5
  308. agno/vectordb/upstashdb/upstashdb.py +76 -8
  309. agno/vectordb/weaviate/weaviate.py +86 -15
  310. agno/workflow/__init__.py +2 -0
  311. agno/workflow/agent.py +299 -0
  312. agno/workflow/condition.py +112 -18
  313. agno/workflow/loop.py +69 -10
  314. agno/workflow/parallel.py +266 -118
  315. agno/workflow/router.py +110 -17
  316. agno/workflow/step.py +645 -136
  317. agno/workflow/steps.py +65 -6
  318. agno/workflow/types.py +71 -33
  319. agno/workflow/workflow.py +2113 -300
  320. agno-2.3.0.dist-info/METADATA +618 -0
  321. agno-2.3.0.dist-info/RECORD +577 -0
  322. agno-2.3.0.dist-info/licenses/LICENSE +201 -0
  323. agno/knowledge/reader/url_reader.py +0 -128
  324. agno/tools/googlesearch.py +0 -98
  325. agno/tools/mcp.py +0 -610
  326. agno/utils/models/aws_claude.py +0 -170
  327. agno-2.0.0rc2.dist-info/METADATA +0 -355
  328. agno-2.0.0rc2.dist-info/RECORD +0 -515
  329. agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
  330. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
  331. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,8 @@
1
- from typing import Any, Dict, List, Optional
1
+ from typing import Any, 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 log_debug, logger
5
+ from agno.utils.log import log_debug, log_warning, logger
5
6
  from agno.vectordb.base import VectorDb
6
7
 
7
8
 
@@ -11,16 +12,23 @@ class LangChainVectorDb(VectorDb):
11
12
  vectorstore: Optional[Any] = None,
12
13
  search_kwargs: Optional[dict] = None,
13
14
  knowledge_retriever: Optional[Any] = None,
15
+ name: Optional[str] = None,
16
+ description: Optional[str] = None,
14
17
  ):
15
18
  """
16
19
  Initialize LangChainVectorDb.
17
20
 
18
21
  Args:
19
22
  vectorstore: The LangChain vectorstore instance
23
+ name (Optional[str]): Name of the vector database.
24
+ description (Optional[str]): Description of the vector database.
20
25
  search_kwargs: Additional search parameters for the retriever
21
26
  knowledge_retriever: An optional LangChain retriever instance
22
27
  """
23
28
  self.vectorstore = vectorstore
29
+ # Initialize base class with name and description
30
+ super().__init__(name=name, description=description)
31
+
24
32
  self.search_kwargs = search_kwargs
25
33
  self.knowledge_retriever = knowledge_retriever
26
34
 
@@ -64,10 +72,16 @@ class LangChainVectorDb(VectorDb):
64
72
  raise NotImplementedError
65
73
 
66
74
  def search(
67
- self, query: str, num_documents: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
75
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
68
76
  ) -> List[Document]:
69
77
  """Returns relevant documents matching the query"""
70
78
 
79
+ if isinstance(filters, List):
80
+ log_warning(
81
+ "Filter Expressions are not supported in LangChainDB. No filters will be applied. Use filters as a dictionary."
82
+ )
83
+ filters = None
84
+
71
85
  try:
72
86
  from langchain_core.documents import Document as LangChainDocument
73
87
  from langchain_core.retrievers import BaseRetriever
@@ -79,7 +93,7 @@ class LangChainVectorDb(VectorDb):
79
93
  if self.vectorstore is not None and self.knowledge_retriever is None:
80
94
  log_debug("Creating knowledge retriever")
81
95
  if self.search_kwargs is None:
82
- self.search_kwargs = {"k": num_documents}
96
+ self.search_kwargs = {"k": limit}
83
97
  if filters is not None:
84
98
  self.search_kwargs.update(filters)
85
99
  self.knowledge_retriever = self.vectorstore.as_retriever(search_kwargs=self.search_kwargs)
@@ -91,7 +105,7 @@ class LangChainVectorDb(VectorDb):
91
105
  if not isinstance(self.knowledge_retriever, BaseRetriever):
92
106
  raise ValueError(f"Knowledge retriever is not of type BaseRetriever: {self.knowledge_retriever}")
93
107
 
94
- log_debug(f"Getting {num_documents} relevant documents for query: {query}")
108
+ log_debug(f"Getting {limit} relevant documents for query: {query}")
95
109
  lc_documents: List[LangChainDocument] = self.knowledge_retriever.invoke(input=query)
96
110
  documents = []
97
111
  for lc_doc in lc_documents:
@@ -104,9 +118,9 @@ class LangChainVectorDb(VectorDb):
104
118
  return documents
105
119
 
106
120
  async def async_search(
107
- self, query: str, num_documents: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
121
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
108
122
  ) -> List[Document]:
109
- return self.search(query, num_documents, filters)
123
+ return self.search(query, limit, filters)
110
124
 
111
125
  def drop(self) -> None:
112
126
  raise NotImplementedError
@@ -143,3 +157,7 @@ class LangChainVectorDb(VectorDb):
143
157
  metadata (Dict[str, Any]): The metadata to update
144
158
  """
145
159
  raise NotImplementedError("update_metadata not supported for LangChain vectorstores")
160
+
161
+ def get_supported_search_types(self) -> List[str]:
162
+ """Get the supported search types for this vector database."""
163
+ return [] # LangChainVectorDb doesn't use SearchType enum
@@ -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
+ ]