agno 0.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 (723) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +44 -5
  3. agno/agent/agent.py +10531 -2975
  4. agno/api/agent.py +14 -53
  5. agno/api/api.py +7 -46
  6. agno/api/evals.py +22 -0
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -25
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +6 -9
  11. agno/api/schemas/evals.py +16 -0
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +10 -10
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +16 -0
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +22 -26
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/compression/__init__.py +3 -0
  25. agno/compression/manager.py +247 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +946 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2781 -0
  33. agno/db/dynamo/schemas.py +442 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +2379 -0
  37. agno/db/firestore/schemas.py +181 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1791 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1312 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1777 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/manager.py +199 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/migrations/versions/v2_3_0.py +938 -0
  51. agno/db/mongo/__init__.py +17 -0
  52. agno/db/mongo/async_mongo.py +2760 -0
  53. agno/db/mongo/mongo.py +2597 -0
  54. agno/db/mongo/schemas.py +119 -0
  55. agno/db/mongo/utils.py +276 -0
  56. agno/db/mysql/__init__.py +4 -0
  57. agno/db/mysql/async_mysql.py +2912 -0
  58. agno/db/mysql/mysql.py +2923 -0
  59. agno/db/mysql/schemas.py +186 -0
  60. agno/db/mysql/utils.py +488 -0
  61. agno/db/postgres/__init__.py +4 -0
  62. agno/db/postgres/async_postgres.py +2579 -0
  63. agno/db/postgres/postgres.py +2870 -0
  64. agno/db/postgres/schemas.py +187 -0
  65. agno/db/postgres/utils.py +442 -0
  66. agno/db/redis/__init__.py +3 -0
  67. agno/db/redis/redis.py +2141 -0
  68. agno/db/redis/schemas.py +159 -0
  69. agno/db/redis/utils.py +346 -0
  70. agno/db/schemas/__init__.py +4 -0
  71. agno/db/schemas/culture.py +120 -0
  72. agno/db/schemas/evals.py +34 -0
  73. agno/db/schemas/knowledge.py +40 -0
  74. agno/db/schemas/memory.py +61 -0
  75. agno/db/singlestore/__init__.py +3 -0
  76. agno/db/singlestore/schemas.py +179 -0
  77. agno/db/singlestore/singlestore.py +2877 -0
  78. agno/db/singlestore/utils.py +384 -0
  79. agno/db/sqlite/__init__.py +4 -0
  80. agno/db/sqlite/async_sqlite.py +2911 -0
  81. agno/db/sqlite/schemas.py +181 -0
  82. agno/db/sqlite/sqlite.py +2908 -0
  83. agno/db/sqlite/utils.py +429 -0
  84. agno/db/surrealdb/__init__.py +3 -0
  85. agno/db/surrealdb/metrics.py +292 -0
  86. agno/db/surrealdb/models.py +334 -0
  87. agno/db/surrealdb/queries.py +71 -0
  88. agno/db/surrealdb/surrealdb.py +1908 -0
  89. agno/db/surrealdb/utils.py +147 -0
  90. agno/db/utils.py +118 -0
  91. agno/eval/__init__.py +24 -0
  92. agno/eval/accuracy.py +666 -276
  93. agno/eval/agent_as_judge.py +861 -0
  94. agno/eval/base.py +29 -0
  95. agno/eval/performance.py +779 -0
  96. agno/eval/reliability.py +241 -62
  97. agno/eval/utils.py +120 -0
  98. agno/exceptions.py +143 -1
  99. agno/filters.py +354 -0
  100. agno/guardrails/__init__.py +6 -0
  101. agno/guardrails/base.py +19 -0
  102. agno/guardrails/openai.py +144 -0
  103. agno/guardrails/pii.py +94 -0
  104. agno/guardrails/prompt_injection.py +52 -0
  105. agno/hooks/__init__.py +3 -0
  106. agno/hooks/decorator.py +164 -0
  107. agno/integrations/discord/__init__.py +3 -0
  108. agno/integrations/discord/client.py +203 -0
  109. agno/knowledge/__init__.py +5 -1
  110. agno/{document → knowledge}/chunking/agentic.py +22 -14
  111. agno/{document → knowledge}/chunking/document.py +2 -2
  112. agno/{document → knowledge}/chunking/fixed.py +7 -6
  113. agno/knowledge/chunking/markdown.py +151 -0
  114. agno/{document → knowledge}/chunking/recursive.py +15 -3
  115. agno/knowledge/chunking/row.py +39 -0
  116. agno/knowledge/chunking/semantic.py +91 -0
  117. agno/knowledge/chunking/strategy.py +165 -0
  118. agno/knowledge/content.py +74 -0
  119. agno/knowledge/document/__init__.py +5 -0
  120. agno/{document → knowledge/document}/base.py +12 -2
  121. agno/knowledge/embedder/__init__.py +5 -0
  122. agno/knowledge/embedder/aws_bedrock.py +343 -0
  123. agno/knowledge/embedder/azure_openai.py +210 -0
  124. agno/{embedder → knowledge/embedder}/base.py +8 -0
  125. agno/knowledge/embedder/cohere.py +323 -0
  126. agno/knowledge/embedder/fastembed.py +62 -0
  127. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  128. agno/knowledge/embedder/google.py +258 -0
  129. agno/knowledge/embedder/huggingface.py +94 -0
  130. agno/knowledge/embedder/jina.py +182 -0
  131. agno/knowledge/embedder/langdb.py +22 -0
  132. agno/knowledge/embedder/mistral.py +206 -0
  133. agno/knowledge/embedder/nebius.py +13 -0
  134. agno/knowledge/embedder/ollama.py +154 -0
  135. agno/knowledge/embedder/openai.py +195 -0
  136. agno/knowledge/embedder/sentence_transformer.py +63 -0
  137. agno/{embedder → knowledge/embedder}/together.py +1 -1
  138. agno/knowledge/embedder/vllm.py +262 -0
  139. agno/knowledge/embedder/voyageai.py +165 -0
  140. agno/knowledge/knowledge.py +3006 -0
  141. agno/knowledge/reader/__init__.py +7 -0
  142. agno/knowledge/reader/arxiv_reader.py +81 -0
  143. agno/knowledge/reader/base.py +95 -0
  144. agno/knowledge/reader/csv_reader.py +164 -0
  145. agno/knowledge/reader/docx_reader.py +82 -0
  146. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  147. agno/knowledge/reader/firecrawl_reader.py +201 -0
  148. agno/knowledge/reader/json_reader.py +88 -0
  149. agno/knowledge/reader/markdown_reader.py +137 -0
  150. agno/knowledge/reader/pdf_reader.py +431 -0
  151. agno/knowledge/reader/pptx_reader.py +101 -0
  152. agno/knowledge/reader/reader_factory.py +313 -0
  153. agno/knowledge/reader/s3_reader.py +89 -0
  154. agno/knowledge/reader/tavily_reader.py +193 -0
  155. agno/knowledge/reader/text_reader.py +127 -0
  156. agno/knowledge/reader/web_search_reader.py +325 -0
  157. agno/knowledge/reader/website_reader.py +455 -0
  158. agno/knowledge/reader/wikipedia_reader.py +91 -0
  159. agno/knowledge/reader/youtube_reader.py +78 -0
  160. agno/knowledge/remote_content/remote_content.py +88 -0
  161. agno/knowledge/reranker/__init__.py +3 -0
  162. agno/{reranker → knowledge/reranker}/base.py +1 -1
  163. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  164. agno/knowledge/reranker/infinity.py +195 -0
  165. agno/knowledge/reranker/sentence_transformer.py +54 -0
  166. agno/knowledge/types.py +39 -0
  167. agno/knowledge/utils.py +234 -0
  168. agno/media.py +439 -95
  169. agno/memory/__init__.py +16 -3
  170. agno/memory/manager.py +1474 -123
  171. agno/memory/strategies/__init__.py +15 -0
  172. agno/memory/strategies/base.py +66 -0
  173. agno/memory/strategies/summarize.py +196 -0
  174. agno/memory/strategies/types.py +37 -0
  175. agno/models/aimlapi/__init__.py +5 -0
  176. agno/models/aimlapi/aimlapi.py +62 -0
  177. agno/models/anthropic/__init__.py +4 -0
  178. agno/models/anthropic/claude.py +960 -496
  179. agno/models/aws/__init__.py +15 -0
  180. agno/models/aws/bedrock.py +686 -451
  181. agno/models/aws/claude.py +190 -183
  182. agno/models/azure/__init__.py +18 -1
  183. agno/models/azure/ai_foundry.py +489 -0
  184. agno/models/azure/openai_chat.py +89 -40
  185. agno/models/base.py +2477 -550
  186. agno/models/cerebras/__init__.py +12 -0
  187. agno/models/cerebras/cerebras.py +565 -0
  188. agno/models/cerebras/cerebras_openai.py +131 -0
  189. agno/models/cohere/__init__.py +4 -0
  190. agno/models/cohere/chat.py +306 -492
  191. agno/models/cometapi/__init__.py +5 -0
  192. agno/models/cometapi/cometapi.py +74 -0
  193. agno/models/dashscope/__init__.py +5 -0
  194. agno/models/dashscope/dashscope.py +90 -0
  195. agno/models/deepinfra/__init__.py +5 -0
  196. agno/models/deepinfra/deepinfra.py +45 -0
  197. agno/models/deepseek/__init__.py +4 -0
  198. agno/models/deepseek/deepseek.py +110 -9
  199. agno/models/fireworks/__init__.py +4 -0
  200. agno/models/fireworks/fireworks.py +19 -22
  201. agno/models/google/__init__.py +3 -7
  202. agno/models/google/gemini.py +1717 -662
  203. agno/models/google/utils.py +22 -0
  204. agno/models/groq/__init__.py +4 -0
  205. agno/models/groq/groq.py +391 -666
  206. agno/models/huggingface/__init__.py +4 -0
  207. agno/models/huggingface/huggingface.py +266 -538
  208. agno/models/ibm/__init__.py +5 -0
  209. agno/models/ibm/watsonx.py +432 -0
  210. agno/models/internlm/__init__.py +3 -0
  211. agno/models/internlm/internlm.py +20 -3
  212. agno/models/langdb/__init__.py +1 -0
  213. agno/models/langdb/langdb.py +60 -0
  214. agno/models/litellm/__init__.py +14 -0
  215. agno/models/litellm/chat.py +503 -0
  216. agno/models/litellm/litellm_openai.py +42 -0
  217. agno/models/llama_cpp/__init__.py +5 -0
  218. agno/models/llama_cpp/llama_cpp.py +22 -0
  219. agno/models/lmstudio/__init__.py +5 -0
  220. agno/models/lmstudio/lmstudio.py +25 -0
  221. agno/models/message.py +361 -39
  222. agno/models/meta/__init__.py +12 -0
  223. agno/models/meta/llama.py +502 -0
  224. agno/models/meta/llama_openai.py +79 -0
  225. agno/models/metrics.py +120 -0
  226. agno/models/mistral/__init__.py +4 -0
  227. agno/models/mistral/mistral.py +293 -393
  228. agno/models/nebius/__init__.py +3 -0
  229. agno/models/nebius/nebius.py +53 -0
  230. agno/models/nexus/__init__.py +3 -0
  231. agno/models/nexus/nexus.py +22 -0
  232. agno/models/nvidia/__init__.py +4 -0
  233. agno/models/nvidia/nvidia.py +22 -3
  234. agno/models/ollama/__init__.py +4 -2
  235. agno/models/ollama/chat.py +257 -492
  236. agno/models/openai/__init__.py +7 -0
  237. agno/models/openai/chat.py +725 -770
  238. agno/models/openai/like.py +16 -2
  239. agno/models/openai/responses.py +1121 -0
  240. agno/models/openrouter/__init__.py +4 -0
  241. agno/models/openrouter/openrouter.py +62 -5
  242. agno/models/perplexity/__init__.py +5 -0
  243. agno/models/perplexity/perplexity.py +203 -0
  244. agno/models/portkey/__init__.py +3 -0
  245. agno/models/portkey/portkey.py +82 -0
  246. agno/models/requesty/__init__.py +5 -0
  247. agno/models/requesty/requesty.py +69 -0
  248. agno/models/response.py +177 -7
  249. agno/models/sambanova/__init__.py +4 -0
  250. agno/models/sambanova/sambanova.py +23 -4
  251. agno/models/siliconflow/__init__.py +5 -0
  252. agno/models/siliconflow/siliconflow.py +42 -0
  253. agno/models/together/__init__.py +4 -0
  254. agno/models/together/together.py +21 -164
  255. agno/models/utils.py +266 -0
  256. agno/models/vercel/__init__.py +3 -0
  257. agno/models/vercel/v0.py +43 -0
  258. agno/models/vertexai/__init__.py +0 -1
  259. agno/models/vertexai/claude.py +190 -0
  260. agno/models/vllm/__init__.py +3 -0
  261. agno/models/vllm/vllm.py +83 -0
  262. agno/models/xai/__init__.py +2 -0
  263. agno/models/xai/xai.py +111 -7
  264. agno/os/__init__.py +3 -0
  265. agno/os/app.py +1027 -0
  266. agno/os/auth.py +244 -0
  267. agno/os/config.py +126 -0
  268. agno/os/interfaces/__init__.py +1 -0
  269. agno/os/interfaces/a2a/__init__.py +3 -0
  270. agno/os/interfaces/a2a/a2a.py +42 -0
  271. agno/os/interfaces/a2a/router.py +249 -0
  272. agno/os/interfaces/a2a/utils.py +924 -0
  273. agno/os/interfaces/agui/__init__.py +3 -0
  274. agno/os/interfaces/agui/agui.py +47 -0
  275. agno/os/interfaces/agui/router.py +147 -0
  276. agno/os/interfaces/agui/utils.py +574 -0
  277. agno/os/interfaces/base.py +25 -0
  278. agno/os/interfaces/slack/__init__.py +3 -0
  279. agno/os/interfaces/slack/router.py +148 -0
  280. agno/os/interfaces/slack/security.py +30 -0
  281. agno/os/interfaces/slack/slack.py +47 -0
  282. agno/os/interfaces/whatsapp/__init__.py +3 -0
  283. agno/os/interfaces/whatsapp/router.py +210 -0
  284. agno/os/interfaces/whatsapp/security.py +55 -0
  285. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  286. agno/os/mcp.py +293 -0
  287. agno/os/middleware/__init__.py +9 -0
  288. agno/os/middleware/jwt.py +797 -0
  289. agno/os/router.py +258 -0
  290. agno/os/routers/__init__.py +3 -0
  291. agno/os/routers/agents/__init__.py +3 -0
  292. agno/os/routers/agents/router.py +599 -0
  293. agno/os/routers/agents/schema.py +261 -0
  294. agno/os/routers/evals/__init__.py +3 -0
  295. agno/os/routers/evals/evals.py +450 -0
  296. agno/os/routers/evals/schemas.py +174 -0
  297. agno/os/routers/evals/utils.py +231 -0
  298. agno/os/routers/health.py +31 -0
  299. agno/os/routers/home.py +52 -0
  300. agno/os/routers/knowledge/__init__.py +3 -0
  301. agno/os/routers/knowledge/knowledge.py +1008 -0
  302. agno/os/routers/knowledge/schemas.py +178 -0
  303. agno/os/routers/memory/__init__.py +3 -0
  304. agno/os/routers/memory/memory.py +661 -0
  305. agno/os/routers/memory/schemas.py +88 -0
  306. agno/os/routers/metrics/__init__.py +3 -0
  307. agno/os/routers/metrics/metrics.py +190 -0
  308. agno/os/routers/metrics/schemas.py +47 -0
  309. agno/os/routers/session/__init__.py +3 -0
  310. agno/os/routers/session/session.py +997 -0
  311. agno/os/routers/teams/__init__.py +3 -0
  312. agno/os/routers/teams/router.py +512 -0
  313. agno/os/routers/teams/schema.py +257 -0
  314. agno/os/routers/traces/__init__.py +3 -0
  315. agno/os/routers/traces/schemas.py +414 -0
  316. agno/os/routers/traces/traces.py +499 -0
  317. agno/os/routers/workflows/__init__.py +3 -0
  318. agno/os/routers/workflows/router.py +624 -0
  319. agno/os/routers/workflows/schema.py +75 -0
  320. agno/os/schema.py +534 -0
  321. agno/os/scopes.py +469 -0
  322. agno/{playground → os}/settings.py +7 -15
  323. agno/os/utils.py +973 -0
  324. agno/reasoning/anthropic.py +80 -0
  325. agno/reasoning/azure_ai_foundry.py +67 -0
  326. agno/reasoning/deepseek.py +63 -0
  327. agno/reasoning/default.py +97 -0
  328. agno/reasoning/gemini.py +73 -0
  329. agno/reasoning/groq.py +71 -0
  330. agno/reasoning/helpers.py +24 -1
  331. agno/reasoning/ollama.py +67 -0
  332. agno/reasoning/openai.py +86 -0
  333. agno/reasoning/step.py +2 -1
  334. agno/reasoning/vertexai.py +76 -0
  335. agno/run/__init__.py +6 -0
  336. agno/run/agent.py +822 -0
  337. agno/run/base.py +247 -0
  338. agno/run/cancel.py +81 -0
  339. agno/run/requirement.py +181 -0
  340. agno/run/team.py +767 -0
  341. agno/run/workflow.py +708 -0
  342. agno/session/__init__.py +10 -0
  343. agno/session/agent.py +260 -0
  344. agno/session/summary.py +265 -0
  345. agno/session/team.py +342 -0
  346. agno/session/workflow.py +501 -0
  347. agno/table.py +10 -0
  348. agno/team/__init__.py +37 -0
  349. agno/team/team.py +9536 -0
  350. agno/tools/__init__.py +7 -0
  351. agno/tools/agentql.py +120 -0
  352. agno/tools/airflow.py +22 -12
  353. agno/tools/api.py +122 -0
  354. agno/tools/apify.py +276 -83
  355. agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
  356. agno/tools/aws_lambda.py +28 -7
  357. agno/tools/aws_ses.py +66 -0
  358. agno/tools/baidusearch.py +11 -4
  359. agno/tools/bitbucket.py +292 -0
  360. agno/tools/brandfetch.py +213 -0
  361. agno/tools/bravesearch.py +106 -0
  362. agno/tools/brightdata.py +367 -0
  363. agno/tools/browserbase.py +209 -0
  364. agno/tools/calcom.py +32 -23
  365. agno/tools/calculator.py +24 -37
  366. agno/tools/cartesia.py +187 -0
  367. agno/tools/{clickup_tool.py → clickup.py} +17 -28
  368. agno/tools/confluence.py +91 -26
  369. agno/tools/crawl4ai.py +139 -43
  370. agno/tools/csv_toolkit.py +28 -22
  371. agno/tools/dalle.py +36 -22
  372. agno/tools/daytona.py +475 -0
  373. agno/tools/decorator.py +169 -14
  374. agno/tools/desi_vocal.py +23 -11
  375. agno/tools/discord.py +32 -29
  376. agno/tools/docker.py +716 -0
  377. agno/tools/duckdb.py +76 -81
  378. agno/tools/duckduckgo.py +43 -40
  379. agno/tools/e2b.py +703 -0
  380. agno/tools/eleven_labs.py +65 -54
  381. agno/tools/email.py +13 -5
  382. agno/tools/evm.py +129 -0
  383. agno/tools/exa.py +324 -42
  384. agno/tools/fal.py +39 -35
  385. agno/tools/file.py +196 -30
  386. agno/tools/file_generation.py +356 -0
  387. agno/tools/financial_datasets.py +288 -0
  388. agno/tools/firecrawl.py +108 -33
  389. agno/tools/function.py +960 -122
  390. agno/tools/giphy.py +34 -12
  391. agno/tools/github.py +1294 -97
  392. agno/tools/gmail.py +922 -0
  393. agno/tools/google_bigquery.py +117 -0
  394. agno/tools/google_drive.py +271 -0
  395. agno/tools/google_maps.py +253 -0
  396. agno/tools/googlecalendar.py +607 -107
  397. agno/tools/googlesheets.py +377 -0
  398. agno/tools/hackernews.py +20 -12
  399. agno/tools/jina.py +24 -14
  400. agno/tools/jira.py +48 -19
  401. agno/tools/knowledge.py +218 -0
  402. agno/tools/linear.py +82 -43
  403. agno/tools/linkup.py +58 -0
  404. agno/tools/local_file_system.py +15 -7
  405. agno/tools/lumalab.py +41 -26
  406. agno/tools/mcp/__init__.py +10 -0
  407. agno/tools/mcp/mcp.py +331 -0
  408. agno/tools/mcp/multi_mcp.py +347 -0
  409. agno/tools/mcp/params.py +24 -0
  410. agno/tools/mcp_toolbox.py +284 -0
  411. agno/tools/mem0.py +193 -0
  412. agno/tools/memory.py +419 -0
  413. agno/tools/mlx_transcribe.py +11 -9
  414. agno/tools/models/azure_openai.py +190 -0
  415. agno/tools/models/gemini.py +203 -0
  416. agno/tools/models/groq.py +158 -0
  417. agno/tools/models/morph.py +186 -0
  418. agno/tools/models/nebius.py +124 -0
  419. agno/tools/models_labs.py +163 -82
  420. agno/tools/moviepy_video.py +18 -13
  421. agno/tools/nano_banana.py +151 -0
  422. agno/tools/neo4j.py +134 -0
  423. agno/tools/newspaper.py +15 -4
  424. agno/tools/newspaper4k.py +19 -6
  425. agno/tools/notion.py +204 -0
  426. agno/tools/openai.py +181 -17
  427. agno/tools/openbb.py +27 -20
  428. agno/tools/opencv.py +321 -0
  429. agno/tools/openweather.py +233 -0
  430. agno/tools/oxylabs.py +385 -0
  431. agno/tools/pandas.py +25 -15
  432. agno/tools/parallel.py +314 -0
  433. agno/tools/postgres.py +238 -185
  434. agno/tools/pubmed.py +125 -13
  435. agno/tools/python.py +48 -35
  436. agno/tools/reasoning.py +283 -0
  437. agno/tools/reddit.py +207 -29
  438. agno/tools/redshift.py +406 -0
  439. agno/tools/replicate.py +69 -26
  440. agno/tools/resend.py +11 -6
  441. agno/tools/scrapegraph.py +179 -19
  442. agno/tools/searxng.py +23 -31
  443. agno/tools/serpapi.py +15 -10
  444. agno/tools/serper.py +255 -0
  445. agno/tools/shell.py +23 -12
  446. agno/tools/shopify.py +1519 -0
  447. agno/tools/slack.py +56 -14
  448. agno/tools/sleep.py +8 -6
  449. agno/tools/spider.py +35 -11
  450. agno/tools/spotify.py +919 -0
  451. agno/tools/sql.py +34 -19
  452. agno/tools/tavily.py +158 -8
  453. agno/tools/telegram.py +18 -8
  454. agno/tools/todoist.py +218 -0
  455. agno/tools/toolkit.py +134 -9
  456. agno/tools/trafilatura.py +388 -0
  457. agno/tools/trello.py +25 -28
  458. agno/tools/twilio.py +18 -9
  459. agno/tools/user_control_flow.py +78 -0
  460. agno/tools/valyu.py +228 -0
  461. agno/tools/visualization.py +467 -0
  462. agno/tools/webbrowser.py +28 -0
  463. agno/tools/webex.py +76 -0
  464. agno/tools/website.py +23 -19
  465. agno/tools/webtools.py +45 -0
  466. agno/tools/whatsapp.py +286 -0
  467. agno/tools/wikipedia.py +28 -19
  468. agno/tools/workflow.py +285 -0
  469. agno/tools/{twitter.py → x.py} +142 -46
  470. agno/tools/yfinance.py +41 -39
  471. agno/tools/youtube.py +34 -17
  472. agno/tools/zendesk.py +15 -5
  473. agno/tools/zep.py +454 -0
  474. agno/tools/zoom.py +86 -37
  475. agno/tracing/__init__.py +12 -0
  476. agno/tracing/exporter.py +157 -0
  477. agno/tracing/schemas.py +276 -0
  478. agno/tracing/setup.py +111 -0
  479. agno/utils/agent.py +938 -0
  480. agno/utils/audio.py +37 -1
  481. agno/utils/certs.py +27 -0
  482. agno/utils/code_execution.py +11 -0
  483. agno/utils/common.py +103 -20
  484. agno/utils/cryptography.py +22 -0
  485. agno/utils/dttm.py +33 -0
  486. agno/utils/events.py +700 -0
  487. agno/utils/functions.py +107 -37
  488. agno/utils/gemini.py +426 -0
  489. agno/utils/hooks.py +171 -0
  490. agno/utils/http.py +185 -0
  491. agno/utils/json_schema.py +159 -37
  492. agno/utils/knowledge.py +36 -0
  493. agno/utils/location.py +19 -0
  494. agno/utils/log.py +221 -8
  495. agno/utils/mcp.py +214 -0
  496. agno/utils/media.py +335 -14
  497. agno/utils/merge_dict.py +22 -1
  498. agno/utils/message.py +77 -2
  499. agno/utils/models/ai_foundry.py +50 -0
  500. agno/utils/models/claude.py +373 -0
  501. agno/utils/models/cohere.py +94 -0
  502. agno/utils/models/llama.py +85 -0
  503. agno/utils/models/mistral.py +100 -0
  504. agno/utils/models/openai_responses.py +140 -0
  505. agno/utils/models/schema_utils.py +153 -0
  506. agno/utils/models/watsonx.py +41 -0
  507. agno/utils/openai.py +257 -0
  508. agno/utils/pickle.py +1 -1
  509. agno/utils/pprint.py +124 -8
  510. agno/utils/print_response/agent.py +930 -0
  511. agno/utils/print_response/team.py +1914 -0
  512. agno/utils/print_response/workflow.py +1668 -0
  513. agno/utils/prompts.py +111 -0
  514. agno/utils/reasoning.py +108 -0
  515. agno/utils/response.py +163 -0
  516. agno/utils/serialize.py +32 -0
  517. agno/utils/shell.py +4 -4
  518. agno/utils/streamlit.py +487 -0
  519. agno/utils/string.py +204 -51
  520. agno/utils/team.py +139 -0
  521. agno/utils/timer.py +9 -2
  522. agno/utils/tokens.py +657 -0
  523. agno/utils/tools.py +19 -1
  524. agno/utils/whatsapp.py +305 -0
  525. agno/utils/yaml_io.py +3 -3
  526. agno/vectordb/__init__.py +2 -0
  527. agno/vectordb/base.py +87 -9
  528. agno/vectordb/cassandra/__init__.py +5 -1
  529. agno/vectordb/cassandra/cassandra.py +383 -27
  530. agno/vectordb/chroma/__init__.py +4 -0
  531. agno/vectordb/chroma/chromadb.py +748 -83
  532. agno/vectordb/clickhouse/__init__.py +7 -1
  533. agno/vectordb/clickhouse/clickhousedb.py +554 -53
  534. agno/vectordb/couchbase/__init__.py +3 -0
  535. agno/vectordb/couchbase/couchbase.py +1446 -0
  536. agno/vectordb/lancedb/__init__.py +5 -0
  537. agno/vectordb/lancedb/lance_db.py +730 -98
  538. agno/vectordb/langchaindb/__init__.py +5 -0
  539. agno/vectordb/langchaindb/langchaindb.py +163 -0
  540. agno/vectordb/lightrag/__init__.py +5 -0
  541. agno/vectordb/lightrag/lightrag.py +388 -0
  542. agno/vectordb/llamaindex/__init__.py +3 -0
  543. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  544. agno/vectordb/milvus/__init__.py +3 -0
  545. agno/vectordb/milvus/milvus.py +966 -78
  546. agno/vectordb/mongodb/__init__.py +9 -1
  547. agno/vectordb/mongodb/mongodb.py +1175 -172
  548. agno/vectordb/pgvector/__init__.py +8 -0
  549. agno/vectordb/pgvector/pgvector.py +599 -115
  550. agno/vectordb/pineconedb/__init__.py +5 -1
  551. agno/vectordb/pineconedb/pineconedb.py +406 -43
  552. agno/vectordb/qdrant/__init__.py +4 -0
  553. agno/vectordb/qdrant/qdrant.py +914 -61
  554. agno/vectordb/redis/__init__.py +9 -0
  555. agno/vectordb/redis/redisdb.py +682 -0
  556. agno/vectordb/singlestore/__init__.py +8 -1
  557. agno/vectordb/singlestore/singlestore.py +771 -0
  558. agno/vectordb/surrealdb/__init__.py +3 -0
  559. agno/vectordb/surrealdb/surrealdb.py +663 -0
  560. agno/vectordb/upstashdb/__init__.py +5 -0
  561. agno/vectordb/upstashdb/upstashdb.py +718 -0
  562. agno/vectordb/weaviate/__init__.py +8 -0
  563. agno/vectordb/weaviate/index.py +15 -0
  564. agno/vectordb/weaviate/weaviate.py +1009 -0
  565. agno/workflow/__init__.py +23 -1
  566. agno/workflow/agent.py +299 -0
  567. agno/workflow/condition.py +759 -0
  568. agno/workflow/loop.py +756 -0
  569. agno/workflow/parallel.py +853 -0
  570. agno/workflow/router.py +723 -0
  571. agno/workflow/step.py +1564 -0
  572. agno/workflow/steps.py +613 -0
  573. agno/workflow/types.py +556 -0
  574. agno/workflow/workflow.py +4327 -514
  575. agno-2.3.13.dist-info/METADATA +639 -0
  576. agno-2.3.13.dist-info/RECORD +613 -0
  577. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
  578. agno-2.3.13.dist-info/licenses/LICENSE +201 -0
  579. agno/api/playground.py +0 -91
  580. agno/api/schemas/playground.py +0 -22
  581. agno/api/schemas/user.py +0 -22
  582. agno/api/schemas/workspace.py +0 -46
  583. agno/api/user.py +0 -160
  584. agno/api/workspace.py +0 -151
  585. agno/cli/auth_server.py +0 -118
  586. agno/cli/config.py +0 -275
  587. agno/cli/console.py +0 -88
  588. agno/cli/credentials.py +0 -23
  589. agno/cli/entrypoint.py +0 -571
  590. agno/cli/operator.py +0 -355
  591. agno/cli/settings.py +0 -85
  592. agno/cli/ws/ws_cli.py +0 -817
  593. agno/constants.py +0 -13
  594. agno/document/__init__.py +0 -1
  595. agno/document/chunking/semantic.py +0 -47
  596. agno/document/chunking/strategy.py +0 -31
  597. agno/document/reader/__init__.py +0 -1
  598. agno/document/reader/arxiv_reader.py +0 -41
  599. agno/document/reader/base.py +0 -22
  600. agno/document/reader/csv_reader.py +0 -84
  601. agno/document/reader/docx_reader.py +0 -46
  602. agno/document/reader/firecrawl_reader.py +0 -99
  603. agno/document/reader/json_reader.py +0 -43
  604. agno/document/reader/pdf_reader.py +0 -219
  605. agno/document/reader/s3/pdf_reader.py +0 -46
  606. agno/document/reader/s3/text_reader.py +0 -51
  607. agno/document/reader/text_reader.py +0 -41
  608. agno/document/reader/website_reader.py +0 -175
  609. agno/document/reader/youtube_reader.py +0 -50
  610. agno/embedder/__init__.py +0 -1
  611. agno/embedder/azure_openai.py +0 -86
  612. agno/embedder/cohere.py +0 -72
  613. agno/embedder/fastembed.py +0 -37
  614. agno/embedder/google.py +0 -73
  615. agno/embedder/huggingface.py +0 -54
  616. agno/embedder/mistral.py +0 -80
  617. agno/embedder/ollama.py +0 -57
  618. agno/embedder/openai.py +0 -74
  619. agno/embedder/sentence_transformer.py +0 -38
  620. agno/embedder/voyageai.py +0 -64
  621. agno/eval/perf.py +0 -201
  622. agno/file/__init__.py +0 -1
  623. agno/file/file.py +0 -16
  624. agno/file/local/csv.py +0 -32
  625. agno/file/local/txt.py +0 -19
  626. agno/infra/app.py +0 -240
  627. agno/infra/base.py +0 -144
  628. agno/infra/context.py +0 -20
  629. agno/infra/db_app.py +0 -52
  630. agno/infra/resource.py +0 -205
  631. agno/infra/resources.py +0 -55
  632. agno/knowledge/agent.py +0 -230
  633. agno/knowledge/arxiv.py +0 -22
  634. agno/knowledge/combined.py +0 -22
  635. agno/knowledge/csv.py +0 -28
  636. agno/knowledge/csv_url.py +0 -19
  637. agno/knowledge/document.py +0 -20
  638. agno/knowledge/docx.py +0 -30
  639. agno/knowledge/json.py +0 -28
  640. agno/knowledge/langchain.py +0 -71
  641. agno/knowledge/llamaindex.py +0 -66
  642. agno/knowledge/pdf.py +0 -28
  643. agno/knowledge/pdf_url.py +0 -26
  644. agno/knowledge/s3/base.py +0 -60
  645. agno/knowledge/s3/pdf.py +0 -21
  646. agno/knowledge/s3/text.py +0 -23
  647. agno/knowledge/text.py +0 -30
  648. agno/knowledge/website.py +0 -88
  649. agno/knowledge/wikipedia.py +0 -31
  650. agno/knowledge/youtube.py +0 -22
  651. agno/memory/agent.py +0 -392
  652. agno/memory/classifier.py +0 -104
  653. agno/memory/db/__init__.py +0 -1
  654. agno/memory/db/base.py +0 -42
  655. agno/memory/db/mongodb.py +0 -189
  656. agno/memory/db/postgres.py +0 -203
  657. agno/memory/db/sqlite.py +0 -193
  658. agno/memory/memory.py +0 -15
  659. agno/memory/row.py +0 -36
  660. agno/memory/summarizer.py +0 -192
  661. agno/memory/summary.py +0 -19
  662. agno/memory/workflow.py +0 -38
  663. agno/models/google/gemini_openai.py +0 -26
  664. agno/models/ollama/hermes.py +0 -221
  665. agno/models/ollama/tools.py +0 -362
  666. agno/models/vertexai/gemini.py +0 -595
  667. agno/playground/__init__.py +0 -3
  668. agno/playground/async_router.py +0 -421
  669. agno/playground/deploy.py +0 -249
  670. agno/playground/operator.py +0 -92
  671. agno/playground/playground.py +0 -91
  672. agno/playground/schemas.py +0 -76
  673. agno/playground/serve.py +0 -55
  674. agno/playground/sync_router.py +0 -405
  675. agno/reasoning/agent.py +0 -68
  676. agno/run/response.py +0 -112
  677. agno/storage/agent/__init__.py +0 -0
  678. agno/storage/agent/base.py +0 -38
  679. agno/storage/agent/dynamodb.py +0 -350
  680. agno/storage/agent/json.py +0 -92
  681. agno/storage/agent/mongodb.py +0 -228
  682. agno/storage/agent/postgres.py +0 -367
  683. agno/storage/agent/session.py +0 -79
  684. agno/storage/agent/singlestore.py +0 -303
  685. agno/storage/agent/sqlite.py +0 -357
  686. agno/storage/agent/yaml.py +0 -93
  687. agno/storage/workflow/__init__.py +0 -0
  688. agno/storage/workflow/base.py +0 -40
  689. agno/storage/workflow/mongodb.py +0 -233
  690. agno/storage/workflow/postgres.py +0 -366
  691. agno/storage/workflow/session.py +0 -60
  692. agno/storage/workflow/sqlite.py +0 -359
  693. agno/tools/googlesearch.py +0 -88
  694. agno/utils/defaults.py +0 -57
  695. agno/utils/filesystem.py +0 -39
  696. agno/utils/git.py +0 -52
  697. agno/utils/json_io.py +0 -30
  698. agno/utils/load_env.py +0 -19
  699. agno/utils/py_io.py +0 -19
  700. agno/utils/pyproject.py +0 -18
  701. agno/utils/resource_filter.py +0 -31
  702. agno/vectordb/singlestore/s2vectordb.py +0 -390
  703. agno/vectordb/singlestore/s2vectordb2.py +0 -355
  704. agno/workspace/__init__.py +0 -0
  705. agno/workspace/config.py +0 -325
  706. agno/workspace/enums.py +0 -6
  707. agno/workspace/helpers.py +0 -48
  708. agno/workspace/operator.py +0 -758
  709. agno/workspace/settings.py +0 -63
  710. agno-0.1.2.dist-info/LICENSE +0 -375
  711. agno-0.1.2.dist-info/METADATA +0 -502
  712. agno-0.1.2.dist-info/RECORD +0 -352
  713. agno-0.1.2.dist-info/entry_points.txt +0 -3
  714. /agno/{cli → db/migrations}/__init__.py +0 -0
  715. /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
  716. /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
  717. /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
  718. /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
  719. /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
  720. /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
  721. /agno/{reranker → utils/models}/__init__.py +0 -0
  722. /agno/{storage → utils/print_response}/__init__.py +0 -0
  723. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/tools/postgres.py CHANGED
