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,7 @@
1
1
  import asyncio
2
2
  import json
3
3
  from hashlib import md5
4
- from typing import Any, Dict, List, Optional
4
+ from typing import Any, Dict, List, Optional, Union
5
5
 
6
6
  try:
7
7
  from sqlalchemy.dialects import mysql
@@ -14,10 +14,11 @@ try:
14
14
  except ImportError:
15
15
  raise ImportError("`sqlalchemy` not installed")
16
16
 
17
+ from agno.filters import FilterExpr
17
18
  from agno.knowledge.document import Document
18
19
  from agno.knowledge.embedder import Embedder
19
20
  from agno.knowledge.reranker.base import Reranker
20
- from agno.utils.log import log_debug, log_error, log_info
21
+ from agno.utils.log import log_debug, log_error, log_info, log_warning
21
22
  from agno.vectordb.base import VectorDb
22
23
  from agno.vectordb.distance import Distance
23
24
 
@@ -32,6 +33,8 @@ class SingleStore(VectorDb):
32
33
  embedder: Optional[Embedder] = None,
33
34
  distance: Distance = Distance.cosine,
34
35
  reranker: Optional[Reranker] = None,
36
+ name: Optional[str] = None,
37
+ description: Optional[str] = None,
35
38
  # index: Optional[Union[Ivfflat, HNSW]] = HNSW(),
36
39
  ):
37
40
  _engine: Optional[Engine] = db_engine
@@ -44,9 +47,11 @@ class SingleStore(VectorDb):
44
47
  self.collection: str = collection
45
48
  self.schema: Optional[str] = schema
46
49
  self.db_url: Optional[str] = db_url
50
+ # Initialize base class with name and description
51
+ super().__init__(name=name, description=description)
52
+
47
53
  self.db_engine: Engine = _engine
48
54
  self.metadata: MetaData = MetaData(schema=self.schema)
49
-
50
55
  if embedder is None:
51
56
  from agno.knowledge.embedder.openai import OpenAIEmbedder
52
57
 
@@ -278,7 +283,9 @@ class SingleStore(VectorDb):
278
283
  sess.commit()
279
284
  log_debug(f"Committed {counter} documents")
280
285
 
281
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
286
+ def search(
287
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
288
+ ) -> List[Document]:
282
289
  """
283
290
  Search for documents based on a query and optional filters.
284
291
 
@@ -290,6 +297,8 @@ class SingleStore(VectorDb):
290
297
  Returns:
291
298
  List[Document]: List of documents that match the query.
292
299
  """
300
+ if filters is not None:
301
+ log_warning("Filters are not supported in SingleStore. No filters will be applied.")
293
302
  query_embedding = self.embedder.get_embedding(query)
294
303
  if query_embedding is None:
295
304
  log_error(f"Error getting embedding for Query: {query}")
@@ -428,9 +437,9 @@ class SingleStore(VectorDb):
428
437
  try:
429
438
  with self.Session.begin() as sess:
430
439
  stmt = delete(self.table).where(self.table.c.id == id)
431
- result = sess.execute(stmt)
432
- log_info(f"Deleted {result.rowcount} records with ID {id} from table '{self.table.name}'.")
433
- return result.rowcount > 0
440
+ result = sess.execute(stmt) # type: ignore
441
+ log_info(f"Deleted {result.rowcount} records with ID {id} from table '{self.table.name}'.") # type: ignore
442
+ return result.rowcount > 0 # type: ignore
434
443
  except Exception as e:
435
444
  log_error(f"Error deleting document with ID {id}: {e}")
436
445
  return False
@@ -444,11 +453,11 @@ class SingleStore(VectorDb):
444
453
  try:
445
454
  with self.Session.begin() as sess:
446
455
  stmt = delete(self.table).where(self.table.c.content_id == content_id)
447
- result = sess.execute(stmt)
456
+ result = sess.execute(stmt) # type: ignore
448
457
  log_info(
449
- f"Deleted {result.rowcount} records with content_id {content_id} from table '{self.table.name}'."
458
+ f"Deleted {result.rowcount} records with content_id {content_id} from table '{self.table.name}'." # type: ignore
450
459
  )
451
- return result.rowcount > 0
460
+ return result.rowcount > 0 # type: ignore
452
461
  except Exception as e:
453
462
  log_error(f"Error deleting document with content_id {content_id}: {e}")
454
463
  return False
