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
agno/tools/tavily.py CHANGED
@@ -15,6 +15,7 @@ class TavilyTools(Toolkit):
15
15
  def __init__(
16
16
  self,
17
17
  api_key: Optional[str] = None,
18
+ api_base_url: Optional[str] = None,
18
19
  enable_search: bool = True,
19
20
  enable_search_context: bool = False,
20
21
  enable_extract: bool = False,
@@ -34,6 +35,7 @@ class TavilyTools(Toolkit):
34
35
 
35
36
  Args:
36
37
  api_key: Tavily API key. If not provided, will use TAVILY_API_KEY env var.
38
+ api_base_url: Tavily API base URL. If not provided, will use TAVILY_API_BASE_URL env var. Defaults to None. If None - will use https://api.tavily.com.
37
39
  enable_search: Enable web search functionality. Defaults to True.
38
40
  enable_search_context: Use search context mode instead of regular search. Defaults to False.
39
41
  enable_extract: Enable URL content extraction functionality. Defaults to False.
@@ -52,8 +54,9 @@ class TavilyTools(Toolkit):
52
54
  self.api_key = api_key or getenv("TAVILY_API_KEY")
53
55
  if not self.api_key:
54
56
  logger.error("TAVILY_API_KEY not provided")
57
+ self.api_base_url = api_base_url or getenv("TAVILY_API_BASE_URL")
55
58
 
56
- self.client: TavilyClient = TavilyClient(api_key=self.api_key)
59
+ self.client: TavilyClient = TavilyClient(api_key=self.api_key, api_base_url=self.api_base_url)
57
60
  self.search_depth: Literal["basic", "advanced"] = search_depth
58
61
  self.extract_depth: Literal["basic", "advanced"] = extract_depth
59
62
  self.max_tokens: int = max_tokens
agno/tools/toolkit.py CHANGED
@@ -1,15 +1,22 @@
1
1
  from collections import OrderedDict
2
- from typing import Any, Callable, Dict, List, Optional
2
+ from inspect import iscoroutinefunction
3
+ from pathlib import Path
4
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union
3
5
 
4
6
  from agno.tools.function import Function
5
- from agno.utils.log import log_debug, log_warning, logger
7
+ from agno.utils.log import log_debug, log_error, log_warning, logger
6
8
 
7
9
 
8
10
  class Toolkit:
11
+ # Set to True for toolkits that require connection management (e.g., database connections)
12
+ # When True, the Agent will automatically call connect() before using tools and close() after
13
+ _requires_connect: bool = False
14
+
9
15
  def __init__(
10
16
  self,
11
17
  name: str = "toolkit",
12
- tools: List[Callable] = [],
18
+ tools: Sequence[Union[Callable[..., Any], Function]] = [],
19
+ async_tools: Optional[Sequence[tuple[Callable[..., Any], str]]] = None,
13
20
  instructions: Optional[str] = None,
14
21
  add_instructions: bool = False,
15
22
  include_tools: Optional[list[str]] = None,
@@ -27,7 +34,10 @@ class Toolkit:
27
34
 
28
35
  Args:
29
36
  name: A descriptive name for the toolkit
30
- tools: List of tools to include in the toolkit
37
+ tools: List of tools to include in the toolkit (can be callables or Function objects from @tool decorator)
38
+ async_tools: List of (async_callable, tool_name) tuples for async variants.
39
+ Used when async methods have different names than sync methods.
40
+ Example: [(self.anavigate_to, "navigate_to"), (self.ascreenshot, "screenshot")]
31
41
  instructions: Instructions for the toolkit
32
42
  add_instructions: Whether to add instructions to the toolkit
33
43
  include_tools: List of tool names to include in the toolkit
@@ -42,8 +52,12 @@ class Toolkit:
42
52
  show_result_tools (Optional[List[str]]): List of function names whose results should be shown.
43
53
  """
44
54
  self.name: str = name
45
- self.tools: List[Callable] = tools
55
+ self.tools: Sequence[Union[Callable[..., Any], Function]] = tools
56
+ self._async_tools: Sequence[tuple[Callable[..., Any], str]] = async_tools or []
57
+ # Functions dict - used by agent.run() and agent.print_response()
46
58
  self.functions: Dict[str, Function] = OrderedDict()
59
+ # Async functions dict - used by agent.arun() and agent.aprint_response()
60
+ self.async_functions: Dict[str, Function] = OrderedDict()
47
61
  self.instructions: Optional[str] = instructions
48
62
  self.add_instructions: bool = add_instructions
49
63
 
@@ -54,7 +68,9 @@ class Toolkit:
54
68
  self.show_result_tools: list[str] = show_result_tools or []
55
69
 
56
70
  self._check_tools_filters(
57
- available_tools=[tool.__name__ for tool in tools], include_tools=include_tools, exclude_tools=exclude_tools
71
+ available_tools=[self._get_tool_name(tool) for tool in tools],
72
+ include_tools=include_tools,
73
+ exclude_tools=exclude_tools,
58
74
  )
59
75
 
60
76
  self.include_tools = include_tools
@@ -65,8 +81,17 @@ class Toolkit:
65
81
  self.cache_dir: Optional[str] = cache_dir
66
82
 
67
83
  # Automatically register all methods if auto_register is True
68
- if auto_register and self.tools:
69
- self._register_tools()
84
+ if auto_register:
85
+ if self.tools:
86
+ self._register_tools()
87
+ if self._async_tools:
88
+ self._register_async_tools()
89
+
90
+ def _get_tool_name(self, tool: Union[Callable[..., Any], Function]) -> str:
91
+ """Get the name of a tool, whether it's a Function or callable."""
92
+ if isinstance(tool, Function):
93
+ return tool.name
94
+ return tool.__name__
70
95
 
71
96
  def _check_tools_filters(
72
97
  self,
@@ -100,22 +125,57 @@ class Toolkit:
100
125
  f"External execution required tool(s) not present in the toolkit: {', '.join(missing_external_execution_required)}"
101
126
  )
102
127
 
128
+ if self.stop_after_tool_call_tools:
129
+ missing_stop_after_tool_call = set(self.stop_after_tool_call_tools) - set(available_tools)
130
+ if missing_stop_after_tool_call:
131
+ log_warning(
132
+ f"Stop after tool call tool(s) not present in the toolkit: {', '.join(missing_stop_after_tool_call)}"
133
+ )
134
+
135
+ if self.show_result_tools:
136
+ missing_show_result = set(self.show_result_tools) - set(available_tools)
137
+ if missing_show_result:
138
+ log_warning(f"Show result tool(s) not present in the toolkit: {', '.join(missing_show_result)}")
139
+
103
140
  def _register_tools(self) -> None:
104
- """Register all tools."""
141
+ """Register all sync tools."""
105
142
  for tool in self.tools:
106
143
  self.register(tool)
107
144
 
108
- def register(self, function: Callable[..., Any], name: Optional[str] = None):
145
+ def _register_async_tools(self) -> None:
146
+ """Register all async tools with their mapped names.
147
+
148
+ Async detection is automatic via iscoroutinefunction.
149
+ """
150
+ for async_func, tool_name in self._async_tools:
151
+ self.register(async_func, name=tool_name)
152
+
153
+ def register(self, function: Union[Callable[..., Any], Function], name: Optional[str] = None) -> None:
109
154
  """Register a function with the toolkit.
110
155
 
111
- Args:
112
- function: The callable to register
113
- name: Optional custom name for the function
156
+ This method supports both regular callables and Function objects (from @tool decorator).
157
+ Automatically detects if the function is async (using iscoroutinefunction) and registers
158
+ it to the appropriate dict (functions for sync, async_functions for async).
114
159
 
115
- Returns:
116
- The registered function
160
+ When a Function object is passed (e.g., from a @tool decorated method), it will:
161
+ 1. Extract the configuration from the Function object
162
+ 2. Look for a bound method with the same name on `self`
163
+ 3. Create a new Function with the bound method as entrypoint, preserving decorator settings
164
+
165
+ Args:
166
+ function: The callable or Function object to register
167
+ name: Optional custom name for the function (useful for aliasing)
117
168
  """
118
169
  try:
170
+ # Handle Function objects (from @tool decorator)
171
+ if isinstance(function, Function):
172
+ # Auto-detect if this is an async function
173
+ is_async = function.entrypoint is not None and iscoroutinefunction(function.entrypoint)
174
+ return self._register_decorated_tool(function, name, is_async=is_async)
175
+
176
+ # Handle regular callables - auto-detect async
177
+ is_async = iscoroutinefunction(function)
178
+
119
179
  tool_name = name or function.__name__
120
180
  if self.include_tools is not None and tool_name not in self.include_tools:
121
181
  return
@@ -133,12 +193,187 @@ class Toolkit:
133
193
  stop_after_tool_call=tool_name in self.stop_after_tool_call_tools,
134
194
  show_result=tool_name in self.show_result_tools or tool_name in self.stop_after_tool_call_tools,
135
195
  )
136
- self.functions[f.name] = f
137
- log_debug(f"Function: {f.name} registered with {self.name}")
196
+
197
+ if is_async:
198
+ self.async_functions[f.name] = f
199
+ log_debug(f"Async function: {f.name} registered with {self.name}")
200
+ else:
201
+ self.functions[f.name] = f
202
+ log_debug(f"Function: {f.name} registered with {self.name}")
138
203
  except Exception as e:
139
- logger.warning(f"Failed to create Function for: {function.__name__}")
204
+ func_name = self._get_tool_name(function)
205
+ logger.warning(f"Failed to create Function for: {func_name}")
140
206
  raise e
141
207
 
208
+ def _register_decorated_tool(self, function: Function, name: Optional[str] = None, is_async: bool = False) -> None:
209
+ """Register a Function object from @tool decorator, binding it to self.
210
+
211
+ When @tool decorator is used on a class method, it creates a Function with an unbound
212
+ method as entrypoint. This method creates a bound version of the entrypoint that
213
+ includes `self`, preserving all decorator settings.
214
+
215
+ Args:
216
+ function: The Function object from @tool decorator
217
+ name: Optional custom name override
218
+ is_async: If True, register to async_functions dict instead of functions
219
+ """
220
+ import inspect
221
+
222
+ tool_name = name or function.name
223
+ if self.include_tools is not None and len(self.include_tools) > 0 and tool_name not in self.include_tools:
224
+ return
225
+ if self.exclude_tools is not None and len(self.exclude_tools) > 0 and tool_name in self.exclude_tools:
226
+ return
227
+
228
+ # Get the original entrypoint from the Function
229
+ if function.entrypoint is None:
230
+ log_warning(f"Function '{tool_name}' has no entrypoint, skipping registration")
231
+ return
232
+
233
+ original_func = function.entrypoint
234
+
235
+ # Check if the function expects 'self' as first argument (i.e., it's an unbound method)
236
+ sig = inspect.signature(original_func)
237
+ params = list(sig.parameters.keys())
238
+
239
+ if params and params[0] == "self":
240
+ # Create a bound method by wrapping the function to include self
241
+ if is_async:
242
+
243
+ def make_bound_method(func, instance):
244
+ async def bound(*args, **kwargs):
245
+ return await func(instance, *args, **kwargs)
246
+
247
+ bound.__name__ = getattr(func, "__name__", tool_name)
248
+ bound.__doc__ = getattr(func, "__doc__", None)
249
+ return bound
250
+ else:
251
+
252
+ def make_bound_method(func, instance):
253
+ def bound(*args, **kwargs):
254
+ return func(instance, *args, **kwargs)
255
+
256
+ bound.__name__ = getattr(func, "__name__", tool_name)
257
+ bound.__doc__ = getattr(func, "__doc__", None)
258
+ return bound
259
+
260
+ bound_method = make_bound_method(original_func, self)
261
+ else:
262
+ # Function doesn't expect self (e.g., static method or already bound)
263
+ bound_method = original_func
264
+
265
+ # decorator settings take precedence, then toolkit settings
266
+ stop_after = function.stop_after_tool_call or tool_name in self.stop_after_tool_call_tools
267
+ show_result = function.show_result or tool_name in self.show_result_tools or stop_after
268
+ requires_confirmation = function.requires_confirmation or tool_name in self.requires_confirmation_tools
269
+ external_execution = function.external_execution or tool_name in self.external_execution_required_tools
270
+
271
+ # Create new Function with bound method, preserving decorator settings
272
+ f = Function(
273
+ name=tool_name,
274
+ description=function.description,
275
+ parameters=function.parameters,
276
+ strict=function.strict,
277
+ instructions=function.instructions,
278
+ add_instructions=function.add_instructions,
279
+ entrypoint=bound_method,
280
+ skip_entrypoint_processing=True, # Parameters already processed by decorator
281
+ show_result=show_result,
282
+ stop_after_tool_call=stop_after,
283
+ pre_hook=function.pre_hook,
284
+ post_hook=function.post_hook,
285
+ tool_hooks=function.tool_hooks,
286
+ requires_confirmation=requires_confirmation,
287
+ requires_user_input=function.requires_user_input,
288
+ user_input_fields=function.user_input_fields,
289
+ user_input_schema=function.user_input_schema,
290
+ external_execution=external_execution,
291
+ cache_results=function.cache_results if function.cache_results else self.cache_results,
292
+ cache_dir=function.cache_dir if function.cache_dir else self.cache_dir,
293
+ cache_ttl=function.cache_ttl if function.cache_ttl != 3600 else self.cache_ttl,
294
+ )
295
+
296
+ if is_async:
297
+ self.async_functions[f.name] = f
298
+ log_debug(f"Async function: {f.name} registered with {self.name} (from @tool decorator)")
299
+ else:
300
+ self.functions[f.name] = f
301
+ log_debug(f"Function: {f.name} registered with {self.name} (from @tool decorator)")
302
+
303
+ def get_functions(self) -> Dict[str, Function]:
304
+ """Get sync functions dict.
305
+
306
+ Returns:
307
+ Dict of function name to Function for sync execution
308
+ """
309
+ return self.functions
310
+
311
+ def get_async_functions(self) -> Dict[str, Function]:
312
+ """Get functions dict optimized for async execution.
313
+
314
+ Returns a merged dict where async_functions take precedence over functions.
315
+ This allows async-optimized implementations to be automatically used in async contexts,
316
+ while falling back to sync implementations for tools without async variants.
317
+
318
+ Returns:
319
+ Dict of function name to Function, with async variants preferred
320
+ """
321
+ # Merge: start with sync functions, override with async variants
322
+ merged = OrderedDict(self.functions)
323
+ merged.update(self.async_functions)
324
+ return merged
325
+
326
+ @property
327
+ def requires_connect(self) -> bool:
328
+ """Whether the toolkit requires connection management."""
329
+ return self._requires_connect
330
+
331
+ def connect(self) -> None:
332
+ """
333
+ Establish any required connections for the toolkit.
334
+ Override this method in subclasses that require connection management.
335
+ Called automatically by the Agent when _requires_connect is True.
336
+ """
337
+ pass
338
+
339
+ def close(self) -> None:
340
+ """
341
+ Close any open connections for the toolkit.
342
+ Override this method in subclasses that require connection management.
343
+ Called automatically by the Agent when _requires_connect is True.
344
+ """
345
+ pass
346
+
347
+ def _check_path(self, file_name: str, base_dir: Path, restrict_to_base_dir: bool = True) -> Tuple[bool, Path]:
348
+ """Check if the file path is within the base directory.
349
+
350
+ This method validates that a given file path resolves to a location
351
+ within the specified base_dir, preventing directory traversal attacks.
352
+
353
+ Args:
354
+ file_name: The file name or relative path to check.
355
+ base_dir: The base directory to validate against.
356
+ restrict_to_base_dir: If True, reject paths outside base_dir.
357
+
358
+ Returns:
359
+ Tuple of (is_safe, resolved_path). If not safe, returns base_dir as the path.
360
+ """
361
+ file_path = base_dir.joinpath(file_name).resolve()
362
+
363
+ if not restrict_to_base_dir:
364
+ return True, file_path
365
+
366
+ if base_dir == file_path:
367
+ return True, file_path
368
+
369
+ try:
370
+ file_path.relative_to(base_dir)
371
+ except ValueError:
372
+ log_error(f"Path escapes base directory: {file_name}")
373
+ return False, base_dir
374
+
375
+ return True, file_path
376
+
142
377
  def __repr__(self):
143
378
  return f"<{self.__class__.__name__} name={self.name} functions={list(self.functions.keys())}>"
144
379
 
@@ -0,0 +1,93 @@
1
+ import json
2
+ from typing import Any, List, Optional
3
+
4
+ from agno.tools import Toolkit
5
+ from agno.utils.log import log_debug
6
+
7
+ try:
8
+ from ddgs import DDGS
9
+ except ImportError:
10
+ raise ImportError("`ddgs` not installed. Please install using `pip install ddgs`")
11
+
12
+
13
+ class WebSearchTools(Toolkit):
14
+ """
15
+ Toolkit for searching the web. Uses the meta-search library DDGS.
16
+ Multiple search backends (e.g. google, bing, duckduckgo) are available.
17
+
18
+ Args:
19
+ enable_search (bool): Enable web search function.
20
+ enable_news (bool): Enable news search function.
21
+ backend (str): The backend to use for searching. Defaults to "auto" which
22
+ automatically selects available backends. Other options include:
23
+ "duckduckgo", "google", "bing", "brave", "yandex", "yahoo", etc.
24
+ modifier (Optional[str]): A modifier to be prepended to search queries.
25
+ fixed_max_results (Optional[int]): A fixed number of maximum results.
26
+ proxy (Optional[str]): Proxy to be used for requests.
27
+ timeout (Optional[int]): The maximum number of seconds to wait for a response.
28
+ verify_ssl (bool): Whether to verify SSL certificates.
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ enable_search: bool = True,
34
+ enable_news: bool = True,
35
+ backend: str = "auto",
36
+ modifier: Optional[str] = None,
37
+ fixed_max_results: Optional[int] = None,
38
+ proxy: Optional[str] = None,
39
+ timeout: Optional[int] = 10,
40
+ verify_ssl: bool = True,
41
+ **kwargs,
42
+ ):
43
+ self.proxy: Optional[str] = proxy
44
+ self.timeout: Optional[int] = timeout
45
+ self.fixed_max_results: Optional[int] = fixed_max_results
46
+ self.modifier: Optional[str] = modifier
47
+ self.verify_ssl: bool = verify_ssl
48
+ self.backend: str = backend
49
+
50
+ tools: List[Any] = []
51
+ if enable_search:
52
+ tools.append(self.web_search)
53
+ if enable_news:
54
+ tools.append(self.search_news)
55
+
56
+ super().__init__(name="websearch", tools=tools, **kwargs)
57
+
58
+ def web_search(self, query: str, max_results: int = 5) -> str:
59
+ """Use this function to search the web for a query.
60
+
61
+ Args:
62
+ query(str): The query to search for.
63
+ max_results (optional, default=5): The maximum number of results to return.
64
+
65
+ Returns:
66
+ The search results from the web.
67
+ """
68
+ actual_max_results = self.fixed_max_results or max_results
69
+ search_query = f"{self.modifier} {query}" if self.modifier else query
70
+
71
+ log_debug(f"Searching web for: {search_query} using backend: {self.backend}")
72
+ with DDGS(proxy=self.proxy, timeout=self.timeout, verify=self.verify_ssl) as ddgs:
73
+ results = ddgs.text(query=search_query, max_results=actual_max_results, backend=self.backend)
74
+
75
+ return json.dumps(results, indent=2)
76
+
77
+ def search_news(self, query: str, max_results: int = 5) -> str:
78
+ """Use this function to get the latest news from the web.
79
+
80
+ Args:
81
+ query(str): The query to search for.
82
+ max_results (optional, default=5): The maximum number of results to return.
83
+
84
+ Returns:
85
+ The latest news from the web.
86
+ """
87
+ actual_max_results = self.fixed_max_results or max_results
88
+
89
+ log_debug(f"Searching web news for: {query} using backend: {self.backend}")
90
+ with DDGS(proxy=self.proxy, timeout=self.timeout, verify=self.verify_ssl) as ddgs:
91
+ results = ddgs.news(query=query, max_results=actual_max_results, backend=self.backend)
92
+
93
+ return json.dumps(results, indent=2)
agno/tools/website.py CHANGED
@@ -36,7 +36,7 @@ class WebsiteTools(Toolkit):
36
36
  return "Knowledge base not provided"
37
37
 
38
38
  log_debug(f"Adding to knowledge base: {url}")
39
- self.knowledge.add_content(url=url)
39
+ self.knowledge.insert(url=url)
40
40
  return "Success"
41
41
 
42
42
  def read_url(self, url: str) -> str:
agno/tools/wikipedia.py CHANGED
@@ -38,7 +38,7 @@ class WikipediaTools(Toolkit):
38
38
  return "Knowledge not provided"
39
39
 
40
40
  log_debug(f"Adding to knowledge: {topic}")
41
- self.knowledge.add_content(
41
+ self.knowledge.insert(
42
42
  topics=[topic],
43
43
  reader=WikipediaReader(),
44
44
  )