@@ -1,35 +1,46 @@
1
- from typing import Any, Dict, Optional
1
+ import csv
2
+ from typing import Any, Dict, List, Optional
2
3
 
3
4
  try:
4
- import psycopg2
5
+ import psycopg
6
+ from psycopg import sql
7
+ from psycopg.connection import Connection as PgConnection
8
+ from psycopg.rows import DictRow, dict_row
5
9
  except ImportError:
6
- raise ImportError(
7
- "`psycopg2` not installed. Please install using `pip install psycopg2`. If you face issues, try `pip install psycopg2-binary`."
8
- )
10
+ raise ImportError("`psycopg` not installed. Please install using `pip install 'psycopg-binary'`.")
9
11
 
10
12
  from agno.tools import Toolkit
11
- from agno.utils.log import logger
13
+ from agno.utils.log import log_debug, log_error
12
14
 
13
15
 
14
16
  class PostgresTools(Toolkit):
15
- """A basic tool to connect to a PostgreSQL database and perform read-only operations on it."""
17
+ """
18
+ A toolkit for interacting with PostgreSQL databases.
19
+
20
+ Args:
21
+ connection (Optional[PgConnection[DictRow]]): Existing database connection to reuse.
22
+ db_name (Optional[str]): Database name to connect to.
23
+ user (Optional[str]): Username for authentication.
24
+ password (Optional[str]): Password for authentication.
25
+ host (Optional[str]): PostgreSQL server hostname.
26
+ port (Optional[int]): PostgreSQL server port number.
27
+ table_schema (str): Default schema for table operations. Default is "public".
28
+ """
29
+
30
+ _requires_connect: bool = True
16
31
 
