agno 1.8.0__py3-none-any.whl → 2.0.0a1__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 (583) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +2781 -4126
  4. agno/api/agent.py +9 -65
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +6 -17
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -41
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +5 -21
  11. agno/api/schemas/evals.py +7 -16
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +5 -21
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +11 -7
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +9 -64
  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/db/__init__.py +24 -0
  25. agno/db/base.py +245 -0
  26. agno/db/dynamo/__init__.py +3 -0
  27. agno/db/dynamo/dynamo.py +1749 -0
  28. agno/db/dynamo/schemas.py +278 -0
  29. agno/db/dynamo/utils.py +684 -0
  30. agno/db/firestore/__init__.py +3 -0
  31. agno/db/firestore/firestore.py +1438 -0
  32. agno/db/firestore/schemas.py +130 -0
  33. agno/db/firestore/utils.py +278 -0
  34. agno/db/gcs_json/__init__.py +3 -0
  35. agno/db/gcs_json/gcs_json_db.py +1001 -0
  36. agno/db/gcs_json/utils.py +194 -0
  37. agno/db/in_memory/__init__.py +3 -0
  38. agno/db/in_memory/in_memory_db.py +888 -0
  39. agno/db/in_memory/utils.py +172 -0
  40. agno/db/json/__init__.py +3 -0
  41. agno/db/json/json_db.py +1051 -0
  42. agno/db/json/utils.py +196 -0
  43. agno/db/migrations/v1_to_v2.py +162 -0
  44. agno/db/mongo/__init__.py +3 -0
  45. agno/db/mongo/mongo.py +1417 -0
  46. agno/db/mongo/schemas.py +77 -0
  47. agno/db/mongo/utils.py +204 -0
  48. agno/db/mysql/__init__.py +3 -0
  49. agno/db/mysql/mysql.py +1719 -0
  50. agno/db/mysql/schemas.py +124 -0
  51. agno/db/mysql/utils.py +298 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1720 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +281 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1371 -0
  58. agno/db/redis/schemas.py +109 -0
  59. agno/db/redis/utils.py +288 -0
  60. agno/db/schemas/__init__.py +3 -0
  61. agno/db/schemas/evals.py +33 -0
  62. agno/db/schemas/knowledge.py +40 -0
  63. agno/db/schemas/memory.py +46 -0
  64. agno/db/singlestore/__init__.py +3 -0
  65. agno/db/singlestore/schemas.py +116 -0
  66. agno/db/singlestore/singlestore.py +1722 -0
  67. agno/db/singlestore/utils.py +327 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1680 -0
  71. agno/db/sqlite/utils.py +269 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +142 -43
  75. agno/eval/performance.py +88 -23
  76. agno/eval/reliability.py +73 -20
  77. agno/eval/utils.py +23 -13
  78. agno/integrations/discord/__init__.py +3 -0
  79. agno/{app → integrations}/discord/client.py +10 -10
  80. agno/knowledge/__init__.py +2 -2
  81. agno/{document → knowledge}/chunking/agentic.py +2 -2
  82. agno/{document → knowledge}/chunking/document.py +2 -2
  83. agno/{document → knowledge}/chunking/fixed.py +3 -3
  84. agno/{document → knowledge}/chunking/markdown.py +2 -2
  85. agno/{document → knowledge}/chunking/recursive.py +2 -2
  86. agno/{document → knowledge}/chunking/row.py +2 -2
  87. agno/knowledge/chunking/semantic.py +59 -0
  88. agno/knowledge/chunking/strategy.py +121 -0
  89. agno/knowledge/content.py +74 -0
  90. agno/knowledge/document/__init__.py +5 -0
  91. agno/{document → knowledge/document}/base.py +12 -2
  92. agno/knowledge/embedder/__init__.py +5 -0
  93. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  94. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  95. agno/{embedder → knowledge/embedder}/base.py +6 -0
  96. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  97. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  98. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  99. agno/{embedder → knowledge/embedder}/google.py +74 -1
  100. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  101. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  102. agno/knowledge/embedder/langdb.py +22 -0
  103. agno/knowledge/embedder/mistral.py +139 -0
  104. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  105. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  106. agno/knowledge/embedder/openai.py +223 -0
  107. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  108. agno/{embedder → knowledge/embedder}/together.py +1 -1
  109. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  110. agno/knowledge/knowledge.py +1515 -0
  111. agno/knowledge/reader/__init__.py +7 -0
  112. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  113. agno/knowledge/reader/base.py +88 -0
  114. agno/{document → knowledge}/reader/csv_reader.py +68 -15
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/knowledge/reader/gcs_reader.py +67 -0
  118. agno/{document → knowledge}/reader/json_reader.py +30 -9
  119. agno/{document → knowledge}/reader/markdown_reader.py +36 -9
  120. agno/{document → knowledge}/reader/pdf_reader.py +79 -21
  121. agno/knowledge/reader/reader_factory.py +275 -0
  122. agno/knowledge/reader/s3_reader.py +171 -0
  123. agno/{document → knowledge}/reader/text_reader.py +31 -10
  124. agno/knowledge/reader/url_reader.py +84 -0
  125. agno/knowledge/reader/web_search_reader.py +389 -0
  126. agno/{document → knowledge}/reader/website_reader.py +37 -10
  127. agno/knowledge/reader/wikipedia_reader.py +59 -0
  128. agno/knowledge/reader/youtube_reader.py +78 -0
  129. agno/knowledge/remote_content/remote_content.py +88 -0
  130. agno/{reranker → knowledge/reranker}/base.py +1 -1
  131. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  132. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  133. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  134. agno/knowledge/types.py +30 -0
  135. agno/knowledge/utils.py +169 -0
  136. agno/media.py +2 -2
  137. agno/memory/__init__.py +2 -10
  138. agno/memory/manager.py +1003 -148
  139. agno/models/aimlapi/__init__.py +2 -2
  140. agno/models/aimlapi/aimlapi.py +6 -6
  141. agno/models/anthropic/claude.py +129 -82
  142. agno/models/aws/bedrock.py +107 -175
  143. agno/models/aws/claude.py +64 -18
  144. agno/models/azure/ai_foundry.py +73 -23
  145. agno/models/base.py +347 -287
  146. agno/models/cerebras/cerebras.py +84 -27
  147. agno/models/cohere/chat.py +106 -98
  148. agno/models/dashscope/dashscope.py +14 -5
  149. agno/models/google/gemini.py +123 -53
  150. agno/models/groq/groq.py +97 -35
  151. agno/models/huggingface/huggingface.py +92 -27
  152. agno/models/ibm/watsonx.py +72 -13
  153. agno/models/litellm/chat.py +85 -13
  154. agno/models/message.py +38 -144
  155. agno/models/meta/llama.py +85 -49
  156. agno/models/metrics.py +120 -0
  157. agno/models/mistral/mistral.py +90 -21
  158. agno/models/ollama/__init__.py +0 -2
  159. agno/models/ollama/chat.py +84 -46
  160. agno/models/openai/chat.py +135 -27
  161. agno/models/openai/responses.py +233 -115
  162. agno/models/perplexity/perplexity.py +26 -2
  163. agno/models/portkey/portkey.py +0 -7
  164. agno/models/response.py +14 -8
  165. agno/models/utils.py +20 -0
  166. agno/models/vercel/__init__.py +2 -2
  167. agno/models/vercel/v0.py +1 -1
  168. agno/models/vllm/__init__.py +2 -2
  169. agno/models/vllm/vllm.py +3 -3
  170. agno/models/xai/xai.py +10 -10
  171. agno/os/__init__.py +3 -0
  172. agno/os/app.py +393 -0
  173. agno/os/auth.py +47 -0
  174. agno/os/config.py +103 -0
  175. agno/os/interfaces/agui/__init__.py +3 -0
  176. agno/os/interfaces/agui/agui.py +31 -0
  177. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  178. agno/{app → os/interfaces}/agui/utils.py +65 -28
  179. agno/os/interfaces/base.py +21 -0
  180. agno/os/interfaces/slack/__init__.py +3 -0
  181. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  182. agno/os/interfaces/slack/slack.py +33 -0
  183. agno/os/interfaces/whatsapp/__init__.py +3 -0
  184. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  185. agno/os/interfaces/whatsapp/whatsapp.py +30 -0
  186. agno/os/router.py +843 -0
  187. agno/os/routers/__init__.py +3 -0
  188. agno/os/routers/evals/__init__.py +3 -0
  189. agno/os/routers/evals/evals.py +204 -0
  190. agno/os/routers/evals/schemas.py +142 -0
  191. agno/os/routers/evals/utils.py +161 -0
  192. agno/os/routers/knowledge/__init__.py +3 -0
  193. agno/os/routers/knowledge/knowledge.py +413 -0
  194. agno/os/routers/knowledge/schemas.py +118 -0
  195. agno/os/routers/memory/__init__.py +3 -0
  196. agno/os/routers/memory/memory.py +179 -0
  197. agno/os/routers/memory/schemas.py +58 -0
  198. agno/os/routers/metrics/__init__.py +3 -0
  199. agno/os/routers/metrics/metrics.py +58 -0
  200. agno/os/routers/metrics/schemas.py +47 -0
  201. agno/os/routers/session/__init__.py +3 -0
  202. agno/os/routers/session/session.py +163 -0
  203. agno/os/schema.py +892 -0
  204. agno/{app/playground → os}/settings.py +8 -15
  205. agno/os/utils.py +270 -0
  206. agno/reasoning/azure_ai_foundry.py +4 -4
  207. agno/reasoning/deepseek.py +4 -4
  208. agno/reasoning/default.py +6 -11
  209. agno/reasoning/groq.py +4 -4
  210. agno/reasoning/helpers.py +4 -6
  211. agno/reasoning/ollama.py +4 -4
  212. agno/reasoning/openai.py +4 -4
  213. agno/run/{response.py → agent.py} +144 -72
  214. agno/run/base.py +44 -58
  215. agno/run/cancel.py +83 -0
  216. agno/run/team.py +133 -77
  217. agno/run/workflow.py +537 -12
  218. agno/session/__init__.py +10 -0
  219. agno/session/agent.py +244 -0
  220. agno/session/summary.py +225 -0
  221. agno/session/team.py +262 -0
  222. agno/{storage/session/v2 → session}/workflow.py +47 -24
  223. agno/team/__init__.py +15 -16
  224. agno/team/team.py +2967 -4243
  225. agno/tools/agentql.py +14 -5
  226. agno/tools/airflow.py +9 -4
  227. agno/tools/api.py +7 -3
  228. agno/tools/apify.py +2 -46
  229. agno/tools/arxiv.py +8 -3
  230. agno/tools/aws_lambda.py +7 -5
  231. agno/tools/aws_ses.py +7 -1
  232. agno/tools/baidusearch.py +4 -1
  233. agno/tools/bitbucket.py +4 -4
  234. agno/tools/brandfetch.py +14 -11
  235. agno/tools/bravesearch.py +4 -1
  236. agno/tools/brightdata.py +42 -22
  237. agno/tools/browserbase.py +13 -4
  238. agno/tools/calcom.py +12 -10
  239. agno/tools/calculator.py +10 -27
  240. agno/tools/cartesia.py +18 -13
  241. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  242. agno/tools/confluence.py +71 -18
  243. agno/tools/crawl4ai.py +7 -1
  244. agno/tools/csv_toolkit.py +9 -8
  245. agno/tools/dalle.py +18 -11
  246. agno/tools/daytona.py +13 -16
  247. agno/tools/decorator.py +6 -3
  248. agno/tools/desi_vocal.py +16 -7
  249. agno/tools/discord.py +11 -8
  250. agno/tools/docker.py +30 -42
  251. agno/tools/duckdb.py +34 -53
  252. agno/tools/duckduckgo.py +8 -7
  253. agno/tools/e2b.py +62 -62
  254. agno/tools/eleven_labs.py +35 -28
  255. agno/tools/email.py +4 -1
  256. agno/tools/evm.py +7 -1
  257. agno/tools/exa.py +19 -14
  258. agno/tools/fal.py +29 -29
  259. agno/tools/file.py +9 -8
  260. agno/tools/financial_datasets.py +25 -44
  261. agno/tools/firecrawl.py +22 -22
  262. agno/tools/function.py +68 -17
  263. agno/tools/giphy.py +22 -10
  264. agno/tools/github.py +48 -126
  265. agno/tools/gmail.py +46 -62
  266. agno/tools/google_bigquery.py +7 -6
  267. agno/tools/google_maps.py +11 -26
  268. agno/tools/googlesearch.py +7 -2
  269. agno/tools/googlesheets.py +21 -17
  270. agno/tools/hackernews.py +9 -5
  271. agno/tools/jina.py +5 -4
  272. agno/tools/jira.py +18 -9
  273. agno/tools/knowledge.py +31 -32
  274. agno/tools/linear.py +18 -33
  275. agno/tools/linkup.py +5 -1
  276. agno/tools/local_file_system.py +8 -5
  277. agno/tools/lumalab.py +31 -19
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +32 -14
  282. agno/tools/models/gemini.py +58 -31
  283. agno/tools/models/groq.py +29 -20
  284. agno/tools/models/nebius.py +27 -11
  285. agno/tools/models_labs.py +39 -15
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +134 -0
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +57 -26
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +62 -46
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +54 -41
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +95 -118
  335. agno/utils/knowledge.py +29 -0
  336. agno/utils/location.py +2 -2
  337. agno/utils/log.py +2 -2
  338. agno/utils/mcp.py +11 -5
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/claude.py +6 -4
  342. agno/utils/models/mistral.py +8 -7
  343. agno/utils/models/schema_utils.py +3 -3
  344. agno/utils/pprint.py +33 -32
  345. agno/utils/print_response/agent.py +779 -0
  346. agno/utils/print_response/team.py +1565 -0
  347. agno/utils/print_response/workflow.py +1451 -0
  348. agno/utils/prompts.py +14 -14
  349. agno/utils/reasoning.py +87 -0
  350. agno/utils/response.py +42 -42
  351. agno/utils/string.py +8 -22
  352. agno/utils/team.py +50 -0
  353. agno/utils/timer.py +2 -2
  354. agno/vectordb/base.py +33 -21
  355. agno/vectordb/cassandra/cassandra.py +287 -23
  356. agno/vectordb/chroma/chromadb.py +482 -59
  357. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  358. agno/vectordb/couchbase/couchbase.py +309 -29
  359. agno/vectordb/lancedb/lance_db.py +360 -21
  360. agno/vectordb/langchaindb/__init__.py +5 -0
  361. agno/vectordb/langchaindb/langchaindb.py +145 -0
  362. agno/vectordb/lightrag/__init__.py +5 -0
  363. agno/vectordb/lightrag/lightrag.py +374 -0
  364. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  365. agno/vectordb/milvus/milvus.py +242 -32
  366. agno/vectordb/mongodb/mongodb.py +200 -24
  367. agno/vectordb/pgvector/pgvector.py +319 -37
  368. agno/vectordb/pineconedb/pineconedb.py +221 -27
  369. agno/vectordb/qdrant/qdrant.py +356 -14
  370. agno/vectordb/singlestore/singlestore.py +286 -29
  371. agno/vectordb/surrealdb/surrealdb.py +187 -7
  372. agno/vectordb/upstashdb/upstashdb.py +342 -26
  373. agno/vectordb/weaviate/weaviate.py +227 -165
  374. agno/workflow/__init__.py +17 -13
  375. agno/workflow/{v2/condition.py → condition.py} +135 -32
  376. agno/workflow/{v2/loop.py → loop.py} +115 -28
  377. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  378. agno/workflow/{v2/router.py → router.py} +133 -32
  379. agno/workflow/{v2/step.py → step.py} +200 -42
  380. agno/workflow/{v2/steps.py → steps.py} +147 -66
  381. agno/workflow/types.py +482 -0
  382. agno/workflow/workflow.py +2394 -696
  383. agno-2.0.0a1.dist-info/METADATA +355 -0
  384. agno-2.0.0a1.dist-info/RECORD +514 -0
  385. agno/agent/metrics.py +0 -107
  386. agno/api/app.py +0 -35
  387. agno/api/playground.py +0 -92
  388. agno/api/schemas/app.py +0 -12
  389. agno/api/schemas/playground.py +0 -22
  390. agno/api/schemas/user.py +0 -35
  391. agno/api/schemas/workspace.py +0 -46
  392. agno/api/user.py +0 -160
  393. agno/api/workflows.py +0 -33
  394. agno/api/workspace.py +0 -175
  395. agno/app/agui/__init__.py +0 -3
  396. agno/app/agui/app.py +0 -17
  397. agno/app/agui/sync_router.py +0 -120
  398. agno/app/base.py +0 -186
  399. agno/app/discord/__init__.py +0 -3
  400. agno/app/fastapi/__init__.py +0 -3
  401. agno/app/fastapi/app.py +0 -107
  402. agno/app/fastapi/async_router.py +0 -457
  403. agno/app/fastapi/sync_router.py +0 -448
  404. agno/app/playground/app.py +0 -228
  405. agno/app/playground/async_router.py +0 -1050
  406. agno/app/playground/deploy.py +0 -249
  407. agno/app/playground/operator.py +0 -183
  408. agno/app/playground/schemas.py +0 -220
  409. agno/app/playground/serve.py +0 -55
  410. agno/app/playground/sync_router.py +0 -1042
  411. agno/app/playground/utils.py +0 -46
  412. agno/app/settings.py +0 -15
  413. agno/app/slack/__init__.py +0 -3
  414. agno/app/slack/app.py +0 -19
  415. agno/app/slack/sync_router.py +0 -92
  416. agno/app/utils.py +0 -54
  417. agno/app/whatsapp/__init__.py +0 -3
  418. agno/app/whatsapp/app.py +0 -15
  419. agno/app/whatsapp/sync_router.py +0 -197
  420. agno/cli/auth_server.py +0 -249
  421. agno/cli/config.py +0 -274
  422. agno/cli/console.py +0 -88
  423. agno/cli/credentials.py +0 -23
  424. agno/cli/entrypoint.py +0 -571
  425. agno/cli/operator.py +0 -357
  426. agno/cli/settings.py +0 -96
  427. agno/cli/ws/ws_cli.py +0 -817
  428. agno/constants.py +0 -13
  429. agno/document/__init__.py +0 -5
  430. agno/document/chunking/semantic.py +0 -45
  431. agno/document/chunking/strategy.py +0 -31
  432. agno/document/reader/__init__.py +0 -5
  433. agno/document/reader/base.py +0 -47
  434. agno/document/reader/docx_reader.py +0 -60
  435. agno/document/reader/gcs/pdf_reader.py +0 -44
  436. agno/document/reader/s3/pdf_reader.py +0 -59
  437. agno/document/reader/s3/text_reader.py +0 -63
  438. agno/document/reader/url_reader.py +0 -59
  439. agno/document/reader/youtube_reader.py +0 -58
  440. agno/embedder/__init__.py +0 -5
  441. agno/embedder/langdb.py +0 -80
  442. agno/embedder/mistral.py +0 -82
  443. agno/embedder/openai.py +0 -78
  444. agno/file/__init__.py +0 -5
  445. agno/file/file.py +0 -16
  446. agno/file/local/csv.py +0 -32
  447. agno/file/local/txt.py +0 -19
  448. agno/infra/app.py +0 -240
  449. agno/infra/base.py +0 -144
  450. agno/infra/context.py +0 -20
  451. agno/infra/db_app.py +0 -52
  452. agno/infra/resource.py +0 -205
  453. agno/infra/resources.py +0 -55
  454. agno/knowledge/agent.py +0 -698
  455. agno/knowledge/arxiv.py +0 -33
  456. agno/knowledge/combined.py +0 -36
  457. agno/knowledge/csv.py +0 -144
  458. agno/knowledge/csv_url.py +0 -124
  459. agno/knowledge/document.py +0 -223
  460. agno/knowledge/docx.py +0 -137
  461. agno/knowledge/firecrawl.py +0 -34
  462. agno/knowledge/gcs/__init__.py +0 -0
  463. agno/knowledge/gcs/base.py +0 -39
  464. agno/knowledge/gcs/pdf.py +0 -125
  465. agno/knowledge/json.py +0 -137
  466. agno/knowledge/langchain.py +0 -71
  467. agno/knowledge/light_rag.py +0 -273
  468. agno/knowledge/llamaindex.py +0 -66
  469. agno/knowledge/markdown.py +0 -154
  470. agno/knowledge/pdf.py +0 -164
  471. agno/knowledge/pdf_bytes.py +0 -42
  472. agno/knowledge/pdf_url.py +0 -148
  473. agno/knowledge/s3/__init__.py +0 -0
  474. agno/knowledge/s3/base.py +0 -64
  475. agno/knowledge/s3/pdf.py +0 -33
  476. agno/knowledge/s3/text.py +0 -34
  477. agno/knowledge/text.py +0 -141
  478. agno/knowledge/url.py +0 -46
  479. agno/knowledge/website.py +0 -179
  480. agno/knowledge/wikipedia.py +0 -32
  481. agno/knowledge/youtube.py +0 -35
  482. agno/memory/agent.py +0 -423
  483. agno/memory/classifier.py +0 -104
  484. agno/memory/db/__init__.py +0 -5
  485. agno/memory/db/base.py +0 -42
  486. agno/memory/db/mongodb.py +0 -189
  487. agno/memory/db/postgres.py +0 -203
  488. agno/memory/db/sqlite.py +0 -193
  489. agno/memory/memory.py +0 -22
  490. agno/memory/row.py +0 -36
  491. agno/memory/summarizer.py +0 -201
  492. agno/memory/summary.py +0 -19
  493. agno/memory/team.py +0 -415
  494. agno/memory/v2/__init__.py +0 -2
  495. agno/memory/v2/db/__init__.py +0 -1
  496. agno/memory/v2/db/base.py +0 -42
  497. agno/memory/v2/db/firestore.py +0 -339
  498. agno/memory/v2/db/mongodb.py +0 -196
  499. agno/memory/v2/db/postgres.py +0 -214
  500. agno/memory/v2/db/redis.py +0 -187
  501. agno/memory/v2/db/schema.py +0 -54
  502. agno/memory/v2/db/sqlite.py +0 -209
  503. agno/memory/v2/manager.py +0 -437
  504. agno/memory/v2/memory.py +0 -1097
  505. agno/memory/v2/schema.py +0 -55
  506. agno/memory/v2/summarizer.py +0 -215
  507. agno/memory/workflow.py +0 -38
  508. agno/models/ollama/tools.py +0 -430
  509. agno/models/qwen/__init__.py +0 -5
  510. agno/playground/__init__.py +0 -10
  511. agno/playground/deploy.py +0 -3
  512. agno/playground/playground.py +0 -3
  513. agno/playground/serve.py +0 -3
  514. agno/playground/settings.py +0 -3
  515. agno/reranker/__init__.py +0 -0
  516. agno/run/v2/__init__.py +0 -0
  517. agno/run/v2/workflow.py +0 -567
  518. agno/storage/__init__.py +0 -0
  519. agno/storage/agent/__init__.py +0 -0
  520. agno/storage/agent/dynamodb.py +0 -1
  521. agno/storage/agent/json.py +0 -1
  522. agno/storage/agent/mongodb.py +0 -1
  523. agno/storage/agent/postgres.py +0 -1
  524. agno/storage/agent/singlestore.py +0 -1
  525. agno/storage/agent/sqlite.py +0 -1
  526. agno/storage/agent/yaml.py +0 -1
  527. agno/storage/base.py +0 -60
  528. agno/storage/dynamodb.py +0 -673
  529. agno/storage/firestore.py +0 -297
  530. agno/storage/gcs_json.py +0 -261
  531. agno/storage/in_memory.py +0 -234
  532. agno/storage/json.py +0 -237
  533. agno/storage/mongodb.py +0 -328
  534. agno/storage/mysql.py +0 -685
  535. agno/storage/postgres.py +0 -682
  536. agno/storage/redis.py +0 -336
  537. agno/storage/session/__init__.py +0 -16
  538. agno/storage/session/agent.py +0 -64
  539. agno/storage/session/team.py +0 -63
  540. agno/storage/session/v2/__init__.py +0 -5
  541. agno/storage/session/workflow.py +0 -61
  542. agno/storage/singlestore.py +0 -606
  543. agno/storage/sqlite.py +0 -646
  544. agno/storage/workflow/__init__.py +0 -0
  545. agno/storage/workflow/mongodb.py +0 -1
  546. agno/storage/workflow/postgres.py +0 -1
  547. agno/storage/workflow/sqlite.py +0 -1
  548. agno/storage/yaml.py +0 -241
  549. agno/tools/thinking.py +0 -73
  550. agno/utils/defaults.py +0 -57
  551. agno/utils/filesystem.py +0 -39
  552. agno/utils/git.py +0 -52
  553. agno/utils/json_io.py +0 -30
  554. agno/utils/load_env.py +0 -19
  555. agno/utils/py_io.py +0 -19
  556. agno/utils/pyproject.py +0 -18
  557. agno/utils/resource_filter.py +0 -31
  558. agno/workflow/v2/__init__.py +0 -21
  559. agno/workflow/v2/types.py +0 -357
  560. agno/workflow/v2/workflow.py +0 -3312
  561. agno/workspace/__init__.py +0 -0
  562. agno/workspace/config.py +0 -325
  563. agno/workspace/enums.py +0 -6
  564. agno/workspace/helpers.py +0 -52
  565. agno/workspace/operator.py +0 -757
  566. agno/workspace/settings.py +0 -158
  567. agno-1.8.0.dist-info/METADATA +0 -979
  568. agno-1.8.0.dist-info/RECORD +0 -565
  569. agno-1.8.0.dist-info/entry_points.txt +0 -3
  570. /agno/{app → db/migrations}/__init__.py +0 -0
  571. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  572. /agno/{cli → integrations}/__init__.py +0 -0
  573. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  574. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  575. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  576. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  577. /agno/{app → os/interfaces}/slack/security.py +0 -0
  578. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  579. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  580. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  581. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
  582. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
  583. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
