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,5 +1,5 @@
1
1
  from hashlib import md5
2
- from typing import Any, Dict, List, Optional
2
+ from typing import Any, Dict, List, Optional, Union
3
3
 
4
4
  try:
5
5
  from qdrant_client import AsyncQdrantClient, QdrantClient # noqa: F401
@@ -9,6 +9,7 @@ except ImportError:
9
9
  "The `qdrant-client` package is not installed. Please install it via `pip install qdrant-client`."
10
10
  )
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
@@ -28,6 +29,9 @@ class Qdrant(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
  location: Optional[str] = None,
@@ -52,6 +56,8 @@ class Qdrant(VectorDb):
52
56
  """
53
57
  Args:
54
58
  collection (str): Name of the Qdrant collection.
59
+ name (Optional[str]): Name of the vector database.
60
+ description (Optional[str]): Description of the vector database.
55
61
  embedder (Optional[Embedder]): Optional embedder for automatic vector generation.
56
62
  distance (Distance): Distance metric to use (default: cosine).
57
63
  location (Optional[str]): `":memory:"` for in-memory, or str used as `url`. If `None`, use default host/port.
@@ -73,6 +79,21 @@ class Qdrant(VectorDb):
73
79
  fastembed_kwargs (Optional[dict]): Keyword args for `fastembed.SparseTextEmbedding.__init__()`.
74
80
  **kwargs: Keyword args for `qdrant_client.QdrantClient.__init__()`.
75
81
  """
82
+ # Validate required parameters
83
+ if not collection:
84
+ raise ValueError("Collection name must be provided.")
85
+
86
+ # Dynamic ID generation based on unique identifiers
87
+ if id is None:
88
+ from agno.utils.string import generate_id
89
+
90
+ host_identifier = host or location or url or "localhost"
91
+ seed = f"{host_identifier}#{collection}"
92
+ id = generate_id(seed)
93
+
94
+ # Initialize base class with name, description, and generated ID
95
+ super().__init__(id=id, name=name, description=description)
96
+
76
97
  # Collection attributes
77
98
  self.collection: str = collection
78
99
 
@@ -131,7 +152,8 @@ class Qdrant(VectorDb):
131
152
  if fastembed_kwargs:
132
153
  default_kwargs.update(fastembed_kwargs)
133
154
 
134
- self.sparse_encoder = SparseTextEmbedding(**default_kwargs)
155
+ # Type ignore for mypy as SparseTextEmbedding constructor accepts flexible kwargs
156
+ self.sparse_encoder = SparseTextEmbedding(**default_kwargs) # type: ignore
135
157
 
136
158
  except ImportError as e:
137
159
  raise ImportError(
@@ -192,10 +214,12 @@ class Qdrant(VectorDb):
192
214
  # Configure vectors based on search type
193
215
  if self.search_type == SearchType.vector:
194
216
  # Maintain backward compatibility with unnamed vectors
195
- vectors_config = models.VectorParams(size=self.dimensions, distance=_distance)
217
+ vectors_config = models.VectorParams(size=self.dimensions or 1536, distance=_distance)
196
218
  else:
197
219
  # Use named vectors for hybrid search
198
- vectors_config = {self.dense_vector_name: models.VectorParams(size=self.dimensions, distance=_distance)} # type: ignore
220
+ vectors_config = {
221
+ self.dense_vector_name: models.VectorParams(size=self.dimensions or 1536, distance=_distance)
222
+ } # type: ignore
199
223
 
200
224
  self.client.create_collection(
201
225
  collection_name=self.collection,
@@ -220,10 +244,12 @@ class Qdrant(VectorDb):
220
244
  # Configure vectors based on search type
221
245
  if self.search_type == SearchType.vector:
222
246
  # Maintain backward compatibility with unnamed vectors
223
- vectors_config = models.VectorParams(size=self.dimensions, distance=_distance)
247
+ vectors_config = models.VectorParams(size=self.dimensions or 1536, distance=_distance)
224
248
  else:
225
249
  # Use named vectors for hybrid search
226
- vectors_config = {self.dense_vector_name: models.VectorParams(size=self.dimensions, distance=_distance)} # type: ignore
250
+ vectors_config = {
251
+ self.dense_vector_name: models.VectorParams(size=self.dimensions or 1536, distance=_distance)
252
+ } # type: ignore
227
253
 
228
254
  await self.async_client.create_collection(
229
255
  collection_name=self.collection,
@@ -281,7 +307,7 @@ class Qdrant(VectorDb):
281
307
  return len(scroll_result[0]) > 0
282
308
  return False
283
309
 
284
- async def async_name_exists(self, name: str) -> bool:
310
+ async def async_name_exists(self, name: str) -> bool: # type: ignore[override]
285
311
  """
286
312
  Asynchronously validates if a document with the given name exists in the collection.
287
313
 
@@ -341,7 +367,9 @@ class Qdrant(VectorDb):
341
367
  vector[self.dense_vector_name] = document.embedding
342
368
 
343
369
  if self.search_type in [SearchType.keyword, SearchType.hybrid]:
344
- vector[self.sparse_vector_name] = next(self.sparse_encoder.embed([document.content])).as_object()
370
+ vector[self.sparse_vector_name] = next(
371
+ iter(self.sparse_encoder.embed([document.content]))
372
+ ).as_object() # type: ignore
345
373
 
346
374
  # Create payload with document properties
347
375
  payload = {
@@ -363,7 +391,7 @@ class Qdrant(VectorDb):
363
391
  points.append(
364
392
  models.PointStruct(
365
393
  id=doc_id,
366
- vector=vector,
394
+ vector=vector, # type: ignore
367
395
  payload=payload,
368
396
  )
369
397
  )
@@ -384,26 +412,69 @@ class Qdrant(VectorDb):
384
412
  """
385
413
  log_debug(f"Inserting {len(documents)} documents asynchronously")
386
414
 
415
+ # Apply batch embedding when needed for vector or hybrid search
416
+ if self.search_type in [SearchType.vector, SearchType.hybrid]:
417
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
418
+ # Use batch embedding when enabled and supported
419
+ try:
420
+ # Extract content from all documents
421
+ doc_contents = [doc.content for doc in documents]
422
+
423
+ # Get batch embeddings and usage
424
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
425
+
426
+ # Process documents with pre-computed embeddings
427
+ for j, doc in enumerate(documents):
428
+ try:
429
+ if j < len(embeddings):
430
+ doc.embedding = embeddings[j]
431
+ doc.usage = usages[j] if j < len(usages) else None
432
+ except Exception as e:
433
+ log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
434
+
435
+ except Exception as e:
436
+ # Check if this is a rate limit error - don't fall back as it would make things worse
437
+ error_str = str(e).lower()
438
+ is_rate_limit = any(
439
+ phrase in error_str
440
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
441
+ )
442
+
443
+ if is_rate_limit:
444
+ log_error(f"Rate limit detected during batch embedding. {e}")
445
+ raise e
446
+ else:
447
+ log_warning(f"Async batch embedding failed, falling back to individual embeddings: {e}")
448
+ # Fall back to individual embedding
449
+ for doc in documents:
450
+ if self.search_type in [SearchType.vector, SearchType.hybrid]:
451
+ doc.embed(embedder=self.embedder)
452
+ else:
453
+ # Use individual embedding
454
+ for doc in documents:
455
+ if self.search_type in [SearchType.vector, SearchType.hybrid]:
456
+ doc.embed(embedder=self.embedder)
457
+
387
458
  async def process_document(document):
388
459
  cleaned_content = document.content.replace("\x00", "\ufffd")
389
460
  doc_id = md5(cleaned_content.encode()).hexdigest()
390
461
 
391
462
  if self.search_type == SearchType.vector:
392
463
  # For vector search, maintain backward compatibility with unnamed vectors
393
- document.embed(embedder=self.embedder)
394
- vector = document.embedding
464
+ vector = document.embedding # Already embedded above
395
465
  else:
396
466
  # For other search types, use named vectors
397
467
  vector = {}
398
468
  if self.search_type in [SearchType.hybrid]:
399
- document.embed(embedder=self.embedder)
400
- vector[self.dense_vector_name] = document.embedding
469
+ vector[self.dense_vector_name] = document.embedding # Already embedded above
401
470
 
402
471
  if self.search_type in [SearchType.keyword, SearchType.hybrid]:
403
- vector[self.sparse_vector_name] = next(self.sparse_encoder.embed([document.content])).as_object()
472
+ vector[self.sparse_vector_name] = next(
473
+ iter(self.sparse_encoder.embed([document.content]))
474
+ ).as_object() # type: ignore
404
475
 
405
476
  if self.search_type in [SearchType.keyword, SearchType.hybrid]:
406
- vector[self.sparse_vector_name] = next(self.sparse_encoder.embed([document.content])).as_object()
477
+ vector[self.sparse_vector_name] = next(iter(self.sparse_encoder.embed([document.content]))).as_object()
407
478
 
408
479
  # Create payload with document properties
409
480
  payload = {
@@ -423,9 +494,9 @@ class Qdrant(VectorDb):
423
494
  payload["meta_data"].update(filters)
424
495
 
425
496
  log_debug(f"Inserted document asynchronously: {document.name} ({document.meta_data})")
426
- return models.PointStruct(
497
+ return models.PointStruct( # type: ignore
427
498
  id=doc_id,
428
- vector=vector,
499
+ vector=vector, # type: ignore
429
500
  payload=payload,
430
501
  )
431
502
 
@@ -458,7 +529,9 @@ class Qdrant(VectorDb):
458
529
  log_debug("Redirecting the async request to async_insert")
459
530
  await self.async_insert(content_hash=content_hash, documents=documents, filters=filters)
460
531
 
461
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
532
+ def search(
533
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
534
+ ) -> List[Document]:
462
535
  """
463
536
  Search for documents in the collection.
464
537
 
@@ -467,28 +540,37 @@ class Qdrant(VectorDb):
467
540
  limit (int): Number of search results to return
468
541
  filters (Optional[Dict[str, Any]]): Filters to apply while searching
469
542
  """
543
+
544
+ if isinstance(filters, List):
545
+ log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
546
+ filters = None
547
+
470
548
  filters = self._format_filters(filters or {}) # type: ignore
471
549
  if self.search_type == SearchType.vector:
472
- results = self._run_vector_search_sync(query, limit, filters)
550
+ results = self._run_vector_search_sync(query, limit, filters) # type: ignore
473
551
  elif self.search_type == SearchType.keyword:
474
- results = self._run_keyword_search_sync(query, limit, filters)
552
+ results = self._run_keyword_search_sync(query, limit, filters) # type: ignore
475
553
  elif self.search_type == SearchType.hybrid:
476
- results = self._run_hybrid_search_sync(query, limit, filters)
554
+ results = self._run_hybrid_search_sync(query, limit, filters) # type: ignore
477
555
  else:
478
556
  raise ValueError(f"Unsupported search type: {self.search_type}")
479
557
 
480
558
  return self._build_search_results(results, query)
481
559
 
482
560
  async def async_search(
483
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
561
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
484
562
  ) -> List[Document]:
563
+ if isinstance(filters, List):
564
+ log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
565
+ filters = None
566
+
485
567
  filters = self._format_filters(filters or {}) # type: ignore
486
568
  if self.search_type == SearchType.vector:
487
- results = await self._run_vector_search_async(query, limit, filters)
569
+ results = await self._run_vector_search_async(query, limit, filters) # type: ignore
488
570
  elif self.search_type == SearchType.keyword:
489
- results = await self._run_keyword_search_async(query, limit, filters)
571
+ results = await self._run_keyword_search_async(query, limit, filters) # type: ignore
490
572
  elif self.search_type == SearchType.hybrid:
491
- results = await self._run_hybrid_search_async(query, limit, filters)
573
+ results = await self._run_hybrid_search_async(query, limit, filters) # type: ignore
492
574
  else:
493
575
  raise ValueError(f"Unsupported search type: {self.search_type}")
494
576
 
@@ -498,15 +580,15 @@ class Qdrant(VectorDb):
498
580
  self,
499
581
  query: str,
500
582
  limit: int,
501
- filters: Optional[Dict[str, Any]],
583
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
502
584
  ) -> List[models.ScoredPoint]:
503
585
  dense_embedding = self.embedder.get_embedding(query)
504
- sparse_embedding = next(self.sparse_encoder.embed([query])).as_object()
586
+ sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
505
587
  call = self.client.query_points(
506
588
  collection_name=self.collection,
507
589
  prefetch=[
508
590
  models.Prefetch(
509
- query=models.SparseVector(**sparse_embedding),
591
+ query=models.SparseVector(**sparse_embedding), # type: ignore # type: ignore
510
592
  limit=limit,
511
593
  using=self.sparse_vector_name,
512
594
  ),
@@ -524,7 +606,7 @@ class Qdrant(VectorDb):
524
606
  self,
525
607
  query: str,
526
608
  limit: int,
527
- filters: Optional[Dict[str, Any]],
609
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
528
610
  ) -> List[models.ScoredPoint]:
529
611
  dense_embedding = self.embedder.get_embedding(query)
530
612
 
@@ -555,12 +637,12 @@ class Qdrant(VectorDb):
555
637
  self,
556
638
  query: str,
557
639
  limit: int,
558
- filters: Optional[Dict[str, Any]],
640
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
559
641
  ) -> List[models.ScoredPoint]:
560
- sparse_embedding = next(self.sparse_encoder.embed([query])).as_object()
642
+ sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
561
643
  call = self.client.query_points(
562
644
  collection_name=self.collection,
563
- query=models.SparseVector(**sparse_embedding),
645
+ query=models.SparseVector(**sparse_embedding), # type: ignore
564
646
  with_vectors=True,
565
647
  with_payload=True,
566
648
  limit=limit,
@@ -606,10 +688,10 @@ class Qdrant(VectorDb):
606
688
  limit: int,
607
689
  filters: Optional[Dict[str, Any]],
608
690
  ) -> List[models.ScoredPoint]:
609
- sparse_embedding = next(self.sparse_encoder.embed([query])).as_object()
691
+ sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
610
692
  call = await self.async_client.query_points(
611
693
  collection_name=self.collection,
612
- query=models.SparseVector(**sparse_embedding),
694
+ query=models.SparseVector(**sparse_embedding), # type: ignore
613
695
  with_vectors=True,
614
696
  with_payload=True,
615
697
  limit=limit,
@@ -622,15 +704,15 @@ class Qdrant(VectorDb):
622
704
  self,
623
705
  query: str,
624
706
  limit: int,
625
- filters: Optional[Dict[str, Any]],
707
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
626
708
  ) -> List[models.ScoredPoint]:
627
709
  dense_embedding = self.embedder.get_embedding(query)
628
- sparse_embedding = next(self.sparse_encoder.embed([query])).as_object()
710
+ sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
629
711
  call = await self.async_client.query_points(
630
712
  collection_name=self.collection,
631
713
  prefetch=[
632
714
  models.Prefetch(
633
- query=models.SparseVector(**sparse_embedding),
715
+ query=models.SparseVector(**sparse_embedding), # type: ignore # type: ignore
634
716
  limit=limit,
635
717
  using=self.sparse_vector_name,
636
718
  ),
@@ -689,7 +771,7 @@ class Qdrant(VectorDb):
689
771
  filter_conditions.append(models.FieldCondition(key=key, match=models.MatchValue(value=value)))
690
772
 
691
773
  if filter_conditions:
692
- return models.Filter(must=filter_conditions)
774
+ return models.Filter(must=filter_conditions) # type: ignore
693
775
 
694
776
  return None
695
777
 
@@ -807,7 +889,7 @@ class Qdrant(VectorDb):
807
889
  )
808
890
 
809
891
  # Create a filter that requires ALL metadata conditions to match
810
- filter_condition = models.Filter(must=filter_conditions)
892
+ filter_condition = models.Filter(must=filter_conditions) # type: ignore
811
893
 
812
894
  # First, count how many points will be deleted
813
895
  count_result = self.client.count(collection_name=self.collection, count_filter=filter_condition, exact=True)
@@ -1046,3 +1128,7 @@ class Qdrant(VectorDb):
1046
1128
  log_debug(f"Error closing async Qdrant client: {e}")
1047
1129
  finally:
1048
1130
  self._async_client = None
1131
+
1132
+ def get_supported_search_types(self) -> List[str]:
1133
+ """Get the supported search types for this vector database."""
1134
+ return [SearchType.vector, SearchType.keyword, SearchType.hybrid]
@@ -0,0 +1,9 @@
1
+ from agno.vectordb.redis.redisdb import RedisDB
2
+
3
+ # Backward compatibility alias
4
+ RedisVectorDb = RedisDB
5
+
6
+ __all__ = [
7
+ "RedisVectorDb",
8
+ "RedisDB",
9
+ ]