@@ -462,9 +471,9 @@ class SingleStore(VectorDb):
462
471
  try:
463
472
  with self.Session.begin() as sess:
464
473
  stmt = delete(self.table).where(self.table.c.name == name)
465
- result = sess.execute(stmt)
466
- log_info(f"Deleted {result.rowcount} records with name '{name}' from table '{self.table.name}'.")
467
- return result.rowcount > 0
474
+ result = sess.execute(stmt) # type: ignore
475
+ log_info(f"Deleted {result.rowcount} records with name '{name}' from table '{self.table.name}'.") # type: ignore
476
+ return result.rowcount > 0 # type: ignore
468
477
  except Exception as e:
469
478
  log_error(f"Error deleting document with name {name}: {e}")
470
479
  return False
@@ -480,9 +489,9 @@ class SingleStore(VectorDb):
480
489
  # Convert metadata to JSON string for comparison
481
490
  metadata_json = json.dumps(metadata, sort_keys=True)
482
491
  stmt = delete(self.table).where(self.table.c.meta_data == metadata_json)
483
- result = sess.execute(stmt)
484
- log_info(f"Deleted {result.rowcount} records with metadata {metadata} from table '{self.table.name}'.")
485
- return result.rowcount > 0
492
+ result = sess.execute(stmt) # type: ignore
493
+ log_info(f"Deleted {result.rowcount} records with metadata {metadata} from table '{self.table.name}'.") # type: ignore
494
+ return result.rowcount > 0 # type: ignore
486
495
  except Exception as e:
487
496
  log_error(f"Error deleting documents with metadata {metadata}: {e}")
488
497
  return False
@@ -496,8 +505,44 @@ class SingleStore(VectorDb):
496
505
  documents: List[Document],
497
506
  filters: Optional[Dict[str, Any]] = None,
498
507
  ) -> None:
499
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
500
- await asyncio.gather(*embed_tasks, return_exceptions=True)
508
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
509
+ # Use batch embedding when enabled and supported
510
+ try:
511
+ # Extract content from all documents
512
+ doc_contents = [doc.content for doc in documents]
513
+
514
+ # Get batch embeddings and usage
515
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
516
+
517
+ # Process documents with pre-computed embeddings
518
+ for j, doc in enumerate(documents):
519
+ try:
520
+ if j < len(embeddings):
521
+ doc.embedding = embeddings[j]
522
+ doc.usage = usages[j] if j < len(usages) else None
523
+ except Exception as e:
524
+ log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
525
+
526
+ except Exception as e:
527
+ # Check if this is a rate limit error - don't fall back as it would make things worse
528
+ error_str = str(e).lower()
529
+ is_rate_limit = any(
530
+ phrase in error_str
531
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
532
+ )
533
+
534
+ if is_rate_limit:
535
+ log_error(f"Rate limit detected during batch embedding. {e}")
536
+ raise e
537
+ else:
538
+ log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
539
+ # Fall back to individual embedding
540
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
541
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
542
+ else:
543
+ # Use individual embedding
544
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
545
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
501
546
 
502
547
  with self.Session.begin() as sess:
503
548
  counter = 0
@@ -543,8 +588,45 @@ class SingleStore(VectorDb):
543
588
  filters (Optional[Dict[str, Any]]): Optional filters for the upsert.
544
589
  batch_size (int): Number of documents to upsert in each batch.
