agno 2.2.13__py3-none-any.whl → 2.4.3__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 (383) hide show
  1. agno/agent/__init__.py +6 -0
  2. agno/agent/agent.py +5252 -3145
  3. agno/agent/remote.py +525 -0
  4. agno/api/api.py +2 -0
  5. agno/client/__init__.py +3 -0
  6. agno/client/a2a/__init__.py +10 -0
  7. agno/client/a2a/client.py +554 -0
  8. agno/client/a2a/schemas.py +112 -0
  9. agno/client/a2a/utils.py +369 -0
  10. agno/client/os.py +2669 -0
  11. agno/compression/__init__.py +3 -0
  12. agno/compression/manager.py +247 -0
  13. agno/culture/manager.py +2 -2
  14. agno/db/base.py +927 -6
  15. agno/db/dynamo/dynamo.py +788 -2
  16. agno/db/dynamo/schemas.py +128 -0
  17. agno/db/dynamo/utils.py +26 -3
  18. agno/db/firestore/firestore.py +674 -50
  19. agno/db/firestore/schemas.py +41 -0
  20. agno/db/firestore/utils.py +25 -10
  21. agno/db/gcs_json/gcs_json_db.py +506 -3
  22. agno/db/gcs_json/utils.py +14 -2
  23. agno/db/in_memory/in_memory_db.py +203 -4
  24. agno/db/in_memory/utils.py +14 -2
  25. agno/db/json/json_db.py +498 -2
  26. agno/db/json/utils.py +14 -2
  27. agno/db/migrations/manager.py +199 -0
  28. agno/db/migrations/utils.py +19 -0
  29. agno/db/migrations/v1_to_v2.py +54 -16
  30. agno/db/migrations/versions/__init__.py +0 -0
  31. agno/db/migrations/versions/v2_3_0.py +977 -0
  32. agno/db/mongo/async_mongo.py +1013 -39
  33. agno/db/mongo/mongo.py +684 -4
  34. agno/db/mongo/schemas.py +48 -0
  35. agno/db/mongo/utils.py +17 -0
  36. agno/db/mysql/__init__.py +2 -1
  37. agno/db/mysql/async_mysql.py +2958 -0
  38. agno/db/mysql/mysql.py +722 -53
  39. agno/db/mysql/schemas.py +77 -11
  40. agno/db/mysql/utils.py +151 -8
  41. agno/db/postgres/async_postgres.py +1254 -137
  42. agno/db/postgres/postgres.py +2316 -93
  43. agno/db/postgres/schemas.py +153 -21
  44. agno/db/postgres/utils.py +22 -7
  45. agno/db/redis/redis.py +531 -3
  46. agno/db/redis/schemas.py +36 -0
  47. agno/db/redis/utils.py +31 -15
  48. agno/db/schemas/evals.py +1 -0
  49. agno/db/schemas/memory.py +20 -9
  50. agno/db/singlestore/schemas.py +70 -1
  51. agno/db/singlestore/singlestore.py +737 -74
  52. agno/db/singlestore/utils.py +13 -3
  53. agno/db/sqlite/async_sqlite.py +1069 -89
  54. agno/db/sqlite/schemas.py +133 -1
  55. agno/db/sqlite/sqlite.py +2203 -165
  56. agno/db/sqlite/utils.py +21 -11
  57. agno/db/surrealdb/models.py +25 -0
  58. agno/db/surrealdb/surrealdb.py +603 -1
  59. agno/db/utils.py +60 -0
  60. agno/eval/__init__.py +26 -3
  61. agno/eval/accuracy.py +25 -12
  62. agno/eval/agent_as_judge.py +871 -0
  63. agno/eval/base.py +29 -0
  64. agno/eval/performance.py +10 -4
  65. agno/eval/reliability.py +22 -13
  66. agno/eval/utils.py +2 -1
  67. agno/exceptions.py +42 -0
  68. agno/hooks/__init__.py +3 -0
  69. agno/hooks/decorator.py +164 -0
  70. agno/integrations/discord/client.py +13 -2
  71. agno/knowledge/__init__.py +4 -0
  72. agno/knowledge/chunking/code.py +90 -0
  73. agno/knowledge/chunking/document.py +65 -4
  74. agno/knowledge/chunking/fixed.py +4 -1
  75. agno/knowledge/chunking/markdown.py +102 -11
  76. agno/knowledge/chunking/recursive.py +2 -2
  77. agno/knowledge/chunking/semantic.py +130 -48
  78. agno/knowledge/chunking/strategy.py +18 -0
  79. agno/knowledge/embedder/azure_openai.py +0 -1
  80. agno/knowledge/embedder/google.py +1 -1
  81. agno/knowledge/embedder/mistral.py +1 -1
  82. agno/knowledge/embedder/nebius.py +1 -1
  83. agno/knowledge/embedder/openai.py +16 -12
  84. agno/knowledge/filesystem.py +412 -0
  85. agno/knowledge/knowledge.py +4261 -1199
  86. agno/knowledge/protocol.py +134 -0
  87. agno/knowledge/reader/arxiv_reader.py +3 -2
  88. agno/knowledge/reader/base.py +9 -7
  89. agno/knowledge/reader/csv_reader.py +91 -42
  90. agno/knowledge/reader/docx_reader.py +9 -10
  91. agno/knowledge/reader/excel_reader.py +225 -0
  92. agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
  93. agno/knowledge/reader/firecrawl_reader.py +3 -2
  94. agno/knowledge/reader/json_reader.py +16 -22
  95. agno/knowledge/reader/markdown_reader.py +15 -14
  96. agno/knowledge/reader/pdf_reader.py +33 -28
  97. agno/knowledge/reader/pptx_reader.py +9 -10
  98. agno/knowledge/reader/reader_factory.py +135 -1
  99. agno/knowledge/reader/s3_reader.py +8 -16
  100. agno/knowledge/reader/tavily_reader.py +3 -3
  101. agno/knowledge/reader/text_reader.py +15 -14
  102. agno/knowledge/reader/utils/__init__.py +17 -0
  103. agno/knowledge/reader/utils/spreadsheet.py +114 -0
  104. agno/knowledge/reader/web_search_reader.py +8 -65
  105. agno/knowledge/reader/website_reader.py +16 -13
  106. agno/knowledge/reader/wikipedia_reader.py +36 -3
  107. agno/knowledge/reader/youtube_reader.py +3 -2
  108. agno/knowledge/remote_content/__init__.py +33 -0
  109. agno/knowledge/remote_content/config.py +266 -0
  110. agno/knowledge/remote_content/remote_content.py +105 -17
  111. agno/knowledge/utils.py +76 -22
  112. agno/learn/__init__.py +71 -0
  113. agno/learn/config.py +463 -0
  114. agno/learn/curate.py +185 -0
  115. agno/learn/machine.py +725 -0
  116. agno/learn/schemas.py +1114 -0
  117. agno/learn/stores/__init__.py +38 -0
  118. agno/learn/stores/decision_log.py +1156 -0
  119. agno/learn/stores/entity_memory.py +3275 -0
  120. agno/learn/stores/learned_knowledge.py +1583 -0
  121. agno/learn/stores/protocol.py +117 -0
  122. agno/learn/stores/session_context.py +1217 -0
  123. agno/learn/stores/user_memory.py +1495 -0
  124. agno/learn/stores/user_profile.py +1220 -0
  125. agno/learn/utils.py +209 -0
  126. agno/media.py +22 -6
  127. agno/memory/__init__.py +14 -1
  128. agno/memory/manager.py +223 -8
  129. agno/memory/strategies/__init__.py +15 -0
  130. agno/memory/strategies/base.py +66 -0
  131. agno/memory/strategies/summarize.py +196 -0
  132. agno/memory/strategies/types.py +37 -0
  133. agno/models/aimlapi/aimlapi.py +17 -0
  134. agno/models/anthropic/claude.py +434 -59
  135. agno/models/aws/bedrock.py +121 -20
  136. agno/models/aws/claude.py +131 -274
  137. agno/models/azure/ai_foundry.py +10 -6
  138. agno/models/azure/openai_chat.py +33 -10
  139. agno/models/base.py +1162 -561
  140. agno/models/cerebras/cerebras.py +120 -24
  141. agno/models/cerebras/cerebras_openai.py +21 -2
  142. agno/models/cohere/chat.py +65 -6
  143. agno/models/cometapi/cometapi.py +18 -1
  144. agno/models/dashscope/dashscope.py +2 -3
  145. agno/models/deepinfra/deepinfra.py +18 -1
  146. agno/models/deepseek/deepseek.py +69 -3
  147. agno/models/fireworks/fireworks.py +18 -1
  148. agno/models/google/gemini.py +959 -89
  149. agno/models/google/utils.py +22 -0
  150. agno/models/groq/groq.py +48 -18
  151. agno/models/huggingface/huggingface.py +17 -6
  152. agno/models/ibm/watsonx.py +16 -6
  153. agno/models/internlm/internlm.py +18 -1
  154. agno/models/langdb/langdb.py +13 -1
  155. agno/models/litellm/chat.py +88 -9
  156. agno/models/litellm/litellm_openai.py +18 -1
  157. agno/models/message.py +24 -5
  158. agno/models/meta/llama.py +40 -13
  159. agno/models/meta/llama_openai.py +22 -21
  160. agno/models/metrics.py +12 -0
  161. agno/models/mistral/mistral.py +8 -4
  162. agno/models/n1n/__init__.py +3 -0
  163. agno/models/n1n/n1n.py +57 -0
  164. agno/models/nebius/nebius.py +6 -7
  165. agno/models/nvidia/nvidia.py +20 -3
  166. agno/models/ollama/__init__.py +2 -0
  167. agno/models/ollama/chat.py +17 -6
  168. agno/models/ollama/responses.py +100 -0
  169. agno/models/openai/__init__.py +2 -0
  170. agno/models/openai/chat.py +117 -26
  171. agno/models/openai/open_responses.py +46 -0
  172. agno/models/openai/responses.py +110 -32
  173. agno/models/openrouter/__init__.py +2 -0
  174. agno/models/openrouter/openrouter.py +67 -2
  175. agno/models/openrouter/responses.py +146 -0
  176. agno/models/perplexity/perplexity.py +19 -1
  177. agno/models/portkey/portkey.py +7 -6
  178. agno/models/requesty/requesty.py +19 -2
  179. agno/models/response.py +20 -2
  180. agno/models/sambanova/sambanova.py +20 -3
  181. agno/models/siliconflow/siliconflow.py +19 -2
  182. agno/models/together/together.py +20 -3
  183. agno/models/vercel/v0.py +20 -3
  184. agno/models/vertexai/claude.py +124 -4
  185. agno/models/vllm/vllm.py +19 -14
  186. agno/models/xai/xai.py +19 -2
  187. agno/os/app.py +467 -137
  188. agno/os/auth.py +253 -5
  189. agno/os/config.py +22 -0
  190. agno/os/interfaces/a2a/a2a.py +7 -6
  191. agno/os/interfaces/a2a/router.py +635 -26
  192. agno/os/interfaces/a2a/utils.py +32 -33
  193. agno/os/interfaces/agui/agui.py +5 -3
  194. agno/os/interfaces/agui/router.py +26 -16
  195. agno/os/interfaces/agui/utils.py +97 -57
  196. agno/os/interfaces/base.py +7 -7
  197. agno/os/interfaces/slack/router.py +16 -7
  198. agno/os/interfaces/slack/slack.py +7 -7
  199. agno/os/interfaces/whatsapp/router.py +35 -7
  200. agno/os/interfaces/whatsapp/security.py +3 -1
  201. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  202. agno/os/managers.py +326 -0
  203. agno/os/mcp.py +652 -79
  204. agno/os/middleware/__init__.py +4 -0
  205. agno/os/middleware/jwt.py +718 -115
  206. agno/os/middleware/trailing_slash.py +27 -0
  207. agno/os/router.py +105 -1558
  208. agno/os/routers/agents/__init__.py +3 -0
  209. agno/os/routers/agents/router.py +655 -0
  210. agno/os/routers/agents/schema.py +288 -0
  211. agno/os/routers/components/__init__.py +3 -0
  212. agno/os/routers/components/components.py +475 -0
  213. agno/os/routers/database.py +155 -0
  214. agno/os/routers/evals/evals.py +111 -18
  215. agno/os/routers/evals/schemas.py +38 -5
  216. agno/os/routers/evals/utils.py +80 -11
  217. agno/os/routers/health.py +3 -3
  218. agno/os/routers/knowledge/knowledge.py +284 -35
  219. agno/os/routers/knowledge/schemas.py +14 -2
  220. agno/os/routers/memory/memory.py +274 -11
  221. agno/os/routers/memory/schemas.py +44 -3
  222. agno/os/routers/metrics/metrics.py +30 -15
  223. agno/os/routers/metrics/schemas.py +10 -6
  224. agno/os/routers/registry/__init__.py +3 -0
  225. agno/os/routers/registry/registry.py +337 -0
  226. agno/os/routers/session/session.py +143 -14
  227. agno/os/routers/teams/__init__.py +3 -0
  228. agno/os/routers/teams/router.py +550 -0
  229. agno/os/routers/teams/schema.py +280 -0
  230. agno/os/routers/traces/__init__.py +3 -0
  231. agno/os/routers/traces/schemas.py +414 -0
  232. agno/os/routers/traces/traces.py +549 -0
  233. agno/os/routers/workflows/__init__.py +3 -0
  234. agno/os/routers/workflows/router.py +757 -0
  235. agno/os/routers/workflows/schema.py +139 -0
  236. agno/os/schema.py +157 -584
  237. agno/os/scopes.py +469 -0
  238. agno/os/settings.py +3 -0
  239. agno/os/utils.py +574 -185
  240. agno/reasoning/anthropic.py +85 -1
  241. agno/reasoning/azure_ai_foundry.py +93 -1
  242. agno/reasoning/deepseek.py +102 -2
  243. agno/reasoning/default.py +6 -7
  244. agno/reasoning/gemini.py +87 -3
  245. agno/reasoning/groq.py +109 -2
  246. agno/reasoning/helpers.py +6 -7
  247. agno/reasoning/manager.py +1238 -0
  248. agno/reasoning/ollama.py +93 -1
  249. agno/reasoning/openai.py +115 -1
  250. agno/reasoning/vertexai.py +85 -1
  251. agno/registry/__init__.py +3 -0
  252. agno/registry/registry.py +68 -0
  253. agno/remote/__init__.py +3 -0
  254. agno/remote/base.py +581 -0
  255. agno/run/__init__.py +2 -4
  256. agno/run/agent.py +134 -19
  257. agno/run/base.py +49 -1
  258. agno/run/cancel.py +65 -52
  259. agno/run/cancellation_management/__init__.py +9 -0
  260. agno/run/cancellation_management/base.py +78 -0
  261. agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
  262. agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
  263. agno/run/requirement.py +181 -0
  264. agno/run/team.py +111 -19
  265. agno/run/workflow.py +2 -1
  266. agno/session/agent.py +57 -92
  267. agno/session/summary.py +1 -1
  268. agno/session/team.py +62 -115
  269. agno/session/workflow.py +353 -57
  270. agno/skills/__init__.py +17 -0
  271. agno/skills/agent_skills.py +377 -0
  272. agno/skills/errors.py +32 -0
  273. agno/skills/loaders/__init__.py +4 -0
  274. agno/skills/loaders/base.py +27 -0
  275. agno/skills/loaders/local.py +216 -0
  276. agno/skills/skill.py +65 -0
  277. agno/skills/utils.py +107 -0
  278. agno/skills/validator.py +277 -0
  279. agno/table.py +10 -0
  280. agno/team/__init__.py +5 -1
  281. agno/team/remote.py +447 -0
  282. agno/team/team.py +3769 -2202
  283. agno/tools/brandfetch.py +27 -18
  284. agno/tools/browserbase.py +225 -16
  285. agno/tools/crawl4ai.py +3 -0
  286. agno/tools/duckduckgo.py +25 -71
  287. agno/tools/exa.py +0 -21
  288. agno/tools/file.py +14 -13
  289. agno/tools/file_generation.py +12 -6
  290. agno/tools/firecrawl.py +15 -7
  291. agno/tools/function.py +94 -113
  292. agno/tools/google_bigquery.py +11 -2
  293. agno/tools/google_drive.py +4 -3
  294. agno/tools/knowledge.py +9 -4
  295. agno/tools/mcp/mcp.py +301 -18
  296. agno/tools/mcp/multi_mcp.py +269 -14
  297. agno/tools/mem0.py +11 -10
  298. agno/tools/memory.py +47 -46
  299. agno/tools/mlx_transcribe.py +10 -7
  300. agno/tools/models/nebius.py +5 -5
  301. agno/tools/models_labs.py +20 -10
  302. agno/tools/nano_banana.py +151 -0
  303. agno/tools/parallel.py +0 -7
  304. agno/tools/postgres.py +76 -36
  305. agno/tools/python.py +14 -6
  306. agno/tools/reasoning.py +30 -23
  307. agno/tools/redshift.py +406 -0
  308. agno/tools/shopify.py +1519 -0
  309. agno/tools/spotify.py +919 -0
  310. agno/tools/tavily.py +4 -1
  311. agno/tools/toolkit.py +253 -18
  312. agno/tools/websearch.py +93 -0
  313. agno/tools/website.py +1 -1
  314. agno/tools/wikipedia.py +1 -1
  315. agno/tools/workflow.py +56 -48
  316. agno/tools/yfinance.py +12 -11
  317. agno/tracing/__init__.py +12 -0
  318. agno/tracing/exporter.py +161 -0
  319. agno/tracing/schemas.py +276 -0
  320. agno/tracing/setup.py +112 -0
  321. agno/utils/agent.py +251 -10
  322. agno/utils/cryptography.py +22 -0
  323. agno/utils/dttm.py +33 -0
  324. agno/utils/events.py +264 -7
  325. agno/utils/hooks.py +111 -3
  326. agno/utils/http.py +161 -2
  327. agno/utils/mcp.py +49 -8
  328. agno/utils/media.py +22 -1
  329. agno/utils/models/ai_foundry.py +9 -2
  330. agno/utils/models/claude.py +20 -5
  331. agno/utils/models/cohere.py +9 -2
  332. agno/utils/models/llama.py +9 -2
  333. agno/utils/models/mistral.py +4 -2
  334. agno/utils/os.py +0 -0
  335. agno/utils/print_response/agent.py +99 -16
  336. agno/utils/print_response/team.py +223 -24
  337. agno/utils/print_response/workflow.py +0 -2
  338. agno/utils/prompts.py +8 -6
  339. agno/utils/remote.py +23 -0
  340. agno/utils/response.py +1 -13
  341. agno/utils/string.py +91 -2
  342. agno/utils/team.py +62 -12
  343. agno/utils/tokens.py +657 -0
  344. agno/vectordb/base.py +15 -2
  345. agno/vectordb/cassandra/cassandra.py +1 -1
  346. agno/vectordb/chroma/__init__.py +2 -1
  347. agno/vectordb/chroma/chromadb.py +468 -23
  348. agno/vectordb/clickhouse/clickhousedb.py +1 -1
  349. agno/vectordb/couchbase/couchbase.py +6 -2
  350. agno/vectordb/lancedb/lance_db.py +7 -38
  351. agno/vectordb/lightrag/lightrag.py +7 -6
  352. agno/vectordb/milvus/milvus.py +118 -84
  353. agno/vectordb/mongodb/__init__.py +2 -1
  354. agno/vectordb/mongodb/mongodb.py +14 -31
  355. agno/vectordb/pgvector/pgvector.py +120 -66
  356. agno/vectordb/pineconedb/pineconedb.py +2 -19
  357. agno/vectordb/qdrant/__init__.py +2 -1
  358. agno/vectordb/qdrant/qdrant.py +33 -56
  359. agno/vectordb/redis/__init__.py +2 -1
  360. agno/vectordb/redis/redisdb.py +19 -31
  361. agno/vectordb/singlestore/singlestore.py +17 -9
  362. agno/vectordb/surrealdb/surrealdb.py +2 -38
  363. agno/vectordb/weaviate/__init__.py +2 -1
  364. agno/vectordb/weaviate/weaviate.py +7 -3
  365. agno/workflow/__init__.py +5 -1
  366. agno/workflow/agent.py +2 -2
  367. agno/workflow/condition.py +12 -10
  368. agno/workflow/loop.py +28 -9
  369. agno/workflow/parallel.py +21 -13
  370. agno/workflow/remote.py +362 -0
  371. agno/workflow/router.py +12 -9
  372. agno/workflow/step.py +261 -36
  373. agno/workflow/steps.py +12 -8
  374. agno/workflow/types.py +40 -77
  375. agno/workflow/workflow.py +939 -213
  376. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
  377. agno-2.4.3.dist-info/RECORD +677 -0
  378. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
  379. agno/tools/googlesearch.py +0 -98
  380. agno/tools/memori.py +0 -339
  381. agno-2.2.13.dist-info/RECORD +0 -575
  382. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
  383. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
