agno 2.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. agno/agent/agent.py +5540 -2273
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/compression/__init__.py +3 -0
  5. agno/compression/manager.py +247 -0
  6. agno/culture/__init__.py +3 -0
  7. agno/culture/manager.py +956 -0
  8. agno/db/async_postgres/__init__.py +3 -0
  9. agno/db/base.py +689 -6
  10. agno/db/dynamo/dynamo.py +933 -37
  11. agno/db/dynamo/schemas.py +174 -10
  12. agno/db/dynamo/utils.py +63 -4
  13. agno/db/firestore/firestore.py +831 -9
  14. agno/db/firestore/schemas.py +51 -0
  15. agno/db/firestore/utils.py +102 -4
  16. agno/db/gcs_json/gcs_json_db.py +660 -12
  17. agno/db/gcs_json/utils.py +60 -26
  18. agno/db/in_memory/in_memory_db.py +287 -14
  19. agno/db/in_memory/utils.py +60 -2
  20. agno/db/json/json_db.py +590 -14
  21. agno/db/json/utils.py +60 -26
  22. agno/db/migrations/manager.py +199 -0
  23. agno/db/migrations/v1_to_v2.py +43 -13
  24. agno/db/migrations/versions/__init__.py +0 -0
  25. agno/db/migrations/versions/v2_3_0.py +938 -0
  26. agno/db/mongo/__init__.py +15 -1
  27. agno/db/mongo/async_mongo.py +2760 -0
  28. agno/db/mongo/mongo.py +879 -11
  29. agno/db/mongo/schemas.py +42 -0
  30. agno/db/mongo/utils.py +80 -8
  31. agno/db/mysql/__init__.py +2 -1
  32. agno/db/mysql/async_mysql.py +2912 -0
  33. agno/db/mysql/mysql.py +946 -68
  34. agno/db/mysql/schemas.py +72 -10
  35. agno/db/mysql/utils.py +198 -7
  36. agno/db/postgres/__init__.py +2 -1
  37. agno/db/postgres/async_postgres.py +2579 -0
  38. agno/db/postgres/postgres.py +942 -57
  39. agno/db/postgres/schemas.py +81 -18
  40. agno/db/postgres/utils.py +164 -2
  41. agno/db/redis/redis.py +671 -7
  42. agno/db/redis/schemas.py +50 -0
  43. agno/db/redis/utils.py +65 -7
  44. agno/db/schemas/__init__.py +2 -1
  45. agno/db/schemas/culture.py +120 -0
  46. agno/db/schemas/evals.py +1 -0
  47. agno/db/schemas/memory.py +17 -2
  48. agno/db/singlestore/schemas.py +63 -0
  49. agno/db/singlestore/singlestore.py +949 -83
  50. agno/db/singlestore/utils.py +60 -2
  51. agno/db/sqlite/__init__.py +2 -1
  52. agno/db/sqlite/async_sqlite.py +2911 -0
  53. agno/db/sqlite/schemas.py +62 -0
  54. agno/db/sqlite/sqlite.py +965 -46
  55. agno/db/sqlite/utils.py +169 -8
  56. agno/db/surrealdb/__init__.py +3 -0
  57. agno/db/surrealdb/metrics.py +292 -0
  58. agno/db/surrealdb/models.py +334 -0
  59. agno/db/surrealdb/queries.py +71 -0
  60. agno/db/surrealdb/surrealdb.py +1908 -0
  61. agno/db/surrealdb/utils.py +147 -0
  62. agno/db/utils.py +2 -0
  63. agno/eval/__init__.py +10 -0
  64. agno/eval/accuracy.py +75 -55
  65. agno/eval/agent_as_judge.py +861 -0
  66. agno/eval/base.py +29 -0
  67. agno/eval/performance.py +16 -7
  68. agno/eval/reliability.py +28 -16
  69. agno/eval/utils.py +35 -17
  70. agno/exceptions.py +27 -2
  71. agno/filters.py +354 -0
  72. agno/guardrails/prompt_injection.py +1 -0
  73. agno/hooks/__init__.py +3 -0
  74. agno/hooks/decorator.py +164 -0
  75. agno/integrations/discord/client.py +1 -1
  76. agno/knowledge/chunking/agentic.py +13 -10
  77. agno/knowledge/chunking/fixed.py +4 -1
  78. agno/knowledge/chunking/semantic.py +9 -4
  79. agno/knowledge/chunking/strategy.py +59 -15
  80. agno/knowledge/embedder/fastembed.py +1 -1
  81. agno/knowledge/embedder/nebius.py +1 -1
  82. agno/knowledge/embedder/ollama.py +8 -0
  83. agno/knowledge/embedder/openai.py +8 -8
  84. agno/knowledge/embedder/sentence_transformer.py +6 -2
  85. agno/knowledge/embedder/vllm.py +262 -0
  86. agno/knowledge/knowledge.py +1618 -318
  87. agno/knowledge/reader/base.py +6 -2
  88. agno/knowledge/reader/csv_reader.py +8 -10
  89. agno/knowledge/reader/docx_reader.py +5 -6
  90. agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
  91. agno/knowledge/reader/json_reader.py +5 -4
  92. agno/knowledge/reader/markdown_reader.py +8 -8
  93. agno/knowledge/reader/pdf_reader.py +17 -19
  94. agno/knowledge/reader/pptx_reader.py +101 -0
  95. agno/knowledge/reader/reader_factory.py +32 -3
  96. agno/knowledge/reader/s3_reader.py +3 -3
  97. agno/knowledge/reader/tavily_reader.py +193 -0
  98. agno/knowledge/reader/text_reader.py +22 -10
  99. agno/knowledge/reader/web_search_reader.py +1 -48
  100. agno/knowledge/reader/website_reader.py +10 -10
  101. agno/knowledge/reader/wikipedia_reader.py +33 -1
  102. agno/knowledge/types.py +1 -0
  103. agno/knowledge/utils.py +72 -7
  104. agno/media.py +22 -6
  105. agno/memory/__init__.py +14 -1
  106. agno/memory/manager.py +544 -83
  107. agno/memory/strategies/__init__.py +15 -0
  108. agno/memory/strategies/base.py +66 -0
  109. agno/memory/strategies/summarize.py +196 -0
  110. agno/memory/strategies/types.py +37 -0
  111. agno/models/aimlapi/aimlapi.py +17 -0
  112. agno/models/anthropic/claude.py +515 -40
  113. agno/models/aws/bedrock.py +102 -21
  114. agno/models/aws/claude.py +131 -274
  115. agno/models/azure/ai_foundry.py +41 -19
  116. agno/models/azure/openai_chat.py +39 -8
  117. agno/models/base.py +1249 -525
  118. agno/models/cerebras/cerebras.py +91 -21
  119. agno/models/cerebras/cerebras_openai.py +21 -2
  120. agno/models/cohere/chat.py +40 -6
  121. agno/models/cometapi/cometapi.py +18 -1
  122. agno/models/dashscope/dashscope.py +2 -3
  123. agno/models/deepinfra/deepinfra.py +18 -1
  124. agno/models/deepseek/deepseek.py +69 -3
  125. agno/models/fireworks/fireworks.py +18 -1
  126. agno/models/google/gemini.py +877 -80
  127. agno/models/google/utils.py +22 -0
  128. agno/models/groq/groq.py +51 -18
  129. agno/models/huggingface/huggingface.py +17 -6
  130. agno/models/ibm/watsonx.py +16 -6
  131. agno/models/internlm/internlm.py +18 -1
  132. agno/models/langdb/langdb.py +13 -1
  133. agno/models/litellm/chat.py +44 -9
  134. agno/models/litellm/litellm_openai.py +18 -1
  135. agno/models/message.py +28 -5
  136. agno/models/meta/llama.py +47 -14
  137. agno/models/meta/llama_openai.py +22 -17
  138. agno/models/mistral/mistral.py +8 -4
  139. agno/models/nebius/nebius.py +6 -7
  140. agno/models/nvidia/nvidia.py +20 -3
  141. agno/models/ollama/chat.py +24 -8
  142. agno/models/openai/chat.py +104 -29
  143. agno/models/openai/responses.py +101 -81
  144. agno/models/openrouter/openrouter.py +60 -3
  145. agno/models/perplexity/perplexity.py +17 -1
  146. agno/models/portkey/portkey.py +7 -6
  147. agno/models/requesty/requesty.py +24 -4
  148. agno/models/response.py +73 -2
  149. agno/models/sambanova/sambanova.py +20 -3
  150. agno/models/siliconflow/siliconflow.py +19 -2
  151. agno/models/together/together.py +20 -3
  152. agno/models/utils.py +254 -8
  153. agno/models/vercel/v0.py +20 -3
  154. agno/models/vertexai/__init__.py +0 -0
  155. agno/models/vertexai/claude.py +190 -0
  156. agno/models/vllm/vllm.py +19 -14
  157. agno/models/xai/xai.py +19 -2
  158. agno/os/app.py +549 -152
  159. agno/os/auth.py +190 -3
  160. agno/os/config.py +23 -0
  161. agno/os/interfaces/a2a/router.py +8 -11
  162. agno/os/interfaces/a2a/utils.py +1 -1
  163. agno/os/interfaces/agui/router.py +18 -3
  164. agno/os/interfaces/agui/utils.py +152 -39
  165. agno/os/interfaces/slack/router.py +55 -37
  166. agno/os/interfaces/slack/slack.py +9 -1
  167. agno/os/interfaces/whatsapp/router.py +0 -1
  168. agno/os/interfaces/whatsapp/security.py +3 -1
  169. agno/os/mcp.py +110 -52
  170. agno/os/middleware/__init__.py +2 -0
  171. agno/os/middleware/jwt.py +676 -112
  172. agno/os/router.py +40 -1478
  173. agno/os/routers/agents/__init__.py +3 -0
  174. agno/os/routers/agents/router.py +599 -0
  175. agno/os/routers/agents/schema.py +261 -0
  176. agno/os/routers/evals/evals.py +96 -39
  177. agno/os/routers/evals/schemas.py +65 -33
  178. agno/os/routers/evals/utils.py +80 -10
  179. agno/os/routers/health.py +10 -4
  180. agno/os/routers/knowledge/knowledge.py +196 -38
  181. agno/os/routers/knowledge/schemas.py +82 -22
  182. agno/os/routers/memory/memory.py +279 -52
  183. agno/os/routers/memory/schemas.py +46 -17
  184. agno/os/routers/metrics/metrics.py +20 -8
  185. agno/os/routers/metrics/schemas.py +16 -16
  186. agno/os/routers/session/session.py +462 -34
  187. agno/os/routers/teams/__init__.py +3 -0
  188. agno/os/routers/teams/router.py +512 -0
  189. agno/os/routers/teams/schema.py +257 -0
  190. agno/os/routers/traces/__init__.py +3 -0
  191. agno/os/routers/traces/schemas.py +414 -0
  192. agno/os/routers/traces/traces.py +499 -0
  193. agno/os/routers/workflows/__init__.py +3 -0
  194. agno/os/routers/workflows/router.py +624 -0
  195. agno/os/routers/workflows/schema.py +75 -0
  196. agno/os/schema.py +256 -693
  197. agno/os/scopes.py +469 -0
  198. agno/os/utils.py +514 -36
  199. agno/reasoning/anthropic.py +80 -0
  200. agno/reasoning/gemini.py +73 -0
  201. agno/reasoning/openai.py +5 -0
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +155 -32
  205. agno/run/base.py +55 -3
  206. agno/run/requirement.py +181 -0
  207. agno/run/team.py +125 -38
  208. agno/run/workflow.py +72 -18
  209. agno/session/agent.py +102 -89
  210. agno/session/summary.py +56 -15
  211. agno/session/team.py +164 -90
  212. agno/session/workflow.py +405 -40
  213. agno/table.py +10 -0
  214. agno/team/team.py +3974 -1903
  215. agno/tools/dalle.py +2 -4
  216. agno/tools/eleven_labs.py +23 -25
  217. agno/tools/exa.py +21 -16
  218. agno/tools/file.py +153 -23
  219. agno/tools/file_generation.py +16 -10
  220. agno/tools/firecrawl.py +15 -7
  221. agno/tools/function.py +193 -38
  222. agno/tools/gmail.py +238 -14
  223. agno/tools/google_drive.py +271 -0
  224. agno/tools/googlecalendar.py +36 -8
  225. agno/tools/googlesheets.py +20 -5
  226. agno/tools/jira.py +20 -0
  227. agno/tools/mcp/__init__.py +10 -0
  228. agno/tools/mcp/mcp.py +331 -0
  229. agno/tools/mcp/multi_mcp.py +347 -0
  230. agno/tools/mcp/params.py +24 -0
  231. agno/tools/mcp_toolbox.py +3 -3
  232. agno/tools/models/nebius.py +5 -5
  233. agno/tools/models_labs.py +20 -10
  234. agno/tools/nano_banana.py +151 -0
  235. agno/tools/notion.py +204 -0
  236. agno/tools/parallel.py +314 -0
  237. agno/tools/postgres.py +76 -36
  238. agno/tools/redshift.py +406 -0
  239. agno/tools/scrapegraph.py +1 -1
  240. agno/tools/shopify.py +1519 -0
  241. agno/tools/slack.py +18 -3
  242. agno/tools/spotify.py +919 -0
  243. agno/tools/tavily.py +146 -0
  244. agno/tools/toolkit.py +25 -0
  245. agno/tools/workflow.py +8 -1
  246. agno/tools/yfinance.py +12 -11
  247. agno/tracing/__init__.py +12 -0
  248. agno/tracing/exporter.py +157 -0
  249. agno/tracing/schemas.py +276 -0
  250. agno/tracing/setup.py +111 -0
  251. agno/utils/agent.py +938 -0
  252. agno/utils/cryptography.py +22 -0
  253. agno/utils/dttm.py +33 -0
  254. agno/utils/events.py +151 -3
  255. agno/utils/gemini.py +15 -5
  256. agno/utils/hooks.py +118 -4
  257. agno/utils/http.py +113 -2
  258. agno/utils/knowledge.py +12 -5
  259. agno/utils/log.py +1 -0
  260. agno/utils/mcp.py +92 -2
  261. agno/utils/media.py +187 -1
  262. agno/utils/merge_dict.py +3 -3
  263. agno/utils/message.py +60 -0
  264. agno/utils/models/ai_foundry.py +9 -2
  265. agno/utils/models/claude.py +49 -14
  266. agno/utils/models/cohere.py +9 -2
  267. agno/utils/models/llama.py +9 -2
  268. agno/utils/models/mistral.py +4 -2
  269. agno/utils/print_response/agent.py +109 -16
  270. agno/utils/print_response/team.py +223 -30
  271. agno/utils/print_response/workflow.py +251 -34
  272. agno/utils/streamlit.py +1 -1
  273. agno/utils/team.py +98 -9
  274. agno/utils/tokens.py +657 -0
  275. agno/vectordb/base.py +39 -7
  276. agno/vectordb/cassandra/cassandra.py +21 -5
  277. agno/vectordb/chroma/chromadb.py +43 -12
  278. agno/vectordb/clickhouse/clickhousedb.py +21 -5
  279. agno/vectordb/couchbase/couchbase.py +29 -5
  280. agno/vectordb/lancedb/lance_db.py +92 -181
  281. agno/vectordb/langchaindb/langchaindb.py +24 -4
  282. agno/vectordb/lightrag/lightrag.py +17 -3
  283. agno/vectordb/llamaindex/llamaindexdb.py +25 -5
  284. agno/vectordb/milvus/milvus.py +50 -37
  285. agno/vectordb/mongodb/__init__.py +7 -1
  286. agno/vectordb/mongodb/mongodb.py +36 -30
  287. agno/vectordb/pgvector/pgvector.py +201 -77
  288. agno/vectordb/pineconedb/pineconedb.py +41 -23
  289. agno/vectordb/qdrant/qdrant.py +67 -54
  290. agno/vectordb/redis/__init__.py +9 -0
  291. agno/vectordb/redis/redisdb.py +682 -0
  292. agno/vectordb/singlestore/singlestore.py +50 -29
  293. agno/vectordb/surrealdb/surrealdb.py +31 -41
  294. agno/vectordb/upstashdb/upstashdb.py +34 -6
  295. agno/vectordb/weaviate/weaviate.py +53 -14
  296. agno/workflow/__init__.py +2 -0
  297. agno/workflow/agent.py +299 -0
  298. agno/workflow/condition.py +120 -18
  299. agno/workflow/loop.py +77 -10
  300. agno/workflow/parallel.py +231 -143
  301. agno/workflow/router.py +118 -17
  302. agno/workflow/step.py +609 -170
  303. agno/workflow/steps.py +73 -6
  304. agno/workflow/types.py +96 -21
  305. agno/workflow/workflow.py +2039 -262
  306. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
  307. agno-2.3.13.dist-info/RECORD +613 -0
  308. agno/tools/googlesearch.py +0 -98
  309. agno/tools/mcp.py +0 -679
  310. agno/tools/memori.py +0 -339
  311. agno-2.1.2.dist-info/RECORD +0 -543
  312. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
  313. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
  314. {agno-2.1.2.dist-info → agno-2.3.13.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
 
@@ -180,8 +185,10 @@ class SingleStore(VectorDb):
180
185
  for document in documents:
181
186
  document.embed(embedder=self.embedder)
182
187
  cleaned_content = document.content.replace("\x00", "\ufffd")
183
- record_id = md5(cleaned_content.encode()).hexdigest()
184
- _id = document.id or record_id
188
+ # Include content_hash in ID to ensure uniqueness across different content hashes
189
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
190
+ record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
191
+ _id = record_id
185
192
 
186
193
  meta_data_json = json.dumps(document.meta_data)
187
194
  usage_json = json.dumps(document.usage)
@@ -241,8 +248,10 @@ class SingleStore(VectorDb):
241
248
  for document in documents:
242
249
  document.embed(embedder=self.embedder)
243
250
  cleaned_content = document.content.replace("\x00", "\ufffd")
244
- record_id = md5(cleaned_content.encode()).hexdigest()
245
- _id = document.id or record_id
251
+ # Include content_hash in ID to ensure uniqueness across different content hashes
252
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
253
+ record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
254
+ _id = record_id
246
255
 
247
256
  meta_data_json = json.dumps(document.meta_data)
248
257
  usage_json = json.dumps(document.usage)
@@ -278,7 +287,9 @@ class SingleStore(VectorDb):
278
287
  sess.commit()
279
288
  log_debug(f"Committed {counter} documents")
280
289
 
281
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
290
+ def search(
291
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
292
+ ) -> List[Document]:
282
293
  """
283
294
  Search for documents based on a query and optional filters.
284
295
 
@@ -290,6 +301,8 @@ class SingleStore(VectorDb):
290
301
  Returns:
291
302
  List[Document]: List of documents that match the query.
292
303
  """
304
+ if filters is not None:
305
+ log_warning("Filters are not supported in SingleStore. No filters will be applied.")
293
306
  query_embedding = self.embedder.get_embedding(query)
294
307
  if query_embedding is None:
295
308
  log_error(f"Error getting embedding for Query: {query}")
@@ -428,9 +441,9 @@ class SingleStore(VectorDb):
428
441
  try:
429
442
  with self.Session.begin() as sess:
430
443
  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
444
+ result = sess.execute(stmt) # type: ignore
445
+ log_info(f"Deleted {result.rowcount} records with ID {id} from table '{self.table.name}'.") # type: ignore
446
+ return result.rowcount > 0 # type: ignore
434
447
  except Exception as e:
435
448
  log_error(f"Error deleting document with ID {id}: {e}")
436
449
  return False
@@ -444,11 +457,11 @@ class SingleStore(VectorDb):
444
457
  try:
445
458
  with self.Session.begin() as sess:
446
459
  stmt = delete(self.table).where(self.table.c.content_id == content_id)
447
- result = sess.execute(stmt)
460
+ result = sess.execute(stmt) # type: ignore
448
461
  log_info(
449
- f"Deleted {result.rowcount} records with content_id {content_id} from table '{self.table.name}'."
462
+ f"Deleted {result.rowcount} records with content_id {content_id} from table '{self.table.name}'." # type: ignore
450
463
  )
451
- return result.rowcount > 0
464
+ return result.rowcount > 0 # type: ignore
452
465
  except Exception as e:
453
466
  log_error(f"Error deleting document with content_id {content_id}: {e}")
454
467
  return False
@@ -462,9 +475,9 @@ class SingleStore(VectorDb):
462
475
  try:
463
476
  with self.Session.begin() as sess:
464
477
  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
478
+ result = sess.execute(stmt) # type: ignore
479
+ log_info(f"Deleted {result.rowcount} records with name '{name}' from table '{self.table.name}'.") # type: ignore
480
+ return result.rowcount > 0 # type: ignore
468
481
  except Exception as e:
469
482
  log_error(f"Error deleting document with name {name}: {e}")
470
483
  return False
@@ -480,9 +493,9 @@ class SingleStore(VectorDb):
480
493
  # Convert metadata to JSON string for comparison
481
494
  metadata_json = json.dumps(metadata, sort_keys=True)
482
495
  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
496
+ result = sess.execute(stmt) # type: ignore
497
+ log_info(f"Deleted {result.rowcount} records with metadata {metadata} from table '{self.table.name}'.") # type: ignore
498
+ return result.rowcount > 0 # type: ignore
486
499
  except Exception as e:
487
500
  log_error(f"Error deleting documents with metadata {metadata}: {e}")
488
501
  return False
@@ -539,8 +552,10 @@ class SingleStore(VectorDb):
539
552
  counter = 0
540
553
  for document in documents:
541
554
  cleaned_content = document.content.replace("\x00", "\ufffd")
542
- record_id = md5(cleaned_content.encode()).hexdigest()
543
- _id = document.id or record_id
555
+ # Include content_hash in ID to ensure uniqueness across different content hashes
556
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
557
+ record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
558
+ _id = record_id
544
559
 
545
560
  meta_data_json = json.dumps(document.meta_data)
546
561
  usage_json = json.dumps(document.usage)
@@ -623,8 +638,10 @@ class SingleStore(VectorDb):
623
638
  counter = 0
624
639
  for document in documents:
625
640
  cleaned_content = document.content.replace("\x00", "\ufffd")
626
- record_id = md5(cleaned_content.encode()).hexdigest()
627
- _id = document.id or record_id
641
+ # Include content_hash in ID to ensure uniqueness across different content hashes
642
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
643
+ record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
644
+ _id = record_id
628
645
 
629
646
  meta_data_json = json.dumps(document.meta_data)
630
647
  usage_json = json.dumps(document.usage)
@@ -661,7 +678,7 @@ class SingleStore(VectorDb):
661
678
  log_debug(f"Committed {counter} documents")
662
679
 
663
680
  async def async_search(
664
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
681
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
665
682
  ) -> List[Document]:
666
683
  return self.search(query=query, limit=limit, filters=filters)
667
684
 
@@ -689,11 +706,11 @@ class SingleStore(VectorDb):
689
706
  try:
690
707
  with self.Session.begin() as sess:
691
708
  stmt = delete(self.table).where(self.table.c.content_hash == content_hash)
692
- result = sess.execute(stmt)
709
+ result = sess.execute(stmt) # type: ignore
693
710
  log_info(
694
- f"Deleted {result.rowcount} records with content_hash '{content_hash}' from table '{self.table.name}'."
711
+ f"Deleted {result.rowcount} records with content_hash '{content_hash}' from table '{self.table.name}'." # type: ignore
695
712
  )
696
- return result.rowcount > 0
713
+ return result.rowcount > 0 # type: ignore
697
714
  except Exception as e:
698
715
  log_error(f"Error deleting documents with content_hash {content_hash}: {e}")
699
716
  return False
@@ -712,7 +729,7 @@ class SingleStore(VectorDb):
712
729
  with self.Session.begin() as sess:
713
730
  # Find documents with the given content_id
714
731
  stmt = select(self.table).where(self.table.c.content_id == content_id)
715
- result = sess.execute(stmt)
732
+ result = sess.execute(stmt) # type: ignore
716
733
 
717
734
  updated_count = 0
718
735
  for row in result:
@@ -748,3 +765,7 @@ class SingleStore(VectorDb):
748
765
  except Exception as e:
749
766
  log_error(f"Error updating metadata for content_id '{content_id}': {e}")
750
767
  raise
768
+
769
+ def get_supported_search_types(self) -> List[str]:
770
+ """Get the supported search types for this vector database."""
771
+ 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
 
@@ -30,12 +31,6 @@ class SurrealDb(VectorDb):
30
31
  DEFINE INDEX IF NOT EXISTS vector_idx ON {collection} FIELDS embedding HNSW DIMENSION {dimensions} DIST {distance};
31
32
  """
32
33
 
33
- DOC_EXISTS_QUERY: Final[str] = """
34
- SELECT * FROM {collection}
35
- WHERE content = $content
36
- LIMIT 1
37
- """
38
-
39
34
  NAME_EXISTS_QUERY: Final[str] = """
40
35
  SELECT * FROM {collection}
41
36
  WHERE meta_data.name = $name
@@ -107,11 +102,13 @@ class SurrealDb(VectorDb):
107
102
  m: int = 12,
108
103
  search_ef: int = 40,
109
104
  embedder: Optional[Embedder] = None,
105
+ name: Optional[str] = None,
106
+ description: Optional[str] = None,
107
+ id: Optional[str] = None,
110
108
  ):