@@ -1,46 +0,0 @@
1
- from typing import Optional
2
-
3
- from fastapi import HTTPException, UploadFile
4
-
5
- from agno.media import Audio, Image, Video
6
- from agno.media import File as FileMedia
7
- from agno.utils.log import logger
8
-
9
-
10
- def process_image(file: UploadFile) -> Image:
11
- content = file.file.read()
12
- if not content:
13
- raise HTTPException(status_code=400, detail="Empty file")
14
- return Image(content=content)
15
-
16
-
17
- def process_audio(file: UploadFile) -> Audio:
18
- content = file.file.read()
19
- if not content:
20
- raise HTTPException(status_code=400, detail="Empty file")
21
- format = None
22
- if file.filename and "." in file.filename:
23
- format = file.filename.split(".")[-1].lower()
24
- elif file.content_type:
25
- format = file.content_type.split("/")[-1]
26
-
27
- return Audio(content=content, format=format)
28
-
29
-
30
- def process_video(file: UploadFile) -> Video:
31
- content = file.file.read()
32
- if not content:
33
- raise HTTPException(status_code=400, detail="Empty file")
34
- return Video(content=content, format=file.content_type)
35
-
36
-
37
- def process_document(file: UploadFile) -> Optional[FileMedia]:
38
- try:
39
- content = file.file.read()
40
- if not content:
41
- raise HTTPException(status_code=400, detail="Empty file")
42
-
43
- return FileMedia(content=content)
44
- except Exception as e:
45
- logger.error(f"Error processing document {file.filename}: {e}")
46
- return None
agno/app/settings.py DELETED
@@ -1,15 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pydantic_settings import BaseSettings
4
-
5
-
6
- class APIAppSettings(BaseSettings):
7
- """App settings for API-based apps that can be set using environment variables.
8
-
9
- Reference: https://pydantic-docs.helpmanual.io/usage/settings/
10
- """
11
-
12
- title: str = "agno-app"
13
-
14
- # Set to False to disable docs server at /docs and /redoc
15
- docs_enabled: bool = True
@@ -1,3 +0,0 @@
1
- from agno.app.slack.app import SlackAPI
2
-
3
- __all__ = ["SlackAPI"]
agno/app/slack/app.py DELETED
@@ -1,19 +0,0 @@
1
- import logging
2
-
3
- from fastapi.routing import APIRouter
4
-
5
- from agno.app.base import BaseAPIApp
6
- from agno.app.slack.async_router import get_async_router
7
- from agno.app.slack.sync_router import get_sync_router
8
-
9
- logger = logging.getLogger(__name__)
10
-
11
-
12
- class SlackAPI(BaseAPIApp):
13
- type = "slack"
14
-
15
- def get_router(self) -> APIRouter:
16
- return get_sync_router(agent=self.agent, team=self.team)
17
-
18
- def get_async_router(self) -> APIRouter:
19
- return get_async_router(agent=self.agent, team=self.team)
@@ -1,92 +0,0 @@
1
- from typing import Optional, cast
2
-
3
- from fastapi import APIRouter, BackgroundTasks, HTTPException, Request
4
-
5
- from agno.agent.agent import Agent
6
- from agno.app.slack.security import verify_slack_signature
7
- from agno.team.team import Team
8
- from agno.tools.slack import SlackTools
9
- from agno.utils.log import log_info
10
-
11
-
12
- def get_sync_router(agent: Optional[Agent] = None, team: Optional[Team] = None) -> APIRouter:
13
- router = APIRouter()
14
-
15
- @router.post("/slack/events")
16
- def slack_events(request: Request, background_tasks: BackgroundTasks):
17
- body = cast(bytes, request.body())
18
- timestamp = request.headers.get("X-Slack-Request-Timestamp")
19
- slack_signature = request.headers.get("X-Slack-Signature", "")
20
-
21
- if not timestamp or not slack_signature:
22
- raise HTTPException(status_code=400, detail="Missing Slack headers")
23
-
24
- if not verify_slack_signature(body, timestamp, slack_signature):
25
- raise HTTPException(status_code=403, detail="Invalid signature")
26
-
27
- data = cast(dict, request.json())
28
-
29
- # Handle URL verification
30
- if data.get("type") == "url_verification":
31
- return {"challenge": data.get("challenge")}
32
-
33
- # Process other event types (e.g., message events) asynchronously
34
- if "event" in data:
35
- event = data["event"]
36
- if event.get("bot_id"):
37
- log_info("bot event")
38
- pass
39
- else:
40
- background_tasks.add_task(_process_slack_event, event)
41
-
42
- return {"status": "ok"}
43
-
44
- def _process_slack_event(event: dict):
45
- if event.get("type") == "message":
46
- user = None
47
- message_text = event.get("text")
48
- channel_id = event.get("channel", "")
49
- user = event.get("user")
50
- if event.get("thread_ts"):
51
- ts = event.get("thread_ts", "")
52
- else:
53
- ts = event.get("ts", "")
54
-
55
- # Use the timestamp as the session id, so that each thread is a separate session
56
- session_id = ts
57
-
58
- if agent:
59
- response = agent.run(message_text, user_id=user if user else None, session_id=session_id)
60
- elif team:
61
- response = team.run(message_text, user_id=user if user else None, session_id=session_id) # type: ignore
62
-
63
- if response.reasoning_content:
64
- _send_slack_message(
65
- channel=channel_id, message=f"Reasoning: \n{response.reasoning_content}", thread_ts=ts, italics=True
66
- )
67
- _send_slack_message(channel=channel_id, message=response.content or "", thread_ts=ts)
68
-
69
- def _send_slack_message(channel: str, thread_ts: str, message: str, italics: bool = False):
70
- if len(message) <= 40000:
71
- if italics:
72
- # Handle multi-line messages by making each line italic
73
- formatted_message = "\n".join([f"_{line}_" for line in message.split("\n")])
74
- SlackTools().send_message_thread(channel=channel, text=formatted_message or "", thread_ts=thread_ts)
75
- else:
76
- SlackTools().send_message_thread(channel=channel, text=message or "", thread_ts=thread_ts)
77
- return
78
-
79
- # Split message into batches of 4000 characters (WhatsApp message limit is 4096)
80
- message_batches = [message[i : i + 40000] for i in range(0, len(message), 40000)]
81
-
82
- # Add a prefix with the batch number
83
- for i, batch in enumerate(message_batches, 1):
84
- batch_message = f"[{i}/{len(message_batches)}] {batch}"
85
- if italics:
86
- # Handle multi-line messages by making each line italic
87
- formatted_batch = "\n".join([f"_{line}_" for line in batch_message.split("\n")])
88
- SlackTools().send_message_thread(channel=channel, text=formatted_batch or "", thread_ts=thread_ts)
89
- else:
90
- SlackTools().send_message_thread(channel=channel, text=message or "", thread_ts=thread_ts)
91
-
92
- return router
agno/app/utils.py DELETED
@@ -1,54 +0,0 @@
1
- from typing import Optional
2
- from uuid import uuid4
3
-
4
- from fastapi import HTTPException, UploadFile
5
-
6
- from agno.media import Audio, Image, Video
7
- from agno.media import File as FileMedia
8
- from agno.utils.log import logger
9
-
10
-
11
- def process_image(file: UploadFile) -> Image:
12
- content = file.file.read()
13
- if not content:
14
- raise HTTPException(status_code=400, detail="Empty file")
15
- return Image(content=content)
16
-
17
-
18
- def process_audio(file: UploadFile) -> Audio:
19
- content = file.file.read()
20
- if not content:
21
- raise HTTPException(status_code=400, detail="Empty file")
22
- format = None
23
- if file.filename and "." in file.filename:
24
- format = file.filename.split(".")[-1].lower()
25
- elif file.content_type:
26
- format = file.content_type.split("/")[-1]
27
-
28
- return Audio(content=content, format=format)
29
-
30
-
31
- def process_video(file: UploadFile) -> Video:
32
- content = file.file.read()
33
- if not content:
34
- raise HTTPException(status_code=400, detail="Empty file")
35
- return Video(content=content, format=file.content_type)
36
-
37
-
38
- def process_document(file: UploadFile) -> Optional[FileMedia]:
39
- try:
40
- content = file.file.read()
41
- if not content:
42
- raise HTTPException(status_code=400, detail="Empty file")
43
-
44
- return FileMedia(content=content, mime_type=file.content_type)
45
- except Exception as e:
46
- logger.error(f"Error processing document {file.filename}: {e}")
47
- return None
48
-
49
-
50
- def generate_id(name: Optional[str] = None) -> str:
51
- if name:
52
- return name.lower().replace(" ", "-").replace("_", "-")
53
- else:
54
- return str(uuid4())
@@ -1,3 +0,0 @@
1
- from agno.app.whatsapp.app import WhatsappAPI
2
-
3
- __all__ = ["WhatsappAPI"]
agno/app/whatsapp/app.py DELETED
@@ -1,15 +0,0 @@
1
- from fastapi.routing import APIRouter
2
-
3
- from agno.app.base import BaseAPIApp
4
- from agno.app.whatsapp.async_router import get_async_router
5
- from agno.app.whatsapp.sync_router import get_sync_router
6
-
7
-
8
- class WhatsappAPI(BaseAPIApp):
9
- type = "whatsapp"
10
-
11
- def get_router(self) -> APIRouter:
12
- return get_sync_router(agent=self.agent, team=self.team)
13
-
14
- def get_async_router(self) -> APIRouter:
15
- return get_async_router(agent=self.agent, team=self.team)
@@ -1,197 +0,0 @@
1
- import base64
2
- from os import getenv
3
- from typing import Optional, cast
4
-
5
- from fastapi import APIRouter, BackgroundTasks, HTTPException, Request
6
- from fastapi.responses import PlainTextResponse
7
-
8
- from agno.agent.agent import Agent
9
- from agno.media import Audio, File, Image, Video
10
- from agno.team.team import Team
11
- from agno.tools.whatsapp import WhatsAppTools
12
- from agno.utils.log import log_debug, log_error, log_info, log_warning
13
- from agno.utils.whatsapp import get_media, send_image_message, typing_indicator, upload_media
14
-
15
- from .security import validate_webhook_signature
16
-
17
-
18
- def get_sync_router(agent: Optional[Agent] = None, team: Optional[Team] = None) -> APIRouter:
19
- router = APIRouter()
20
-
21
- if agent is None and team is None:
22
- raise ValueError("Either agent or team must be provided.")
23
-
24
- @router.get("/status")
25
- def status():
26
- return {"status": "available"}
27
-
28
- @router.get("/webhook")
29
- def verify_webhook(request: Request):
30
- """Handle WhatsApp webhook verification"""
31
- mode = request.query_params.get("hub.mode")
32
- token = request.query_params.get("hub.verify_token")
33
- challenge = request.query_params.get("hub.challenge")
34
-
35
- verify_token = getenv("WHATSAPP_VERIFY_TOKEN")
36
- if not verify_token:
37
- raise HTTPException(status_code=500, detail="WHATSAPP_VERIFY_TOKEN is not set")
38
-
39
- if mode == "subscribe" and token == verify_token:
40
- if not challenge:
41
- raise HTTPException(status_code=400, detail="No challenge received")
42
- return PlainTextResponse(content=challenge)
43
-
44
- raise HTTPException(status_code=403, detail="Invalid verify token or mode")
45
-
46
- @router.post("/webhook")
47
- def webhook(request: Request, background_tasks: BackgroundTasks):
48
- """Handle incoming WhatsApp messages"""
49
- try:
50
- # Get raw payload for signature validation
51
- payload = cast(bytes, request.body())
52
- signature = request.headers.get("X-Hub-Signature-256")
53
-
54
- # Validate webhook signature
55
- if not validate_webhook_signature(payload, signature):
56
- log_warning("Invalid webhook signature")
57
- raise HTTPException(status_code=403, detail="Invalid signature")
58
-
59
- body = cast(dict, request.json())
60
-
61
- # Validate webhook data
62
- if body.get("object") != "whatsapp_business_account":
63
- log_warning(f"Received non-WhatsApp webhook object: {body.get('object')}")
64
- return {"status": "ignored"}
65
-
66
- # Process messages in background
67
- for entry in body.get("entry", []):
68
- for change in entry.get("changes", []):
69
- messages = change.get("value", {}).get("messages", [])
70
-
71
- if not messages:
72
- continue
73
-
74
- message = messages[0]
75
- background_tasks.add_task(process_message, message, agent, team)
76
-
77
- return {"status": "processing"}
78
-
79
- except Exception as e:
80
- log_error(f"Error processing webhook: {str(e)}")
81
- raise HTTPException(status_code=500, detail=str(e))
82
-
83
- def process_message(message: dict, agent: Optional[Agent], team: Optional[Team]):
84
- """Process a single WhatsApp message in the background"""
85
- try:
86
- message_image = None
87
- message_video = None
88
- message_audio = None
89
- message_doc = None
90
-
91
- message_id = message.get("id")
92
- typing_indicator(message_id)
93
-
94
- if message.get("type") == "text":
95
- message_text = message["text"]["body"]
96
- elif message.get("type") == "image":
97
- try:
98
- message_text = message["image"]["caption"]
99
- except Exception:
100
- message_text = "Describe the image"
101
- message_image = message["image"]["id"]
102
- elif message.get("type") == "video":
103
- try:
104
- message_text = message["video"]["caption"]
105
- except Exception:
106
- message_text = "Describe the video"
107
- message_video = message["video"]["id"]
108
- elif message.get("type") == "audio":
109
- message_text = "Reply to audio"
110
- message_audio = message["audio"]["id"]
111
- elif message.get("type") == "document":
112
- message_text = "Process the document"
113
- message_doc = message["document"]["id"]
114
- else:
115
- return
116
-
117
- phone_number = message.get("from", "")
118
- log_debug(f"Processing message from {phone_number}: {message_text}")
119
-
120
- # Generate and send response
121
- if agent:
122
- response = agent.run(
123
- message_text,
124
- user_id=phone_number,
125
- images=[Image(content=get_media(message_image))] if message_image else None,
126
- files=[File(content=get_media(message_doc))] if message_doc else None,
127
- videos=[Video(content=get_media(message_video))] if message_video else None,
128
- audio=[Audio(content=get_media(message_audio))] if message_audio else None,
129
- )
130
- elif team:
131
- response = team.run( # type: ignore
132
- message_text,
133
- user_id=phone_number,
134
- files=[File(content=get_media(message_doc))] if message_doc else None,
135
- images=[Image(content=get_media(message_image))] if message_image else None,
136
- videos=[Video(content=get_media(message_video))] if message_video else None,
137
- audio=[Audio(content=get_media(message_audio))] if message_audio else None,
138
- )
139
-
140
- if response.reasoning_content:
141
- _send_whatsapp_message(phone_number, f"Reasoning: \n{response.reasoning_content}", italics=True)
142
-
143
- if response.images:
144
- number_of_images = len(response.images)
145
- log_info(f"images generated: f{number_of_images}")
146
- for i in range(number_of_images):
147
- image_content = response.images[0].content
148
- image_bytes = None
149
- if isinstance(image_content, bytes):
150
- try:
151
- decoded_string = image_content.decode("utf-8")
152
-
153
- image_bytes = base64.b64decode(decoded_string)
154
- except UnicodeDecodeError:
155
- image_bytes = image_content
156
- elif isinstance(image_content, str):
157
- image_bytes = base64.b64decode(image_content)
158
- else:
159
- log_error(f"Unexpected image content type: {type(image_content)} for user {phone_number}")
160
-
161
- if image_bytes:
162
- media_id = upload_media(media_data=image_bytes, mime_type="image/png", filename="image.png")
163
- send_image_message(media_id=media_id, recipient=phone_number, text=response.content)
164
- else:
165
- log_warning(
166
- f"Could not process image content for user {phone_number}. Type: {type(image_content)}"
167
- )
168
- _send_whatsapp_message(phone_number, response.content or "")
169
- else:
170
- _send_whatsapp_message(phone_number, response.content or "")
171
-
172
- except Exception as e:
173
- log_error(f"Error processing message: {str(e)}")
174
- # Optionally send an error message to the user
175
- try:
176
- _send_whatsapp_message(
177
- phone_number, "Sorry, there was an error processing your message. Please try again later."
178
- )
179
- except Exception as send_error:
180
- log_error(f"Error sending error message: {str(send_error)}")
181
-
182
- def _send_whatsapp_message(recipient: str, message: str, italics: bool = False):
183
- if len(message) <= 4096:
184
- WhatsAppTools().send_text_message_sync(recipient=recipient, text=f"_{message}_" if italics else message)
185
- return
186
-
187
- # Split message into batches of 4000 characters (WhatsApp message limit is 4096)
188
- message_batches = [message[i : i + 4000] for i in range(0, len(message), 4000)]
189
-
190
- # Add a prefix with the batch number
191
- for i, batch in enumerate(message_batches, 1):
192
- batch_message = f"[{i}/{len(message_batches)}] {batch}"
193
- WhatsAppTools().send_text_message_sync(
194
- recipient=recipient, text=f"_{batch_message}_" if italics else batch_message
195
- )
196
-
197
- return router