@@ -102,7 +102,7 @@ class Qdrant(VectorDb):
102
102
  from agno.knowledge.embedder.openai import OpenAIEmbedder
103
103
 
104
104
  embedder = OpenAIEmbedder()
105
- log_info("Embedder not provided, using OpenAIEmbedder as default.")
105
+ log_debug("Embedder not provided, using OpenAIEmbedder as default.")
106
106
 
107
107
  self.embedder: Embedder = embedder
108
108
  self.dimensions: Optional[int] = self.embedder.dimensions
@@ -259,33 +259,6 @@ class Qdrant(VectorDb):
259
259
  else None,
260
260
  )
261
261
 
262
- def doc_exists(self, document: Document) -> bool:
263
- """
264
- Validating if the document exists or not
265
-
266
- Args:
267
- document (Document): Document to validate
268
- """
269
- if self.client:
270
- cleaned_content = document.content.replace("\x00", "\ufffd")
271
- doc_id = md5(cleaned_content.encode()).hexdigest()
272
- collection_points = self.client.retrieve(
273
- collection_name=self.collection,
274
- ids=[doc_id],
275
- )
276
- return len(collection_points) > 0
277
- return False
278
-
279
- async def async_doc_exists(self, document: Document) -> bool:
280
- """Check if a document exists asynchronously."""
281
- cleaned_content = document.content.replace("\x00", "\ufffd")
282
- doc_id = md5(cleaned_content.encode()).hexdigest()
283
- collection_points = await self.async_client.retrieve(
284
- collection_name=self.collection,
285
- ids=[doc_id],
286
- )
287
- return len(collection_points) > 0
288
-
289
262
  def name_exists(self, name: str) -> bool:
290
263
  """
291
264
  Validates if a document with the given name exists in the collection.
@@ -347,7 +320,9 @@ class Qdrant(VectorDb):
347
320
  points = []
348
321
  for document in documents:
349
322
  cleaned_content = document.content.replace("\x00", "\ufffd")
350
- doc_id = md5(cleaned_content.encode()).hexdigest()
323
+ # Include content_hash in ID to ensure uniqueness across different content hashes
324
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
325
+ doc_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
351
326
 
352
327
  # TODO(v2.0.0): Remove conditional vector naming logic
353
328
  if self.use_named_vectors:
@@ -448,16 +423,18 @@ class Qdrant(VectorDb):
448
423
  # Fall back to individual embedding
449
424
  for doc in documents:
450
425
  if self.search_type in [SearchType.vector, SearchType.hybrid]:
451
- doc.embed(embedder=self.embedder)
426
+ await doc.async_embed(embedder=self.embedder)
452
427
  else:
453
428
  # Use individual embedding
454
429
  for doc in documents:
455
430
  if self.search_type in [SearchType.vector, SearchType.hybrid]:
456
- doc.embed(embedder=self.embedder)
431
+ await doc.async_embed(embedder=self.embedder)
457
432
 
458
433
  async def process_document(document):
459
434
  cleaned_content = document.content.replace("\x00", "\ufffd")
460
- doc_id = md5(cleaned_content.encode()).hexdigest()
435
+ # Include content_hash in ID to ensure uniqueness across different content hashes
436
+ base_id = document.id or md5(cleaned_content.encode()).hexdigest()
437
+ doc_id = md5(f"{base_id}_{content_hash}".encode()).hexdigest()
461
438
 
462
439
  if self.search_type == SearchType.vector:
463
440
  # For vector search, maintain backward compatibility with unnamed vectors
@@ -545,13 +522,13 @@ class Qdrant(VectorDb):
545
522
  log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
546
523
  filters = None
547
524
 
548
- filters = self._format_filters(filters or {}) # type: ignore
525
+ formatted_filters = self._format_filters(filters or {}) # type: ignore
549
526
  if self.search_type == SearchType.vector:
550
- results = self._run_vector_search_sync(query, limit, filters) # type: ignore
527
+ results = self._run_vector_search_sync(query, limit, formatted_filters=formatted_filters) # type: ignore
551
528
  elif self.search_type == SearchType.keyword:
552
- results = self._run_keyword_search_sync(query, limit, filters) # type: ignore
529
+ results = self._run_keyword_search_sync(query, limit, formatted_filters=formatted_filters) # type: ignore
553
530
  elif self.search_type == SearchType.hybrid:
554
- results = self._run_hybrid_search_sync(query, limit, filters) # type: ignore
531
+ results = self._run_hybrid_search_sync(query, limit, formatted_filters=formatted_filters) # type: ignore
555
532
  else:
556
533
  raise ValueError(f"Unsupported search type: {self.search_type}")
557
534
 
@@ -564,13 +541,13 @@ class Qdrant(VectorDb):
564
541
  log_warning("Filters Expressions are not supported in Qdrant. No filters will be applied.")
565
542
  filters = None
566
543
 
567
- filters = self._format_filters(filters or {}) # type: ignore
544
+ formatted_filters = self._format_filters(filters or {}) # type: ignore
568
545
  if self.search_type == SearchType.vector:
569
- results = await self._run_vector_search_async(query, limit, filters) # type: ignore
546
+ results = await self._run_vector_search_async(query, limit, formatted_filters=formatted_filters) # type: ignore
570
547
  elif self.search_type == SearchType.keyword:
571
- results = await self._run_keyword_search_async(query, limit, filters) # type: ignore
548
+ results = await self._run_keyword_search_async(query, limit, formatted_filters=formatted_filters) # type: ignore
572
549
  elif self.search_type == SearchType.hybrid:
573
- results = await self._run_hybrid_search_async(query, limit, filters) # type: ignore
550
+ results = await self._run_hybrid_search_async(query, limit, formatted_filters=formatted_filters) # type: ignore
574
551
  else:
575
552
  raise ValueError(f"Unsupported search type: {self.search_type}")
576
553
 
@@ -580,7 +557,7 @@ class Qdrant(VectorDb):
580
557
  self,
581
558
  query: str,
582
559
  limit: int,
583
- filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
560
+ formatted_filters: Optional[models.Filter],
584
561
  ) -> List[models.ScoredPoint]:
585
562
  dense_embedding = self.embedder.get_embedding(query)
586
563
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
@@ -598,7 +575,7 @@ class Qdrant(VectorDb):
598
575
  with_vectors=True,
599
576
  with_payload=True,
600
577
  limit=limit,
601
- query_filter=filters,
578
+ query_filter=formatted_filters,
602
579
  )
603
580
  return call.points
604
581
 
@@ -606,7 +583,7 @@ class Qdrant(VectorDb):
606
583
  self,
607
584
  query: str,
608
585
  limit: int,
609
- filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
586
+ formatted_filters: Optional[models.Filter],
610
587
  ) -> List[models.ScoredPoint]:
611
588
  dense_embedding = self.embedder.get_embedding(query)
612
589
 
@@ -618,7 +595,7 @@ class Qdrant(VectorDb):
618
595
  with_vectors=True,
619
596
  with_payload=True,
620
597
  limit=limit,
621
- query_filter=filters,
598
+ query_filter=formatted_filters,
622
599
  using=self.dense_vector_name,
623
600
  )
624
601
  else:
@@ -629,7 +606,7 @@ class Qdrant(VectorDb):
629
606
  with_vectors=True,
630
607
  with_payload=True,
631
608
  limit=limit,
632
- query_filter=filters,
609
+ query_filter=formatted_filters,
633
610
  )
634
611
  return call.points
635
612
 
@@ -637,7 +614,7 @@ class Qdrant(VectorDb):
637
614
  self,
638
615
  query: str,
639
616
  limit: int,
640
- filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
617
+ formatted_filters: Optional[models.Filter],
641
618
  ) -> List[models.ScoredPoint]:
642
619
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
643
620
  call = self.client.query_points(
@@ -647,7 +624,7 @@ class Qdrant(VectorDb):
647
624
  with_payload=True,
648
625
  limit=limit,
649
626
  using=self.sparse_vector_name,
650
- query_filter=filters,
627
+ query_filter=formatted_filters,
651
628
  )
652
629
  return call.points
653
630
 
@@ -655,9 +632,9 @@ class Qdrant(VectorDb):
655
632
  self,
656
633
  query: str,
657
634
  limit: int,
658
- filters: Optional[Dict[str, Any]],
635
+ formatted_filters: Optional[models.Filter],
659
636
  ) -> List[models.ScoredPoint]:
660
- dense_embedding = self.embedder.get_embedding(query)
637
+ dense_embedding = await self.embedder.async_get_embedding(query)
661
638
 
662
639
  # TODO(v2.0.0): Remove this conditional and always use named vectors
663
640
  if self.use_named_vectors:
@@ -667,7 +644,7 @@ class Qdrant(VectorDb):
667
644
  with_vectors=True,
668
645
  with_payload=True,
669
646
  limit=limit,
670
- query_filter=filters,
647
+ query_filter=formatted_filters,
671
648
  using=self.dense_vector_name,
672
649
  )
673
650
  else:
@@ -678,7 +655,7 @@ class Qdrant(VectorDb):
678
655
  with_vectors=True,
679
656
  with_payload=True,
680
657
  limit=limit,
681
- query_filter=filters,
658
+ query_filter=formatted_filters,
682
659
  )
683
660
  return call.points
684
661
 
@@ -686,7 +663,7 @@ class Qdrant(VectorDb):
686
663
  self,
687
664
  query: str,
688
665
  limit: int,
689
- filters: Optional[Dict[str, Any]],
666
+ formatted_filters: Optional[models.Filter],
690
667
  ) -> List[models.ScoredPoint]:
691
668
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
692
669
  call = await self.async_client.query_points(
@@ -696,7 +673,7 @@ class Qdrant(VectorDb):
696
673
  with_payload=True,
697
674
  limit=limit,
698
675
  using=self.sparse_vector_name,
699
- query_filter=filters,
676
+ query_filter=formatted_filters,
700
677
  )
701
678
  return call.points
702
679
 
@@ -704,9 +681,9 @@ class Qdrant(VectorDb):
704
681
  self,
705
682
  query: str,
706
683
  limit: int,
707
- filters: Optional[Union[Dict[str, Any], List[FilterExpr]]],
684
+ formatted_filters: Optional[models.Filter],
708
685
  ) -> List[models.ScoredPoint]:
709
- dense_embedding = self.embedder.get_embedding(query)
686
+ dense_embedding = await self.embedder.async_get_embedding(query)
710
687
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
711
688
  call = await self.async_client.query_points(
712
689
  collection_name=self.collection,
@@ -722,7 +699,7 @@ class Qdrant(VectorDb):
722
699
  with_vectors=True,
723
700
  with_payload=True,
724
701
  limit=limit,
725
- query_filter=filters,
702
+ query_filter=formatted_filters,
726
703
  )
727
704
  return call.points
728
705
 
@@ -1,4 +1,4 @@
1
- from agno.vectordb.redis.redisdb import RedisDB
1
+ from agno.vectordb.redis.redisdb import RedisDB, SearchType
2
2
 
3
3
  # Backward compatibility alias
4
4
  RedisVectorDb = RedisDB
@@ -6,4 +6,5 @@ RedisVectorDb = RedisDB
6
6
  __all__ = [
7
7
  "RedisVectorDb",
8
8
  "RedisDB",
9
+ "SearchType",
9
10
  ]
@@ -15,7 +15,8 @@ except ImportError:
15
15
  from agno.filters import FilterExpr
16
16
  from agno.knowledge.document import Document
17
17
  from agno.knowledge.embedder import Embedder
18
- from agno.utils.log import log_debug, log_error, log_info, log_warning
18
+ from agno.knowledge.reranker.base import Reranker
19
+ from agno.utils.log import log_debug, log_error, log_warning
19
20
  from agno.utils.string import hash_string_sha256
20
21
  from agno.vectordb.base import VectorDb
21
22
  from agno.vectordb.distance import Distance
@@ -39,6 +40,7 @@ class RedisDB(VectorDb):
39
40
  search_type: SearchType = SearchType.vector,
40
41
  distance: Distance = Distance.cosine,
41
42
  vector_score_weight: float = 0.7,
43
+ reranker: Optional[Reranker] = None,
42
44
  **redis_kwargs,
43
45
  ):
44
46
  """