111
109
  """Initialize SurrealDB connection.
112
110
 
113
111
  Args:
114
- url: SurrealDB server URL (e.g. ws://localhost:8000/rpc)
115
112
  client: A blocking connection, either HTTP or WS
116
113
  async_client: An async connection, either HTTP or WS (default: None)
117
114
  collection: Collection name to store documents (default: documents)
@@ -122,6 +119,17 @@ class SurrealDb(VectorDb):
122
119
  embedder: Embedder instance for creating embeddings (default: OpenAIEmbedder)
123
120
 
124
121
  """
122
+ # Dynamic ID generation based on unique identifiers
123
+ if id is None:
124
+ from agno.utils.string import generate_id
125
+
126
+ client_info = str(client) if client else str(async_client) if async_client else "default"
127
+ seed = f"{client_info}#{collection}"
128
+ id = generate_id(seed)
129
+
130
+ # Initialize base class with name, description, and generated ID
131
+ super().__init__(id=id, name=name, description=description)
132
+
125
133
  # Embedder for embedding the document contents
126
134
  if embedder is None:
127
135
  from agno.knowledge.embedder.openai import OpenAIEmbedder
@@ -131,7 +139,6 @@ class SurrealDb(VectorDb):
131
139
  self.embedder: Embedder = embedder
