agno 1.8.2__py3-none-any.whl → 2.0.0__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 (589) hide show
  1. agno/agent/__init__.py +19 -27
  2. agno/agent/agent.py +3143 -4170
  3. agno/api/agent.py +11 -67
  4. agno/api/api.py +5 -46
  5. agno/api/evals.py +8 -19
  6. agno/api/os.py +17 -0
  7. agno/api/routes.py +6 -41
  8. agno/api/schemas/__init__.py +9 -0
  9. agno/api/schemas/agent.py +5 -21
  10. agno/api/schemas/evals.py +7 -16
  11. agno/api/schemas/os.py +14 -0
  12. agno/api/schemas/team.py +5 -21
  13. agno/api/schemas/utils.py +21 -0
  14. agno/api/schemas/workflows.py +11 -7
  15. agno/api/settings.py +53 -0
  16. agno/api/team.py +11 -66
  17. agno/api/workflow.py +28 -0
  18. agno/cloud/aws/base.py +214 -0
  19. agno/cloud/aws/s3/__init__.py +2 -0
  20. agno/cloud/aws/s3/api_client.py +43 -0
  21. agno/cloud/aws/s3/bucket.py +195 -0
  22. agno/cloud/aws/s3/object.py +57 -0
  23. agno/db/__init__.py +24 -0
  24. agno/db/base.py +245 -0
  25. agno/db/dynamo/__init__.py +3 -0
  26. agno/db/dynamo/dynamo.py +1743 -0
  27. agno/db/dynamo/schemas.py +278 -0
  28. agno/db/dynamo/utils.py +684 -0
  29. agno/db/firestore/__init__.py +3 -0
  30. agno/db/firestore/firestore.py +1432 -0
  31. agno/db/firestore/schemas.py +130 -0
  32. agno/db/firestore/utils.py +278 -0
  33. agno/db/gcs_json/__init__.py +3 -0
  34. agno/db/gcs_json/gcs_json_db.py +1001 -0
  35. agno/db/gcs_json/utils.py +194 -0
  36. agno/db/in_memory/__init__.py +3 -0
  37. agno/db/in_memory/in_memory_db.py +882 -0
  38. agno/db/in_memory/utils.py +172 -0
  39. agno/db/json/__init__.py +3 -0
  40. agno/db/json/json_db.py +1045 -0
  41. agno/db/json/utils.py +196 -0
  42. agno/db/migrations/v1_to_v2.py +162 -0
  43. agno/db/mongo/__init__.py +3 -0
  44. agno/db/mongo/mongo.py +1416 -0
  45. agno/db/mongo/schemas.py +77 -0
  46. agno/db/mongo/utils.py +204 -0
  47. agno/db/mysql/__init__.py +3 -0
  48. agno/db/mysql/mysql.py +1719 -0
  49. agno/db/mysql/schemas.py +124 -0
  50. agno/db/mysql/utils.py +297 -0
  51. agno/db/postgres/__init__.py +3 -0
  52. agno/db/postgres/postgres.py +1710 -0
  53. agno/db/postgres/schemas.py +124 -0
  54. agno/db/postgres/utils.py +280 -0
  55. agno/db/redis/__init__.py +3 -0
  56. agno/db/redis/redis.py +1367 -0
  57. agno/db/redis/schemas.py +109 -0
  58. agno/db/redis/utils.py +288 -0
  59. agno/db/schemas/__init__.py +3 -0
  60. agno/db/schemas/evals.py +33 -0
  61. agno/db/schemas/knowledge.py +40 -0
  62. agno/db/schemas/memory.py +46 -0
  63. agno/db/singlestore/__init__.py +3 -0
  64. agno/db/singlestore/schemas.py +116 -0
  65. agno/db/singlestore/singlestore.py +1712 -0
  66. agno/db/singlestore/utils.py +326 -0
  67. agno/db/sqlite/__init__.py +3 -0
  68. agno/db/sqlite/schemas.py +119 -0
  69. agno/db/sqlite/sqlite.py +1676 -0
  70. agno/db/sqlite/utils.py +268 -0
  71. agno/db/utils.py +88 -0
  72. agno/eval/__init__.py +14 -0
  73. agno/eval/accuracy.py +154 -48
  74. agno/eval/performance.py +88 -23
  75. agno/eval/reliability.py +73 -20
  76. agno/eval/utils.py +23 -13
  77. agno/integrations/discord/__init__.py +3 -0
  78. agno/{app → integrations}/discord/client.py +10 -10
  79. agno/knowledge/__init__.py +2 -2
  80. agno/{document → knowledge}/chunking/agentic.py +2 -2
  81. agno/{document → knowledge}/chunking/document.py +2 -2
  82. agno/{document → knowledge}/chunking/fixed.py +3 -3
  83. agno/{document → knowledge}/chunking/markdown.py +2 -2
  84. agno/{document → knowledge}/chunking/recursive.py +2 -2
  85. agno/{document → knowledge}/chunking/row.py +2 -2
  86. agno/knowledge/chunking/semantic.py +59 -0
  87. agno/knowledge/chunking/strategy.py +121 -0
  88. agno/knowledge/content.py +74 -0
  89. agno/knowledge/document/__init__.py +5 -0
  90. agno/{document → knowledge/document}/base.py +12 -2
  91. agno/knowledge/embedder/__init__.py +5 -0
  92. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  93. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  94. agno/{embedder → knowledge/embedder}/base.py +6 -0
  95. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  96. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  97. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  98. agno/{embedder → knowledge/embedder}/google.py +74 -1
  99. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  100. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  101. agno/knowledge/embedder/langdb.py +22 -0
  102. agno/knowledge/embedder/mistral.py +139 -0
  103. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  104. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  105. agno/knowledge/embedder/openai.py +223 -0
  106. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  107. agno/{embedder → knowledge/embedder}/together.py +1 -1
  108. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  109. agno/knowledge/knowledge.py +1551 -0
  110. agno/knowledge/reader/__init__.py +7 -0
  111. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  112. agno/knowledge/reader/base.py +88 -0
  113. agno/{document → knowledge}/reader/csv_reader.py +47 -65
  114. agno/knowledge/reader/docx_reader.py +83 -0
  115. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  116. agno/{document → knowledge}/reader/json_reader.py +30 -9
  117. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  118. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  119. agno/knowledge/reader/reader_factory.py +268 -0
  120. agno/knowledge/reader/s3_reader.py +101 -0
  121. agno/{document → knowledge}/reader/text_reader.py +31 -10
  122. agno/knowledge/reader/url_reader.py +128 -0
  123. agno/knowledge/reader/web_search_reader.py +366 -0
  124. agno/{document → knowledge}/reader/website_reader.py +37 -10
  125. agno/knowledge/reader/wikipedia_reader.py +59 -0
  126. agno/knowledge/reader/youtube_reader.py +78 -0
  127. agno/knowledge/remote_content/remote_content.py +88 -0
  128. agno/{reranker → knowledge/reranker}/base.py +1 -1
  129. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  130. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  131. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  132. agno/knowledge/types.py +30 -0
  133. agno/knowledge/utils.py +169 -0
  134. agno/media.py +269 -268
  135. agno/memory/__init__.py +2 -10
  136. agno/memory/manager.py +1003 -148
  137. agno/models/aimlapi/__init__.py +2 -2
  138. agno/models/aimlapi/aimlapi.py +6 -6
  139. agno/models/anthropic/claude.py +128 -72
  140. agno/models/aws/bedrock.py +107 -175
  141. agno/models/aws/claude.py +64 -18
  142. agno/models/azure/ai_foundry.py +73 -23
  143. agno/models/base.py +346 -290
  144. agno/models/cerebras/cerebras.py +84 -27
  145. agno/models/cohere/chat.py +106 -98
  146. agno/models/google/gemini.py +105 -46
  147. agno/models/groq/groq.py +97 -35
  148. agno/models/huggingface/huggingface.py +92 -27
  149. agno/models/ibm/watsonx.py +72 -13
  150. agno/models/litellm/chat.py +85 -13
  151. agno/models/message.py +46 -151
  152. agno/models/meta/llama.py +85 -49
  153. agno/models/metrics.py +120 -0
  154. agno/models/mistral/mistral.py +90 -21
  155. agno/models/ollama/__init__.py +0 -2
  156. agno/models/ollama/chat.py +85 -47
  157. agno/models/openai/chat.py +154 -37
  158. agno/models/openai/responses.py +178 -105
  159. agno/models/perplexity/perplexity.py +26 -2
  160. agno/models/portkey/portkey.py +0 -7
  161. agno/models/response.py +15 -9
  162. agno/models/utils.py +20 -0
  163. agno/models/vercel/__init__.py +2 -2
  164. agno/models/vercel/v0.py +1 -1
  165. agno/models/vllm/__init__.py +2 -2
  166. agno/models/vllm/vllm.py +3 -3
  167. agno/models/xai/xai.py +10 -10
  168. agno/os/__init__.py +3 -0
  169. agno/os/app.py +497 -0
  170. agno/os/auth.py +47 -0
  171. agno/os/config.py +103 -0
  172. agno/os/interfaces/agui/__init__.py +3 -0
  173. agno/os/interfaces/agui/agui.py +31 -0
  174. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  175. agno/{app → os/interfaces}/agui/utils.py +65 -28
  176. agno/os/interfaces/base.py +21 -0
  177. agno/os/interfaces/slack/__init__.py +3 -0
  178. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  179. agno/os/interfaces/slack/slack.py +32 -0
  180. agno/os/interfaces/whatsapp/__init__.py +3 -0
  181. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  182. agno/os/interfaces/whatsapp/whatsapp.py +29 -0
  183. agno/os/mcp.py +235 -0
  184. agno/os/router.py +1400 -0
  185. agno/os/routers/__init__.py +3 -0
  186. agno/os/routers/evals/__init__.py +3 -0
  187. agno/os/routers/evals/evals.py +393 -0
  188. agno/os/routers/evals/schemas.py +142 -0
  189. agno/os/routers/evals/utils.py +161 -0
  190. agno/os/routers/knowledge/__init__.py +3 -0
  191. agno/os/routers/knowledge/knowledge.py +850 -0
  192. agno/os/routers/knowledge/schemas.py +118 -0
  193. agno/os/routers/memory/__init__.py +3 -0
  194. agno/os/routers/memory/memory.py +410 -0
  195. agno/os/routers/memory/schemas.py +58 -0
  196. agno/os/routers/metrics/__init__.py +3 -0
  197. agno/os/routers/metrics/metrics.py +178 -0
  198. agno/os/routers/metrics/schemas.py +47 -0
  199. agno/os/routers/session/__init__.py +3 -0
  200. agno/os/routers/session/session.py +536 -0
  201. agno/os/schema.py +945 -0
  202. agno/{app/playground → os}/settings.py +7 -15
  203. agno/os/utils.py +270 -0
  204. agno/reasoning/azure_ai_foundry.py +4 -4
  205. agno/reasoning/deepseek.py +4 -4
  206. agno/reasoning/default.py +6 -11
  207. agno/reasoning/groq.py +4 -4
  208. agno/reasoning/helpers.py +4 -6
  209. agno/reasoning/ollama.py +4 -4
  210. agno/reasoning/openai.py +4 -4
  211. agno/run/agent.py +633 -0
  212. agno/run/base.py +53 -77
  213. agno/run/cancel.py +81 -0
  214. agno/run/team.py +243 -96
  215. agno/run/workflow.py +550 -12
  216. agno/session/__init__.py +10 -0
  217. agno/session/agent.py +244 -0
  218. agno/session/summary.py +225 -0
  219. agno/session/team.py +262 -0
  220. agno/{storage/session/v2 → session}/workflow.py +47 -24
  221. agno/team/__init__.py +15 -16
  222. agno/team/team.py +3260 -4824
  223. agno/tools/agentql.py +14 -5
  224. agno/tools/airflow.py +9 -4
  225. agno/tools/api.py +7 -3
  226. agno/tools/apify.py +2 -46
  227. agno/tools/arxiv.py +8 -3
  228. agno/tools/aws_lambda.py +7 -5
  229. agno/tools/aws_ses.py +7 -1
  230. agno/tools/baidusearch.py +4 -1
  231. agno/tools/bitbucket.py +4 -4
  232. agno/tools/brandfetch.py +14 -11
  233. agno/tools/bravesearch.py +4 -1
  234. agno/tools/brightdata.py +43 -23
  235. agno/tools/browserbase.py +13 -4
  236. agno/tools/calcom.py +12 -10
  237. agno/tools/calculator.py +10 -27
  238. agno/tools/cartesia.py +20 -17
  239. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  240. agno/tools/confluence.py +8 -8
  241. agno/tools/crawl4ai.py +7 -1
  242. agno/tools/csv_toolkit.py +9 -8
  243. agno/tools/dalle.py +22 -12
  244. agno/tools/daytona.py +13 -16
  245. agno/tools/decorator.py +6 -3
  246. agno/tools/desi_vocal.py +17 -8
  247. agno/tools/discord.py +11 -8
  248. agno/tools/docker.py +30 -42
  249. agno/tools/duckdb.py +34 -53
  250. agno/tools/duckduckgo.py +8 -7
  251. agno/tools/e2b.py +62 -62
  252. agno/tools/eleven_labs.py +36 -29
  253. agno/tools/email.py +4 -1
  254. agno/tools/evm.py +7 -1
  255. agno/tools/exa.py +19 -14
  256. agno/tools/fal.py +30 -30
  257. agno/tools/file.py +9 -8
  258. agno/tools/financial_datasets.py +25 -44
  259. agno/tools/firecrawl.py +17 -18
  260. agno/tools/function.py +127 -18
  261. agno/tools/giphy.py +23 -11
  262. agno/tools/github.py +48 -126
  263. agno/tools/gmail.py +45 -61
  264. agno/tools/google_bigquery.py +7 -6
  265. agno/tools/google_maps.py +11 -26
  266. agno/tools/googlesearch.py +7 -2
  267. agno/tools/googlesheets.py +21 -17
  268. agno/tools/hackernews.py +9 -5
  269. agno/tools/jina.py +5 -4
  270. agno/tools/jira.py +18 -9
  271. agno/tools/knowledge.py +31 -32
  272. agno/tools/linear.py +18 -33
  273. agno/tools/linkup.py +5 -1
  274. agno/tools/local_file_system.py +8 -5
  275. agno/tools/lumalab.py +32 -20
  276. agno/tools/mcp.py +1 -2
  277. agno/tools/mem0.py +18 -12
  278. agno/tools/memori.py +14 -10
  279. agno/tools/mlx_transcribe.py +3 -2
  280. agno/tools/models/azure_openai.py +33 -15
  281. agno/tools/models/gemini.py +59 -32
  282. agno/tools/models/groq.py +30 -23
  283. agno/tools/models/nebius.py +28 -12
  284. agno/tools/models_labs.py +40 -16
  285. agno/tools/moviepy_video.py +7 -6
  286. agno/tools/neo4j.py +10 -8
  287. agno/tools/newspaper.py +7 -2
  288. agno/tools/newspaper4k.py +8 -3
  289. agno/tools/openai.py +58 -32
  290. agno/tools/openbb.py +12 -11
  291. agno/tools/opencv.py +63 -47
  292. agno/tools/openweather.py +14 -12
  293. agno/tools/pandas.py +11 -3
  294. agno/tools/postgres.py +4 -12
  295. agno/tools/pubmed.py +4 -1
  296. agno/tools/python.py +9 -22
  297. agno/tools/reasoning.py +35 -27
  298. agno/tools/reddit.py +11 -26
  299. agno/tools/replicate.py +55 -42
  300. agno/tools/resend.py +4 -1
  301. agno/tools/scrapegraph.py +15 -14
  302. agno/tools/searxng.py +10 -23
  303. agno/tools/serpapi.py +6 -3
  304. agno/tools/serper.py +13 -4
  305. agno/tools/shell.py +9 -2
  306. agno/tools/slack.py +12 -11
  307. agno/tools/sleep.py +3 -2
  308. agno/tools/spider.py +24 -4
  309. agno/tools/sql.py +7 -6
  310. agno/tools/tavily.py +6 -4
  311. agno/tools/telegram.py +12 -4
  312. agno/tools/todoist.py +11 -31
  313. agno/tools/toolkit.py +1 -1
  314. agno/tools/trafilatura.py +22 -6
  315. agno/tools/trello.py +9 -22
  316. agno/tools/twilio.py +10 -3
  317. agno/tools/user_control_flow.py +6 -1
  318. agno/tools/valyu.py +34 -5
  319. agno/tools/visualization.py +19 -28
  320. agno/tools/webbrowser.py +4 -3
  321. agno/tools/webex.py +11 -7
  322. agno/tools/website.py +15 -46
  323. agno/tools/webtools.py +12 -4
  324. agno/tools/whatsapp.py +5 -9
  325. agno/tools/wikipedia.py +20 -13
  326. agno/tools/x.py +14 -13
  327. agno/tools/yfinance.py +13 -40
  328. agno/tools/youtube.py +26 -20
  329. agno/tools/zendesk.py +7 -2
  330. agno/tools/zep.py +10 -7
  331. agno/tools/zoom.py +10 -9
  332. agno/utils/common.py +1 -19
  333. agno/utils/events.py +100 -123
  334. agno/utils/gemini.py +1 -1
  335. agno/utils/knowledge.py +29 -0
  336. agno/utils/log.py +54 -4
  337. agno/utils/mcp.py +68 -10
  338. agno/utils/media.py +39 -0
  339. agno/utils/message.py +12 -1
  340. agno/utils/models/aws_claude.py +1 -1
  341. agno/utils/models/claude.py +6 -12
  342. agno/utils/models/cohere.py +1 -1
  343. agno/utils/models/mistral.py +8 -7
  344. agno/utils/models/schema_utils.py +3 -3
  345. agno/utils/models/watsonx.py +1 -1
  346. agno/utils/openai.py +1 -1
  347. agno/utils/pprint.py +33 -32
  348. agno/utils/print_response/agent.py +779 -0
  349. agno/utils/print_response/team.py +1669 -0
  350. agno/utils/print_response/workflow.py +1451 -0
  351. agno/utils/prompts.py +14 -14
  352. agno/utils/reasoning.py +87 -0
  353. agno/utils/response.py +42 -42
  354. agno/utils/streamlit.py +481 -0
  355. agno/utils/string.py +8 -22
  356. agno/utils/team.py +50 -0
  357. agno/utils/timer.py +2 -2
  358. agno/vectordb/base.py +33 -21
  359. agno/vectordb/cassandra/cassandra.py +287 -23
  360. agno/vectordb/chroma/chromadb.py +482 -59
  361. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  362. agno/vectordb/couchbase/couchbase.py +309 -29
  363. agno/vectordb/lancedb/lance_db.py +360 -21
  364. agno/vectordb/langchaindb/__init__.py +5 -0
  365. agno/vectordb/langchaindb/langchaindb.py +145 -0
  366. agno/vectordb/lightrag/__init__.py +5 -0
  367. agno/vectordb/lightrag/lightrag.py +374 -0
  368. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  369. agno/vectordb/milvus/milvus.py +242 -32
  370. agno/vectordb/mongodb/mongodb.py +200 -24
  371. agno/vectordb/pgvector/pgvector.py +319 -37
  372. agno/vectordb/pineconedb/pineconedb.py +221 -27
  373. agno/vectordb/qdrant/qdrant.py +334 -14
  374. agno/vectordb/singlestore/singlestore.py +286 -29
  375. agno/vectordb/surrealdb/surrealdb.py +187 -7
  376. agno/vectordb/upstashdb/upstashdb.py +342 -26
  377. agno/vectordb/weaviate/weaviate.py +227 -165
  378. agno/workflow/__init__.py +17 -13
  379. agno/workflow/{v2/condition.py → condition.py} +135 -32
  380. agno/workflow/{v2/loop.py → loop.py} +115 -28
  381. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  382. agno/workflow/{v2/router.py → router.py} +133 -32
  383. agno/workflow/{v2/step.py → step.py} +207 -49
  384. agno/workflow/{v2/steps.py → steps.py} +147 -66
  385. agno/workflow/types.py +482 -0
  386. agno/workflow/workflow.py +2410 -696
  387. agno-2.0.0.dist-info/METADATA +494 -0
  388. agno-2.0.0.dist-info/RECORD +515 -0
  389. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  390. agno/agent/metrics.py +0 -110
  391. agno/api/app.py +0 -35
  392. agno/api/playground.py +0 -92
  393. agno/api/schemas/app.py +0 -12
  394. agno/api/schemas/playground.py +0 -22
  395. agno/api/schemas/user.py +0 -35
  396. agno/api/schemas/workspace.py +0 -46
  397. agno/api/user.py +0 -160
  398. agno/api/workflows.py +0 -33
  399. agno/api/workspace.py +0 -175
  400. agno/app/agui/__init__.py +0 -3
  401. agno/app/agui/app.py +0 -17
  402. agno/app/agui/sync_router.py +0 -120
  403. agno/app/base.py +0 -186
  404. agno/app/discord/__init__.py +0 -3
  405. agno/app/fastapi/__init__.py +0 -3
  406. agno/app/fastapi/app.py +0 -107
  407. agno/app/fastapi/async_router.py +0 -457
  408. agno/app/fastapi/sync_router.py +0 -448
  409. agno/app/playground/app.py +0 -228
  410. agno/app/playground/async_router.py +0 -1053
  411. agno/app/playground/deploy.py +0 -249
  412. agno/app/playground/operator.py +0 -183
  413. agno/app/playground/schemas.py +0 -223
  414. agno/app/playground/serve.py +0 -55
  415. agno/app/playground/sync_router.py +0 -1045
  416. agno/app/playground/utils.py +0 -46
  417. agno/app/settings.py +0 -15
  418. agno/app/slack/__init__.py +0 -3
  419. agno/app/slack/app.py +0 -19
  420. agno/app/slack/sync_router.py +0 -92
  421. agno/app/utils.py +0 -54
  422. agno/app/whatsapp/__init__.py +0 -3
  423. agno/app/whatsapp/app.py +0 -15
  424. agno/app/whatsapp/sync_router.py +0 -197
  425. agno/cli/auth_server.py +0 -249
  426. agno/cli/config.py +0 -274
  427. agno/cli/console.py +0 -88
  428. agno/cli/credentials.py +0 -23
  429. agno/cli/entrypoint.py +0 -571
  430. agno/cli/operator.py +0 -357
  431. agno/cli/settings.py +0 -96
  432. agno/cli/ws/ws_cli.py +0 -817
  433. agno/constants.py +0 -13
  434. agno/document/__init__.py +0 -5
  435. agno/document/chunking/semantic.py +0 -45
  436. agno/document/chunking/strategy.py +0 -31
  437. agno/document/reader/__init__.py +0 -5
  438. agno/document/reader/base.py +0 -47
  439. agno/document/reader/docx_reader.py +0 -60
  440. agno/document/reader/gcs/pdf_reader.py +0 -44
  441. agno/document/reader/s3/pdf_reader.py +0 -59
  442. agno/document/reader/s3/text_reader.py +0 -63
  443. agno/document/reader/url_reader.py +0 -59
  444. agno/document/reader/youtube_reader.py +0 -58
  445. agno/embedder/__init__.py +0 -5
  446. agno/embedder/langdb.py +0 -80
  447. agno/embedder/mistral.py +0 -82
  448. agno/embedder/openai.py +0 -78
  449. agno/file/__init__.py +0 -5
  450. agno/file/file.py +0 -16
  451. agno/file/local/csv.py +0 -32
  452. agno/file/local/txt.py +0 -19
  453. agno/infra/app.py +0 -240
  454. agno/infra/base.py +0 -144
  455. agno/infra/context.py +0 -20
  456. agno/infra/db_app.py +0 -52
  457. agno/infra/resource.py +0 -205
  458. agno/infra/resources.py +0 -55
  459. agno/knowledge/agent.py +0 -702
  460. agno/knowledge/arxiv.py +0 -33
  461. agno/knowledge/combined.py +0 -36
  462. agno/knowledge/csv.py +0 -144
  463. agno/knowledge/csv_url.py +0 -124
  464. agno/knowledge/document.py +0 -223
  465. agno/knowledge/docx.py +0 -137
  466. agno/knowledge/firecrawl.py +0 -34
  467. agno/knowledge/gcs/__init__.py +0 -0
  468. agno/knowledge/gcs/base.py +0 -39
  469. agno/knowledge/gcs/pdf.py +0 -125
  470. agno/knowledge/json.py +0 -137
  471. agno/knowledge/langchain.py +0 -71
  472. agno/knowledge/light_rag.py +0 -273
  473. agno/knowledge/llamaindex.py +0 -66
  474. agno/knowledge/markdown.py +0 -154
  475. agno/knowledge/pdf.py +0 -164
  476. agno/knowledge/pdf_bytes.py +0 -42
  477. agno/knowledge/pdf_url.py +0 -148
  478. agno/knowledge/s3/__init__.py +0 -0
  479. agno/knowledge/s3/base.py +0 -64
  480. agno/knowledge/s3/pdf.py +0 -33
  481. agno/knowledge/s3/text.py +0 -34
  482. agno/knowledge/text.py +0 -141
  483. agno/knowledge/url.py +0 -46
  484. agno/knowledge/website.py +0 -179
  485. agno/knowledge/wikipedia.py +0 -32
  486. agno/knowledge/youtube.py +0 -35
  487. agno/memory/agent.py +0 -423
  488. agno/memory/classifier.py +0 -104
  489. agno/memory/db/__init__.py +0 -5
  490. agno/memory/db/base.py +0 -42
  491. agno/memory/db/mongodb.py +0 -189
  492. agno/memory/db/postgres.py +0 -203
  493. agno/memory/db/sqlite.py +0 -193
  494. agno/memory/memory.py +0 -22
  495. agno/memory/row.py +0 -36
  496. agno/memory/summarizer.py +0 -201
  497. agno/memory/summary.py +0 -19
  498. agno/memory/team.py +0 -415
  499. agno/memory/v2/__init__.py +0 -2
  500. agno/memory/v2/db/__init__.py +0 -1
  501. agno/memory/v2/db/base.py +0 -42
  502. agno/memory/v2/db/firestore.py +0 -339
  503. agno/memory/v2/db/mongodb.py +0 -196
  504. agno/memory/v2/db/postgres.py +0 -214
  505. agno/memory/v2/db/redis.py +0 -187
  506. agno/memory/v2/db/schema.py +0 -54
  507. agno/memory/v2/db/sqlite.py +0 -209
  508. agno/memory/v2/manager.py +0 -437
  509. agno/memory/v2/memory.py +0 -1097
  510. agno/memory/v2/schema.py +0 -55
  511. agno/memory/v2/summarizer.py +0 -215
  512. agno/memory/workflow.py +0 -38
  513. agno/models/ollama/tools.py +0 -430
  514. agno/models/qwen/__init__.py +0 -5
  515. agno/playground/__init__.py +0 -10
  516. agno/playground/deploy.py +0 -3
  517. agno/playground/playground.py +0 -3
  518. agno/playground/serve.py +0 -3
  519. agno/playground/settings.py +0 -3
  520. agno/reranker/__init__.py +0 -0
  521. agno/run/response.py +0 -467
  522. agno/run/v2/__init__.py +0 -0
  523. agno/run/v2/workflow.py +0 -567
  524. agno/storage/__init__.py +0 -0
  525. agno/storage/agent/__init__.py +0 -0
  526. agno/storage/agent/dynamodb.py +0 -1
  527. agno/storage/agent/json.py +0 -1
  528. agno/storage/agent/mongodb.py +0 -1
  529. agno/storage/agent/postgres.py +0 -1
  530. agno/storage/agent/singlestore.py +0 -1
  531. agno/storage/agent/sqlite.py +0 -1
  532. agno/storage/agent/yaml.py +0 -1
  533. agno/storage/base.py +0 -60
  534. agno/storage/dynamodb.py +0 -673
  535. agno/storage/firestore.py +0 -297
  536. agno/storage/gcs_json.py +0 -261
  537. agno/storage/in_memory.py +0 -234
  538. agno/storage/json.py +0 -237
  539. agno/storage/mongodb.py +0 -328
  540. agno/storage/mysql.py +0 -685
  541. agno/storage/postgres.py +0 -682
  542. agno/storage/redis.py +0 -336
  543. agno/storage/session/__init__.py +0 -16
  544. agno/storage/session/agent.py +0 -64
  545. agno/storage/session/team.py +0 -63
  546. agno/storage/session/v2/__init__.py +0 -5
  547. agno/storage/session/workflow.py +0 -61
  548. agno/storage/singlestore.py +0 -606
  549. agno/storage/sqlite.py +0 -646
  550. agno/storage/workflow/__init__.py +0 -0
  551. agno/storage/workflow/mongodb.py +0 -1
  552. agno/storage/workflow/postgres.py +0 -1
  553. agno/storage/workflow/sqlite.py +0 -1
  554. agno/storage/yaml.py +0 -241
  555. agno/tools/thinking.py +0 -73
  556. agno/utils/defaults.py +0 -57
  557. agno/utils/filesystem.py +0 -39
  558. agno/utils/git.py +0 -52
  559. agno/utils/json_io.py +0 -30
  560. agno/utils/load_env.py +0 -19
  561. agno/utils/py_io.py +0 -19
  562. agno/utils/pyproject.py +0 -18
  563. agno/utils/resource_filter.py +0 -31
  564. agno/workflow/v2/__init__.py +0 -21
  565. agno/workflow/v2/types.py +0 -357
  566. agno/workflow/v2/workflow.py +0 -3313
  567. agno/workspace/__init__.py +0 -0
  568. agno/workspace/config.py +0 -325
  569. agno/workspace/enums.py +0 -6
  570. agno/workspace/helpers.py +0 -52
  571. agno/workspace/operator.py +0 -757
  572. agno/workspace/settings.py +0 -158
  573. agno-1.8.2.dist-info/METADATA +0 -982
  574. agno-1.8.2.dist-info/RECORD +0 -566
  575. agno-1.8.2.dist-info/entry_points.txt +0 -3
  576. agno-1.8.2.dist-info/licenses/LICENSE +0 -375
  577. /agno/{app → db/migrations}/__init__.py +0 -0
  578. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  579. /agno/{cli → integrations}/__init__.py +0 -0
  580. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  581. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  582. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  583. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  584. /agno/{app → os/interfaces}/slack/security.py +0 -0
  585. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  586. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  587. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  588. {agno-1.8.2.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  589. {agno-1.8.2.dist-info → agno-2.0.0.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