@@ -78,7 +80,7 @@ class RedisDB(VectorDb):
78
80
  from agno.knowledge.embedder.openai import OpenAIEmbedder
79
81
 
80
82
  embedder = OpenAIEmbedder()
81
- log_info("Embedder not provided, using OpenAIEmbedder as default.")
83
+ log_debug("Embedder not provided, using OpenAIEmbedder as default.")
82
84
 
83
85
  self.embedder: Embedder = embedder
84
86
  self.dimensions: Optional[int] = self.embedder.dimensions
@@ -91,8 +93,8 @@ class RedisDB(VectorDb):
91
93
  self.distance: Distance = distance
92
94
  self.vector_score_weight: float = vector_score_weight
93
95
 
94
- # # Reranker instance
95
- # self.reranker: Optional[Reranker] = reranker
96
+ # Reranker instance
97
+ self.reranker: Optional[Reranker] = reranker
96
98
 
97
99
  # Create index schema
98
100
  self.schema = self._get_schema()
@@ -184,32 +186,6 @@ class RedisDB(VectorDb):
184
186
  log_error(f"Error creating Redis index: {e}")
185
187
  raise
186
188
 
187
- def doc_exists(self, document: Document) -> bool:
188
- """Check if a document exists in the index."""
189
- try:
190
- doc_id = document.id or hash_string_sha256(document.content)
191
- return self.id_exists(doc_id)
192
- except Exception as e:
193
- log_error(f"Error checking if document exists: {e}")
194
- return False
195
-
196
- async def async_doc_exists(self, document: Document) -> bool:
197
- """Async version of doc_exists method."""
198
- try:
199
- doc_id = document.id or hash_string_sha256(document.content)
200
- async_index = await self._get_async_index()
201
- id_filter = Tag("id") == doc_id
202
- query = FilterQuery(
203
- filter_expression=id_filter,
204
- return_fields=["id"],
205
- num_results=1,
206
- )
207
- results = await async_index.query(query)
208
- return len(results) > 0
209
- except Exception as e:
210
- log_error(f"Error checking if document exists: {e}")
211
- return False
212
-
213
189
  def name_exists(self, name: str) -> bool:
214
190
  """Check if a document with the given name exists."""
215
191
  try:
@@ -453,6 +429,10 @@ class RedisDB(VectorDb):
453
429
  # Convert results to documents
454
430
  documents = [Document.from_dict(r) for r in results]
455
431
 
432
+ # Apply reranking if reranker is available
433
+ if self.reranker:
434
+ documents = self.reranker.rerank(query=query, documents=documents)
435
+
456
436
  return documents
457
437
  except Exception as e:
458
438
  log_error(f"Error in vector search: {e}")
@@ -476,6 +456,10 @@ class RedisDB(VectorDb):
476
456
  # Convert results to documents
477
457
  documents = [Document.from_dict(p) for p in parsed]
478
458
 
459
+ # Apply reranking if reranker is available
460
+ if self.reranker:
461
+ documents = self.reranker.rerank(query=query, documents=documents)
462
+
479
463
  return documents
480
464
  except Exception as e:
481
465
  log_error(f"Error in keyword search: {e}")
@@ -493,7 +477,7 @@ class RedisDB(VectorDb):
493
477
  vector_field_name="embedding",
494
478
  text=query,
495
479
  text_field_name="content",
496
- alpha=self.vector_score_weight,
480
+ linear_alpha=self.vector_score_weight,
497
481
  return_fields=["id", "name", "content"],
