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