17
32
  def __init__(
18
33
  self,
19
- connection: Optional[psycopg2.extensions.connection] = None,
34
+ connection: Optional[PgConnection[DictRow]] = None,
20
35
  db_name: Optional[str] = None,
21
36
  user: Optional[str] = None,
22
37
  password: Optional[str] = None,
23
38
  host: Optional[str] = None,
24
39
  port: Optional[int] = None,
25
- run_queries: bool = True,
26
- inspect_queries: bool = False,
27
- summarize_tables: bool = True,
28
- export_tables: bool = False,
29
40
  table_schema: str = "public",
41
+ **kwargs,
30
42
  ):
31
- super().__init__(name="postgres_tools")
32
- self._connection: Optional[psycopg2.extensions.connection] = connection
43
+ self._connection: Optional[PgConnection[DictRow]] = connection
33
44
  self.db_name: Optional[str] = db_name
34
45
  self.user: Optional[str] = user
35
46
  self.password: Optional[str] = password
@@ -37,208 +48,250 @@ class PostgresTools(Toolkit):
37
48
  self.port: Optional[int] = port
38
49
  self.table_schema: str = table_schema
39
50
 
40
- self.register(self.show_tables)
41
- self.register(self.describe_table)
42
- if inspect_queries:
43
- self.register(self.inspect_query)
44
- if run_queries:
45
- self.register(self.run_query)
46
- if summarize_tables:
47
- self.register(self.summarize_table)
48
- if export_tables:
49
- self.register(self.export_table_to_path)
51
+ tools: List[Any] = [
52
+ self.show_tables,
53
+ self.describe_table,
54
+ self.summarize_table,
55
+ self.inspect_query,
56
+ self.run_query,
57
+ self.export_table_to_path,
58
+ ]
59
+
60
+ super().__init__(name="postgres_tools", tools=tools, **kwargs)
61
+
62
+ def connect(self) -> PgConnection[DictRow]:
63
+ """
64
+ Establish a connection to the PostgreSQL database.
65
+
66
+ Returns:
67
+ The database connection object.
68
+ """
69
+ if self._connection is not None and not self._connection.closed:
70
+ log_debug("Connection already established, reusing existing connection")
71
+ return self._connection
72
+
73
+ log_debug("Establishing new PostgreSQL connection.")
74
+ connection_kwargs: Dict[str, Any] = {"row_factory": dict_row}
75
+ if self.db_name:
76
+ connection_kwargs["dbname"] = self.db_name
77
+ if self.user:
78
+ connection_kwargs["user"] = self.user
79
+ if self.password:
80
+ connection_kwargs["password"] = self.password
81
+ if self.host:
82
+ connection_kwargs["host"] = self.host
83
+ if self.port:
84
+ connection_kwargs["port"] = self.port
85
+
86
+ connection_kwargs["options"] = f"-c search_path={self.table_schema}"
87
+
88
+ self._connection = psycopg.connect(**connection_kwargs)
89
+ self._connection.read_only = True
90
+ return self._connection
91
+
92
+ def close(self) -> None:
93
+ """Closes the database connection if it's open."""
94
+ if self._connection and not self._connection.closed:
95
+ log_debug("Closing PostgreSQL connection.")
96
+ self._connection.close()
97
+ self._connection = None
50
98
 