498
482
  num_results=limit,
499
483
  )
@@ -505,6 +489,10 @@ class RedisDB(VectorDb):
505
489
  # Convert results to documents
506
490
  documents = [Document.from_dict(p) for p in parsed]
507
491
 
492
+ # Apply reranking if reranker is available
493
+ if self.reranker:
494
+ documents = self.reranker.rerank(query=query, documents=documents)
495
+
508
496
  return documents
509
497
  except Exception as e:
510
498
  log_error(f"Error in hybrid search: {e}")
@@ -56,7 +56,7 @@ class SingleStore(VectorDb):
56
56
  from agno.knowledge.embedder.openai import OpenAIEmbedder
57
57
 
58
58
  embedder = OpenAIEmbedder()
59
- log_info("Embedder not provided, using OpenAIEmbedder as default.")
59
+ log_debug("Embedder not provided, using OpenAIEmbedder as default.")
60
60
  self.embedder: Embedder = embedder
61
61
  self.dimensions: Optional[int] = self.embedder.dimensions
62
62
 
@@ -185,8 +185,10 @@ class SingleStore(VectorDb):
185
185
  for document in documents:
186
186
  document.embed(embedder=self.embedder)
187
187
  cleaned_content = document.content.replace("\x00", "\ufffd")
188
- record_id = md5(cleaned_content.encode()).hexdigest()
189
- _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
190
192
 