132
140
  self.dimensions = self.embedder.dimensions
133
141
  self.collection = collection
134
-
135
142
  # Convert Distance enum to SurrealDB distance type
136
143
  self.distance = {Distance.cosine: "COSINE", Distance.l2: "EUCLIDEAN", Distance.max_inner_product: "DOT"}[
137
144
  distance
@@ -208,23 +215,6 @@ class SurrealDb(VectorDb):
208
215
  )
209
216
  self.client.query(query)
210
217
 
211
- def doc_exists(self, document: Document) -> bool:
212
- """Check if a document exists by its content.
213
-
214
- Args:
215
- document: The document to check.
216
-
217
- Returns:
218
- True if the document exists, False otherwise.
219
-
220
- """
221
- log_debug(f"Checking if document exists: {document.content}")
222
- result = self.client.query(
223
- self.DOC_EXISTS_QUERY.format(collection=self.collection),
224
- {"content": document.content},
225
- )
226
- return bool(self._extract_result(result))
227
-
228
218
  def name_exists(self, name: str) -> bool:
229
219
  """Check if a document exists by its name.
230
220
 
@@ -306,7 +296,9 @@ class SurrealDb(VectorDb):
306
296
  thing = f"{self.collection}:{doc.id}" if doc.id else self.collection
307
297
  self.client.query(self.UPSERT_QUERY.format(thing=thing), data)
308
298
 
309
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
299
+ def search(
300
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
301
+ ) -> List[Document]:
310
302
  """Search for similar documents.