51
99
  @property
52
- def connection(self) -> psycopg2.extensions.connection:
100
+ def is_connected(self) -> bool:
101
+ """Check if a connection is currently established."""
102
+ return self._connection is not None and not self._connection.closed
103
+
104
+ def _ensure_connection(self) -> PgConnection[DictRow]:
53
105
  """
54
- Returns the Postgres psycopg2 connection.
106
+ Ensure a connection exists, creating one if necessary.
55
107
 
56
- :return psycopg2.extensions.connection: psycopg2 connection
108
+ Returns:
109
+ The database connection object.
57
110
  """
58
- if self._connection is None:
59
- connection_kwargs: Dict[str, Any] = {}
60
- if self.db_name is not None:
61
- connection_kwargs["database"] = self.db_name
62
- if self.user is not None:
63
- connection_kwargs["user"] = self.user
64
- if self.password is not None:
65
- connection_kwargs["password"] = self.password
66
- if self.host is not None:
67
- connection_kwargs["host"] = self.host
68
- if self.port is not None:
69
- connection_kwargs["port"] = self.port
70
- if self.table_schema is not None:
71
- connection_kwargs["options"] = f"-c search_path={self.table_schema}"
72
-
73
- self._connection = psycopg2.connect(**connection_kwargs)
74
- self._connection.set_session(readonly=True)
111
+ if not self.is_connected:
112
+ return self.connect()
113
+ return self._connection # type: ignore
75
114
 