191
193
  meta_data_json = json.dumps(document.meta_data)
192
194
  usage_json = json.dumps(document.usage)
@@ -246,8 +248,10 @@ class SingleStore(VectorDb):
246
248
  for document in documents:
247
249
  document.embed(embedder=self.embedder)
248
250
  cleaned_content = document.content.replace("\x00", "\ufffd")
249
- record_id = md5(cleaned_content.encode()).hexdigest()
250
- _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
251
255
 
252
256
  meta_data_json = json.dumps(document.meta_data)
253
257
  usage_json = json.dumps(document.usage)
@@ -548,8 +552,10 @@ class SingleStore(VectorDb):
548
552
  counter = 0
549
553
  for document in documents:
550
554
  cleaned_content = document.content.replace("\x00", "\ufffd")
551
- record_id = md5(cleaned_content.encode()).hexdigest()
552
- _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
553
559
 
554
560
  meta_data_json = json.dumps(document.meta_data)
555
561
  usage_json = json.dumps(document.usage)
@@ -632,8 +638,10 @@ class SingleStore(VectorDb):
632
638
  counter = 0
633
639
  for document in documents:
634
640
  cleaned_content = document.content.replace("\x00", "\ufffd")
635
- record_id = md5(cleaned_content.encode()).hexdigest()
636
- _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
637
645
 
638
646
  meta_data_json = json.dumps(document.meta_data)
639
647
  usage_json = json.dumps(document.usage)
@@ -14,7 +14,7 @@ except ImportError as e:
14
14
  from agno.filters import FilterExpr
15
15
  from agno.knowledge.document import Document
16
16
  from agno.knowledge.embedder import Embedder
17
- from agno.utils.log import log_debug, log_error, log_info, log_warning
17
+ from agno.utils.log import log_debug, log_error, log_warning
18
18
  from agno.vectordb.base import VectorDb
19
19
  from agno.vectordb.distance import Distance
20
20
 
@@ -31,12 +31,6 @@ class SurrealDb(VectorDb):
31
31
  DEFINE INDEX IF NOT EXISTS vector_idx ON {collection} FIELDS embedding HNSW DIMENSION {dimensions} DIST {distance};
32
32
  """
33
33
 
34
- DOC_EXISTS_QUERY: Final[str] = """
35
- SELECT * FROM {collection}
36
- WHERE content = $content
37
- LIMIT 1
38
- """
39
-
40
34
  NAME_EXISTS_QUERY: Final[str] = """
41
35
  SELECT * FROM {collection}
42
36
  WHERE meta_data.name = $name