311
303
 
312
304
  Args:
@@ -318,6 +310,9 @@ class SurrealDb(VectorDb):
318
310
  A list of documents that are similar to the query.
319
311
 
320
312
  """
313
+ if isinstance(filters, List):
314
+ log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
315
+ filters = None
321
316
  query_embedding = self.embedder.get_embedding(query)
322
317
  if query_embedding is None:
323
318
  log_error(f"Error getting embedding for Query: {query}")
@@ -475,19 +470,6 @@ class SurrealDb(VectorDb):
475
470
  ),
476
471
  )
477
472
 
478
- async def async_doc_exists(self, document: Document) -> bool:
479
- """Check if a document exists by its content asynchronously.
480
-
481
- Returns:
482
- True if the document exists, False otherwise.
483
-
484
- """
485
- response = await self.async_client.query(
486
- self.DOC_EXISTS_QUERY.format(collection=self.collection),
487
- {"content": document.content},
488
- )
489
- return bool(self._extract_result(response))
490
-
491
473
  async def async_name_exists(self, name: str) -> bool:
492
474
  """Check if a document exists by its name asynchronously.
493
475
 
@@ -548,7 +530,7 @@ class SurrealDb(VectorDb):
548
530
  self,
549
531
  query: str,
550
532
  limit: int = 5,