76
- return self._connection
115
+ def __enter__(self):
116
+ return self.connect()
117
+
118
+ def __exit__(self, exc_type, exc_val, exc_tb):
119
+ if self.is_connected:
120
+ self.close()
121
+
122
+ def _execute_query(self, query: str, params: Optional[tuple] = None) -> str:
123
+ try:
124
+ connection = self._ensure_connection()
125
+ with connection.cursor() as cursor:
126
+ log_debug("Running PostgreSQL query")
127
+ cursor.execute(query, params)
128
+
129
+ if cursor.description is None:
130
+ return cursor.statusmessage or "Query executed successfully with no output."
131
+
132
+ columns = [desc[0] for desc in cursor.description]
133
+ rows = cursor.fetchall()
134
+
135
+ if not rows:
136
+ return f"Query returned no results.\nColumns: {', '.join(columns)}"
137
+
138
+ header = ",".join(columns)
139
+ data_rows = [",".join(map(str, row.values())) for row in rows]
140
+ return f"{header}\n" + "\n".join(data_rows)
141
+
142
+ except psycopg.Error as e:
143
+ log_error(f"Database error: {e}")
144
+ if self._connection and not self._connection.closed:
145
+ self._connection.rollback()
146
+ return f"Error executing query: {e}"
147
+ except Exception as e:
148
+ log_error(f"An unexpected error occurred: {e}")
149
+ return f"An unexpected error occurred: {e}"
77
150
 
