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
@@ -1,162 +1,233 @@
1
1
  import datetime
2
2
  import json
3
- import os.path
3
+ import uuid
4
4
  from functools import wraps
5
+ from os import getenv
6
+ from pathlib import Path
7
+ from typing import Any, Dict, List, Optional, cast
5
8
 
6
9
  from agno.tools import Toolkit
7
- from agno.utils.log import logger
10
+ from agno.utils.log import log_debug, log_error, log_info
8
11
 
9
12
  try:
10
13
  from google.auth.transport.requests import Request
11
14
  from google.oauth2.credentials import Credentials
12
- from google_auth_oauthlib.flow import InstalledAppFlow # type: ignore
13
- from googleapiclient.discovery import build # type: ignore
14
- from googleapiclient.errors import HttpError # type: ignore
15
+ from google_auth_oauthlib.flow import InstalledAppFlow
16
+ from googleapiclient.discovery import Resource, build
17
+ from googleapiclient.errors import HttpError
18
+
15
19
  except ImportError:
16
20
  raise ImportError(
17
- "Google client library for Python not found , install it using `pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib`"
21
+ "Google client libraries not found, Please install using `pip install google-api-python-client google-auth-httplib2 google-auth-oauthlib`"
18
22
  )
19
- from typing import List, Optional
20
23
 
21
24
  SCOPES = ["https://www.googleapis.com/auth/calendar"]
22
25
 
23
26
 
24
- def authenticated(func):
27
+ def authenticate(func):
25
28
  """Decorator to ensure authentication before executing the method."""
26
29
 
27
30
  @wraps(func)
28
31
  def wrapper(self, *args, **kwargs):
29
- # Ensure credentials are valid
30
- if os.path.exists(self.token_path):
31
- self.creds = Credentials.from_authorized_user_file(self.token_path, SCOPES)
32
- if not self.creds or not self.creds.valid:
33
- if self.creds and self.creds.expired and self.creds.refresh_token:
34
- self.creds.refresh(Request())
35
- else:
36
- flow = InstalledAppFlow.from_client_secrets_file(self.creds_path, SCOPES)
37
- self.creds = flow.run_local_server(port=0)
38
- # Save the credentials for future use
39
- with open(self.token_path, "w") as token:
40
- token.write(self.creds.to_json())
41
-
42
- # Initialize the Google Calendar service
43
32
  try:
44
- self.service = build("calendar", "v3", credentials=self.creds)
45
- except HttpError as error:
46
- logger.error(f"An error occurred while creating the service: {error}")
47
- raise
48
-
49
- # Ensure the service is available
50
- if not self.service:
51
- raise ValueError("Google Calendar service could not be initialized.")
52
-
33
+ if not self.creds or not self.creds.valid:
34
+ self._auth()
35
+ if not self.service:
36
+ self.service = build("calendar", "v3", credentials=self.creds)
37
+ except Exception as e:
38
+ log_error(f"An error occurred: {e}")
53
39
  return func(self, *args, **kwargs)
54
40
 
55
41
  return wrapper
56
42
 
57
43
 
58
44
  class GoogleCalendarTools(Toolkit):
59
- def __init__(self, credentials_path: Optional[str] = None, token_path: Optional[str] = None):
60
- """
61
- Google Calendar Tool.
45
+ # Default scopes for Google Calendar API access
46
+ DEFAULT_SCOPES = {
47
+ "read": "https://www.googleapis.com/auth/calendar.readonly",
48
+ "write": "https://www.googleapis.com/auth/calendar",
49
+ }
62
50
 
63
- :param credentials_path: Path of the file credentials.json file which contains OAuth 2.0 Client ID. A client ID is used to identify a single app to Google's OAuth servers. If your app runs on multiple platforms, you must create a separate client ID for each platform. Refer doc https://developers.google.com/calendar/api/quickstart/python#authorize_credentials_for_a_desktop_application
64
- :param token_path: Path of the file token.json which stores the user's access and refresh tokens, and is created automatically when the authorization flow completes for the first time.
51
+ service: Optional[Resource]
65
52
 
66
- """
67
- super().__init__(name="google_calendar_tools")
53
+ def __init__(
54
+ self,
55
+ scopes: Optional[List[str]] = None,
56
+ credentials_path: Optional[str] = None,
57
+ token_path: Optional[str] = "token.json",
58
+ access_token: Optional[str] = None,
59
+ calendar_id: str = "primary",
60
+ oauth_port: int = 8080,
61
+ allow_update: bool = False,
62
+ **kwargs,
63
+ ):
64
+ self.creds: Optional[Credentials] = None
65
+ self.service: Optional[Resource] = None
66
+ self.calendar_id: str = calendar_id
67
+ self.oauth_port: int = oauth_port
68
+ self.access_token = access_token
69
+ self.credentials_path = credentials_path
70
+ self.token_path = token_path
71
+ self.allow_update = allow_update
72
+ self.scopes = scopes or []
68
73
 