551
- filters: Optional[Dict[str, Any]] = None,
533
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
552
534
  ) -> List[Document]:
553
535
  """Search for similar documents asynchronously.
554
536
 
@@ -561,6 +543,10 @@ class SurrealDb(VectorDb):
561
543
  A list of documents that are similar to the query.
562
544
 
563
545
  """
546
+ if isinstance(filters, List):
547
+ log_warning("Filters Expressions are not supported in SurrealDB. No filters will be applied.")
548
+ filters = None
549
+
564
550
  query_embedding = self.embedder.get_embedding(query)
565
551
  if query_embedding is None:
566
552
  log_error(f"Error getting embedding for Query: {query}")
@@ -671,3 +657,7 @@ class SurrealDb(VectorDb):
671
657
  except Exception as e:
672
658
  log_error(f"Error updating metadata for content_id '{content_id}': {e}")
673
659
  raise
660
+
661
+ def get_supported_search_types(self) -> List[str]:
662
+ """Get the supported search types for this vector database."""
663
+ 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:
@@ -602,7 +626,7 @@ class UpstashVectorDb(VectorDb):
602
626
  self.index.upsert(vectors, namespace=_namespace)
603
627
 
604
628
  async def async_search(
605
- 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
606
630
  ) -> List[Document]:
607
631
  raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