78
151
  def show_tables(self) -> str:
79
- """Function to show tables in the database
152
+ """Lists all tables in the configured schema."""
80
153
 
81
- :return: List of tables in the database
82
- """
83
- stmt = f"SELECT table_name FROM information_schema.tables WHERE table_schema = '{self.table_schema}';"
84
- tables = self.run_query(stmt)
85
- logger.debug(f"Tables: {tables}")
86
- return tables
154
+ stmt = "SELECT table_name FROM information_schema.tables WHERE table_schema = %s;"
155
+ return self._execute_query(stmt, (self.table_schema,))
87
156
 
88
157
  def describe_table(self, table: str) -> str:
89
- """Function to describe a table
90
-
91
- :param table: Table to describe
92
- :return: Description of the table
93
158
  """
94
- stmt = f"SELECT column_name, data_type, character_maximum_length FROM information_schema.columns WHERE table_name = '{table}' AND table_schema = '{self.table_schema}';"
95
- table_description = self.run_query(stmt)
159
+ Provides the schema (column name, data type, is nullable) for a given table.
96
160
 
97
- logger.debug(f"Table description: {table_description}")
98
- return f"{table}\n{table_description}"
161
+ Args:
162
+ table: The name of the table to describe.
99
163
 
100
- def summarize_table(self, table: str) -> str:
101
- """Function to compute a number of aggregates over a table.
102
- The function launches a query that computes a number of aggregates over all columns,
103
- including min, max, avg, std and approx_unique.
104
-
105
- :param table: Table to summarize
106
- :return: Summary of the table
164
+ Returns:
165
+ A string describing the table's columns and data types.
107
166
  """
108
- stmt = f"""WITH column_stats AS (
109
- SELECT
110
- column_name,
111
- data_type
112
- FROM
113
- information_schema.columns
114
- WHERE
115
- table_name = '{table}'
116
- AND table_schema = '{self.table_schema}'
117
- )
118
- SELECT
119
- column_name,
120
- data_type,
121
- COUNT(COALESCE(column_name::text, '')) AS non_null_count,
122
- COUNT(*) - COUNT(COALESCE(column_name::text, '')) AS null_count,
123
- SUM(COALESCE(column_name::numeric, 0)) AS sum,
124
- AVG(COALESCE(column_name::numeric, 0)) AS mean,
125
- MIN(column_name::numeric) AS min,
126
- MAX(column_name::numeric) AS max,
127
- STDDEV(COALESCE(column_name::numeric, 0)) AS stddev
128
- FROM
129
- column_stats,
130
- LATERAL (
131
- SELECT
132
- *
133
- FROM
134
- {table}
135
- ) AS tbl
136
- WHERE
137
- data_type IN ('integer', 'numeric', 'real', 'double precision')
138
- GROUP BY
139
- column_name, data_type
140
- UNION ALL
141
- SELECT
142
- column_name,
143
- data_type,
144
- COUNT(COALESCE(column_name::text, '')) AS non_null_count,
145
- COUNT(*) - COUNT(COALESCE(column_name::text, '')) AS null_count,
146
- NULL AS sum,
147
- NULL AS mean,
148
- NULL AS min,
149
- NULL AS max,
150
- NULL AS stddev
151
- FROM
152
- column_stats,
153
- LATERAL (
154
- SELECT
155
- *
156
- FROM
157
- {table}
158
- ) AS tbl
159
- WHERE
160
- data_type NOT IN ('integer', 'numeric', 'real', 'double precision')
161
- GROUP BY
162
- column_name, data_type;
167
+ stmt = """
168
+ SELECT column_name, data_type, is_nullable
169
+ FROM information_schema.columns
170
+ WHERE table_schema = %s AND table_name = %s;
163
171
  """
164
- table_summary = self.run_query(stmt)
172
+ return self._execute_query(stmt, (self.table_schema, table))
165
173
 
166
- logger.debug(f"Table summary: {table_summary}")
167
- return table_summary
174
+ def summarize_table(self, table: str) -> str:
175
+ """
176
+ Computes and returns key summary statistics for a table's columns.
168
177
 
