agno 2.2.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (575) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +51 -0
  3. agno/agent/agent.py +10405 -0
  4. agno/api/__init__.py +0 -0
  5. agno/api/agent.py +28 -0
  6. agno/api/api.py +40 -0
  7. agno/api/evals.py +22 -0
  8. agno/api/os.py +17 -0
  9. agno/api/routes.py +13 -0
  10. agno/api/schemas/__init__.py +9 -0
  11. agno/api/schemas/agent.py +16 -0
  12. agno/api/schemas/evals.py +16 -0
  13. agno/api/schemas/os.py +14 -0
  14. agno/api/schemas/response.py +6 -0
  15. agno/api/schemas/team.py +16 -0
  16. agno/api/schemas/utils.py +21 -0
  17. agno/api/schemas/workflows.py +16 -0
  18. agno/api/settings.py +53 -0
  19. agno/api/team.py +30 -0
  20. agno/api/workflow.py +28 -0
  21. agno/cloud/aws/base.py +214 -0
  22. agno/cloud/aws/s3/__init__.py +2 -0
  23. agno/cloud/aws/s3/api_client.py +43 -0
  24. agno/cloud/aws/s3/bucket.py +195 -0
  25. agno/cloud/aws/s3/object.py +57 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +598 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2042 -0
  33. agno/db/dynamo/schemas.py +314 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +1795 -0
  37. agno/db/firestore/schemas.py +140 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1335 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1160 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1328 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/__init__.py +0 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/mongo/__init__.py +17 -0
  51. agno/db/mongo/async_mongo.py +2026 -0
  52. agno/db/mongo/mongo.py +1982 -0
  53. agno/db/mongo/schemas.py +87 -0
  54. agno/db/mongo/utils.py +259 -0
  55. agno/db/mysql/__init__.py +3 -0
  56. agno/db/mysql/mysql.py +2308 -0
  57. agno/db/mysql/schemas.py +138 -0
  58. agno/db/mysql/utils.py +355 -0
  59. agno/db/postgres/__init__.py +4 -0
  60. agno/db/postgres/async_postgres.py +1927 -0
  61. agno/db/postgres/postgres.py +2260 -0
  62. agno/db/postgres/schemas.py +139 -0
  63. agno/db/postgres/utils.py +442 -0
  64. agno/db/redis/__init__.py +3 -0
  65. agno/db/redis/redis.py +1660 -0
  66. agno/db/redis/schemas.py +123 -0
  67. agno/db/redis/utils.py +346 -0
  68. agno/db/schemas/__init__.py +4 -0
  69. agno/db/schemas/culture.py +120 -0
  70. agno/db/schemas/evals.py +33 -0
  71. agno/db/schemas/knowledge.py +40 -0
  72. agno/db/schemas/memory.py +46 -0
  73. agno/db/schemas/metrics.py +0 -0
  74. agno/db/singlestore/__init__.py +3 -0
  75. agno/db/singlestore/schemas.py +130 -0
  76. agno/db/singlestore/singlestore.py +2272 -0
  77. agno/db/singlestore/utils.py +384 -0
  78. agno/db/sqlite/__init__.py +4 -0
  79. agno/db/sqlite/async_sqlite.py +2293 -0
  80. agno/db/sqlite/schemas.py +133 -0
  81. agno/db/sqlite/sqlite.py +2288 -0
  82. agno/db/sqlite/utils.py +431 -0
  83. agno/db/surrealdb/__init__.py +3 -0
  84. agno/db/surrealdb/metrics.py +292 -0
  85. agno/db/surrealdb/models.py +309 -0
  86. agno/db/surrealdb/queries.py +71 -0
  87. agno/db/surrealdb/surrealdb.py +1353 -0
  88. agno/db/surrealdb/utils.py +147 -0
  89. agno/db/utils.py +116 -0
  90. agno/debug.py +18 -0
  91. agno/eval/__init__.py +14 -0
  92. agno/eval/accuracy.py +834 -0
  93. agno/eval/performance.py +773 -0
  94. agno/eval/reliability.py +306 -0
  95. agno/eval/utils.py +119 -0
  96. agno/exceptions.py +161 -0
  97. agno/filters.py +354 -0
  98. agno/guardrails/__init__.py +6 -0
  99. agno/guardrails/base.py +19 -0
  100. agno/guardrails/openai.py +144 -0
  101. agno/guardrails/pii.py +94 -0
  102. agno/guardrails/prompt_injection.py +52 -0
  103. agno/integrations/__init__.py +0 -0
  104. agno/integrations/discord/__init__.py +3 -0
  105. agno/integrations/discord/client.py +203 -0
  106. agno/knowledge/__init__.py +5 -0
  107. agno/knowledge/chunking/__init__.py +0 -0
  108. agno/knowledge/chunking/agentic.py +79 -0
  109. agno/knowledge/chunking/document.py +91 -0
  110. agno/knowledge/chunking/fixed.py +57 -0
  111. agno/knowledge/chunking/markdown.py +151 -0
  112. agno/knowledge/chunking/recursive.py +63 -0
  113. agno/knowledge/chunking/row.py +39 -0
  114. agno/knowledge/chunking/semantic.py +86 -0
  115. agno/knowledge/chunking/strategy.py +165 -0
  116. agno/knowledge/content.py +74 -0
  117. agno/knowledge/document/__init__.py +5 -0
  118. agno/knowledge/document/base.py +58 -0
  119. agno/knowledge/embedder/__init__.py +5 -0
  120. agno/knowledge/embedder/aws_bedrock.py +343 -0
  121. agno/knowledge/embedder/azure_openai.py +210 -0
  122. agno/knowledge/embedder/base.py +23 -0
  123. agno/knowledge/embedder/cohere.py +323 -0
  124. agno/knowledge/embedder/fastembed.py +62 -0
  125. agno/knowledge/embedder/fireworks.py +13 -0
  126. agno/knowledge/embedder/google.py +258 -0
  127. agno/knowledge/embedder/huggingface.py +94 -0
  128. agno/knowledge/embedder/jina.py +182 -0
  129. agno/knowledge/embedder/langdb.py +22 -0
  130. agno/knowledge/embedder/mistral.py +206 -0
  131. agno/knowledge/embedder/nebius.py +13 -0
  132. agno/knowledge/embedder/ollama.py +154 -0
  133. agno/knowledge/embedder/openai.py +195 -0
  134. agno/knowledge/embedder/sentence_transformer.py +63 -0
  135. agno/knowledge/embedder/together.py +13 -0
  136. agno/knowledge/embedder/vllm.py +262 -0
  137. agno/knowledge/embedder/voyageai.py +165 -0
  138. agno/knowledge/knowledge.py +1988 -0
  139. agno/knowledge/reader/__init__.py +7 -0
  140. agno/knowledge/reader/arxiv_reader.py +81 -0
  141. agno/knowledge/reader/base.py +95 -0
  142. agno/knowledge/reader/csv_reader.py +166 -0
  143. agno/knowledge/reader/docx_reader.py +82 -0
  144. agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
  145. agno/knowledge/reader/firecrawl_reader.py +201 -0
  146. agno/knowledge/reader/json_reader.py +87 -0
  147. agno/knowledge/reader/markdown_reader.py +137 -0
  148. agno/knowledge/reader/pdf_reader.py +431 -0
  149. agno/knowledge/reader/pptx_reader.py +101 -0
  150. agno/knowledge/reader/reader_factory.py +313 -0
  151. agno/knowledge/reader/s3_reader.py +89 -0
  152. agno/knowledge/reader/tavily_reader.py +194 -0
  153. agno/knowledge/reader/text_reader.py +115 -0
  154. agno/knowledge/reader/web_search_reader.py +372 -0
  155. agno/knowledge/reader/website_reader.py +455 -0
  156. agno/knowledge/reader/wikipedia_reader.py +59 -0
  157. agno/knowledge/reader/youtube_reader.py +78 -0
  158. agno/knowledge/remote_content/__init__.py +0 -0
  159. agno/knowledge/remote_content/remote_content.py +88 -0
  160. agno/knowledge/reranker/__init__.py +3 -0
  161. agno/knowledge/reranker/base.py +14 -0
  162. agno/knowledge/reranker/cohere.py +64 -0
  163. agno/knowledge/reranker/infinity.py +195 -0
  164. agno/knowledge/reranker/sentence_transformer.py +54 -0
  165. agno/knowledge/types.py +39 -0
  166. agno/knowledge/utils.py +189 -0
  167. agno/media.py +462 -0
  168. agno/memory/__init__.py +3 -0
  169. agno/memory/manager.py +1327 -0
  170. agno/models/__init__.py +0 -0
  171. agno/models/aimlapi/__init__.py +5 -0
  172. agno/models/aimlapi/aimlapi.py +45 -0
  173. agno/models/anthropic/__init__.py +5 -0
  174. agno/models/anthropic/claude.py +757 -0
  175. agno/models/aws/__init__.py +15 -0
  176. agno/models/aws/bedrock.py +701 -0
  177. agno/models/aws/claude.py +378 -0
  178. agno/models/azure/__init__.py +18 -0
  179. agno/models/azure/ai_foundry.py +485 -0
  180. agno/models/azure/openai_chat.py +131 -0
  181. agno/models/base.py +2175 -0
  182. agno/models/cerebras/__init__.py +12 -0
  183. agno/models/cerebras/cerebras.py +501 -0
  184. agno/models/cerebras/cerebras_openai.py +112 -0
  185. agno/models/cohere/__init__.py +5 -0
  186. agno/models/cohere/chat.py +389 -0
  187. agno/models/cometapi/__init__.py +5 -0
  188. agno/models/cometapi/cometapi.py +57 -0
  189. agno/models/dashscope/__init__.py +5 -0
  190. agno/models/dashscope/dashscope.py +91 -0
  191. agno/models/deepinfra/__init__.py +5 -0
  192. agno/models/deepinfra/deepinfra.py +28 -0
  193. agno/models/deepseek/__init__.py +5 -0
  194. agno/models/deepseek/deepseek.py +61 -0
  195. agno/models/defaults.py +1 -0
  196. agno/models/fireworks/__init__.py +5 -0
  197. agno/models/fireworks/fireworks.py +26 -0
  198. agno/models/google/__init__.py +5 -0
  199. agno/models/google/gemini.py +1085 -0
  200. agno/models/groq/__init__.py +5 -0
  201. agno/models/groq/groq.py +556 -0
  202. agno/models/huggingface/__init__.py +5 -0
  203. agno/models/huggingface/huggingface.py +491 -0
  204. agno/models/ibm/__init__.py +5 -0
  205. agno/models/ibm/watsonx.py +422 -0
  206. agno/models/internlm/__init__.py +3 -0
  207. agno/models/internlm/internlm.py +26 -0
  208. agno/models/langdb/__init__.py +1 -0
  209. agno/models/langdb/langdb.py +48 -0
  210. agno/models/litellm/__init__.py +14 -0
  211. agno/models/litellm/chat.py +468 -0
  212. agno/models/litellm/litellm_openai.py +25 -0
  213. agno/models/llama_cpp/__init__.py +5 -0
  214. agno/models/llama_cpp/llama_cpp.py +22 -0
  215. agno/models/lmstudio/__init__.py +5 -0
  216. agno/models/lmstudio/lmstudio.py +25 -0
  217. agno/models/message.py +434 -0
  218. agno/models/meta/__init__.py +12 -0
  219. agno/models/meta/llama.py +475 -0
  220. agno/models/meta/llama_openai.py +78 -0
  221. agno/models/metrics.py +120 -0
  222. agno/models/mistral/__init__.py +5 -0
  223. agno/models/mistral/mistral.py +432 -0
  224. agno/models/nebius/__init__.py +3 -0
  225. agno/models/nebius/nebius.py +54 -0
  226. agno/models/nexus/__init__.py +3 -0
  227. agno/models/nexus/nexus.py +22 -0
  228. agno/models/nvidia/__init__.py +5 -0
  229. agno/models/nvidia/nvidia.py +28 -0
  230. agno/models/ollama/__init__.py +5 -0
  231. agno/models/ollama/chat.py +441 -0
  232. agno/models/openai/__init__.py +9 -0
  233. agno/models/openai/chat.py +883 -0
  234. agno/models/openai/like.py +27 -0
  235. agno/models/openai/responses.py +1050 -0
  236. agno/models/openrouter/__init__.py +5 -0
  237. agno/models/openrouter/openrouter.py +66 -0
  238. agno/models/perplexity/__init__.py +5 -0
  239. agno/models/perplexity/perplexity.py +187 -0
  240. agno/models/portkey/__init__.py +3 -0
  241. agno/models/portkey/portkey.py +81 -0
  242. agno/models/requesty/__init__.py +5 -0
  243. agno/models/requesty/requesty.py +52 -0
  244. agno/models/response.py +199 -0
  245. agno/models/sambanova/__init__.py +5 -0
  246. agno/models/sambanova/sambanova.py +28 -0
  247. agno/models/siliconflow/__init__.py +5 -0
  248. agno/models/siliconflow/siliconflow.py +25 -0
  249. agno/models/together/__init__.py +5 -0
  250. agno/models/together/together.py +25 -0
  251. agno/models/utils.py +266 -0
  252. agno/models/vercel/__init__.py +3 -0
  253. agno/models/vercel/v0.py +26 -0
  254. agno/models/vertexai/__init__.py +0 -0
  255. agno/models/vertexai/claude.py +70 -0
  256. agno/models/vllm/__init__.py +3 -0
  257. agno/models/vllm/vllm.py +78 -0
  258. agno/models/xai/__init__.py +3 -0
  259. agno/models/xai/xai.py +113 -0
  260. agno/os/__init__.py +3 -0
  261. agno/os/app.py +876 -0
  262. agno/os/auth.py +57 -0
  263. agno/os/config.py +104 -0
  264. agno/os/interfaces/__init__.py +1 -0
  265. agno/os/interfaces/a2a/__init__.py +3 -0
  266. agno/os/interfaces/a2a/a2a.py +42 -0
  267. agno/os/interfaces/a2a/router.py +250 -0
  268. agno/os/interfaces/a2a/utils.py +924 -0
  269. agno/os/interfaces/agui/__init__.py +3 -0
  270. agno/os/interfaces/agui/agui.py +47 -0
  271. agno/os/interfaces/agui/router.py +144 -0
  272. agno/os/interfaces/agui/utils.py +534 -0
  273. agno/os/interfaces/base.py +25 -0
  274. agno/os/interfaces/slack/__init__.py +3 -0
  275. agno/os/interfaces/slack/router.py +148 -0
  276. agno/os/interfaces/slack/security.py +30 -0
  277. agno/os/interfaces/slack/slack.py +47 -0
  278. agno/os/interfaces/whatsapp/__init__.py +3 -0
  279. agno/os/interfaces/whatsapp/router.py +211 -0
  280. agno/os/interfaces/whatsapp/security.py +53 -0
  281. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  282. agno/os/mcp.py +292 -0
  283. agno/os/middleware/__init__.py +7 -0
  284. agno/os/middleware/jwt.py +233 -0
  285. agno/os/router.py +1763 -0
  286. agno/os/routers/__init__.py +3 -0
  287. agno/os/routers/evals/__init__.py +3 -0
  288. agno/os/routers/evals/evals.py +430 -0
  289. agno/os/routers/evals/schemas.py +142 -0
  290. agno/os/routers/evals/utils.py +162 -0
  291. agno/os/routers/health.py +31 -0
  292. agno/os/routers/home.py +52 -0
  293. agno/os/routers/knowledge/__init__.py +3 -0
  294. agno/os/routers/knowledge/knowledge.py +997 -0
  295. agno/os/routers/knowledge/schemas.py +178 -0
  296. agno/os/routers/memory/__init__.py +3 -0
  297. agno/os/routers/memory/memory.py +515 -0
  298. agno/os/routers/memory/schemas.py +62 -0
  299. agno/os/routers/metrics/__init__.py +3 -0
  300. agno/os/routers/metrics/metrics.py +190 -0
  301. agno/os/routers/metrics/schemas.py +47 -0
  302. agno/os/routers/session/__init__.py +3 -0
  303. agno/os/routers/session/session.py +997 -0
  304. agno/os/schema.py +1055 -0
  305. agno/os/settings.py +43 -0
  306. agno/os/utils.py +630 -0
  307. agno/py.typed +0 -0
  308. agno/reasoning/__init__.py +0 -0
  309. agno/reasoning/anthropic.py +80 -0
  310. agno/reasoning/azure_ai_foundry.py +67 -0
  311. agno/reasoning/deepseek.py +63 -0
  312. agno/reasoning/default.py +97 -0
  313. agno/reasoning/gemini.py +73 -0
  314. agno/reasoning/groq.py +71 -0
  315. agno/reasoning/helpers.py +63 -0
  316. agno/reasoning/ollama.py +67 -0
  317. agno/reasoning/openai.py +86 -0
  318. agno/reasoning/step.py +31 -0
  319. agno/reasoning/vertexai.py +76 -0
  320. agno/run/__init__.py +6 -0
  321. agno/run/agent.py +787 -0
  322. agno/run/base.py +229 -0
  323. agno/run/cancel.py +81 -0
  324. agno/run/messages.py +32 -0
  325. agno/run/team.py +753 -0
  326. agno/run/workflow.py +708 -0
  327. agno/session/__init__.py +10 -0
  328. agno/session/agent.py +295 -0
  329. agno/session/summary.py +265 -0
  330. agno/session/team.py +392 -0
  331. agno/session/workflow.py +205 -0
  332. agno/team/__init__.py +37 -0
  333. agno/team/team.py +8793 -0
  334. agno/tools/__init__.py +10 -0
  335. agno/tools/agentql.py +120 -0
  336. agno/tools/airflow.py +69 -0
  337. agno/tools/api.py +122 -0
  338. agno/tools/apify.py +314 -0
  339. agno/tools/arxiv.py +127 -0
  340. agno/tools/aws_lambda.py +53 -0
  341. agno/tools/aws_ses.py +66 -0
  342. agno/tools/baidusearch.py +89 -0
  343. agno/tools/bitbucket.py +292 -0
  344. agno/tools/brandfetch.py +213 -0
  345. agno/tools/bravesearch.py +106 -0
  346. agno/tools/brightdata.py +367 -0
  347. agno/tools/browserbase.py +209 -0
  348. agno/tools/calcom.py +255 -0
  349. agno/tools/calculator.py +151 -0
  350. agno/tools/cartesia.py +187 -0
  351. agno/tools/clickup.py +244 -0
  352. agno/tools/confluence.py +240 -0
  353. agno/tools/crawl4ai.py +158 -0
  354. agno/tools/csv_toolkit.py +185 -0
  355. agno/tools/dalle.py +110 -0
  356. agno/tools/daytona.py +475 -0
  357. agno/tools/decorator.py +262 -0
  358. agno/tools/desi_vocal.py +108 -0
  359. agno/tools/discord.py +161 -0
  360. agno/tools/docker.py +716 -0
  361. agno/tools/duckdb.py +379 -0
  362. agno/tools/duckduckgo.py +91 -0
  363. agno/tools/e2b.py +703 -0
  364. agno/tools/eleven_labs.py +196 -0
  365. agno/tools/email.py +67 -0
  366. agno/tools/evm.py +129 -0
  367. agno/tools/exa.py +396 -0
  368. agno/tools/fal.py +127 -0
  369. agno/tools/file.py +240 -0
  370. agno/tools/file_generation.py +350 -0
  371. agno/tools/financial_datasets.py +288 -0
  372. agno/tools/firecrawl.py +143 -0
  373. agno/tools/function.py +1187 -0
  374. agno/tools/giphy.py +93 -0
  375. agno/tools/github.py +1760 -0
  376. agno/tools/gmail.py +922 -0
  377. agno/tools/google_bigquery.py +117 -0
  378. agno/tools/google_drive.py +270 -0
  379. agno/tools/google_maps.py +253 -0
  380. agno/tools/googlecalendar.py +674 -0
  381. agno/tools/googlesearch.py +98 -0
  382. agno/tools/googlesheets.py +377 -0
  383. agno/tools/hackernews.py +77 -0
  384. agno/tools/jina.py +101 -0
  385. agno/tools/jira.py +170 -0
  386. agno/tools/knowledge.py +218 -0
  387. agno/tools/linear.py +426 -0
  388. agno/tools/linkup.py +58 -0
  389. agno/tools/local_file_system.py +90 -0
  390. agno/tools/lumalab.py +183 -0
  391. agno/tools/mcp/__init__.py +10 -0
  392. agno/tools/mcp/mcp.py +331 -0
  393. agno/tools/mcp/multi_mcp.py +347 -0
  394. agno/tools/mcp/params.py +24 -0
  395. agno/tools/mcp_toolbox.py +284 -0
  396. agno/tools/mem0.py +193 -0
  397. agno/tools/memori.py +339 -0
  398. agno/tools/memory.py +419 -0
  399. agno/tools/mlx_transcribe.py +139 -0
  400. agno/tools/models/__init__.py +0 -0
  401. agno/tools/models/azure_openai.py +190 -0
  402. agno/tools/models/gemini.py +203 -0
  403. agno/tools/models/groq.py +158 -0
  404. agno/tools/models/morph.py +186 -0
  405. agno/tools/models/nebius.py +124 -0
  406. agno/tools/models_labs.py +195 -0
  407. agno/tools/moviepy_video.py +349 -0
  408. agno/tools/neo4j.py +134 -0
  409. agno/tools/newspaper.py +46 -0
  410. agno/tools/newspaper4k.py +93 -0
  411. agno/tools/notion.py +204 -0
  412. agno/tools/openai.py +202 -0
  413. agno/tools/openbb.py +160 -0
  414. agno/tools/opencv.py +321 -0
  415. agno/tools/openweather.py +233 -0
  416. agno/tools/oxylabs.py +385 -0
  417. agno/tools/pandas.py +102 -0
  418. agno/tools/parallel.py +314 -0
  419. agno/tools/postgres.py +257 -0
  420. agno/tools/pubmed.py +188 -0
  421. agno/tools/python.py +205 -0
  422. agno/tools/reasoning.py +283 -0
  423. agno/tools/reddit.py +467 -0
  424. agno/tools/replicate.py +117 -0
  425. agno/tools/resend.py +62 -0
  426. agno/tools/scrapegraph.py +222 -0
  427. agno/tools/searxng.py +152 -0
  428. agno/tools/serpapi.py +116 -0
  429. agno/tools/serper.py +255 -0
  430. agno/tools/shell.py +53 -0
  431. agno/tools/slack.py +136 -0
  432. agno/tools/sleep.py +20 -0
  433. agno/tools/spider.py +116 -0
  434. agno/tools/sql.py +154 -0
  435. agno/tools/streamlit/__init__.py +0 -0
  436. agno/tools/streamlit/components.py +113 -0
  437. agno/tools/tavily.py +254 -0
  438. agno/tools/telegram.py +48 -0
  439. agno/tools/todoist.py +218 -0
  440. agno/tools/tool_registry.py +1 -0
  441. agno/tools/toolkit.py +146 -0
  442. agno/tools/trafilatura.py +388 -0
  443. agno/tools/trello.py +274 -0
  444. agno/tools/twilio.py +186 -0
  445. agno/tools/user_control_flow.py +78 -0
  446. agno/tools/valyu.py +228 -0
  447. agno/tools/visualization.py +467 -0
  448. agno/tools/webbrowser.py +28 -0
  449. agno/tools/webex.py +76 -0
  450. agno/tools/website.py +54 -0
  451. agno/tools/webtools.py +45 -0
  452. agno/tools/whatsapp.py +286 -0
  453. agno/tools/wikipedia.py +63 -0
  454. agno/tools/workflow.py +278 -0
  455. agno/tools/x.py +335 -0
  456. agno/tools/yfinance.py +257 -0
  457. agno/tools/youtube.py +184 -0
  458. agno/tools/zendesk.py +82 -0
  459. agno/tools/zep.py +454 -0
  460. agno/tools/zoom.py +382 -0
  461. agno/utils/__init__.py +0 -0
  462. agno/utils/agent.py +820 -0
  463. agno/utils/audio.py +49 -0
  464. agno/utils/certs.py +27 -0
  465. agno/utils/code_execution.py +11 -0
  466. agno/utils/common.py +132 -0
  467. agno/utils/dttm.py +13 -0
  468. agno/utils/enum.py +22 -0
  469. agno/utils/env.py +11 -0
  470. agno/utils/events.py +696 -0
  471. agno/utils/format_str.py +16 -0
  472. agno/utils/functions.py +166 -0
  473. agno/utils/gemini.py +426 -0
  474. agno/utils/hooks.py +57 -0
  475. agno/utils/http.py +74 -0
  476. agno/utils/json_schema.py +234 -0
  477. agno/utils/knowledge.py +36 -0
  478. agno/utils/location.py +19 -0
  479. agno/utils/log.py +255 -0
  480. agno/utils/mcp.py +214 -0
  481. agno/utils/media.py +352 -0
  482. agno/utils/merge_dict.py +41 -0
  483. agno/utils/message.py +118 -0
  484. agno/utils/models/__init__.py +0 -0
  485. agno/utils/models/ai_foundry.py +43 -0
  486. agno/utils/models/claude.py +358 -0
  487. agno/utils/models/cohere.py +87 -0
  488. agno/utils/models/llama.py +78 -0
  489. agno/utils/models/mistral.py +98 -0
  490. agno/utils/models/openai_responses.py +140 -0
  491. agno/utils/models/schema_utils.py +153 -0
  492. agno/utils/models/watsonx.py +41 -0
  493. agno/utils/openai.py +257 -0
  494. agno/utils/pickle.py +32 -0
  495. agno/utils/pprint.py +178 -0
  496. agno/utils/print_response/__init__.py +0 -0
  497. agno/utils/print_response/agent.py +842 -0
  498. agno/utils/print_response/team.py +1724 -0
  499. agno/utils/print_response/workflow.py +1668 -0
  500. agno/utils/prompts.py +111 -0
  501. agno/utils/reasoning.py +108 -0
  502. agno/utils/response.py +163 -0
  503. agno/utils/response_iterator.py +17 -0
  504. agno/utils/safe_formatter.py +24 -0
  505. agno/utils/serialize.py +32 -0
  506. agno/utils/shell.py +22 -0
  507. agno/utils/streamlit.py +487 -0
  508. agno/utils/string.py +231 -0
  509. agno/utils/team.py +139 -0
  510. agno/utils/timer.py +41 -0
  511. agno/utils/tools.py +102 -0
  512. agno/utils/web.py +23 -0
  513. agno/utils/whatsapp.py +305 -0
  514. agno/utils/yaml_io.py +25 -0
  515. agno/vectordb/__init__.py +3 -0
  516. agno/vectordb/base.py +127 -0
  517. agno/vectordb/cassandra/__init__.py +5 -0
  518. agno/vectordb/cassandra/cassandra.py +501 -0
  519. agno/vectordb/cassandra/extra_param_mixin.py +11 -0
  520. agno/vectordb/cassandra/index.py +13 -0
  521. agno/vectordb/chroma/__init__.py +5 -0
  522. agno/vectordb/chroma/chromadb.py +929 -0
  523. agno/vectordb/clickhouse/__init__.py +9 -0
  524. agno/vectordb/clickhouse/clickhousedb.py +835 -0
  525. agno/vectordb/clickhouse/index.py +9 -0
  526. agno/vectordb/couchbase/__init__.py +3 -0
  527. agno/vectordb/couchbase/couchbase.py +1442 -0
  528. agno/vectordb/distance.py +7 -0
  529. agno/vectordb/lancedb/__init__.py +6 -0
  530. agno/vectordb/lancedb/lance_db.py +995 -0
  531. agno/vectordb/langchaindb/__init__.py +5 -0
  532. agno/vectordb/langchaindb/langchaindb.py +163 -0
  533. agno/vectordb/lightrag/__init__.py +5 -0
  534. agno/vectordb/lightrag/lightrag.py +388 -0
  535. agno/vectordb/llamaindex/__init__.py +3 -0
  536. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  537. agno/vectordb/milvus/__init__.py +4 -0
  538. agno/vectordb/milvus/milvus.py +1182 -0
  539. agno/vectordb/mongodb/__init__.py +9 -0
  540. agno/vectordb/mongodb/mongodb.py +1417 -0
  541. agno/vectordb/pgvector/__init__.py +12 -0
  542. agno/vectordb/pgvector/index.py +23 -0
  543. agno/vectordb/pgvector/pgvector.py +1462 -0
  544. agno/vectordb/pineconedb/__init__.py +5 -0
  545. agno/vectordb/pineconedb/pineconedb.py +747 -0
  546. agno/vectordb/qdrant/__init__.py +5 -0
  547. agno/vectordb/qdrant/qdrant.py +1134 -0
  548. agno/vectordb/redis/__init__.py +9 -0
  549. agno/vectordb/redis/redisdb.py +694 -0
  550. agno/vectordb/search.py +7 -0
  551. agno/vectordb/singlestore/__init__.py +10 -0
  552. agno/vectordb/singlestore/index.py +41 -0
  553. agno/vectordb/singlestore/singlestore.py +763 -0
  554. agno/vectordb/surrealdb/__init__.py +3 -0
  555. agno/vectordb/surrealdb/surrealdb.py +699 -0
  556. agno/vectordb/upstashdb/__init__.py +5 -0
  557. agno/vectordb/upstashdb/upstashdb.py +718 -0
  558. agno/vectordb/weaviate/__init__.py +8 -0
  559. agno/vectordb/weaviate/index.py +15 -0
  560. agno/vectordb/weaviate/weaviate.py +1005 -0
  561. agno/workflow/__init__.py +23 -0
  562. agno/workflow/agent.py +299 -0
  563. agno/workflow/condition.py +738 -0
  564. agno/workflow/loop.py +735 -0
  565. agno/workflow/parallel.py +824 -0
  566. agno/workflow/router.py +702 -0
  567. agno/workflow/step.py +1432 -0
  568. agno/workflow/steps.py +592 -0
  569. agno/workflow/types.py +520 -0
  570. agno/workflow/workflow.py +4321 -0
  571. agno-2.2.13.dist-info/METADATA +614 -0
  572. agno-2.2.13.dist-info/RECORD +575 -0
  573. agno-2.2.13.dist-info/WHEEL +5 -0
  574. agno-2.2.13.dist-info/licenses/LICENSE +201 -0
  575. agno-2.2.13.dist-info/top_level.txt +1 -0