608
632
 
@@ -688,3 +712,7 @@ class UpstashVectorDb(VectorDb):
688
712
  except Exception as e:
689
713
  logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
690
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
@@ -3,7 +3,7 @@ import json
3
3
  import uuid
4
4
  from hashlib import md5
5
5
  from os import getenv
6
- from typing import Any, Dict, List, Optional, Tuple
6
+ from typing import Any, Dict, List, Optional, Tuple, Union
7
7
 
8
8
  try:
9
9
  from warnings import filterwarnings
@@ -18,10 +18,11 @@ try:
18
18
  except ImportError:
19
19
  raise ImportError("Weaviate is not installed. Install using 'pip install weaviate-client'.")
20
20
 
21
+ from agno.filters import FilterExpr
21
22
  from agno.knowledge.document import Document
22
23
  from agno.knowledge.embedder import Embedder
23
24
  from agno.knowledge.reranker.base import Reranker
24
- from agno.utils.log import log_debug, log_info, logger
25
+ from agno.utils.log import log_debug, log_info, log_warning, logger
25
26
  from agno.vectordb.base import VectorDb
26
27
  from agno.vectordb.search import SearchType
27
28
  from agno.vectordb.weaviate.index import Distance, VectorIndex
@@ -41,6 +42,9 @@ class Weaviate(VectorDb):
41
42
  local: bool = False,