69
- if not credentials_path:
70
- logger.error(
71
- "Google Calendar Tool : Please Provide Valid Credentials Path , You can refer https://developers.google.com/calendar/api/quickstart/python#authorize_credentials_for_a_desktop_application to create your credentials"
72
- )
73
- raise ValueError("Credential path is required")
74
- elif not os.path.exists(credentials_path):
75
- logger.error(
76
- "Google Calendar Tool : Credential file Path is invalid , please provide the full path of the credentials json file"
77
- )
78
- raise ValueError("Credentials Path is invalid")
74
+ super().__init__(
75
+ name="google_calendar_tools",
76
+ tools=[
77
+ self.list_events,
78
+ self.create_event,
79
+ self.update_event,
80
+ self.delete_event,
81
+ self.fetch_all_events,
82
+ self.find_available_slots,
83
+ self.list_calendars,
84
+ ],
85
+ **kwargs,
86
+ )
87
+ if not self.scopes:
88
+ # Add read permission by default
89
+ self.scopes.append(self.DEFAULT_SCOPES["read"])
90
+ # Add write permission if allow_update is True
91
+ if self.allow_update:
92
+ self.scopes.append(self.DEFAULT_SCOPES["write"])
79
93
 
80
- if not token_path:
81
- logger.warning(
82
- f"Google Calendar Tool : Token path is not provided, using {os.getcwd()}/token.json as default path"
94
+ # Validate that required scopes are present for requested operations
95
+ if self.allow_update and self.DEFAULT_SCOPES["write"] not in self.scopes:
96
+ raise ValueError(f"The scope {self.DEFAULT_SCOPES['write']} is required for write operations")
97
+ if self.DEFAULT_SCOPES["read"] not in self.scopes and self.DEFAULT_SCOPES["write"] not in self.scopes:
98
+ raise ValueError(
99
+ f"Either {self.DEFAULT_SCOPES['read']} or {self.DEFAULT_SCOPES['write']} is required for read operations"
83
100
  )
84
- token_path = "token.json"
85
101
 
86
- self.creds = None
87
- self.service = None
88
- self.token_path = token_path
89
- self.creds_path = credentials_path
90
- self.register(self.list_events)
91
- self.register(self.create_event)
102
+ def _auth(self) -> None:
103
+ """
104
+ Authenticate with Google Calendar API
105
+ """
106
+ if self.creds and self.creds.valid:
107
+ return
108
+
109
+ token_file = Path(self.token_path or "token.json")
110
+ creds_file = Path(self.credentials_path or "credentials.json")
111
+
112
+ if token_file.exists():
113
+ self.creds = Credentials.from_authorized_user_file(str(token_file), self.DEFAULT_SCOPES)
92
114
 
93
- @authenticated
94
- def list_events(self, limit: int = 10, date_from: str = datetime.date.today().isoformat()) -> str:
115
+ if not self.creds or not self.creds.valid:
116
+ if self.creds and self.creds.expired and self.creds.refresh_token:
117
+ self.creds.refresh(Request())
118
+ else:
119
+ client_config = {
120
+ "installed": {
121
+ "client_id": getenv("GOOGLE_CLIENT_ID"),
122
+ "client_secret": getenv("GOOGLE_CLIENT_SECRET"),
123
+ "project_id": getenv("GOOGLE_PROJECT_ID"),
124
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
125
+ "token_uri": "https://oauth2.googleapis.com/token",
126
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
127
+ "redirect_uris": [getenv("GOOGLE_REDIRECT_URI", "http://localhost")],
128
+ }
129
+ }
130
+ # File based authentication
131
+ if creds_file.exists():
132
+ flow = InstalledAppFlow.from_client_secrets_file(str(creds_file), self.scopes)
133
+ else:
134
+ flow = InstalledAppFlow.from_client_config(client_config, self.scopes)
135
+ # Opens up a browser window for OAuth authentication
136
+ self.creds = flow.run_local_server(port=self.oauth_port)
137
+
138
+ if self.creds:
139
+ token_file.write_text(self.creds.to_json())
140
+ log_debug("Successfully authenticated with Google Calendar API.")
141
+ log_info(f"Token file path: {token_file}")
142
+
143
+ @authenticate
144
+ def list_events(self, limit: int = 10, start_date: Optional[str] = None) -> str:
95
145
  """
96
- List events from the user's primary calendar.
146
+ List upcoming events from the user's Google Calendar.
97
147
 
98
148
  Args:
99
- limit (Optional[int]): Number of events to return , default value is 10
100
- date_from (str) : the start date to return events from in date isoformat. Defaults to current datetime.
149
+ limit (Optional[int]): Number of events to return, default value is 10
150
+ start_date (Optional[str]): The start date to return events from in ISO format (YYYY-MM-DDTHH:MM:SS)
101
151
 
152
+ Returns:
153
+ str: JSON string containing the Google Calendar events or error message
102
154
  """
103
- if date_from is None:
104
- date_from = datetime.datetime.now(datetime.timezone.utc).isoformat()
105
- elif isinstance(date_from, str):
106
- date_from = datetime.datetime.fromisoformat(date_from).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
155
+ if start_date is None:
156
+ start_date = datetime.datetime.now(datetime.timezone.utc).isoformat()
157
+ log_debug(f"No start date provided, using current datetime: {start_date}")
158
+ elif isinstance(start_date, str):
159
+ try:
160
+ start_date = datetime.datetime.fromisoformat(start_date).strftime("%Y-%m-%dT%H:%M:%S.%fZ")
161
+ except ValueError:
162
+ return json.dumps(
163
+ {"error": f"Invalid date format: {start_date}. Use ISO format (YYYY-MM-DDTHH:MM:SS)."}
164
+ )
107
165
 
108
166
  try:
109
- if self.service:
110
- events_result = (
111
- self.service.events()
112
- .list(
113
- calendarId="primary",
114
- timeMin=date_from,
115
- maxResults=limit,
116
- singleEvents=True,
117
- orderBy="startTime",
118
- )
119
- .execute()
167
+ service = cast(Resource, self.service)
168
+
169
+ events_result = (
170
+ service.events()
171
+ .list(
172
+ calendarId=self.calendar_id,
173
+ timeMin=start_date,
174
+ maxResults=limit,
175
+ singleEvents=True,
176
+ orderBy="startTime",
120
177
  )
121
- events = events_result.get("items", [])
122
- if not events:
123
- return json.dumps({"error": "No upcoming events found."})
124
- return json.dumps(events)
125
- else:
126
- return json.dumps({"error": "authentication issue"})
178
+ .execute()
179
+ )
180
+ events = events_result.get("items", [])
181
+ if not events:
182
+ return json.dumps({"message": "No upcoming events found."})
183
+ return json.dumps(events)
127
184
  except HttpError as error:
185
+ log_error(f"An error occurred: {error}")
128
186
  return json.dumps({"error": f"An error occurred: {error}"})
129
187
 
130
- @authenticated
188
+ @authenticate
131
189
  def create_event(
132
190
  self,
133
- start_datetime: str,
134
- end_datetime: str,
191
+ start_date: str,
192
+ end_date: str,
135
193
  title: Optional[str] = None,
136
194
  description: Optional[str] = None,
137
195
  location: Optional[str] = None,
138
- timezone: Optional[str] = None,
139
- attendees: List[str] = [],
196
+ timezone: Optional[str] = "UTC",
197
+ attendees: Optional[List[str]] = None,
198
+ add_google_meet_link: Optional[bool] = False,
199
+ notify_attendees: Optional[bool] = False,
140
200
  ) -> str:
141
201
  """
142
- Create a new event in the user's primary calendar.
202
+ Create a new event in the Google Calendar.
143
203
 
144
204
  Args:
145
- title (Optional[str]): Title of the Event
146
- description (Optional[str]) : Detailed description of the event
147
- location (Optional[str]) : Location of the event
148
- start_datetime (Optional[str]) : start date and time of the event
149
- end_datetime (Optional[str]) : end date and time of the event
150
- attendees (Optional[List[str]]) : List of emails of the attendees
151
- """
205
+ start_date (str): Start date and time of the event in ISO format (YYYY-MM-DDTHH:MM:SS)
206
+ end_date (str): End date and time of the event in ISO format (YYYY-MM-DDTHH:MM:SS)
207
+ title (Optional[str]): Title/summary of the event
208
+ description (Optional[str]): Detailed description of the event
209
+ location (Optional[str]): Location of the event
210
+ timezone (Optional[str]): Timezone for the event (default: UTC)
211
+ attendees (Optional[List[str]]): List of email addresses of the attendees
212
+ add_google_meet_link (Optional[bool]): Whether to add a Google Meet video link to the event
213
+ notify_attendees (Optional[bool]): Whether to send email notifications to attendees (default: False)
152
214
 
153
- attendees_list = [{"email": attendee} for attendee in attendees] if attendees else []
215
+ Returns:
216
+ str: JSON string containing the created Google Calendar event or error message
217
+ """
218
+ try:
219
+ # Format attendees if provided
220
+ attendees_list = [{"email": attendee} for attendee in attendees] if attendees else []
154
221
 
155
- start_time = datetime.datetime.fromisoformat(start_datetime).strftime("%Y-%m-%dT%H:%M:%S")
222
+ # Convert ISO string to datetime and format as required
223
+ try:
224
+ start_time = datetime.datetime.fromisoformat(start_date).strftime("%Y-%m-%dT%H:%M:%S")
225
+ end_time = datetime.datetime.fromisoformat(end_date).strftime("%Y-%m-%dT%H:%M:%S")
226
+ except ValueError:
227
+ return json.dumps({"error": "Invalid datetime format. Use ISO format (YYYY-MM-DDTHH:MM:SS)."})
156
228
 
157
- end_time = datetime.datetime.fromisoformat(end_datetime).strftime("%Y-%m-%dT%H:%M:%S")
158
- try:
159
- event = {
229
+ # Create event dictionary
230
+ event: Dict[str, Any] = {
160
231
  "summary": title,
161
232
  "location": location,
162
233
  "description": description,
@@ -164,11 +235,440 @@ class GoogleCalendarTools(Toolkit):
164
235
  "end": {"dateTime": end_time, "timeZone": timezone},
165
236
  "attendees": attendees_list,
166
237
  }
167
- if self.service:
168
- event_result = self.service.events().insert(calendarId="primary", body=event).execute()
169
- return json.dumps(event_result)
238
+
239
+ # Add Google Meet link if requested
240
+ if add_google_meet_link:
241
+ event["conferenceData"] = {
242
+ "createRequest": {"requestId": str(uuid.uuid4()), "conferenceSolutionKey": {"type": "hangoutsMeet"}}
243
+ }
244
+
245
+ # Remove None values
246
+ event = {k: v for k, v in event.items() if v is not None}
247
+
248
+ # Determine sendUpdates value based on notify_attendees parameter
249
+ send_updates = "all" if notify_attendees and attendees else "none"
250
+
251
+ service = cast(Resource, self.service)
252
+
253
+ event_result = (
254
+ service.events()
255
+ .insert(
256
+ calendarId=self.calendar_id,
257
+ body=event,
258
+ conferenceDataVersion=1 if add_google_meet_link else 0,
259
+ sendUpdates=send_updates,
260
+ )
261
+ .execute()
262
+ )
263
+ log_debug(f"Event created successfully in calendar {self.calendar_id}. Event ID: {event_result['id']}")
264
+ return json.dumps(event_result)
265
+ except HttpError as error:
266
+ log_error(f"An error occurred: {error}")
267
+ return json.dumps({"error": f"An error occurred: {error}"})
268
+
269
+ @authenticate
270
+ def update_event(
271
+ self,
272
+ event_id: str,
273
+ title: Optional[str] = None,
274
+ description: Optional[str] = None,
275
+ location: Optional[str] = None,
276
+ start_date: Optional[str] = None,
277
+ end_date: Optional[str] = None,
278
+ timezone: Optional[str] = None,
279
+ attendees: Optional[List[str]] = None,
280
+ notify_attendees: Optional[bool] = False,
281
+ ) -> str:
282
+ """
283
+ Update an existing event in the Google Calendar.
284
+
285
+ Args:
286
+ event_id (str): ID of the event to update
287
+ title (Optional[str]): New title/summary of the event
288
+ description (Optional[str]): New description of the event
289
+ location (Optional[str]): New location of the event
290
+ start_date (Optional[str]): New start date and time in ISO format (YYYY-MM-DDTHH:MM:SS)
291
+ end_date (Optional[str]): New end date and time in ISO format (YYYY-MM-DDTHH:MM:SS)
292
+ timezone (Optional[str]): New timezone for the event
293
+ attendees (Optional[List[str]]): Updated list of attendee email addresses
294
+ notify_attendees (Optional[bool]): Whether to send email notifications to attendees (default: False)
295
+
296
+ Returns:
297
+ str: JSON string containing the updated Google Calendar event or error message
298
+ """
299
+ try:
300
+ service = cast(Resource, self.service)
301
+
302
+ # First get the existing event to preserve its structure
303
+ event = service.events().get(calendarId=self.calendar_id, eventId=event_id).execute()
304
+
305
+ # Update only the fields that are provided
306
+ if title is not None:
307
+ event["summary"] = title
308
+ if description is not None:
309
+ event["description"] = description
310
+ if location is not None:
311
+ event["location"] = location
312
+ if attendees is not None:
313
+ event["attendees"] = [{"email": attendee} for attendee in attendees]
314
+
315
+ # Handle datetime updates
316
+ if start_date:
317
+ try:
318
+ start_time = datetime.datetime.fromisoformat(start_date).strftime("%Y-%m-%dT%H:%M:%S")
319
+ event["start"]["dateTime"] = start_time
320
+ if timezone:
321
+ event["start"]["timeZone"] = timezone
322
+ except ValueError:
323
+ return json.dumps({"error": f"Invalid start datetime format: {start_date}. Use ISO format."})
324
+
325
+ if end_date:
326
+ try:
327
+ end_time = datetime.datetime.fromisoformat(end_date).strftime("%Y-%m-%dT%H:%M:%S")
328
+ event["end"]["dateTime"] = end_time
329
+ if timezone:
330
+ event["end"]["timeZone"] = timezone
331
+ except ValueError:
332
+ return json.dumps({"error": f"Invalid end datetime format: {end_date}. Use ISO format."})
333
+
334
+ # Determine sendUpdates value based on notify_attendees parameter
335
+ send_updates = "all" if notify_attendees and attendees else "none"
336
+
337
+ # Update the event
338
+
339
+ updated_event = (
340
+ service.events()
341
+ .update(calendarId=self.calendar_id, eventId=event_id, body=event, sendUpdates=send_updates)
342
+ .execute()
343
+ )
344
+
345
+ log_debug(f"Event {event_id} updated successfully.")
346
+ return json.dumps(updated_event)
347
+ except HttpError as error:
348
+ log_error(f"An error occurred while updating event: {error}")
349
+ return json.dumps({"error": f"An error occurred: {error}"})
350
+
351
+ @authenticate
352
+ def delete_event(self, event_id: str, notify_attendees: Optional[bool] = True) -> str:
353
+ """
354
+ Delete an event from the Google Calendar.
355
+
356
+ Args:
357
+ event_id (str): ID of the event to delete
358
+ notify_attendees (Optional[bool]): Whether to send email notifications to attendees (default: False)
359
+
360
+ Returns:
361
+ str: JSON string containing success message or error message
362
+ """
363
+ try:
364
+ # Determine sendUpdates value based on notify_attendees parameter
365
+ send_updates = "all" if notify_attendees else "none"
366
+
367
+ service = cast(Resource, self.service)
368
+
369
+ service.events().delete(calendarId=self.calendar_id, eventId=event_id, sendUpdates=send_updates).execute()
370
+
371
+ log_debug(f"Event {event_id} deleted successfully.")
372
+ return json.dumps({"success": True, "message": f"Event {event_id} deleted successfully."})
373
+ except HttpError as error:
374
+ log_error(f"An error occurred while deleting event: {error}")
375
+ return json.dumps({"error": f"An error occurred: {error}"})
376
+
377
+ @authenticate
378
+ def fetch_all_events(
379
+ self,
380
+ max_results: int = 10,
381
+ start_date: Optional[str] = None,
382
+ end_date: Optional[str] = None,
383
+ ) -> str:
384
+ """
385
+ Fetch all Google Calendar events in a given date range.
386
+
387
+ Args:
388
+ start_date (Optional[str]): The minimum date to include events from in ISO format (YYYY-MM-DDTHH:MM:SS).
389
+ end_date (Optional[str]): The maximum date to include events up to in ISO format (YYYY-MM-DDTHH:MM:SS).
390
+
391
+ Returns:
392
+ str: JSON string containing all Google Calendar events or error message
393
+ """
394
+ try:
395
+ service = cast(Resource, self.service)
396
+
397
+ params = {
398
+ "calendarId": self.calendar_id,
399
+ "maxResults": min(max_results, 100),
400
+ "singleEvents": True,
401
+ "orderBy": "startTime",
402
+ }
403
+
404
+ # Set time parameters if provided
405
+ if start_date:
406
+ # Accept both string and already formatted ISO strings
407
+ if isinstance(start_date, str):
408
+ try:
409
+ # Try to parse and reformat to ensure proper timezone format
410
+ dt = datetime.datetime.fromisoformat(start_date)
411
+ if dt.tzinfo is None:
412
+ dt = dt.replace(tzinfo=datetime.timezone.utc)
413
+ params["timeMin"] = dt.isoformat()
414
+ except ValueError:
415
+ # If it's already a valid ISO string, use it directly
416
+ params["timeMin"] = start_date
417
+ else:
418
+ params["timeMin"] = start_date
419
+
420
+ if end_date:
421
+ # Accept both string and already formatted ISO strings
422
+ if isinstance(end_date, str):
423
+ try:
424
+ # Try to parse and reformat to ensure proper timezone format
425
+ dt = datetime.datetime.fromisoformat(end_date)
426
+ if dt.tzinfo is None:
427
+ dt = dt.replace(tzinfo=datetime.timezone.utc)
428
+ params["timeMax"] = dt.isoformat()
429
+ except ValueError:
430
+ # If it's already a valid ISO string, use it directly
431
+ params["timeMax"] = end_date
432
+ else:
433
+ params["timeMax"] = end_date
434
+
435
+ # Handle pagination
436
+ all_events = []
437
+ page_token = None
438
+
439
+ while True:
440
+ if page_token:
441
+ params["pageToken"] = page_token
442
+
443
+ events_result = service.events().list(**params).execute()
444
+ all_events.extend(events_result.get("items", []))
445
+
446
+ page_token = events_result.get("nextPageToken")
447
+ if not page_token:
448
+ break
449
+
450
+ log_debug(f"Fetched {len(all_events)} events from calendar: {self.calendar_id}")
451
+
452
+ if not all_events:
453
+ return json.dumps({"message": "No events found."})
454
+ return json.dumps(all_events)
455
+ except HttpError as error:
456
+ log_error(f"An error occurred while fetching events: {error}")
457
+ return json.dumps({"error": f"An error occurred: {error}"})
458
+
459
+ @authenticate
460
+ def find_available_slots(
461
+ self,
462
+ start_date: str,
463
+ end_date: str,
464
+ duration_minutes: int = 30,
465
+ ) -> str:
466
+ """
467
+ Find available time slots within a date range.
468
+
469
+ This method fetches your actual calendar events to determine busy periods,
470
+ then finds available slots within standard working hours (9 AM - 5 PM).
471
+
472
+ Args:
473
+ start_date (str): Start date to search from in ISO format (YYYY-MM-DD)
474
+ end_date (str): End date to search to in ISO format (YYYY-MM-DD)
475
+ duration_minutes (int): Length of the desired slot in minutes (default: 30 minutes)
476
+
477
+ Returns:
478
+ str: JSON string containing available Google Calendar time slots or error message
479
+ """
480
+ try:
481
+ start_dt = datetime.datetime.fromisoformat(start_date)
482
+ end_dt = datetime.datetime.fromisoformat(end_date)
483
+ # Ensure dates are timezone-aware (use UTC if no timezone specified)
484
+ if start_dt.tzinfo is None:
485
+ start_dt = start_dt.replace(tzinfo=datetime.timezone.utc)
486
+ if end_dt.tzinfo is None:
487
+ end_dt = end_dt.replace(tzinfo=datetime.timezone.utc)
488
+
489
+ # Get working hours from user settings
490
+ working_hours_json = self._get_working_hours()
491
+ working_hours_data = json.loads(working_hours_json)
492
+
493
+ if "error" not in working_hours_data:
494
+ working_hours_start = working_hours_data["start_hour"]
495
+ working_hours_end = working_hours_data["end_hour"]
496
+ timezone = working_hours_data["timezone"]
497
+ locale = working_hours_data["locale"]
498
+ log_debug(
499
+ f"Using working hours from settings: {working_hours_start}:00-{working_hours_end}:00 ({locale})"
500
+ )
170
501
  else:
171
- return json.dumps({"error": "authentication issue"})
502
+ # Fallback defaults
503
+ working_hours_start, working_hours_end = 9, 17
504
+ timezone = "UTC"
505
+ locale = "en"
506
+ log_debug("Using default working hours: 9:00-17:00")
507
+
508
+ # Fetch actual calendar events to determine busy periods
509
+ events_json = self.fetch_all_events(start_date=start_date, end_date=end_date)
510
+ events_data = json.loads(events_json)
511
+
512
+ if "error" in events_data:
513
+ return json.dumps({"error": events_data["error"]})
514
+
515
+ events = events_data if isinstance(events_data, list) else events_data.get("items", [])
516
+
517
+ # Extract busy periods from actual calendar events
518
+ busy_periods = []
519
+ for event in events:
520
+ # Skip all-day events and transparent events
521
+ if event.get("transparency") == "transparent":
522
+ continue
523
+
524
+ start_info = event.get("start", {})
525
+ end_info = event.get("end", {})
526
+
527
+ # Only process timed events (not all-day)
528
+ if "dateTime" in start_info and "dateTime" in end_info:
529
+ try:
530
+ start_time = datetime.datetime.fromisoformat(start_info["dateTime"].replace("Z", "+00:00"))
531
+ end_time = datetime.datetime.fromisoformat(end_info["dateTime"].replace("Z", "+00:00"))
532
+ busy_periods.append((start_time, end_time))
533
+ except (ValueError, KeyError) as e:
534
+ log_debug(f"Skipping invalid event: {e}")
535
+ continue
536
+
537
+ # Generate available slots within working hours
538
+ available_slots = []
539
+ current_date = start_dt.replace(hour=working_hours_start, minute=0, second=0, microsecond=0)
540
+ end_search = end_dt.replace(hour=working_hours_end, minute=0, second=0, microsecond=0)
541
+
542
+ while current_date <= end_search:
543
+ # Skip weekends if not in working hours
544
+ if current_date.weekday() >= 5: # Saturday=5, Sunday=6
545
+ current_date = (current_date + datetime.timedelta(days=1)).replace(
546
+ hour=working_hours_start, minute=0, second=0, microsecond=0
547
+ )
548
+ continue
549
+
550
+ slot_end = current_date + datetime.timedelta(minutes=duration_minutes)
551
+
552
+ # Check if this slot conflicts with any busy period
553
+ is_available = True
554
+ for busy_start, busy_end in busy_periods:
555
+ if not (slot_end <= busy_start or current_date >= busy_end):
556
+ is_available = False
557
+ break
558
+
559
+ # Only add slots within working hours
560
+ if is_available and slot_end.hour <= working_hours_end:
561
+ available_slots.append({"start": current_date.isoformat(), "end": slot_end.isoformat()})
562
+
563
+ # Move to next slot (30-minute intervals)
564
+ current_date += datetime.timedelta(minutes=30)
565
+
566
+ # Skip to next day at working hours start if past working hours end
567
+ if current_date.hour >= working_hours_end:
568
+ current_date = (current_date + datetime.timedelta(days=1)).replace(
569
+ hour=working_hours_start, minute=0, second=0, microsecond=0
570
+ )
571
+
572
+ result = {
573
+ "available_slots": available_slots,
574
+ "duration_minutes": duration_minutes,
575
+ "working_hours": {"start": f"{working_hours_start:02d}:00", "end": f"{working_hours_end:02d}:00"},
576
+ "timezone": timezone,
577
+ "locale": locale,
578
+ "events_analyzed": len(busy_periods),
579
+ }
580
+
581
+ log_debug(f"Found {len(available_slots)} available slots")
582
+ return json.dumps(result)
583
+
584
+ except Exception as e:
585
+ log_error(f"An error occurred while finding available slots: {e}")
586
+ return json.dumps({"error": f"An error occurred: {str(e)}"})
587
+
588
+ @authenticate
589
+ def _get_working_hours(self) -> str:
590
+ """
591
+ Get working hours based on user's calendar settings and locale.
592
+
593
+ Returns:
594
+ str: JSON string containing working hours information
595
+ """
596
+ try:
597
+ # Get all user settings
598
+ settings_result = self.service.settings().list().execute() # type: ignore
599
+ settings = settings_result.get("items", [])
600
+
601
+ # Process settings into a more usable format
602
+ user_prefs = {}
603
+ for setting in settings:
604
+ user_prefs[setting["id"]] = setting["value"]
605
+
606
+ # Extract relevant settings
607
+ timezone = user_prefs.get("timezone", "UTC")
608
+ locale = user_prefs.get("locale", "en")
609
+ week_start = int(user_prefs.get("weekStart", "0")) # 0=Sunday, 1=Monday, 6=Saturday
610
+ hide_weekends = user_prefs.get("hideWeekends", "false") == "true"
611
+
612
+ # Determine working hours based on locale/culture
613
+ if locale.startswith(("es", "it", "pt")): # Spain, Italy, Portugal
614
+ start_hour, end_hour = 9, 18
615
+ elif locale.startswith(("de", "nl", "dk", "se", "no")): # Northern Europe
616
+ start_hour, end_hour = 8, 17
617
+ elif locale.startswith(("ja", "ko")): # East Asia
618
+ start_hour, end_hour = 9, 18
619
+ else: # Default US/International
620
+ start_hour, end_hour = 9, 17
621
+
622
+ working_hours = {
623
+ "start_hour": start_hour,
624
+ "end_hour": end_hour,
625
+ "start_time": f"{start_hour:02d}:00",
626
+ "end_time": f"{end_hour:02d}:00",
627
+ "timezone": timezone,
628
+ "locale": locale,
629
+ "week_start": week_start,
630
+ "hide_weekends": hide_weekends,
631
+ }
632
+
633
+ log_debug(f"Working hours for locale {locale}: {start_hour}:00-{end_hour}:00")
634
+ return json.dumps(working_hours)
635
+
636
+ except HttpError as error:
637
+ log_error(f"An error occurred while getting working hours: {error}")
638
+ return json.dumps({"error": f"An error occurred: {error}"})
639
+
640
+ @authenticate
641
+ def list_calendars(self) -> str:
642
+ """
643
+ List all available Google Calendars for the authenticated user.
644
+
645
+ Returns:
646
+ str: JSON string containing available calendars with their IDs and names
647
+ """
648
+ try:
649
+ calendar_list = self.service.calendarList().list().execute() # type: ignore
650
+ calendars = calendar_list.get("items", [])
651
+
652
+ all_calendars = []
653
+ for calendar in calendars:
654
+ calendar_info = {
655
+ "id": calendar.get("id"),
656
+ "name": calendar.get("summary", "Unnamed Calendar"),
657
+ "description": calendar.get("description", ""),
658
+ "primary": calendar.get("primary", False),
659
+ "access_role": calendar.get("accessRole", "unknown"),
660
+ "color": calendar.get("backgroundColor", "#ffffff"),
661
+ }
662
+ all_calendars.append(calendar_info)
663
+
664
+ log_debug(f"Found {len(all_calendars)} calendars for user")
665
+ return json.dumps(
666
+ {
667
+ "calendars": all_calendars,
668
+ "current_default": self.calendar_id,
669
+ }
670
+ )
671
+
172
672
  except HttpError as error:
173
- logger.error(f"An error occurred: {error}")
673
+ log_error(f"An error occurred while listing calendars: {error}")
174
674
  return json.dumps({"error": f"An error occurred: {error}"})