agno/utils/message.py ADDED
@@ -0,0 +1,118 @@
1
+ from copy import deepcopy
2
+ from typing import Dict, List, Union
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from agno.models.message import Message
7
+ from agno.utils.log import log_debug
8
+
9
+
10
+ def filter_tool_calls(messages: List[Message], max_tool_calls: int) -> None:
11
+ """
12
+ Filter messages (in-place) to keep only the most recent N tool calls.
13
+
14
+ Args:
15
+ messages: List of messages to filter (modified in-place)
16
+ max_tool_calls: Number of recent tool calls to keep
17
+ """
18
+ # Count total tool calls
19
+ tool_call_count = sum(1 for m in messages if m.role == "tool")
20
+
21
+ # No filtering needed
22
+ if tool_call_count <= max_tool_calls:
23
+ return
24
+
25
+ # Collect tool_call_ids to keep (most recent N)
26
+ tool_call_ids_list: List[str] = []
27
+ for msg in reversed(messages):
28
+ if msg.role == "tool" and len(tool_call_ids_list) < max_tool_calls:
29
+ if msg.tool_call_id:
30
+ tool_call_ids_list.append(msg.tool_call_id)
31
+
32
+ tool_call_ids_to_keep: set[str] = set(tool_call_ids_list)
33
+
34
+ # Filter messages in-place
35
+ filtered_messages = []
36
+ for msg in messages:
37
+ if msg.role == "tool":
38
+ # Keep only tool results in our window
39
+ if msg.tool_call_id in tool_call_ids_to_keep:
40
+ filtered_messages.append(msg)
41
+ elif msg.role == "assistant" and msg.tool_calls:
42
+ # Filter tool_calls within the assistant message
43
+ # Use deepcopy to ensure complete isolation of the filtered message
44
+ filtered_msg = deepcopy(msg)
45
+ # Filter tool_calls
46
+ if filtered_msg.tool_calls is not None:
47
+ filtered_msg.tool_calls = [
48
+ tc for tc in filtered_msg.tool_calls if tc.get("id") in tool_call_ids_to_keep
49
+ ]
50
+
51
+ if filtered_msg.tool_calls:
52
+ # Has tool_calls remaining, keep it
53
+ filtered_messages.append(filtered_msg)
54
+ # skip empty messages
55
+ elif filtered_msg.content:
56
+ filtered_msg.tool_calls = None
57
+ filtered_messages.append(filtered_msg)
58
+ else:
59
+ filtered_messages.append(msg)
60
+
61
+ messages[:] = filtered_messages
62
+
63
+ # Log filtering information
64
+ num_filtered = tool_call_count - len(tool_call_ids_to_keep)
65
+ log_debug(f"Filtered {num_filtered} tool calls, kept {len(tool_call_ids_to_keep)}")
66
+
67
+
68
+ def get_text_from_message(message: Union[List, Dict, str, Message, BaseModel]) -> str:
69
+ """Return the user texts from the message"""
70
+ import json
71
+
72
+ if isinstance(message, str):
73
+ return message
74
+ if isinstance(message, BaseModel):
75
+ return message.model_dump_json(indent=2, exclude_none=True)
76
+ if isinstance(message, list):
77
+ text_messages = []
78
+ if len(message) == 0:
79
+ return ""
80
+
81
+ # Check if it's a list of Message objects
82
+ if isinstance(message[0], Message):
83
+ for m in message:
84
+ if isinstance(m, Message) and m.role == "user" and m.content is not None:
85
+ # Recursively extract text from the message content
86
+ content_text = get_text_from_message(m.content)
87
+ if content_text:
88
+ text_messages.append(content_text)
89
+ elif "type" in message[0]:
90
+ for m in message:
91
+ m_type = m.get("type")
92
+ if m_type is not None and isinstance(m_type, str):
93
+ m_value = m.get(m_type)
94
+ if m_value is not None and isinstance(m_value, str):
95
+ if m_type == "text":
96
+ text_messages.append(m_value)
97
+ # if m_type == "image_url":
98
+ # text_messages.append(f"Image: {m_value}")
99
+ # else:
100
+ # text_messages.append(f"{m_type}: {m_value}")
101
+ elif "role" in message[0]:
102
+ for m in message:
103
+ m_role = m.get("role")
104
+ if m_role is not None and isinstance(m_role, str):
105
+ m_content = m.get("content")
106
+ if m_content is not None and isinstance(m_content, str):
107
+ if m_role == "user":
108
+ text_messages.append(m_content)
109
+ if len(text_messages) > 0:
110
+ return "\n".join(text_messages)
111
+ if isinstance(message, dict):
112
+ if "content" in message:
113
+ return get_text_from_message(message["content"])
114
+ else:
115
+ return json.dumps(message, indent=2)
116
+ if isinstance(message, Message) and message.content is not None:
117
+ return get_text_from_message(message.content)
118
+ return ""
File without changes
@@ -0,0 +1,43 @@
1
+ from typing import Any, Dict
2
+
3
+ from agno.models.message import Message
4
+ from agno.utils.log import log_warning
5
+ from agno.utils.openai import images_to_message
6
+
7
+
8
+ def format_message(message: Message) -> Dict[str, Any]:
9
+ """
10
+ Format a message into the format expected by OpenAI.
11
+
12
+ Args:
13
+ message (Message): The message to format.
14
+
15
+ Returns:
16
+ Dict[str, Any]: The formatted message.
17
+ """
18
+ message_dict: Dict[str, Any] = {
19
+ "role": message.role,
20
+ "content": message.content,
21
+ "name": message.name,
22
+ "tool_call_id": message.tool_call_id,
23
+ "tool_calls": message.tool_calls,
24
+ }
25
+ message_dict = {k: v for k, v in message_dict.items() if v is not None}
26
+
27
+ if message.images is not None and len(message.images) > 0:
28
+ # Ignore non-string message content
29
+ # because we assume that the images/audio are already added to the message
30
+ if isinstance(message.content, str):
31
+ message_dict["content"] = [{"type": "text", "text": message.content}]
32
+ message_dict["content"].extend(images_to_message(images=message.images))
33
+
34
+ if message.audio is not None and len(message.audio) > 0:
35
+ log_warning("Audio input is currently unsupported.")
36
+
37
+ if message.files is not None and len(message.files) > 0:
38
+ log_warning("File input is currently unsupported.")
39
+
40
+ if message.videos is not None and len(message.videos) > 0:
41
+ log_warning("Video input is currently unsupported.")
42
+
43
+ return message_dict
@@ -0,0 +1,358 @@
1
+ import json
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, Dict, List, Optional, Tuple
4
+
5
+ from agno.media import File, Image
6
+ from agno.models.message import Message
7
+ from agno.utils.log import log_error, log_warning
8
+
9
+ try:
10
+ from anthropic.types import (
11
+ TextBlock,
12
+ ToolUseBlock,
13
+ )
14
+ except ImportError:
15
+ raise ImportError("`anthropic` not installed. Please install using `pip install anthropic`")
16
+
17
+
18
+ @dataclass
19
+ class MCPToolConfiguration:
20
+ enabled: bool = True
21
+ allowed_tools: List[str] = field(default_factory=list)
22
+
23
+
24
+ @dataclass
25
+ class MCPServerConfiguration:
26
+ type: str
27
+ url: str
28
+ name: str
29
+ tool_configuration: Optional[MCPToolConfiguration] = None
30
+ authorization_token: Optional[str] = None
31
+
32
+
33
+ ROLE_MAP = {
34
+ "system": "system",
35
+ "developer": "system",
36
+ "user": "user",
37
+ "assistant": "assistant",
38
+ "tool": "user",
39
+ }
40
+
41
+
42
+ def _format_image_for_message(image: Image) -> Optional[Dict[str, Any]]:
43
+ """
44
+ Add an image to a message by converting it to base64 encoded format.
45
+ """
46
+ using_filetype = False
47
+
48
+ import base64
49
+
50
+ # 'imghdr' was deprecated in Python 3.11: https://docs.python.org/3/library/imghdr.html
51
+ # 'filetype' used as a fallback
52
+ try:
53
+ import imghdr
54
+ except ImportError:
55
+ try:
56
+ import filetype
57
+
58
+ using_filetype = True
59
+ except ImportError:
60
+ raise ImportError("`filetype` not installed. Please install using `pip install filetype`")
61
+
62
+ type_mapping = {
63
+ "jpeg": "image/jpeg",
64
+ "jpg": "image/jpeg",
65
+ "png": "image/png",
66
+ "gif": "image/gif",
67
+ "webp": "image/webp",
68
+ }
69
+
70
+ try:
71
+ img_type = None
72
+
73
+ # Case 0: Image is an Anthropic uploaded file
74
+ if image.content is not None and hasattr(image.content, "id"):
75
+ content_bytes = image.content
76
+
77
+ # Case 1: Image is a URL
78
+ if image.url is not None:
79
+ content_bytes = image.get_content_bytes() # type: ignore
80
+
81
+ # If image URL has a suffix, use it as the type (without dot)
82
+ import os
83
+ from urllib.parse import urlparse
84
+
85
+ if image.url:
86
+ parsed_url = urlparse(image.url)
87
+ _, ext = os.path.splitext(parsed_url.path)
88
+ if ext:
89
+ img_type = ext.lstrip(".").lower()
90
+
91
+ # Case 2: Image is a local file path
92
+ elif image.filepath is not None:
93
+ from pathlib import Path
94
+
95
+ path = Path(image.filepath) if isinstance(image.filepath, str) else image.filepath
96
+ if path.exists() and path.is_file():
97
+ with open(image.filepath, "rb") as f:
98
+ content_bytes = f.read()
99
+
100
+ # If image file path has a suffix, use it as the type (without dot)
101
+ path_ext = path.suffix.lstrip(".")
102
+ if path_ext:
103
+ img_type = path_ext.lower()
104
+ else:
105
+ log_error(f"Image file not found: {image}")
106
+ return None
107
+
108
+ # Case 3: Image is a bytes object
109
+ elif image.content is not None:
110
+ content_bytes = image.content
111
+
112
+ else:
113
+ log_error(f"Unsupported image type: {type(image)}")
114
+ return None
115
+
116
+ if not img_type:
117
+ if using_filetype:
118
+ kind = filetype.guess(content_bytes)
119
+ if not kind:
120
+ log_error("Unable to determine image type")
121
+ return None
122
+
123
+ img_type = kind.extension
124
+ else:
125
+ img_type = imghdr.what(None, h=content_bytes) # type: ignore
126
+
127
+ if not img_type:
128
+ log_error("Unable to determine image type")
129
+ return None
130
+
131
+ media_type = type_mapping.get(img_type)
132
+ if not media_type:
133
+ log_error(f"Unsupported image type: {img_type}")
134
+ return None
135
+
136
+ return {
137
+ "type": "image",
138
+ "source": {
139
+ "type": "base64",
140
+ "media_type": media_type,
141
+ "data": base64.b64encode(content_bytes).decode("utf-8"), # type: ignore
142
+ },
143
+ }
144
+
145
+ except Exception as e:
146
+ log_error(f"Error processing image: {e}")
147
+ return None
148
+
149
+
150
+ def _format_file_for_message(file: File) -> Optional[Dict[str, Any]]:
151
+ """
152
+ Add a document url or base64 encoded content to a message.
153
+ """
154
+
155
+ mime_mapping = {
156
+ "application/pdf": "base64",
157
+ "text/plain": "text",
158
+ }
159
+
160
+ # Case 0: File is an Anthropic uploaded file
161
+ if file.external is not None and hasattr(file.external, "id"):
162
+ return {
163
+ "type": "document",
164
+ "source": {
165
+ "type": "file",
166
+ "file_id": file.external.id,
167
+ },
168
+ }
169
+
170
+ # Case 1: Document is a URL
171
+ if file.url is not None:
172
+ return {
173
+ "type": "document",
174
+ "source": {
175
+ "type": "url",
176
+ "url": file.url,
177
+ },
178
+ "citations": {"enabled": True},
179
+ }
180
+ # Case 2: Document is a local file path
181
+ elif file.filepath is not None:
182
+ import base64
183
+ from pathlib import Path
184
+
185
+ path = Path(file.filepath) if isinstance(file.filepath, str) else file.filepath
186
+ if path.exists() and path.is_file():
187
+ file_data = base64.standard_b64encode(path.read_bytes()).decode("utf-8")
188
+
189
+ # Determine media type
190
+ media_type = file.mime_type
191
+ if media_type is None:
192
+ import mimetypes
193
+
194
+ media_type = mimetypes.guess_type(file.filepath)[0] or "application/pdf"
195
+
196
+ # Map media type to type, default to "base64" if no mapping exists
197
+ type = mime_mapping.get(media_type, "base64")
198
+
199
+ return {
200
+ "type": "document",
201
+ "source": {
202
+ "type": type,
203
+ "media_type": media_type,
204
+ "data": file_data,
205
+ },
206
+ "citations": {"enabled": True},
207
+ }
208
+ else:
209
+ log_error(f"Document file not found: {file}")
210
+ return None
211
+ # Case 3: Document is bytes content
212
+ elif file.content is not None:
213
+ import base64
214
+
215
+ file_data = base64.standard_b64encode(file.content).decode("utf-8")
216
+ return {
217
+ "type": "document",
218
+ "source": {"type": "base64", "media_type": file.mime_type or "application/pdf", "data": file_data},
219
+ "citations": {"enabled": True},
220
+ }
221
+ return None
222
+
223
+
224
+ def format_messages(messages: List[Message]) -> Tuple[List[Dict[str, str]], str]:
225
+ """
226
+ Process the list of messages and separate them into API messages and system messages.
227
+
228
+ Args:
229
+ messages (List[Message]): The list of messages to process.
230
+
231
+ Returns:
232
+ Tuple[List[Dict[str, str]], str]: A tuple containing the list of API messages and the concatenated system messages.
233
+ """
234
+ chat_messages: List[Dict[str, str]] = []
235
+ system_messages: List[str] = []
236
+
237
+ for message in messages:
238
+ content = message.content or ""
239
+ # Both "system" and "developer" roles should be extracted as system messages
240
+ if message.role in ("system", "developer"):
241
+ if content is not None:
242
+ system_messages.append(content) # type: ignore
243
+ continue
244
+ elif message.role == "user":
245
+ if isinstance(content, str):
246
+ content = [{"type": "text", "text": content}]
247
+
248
+ if message.images is not None:
249
+ for image in message.images:
250
+ image_content = _format_image_for_message(image)
251
+ if image_content:
252
+ content.append(image_content)
253
+
254
+ if message.files is not None:
255
+ for file in message.files:
256
+ file_content = _format_file_for_message(file)
257
+ if file_content:
258
+ content.append(file_content)
259
+
260
+ if message.audio is not None and len(message.audio) > 0:
261
+ log_warning("Audio input is currently unsupported.")
262
+
263
+ if message.videos is not None and len(message.videos) > 0:
264
+ log_warning("Video input is currently unsupported.")
265
+
266
+ elif message.role == "assistant":
267
+ content = []
268
+
269
+ if message.reasoning_content is not None and message.provider_data is not None:
270
+ from anthropic.types import RedactedThinkingBlock, ThinkingBlock
271
+
272
+ content.append(
273
+ ThinkingBlock(
274
+ thinking=message.reasoning_content,
275
+ signature=message.provider_data.get("signature"),
276
+ type="thinking",
277
+ )
278
+ )
279
+
280
+ if message.redacted_reasoning_content is not None:
281
+ from anthropic.types import RedactedThinkingBlock
282
+
283
+ content.append(
284
+ RedactedThinkingBlock(data=message.redacted_reasoning_content, type="redacted_reasoning_content")
285
+ )
286
+
287
+ if isinstance(message.content, str) and message.content and len(message.content.strip()) > 0:
288
+ content.append(TextBlock(text=message.content, type="text"))
289
+
290
+ if message.tool_calls:
291
+ for tool_call in message.tool_calls:
292
+ content.append(
293
+ ToolUseBlock(
294
+ id=tool_call["id"],
295
+ input=json.loads(tool_call["function"]["arguments"])
296
+ if "arguments" in tool_call["function"]
297
+ else {},
298
+ name=tool_call["function"]["name"],
299
+ type="tool_use",
300
+ )
301
+ )
302
+ elif message.role == "tool":
303
+ content = []
304
+ content.append(
305
+ {
306
+ "type": "tool_result",
307
+ "tool_use_id": message.tool_call_id,
308
+ "content": str(message.content),
309
+ }
310
+ )
311
+
312
+ # Skip empty assistant responses
313
+ if message.role == "assistant" and not content:
314
+ continue
315
+
316
+ chat_messages.append({"role": ROLE_MAP[message.role], "content": content}) # type: ignore
317
+ return chat_messages, " ".join(system_messages)
318
+
319
+
320
+ def format_tools_for_model(tools: Optional[List[Dict[str, Any]]] = None) -> Optional[List[Dict[str, Any]]]:
321
+ """
322
+ Transforms function definitions into a format accepted by the Anthropic API.
323
+ """
324
+ if not tools:
325
+ return None
326
+
327
+ parsed_tools: List[Dict[str, Any]] = []
328
+ for tool_def in tools:
329
+ if tool_def.get("type", "") != "function":
330
+ parsed_tools.append(tool_def)
331
+ continue
332
+
333
+ func_def = tool_def.get("function", {})
334
+ parameters: Dict[str, Any] = func_def.get("parameters", {})
335
+ properties: Dict[str, Any] = parameters.get("properties", {})
336
+ required: List[str] = parameters.get("required", [])
337
+ required_params: List[str] = required
338
+
339
+ input_properties: Dict[str, Any] = {}
340
+ for param_name, param_info in properties.items():
341
+ # Preserve the complete schema structure for complex types
342
+ input_properties[param_name] = param_info.copy()
343
+
344
+ # Ensure description is present (default to empty if missing)
345
+ if "description" not in input_properties[param_name]:
346
+ input_properties[param_name]["description"] = ""
347
+
348
+ tool = {
349
+ "name": func_def.get("name") or "",
350
+ "description": func_def.get("description") or "",
351
+ "input_schema": {
352
+ "type": parameters.get("type", "object"),
353
+ "properties": input_properties,
354
+ "required": required_params,
355
+ },
356
+ }
357
+ parsed_tools.append(tool)
358
+ return parsed_tools
@@ -0,0 +1,87 @@
1
+ from pathlib import Path
2
+ from typing import Any, Dict, List, Sequence
3
+
4
+ from agno.media import Image
5
+ from agno.models.message import Message
6
+ from agno.utils.log import log_error, log_warning
7
+
8
+
9
+ def _format_images_for_message(message: Message, images: Sequence[Image]) -> List[Dict[str, Any]]:
10
+ """
11
+ Format an image into the format expected by WatsonX.
12
+ """
13
+
14
+ # Create a default message content with text
15
+ message_content_with_image: List[Dict[str, Any]] = [{"type": "text", "text": message.content}]
16
+
17
+ # Add images to the message content
18
+ for image in images:
19
+ try:
20
+ if image.content is not None:
21
+ image_content = image.content
22
+ elif image.url is not None:
23
+ image_content = image.get_content_bytes() # type: ignore
24
+ elif image.filepath is not None:
25
+ if isinstance(image.filepath, Path):
26
+ image_content = image.filepath.read_bytes()
27
+ else:
28
+ with open(image.filepath, "rb") as f:
29
+ image_content = f.read()
30
+ else:
31
+ log_warning(f"Unsupported image format: {image}")
32
+ continue
33
+
34
+ if image_content is not None:
35
+ import base64
36
+
37
+ base64_image = base64.b64encode(image_content).decode("utf-8")
38
+ image_url = f"data:image/jpeg;base64,{base64_image}"
39
+ image_payload = {"type": "image_url", "image_url": {"url": image_url}}
40
+ message_content_with_image.append(image_payload)
41
+
42
+ except Exception as e:
43
+ log_error(f"Failed to process image: {str(e)}")
44
+
45
+ # Update the message content with the images
46
+ return message_content_with_image
47
+
48
+
49
+ def format_messages(messages: List[Message]) -> List[Dict[str, Any]]:
50
+ """
51
+ Format messages for the Cohere API.
52
+
53
+ Args:
54
+ messages (List[Message]): The list of messages.
55
+
56
+ Returns:
57
+ List[Dict[str, Any]]: The formatted messages.
58
+ """
59
+ formatted_messages = []
60
+ for message in messages:
61
+ message_dict = {
62
+ "role": message.role,
63
+ "content": message.content,
64
+ "name": message.name,
65
+ "tool_call_id": message.tool_call_id,
66
+ "tool_calls": message.tool_calls,
67
+ }
68
+
69
+ if message.images is not None and len(message.images) > 0:
70
+ # Ignore non-string message content
71
+ if isinstance(message.content, str):
72
+ message_content_with_image = _format_images_for_message(message=message, images=message.images)
73
+ if len(message_content_with_image) > 1:
74
+ message_dict["content"] = message_content_with_image
75
+
76
+ if message.videos is not None and len(message.videos) > 0:
77
+ log_warning("Video input is currently unsupported.")
78
+
79
+ if message.audio is not None and len(message.audio) > 0:
80
+ log_warning("Audio input is currently unsupported.")
81
+
82
+ if message.files is not None and len(message.files) > 0:
83
+ log_warning("File input is currently unsupported.")
84
+
85
+ message_dict = {k: v for k, v in message_dict.items() if v is not None}
86
+ formatted_messages.append(message_dict)
87
+ return formatted_messages
@@ -0,0 +1,78 @@
1
+ from typing import Any, Dict
2
+
3
+ from agno.agent import Message
4
+ from agno.utils.log import log_warning
5
+ from agno.utils.openai import process_image
6
+
7
+ ROLE_MAP = {
8
+ "user": "user",
9
+ "assistant": "assistant",
10
+ "system": "system",
11
+ "tool": "tool",
12
+ }
13
+
14
+ TOOL_CALL_ROLE_MAP = {
15
+ "user": "user",
16
+ "assistant": "assistant",
17
+ "system": "user",
18
+ "tool": "tool",
19
+ }
20
+
21
+
22
+ def format_message(message: Message, openai_like: bool = False, tool_calls: bool = False) -> Dict[str, Any]:
23
+ """
24
+ Format a message into the format expected by Llama API.
25
+
26
+ Args:
27
+ message (Message): The message to format.
28
+ openai_like (bool): Whether to format the message as an OpenAI-like message.
29
+
30
+ Returns:
31
+ Dict[str, Any]: The formatted message.
32
+ """
33
+ message_dict: Dict[str, Any] = {
34
+ "role": ROLE_MAP[message.role] if not tool_calls else TOOL_CALL_ROLE_MAP[message.role],
35
+ "content": [{"type": "text", "text": message.content or " "}],
36
+ "name": message.name,
37
+ "tool_call_id": message.tool_call_id,
38
+ "tool_calls": message.tool_calls,
39
+ }
40
+ message_dict = {k: v for k, v in message_dict.items() if v is not None}
41
+
42
+ if message.images is not None and len(message.images) > 0:
43
+ for image in message.images:
44
+ image_payload = process_image(image)
45
+ if image_payload:
46
+ message_dict["content"].append(image_payload)
47
+
48
+ if message.videos is not None and len(message.videos) > 0:
49
+ log_warning("Video input is currently unsupported.")
50
+
51
+ if message.audio is not None and len(message.audio) > 0:
52
+ log_warning("Audio input is currently unsupported.")
53
+
54
+ if message.role == "tool":
55
+ message_dict = {
56
+ "role": "tool",
57
+ "tool_call_id": message.tool_call_id,
58
+ "content": message.content,
59
+ }
60
+
61
+ if message.role == "assistant":
62
+ text_content = {"type": "text", "text": message.content or " "}
63
+
64
+ if message.tool_calls is not None and len(message.tool_calls) > 0:
65
+ message_dict = {
66
+ "content": [text_content] if openai_like else text_content,
67
+ "role": "assistant",
68
+ "tool_calls": message.tool_calls,
69
+ "stop_reason": "tool_calls",
70
+ }
71
+ else:
72
+ message_dict = {
73
+ "role": "assistant",
74
+ "content": [text_content] if openai_like else text_content,
75
+ "stop_reason": "stop",
76
+ }
77
+
78
+ return message_dict