169
- def inspect_query(self, query: str) -> str:
170
- """Function to inspect a query and return the query plan. Always inspect your query before running them.
178
+ Args:
179
+ table: The name of the table to summarize.
171
180
 
172
- :param query: Query to inspect
173
- :return: Query plan
181
+ Returns:
182
+ A string containing a summary of the table.
174
183
  """
175
- stmt = f"EXPLAIN {query};"
176
- explain_plan = self.run_query(stmt)
184
+ try:
185
+ connection = self._ensure_connection()
186
+ with connection.cursor() as cursor:
187
+ # First, get column information using a parameterized query
188
+ schema_query = """
189
+ SELECT column_name, data_type
190
+ FROM information_schema.columns
191
+ WHERE table_schema = %s AND table_name = %s;
192
+ """
193
+ cursor.execute(schema_query, (self.table_schema, table))
194
+ columns = cursor.fetchall()
195
+ if not columns:
196
+ return f"Error: Table '{table}' not found in schema '{self.table_schema}'."
197
+
198
+ summary_parts = [f"Summary for table: {table}\n"]
199
+ table_identifier = sql.Identifier(self.table_schema, table)
200
+
201
+ for col in columns:
202
+ col_name, data_type = col["column_name"], col["data_type"]
203
+ col_identifier = sql.Identifier(col_name)
204
+
205
+ query = None
206
+ if any(
207
+ t in data_type for t in ["integer", "numeric", "real", "double precision", "bigint", "smallint"]
208
+ ):
209
+ query = sql.SQL("""
210
+ SELECT
211
+ COUNT(*) AS total_rows,
212
+ COUNT({col}) AS non_null_rows,
213
+ MIN({col}) AS min,
214
+ MAX({col}) AS max,
215
+ AVG({col}) AS average,
216
+ STDDEV({col}) AS std_deviation
217
+ FROM {tbl};
218
+ """).format(col=col_identifier, tbl=table_identifier)
219
+ elif any(t in data_type for t in ["char", "text", "uuid"]):
220
+ query = sql.SQL("""
221
+ SELECT
222
+ COUNT(*) AS total_rows,
223
+ COUNT({col}) AS non_null_rows,
224
+ COUNT(DISTINCT {col}) AS unique_values,
225
+ AVG(LENGTH({col}::text)) as avg_length
226
+ FROM {tbl};
227
+ """).format(col=col_identifier, tbl=table_identifier)
228
+
229
+ if query:
230
+ cursor.execute(query)
231
+ stats = cursor.fetchone()
232
+ summary_parts.append(f"\n--- Column: {col_name} (Type: {data_type}) ---")
233
+ if stats is not None:
234
+ for key, value in stats.items():
235
+ val_str = (
236
+ f"{value:.2f}" if isinstance(value, float) and value is not None else str(value)
237
+ )
238
+ summary_parts.append(f" {key}: {val_str}")
239
+ else:
240
+ summary_parts.append(" No statistics available")
177
241
 
178
- logger.debug(f"Explain plan: {explain_plan}")
179
- return explain_plan
242
+ return "\n".join(summary_parts)
180
243
 
181
- def export_table_to_path(self, table: str, path: Optional[str] = None) -> str:
182
- """Save a table in CSV format.
183
- If the path is provided, the table will be saved under that path.
184
- Eg: If path is /tmp, the table will be saved as /tmp/table.csv
185
- Otherwise it will be saved in the current directory
244
+ except psycopg.Error as e:
245
+ return f"Error summarizing table: {e}"
186
246
 
187
- :param table: Table to export
188
- :param path: Path to export to
189
- :return: None
247
+ def inspect_query(self, query: str) -> str:
190
248
  """
249
+ Shows the execution plan for a SQL query (using EXPLAIN).
191
250
 
192
- logger.debug(f"Exporting Table {table} as CSV to path {path}")
193
- if path is None:
194
- path = f"{table}.csv"
195
- else:
196
- path = f"{path}/{table}.csv"
197
-
198
- export_statement = f"COPY {self.table_schema}.{table} TO '{path}' DELIMITER ',' CSV HEADER;"
199
- result = self.run_query(export_statement)
200
- logger.debug(f"Exported {table} to {path}/{table}")
201
-
202
- return result
251
+ :param query: The SQL query to inspect.
252
+ :return: The query's execution plan.
253
+ """
254
+ return self._execute_query(f"EXPLAIN {query}")
203
255
 
204
- def run_query(self, query: str) -> str:
205
- """Function that runs a query and returns the result.
256
+ def export_table_to_path(self, table: str, path: str) -> str:
257
+ """
258
+ Exports a table's data to a local CSV file.
206
259
 
207
- :param query: SQL query to run
208
- :return: Result of the query
260
+ :param table: The name of the table to export.
261
+ :param path: The local file path to save the file.
262
+ :return: A confirmation message with the file path.
209
263
  """
264
+ log_debug(f"Exporting Table {table} as CSV to local path {path}")
210
265
 
211
- # -*- Format the SQL Query
212
- # Remove backticks
213
- formatted_sql = query.replace("`", "")
214
- # If there are multiple statements, only run the first one
215
- formatted_sql = formatted_sql.split(";")[0]
266
+ table_identifier = sql.Identifier(self.table_schema, table)
267
+ stmt = sql.SQL("SELECT * FROM {tbl};").format(tbl=table_identifier)
216
268
 
217
269
  try:
218
- logger.info(f"Running: {formatted_sql}")
219
-
220
- cursor = self.connection.cursor()
221
- cursor.execute(query)
222
- query_result = cursor.fetchall()
223
-
224
- result_output = "No output"
225
- if query_result is not None:
226
- try:
227
- results_as_python_objects = query_result
228
- result_rows = []
229
- for row in results_as_python_objects:
230
- if len(row) == 1:
231
- result_rows.append(str(row[0]))
232
- else:
233
- result_rows.append(",".join(str(x) for x in row))
270
+ connection = self._ensure_connection()
271
+ with connection.cursor() as cursor:
272
+ cursor.execute(stmt)
234
273
 
235
- result_data = "\n".join(result_rows)
236
- result_output = ",".join(query_result.columns) + "\n" + result_data
237
- except AttributeError:
238
- result_output = str(query_result)
274
+ if cursor.description is None:
275
+ return f"Error: Query returned no description for table '{table}'."
239
276
 
240
- logger.debug(f"Query result: {result_output}")
277
+ columns = [desc[0] for desc in cursor.description]
241
278
 
242
- return result_output
243
- except Exception as e:
244
- return str(e)
279
+ with open(path, "w", newline="", encoding="utf-8") as f:
280
+ writer = csv.writer(f)
281
+ writer.writerow(columns)
282
+ writer.writerows(row.values() for row in cursor)
283
+
284
+ return f"Successfully exported table '{table}' to '{path}'."
285
+ except (psycopg.Error, IOError) as e:
286
+ if self._connection and not self._connection.closed:
287
+ self._connection.rollback()
288
+ return f"Error exporting table: {e}"
289
+
290
+ def run_query(self, query: str) -> str:
291
+ """
292
+ Runs a read-only SQL query and returns the result.
293
+
294
+ :param query: The SQL query to run.
295
+ :return: The query result as a formatted string.
296
+ """
297
+ return self._execute_query(query)
agno/tools/pubmed.py CHANGED
@@ -5,6 +5,7 @@ from xml.etree import ElementTree
5
5
  import httpx
6
6
 
