agno 2.2.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 (575) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +51 -0
  3. agno/agent/agent.py +10405 -0
  4. agno/api/__init__.py +0 -0
  5. agno/api/agent.py +28 -0
  6. agno/api/api.py +40 -0
  7. agno/api/evals.py +22 -0
  8. agno/api/os.py +17 -0
  9. agno/api/routes.py +13 -0
  10. agno/api/schemas/__init__.py +9 -0
  11. agno/api/schemas/agent.py +16 -0
  12. agno/api/schemas/evals.py +16 -0
  13. agno/api/schemas/os.py +14 -0
  14. agno/api/schemas/response.py +6 -0
  15. agno/api/schemas/team.py +16 -0
  16. agno/api/schemas/utils.py +21 -0
  17. agno/api/schemas/workflows.py +16 -0
  18. agno/api/settings.py +53 -0
  19. agno/api/team.py +30 -0
  20. agno/api/workflow.py +28 -0
  21. agno/cloud/aws/base.py +214 -0
  22. agno/cloud/aws/s3/__init__.py +2 -0
  23. agno/cloud/aws/s3/api_client.py +43 -0
  24. agno/cloud/aws/s3/bucket.py +195 -0
  25. agno/cloud/aws/s3/object.py +57 -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 +598 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2042 -0
  33. agno/db/dynamo/schemas.py +314 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +1795 -0
  37. agno/db/firestore/schemas.py +140 -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 +1335 -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 +1160 -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 +1328 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/__init__.py +0 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/mongo/__init__.py +17 -0
  51. agno/db/mongo/async_mongo.py +2026 -0
  52. agno/db/mongo/mongo.py +1982 -0
  53. agno/db/mongo/schemas.py +87 -0
  54. agno/db/mongo/utils.py +259 -0
  55. agno/db/mysql/__init__.py +3 -0
  56. agno/db/mysql/mysql.py +2308 -0
  57. agno/db/mysql/schemas.py +138 -0
  58. agno/db/mysql/utils.py +355 -0
  59. agno/db/postgres/__init__.py +4 -0
  60. agno/db/postgres/async_postgres.py +1927 -0
  61. agno/db/postgres/postgres.py +2260 -0
  62. agno/db/postgres/schemas.py +139 -0
  63. agno/db/postgres/utils.py +442 -0
  64. agno/db/redis/__init__.py +3 -0
  65. agno/db/redis/redis.py +1660 -0
  66. agno/db/redis/schemas.py +123 -0
  67. agno/db/redis/utils.py +346 -0
  68. agno/db/schemas/__init__.py +4 -0
  69. agno/db/schemas/culture.py +120 -0
  70. agno/db/schemas/evals.py +33 -0
  71. agno/db/schemas/knowledge.py +40 -0
  72. agno/db/schemas/memory.py +46 -0
  73. agno/db/schemas/metrics.py +0 -0
  74. agno/db/singlestore/__init__.py +3 -0
  75. agno/db/singlestore/schemas.py +130 -0
  76. agno/db/singlestore/singlestore.py +2272 -0
  77. agno/db/singlestore/utils.py +384 -0
  78. agno/db/sqlite/__init__.py +4 -0
  79. agno/db/sqlite/async_sqlite.py +2293 -0
  80. agno/db/sqlite/schemas.py +133 -0
  81. agno/db/sqlite/sqlite.py +2288 -0
  82. agno/db/sqlite/utils.py +431 -0
  83. agno/db/surrealdb/__init__.py +3 -0
  84. agno/db/surrealdb/metrics.py +292 -0
  85. agno/db/surrealdb/models.py +309 -0
  86. agno/db/surrealdb/queries.py +71 -0
  87. agno/db/surrealdb/surrealdb.py +1353 -0
  88. agno/db/surrealdb/utils.py +147 -0
  89. agno/db/utils.py +116 -0
  90. agno/debug.py +18 -0
  91. agno/eval/__init__.py +14 -0
  92. agno/eval/accuracy.py +834 -0
  93. agno/eval/performance.py +773 -0
  94. agno/eval/reliability.py +306 -0
  95. agno/eval/utils.py +119 -0
  96. agno/exceptions.py +161 -0
  97. agno/filters.py +354 -0
  98. agno/guardrails/__init__.py +6 -0
  99. agno/guardrails/base.py +19 -0
  100. agno/guardrails/openai.py +144 -0
  101. agno/guardrails/pii.py +94 -0
  102. agno/guardrails/prompt_injection.py +52 -0
  103. agno/integrations/__init__.py +0 -0
  104. agno/integrations/discord/__init__.py +3 -0
  105. agno/integrations/discord/client.py +203 -0
  106. agno/knowledge/__init__.py +5 -0
  107. agno/knowledge/chunking/__init__.py +0 -0
  108. agno/knowledge/chunking/agentic.py +79 -0
  109. agno/knowledge/chunking/document.py +91 -0
  110. agno/knowledge/chunking/fixed.py +57 -0
  111. agno/knowledge/chunking/markdown.py +151 -0
  112. agno/knowledge/chunking/recursive.py +63 -0
  113. agno/knowledge/chunking/row.py +39 -0
  114. agno/knowledge/chunking/semantic.py +86 -0
  115. agno/knowledge/chunking/strategy.py +165 -0
  116. agno/knowledge/content.py +74 -0
  117. agno/knowledge/document/__init__.py +5 -0
  118. agno/knowledge/document/base.py +58 -0
  119. agno/knowledge/embedder/__init__.py +5 -0
  120. agno/knowledge/embedder/aws_bedrock.py +343 -0
  121. agno/knowledge/embedder/azure_openai.py +210 -0
  122. agno/knowledge/embedder/base.py +23 -0
  123. agno/knowledge/embedder/cohere.py +323 -0
  124. agno/knowledge/embedder/fastembed.py +62 -0
  125. agno/knowledge/embedder/fireworks.py +13 -0
  126. agno/knowledge/embedder/google.py +258 -0
  127. agno/knowledge/embedder/huggingface.py +94 -0
  128. agno/knowledge/embedder/jina.py +182 -0
  129. agno/knowledge/embedder/langdb.py +22 -0
  130. agno/knowledge/embedder/mistral.py +206 -0
  131. agno/knowledge/embedder/nebius.py +13 -0
  132. agno/knowledge/embedder/ollama.py +154 -0
  133. agno/knowledge/embedder/openai.py +195 -0
  134. agno/knowledge/embedder/sentence_transformer.py +63 -0
  135. agno/knowledge/embedder/together.py +13 -0
  136. agno/knowledge/embedder/vllm.py +262 -0
  137. agno/knowledge/embedder/voyageai.py +165 -0
  138. agno/knowledge/knowledge.py +1988 -0
  139. agno/knowledge/reader/__init__.py +7 -0
  140. agno/knowledge/reader/arxiv_reader.py +81 -0
  141. agno/knowledge/reader/base.py +95 -0
  142. agno/knowledge/reader/csv_reader.py +166 -0
  143. agno/knowledge/reader/docx_reader.py +82 -0
  144. agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
  145. agno/knowledge/reader/firecrawl_reader.py +201 -0
  146. agno/knowledge/reader/json_reader.py +87 -0
  147. agno/knowledge/reader/markdown_reader.py +137 -0
  148. agno/knowledge/reader/pdf_reader.py +431 -0
  149. agno/knowledge/reader/pptx_reader.py +101 -0
  150. agno/knowledge/reader/reader_factory.py +313 -0
  151. agno/knowledge/reader/s3_reader.py +89 -0
  152. agno/knowledge/reader/tavily_reader.py +194 -0
  153. agno/knowledge/reader/text_reader.py +115 -0
  154. agno/knowledge/reader/web_search_reader.py +372 -0
  155. agno/knowledge/reader/website_reader.py +455 -0
  156. agno/knowledge/reader/wikipedia_reader.py +59 -0
  157. agno/knowledge/reader/youtube_reader.py +78 -0
  158. agno/knowledge/remote_content/__init__.py +0 -0
  159. agno/knowledge/remote_content/remote_content.py +88 -0
  160. agno/knowledge/reranker/__init__.py +3 -0
  161. agno/knowledge/reranker/base.py +14 -0
  162. agno/knowledge/reranker/cohere.py +64 -0
  163. agno/knowledge/reranker/infinity.py +195 -0
  164. agno/knowledge/reranker/sentence_transformer.py +54 -0
  165. agno/knowledge/types.py +39 -0
  166. agno/knowledge/utils.py +189 -0
  167. agno/media.py +462 -0
  168. agno/memory/__init__.py +3 -0
  169. agno/memory/manager.py +1327 -0
  170. agno/models/__init__.py +0 -0
  171. agno/models/aimlapi/__init__.py +5 -0
  172. agno/models/aimlapi/aimlapi.py +45 -0
  173. agno/models/anthropic/__init__.py +5 -0
  174. agno/models/anthropic/claude.py +757 -0
  175. agno/models/aws/__init__.py +15 -0
  176. agno/models/aws/bedrock.py +701 -0
  177. agno/models/aws/claude.py +378 -0
  178. agno/models/azure/__init__.py +18 -0
  179. agno/models/azure/ai_foundry.py +485 -0
  180. agno/models/azure/openai_chat.py +131 -0
  181. agno/models/base.py +2175 -0
  182. agno/models/cerebras/__init__.py +12 -0
  183. agno/models/cerebras/cerebras.py +501 -0
  184. agno/models/cerebras/cerebras_openai.py +112 -0
  185. agno/models/cohere/__init__.py +5 -0
  186. agno/models/cohere/chat.py +389 -0
  187. agno/models/cometapi/__init__.py +5 -0
  188. agno/models/cometapi/cometapi.py +57 -0
  189. agno/models/dashscope/__init__.py +5 -0
  190. agno/models/dashscope/dashscope.py +91 -0
  191. agno/models/deepinfra/__init__.py +5 -0
  192. agno/models/deepinfra/deepinfra.py +28 -0
  193. agno/models/deepseek/__init__.py +5 -0
  194. agno/models/deepseek/deepseek.py +61 -0
  195. agno/models/defaults.py +1 -0
  196. agno/models/fireworks/__init__.py +5 -0
  197. agno/models/fireworks/fireworks.py +26 -0
  198. agno/models/google/__init__.py +5 -0
  199. agno/models/google/gemini.py +1085 -0
  200. agno/models/groq/__init__.py +5 -0
  201. agno/models/groq/groq.py +556 -0
  202. agno/models/huggingface/__init__.py +5 -0
  203. agno/models/huggingface/huggingface.py +491 -0
  204. agno/models/ibm/__init__.py +5 -0
  205. agno/models/ibm/watsonx.py +422 -0
  206. agno/models/internlm/__init__.py +3 -0
  207. agno/models/internlm/internlm.py +26 -0
  208. agno/models/langdb/__init__.py +1 -0
  209. agno/models/langdb/langdb.py +48 -0
  210. agno/models/litellm/__init__.py +14 -0
  211. agno/models/litellm/chat.py +468 -0
  212. agno/models/litellm/litellm_openai.py +25 -0
  213. agno/models/llama_cpp/__init__.py +5 -0
  214. agno/models/llama_cpp/llama_cpp.py +22 -0
  215. agno/models/lmstudio/__init__.py +5 -0
  216. agno/models/lmstudio/lmstudio.py +25 -0
  217. agno/models/message.py +434 -0
  218. agno/models/meta/__init__.py +12 -0
  219. agno/models/meta/llama.py +475 -0
  220. agno/models/meta/llama_openai.py +78 -0
  221. agno/models/metrics.py +120 -0
  222. agno/models/mistral/__init__.py +5 -0
  223. agno/models/mistral/mistral.py +432 -0
  224. agno/models/nebius/__init__.py +3 -0
  225. agno/models/nebius/nebius.py +54 -0
  226. agno/models/nexus/__init__.py +3 -0
  227. agno/models/nexus/nexus.py +22 -0
  228. agno/models/nvidia/__init__.py +5 -0
  229. agno/models/nvidia/nvidia.py +28 -0
  230. agno/models/ollama/__init__.py +5 -0
  231. agno/models/ollama/chat.py +441 -0
  232. agno/models/openai/__init__.py +9 -0
  233. agno/models/openai/chat.py +883 -0
  234. agno/models/openai/like.py +27 -0
  235. agno/models/openai/responses.py +1050 -0
  236. agno/models/openrouter/__init__.py +5 -0
  237. agno/models/openrouter/openrouter.py +66 -0
  238. agno/models/perplexity/__init__.py +5 -0
  239. agno/models/perplexity/perplexity.py +187 -0
  240. agno/models/portkey/__init__.py +3 -0
  241. agno/models/portkey/portkey.py +81 -0
  242. agno/models/requesty/__init__.py +5 -0
  243. agno/models/requesty/requesty.py +52 -0
  244. agno/models/response.py +199 -0
  245. agno/models/sambanova/__init__.py +5 -0
  246. agno/models/sambanova/sambanova.py +28 -0
  247. agno/models/siliconflow/__init__.py +5 -0
  248. agno/models/siliconflow/siliconflow.py +25 -0
  249. agno/models/together/__init__.py +5 -0
  250. agno/models/together/together.py +25 -0
  251. agno/models/utils.py +266 -0
  252. agno/models/vercel/__init__.py +3 -0
  253. agno/models/vercel/v0.py +26 -0
  254. agno/models/vertexai/__init__.py +0 -0
  255. agno/models/vertexai/claude.py +70 -0
  256. agno/models/vllm/__init__.py +3 -0
  257. agno/models/vllm/vllm.py +78 -0
  258. agno/models/xai/__init__.py +3 -0
  259. agno/models/xai/xai.py +113 -0
  260. agno/os/__init__.py +3 -0
  261. agno/os/app.py +876 -0
  262. agno/os/auth.py +57 -0
  263. agno/os/config.py +104 -0
  264. agno/os/interfaces/__init__.py +1 -0
  265. agno/os/interfaces/a2a/__init__.py +3 -0
  266. agno/os/interfaces/a2a/a2a.py +42 -0
  267. agno/os/interfaces/a2a/router.py +250 -0
  268. agno/os/interfaces/a2a/utils.py +924 -0
  269. agno/os/interfaces/agui/__init__.py +3 -0
  270. agno/os/interfaces/agui/agui.py +47 -0
  271. agno/os/interfaces/agui/router.py +144 -0
  272. agno/os/interfaces/agui/utils.py +534 -0
  273. agno/os/interfaces/base.py +25 -0
  274. agno/os/interfaces/slack/__init__.py +3 -0
  275. agno/os/interfaces/slack/router.py +148 -0
  276. agno/os/interfaces/slack/security.py +30 -0
  277. agno/os/interfaces/slack/slack.py +47 -0
  278. agno/os/interfaces/whatsapp/__init__.py +3 -0
  279. agno/os/interfaces/whatsapp/router.py +211 -0
  280. agno/os/interfaces/whatsapp/security.py +53 -0
  281. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  282. agno/os/mcp.py +292 -0
  283. agno/os/middleware/__init__.py +7 -0
  284. agno/os/middleware/jwt.py +233 -0
  285. agno/os/router.py +1763 -0
  286. agno/os/routers/__init__.py +3 -0
  287. agno/os/routers/evals/__init__.py +3 -0
  288. agno/os/routers/evals/evals.py +430 -0
  289. agno/os/routers/evals/schemas.py +142 -0
  290. agno/os/routers/evals/utils.py +162 -0
  291. agno/os/routers/health.py +31 -0
  292. agno/os/routers/home.py +52 -0
  293. agno/os/routers/knowledge/__init__.py +3 -0
  294. agno/os/routers/knowledge/knowledge.py +997 -0
  295. agno/os/routers/knowledge/schemas.py +178 -0
  296. agno/os/routers/memory/__init__.py +3 -0
  297. agno/os/routers/memory/memory.py +515 -0
  298. agno/os/routers/memory/schemas.py +62 -0
  299. agno/os/routers/metrics/__init__.py +3 -0
  300. agno/os/routers/metrics/metrics.py +190 -0
  301. agno/os/routers/metrics/schemas.py +47 -0
  302. agno/os/routers/session/__init__.py +3 -0
  303. agno/os/routers/session/session.py +997 -0
  304. agno/os/schema.py +1055 -0
  305. agno/os/settings.py +43 -0
  306. agno/os/utils.py +630 -0
  307. agno/py.typed +0 -0
  308. agno/reasoning/__init__.py +0 -0
  309. agno/reasoning/anthropic.py +80 -0
  310. agno/reasoning/azure_ai_foundry.py +67 -0
  311. agno/reasoning/deepseek.py +63 -0
  312. agno/reasoning/default.py +97 -0
  313. agno/reasoning/gemini.py +73 -0
  314. agno/reasoning/groq.py +71 -0
  315. agno/reasoning/helpers.py +63 -0
  316. agno/reasoning/ollama.py +67 -0
  317. agno/reasoning/openai.py +86 -0
  318. agno/reasoning/step.py +31 -0
  319. agno/reasoning/vertexai.py +76 -0
  320. agno/run/__init__.py +6 -0
  321. agno/run/agent.py +787 -0
  322. agno/run/base.py +229 -0
  323. agno/run/cancel.py +81 -0
  324. agno/run/messages.py +32 -0
  325. agno/run/team.py +753 -0
  326. agno/run/workflow.py +708 -0
  327. agno/session/__init__.py +10 -0
  328. agno/session/agent.py +295 -0
  329. agno/session/summary.py +265 -0
  330. agno/session/team.py +392 -0
  331. agno/session/workflow.py +205 -0
  332. agno/team/__init__.py +37 -0
  333. agno/team/team.py +8793 -0
  334. agno/tools/__init__.py +10 -0
  335. agno/tools/agentql.py +120 -0
  336. agno/tools/airflow.py +69 -0
  337. agno/tools/api.py +122 -0
  338. agno/tools/apify.py +314 -0
  339. agno/tools/arxiv.py +127 -0
  340. agno/tools/aws_lambda.py +53 -0
  341. agno/tools/aws_ses.py +66 -0
  342. agno/tools/baidusearch.py +89 -0
  343. agno/tools/bitbucket.py +292 -0
  344. agno/tools/brandfetch.py +213 -0
  345. agno/tools/bravesearch.py +106 -0
  346. agno/tools/brightdata.py +367 -0
  347. agno/tools/browserbase.py +209 -0
  348. agno/tools/calcom.py +255 -0
  349. agno/tools/calculator.py +151 -0
  350. agno/tools/cartesia.py +187 -0
  351. agno/tools/clickup.py +244 -0
  352. agno/tools/confluence.py +240 -0
  353. agno/tools/crawl4ai.py +158 -0
  354. agno/tools/csv_toolkit.py +185 -0
  355. agno/tools/dalle.py +110 -0
  356. agno/tools/daytona.py +475 -0
  357. agno/tools/decorator.py +262 -0
  358. agno/tools/desi_vocal.py +108 -0
  359. agno/tools/discord.py +161 -0
  360. agno/tools/docker.py +716 -0
  361. agno/tools/duckdb.py +379 -0
  362. agno/tools/duckduckgo.py +91 -0
  363. agno/tools/e2b.py +703 -0
  364. agno/tools/eleven_labs.py +196 -0
  365. agno/tools/email.py +67 -0
  366. agno/tools/evm.py +129 -0
  367. agno/tools/exa.py +396 -0
  368. agno/tools/fal.py +127 -0
  369. agno/tools/file.py +240 -0
  370. agno/tools/file_generation.py +350 -0
  371. agno/tools/financial_datasets.py +288 -0
  372. agno/tools/firecrawl.py +143 -0
  373. agno/tools/function.py +1187 -0
  374. agno/tools/giphy.py +93 -0
  375. agno/tools/github.py +1760 -0
  376. agno/tools/gmail.py +922 -0
  377. agno/tools/google_bigquery.py +117 -0
  378. agno/tools/google_drive.py +270 -0
  379. agno/tools/google_maps.py +253 -0
  380. agno/tools/googlecalendar.py +674 -0
  381. agno/tools/googlesearch.py +98 -0
  382. agno/tools/googlesheets.py +377 -0
  383. agno/tools/hackernews.py +77 -0
  384. agno/tools/jina.py +101 -0
  385. agno/tools/jira.py +170 -0
  386. agno/tools/knowledge.py +218 -0
  387. agno/tools/linear.py +426 -0
  388. agno/tools/linkup.py +58 -0
  389. agno/tools/local_file_system.py +90 -0
  390. agno/tools/lumalab.py +183 -0
  391. agno/tools/mcp/__init__.py +10 -0
  392. agno/tools/mcp/mcp.py +331 -0
  393. agno/tools/mcp/multi_mcp.py +347 -0
  394. agno/tools/mcp/params.py +24 -0
  395. agno/tools/mcp_toolbox.py +284 -0
  396. agno/tools/mem0.py +193 -0
  397. agno/tools/memori.py +339 -0
  398. agno/tools/memory.py +419 -0
  399. agno/tools/mlx_transcribe.py +139 -0
  400. agno/tools/models/__init__.py +0 -0
  401. agno/tools/models/azure_openai.py +190 -0
  402. agno/tools/models/gemini.py +203 -0
  403. agno/tools/models/groq.py +158 -0
  404. agno/tools/models/morph.py +186 -0
  405. agno/tools/models/nebius.py +124 -0
  406. agno/tools/models_labs.py +195 -0
  407. agno/tools/moviepy_video.py +349 -0
  408. agno/tools/neo4j.py +134 -0
  409. agno/tools/newspaper.py +46 -0
  410. agno/tools/newspaper4k.py +93 -0
  411. agno/tools/notion.py +204 -0
  412. agno/tools/openai.py +202 -0
  413. agno/tools/openbb.py +160 -0
  414. agno/tools/opencv.py +321 -0
  415. agno/tools/openweather.py +233 -0
  416. agno/tools/oxylabs.py +385 -0
  417. agno/tools/pandas.py +102 -0
  418. agno/tools/parallel.py +314 -0
  419. agno/tools/postgres.py +257 -0
  420. agno/tools/pubmed.py +188 -0
  421. agno/tools/python.py +205 -0
  422. agno/tools/reasoning.py +283 -0
  423. agno/tools/reddit.py +467 -0
  424. agno/tools/replicate.py +117 -0
  425. agno/tools/resend.py +62 -0
  426. agno/tools/scrapegraph.py +222 -0
  427. agno/tools/searxng.py +152 -0
  428. agno/tools/serpapi.py +116 -0
  429. agno/tools/serper.py +255 -0
  430. agno/tools/shell.py +53 -0
  431. agno/tools/slack.py +136 -0
  432. agno/tools/sleep.py +20 -0
  433. agno/tools/spider.py +116 -0
  434. agno/tools/sql.py +154 -0
  435. agno/tools/streamlit/__init__.py +0 -0
  436. agno/tools/streamlit/components.py +113 -0
  437. agno/tools/tavily.py +254 -0
  438. agno/tools/telegram.py +48 -0
  439. agno/tools/todoist.py +218 -0
  440. agno/tools/tool_registry.py +1 -0
  441. agno/tools/toolkit.py +146 -0
  442. agno/tools/trafilatura.py +388 -0
  443. agno/tools/trello.py +274 -0
  444. agno/tools/twilio.py +186 -0
  445. agno/tools/user_control_flow.py +78 -0
  446. agno/tools/valyu.py +228 -0
  447. agno/tools/visualization.py +467 -0
  448. agno/tools/webbrowser.py +28 -0
  449. agno/tools/webex.py +76 -0
  450. agno/tools/website.py +54 -0
  451. agno/tools/webtools.py +45 -0
  452. agno/tools/whatsapp.py +286 -0
  453. agno/tools/wikipedia.py +63 -0
  454. agno/tools/workflow.py +278 -0
  455. agno/tools/x.py +335 -0
  456. agno/tools/yfinance.py +257 -0
  457. agno/tools/youtube.py +184 -0
  458. agno/tools/zendesk.py +82 -0
  459. agno/tools/zep.py +454 -0
  460. agno/tools/zoom.py +382 -0
  461. agno/utils/__init__.py +0 -0
  462. agno/utils/agent.py +820 -0
  463. agno/utils/audio.py +49 -0
  464. agno/utils/certs.py +27 -0
  465. agno/utils/code_execution.py +11 -0
  466. agno/utils/common.py +132 -0
  467. agno/utils/dttm.py +13 -0
  468. agno/utils/enum.py +22 -0
  469. agno/utils/env.py +11 -0
  470. agno/utils/events.py +696 -0
  471. agno/utils/format_str.py +16 -0
  472. agno/utils/functions.py +166 -0
  473. agno/utils/gemini.py +426 -0
  474. agno/utils/hooks.py +57 -0
  475. agno/utils/http.py +74 -0
  476. agno/utils/json_schema.py +234 -0
  477. agno/utils/knowledge.py +36 -0
  478. agno/utils/location.py +19 -0
  479. agno/utils/log.py +255 -0
  480. agno/utils/mcp.py +214 -0
  481. agno/utils/media.py +352 -0
  482. agno/utils/merge_dict.py +41 -0
  483. agno/utils/message.py +118 -0
  484. agno/utils/models/__init__.py +0 -0
  485. agno/utils/models/ai_foundry.py +43 -0
  486. agno/utils/models/claude.py +358 -0
  487. agno/utils/models/cohere.py +87 -0
  488. agno/utils/models/llama.py +78 -0
  489. agno/utils/models/mistral.py +98 -0
  490. agno/utils/models/openai_responses.py +140 -0
  491. agno/utils/models/schema_utils.py +153 -0
  492. agno/utils/models/watsonx.py +41 -0
  493. agno/utils/openai.py +257 -0
  494. agno/utils/pickle.py +32 -0
  495. agno/utils/pprint.py +178 -0
  496. agno/utils/print_response/__init__.py +0 -0
  497. agno/utils/print_response/agent.py +842 -0
  498. agno/utils/print_response/team.py +1724 -0
  499. agno/utils/print_response/workflow.py +1668 -0
  500. agno/utils/prompts.py +111 -0
  501. agno/utils/reasoning.py +108 -0
  502. agno/utils/response.py +163 -0
  503. agno/utils/response_iterator.py +17 -0
  504. agno/utils/safe_formatter.py +24 -0
  505. agno/utils/serialize.py +32 -0
  506. agno/utils/shell.py +22 -0
  507. agno/utils/streamlit.py +487 -0
  508. agno/utils/string.py +231 -0
  509. agno/utils/team.py +139 -0
  510. agno/utils/timer.py +41 -0
  511. agno/utils/tools.py +102 -0
  512. agno/utils/web.py +23 -0
  513. agno/utils/whatsapp.py +305 -0
  514. agno/utils/yaml_io.py +25 -0
  515. agno/vectordb/__init__.py +3 -0
  516. agno/vectordb/base.py +127 -0
  517. agno/vectordb/cassandra/__init__.py +5 -0
  518. agno/vectordb/cassandra/cassandra.py +501 -0
  519. agno/vectordb/cassandra/extra_param_mixin.py +11 -0
  520. agno/vectordb/cassandra/index.py +13 -0
  521. agno/vectordb/chroma/__init__.py +5 -0
  522. agno/vectordb/chroma/chromadb.py +929 -0
  523. agno/vectordb/clickhouse/__init__.py +9 -0
  524. agno/vectordb/clickhouse/clickhousedb.py +835 -0
  525. agno/vectordb/clickhouse/index.py +9 -0
  526. agno/vectordb/couchbase/__init__.py +3 -0
  527. agno/vectordb/couchbase/couchbase.py +1442 -0
  528. agno/vectordb/distance.py +7 -0
  529. agno/vectordb/lancedb/__init__.py +6 -0
  530. agno/vectordb/lancedb/lance_db.py +995 -0
  531. agno/vectordb/langchaindb/__init__.py +5 -0
  532. agno/vectordb/langchaindb/langchaindb.py +163 -0
  533. agno/vectordb/lightrag/__init__.py +5 -0
  534. agno/vectordb/lightrag/lightrag.py +388 -0
  535. agno/vectordb/llamaindex/__init__.py +3 -0
  536. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  537. agno/vectordb/milvus/__init__.py +4 -0
  538. agno/vectordb/milvus/milvus.py +1182 -0
  539. agno/vectordb/mongodb/__init__.py +9 -0
  540. agno/vectordb/mongodb/mongodb.py +1417 -0
  541. agno/vectordb/pgvector/__init__.py +12 -0
  542. agno/vectordb/pgvector/index.py +23 -0
  543. agno/vectordb/pgvector/pgvector.py +1462 -0
  544. agno/vectordb/pineconedb/__init__.py +5 -0
  545. agno/vectordb/pineconedb/pineconedb.py +747 -0
  546. agno/vectordb/qdrant/__init__.py +5 -0
  547. agno/vectordb/qdrant/qdrant.py +1134 -0
  548. agno/vectordb/redis/__init__.py +9 -0
  549. agno/vectordb/redis/redisdb.py +694 -0
  550. agno/vectordb/search.py +7 -0
  551. agno/vectordb/singlestore/__init__.py +10 -0
  552. agno/vectordb/singlestore/index.py +41 -0
  553. agno/vectordb/singlestore/singlestore.py +763 -0
  554. agno/vectordb/surrealdb/__init__.py +3 -0
  555. agno/vectordb/surrealdb/surrealdb.py +699 -0
  556. agno/vectordb/upstashdb/__init__.py +5 -0
  557. agno/vectordb/upstashdb/upstashdb.py +718 -0
  558. agno/vectordb/weaviate/__init__.py +8 -0
  559. agno/vectordb/weaviate/index.py +15 -0
  560. agno/vectordb/weaviate/weaviate.py +1005 -0
  561. agno/workflow/__init__.py +23 -0
  562. agno/workflow/agent.py +299 -0
  563. agno/workflow/condition.py +738 -0
  564. agno/workflow/loop.py +735 -0
  565. agno/workflow/parallel.py +824 -0
  566. agno/workflow/router.py +702 -0
  567. agno/workflow/step.py +1432 -0
  568. agno/workflow/steps.py +592 -0
  569. agno/workflow/types.py +520 -0
  570. agno/workflow/workflow.py +4321 -0
  571. agno-2.2.13.dist-info/METADATA +614 -0
  572. agno-2.2.13.dist-info/RECORD +575 -0
  573. agno-2.2.13.dist-info/WHEEL +5 -0
  574. agno-2.2.13.dist-info/licenses/LICENSE +201 -0
  575. agno-2.2.13.dist-info/top_level.txt +1 -0