545
590
  """
546
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
547
- await asyncio.gather(*embed_tasks, return_exceptions=True)
591
+
592
+ if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
593
+ # Use batch embedding when enabled and supported
594
+ try:
595
+ # Extract content from all documents
596
+ doc_contents = [doc.content for doc in documents]
597
+
598
+ # Get batch embeddings and usage
599
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
600
+
601
+ # Process documents with pre-computed embeddings
602
+ for j, doc in enumerate(documents):
603
+ try:
604
+ if j < len(embeddings):
605
+ doc.embedding = embeddings[j]
606
+ doc.usage = usages[j] if j < len(usages) else None
607
+ except Exception as e:
608
+ log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
609
+
610
+ except Exception as e:
611
+ # Check if this is a rate limit error - don't fall back as it would make things worse
612
+ error_str = str(e).lower()
613
+ is_rate_limit = any(
614
+ phrase in error_str
615
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
616
+ )
617
+
618
+ if is_rate_limit:
619
+ log_error(f"Rate limit detected during batch embedding. {e}")
620
+ raise e
621
+ else:
622
+ log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
623
+ # Fall back to individual embedding
624
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
625
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
626
+ else:
627
+ # Use individual embedding
628
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
629
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
548
630
 
549
631
  with self.Session.begin() as sess:
550
632
  counter = 0
@@ -588,7 +670,7 @@ class SingleStore(VectorDb):
588
670
  log_debug(f"Committed {counter} documents")
589
671
 
590
672
  async def async_search(
591
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
673
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
592
674
  ) -> List[Document]:
593
675
  return self.search(query=query, limit=limit, filters=filters)
594
676
 
@@ -616,11 +698,11 @@ class SingleStore(VectorDb):
616
698
  try:
617
699
  with self.Session.begin() as sess:
618
700
  stmt = delete(self.table).where(self.table.c.content_hash == content_hash)
619
- result = sess.execute(stmt)
701
+ result = sess.execute(stmt) # type: ignore
620
702
  log_info(
621
- f"Deleted {result.rowcount} records with content_hash '{content_hash}' from table '{self.table.name}'."
703
+ f"Deleted {result.rowcount} records with content_hash '{content_hash}' from table '{self.table.name}'." # type: ignore
622
704
  )
623
- return result.rowcount > 0
705
+ return result.rowcount > 0 # type: ignore
624
706
  except Exception as e:
625
707
  log_error(f"Error deleting documents with content_hash {content_hash}: {e}")
626
708
  return False
@@ -639,7 +721,7 @@ class SingleStore(VectorDb):
639
721
  with self.Session.begin() as sess:
640
722
  # Find documents with the given content_id
641
723
  stmt = select(self.table).where(self.table.c.content_id == content_id)
642
- result = sess.execute(stmt)
724
+ result = sess.execute(stmt) # type: ignore
643
725
 
644
726
  updated_count = 0
645
727
  for row in result:
@@ -675,3 +757,7 @@ class SingleStore(VectorDb):
675
757
  except Exception as e:
676
758
  log_error(f"Error updating metadata for content_id '{content_id}': {e}")
677
759
  raise
760
+
761
+ def get_supported_search_types(self) -> List[str]:
762
+ """Get the supported search types for this vector database."""
763
+ return [] # SingleStore doesn't use SearchType enum
@@ -11,9 +11,10 @@ except ImportError as e:
11
11
  msg = "The `surrealdb` package is not installed. Please install it via `pip install surrealdb`."
12
12
  raise ImportError(msg) from e
13
13
 
14
+ from agno.filters import FilterExpr
14
15
  from agno.knowledge.document import Document
15
16
  from agno.knowledge.embedder import Embedder
16
- from agno.utils.log import log_debug, log_error, log_info
17
+ from agno.utils.log import log_debug, log_error, log_info, log_warning
17
18
  from agno.vectordb.base import VectorDb
18
19
  from agno.vectordb.distance import Distance
19
20
 
@@ -107,11 +108,13 @@ class SurrealDb(VectorDb):
107
108
  m: int = 12,
108
109
  search_ef: int = 40,
109
110
  embedder: Optional[Embedder] = None,
111
+ name: Optional[str] = None,
112
+ description: Optional[str] = None,
113
+ id: Optional[str] = None,
110
114
  ):
111
115
  """Initialize SurrealDB connection.
112
116
 
113
117
  Args:
114
- url: SurrealDB server URL (e.g. ws://localhost:8000/rpc)
115
118
  client: A blocking connection, either HTTP or WS
116
119
  async_client: An async connection, either HTTP or WS (default: None)
117
120
  collection: Collection name to store documents (default: documents)
@@ -122,6 +125,17 @@ class SurrealDb(VectorDb):
122
125
  embedder: Embedder instance for creating embeddings (default: OpenAIEmbedder)
123
126
 
124
127
  """
128
+ # Dynamic ID generation based on unique identifiers
129
+ if id is None:
130
+ from agno.utils.string import generate_id
131
+
132
+ client_info = str(client) if client else str(async_client) if async_client else "default"
133
+ seed = f"{client_info}#{collection}"
134
+ id = generate_id(seed)
135
+
136
+ # Initialize base class with name, description, and generated ID
137
+ super().__init__(id=id, name=name, description=description)
138
+
125
139
  # Embedder for embedding the document contents
126
140
  if embedder is None:
127
141
  from agno.knowledge.embedder.openai import OpenAIEmbedder
@@ -131,7 +145,6 @@ class SurrealDb(VectorDb):
131
145
  self.embedder: Embedder = embedder
132
146
  self.dimensions = self.embedder.dimensions
133
147
  self.collection = collection
134
-
135
148
  # Convert Distance enum to SurrealDB distance type
136
149
  self.distance = {Distance.cosine: "COSINE", Distance.l2: "EUCLIDEAN", Distance.max_inner_product: "DOT"}[
137
150
  distance
@@ -306,7 +319,9 @@ class SurrealDb(VectorDb):
306
319
  thing = f"{self.collection}:{doc.id}" if doc.id else self.collection
307
320
  self.client.query(self.UPSERT_QUERY.format(thing=thing), data)
308
321
 
309
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
322
+ def search(
323
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
324
+ ) -> List[Document]:
310
325
  """Search for similar documents.
311
326
 
312
327
  Args:
@@ -318,6 +333,9 @@ class SurrealDb(VectorDb):
318
333
  A list of documents that are similar to the query.
319
334
 
320
335
  """
336
+ if isinstance(filters, List):
337
+ log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
338
+ filters = None
321
339
  query_embedding = self.embedder.get_embedding(query)
322
340
  if query_embedding is None:
323
341
  log_error(f"Error getting embedding for Query: {query}")
@@ -548,7 +566,7 @@ class SurrealDb(VectorDb):
548
566
  self,
549
567
  query: str,
550
568
  limit: int = 5,
551
- filters: Optional[Dict[str, Any]] = None,
569
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
552
570
  ) -> List[Document]:
553
571
  """Search for similar documents asynchronously.
554
572
 
@@ -561,6 +579,10 @@ class SurrealDb(VectorDb):
561
579
  A list of documents that are similar to the query.
562
580
 
563
581
  """
582
+ if isinstance(filters, List):
583
+ log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
584
+ filters = None
585
+
564
586
  query_embedding = self.embedder.get_embedding(query)
565
587
  if query_embedding is None:
566
588
  log_error(f"Error getting embedding for Query: {query}")
@@ -671,3 +693,7 @@ class SurrealDb(VectorDb):
671
693
  except Exception as e:
672
694
  log_error(f"Error updating metadata for content_id '{content_id}': {e}")
673
695
  raise
696
+
697
+ def get_supported_search_types(self) -> List[str]:
698
+ """Get the supported search types for this vector database."""
699
+ return [] # SurrealDb doesn't use SearchType enum
@@ -1,5 +1,5 @@
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
  try:
5
5
  from upstash_vector import Index, Vector
@@ -9,10 +9,11 @@ except ImportError:
9
9
  "The `upstash-vector` package is not installed, please install using `pip install upstash-vector`"
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
15
- from agno.utils.log import log_info, logger
16
+ from agno.utils.log import log_info, log_warning, logger
16
17
  from agno.vectordb.base import VectorDb
17
18
 
18
19
  DEFAULT_NAMESPACE = ""
@@ -32,6 +33,8 @@ class UpstashVectorDb(VectorDb):
32
33
  embedder (Optional[Embedder], optional): The embedder to use. If None, uses Upstash hosted embedding models.
33
34
  namespace (Optional[str], optional): The namespace to use. Defaults to DEFAULT_NAMESPACE.
34
35
  reranker (Optional[Reranker], optional): The reranker to use. Defaults to None.
36
+ name (Optional[str], optional): The name of the vector database. Defaults to None.
37
+ description (Optional[str], optional): The description of the vector database. Defaults to None.
35
38
  **kwargs: Additional keyword arguments.
36
39
  """
37
40
 
@@ -45,8 +48,28 @@ class UpstashVectorDb(VectorDb):
45
48
  embedder: Optional[Embedder] = None,
46
49
  namespace: Optional[str] = DEFAULT_NAMESPACE,
47
50
  reranker: Optional[Reranker] = None,
51
+ name: Optional[str] = None,
52
+ description: Optional[str] = None,
53
+ id: Optional[str] = None,
48
54
  **kwargs: Any,
49
55
  ) -> None:
56
+ # Validate required parameters
57
+ if not url:
58
+ raise ValueError("URL must be provided.")
59
+ if not token:
60
+ raise ValueError("Token must be provided.")
61
+
62
+ # Dynamic ID generation based on unique identifiers
63
+ if id is None:
64
+ from agno.utils.string import generate_id
65
+
66
+ namespace_identifier = namespace or DEFAULT_NAMESPACE
67
+ seed = f"{url}#{namespace_identifier}"
68
+ id = generate_id(seed)
69
+
70
+ # Initialize base class with name, description, and generated ID
71
+ super().__init__(id=id, name=name, description=description)
72
+
50
73
  self._index: Optional[Index] = None
51
74
  self.url: str = url
52
75
  self.token: str = token
@@ -56,7 +79,6 @@ class UpstashVectorDb(VectorDb):
56
79
  self.namespace: str = namespace if namespace is not None else DEFAULT_NAMESPACE
57
80
  self.kwargs: Dict[str, Any] = kwargs
58
81
  self.use_upstash_embeddings: bool = embedder is None
59
-
60
82
  if embedder is None:
61
83
  logger.warning(
62
84
  "You have not provided an embedder, using Upstash hosted embedding models. "
@@ -303,7 +325,7 @@ class UpstashVectorDb(VectorDb):
303
325
  self,
304
326
  query: str,
305
327
  limit: int = 5,
306
- filters: Optional[Dict[str, Any]] = None,
328
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
307
329
  namespace: Optional[str] = None,
308
330
  ) -> List[Document]:
309
331
  """Search for documents in the index.
@@ -316,7 +338,9 @@ class UpstashVectorDb(VectorDb):
316
338
  List[Document]: List of matching documents.
317
339
  """
318
340
  _namespace = self.namespace if namespace is None else namespace
319
-
341
+ if isinstance(filters, List):
342
+ log_warning("Filters Expressions are not supported in UpstashDB. No filters will be applied.")
343
+ filters = None
320
344
  filter_str = "" if filters is None else str(filters)
321
345
 
322
346
  if not self.use_upstash_embeddings and self.embedder is not None:
@@ -504,8 +528,48 @@ class UpstashVectorDb(VectorDb):
504
528
  _namespace = self.namespace if namespace is None else namespace
505
529
  vectors = []
506
530
 
507
- embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
508
- await asyncio.gather(*embed_tasks, return_exceptions=True)
531
+ if (
532
+ self.embedder
533
+ and self.embedder.enable_batch
534
+ and hasattr(self.embedder, "async_get_embeddings_batch_and_usage")
535
+ ):
536
+ # Use batch embedding when enabled and supported
537
+ try:
538
+ # Extract content from all documents
539
+ doc_contents = [doc.content for doc in documents]
540
+
541
+ # Get batch embeddings and usage
542
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
543
+
544
+ # Process documents with pre-computed embeddings
545
+ for j, doc in enumerate(documents):
546
+ try:
547
+ if j < len(embeddings):
548
+ doc.embedding = embeddings[j]
549
+ doc.usage = usages[j] if j < len(usages) else None
550
+ except Exception as e:
551
+ logger.error(f"Error assigning batch embedding to document '{doc.name}': {e}")
552
+
553
+ except Exception as e:
554
+ # Check if this is a rate limit error - don't fall back as it would make things worse
555
+ error_str = str(e).lower()
556
+ is_rate_limit = any(
557
+ phrase in error_str
558
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
559
+ )
560
+
561
+ if is_rate_limit:
562
+ logger.error(f"Rate limit detected during batch embedding. {e}")
563
+ raise e
564
+ else:
565
+ logger.warning(f"Async batch embedding failed, falling back to individual embeddings: {e}")
566
+ # Fall back to individual embedding
567
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
568
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
569
+ else:
570
+ # Use individual embedding
571
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
572
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
509
573
 
510
574
  for i, document in enumerate(documents):
511
575
  if document.id is None:
@@ -562,7 +626,7 @@ class UpstashVectorDb(VectorDb):
562
626
  self.index.upsert(vectors, namespace=_namespace)
563
627
 
564
628
  async def async_search(
565
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
629
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
566
630
  ) -> List[Document]:
567
631
  raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
568
632
 
@@ -648,3 +712,7 @@ class UpstashVectorDb(VectorDb):
648
712
  except Exception as e:
649
713
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
650
714
  raise
715
+
716
+ def get_supported_search_types(self) -> List[str]:
717
+ """Get the supported search types for this vector database."""
718
+ return [] # UpstashVectorDb doesn't use SearchType enum