42
43
  # Collection params
43
44
  collection: str = "default",
45
+ name: Optional[str] = None,
46
+ description: Optional[str] = None,
47
+ id: Optional[str] = None,
44
48
  vector_index: VectorIndex = VectorIndex.HNSW,
45
49
  distance: Distance = Distance.COSINE,
46
50
  # Search/Embedding params
@@ -49,6 +53,17 @@ class Weaviate(VectorDb):
49
53
  reranker: Optional[Reranker] = None,
50
54
  hybrid_search_alpha: float = 0.5,
51
55
  ):
56
+ # Dynamic ID generation based on unique identifiers
57
+ if id is None:
58
+ from agno.utils.string import generate_id
59
+
60
+ connection_identifier = wcd_url or "local" if local else "default"
61
+ seed = f"{connection_identifier}#{collection}"
62
+ id = generate_id(seed)
63
+
64
+ # Initialize base class with name, description, and generated ID
65
+ super().__init__(id=id, name=name, description=description)
66
+
52
67
  # Connection setup
53
68
  self.wcd_url = wcd_url or getenv("WCD_URL")
54
69
  self.wcd_api_key = wcd_api_key or getenv("WCD_API_KEY")
@@ -232,7 +247,9 @@ class Weaviate(VectorDb):
232
247
  continue
233
248
 
234
249
  cleaned_content = document.content.replace("\x00", "\ufffd")
235
- record_id = md5(cleaned_content.encode()).hexdigest()
250
+ # Include content_hash in ID to ensure uniqueness across different content hashes
251
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
252
+ record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
236
253
  doc_uuid = uuid.UUID(hex=record_id[:32])
237
254
 
238
255
  # Merge filters with metadata
@@ -323,7 +340,9 @@ class Weaviate(VectorDb):
323
340
 
324
341
  # Clean content and generate UUID
325
342
  cleaned_content = document.content.replace("\x00", "\ufffd")
326
- record_id = md5(cleaned_content.encode()).hexdigest()
343
+ # Include content_hash in ID to ensure uniqueness across different content hashes
344
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
345
+ record_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
327
346
  doc_uuid = uuid.UUID(hex=record_id[:32])
328
347
 
329
348
  # Serialize meta_data to JSON string
@@ -378,7 +397,9 @@ class Weaviate(VectorDb):
378
397
  await self.async_insert(content_hash=content_hash, documents=documents, filters=filters)
379
398
  return
380
399
 
381
- def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
400
+ def search(
401
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
402
+ ) -> List[Document]:
382
403
  """
383
404
  Perform a search based on the configured search type.
384
405
 
@@ -390,6 +411,9 @@ class Weaviate(VectorDb):
390
411
  Returns:
391
412
  List[Document]: List of matching documents.
392
413
  """