@@ -141,7 +135,7 @@ class SurrealDb(VectorDb):
141
135
  from agno.knowledge.embedder.openai import OpenAIEmbedder
142
136
 
143
137
  embedder = OpenAIEmbedder()
144
- log_info("Embedder not provided, using OpenAIEmbedder as default.")
138
+ log_debug("Embedder not provided, using OpenAIEmbedder as default.")
145
139
  self.embedder: Embedder = embedder
146
140
  self.dimensions = self.embedder.dimensions
147
141
  self.collection = collection
@@ -221,23 +215,6 @@ class SurrealDb(VectorDb):
221
215
  )
222
216
  self.client.query(query)
223
217
 
224
- def doc_exists(self, document: Document) -> bool:
225
- """Check if a document exists by its content.
226
-
227
- Args:
228
- document: The document to check.
229
-
230
- Returns:
231
- True if the document exists, False otherwise.
232
-
233
- """
234
- log_debug(f"Checking if document exists: {document.content}")
235
- result = self.client.query(
236
- self.DOC_EXISTS_QUERY.format(collection=self.collection),
237
- {"content": document.content},
238
- )
239
- return bool(self._extract_result(result))
240
-
241
218
  def name_exists(self, name: str) -> bool:
242
219
  """Check if a document exists by its name.
243
220
 
@@ -493,19 +470,6 @@ class SurrealDb(VectorDb):
493
470
  ),
494
471
  )
495
472
 
496
- async def async_doc_exists(self, document: Document) -> bool:
497
- """Check if a document exists by its content asynchronously.
498
-
499
- Returns:
500
- True if the document exists, False otherwise.
501
-
502
- """
503
- response = await self.async_client.query(
504
- self.DOC_EXISTS_QUERY.format(collection=self.collection),
505
- {"content": document.content},
506
- )
507
- return bool(self._extract_result(response))
508
-
509
473
  async def async_name_exists(self, name: str) -> bool:
510
474
  """Check if a document exists by its name asynchronously.
511
475
 
@@ -1,8 +1,9 @@
1
1
  from agno.vectordb.weaviate.index import Distance, VectorIndex
2
- from agno.vectordb.weaviate.weaviate import Weaviate
2
+ from agno.vectordb.weaviate.weaviate import SearchType, Weaviate
3
3
 
4
4
  __all__ = [
5
5
  "Distance",
6
6
  "VectorIndex",
7
7
  "Weaviate",
8
+ "SearchType",
8
9
  ]
@@ -81,7 +81,7 @@ class Weaviate(VectorDb):
81
81
  from agno.knowledge.embedder.openai import OpenAIEmbedder
82
82
 
83
83
  embedder = OpenAIEmbedder()
84
- log_info("Embedder not provided, using OpenAIEmbedder as default.")
84
+ log_debug("Embedder not provided, using OpenAIEmbedder as default.")
85
85
  self.embedder: Embedder = embedder
86
86
 
87
87
  # Search setup
@@ -247,7 +247,9 @@ class Weaviate(VectorDb):
247
247
  continue
248
248
 
249
249
  cleaned_content = document.content.replace("\x00", "\ufffd")
250
- 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()
251
253
  doc_uuid = uuid.UUID(hex=record_id[:32])
252
254
 
253
255
  # Merge filters with metadata
@@ -338,7 +340,9 @@ class Weaviate(VectorDb):
338
340
 
339
341
  # Clean content and generate UUID
340
342
  cleaned_content = document.content.replace("\x00", "\ufffd")
341
- 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()
342
346
  doc_uuid = uuid.UUID(hex=record_id[:32])
343
347
 
344
348
  # Serialize meta_data to JSON string
agno/workflow/__init__.py CHANGED
@@ -2,15 +2,17 @@ from agno.workflow.agent import WorkflowAgent
2
2
  from agno.workflow.condition import Condition
3
3
  from agno.workflow.loop import Loop
4
4
  from agno.workflow.parallel import Parallel
5
+ from agno.workflow.remote import RemoteWorkflow
5
6
  from agno.workflow.router import Router
6
7
  from agno.workflow.step import Step
7
8
  from agno.workflow.steps import Steps
8
9
  from agno.workflow.types import StepInput, StepOutput, WorkflowExecutionInput
9
- from agno.workflow.workflow import Workflow
10
+ from agno.workflow.workflow import Workflow, get_workflow_by_id, get_workflows
10
11
 
11
12
  __all__ = [
12
13
  "Workflow",
13
14
  "WorkflowAgent",
15
+ "RemoteWorkflow",
14
16
  "Steps",
15
17
  "Step",
16
18
  "Loop",
@@ -20,4 +22,6 @@ __all__ = [
20
22
  "WorkflowExecutionInput",
21
23
  "StepInput",
22
24
  "StepOutput",
25
+ "get_workflow_by_id",
26
+ "get_workflows",
23
27
  ]
agno/workflow/agent.py CHANGED
@@ -5,9 +5,9 @@ from typing import TYPE_CHECKING, Any, Callable, Optional
5
5
  from agno.agent import Agent
6
6
  from agno.models.base import Model
7
7
  from agno.run import RunContext
8
- from agno.workflow.types import WebSocketHandler
9
8
 
10
9
  if TYPE_CHECKING:
10
+ from agno.os.managers import WebSocketHandler
11
11
  from agno.session.workflow import WorkflowSession
12
12
  from agno.workflow.types import WorkflowExecutionInput
13
13
 
@@ -190,7 +190,7 @@ Guidelines:
190
190
  execution_input: "WorkflowExecutionInput",
191
191
  run_context: RunContext,
192
192
  stream: bool = False,
193
- websocket_handler: Optional[WebSocketHandler] = None,
193
+ websocket_handler: Optional["WebSocketHandler"] = None,
194
194
  ) -> Callable:
195
195
  """
196
196
  Create the async workflow execution tool that this agent can call.