agno/tools/opencv.py ADDED
@@ -0,0 +1,321 @@
1
+ import time
2
+ from pathlib import Path
3
+ from typing import Callable, List
4
+ from uuid import uuid4
5
+
6
+ from agno.agent import Agent
7
+ from agno.media import Image, Video
8
+ from agno.tools import Toolkit
9
+ from agno.tools.function import ToolResult
10
+ from agno.utils.log import log_debug, log_error, log_info
11
+
12
+ try:
13
+ import cv2
14
+ except ImportError:
15
+ raise ImportError("`opencv-python` package not found. Please install it with `pip install opencv-python`")
16
+
17
+
18
+ class OpenCVTools(Toolkit):
19
+ """Tools for capturing images and videos from the webcam using OpenCV"""
20
+
21
+ def __init__(
22
+ self,
23
+ show_preview=False,
24
+ enable_capture_image: bool = True,
25
+ enable_capture_video: bool = True,
26
+ all: bool = False,
27
+ **kwargs,
28
+ ):
29
+ self.show_preview = show_preview
30
+
31
+ tools: List[Callable] = []
32
+ if all or enable_capture_image:
33
+ tools.append(self.capture_image)
34
+ if all or enable_capture_video:
35
+ tools.append(self.capture_video)
36
+
37
+ super().__init__(
38
+ name="opencv_tools",
39
+ tools=tools,
40
+ **kwargs,
41
+ )
42
+
43
+ def capture_image(
44
+ self,
45
+ agent: Agent,
46
+ prompt: str = "Webcam capture",
47
+ ) -> ToolResult:
48
+ """Capture an image from the webcam.
49
+
50
+ Args:
51
+ prompt (str): Description of the image capture. Defaults to "Webcam capture".
52
+
53
+ Returns:
54
+ ToolResult: A ToolResult containing the captured image or error message.
55
+ """
56
+ try:
57
+ log_debug("Initializing webcam for image capture...")
58
+ cam = cv2.VideoCapture(0)
59
+
60
+ if not cam.isOpened():
61
+ cam = cv2.VideoCapture(0, cv2.CAP_AVFOUNDATION) # macOS
62
+ if not cam.isOpened():
63
+ cam = cv2.VideoCapture(0, cv2.CAP_DSHOW) # Windows
64
+ if not cam.isOpened():
65
+ cam = cv2.VideoCapture(0, cv2.CAP_V4L2) # Linux
66
+
67
+ if not cam.isOpened():
68
+ error_msg = "Could not open webcam. Please ensure your terminal has camera permissions and the camera is not being used by another application."
69
+ log_error(error_msg)
70
+ return ToolResult(content=error_msg)
71
+
72
+ try:
73
+ cam.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
74
+ cam.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
75
+ cam.set(cv2.CAP_PROP_FPS, 30)
76
+
77
+ log_debug("Camera initialized successfully")
78
+ captured_frame = None
79
+
80
+ if self.show_preview:
81
+ log_info("Live preview started. Press 'c' to capture image, 'q' to quit.")
82
+
83
+ while True:
84
+ ret, frame = cam.read()
85
+ if not ret:
86
+ error_msg = "Failed to read frame from webcam"
87
+ log_error(error_msg)
88
+ return ToolResult(content=error_msg)
89
+
90
+ cv2.imshow('Camera Preview - Press "c" to capture, "q" to quit', frame)
91
+
92
+ key = cv2.waitKey(1) & 0xFF
93
+ if key == ord("c"):
94
+ captured_frame = frame.copy()
95
+ log_info("Image captured!")
96
+ break
97
+ elif key == ord("q"):
98
+ log_info("Capture cancelled by user")
99
+ return ToolResult(content="Image capture cancelled by user")
100
+ else:
101
+ ret, captured_frame = cam.read()
102
+ if not ret:
103
+ error_msg = "Failed to capture image from webcam"
104
+ log_error(error_msg)
105
+ return ToolResult(content=error_msg)
106
+
107
+ if captured_frame is None:
108
+ error_msg = "No frame captured"
109
+ log_error(error_msg)
110
+ return ToolResult(content=error_msg)
111
+
112
+ success, encoded_image = cv2.imencode(".png", captured_frame)
113
+
114
+ if not success:
115
+ error_msg = "Failed to encode captured image"
116
+ log_error(error_msg)
117
+ return ToolResult(content=error_msg)
118
+
119
+ image_bytes = encoded_image.tobytes()
120
+ media_id = str(uuid4())
121
+
122
+ # Create ImageArtifact with raw bytes (not base64 encoded)
123
+ image_artifact = Image(
124
+ id=media_id,
125
+ content=image_bytes, # Store as raw bytes
126
+ original_prompt=prompt,
127
+ mime_type="image/png",
128
+ )
129
+
130
+ log_debug(f"Successfully captured and attached image {media_id}")
131
+ return ToolResult(
132
+ content="Image captured successfully",
133
+ images=[image_artifact],
134
+ )
135
+
136
+ finally:
137
+ # Release the camera and close windows
138
+ cam.release()
139
+ cv2.destroyAllWindows()
140
+ log_debug("Camera resources released")
141
+
142
+ except Exception as e:
143
+ error_msg = f"Error capturing image: {str(e)}"
144
+ log_error(error_msg)
145
+ return ToolResult(content=error_msg)
146
+
147
+ def capture_video(
148
+ self,
149
+ agent: Agent,
150
+ duration: int = 10,
151
+ prompt: str = "Webcam video capture",
152
+ ) -> ToolResult:
153
+ """Capture a video from the webcam.
154
+
155
+ Args:
156
+ duration (int): Duration in seconds to record video. Defaults to 10 seconds.
157
+ prompt (str): Description of the video capture. Defaults to "Webcam video capture".
158
+
159
+ Returns:
160
+ ToolResult: A ToolResult containing the captured video or error message.
161
+ """
162
+ try:
163
+ log_debug("Initializing webcam for video capture...")
164
+ cap = cv2.VideoCapture(0)
165
+
166
+ # Try different backends for better compatibility
167
+ if not cap.isOpened():
168
+ cap = cv2.VideoCapture(0, cv2.CAP_AVFOUNDATION) # macOS
169
+ if not cap.isOpened():
170
+ cap = cv2.VideoCapture(0, cv2.CAP_DSHOW) # Windows
171
+ if not cap.isOpened():
172
+ cap = cv2.VideoCapture(0, cv2.CAP_V4L2) # Linux
173
+
174
+ if not cap.isOpened():
175
+ error_msg = "Could not open webcam. Please ensure your terminal has camera permissions and the camera is not being used by another application."
176
+ log_error(error_msg)
177
+ return ToolResult(content=error_msg)
178
+
179
+ try:
180
+ frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
181
+ frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
182
+ actual_fps = cap.get(cv2.CAP_PROP_FPS)
183
+
184
+ # Use actual FPS or default to 30 if detection fails
185
+ if actual_fps <= 0 or actual_fps > 60:
186
+ actual_fps = 30.0
187
+
188
+ log_debug(f"Video properties: {frame_width}x{frame_height} at {actual_fps} FPS")
189
+
190
+ # Try different codecs in order of preference for compatibility
191
+ codecs_to_try = [
192
+ ("avc1", "H.264"), # Most compatible
193
+ ("mp4v", "MPEG-4"), # Fallback
194
+ ("XVID", "Xvid"), # Another fallback
195
+ ]
196
+
197
+ import os
198
+ import tempfile
199
+
200
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
201
+ temp_filepath = temp_file.name
202
+
203
+ out = None
204
+ successful_codec = None
205
+
206
+ for codec_fourcc, codec_name in codecs_to_try:
207
+ try:
208
+ fourcc = getattr(cv2, "VideoWriter_fourcc")(*codec_fourcc)
209
+ out = cv2.VideoWriter(temp_filepath, fourcc, actual_fps, (frame_width, frame_height))
210
+
211
+ if out.isOpened():
212
+ successful_codec = codec_name
213
+ log_debug(f"Successfully initialized video writer with {codec_name} codec")
214
+ break
215
+ else:
216
+ out.release()
217
+ out = None
218
+ except Exception as e:
219
+ log_debug(f"Failed to initialize {codec_name} codec: {e}")
220
+ if out:
221
+ out.release()
222
+ out = None
223
+
224
+ if not out or not out.isOpened():
225
+ error_msg = "Failed to initialize video writer with any codec"
226
+ log_error(error_msg)
227
+ return ToolResult(content=error_msg)
228
+
229
+ start_time = time.time()
230
+ frame_count = 0
231
+
232
+ if self.show_preview:
233
+ log_info(f"Recording {duration}s video with live preview using {successful_codec} codec...")
234
+ else:
235
+ log_info(f"Recording {duration}s video using {successful_codec} codec...")
236
+
237
+ while True:
238
+ ret, frame = cap.read()
239
+
240
+ if not ret:
241
+ error_msg = "Failed to capture video frame"
242
+ log_error(error_msg)
243
+ return ToolResult(content=error_msg)
244
+
245
+ # Write the frame to the output file
246
+ out.write(frame)
247
+ frame_count += 1
248
+
249
+ # Show live preview if enabled
250
+ if self.show_preview:
251
+ # Add recording indicator
252
+ elapsed = time.time() - start_time
253
+ remaining = max(0, duration - elapsed)
254
+
255
+ # Draw recording info on frame
256
+ display_frame = frame.copy()
257
+ cv2.putText(
258
+ display_frame,
259
+ f"REC {remaining:.1f}s",
260
+ (10, 30),
261
+ cv2.FONT_HERSHEY_SIMPLEX,
262
+ 1,
263
+ (0, 0, 255),
264
+ 2,
265
+ )
266
+ cv2.circle(display_frame, (30, 60), 10, (0, 0, 255), -1) # Red dot
267
+
268
+ cv2.imshow(f"Recording Video - {remaining:.1f}s remaining", display_frame)
269
+ cv2.waitKey(1)
270
+
271
+ # Check if recording duration is reached
272
+ if time.time() - start_time >= duration:
273
+ break
274
+
275
+ # Release video writer
276
+ out.release()
277
+
278
+ # Verify the file was created and has content
279
+ temp_path = Path(temp_filepath)
280
+ if not temp_path.exists() or temp_path.stat().st_size == 0:
281
+ error_msg = "Video file was not created or is empty"
282
+ log_error(error_msg)
283
+ return ToolResult(content=error_msg)
284
+
285
+ # Read the video file and encode to base64
286
+ with open(temp_filepath, "rb") as video_file:
287
+ video_bytes = video_file.read()
288
+
289
+ # Clean up temporary file
290
+ os.unlink(temp_filepath)
291
+
292
+ media_id = str(uuid4())
293
+
294
+ # Create VideoArtifact with base64 encoded content
295
+ video_artifact = Video(
296
+ id=media_id,
297
+ content=video_bytes,
298
+ original_prompt=prompt,
299
+ mime_type="video/mp4",
300
+ )
301
+
302
+ actual_duration = time.time() - start_time
303
+ log_debug(
304
+ f"Successfully captured and attached video {media_id} ({actual_duration:.1f}s, {frame_count} frames)"
305
+ )
306
+
307
+ return ToolResult(
308
+ content=f"Video captured successfully and attached as artifact {media_id} ({actual_duration:.1f}s, {frame_count} frames, {successful_codec} codec)",
309
+ videos=[video_artifact],
310
+ )
311
+
312
+ finally:
313
+ if "cap" in locals():
314
+ cap.release()
315
+ cv2.destroyAllWindows()
316
+ log_debug("Video capture resources released")
317
+
318
+ except Exception as e:
319
+ error_msg = f"Error capturing video: {str(e)}"
320
+ log_error(error_msg)
321
+ return ToolResult(content=error_msg)
@@ -0,0 +1,233 @@
1
+ import json
2
+ from os import getenv
3
+ from typing import Any, Dict, List, Optional
4
+
5
+ from agno.tools import Toolkit
6
+ from agno.utils.log import log_info, logger
7
+
8
+ try:
9
+ import requests
10
+ except ImportError:
11
+ raise ImportError("`requests` not installed. Please install using `pip install requests`")
12
+
13
+
14
+ class OpenWeatherTools(Toolkit):
15
+ """
16
+ OpenWeather is a toolkit for accessing weather data from OpenWeatherMap API.
17
+
18
+ Args:
19
+ api_key (Optional[str]): OpenWeatherMap API key. If not provided, will try to get from OPENWEATHER_API_KEY env var.
20
+ units (str): Units of measurement. Options are 'standard', 'metric', and 'imperial'. Default is 'metric'.
21
+ enable_current_weather (bool): Enable current weather function. Default is True.
22
+ enable_forecast (bool): Enable forecast function. Default is True.
23
+ enable_air_pollution (bool): Enable air pollution function. Default is True.
24
+ enable_geocoding (bool): Enable geocoding function. Default is True.
25
+ all (bool): Enable all functions. Default is False.
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ api_key: Optional[str] = None,
31
+ units: str = "metric",
32
+ enable_current_weather: bool = True,
33
+ enable_forecast: bool = True,
34
+ enable_air_pollution: bool = True,
35
+ enable_geocoding: bool = True,
36
+ all: bool = False,
37
+ **kwargs,
38
+ ):
39
+ self.api_key = api_key or getenv("OPENWEATHER_API_KEY")
40
+ if not self.api_key:
41
+ raise ValueError(
42
+ "OpenWeather API key is required. Provide it as an argument or set the OPENWEATHER_API_KEY environment variable."
43
+ )
44
+
45
+ self.units = units
46
+ self.base_url = "https://api.openweathermap.org/data/2.5"
47
+ self.geo_url = "https://api.openweathermap.org/geo/1.0"
48
+
49
+ tools: List[Any] = []
50
+ if enable_current_weather or all:
51
+ tools.append(self.get_current_weather)
52
+ if enable_forecast or all:
53
+ tools.append(self.get_forecast)
54
+ if enable_air_pollution or all:
55
+ tools.append(self.get_air_pollution)
56
+ if enable_geocoding or all:
57
+ tools.append(self.geocode_location)
58
+
59
+ super().__init__(name="openweather_tools", tools=tools, **kwargs)
60
+
61
+ def _make_request(self, url: str, params: Dict) -> Dict:
62
+ """Make a request to the OpenWeatherMap API.
63
+
64
+ Args:
65
+ url (str): The API endpoint URL.
66
+ params (Dict): Query parameters for the request.
67
+
68
+ Returns:
69
+ Dict: The JSON response from the API.
70
+ """
71
+ try:
72
+ params["appid"] = self.api_key
73
+ response = requests.get(url, params=params)
74
+ response.raise_for_status()
75
+ return response.json()
76
+ except requests.exceptions.RequestException as e:
77
+ logger.error(f"Error making request to {url}: {e}")
78
+ return {"error": str(e)}
79
+
80
+ def geocode_location(self, location: str, limit: int = 1) -> str:
81
+ """Convert a location name to geographic coordinates.
82
+
83
+ Args:
84
+ location (str): The name of the city, e.g., "London", "Paris", "New York".
85
+ limit (int): Maximum number of location results. Default is 1.
86
+
87
+ Returns:
88
+ str: JSON string containing location data with coordinates.
89
+ """
90
+ try:
91
+ log_info(f"Geocoding location: {location}")
92
+ url = f"{self.geo_url}/direct"
93
+ params = {"q": location, "limit": limit}
94
+
95
+ result = self._make_request(url, params)
96
+
97
+ if "error" in result:
98
+ return json.dumps(result)
99
+
100
+ if not result:
101
+ return json.dumps({"error": f"No location found for '{location}'"})
102
+
103
+ return json.dumps(result, indent=2)
104
+ except Exception as e:
105
+ logger.error(f"Error geocoding location: {e}")
106
+ return json.dumps({"error": str(e)})
107
+
108
+ def get_current_weather(self, location: str) -> str:
109
+ """Get current weather data for a location.
110
+
111
+ Args:
112
+ location (str): The name of the city, e.g., "London", "Paris", "New York".
113
+
114
+ Returns:
115
+ str: JSON string containing current weather data.
116
+ """
117
+ try:
118
+ log_info(f"Getting current weather for: {location}")
119
+
120
+ # First geocode the location to get coordinates
121
+ geocode_result = json.loads(self.geocode_location(location))
122
+ if "error" in geocode_result:
123
+ return json.dumps(geocode_result)
124
+
125
+ if not geocode_result:
126
+ return json.dumps({"error": f"No location found for '{location}'"})
127
+
128
+ # Get the first location result
129
+ loc_data = geocode_result[0]
130
+ lat, lon = loc_data["lat"], loc_data["lon"]
131
+
132
+ # Get current weather using coordinates
133
+ url = f"{self.base_url}/weather"
134
+ params = {"lat": lat, "lon": lon, "units": self.units}
135
+
136
+ result = self._make_request(url, params)
137
+
138
+ # Add the location name to the result
139
+ if "error" not in result:
140
+ result["location_name"] = loc_data.get("name", location)
141
+ result["country"] = loc_data.get("country", "")
142
+
143
+ return json.dumps(result, indent=2)
144
+ except Exception as e:
145
+ logger.error(f"Error getting current weather: {e}")
146
+ return json.dumps({"error": str(e)})
147
+
148
+ def get_forecast(self, location: str, days: int = 5) -> str:
149
+ """Get weather forecast for a location.
150
+
151
+ Args:
152
+ location (str): The name of the city, e.g., "London", "Paris", "New York".
153
+ days (int): Number of days for forecast (max 5). Default is 5.
154
+
155
+ Returns:
156
+ str: JSON string containing forecast data.
157
+ """
158
+ try:
159
+ log_info(f"Getting {days}-day forecast for: {location}")
160
+
161
+ # First geocode the location to get coordinates
162
+ geocode_result = json.loads(self.geocode_location(location))
163
+ if "error" in geocode_result:
164
+ return json.dumps(geocode_result)
165
+
166
+ if not geocode_result:
167
+ return json.dumps({"error": f"No location found for '{location}'"})
168
+
169
+ # Get the first location result
170
+ loc_data = geocode_result[0]
171
+ lat, lon = loc_data["lat"], loc_data["lon"]
172
+
173
+ # Get forecast using coordinates
174
+ url = f"{self.base_url}/forecast"
175
+ params = {
176
+ "lat": lat,
177
+ "lon": lon,
178
+ "units": self.units,
179
+ # Each day has 8 3-hour forecasts, max 5 days (40 entries)
180
+ "cnt": min(days * 8, 40),
181
+ }
182
+
183
+ result = self._make_request(url, params)
184
+
185
+ # Add the location name to the result
186
+ if "error" not in result:
187
+ result["location_name"] = loc_data.get("name", location)
188
+ result["country"] = loc_data.get("country", "")
189
+
190
+ return json.dumps(result, indent=2)
191
+ except Exception as e:
192
+ logger.error(f"Error getting forecast: {e}")
193
+ return json.dumps({"error": str(e)})
194
+
195
+ def get_air_pollution(self, location: str) -> str:
196
+ """Get current air pollution data for a location.
197
+
198
+ Args:
199
+ location (str): The name of the city, e.g., "London", "Paris", "New York".
200
+
201
+ Returns:
202
+ str: JSON string containing air pollution data.
203
+ """
204
+ try:
205
+ log_info(f"Getting air pollution data for: {location}")
206
+
207
+ # First geocode the location to get coordinates
208
+ geocode_result = json.loads(self.geocode_location(location))
209
+ if "error" in geocode_result:
210
+ return json.dumps(geocode_result)
211
+
212
+ if not geocode_result:
213
+ return json.dumps({"error": f"No location found for '{location}'"})
214
+
215
+ # Get the first location result
216
+ loc_data = geocode_result[0]
217
+ lat, lon = loc_data["lat"], loc_data["lon"]
218
+
219
+ # Get air pollution data using coordinates
220
+ url = f"{self.base_url}/air_pollution"
221
+ params = {"lat": lat, "lon": lon}
222
+
223
+ result = self._make_request(url, params)
224
+
225
+ # Add the location name to the result
226
+ if "error" not in result:
227
+ result["location_name"] = loc_data.get("name", location)
228
+ result["country"] = loc_data.get("country", "")
229
+
230
+ return json.dumps(result, indent=2)
231
+ except Exception as e:
232
+ logger.error(f"Error getting air pollution data: {e}")
233
+ return json.dumps({"error": str(e)})