414
+ if isinstance(filters, List):
415
+ log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
416
+ filters = None
393
417
  if self.search_type == SearchType.vector:
394
418
  return self.vector_search(query, limit, filters)
395
419
  elif self.search_type == SearchType.keyword:
@@ -401,7 +425,7 @@ class Weaviate(VectorDb):
401
425
  return []
402
426
 
403
427
  async def async_search(
404
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
428
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
405
429
  ) -> List[Document]:
406
430
  """
407
431
  Perform a search based on the configured search type asynchronously.
@@ -414,6 +438,9 @@ class Weaviate(VectorDb):
414
438
  Returns:
415
439
  List[Document]: List of matching documents.
416
440
  """
441
+ if isinstance(filters, List):
442
+ log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
443
+ filters = None
417
444
  if self.search_type == SearchType.vector:
418
445
  return await self.async_vector_search(query, limit, filters)
419
446
  elif self.search_type == SearchType.keyword:
@@ -424,7 +451,9 @@ class Weaviate(VectorDb):
424
451
  logger.error(f"Invalid search type '{self.search_type}'.")
425
452
  return []
426
453
 
427
- def vector_search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
454
+ def vector_search(
455
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
456
+ ) -> List[Document]:
428
457
  try:
429
458
  query_embedding = self.embedder.get_embedding(query)
430
459
  if query_embedding is None:
@@ -459,7 +488,7 @@ class Weaviate(VectorDb):
459
488
  self.get_client().close()
460
489
 
461
490
  async def async_vector_search(
462
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
491
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
463
492
  ) -> List[Document]:
464
493
  """
465
494
  Perform a vector search in Weaviate asynchronously.
@@ -504,7 +533,9 @@ class Weaviate(VectorDb):
504
533
  logger.error(f"Error searching for documents: {e}")
505
534
  return []
506
535
 
507
- def keyword_search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
536
+ def keyword_search(
537
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
538
+ ) -> List[Document]:
508
539
  try:
509
540
  collection = self.get_client().collections.get(self.collection)
510
541
  filter_expr = self._build_filter_expression(filters)
@@ -535,7 +566,7 @@ class Weaviate(VectorDb):
535
566
  self.get_client().close()
536
567
 
537
568
  async def async_keyword_search(
538
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
569
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
539
570
  ) -> List[Document]:
540
571
  """
541
572
  Perform a keyword search in Weaviate asynchronously.
@@ -576,7 +607,9 @@ class Weaviate(VectorDb):
576
607
  logger.error(f"Error searching for documents: {e}")
577
608
  return []
578
609
 
579
- def hybrid_search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
610
+ def hybrid_search(
611
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
612
+ ) -> List[Document]:
580
613
  try:
581
614
  query_embedding = self.embedder.get_embedding(query)
582
615
  if query_embedding is None:
@@ -614,7 +647,7 @@ class Weaviate(VectorDb):
614
647
  self.get_client().close()
615
648
 
616
649
  async def async_hybrid_search(
617
- self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None
650
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
618
651
  ) -> List[Document]:
619
652
  """
620
653
  Perform a hybrid search combining vector and keyword search in Weaviate asynchronously.
@@ -835,7 +868,7 @@ class Weaviate(VectorDb):
835
868
  """Indicate that upsert functionality is available."""
836
869
  return True
837
870
 
838
- def _build_filter_expression(self, filters: Optional[Dict[str, Any]]):
871
+ def _build_filter_expression(self, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]]):
839
872
  """
840
873
  Build a filter expression for Weaviate queries.
841
874
 
@@ -847,7 +880,9 @@ class Weaviate(VectorDb):
847
880
  """
848
881
  if not filters:
849
882
  return None
850
-
883
+ if isinstance(filters, List):
884
+ log_warning("Filters Expressions are not supported in Weaviate. No filters will be applied.")
885
+ return None
851
886
  try:
852
887
  # Create a filter for each key-value pair
853
888
  filter_conditions = []
@@ -968,3 +1003,7 @@ class Weaviate(VectorDb):
968
1003
  except Exception as e:
969
1004
  logger.error(f"Error deleting documents by content_hash '{content_hash}': {e}")
970
1005
  return False
1006
+
1007
+ def get_supported_search_types(self) -> List[str]:
1008
+ """Get the supported search types for this vector database."""
1009
+ return [SearchType.vector, SearchType.keyword, SearchType.hybrid]
agno/workflow/__init__.py CHANGED
@@ -1,3 +1,4 @@
1
+ from agno.workflow.agent import WorkflowAgent
1
2
  from agno.workflow.condition import Condition
2
3
  from agno.workflow.loop import Loop
3
4
  from agno.workflow.parallel import Parallel
@@ -9,6 +10,7 @@ from agno.workflow.workflow import Workflow
9
10
 
10
11
  __all__ = [
11
12
  "Workflow",
13
+ "WorkflowAgent",
12
14
  "Steps",
13
15
  "Step",
14
16
  "Loop",