7
7
  from agno.tools import Toolkit
8
+ from agno.utils.log import log_debug
8
9
 
9
10
 
10
11
  class PubmedTools(Toolkit):
@@ -12,12 +13,20 @@ class PubmedTools(Toolkit):
12
13
  self,
13
14
  email: str = "your_email@example.com",
14
15
  max_results: Optional[int] = None,
16
+ results_expanded: bool = False,
17
+ enable_search_pubmed: bool = True,
18
+ all: bool = False,
19
+ **kwargs,
15
20
  ):
16
- super().__init__(name="pubmed")
17
21
  self.max_results: Optional[int] = max_results
18
22
  self.email: str = email
23
+ self.results_expanded: bool = results_expanded
19
24
 
20
- self.register(self.search_pubmed)
25
+ tools: List[Any] = []
26
+ if enable_search_pubmed or all:
27
+ tools.append(self.search_pubmed)
28
+
29
+ super().__init__(name="pubmed", tools=tools, **kwargs)
21
30
 
22
31
  def fetch_pubmed_ids(self, query: str, max_results: int, email: str) -> List[str]:
23
32
  url = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi"
@@ -41,36 +50,139 @@ class PubmedTools(Toolkit):
41
50
  def parse_details(self, xml_root: ElementTree.Element) -> List[Dict[str, Any]]:
42
51
  articles = []
43
52
  for article in xml_root.findall(".//PubmedArticle"):
53
+ # Get existing fields
44
54
  pub_date = article.find(".//PubDate/Year")
45
55
  title = article.find(".//ArticleTitle")
46
- abstract = article.find(".//AbstractText")
56
+
57
+ # Handle abstract sections with labels (methods, results, etc.)
58
+ abstract_sections = article.findall(".//AbstractText")
59
+ abstract_text = ""
60
+ if abstract_sections:
61
+ for section in abstract_sections:
62
+ label = section.get("Label", "")
63
+ if label:
64
+ abstract_text += f"{label}: {section.text}\n\n"
65
+ else:
66
+ abstract_text += f"{section.text}\n\n"
67
+ abstract_text = abstract_text.strip()
68
+ else:
69
+ abstract_text = "No abstract available"
70
+
71
+ # Get first author
72
+ first_author_elem = article.find(".//AuthorList/Author[1]")
73
+ first_author = "Unknown"
74
+ if first_author_elem is not None:
75
+ last_name = first_author_elem.find("LastName")
76
+ fore_name = first_author_elem.find("ForeName")
77
+ if last_name is not None and fore_name is not None:
78
+ first_author = f"{last_name.text}, {fore_name.text}"
79
+ elif last_name is not None and last_name.text:
80
+ first_author = last_name.text
81
+
82
+ # Get DOI
83
+ doi_elem = article.find(".//ArticleIdList/ArticleId[@IdType='doi']")
84
+ doi = doi_elem.text if doi_elem is not None else "No DOI available"
85
+
86
+ # Get PMID for URL construction
87
+ pmid_elem = article.find(".//PMID")
88
+ pmid = pmid_elem.text if pmid_elem is not None else ""
89
+ pubmed_url = f"https://pubmed.ncbi.nlm.nih.gov/{pmid}/" if pmid else "No URL available"
90
+
91
+ # Check if full text is available via PMC
92
+ pmc_elem = article.find(".//ArticleIdList/ArticleId[@IdType='pmc']")
93
+ full_text_url = "Not available"
94
+ if pmc_elem is not None:
95
+ full_text_url = f"https://www.ncbi.nlm.nih.gov/pmc/articles/{pmc_elem.text}/"
96
+ elif doi_elem is not None:
97
+ full_text_url = f"https://doi.org/{doi}"
98
+
99
+ # Get keywords
100
+ keywords = []
101
+ for keyword in article.findall(".//KeywordList/Keyword"):
102
+ if keyword.text:
103
+ keywords.append(keyword.text)
104
+
105
+ # Get MeSH terms (useful for understanding medical context)
106
+ mesh_terms = []
107
+ for mesh in article.findall(".//MeshHeading/DescriptorName"):
108
+ if mesh.text:
109
+ mesh_terms.append(mesh.text)
110
+
111
+ # Get journal info
112
+ journal_elem = article.find(".//Journal/Title")
113
+ journal = journal_elem.text if journal_elem is not None else "Unknown Journal"
114
+
115
+ # Publication type (research article, review, etc.)
116
+ pub_types = []
117
+ for pub_type in article.findall(".//PublicationTypeList/PublicationType"):
118
+ if pub_type.text:
119
+ pub_types.append(pub_type.text)
120
+
47
121
  articles.append(
48
122
  {
49
- "Published": (pub_date.text if pub_date is not None else "No date available"),
123
+ "Published": pub_date.text if pub_date is not None else "No date available",
50
124
  "Title": title.text if title is not None else "No title available",
51
- "Summary": (abstract.text if abstract is not None else "No abstract available"),
125
+ "Summary": abstract_text,
126
+ "First_Author": first_author,
127
+ "DOI": doi,
128
+ "PubMed_URL": pubmed_url,
129
+ "Full_Text_URL": full_text_url,
130
+ "Keywords": ", ".join(keywords) if keywords else "No keywords available",
131
+ "MeSH_Terms": ", ".join(mesh_terms) if mesh_terms else "No MeSH terms available",
132
+ "Journal": journal,
133
+ "Publication_Type": ", ".join(pub_types) if pub_types else "Not specified",
52
134
  }
53
135
  )
136
+
54
137
  return articles
55
138
 
56
- def search_pubmed(self, query: str, max_results: int = 10) -> str:
139
+ def search_pubmed(self, query: str, max_results: Optional[int] = 10) -> str:
57
140
  """Use this function to search PubMed for articles.
58
141
 
59
142
  Args:
60
143
  query (str): The search query.
61
- max_results (int): The maximum number of results to return.
144
+ max_results (int): The maximum number of results to return (default 10).
62
145
 
63
146
  Returns:
64
147
  str: A JSON string containing the search results.
65
148
  """
66
149
  try:
67
- ids = self.fetch_pubmed_ids(query, self.max_results or max_results, self.email)
150
+ log_debug(f"Searching PubMed for: {query}")
151
+ max_results = max_results or self.max_results or 10
152
+ ids = self.fetch_pubmed_ids(query, max_results, self.email)
68
153
  details_root = self.fetch_details(ids)
69
154
  articles = self.parse_details(details_root)
70
- results = [
71
- f"Published: {article.get('Published')}\nTitle: {article.get('Title')}\nSummary:\n{article.get('Summary')}"
72
- for article in articles
73
- ]
155
+
156
+ # Create result strings based on configured detail level
157
+ results = []
158
+ for article in articles:
159
+ if self.results_expanded:
160
+ # Comprehensive format with all metadata
161
+ article_text = (
162
+ f"Published: {article.get('Published')}\n"
163
+ f"Title: {article.get('Title')}\n"
164
+ f"First Author: {article.get('First_Author')}\n"
165
+ f"Journal: {article.get('Journal')}\n"
166
+ f"Publication Type: {article.get('Publication_Type')}\n"
167
+ f"DOI: {article.get('DOI')}\n"
168
+ f"PubMed URL: {article.get('PubMed_URL')}\n"
169
+ f"Full Text URL: {article.get('Full_Text_URL')}\n"
170
+ f"Keywords: {article.get('Keywords')}\n"
171
+ f"MeSH Terms: {article.get('MeSH_Terms')}\n"
172
+ f"Summary:\n{article.get('Summary')}"
173
+ )
174
+ else:
175
+ # Concise format with just essential information
176
+ summary = article.get("Summary", "")
177
+ article_text = (
178
+ f"Title: {article.get('Title')}\n"
179
+ f"Published: {article.get('Published')}\n"
180
+ f"Summary: {summary[:200]}..."
181
+ if len(summary) > 200
182
+ else f"Summary: {summary}"
183
+ )
184
+ results.append(article_text)
185
+
74
186
  return json.dumps(results)
75
187
  except Exception as e:
76
- return f"Cound not fetch articles. Error: {e}"
188
+ return f"Could not fetch articles. Error: {e}"