agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (723) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +44 -5
  3. agno/agent/agent.py +10531 -2975
  4. agno/api/agent.py +14 -53
  5. agno/api/api.py +7 -46
  6. agno/api/evals.py +22 -0
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -25
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +6 -9
  11. agno/api/schemas/evals.py +16 -0
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +10 -10
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +16 -0
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +22 -26
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/compression/__init__.py +3 -0
  25. agno/compression/manager.py +247 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +946 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2781 -0
  33. agno/db/dynamo/schemas.py +442 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +2379 -0
  37. agno/db/firestore/schemas.py +181 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1791 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1312 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1777 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/manager.py +199 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/migrations/versions/v2_3_0.py +938 -0
  51. agno/db/mongo/__init__.py +17 -0
  52. agno/db/mongo/async_mongo.py +2760 -0
  53. agno/db/mongo/mongo.py +2597 -0
  54. agno/db/mongo/schemas.py +119 -0
  55. agno/db/mongo/utils.py +276 -0
  56. agno/db/mysql/__init__.py +4 -0
  57. agno/db/mysql/async_mysql.py +2912 -0
  58. agno/db/mysql/mysql.py +2923 -0
  59. agno/db/mysql/schemas.py +186 -0
  60. agno/db/mysql/utils.py +488 -0
  61. agno/db/postgres/__init__.py +4 -0
  62. agno/db/postgres/async_postgres.py +2579 -0
  63. agno/db/postgres/postgres.py +2870 -0
  64. agno/db/postgres/schemas.py +187 -0
  65. agno/db/postgres/utils.py +442 -0
  66. agno/db/redis/__init__.py +3 -0
  67. agno/db/redis/redis.py +2141 -0
  68. agno/db/redis/schemas.py +159 -0
  69. agno/db/redis/utils.py +346 -0
  70. agno/db/schemas/__init__.py +4 -0
  71. agno/db/schemas/culture.py +120 -0
  72. agno/db/schemas/evals.py +34 -0
  73. agno/db/schemas/knowledge.py +40 -0
  74. agno/db/schemas/memory.py +61 -0
  75. agno/db/singlestore/__init__.py +3 -0
  76. agno/db/singlestore/schemas.py +179 -0
  77. agno/db/singlestore/singlestore.py +2877 -0
  78. agno/db/singlestore/utils.py +384 -0
  79. agno/db/sqlite/__init__.py +4 -0
  80. agno/db/sqlite/async_sqlite.py +2911 -0
  81. agno/db/sqlite/schemas.py +181 -0
  82. agno/db/sqlite/sqlite.py +2908 -0
  83. agno/db/sqlite/utils.py +429 -0
  84. agno/db/surrealdb/__init__.py +3 -0
  85. agno/db/surrealdb/metrics.py +292 -0
  86. agno/db/surrealdb/models.py +334 -0
  87. agno/db/surrealdb/queries.py +71 -0
  88. agno/db/surrealdb/surrealdb.py +1908 -0
  89. agno/db/surrealdb/utils.py +147 -0
  90. agno/db/utils.py +118 -0
  91. agno/eval/__init__.py +24 -0
  92. agno/eval/accuracy.py +666 -276
  93. agno/eval/agent_as_judge.py +861 -0
  94. agno/eval/base.py +29 -0
  95. agno/eval/performance.py +779 -0
  96. agno/eval/reliability.py +241 -62
  97. agno/eval/utils.py +120 -0
  98. agno/exceptions.py +143 -1
  99. agno/filters.py +354 -0
  100. agno/guardrails/__init__.py +6 -0
  101. agno/guardrails/base.py +19 -0
  102. agno/guardrails/openai.py +144 -0
  103. agno/guardrails/pii.py +94 -0
  104. agno/guardrails/prompt_injection.py +52 -0
  105. agno/hooks/__init__.py +3 -0
  106. agno/hooks/decorator.py +164 -0
  107. agno/integrations/discord/__init__.py +3 -0
  108. agno/integrations/discord/client.py +203 -0
  109. agno/knowledge/__init__.py +5 -1
  110. agno/{document → knowledge}/chunking/agentic.py +22 -14
  111. agno/{document → knowledge}/chunking/document.py +2 -2
  112. agno/{document → knowledge}/chunking/fixed.py +7 -6
  113. agno/knowledge/chunking/markdown.py +151 -0
  114. agno/{document → knowledge}/chunking/recursive.py +15 -3
  115. agno/knowledge/chunking/row.py +39 -0
  116. agno/knowledge/chunking/semantic.py +91 -0
  117. agno/knowledge/chunking/strategy.py +165 -0
  118. agno/knowledge/content.py +74 -0
  119. agno/knowledge/document/__init__.py +5 -0
  120. agno/{document → knowledge/document}/base.py +12 -2
  121. agno/knowledge/embedder/__init__.py +5 -0
  122. agno/knowledge/embedder/aws_bedrock.py +343 -0
  123. agno/knowledge/embedder/azure_openai.py +210 -0
  124. agno/{embedder → knowledge/embedder}/base.py +8 -0
  125. agno/knowledge/embedder/cohere.py +323 -0
  126. agno/knowledge/embedder/fastembed.py +62 -0
  127. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  128. agno/knowledge/embedder/google.py +258 -0
  129. agno/knowledge/embedder/huggingface.py +94 -0
  130. agno/knowledge/embedder/jina.py +182 -0
  131. agno/knowledge/embedder/langdb.py +22 -0
  132. agno/knowledge/embedder/mistral.py +206 -0
  133. agno/knowledge/embedder/nebius.py +13 -0
  134. agno/knowledge/embedder/ollama.py +154 -0
  135. agno/knowledge/embedder/openai.py +195 -0
  136. agno/knowledge/embedder/sentence_transformer.py +63 -0
  137. agno/{embedder → knowledge/embedder}/together.py +1 -1
  138. agno/knowledge/embedder/vllm.py +262 -0
  139. agno/knowledge/embedder/voyageai.py +165 -0
  140. agno/knowledge/knowledge.py +3006 -0
  141. agno/knowledge/reader/__init__.py +7 -0
  142. agno/knowledge/reader/arxiv_reader.py +81 -0
  143. agno/knowledge/reader/base.py +95 -0
  144. agno/knowledge/reader/csv_reader.py +164 -0
  145. agno/knowledge/reader/docx_reader.py +82 -0
  146. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  147. agno/knowledge/reader/firecrawl_reader.py +201 -0
  148. agno/knowledge/reader/json_reader.py +88 -0
  149. agno/knowledge/reader/markdown_reader.py +137 -0
  150. agno/knowledge/reader/pdf_reader.py +431 -0
  151. agno/knowledge/reader/pptx_reader.py +101 -0
  152. agno/knowledge/reader/reader_factory.py +313 -0
  153. agno/knowledge/reader/s3_reader.py +89 -0
  154. agno/knowledge/reader/tavily_reader.py +193 -0
  155. agno/knowledge/reader/text_reader.py +127 -0
  156. agno/knowledge/reader/web_search_reader.py +325 -0
  157. agno/knowledge/reader/website_reader.py +455 -0
  158. agno/knowledge/reader/wikipedia_reader.py +91 -0
  159. agno/knowledge/reader/youtube_reader.py +78 -0
  160. agno/knowledge/remote_content/remote_content.py +88 -0
  161. agno/knowledge/reranker/__init__.py +3 -0
  162. agno/{reranker → knowledge/reranker}/base.py +1 -1
  163. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  164. agno/knowledge/reranker/infinity.py +195 -0
  165. agno/knowledge/reranker/sentence_transformer.py +54 -0
  166. agno/knowledge/types.py +39 -0
  167. agno/knowledge/utils.py +234 -0
  168. agno/media.py +439 -95
  169. agno/memory/__init__.py +16 -3
  170. agno/memory/manager.py +1474 -123
  171. agno/memory/strategies/__init__.py +15 -0
  172. agno/memory/strategies/base.py +66 -0
  173. agno/memory/strategies/summarize.py +196 -0
  174. agno/memory/strategies/types.py +37 -0
  175. agno/models/aimlapi/__init__.py +5 -0
  176. agno/models/aimlapi/aimlapi.py +62 -0
  177. agno/models/anthropic/__init__.py +4 -0
  178. agno/models/anthropic/claude.py +960 -496
  179. agno/models/aws/__init__.py +15 -0
  180. agno/models/aws/bedrock.py +686 -451
  181. agno/models/aws/claude.py +190 -183
  182. agno/models/azure/__init__.py +18 -1
  183. agno/models/azure/ai_foundry.py +489 -0
  184. agno/models/azure/openai_chat.py +89 -40
  185. agno/models/base.py +2477 -550
  186. agno/models/cerebras/__init__.py +12 -0
  187. agno/models/cerebras/cerebras.py +565 -0
  188. agno/models/cerebras/cerebras_openai.py +131 -0
  189. agno/models/cohere/__init__.py +4 -0
  190. agno/models/cohere/chat.py +306 -492
  191. agno/models/cometapi/__init__.py +5 -0
  192. agno/models/cometapi/cometapi.py +74 -0
  193. agno/models/dashscope/__init__.py +5 -0
  194. agno/models/dashscope/dashscope.py +90 -0
  195. agno/models/deepinfra/__init__.py +5 -0
  196. agno/models/deepinfra/deepinfra.py +45 -0
  197. agno/models/deepseek/__init__.py +4 -0
  198. agno/models/deepseek/deepseek.py +110 -9
  199. agno/models/fireworks/__init__.py +4 -0
  200. agno/models/fireworks/fireworks.py +19 -22
  201. agno/models/google/__init__.py +3 -7
  202. agno/models/google/gemini.py +1717 -662
  203. agno/models/google/utils.py +22 -0
  204. agno/models/groq/__init__.py +4 -0
  205. agno/models/groq/groq.py +391 -666
  206. agno/models/huggingface/__init__.py +4 -0
  207. agno/models/huggingface/huggingface.py +266 -538
  208. agno/models/ibm/__init__.py +5 -0
  209. agno/models/ibm/watsonx.py +432 -0
  210. agno/models/internlm/__init__.py +3 -0
  211. agno/models/internlm/internlm.py +20 -3
  212. agno/models/langdb/__init__.py +1 -0
  213. agno/models/langdb/langdb.py +60 -0
  214. agno/models/litellm/__init__.py +14 -0
  215. agno/models/litellm/chat.py +503 -0
  216. agno/models/litellm/litellm_openai.py +42 -0
  217. agno/models/llama_cpp/__init__.py +5 -0
  218. agno/models/llama_cpp/llama_cpp.py +22 -0
  219. agno/models/lmstudio/__init__.py +5 -0
  220. agno/models/lmstudio/lmstudio.py +25 -0
  221. agno/models/message.py +361 -39
  222. agno/models/meta/__init__.py +12 -0
  223. agno/models/meta/llama.py +502 -0
  224. agno/models/meta/llama_openai.py +79 -0
  225. agno/models/metrics.py +120 -0
  226. agno/models/mistral/__init__.py +4 -0
  227. agno/models/mistral/mistral.py +293 -393
  228. agno/models/nebius/__init__.py +3 -0
  229. agno/models/nebius/nebius.py +53 -0
  230. agno/models/nexus/__init__.py +3 -0
  231. agno/models/nexus/nexus.py +22 -0
  232. agno/models/nvidia/__init__.py +4 -0
  233. agno/models/nvidia/nvidia.py +22 -3
  234. agno/models/ollama/__init__.py +4 -2
  235. agno/models/ollama/chat.py +257 -492
  236. agno/models/openai/__init__.py +7 -0
  237. agno/models/openai/chat.py +725 -770
  238. agno/models/openai/like.py +16 -2
  239. agno/models/openai/responses.py +1121 -0
  240. agno/models/openrouter/__init__.py +4 -0
  241. agno/models/openrouter/openrouter.py +62 -5
  242. agno/models/perplexity/__init__.py +5 -0
  243. agno/models/perplexity/perplexity.py +203 -0
  244. agno/models/portkey/__init__.py +3 -0
  245. agno/models/portkey/portkey.py +82 -0
  246. agno/models/requesty/__init__.py +5 -0
  247. agno/models/requesty/requesty.py +69 -0
  248. agno/models/response.py +177 -7
  249. agno/models/sambanova/__init__.py +4 -0
  250. agno/models/sambanova/sambanova.py +23 -4
  251. agno/models/siliconflow/__init__.py +5 -0
  252. agno/models/siliconflow/siliconflow.py +42 -0
  253. agno/models/together/__init__.py +4 -0
  254. agno/models/together/together.py +21 -164
  255. agno/models/utils.py +266 -0
  256. agno/models/vercel/__init__.py +3 -0
  257. agno/models/vercel/v0.py +43 -0
  258. agno/models/vertexai/__init__.py +0 -1
  259. agno/models/vertexai/claude.py +190 -0
  260. agno/models/vllm/__init__.py +3 -0
  261. agno/models/vllm/vllm.py +83 -0
  262. agno/models/xai/__init__.py +2 -0
  263. agno/models/xai/xai.py +111 -7
  264. agno/os/__init__.py +3 -0
  265. agno/os/app.py +1027 -0
  266. agno/os/auth.py +244 -0
  267. agno/os/config.py +126 -0
  268. agno/os/interfaces/__init__.py +1 -0
  269. agno/os/interfaces/a2a/__init__.py +3 -0
  270. agno/os/interfaces/a2a/a2a.py +42 -0
  271. agno/os/interfaces/a2a/router.py +249 -0
  272. agno/os/interfaces/a2a/utils.py +924 -0
  273. agno/os/interfaces/agui/__init__.py +3 -0
  274. agno/os/interfaces/agui/agui.py +47 -0
  275. agno/os/interfaces/agui/router.py +147 -0
  276. agno/os/interfaces/agui/utils.py +574 -0
  277. agno/os/interfaces/base.py +25 -0
  278. agno/os/interfaces/slack/__init__.py +3 -0
  279. agno/os/interfaces/slack/router.py +148 -0
  280. agno/os/interfaces/slack/security.py +30 -0
  281. agno/os/interfaces/slack/slack.py +47 -0
  282. agno/os/interfaces/whatsapp/__init__.py +3 -0
  283. agno/os/interfaces/whatsapp/router.py +210 -0
  284. agno/os/interfaces/whatsapp/security.py +55 -0
  285. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  286. agno/os/mcp.py +293 -0
  287. agno/os/middleware/__init__.py +9 -0
  288. agno/os/middleware/jwt.py +797 -0
  289. agno/os/router.py +258 -0
  290. agno/os/routers/__init__.py +3 -0
  291. agno/os/routers/agents/__init__.py +3 -0
  292. agno/os/routers/agents/router.py +599 -0
  293. agno/os/routers/agents/schema.py +261 -0
  294. agno/os/routers/evals/__init__.py +3 -0
  295. agno/os/routers/evals/evals.py +450 -0
  296. agno/os/routers/evals/schemas.py +174 -0
  297. agno/os/routers/evals/utils.py +231 -0
  298. agno/os/routers/health.py +31 -0
  299. agno/os/routers/home.py +52 -0
  300. agno/os/routers/knowledge/__init__.py +3 -0
  301. agno/os/routers/knowledge/knowledge.py +1008 -0
  302. agno/os/routers/knowledge/schemas.py +178 -0
  303. agno/os/routers/memory/__init__.py +3 -0
  304. agno/os/routers/memory/memory.py +661 -0
  305. agno/os/routers/memory/schemas.py +88 -0
  306. agno/os/routers/metrics/__init__.py +3 -0
  307. agno/os/routers/metrics/metrics.py +190 -0
  308. agno/os/routers/metrics/schemas.py +47 -0
  309. agno/os/routers/session/__init__.py +3 -0
  310. agno/os/routers/session/session.py +997 -0
  311. agno/os/routers/teams/__init__.py +3 -0
  312. agno/os/routers/teams/router.py +512 -0
  313. agno/os/routers/teams/schema.py +257 -0
  314. agno/os/routers/traces/__init__.py +3 -0
  315. agno/os/routers/traces/schemas.py +414 -0
  316. agno/os/routers/traces/traces.py +499 -0
  317. agno/os/routers/workflows/__init__.py +3 -0
  318. agno/os/routers/workflows/router.py +624 -0
  319. agno/os/routers/workflows/schema.py +75 -0
  320. agno/os/schema.py +534 -0
  321. agno/os/scopes.py +469 -0
  322. agno/{playground → os}/settings.py +7 -15
  323. agno/os/utils.py +973 -0
  324. agno/reasoning/anthropic.py +80 -0
  325. agno/reasoning/azure_ai_foundry.py +67 -0
  326. agno/reasoning/deepseek.py +63 -0
  327. agno/reasoning/default.py +97 -0
  328. agno/reasoning/gemini.py +73 -0
  329. agno/reasoning/groq.py +71 -0
  330. agno/reasoning/helpers.py +24 -1
  331. agno/reasoning/ollama.py +67 -0
  332. agno/reasoning/openai.py +86 -0
  333. agno/reasoning/step.py +2 -1
  334. agno/reasoning/vertexai.py +76 -0
  335. agno/run/__init__.py +6 -0
  336. agno/run/agent.py +822 -0
  337. agno/run/base.py +247 -0
  338. agno/run/cancel.py +81 -0
  339. agno/run/requirement.py +181 -0
  340. agno/run/team.py +767 -0
  341. agno/run/workflow.py +708 -0
  342. agno/session/__init__.py +10 -0
  343. agno/session/agent.py +260 -0
  344. agno/session/summary.py +265 -0
  345. agno/session/team.py +342 -0
  346. agno/session/workflow.py +501 -0
  347. agno/table.py +10 -0
  348. agno/team/__init__.py +37 -0
  349. agno/team/team.py +9536 -0
  350. agno/tools/__init__.py +7 -0
  351. agno/tools/agentql.py +120 -0
  352. agno/tools/airflow.py +22 -12
  353. agno/tools/api.py +122 -0
  354. agno/tools/apify.py +276 -83
  355. agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
  356. agno/tools/aws_lambda.py +28 -7
  357. agno/tools/aws_ses.py +66 -0
  358. agno/tools/baidusearch.py +11 -4
  359. agno/tools/bitbucket.py +292 -0
  360. agno/tools/brandfetch.py +213 -0
  361. agno/tools/bravesearch.py +106 -0
  362. agno/tools/brightdata.py +367 -0
  363. agno/tools/browserbase.py +209 -0
  364. agno/tools/calcom.py +32 -23
  365. agno/tools/calculator.py +24 -37
  366. agno/tools/cartesia.py +187 -0
  367. agno/tools/{clickup_tool.py → clickup.py} +17 -28
  368. agno/tools/confluence.py +91 -26
  369. agno/tools/crawl4ai.py +139 -43
  370. agno/tools/csv_toolkit.py +28 -22
  371. agno/tools/dalle.py +36 -22
  372. agno/tools/daytona.py +475 -0
  373. agno/tools/decorator.py +169 -14
  374. agno/tools/desi_vocal.py +23 -11
  375. agno/tools/discord.py +32 -29
  376. agno/tools/docker.py +716 -0
  377. agno/tools/duckdb.py +76 -81
  378. agno/tools/duckduckgo.py +43 -40
  379. agno/tools/e2b.py +703 -0
  380. agno/tools/eleven_labs.py +65 -54
  381. agno/tools/email.py +13 -5
  382. agno/tools/evm.py +129 -0
  383. agno/tools/exa.py +324 -42
  384. agno/tools/fal.py +39 -35
  385. agno/tools/file.py +196 -30
  386. agno/tools/file_generation.py +356 -0
  387. agno/tools/financial_datasets.py +288 -0
  388. agno/tools/firecrawl.py +108 -33
  389. agno/tools/function.py +960 -122
  390. agno/tools/giphy.py +34 -12
  391. agno/tools/github.py +1294 -97
  392. agno/tools/gmail.py +922 -0
  393. agno/tools/google_bigquery.py +117 -0
  394. agno/tools/google_drive.py +271 -0
  395. agno/tools/google_maps.py +253 -0
  396. agno/tools/googlecalendar.py +607 -107
  397. agno/tools/googlesheets.py +377 -0
  398. agno/tools/hackernews.py +20 -12
  399. agno/tools/jina.py +24 -14
  400. agno/tools/jira.py +48 -19
  401. agno/tools/knowledge.py +218 -0
  402. agno/tools/linear.py +82 -43
  403. agno/tools/linkup.py +58 -0
  404. agno/tools/local_file_system.py +15 -7
  405. agno/tools/lumalab.py +41 -26
  406. agno/tools/mcp/__init__.py +10 -0
  407. agno/tools/mcp/mcp.py +331 -0
  408. agno/tools/mcp/multi_mcp.py +347 -0
  409. agno/tools/mcp/params.py +24 -0
  410. agno/tools/mcp_toolbox.py +284 -0
  411. agno/tools/mem0.py +193 -0
  412. agno/tools/memory.py +419 -0
  413. agno/tools/mlx_transcribe.py +11 -9
  414. agno/tools/models/azure_openai.py +190 -0
  415. agno/tools/models/gemini.py +203 -0
  416. agno/tools/models/groq.py +158 -0
  417. agno/tools/models/morph.py +186 -0
  418. agno/tools/models/nebius.py +124 -0
  419. agno/tools/models_labs.py +163 -82
  420. agno/tools/moviepy_video.py +18 -13
  421. agno/tools/nano_banana.py +151 -0
  422. agno/tools/neo4j.py +134 -0
  423. agno/tools/newspaper.py +15 -4
  424. agno/tools/newspaper4k.py +19 -6
  425. agno/tools/notion.py +204 -0
  426. agno/tools/openai.py +181 -17
  427. agno/tools/openbb.py +27 -20
  428. agno/tools/opencv.py +321 -0
  429. agno/tools/openweather.py +233 -0
  430. agno/tools/oxylabs.py +385 -0
  431. agno/tools/pandas.py +25 -15
  432. agno/tools/parallel.py +314 -0
  433. agno/tools/postgres.py +238 -185
  434. agno/tools/pubmed.py +125 -13
  435. agno/tools/python.py +48 -35
  436. agno/tools/reasoning.py +283 -0
  437. agno/tools/reddit.py +207 -29
  438. agno/tools/redshift.py +406 -0
  439. agno/tools/replicate.py +69 -26
  440. agno/tools/resend.py +11 -6
  441. agno/tools/scrapegraph.py +179 -19
  442. agno/tools/searxng.py +23 -31
  443. agno/tools/serpapi.py +15 -10
  444. agno/tools/serper.py +255 -0
  445. agno/tools/shell.py +23 -12
  446. agno/tools/shopify.py +1519 -0
  447. agno/tools/slack.py +56 -14
  448. agno/tools/sleep.py +8 -6
  449. agno/tools/spider.py +35 -11
  450. agno/tools/spotify.py +919 -0
  451. agno/tools/sql.py +34 -19
  452. agno/tools/tavily.py +158 -8
  453. agno/tools/telegram.py +18 -8
  454. agno/tools/todoist.py +218 -0
  455. agno/tools/toolkit.py +134 -9
  456. agno/tools/trafilatura.py +388 -0
  457. agno/tools/trello.py +25 -28
  458. agno/tools/twilio.py +18 -9
  459. agno/tools/user_control_flow.py +78 -0
  460. agno/tools/valyu.py +228 -0
  461. agno/tools/visualization.py +467 -0
  462. agno/tools/webbrowser.py +28 -0
  463. agno/tools/webex.py +76 -0
  464. agno/tools/website.py +23 -19
  465. agno/tools/webtools.py +45 -0
  466. agno/tools/whatsapp.py +286 -0
  467. agno/tools/wikipedia.py +28 -19
  468. agno/tools/workflow.py +285 -0
  469. agno/tools/{twitter.py → x.py} +142 -46
  470. agno/tools/yfinance.py +41 -39
  471. agno/tools/youtube.py +34 -17
  472. agno/tools/zendesk.py +15 -5
  473. agno/tools/zep.py +454 -0
  474. agno/tools/zoom.py +86 -37
  475. agno/tracing/__init__.py +12 -0
  476. agno/tracing/exporter.py +157 -0
  477. agno/tracing/schemas.py +276 -0
  478. agno/tracing/setup.py +111 -0
  479. agno/utils/agent.py +938 -0
  480. agno/utils/audio.py +37 -1
  481. agno/utils/certs.py +27 -0
  482. agno/utils/code_execution.py +11 -0
  483. agno/utils/common.py +103 -20
  484. agno/utils/cryptography.py +22 -0
  485. agno/utils/dttm.py +33 -0
  486. agno/utils/events.py +700 -0
  487. agno/utils/functions.py +107 -37
  488. agno/utils/gemini.py +426 -0
  489. agno/utils/hooks.py +171 -0
  490. agno/utils/http.py +185 -0
  491. agno/utils/json_schema.py +159 -37
  492. agno/utils/knowledge.py +36 -0
  493. agno/utils/location.py +19 -0
  494. agno/utils/log.py +221 -8
  495. agno/utils/mcp.py +214 -0
  496. agno/utils/media.py +335 -14
  497. agno/utils/merge_dict.py +22 -1
  498. agno/utils/message.py +77 -2
  499. agno/utils/models/ai_foundry.py +50 -0
  500. agno/utils/models/claude.py +373 -0
  501. agno/utils/models/cohere.py +94 -0
  502. agno/utils/models/llama.py +85 -0
  503. agno/utils/models/mistral.py +100 -0
  504. agno/utils/models/openai_responses.py +140 -0
  505. agno/utils/models/schema_utils.py +153 -0
  506. agno/utils/models/watsonx.py +41 -0
  507. agno/utils/openai.py +257 -0
  508. agno/utils/pickle.py +1 -1
  509. agno/utils/pprint.py +124 -8
  510. agno/utils/print_response/agent.py +930 -0
  511. agno/utils/print_response/team.py +1914 -0
  512. agno/utils/print_response/workflow.py +1668 -0
  513. agno/utils/prompts.py +111 -0
  514. agno/utils/reasoning.py +108 -0
  515. agno/utils/response.py +163 -0
  516. agno/utils/serialize.py +32 -0
  517. agno/utils/shell.py +4 -4
  518. agno/utils/streamlit.py +487 -0
  519. agno/utils/string.py +204 -51
  520. agno/utils/team.py +139 -0
  521. agno/utils/timer.py +9 -2
  522. agno/utils/tokens.py +657 -0
  523. agno/utils/tools.py +19 -1
  524. agno/utils/whatsapp.py +305 -0
  525. agno/utils/yaml_io.py +3 -3
  526. agno/vectordb/__init__.py +2 -0
  527. agno/vectordb/base.py +87 -9
  528. agno/vectordb/cassandra/__init__.py +5 -1
  529. agno/vectordb/cassandra/cassandra.py +383 -27
  530. agno/vectordb/chroma/__init__.py +4 -0
  531. agno/vectordb/chroma/chromadb.py +748 -83
  532. agno/vectordb/clickhouse/__init__.py +7 -1
  533. agno/vectordb/clickhouse/clickhousedb.py +554 -53
  534. agno/vectordb/couchbase/__init__.py +3 -0
  535. agno/vectordb/couchbase/couchbase.py +1446 -0
  536. agno/vectordb/lancedb/__init__.py +5 -0
  537. agno/vectordb/lancedb/lance_db.py +730 -98
  538. agno/vectordb/langchaindb/__init__.py +5 -0
  539. agno/vectordb/langchaindb/langchaindb.py +163 -0
  540. agno/vectordb/lightrag/__init__.py +5 -0
  541. agno/vectordb/lightrag/lightrag.py +388 -0
  542. agno/vectordb/llamaindex/__init__.py +3 -0
  543. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  544. agno/vectordb/milvus/__init__.py +3 -0
  545. agno/vectordb/milvus/milvus.py +966 -78
  546. agno/vectordb/mongodb/__init__.py +9 -1
  547. agno/vectordb/mongodb/mongodb.py +1175 -172
  548. agno/vectordb/pgvector/__init__.py +8 -0
  549. agno/vectordb/pgvector/pgvector.py +599 -115
  550. agno/vectordb/pineconedb/__init__.py +5 -1
  551. agno/vectordb/pineconedb/pineconedb.py +406 -43
  552. agno/vectordb/qdrant/__init__.py +4 -0
  553. agno/vectordb/qdrant/qdrant.py +914 -61
  554. agno/vectordb/redis/__init__.py +9 -0
  555. agno/vectordb/redis/redisdb.py +682 -0
  556. agno/vectordb/singlestore/__init__.py +8 -1
  557. agno/vectordb/singlestore/singlestore.py +771 -0
  558. agno/vectordb/surrealdb/__init__.py +3 -0
  559. agno/vectordb/surrealdb/surrealdb.py +663 -0
  560. agno/vectordb/upstashdb/__init__.py +5 -0
  561. agno/vectordb/upstashdb/upstashdb.py +718 -0
  562. agno/vectordb/weaviate/__init__.py +8 -0
  563. agno/vectordb/weaviate/index.py +15 -0
  564. agno/vectordb/weaviate/weaviate.py +1009 -0
  565. agno/workflow/__init__.py +23 -1
  566. agno/workflow/agent.py +299 -0
  567. agno/workflow/condition.py +759 -0
  568. agno/workflow/loop.py +756 -0
  569. agno/workflow/parallel.py +853 -0
  570. agno/workflow/router.py +723 -0
  571. agno/workflow/step.py +1564 -0
  572. agno/workflow/steps.py +613 -0
  573. agno/workflow/types.py +556 -0
  574. agno/workflow/workflow.py +4327 -514
  575. agno-2.3.13.dist-info/METADATA +639 -0
  576. agno-2.3.13.dist-info/RECORD +613 -0
  577. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
  578. agno-2.3.13.dist-info/licenses/LICENSE +201 -0
  579. agno/api/playground.py +0 -91
  580. agno/api/schemas/playground.py +0 -22
  581. agno/api/schemas/user.py +0 -22
  582. agno/api/schemas/workspace.py +0 -46
  583. agno/api/user.py +0 -160
  584. agno/api/workspace.py +0 -151
  585. agno/cli/auth_server.py +0 -118
  586. agno/cli/config.py +0 -275
  587. agno/cli/console.py +0 -88
  588. agno/cli/credentials.py +0 -23
  589. agno/cli/entrypoint.py +0 -571
  590. agno/cli/operator.py +0 -355
  591. agno/cli/settings.py +0 -85
  592. agno/cli/ws/ws_cli.py +0 -817
  593. agno/constants.py +0 -13
  594. agno/document/__init__.py +0 -1
  595. agno/document/chunking/semantic.py +0 -47
  596. agno/document/chunking/strategy.py +0 -31
  597. agno/document/reader/__init__.py +0 -1
  598. agno/document/reader/arxiv_reader.py +0 -41
  599. agno/document/reader/base.py +0 -22
  600. agno/document/reader/csv_reader.py +0 -84
  601. agno/document/reader/docx_reader.py +0 -46
  602. agno/document/reader/firecrawl_reader.py +0 -99
  603. agno/document/reader/json_reader.py +0 -43
  604. agno/document/reader/pdf_reader.py +0 -219
  605. agno/document/reader/s3/pdf_reader.py +0 -46
  606. agno/document/reader/s3/text_reader.py +0 -51
  607. agno/document/reader/text_reader.py +0 -41
  608. agno/document/reader/website_reader.py +0 -175
  609. agno/document/reader/youtube_reader.py +0 -50
  610. agno/embedder/__init__.py +0 -1
  611. agno/embedder/azure_openai.py +0 -86
  612. agno/embedder/cohere.py +0 -72
  613. agno/embedder/fastembed.py +0 -37
  614. agno/embedder/google.py +0 -73
  615. agno/embedder/huggingface.py +0 -54
  616. agno/embedder/mistral.py +0 -80
  617. agno/embedder/ollama.py +0 -57
  618. agno/embedder/openai.py +0 -74
  619. agno/embedder/sentence_transformer.py +0 -38
  620. agno/embedder/voyageai.py +0 -64
  621. agno/eval/perf.py +0 -201
  622. agno/file/__init__.py +0 -1
  623. agno/file/file.py +0 -16
  624. agno/file/local/csv.py +0 -32
  625. agno/file/local/txt.py +0 -19
  626. agno/infra/app.py +0 -240
  627. agno/infra/base.py +0 -144
  628. agno/infra/context.py +0 -20
  629. agno/infra/db_app.py +0 -52
  630. agno/infra/resource.py +0 -205
  631. agno/infra/resources.py +0 -55
  632. agno/knowledge/agent.py +0 -230
  633. agno/knowledge/arxiv.py +0 -22
  634. agno/knowledge/combined.py +0 -22
  635. agno/knowledge/csv.py +0 -28
  636. agno/knowledge/csv_url.py +0 -19
  637. agno/knowledge/document.py +0 -20
  638. agno/knowledge/docx.py +0 -30
  639. agno/knowledge/json.py +0 -28
  640. agno/knowledge/langchain.py +0 -71
  641. agno/knowledge/llamaindex.py +0 -66
  642. agno/knowledge/pdf.py +0 -28
  643. agno/knowledge/pdf_url.py +0 -26
  644. agno/knowledge/s3/base.py +0 -60
  645. agno/knowledge/s3/pdf.py +0 -21
  646. agno/knowledge/s3/text.py +0 -23
  647. agno/knowledge/text.py +0 -30
  648. agno/knowledge/website.py +0 -88
  649. agno/knowledge/wikipedia.py +0 -31
  650. agno/knowledge/youtube.py +0 -22
  651. agno/memory/agent.py +0 -392
  652. agno/memory/classifier.py +0 -104
  653. agno/memory/db/__init__.py +0 -1
  654. agno/memory/db/base.py +0 -42
  655. agno/memory/db/mongodb.py +0 -189
  656. agno/memory/db/postgres.py +0 -203
  657. agno/memory/db/sqlite.py +0 -193
  658. agno/memory/memory.py +0 -15
  659. agno/memory/row.py +0 -36
  660. agno/memory/summarizer.py +0 -192
  661. agno/memory/summary.py +0 -19
  662. agno/memory/workflow.py +0 -38
  663. agno/models/google/gemini_openai.py +0 -26
  664. agno/models/ollama/hermes.py +0 -221
  665. agno/models/ollama/tools.py +0 -362
  666. agno/models/vertexai/gemini.py +0 -595
  667. agno/playground/__init__.py +0 -3
  668. agno/playground/async_router.py +0 -421
  669. agno/playground/deploy.py +0 -249
  670. agno/playground/operator.py +0 -92
  671. agno/playground/playground.py +0 -91
  672. agno/playground/schemas.py +0 -76
  673. agno/playground/serve.py +0 -55
  674. agno/playground/sync_router.py +0 -405
  675. agno/reasoning/agent.py +0 -68
  676. agno/run/response.py +0 -112
  677. agno/storage/agent/__init__.py +0 -0
  678. agno/storage/agent/base.py +0 -38
  679. agno/storage/agent/dynamodb.py +0 -350
  680. agno/storage/agent/json.py +0 -92
  681. agno/storage/agent/mongodb.py +0 -228
  682. agno/storage/agent/postgres.py +0 -367
  683. agno/storage/agent/session.py +0 -79
  684. agno/storage/agent/singlestore.py +0 -303
  685. agno/storage/agent/sqlite.py +0 -357
  686. agno/storage/agent/yaml.py +0 -93
  687. agno/storage/workflow/__init__.py +0 -0
  688. agno/storage/workflow/base.py +0 -40
  689. agno/storage/workflow/mongodb.py +0 -233
  690. agno/storage/workflow/postgres.py +0 -366
  691. agno/storage/workflow/session.py +0 -60
  692. agno/storage/workflow/sqlite.py +0 -359
  693. agno/tools/googlesearch.py +0 -88
  694. agno/utils/defaults.py +0 -57
  695. agno/utils/filesystem.py +0 -39
  696. agno/utils/git.py +0 -52
  697. agno/utils/json_io.py +0 -30
  698. agno/utils/load_env.py +0 -19
  699. agno/utils/py_io.py +0 -19
  700. agno/utils/pyproject.py +0 -18
  701. agno/utils/resource_filter.py +0 -31
  702. agno/vectordb/singlestore/s2vectordb.py +0 -390
  703. agno/vectordb/singlestore/s2vectordb2.py +0 -355
  704. agno/workspace/__init__.py +0 -0
  705. agno/workspace/config.py +0 -325
  706. agno/workspace/enums.py +0 -6
  707. agno/workspace/helpers.py +0 -48
  708. agno/workspace/operator.py +0 -758
  709. agno/workspace/settings.py +0 -63
  710. agno-0.1.2.dist-info/LICENSE +0 -375
  711. agno-0.1.2.dist-info/METADATA +0 -502
  712. agno-0.1.2.dist-info/RECORD +0 -352
  713. agno-0.1.2.dist-info/entry_points.txt +0 -3
  714. /agno/{cli → db/migrations}/__init__.py +0 -0
  715. /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
  716. /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
  717. /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
  718. /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
  719. /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
  720. /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
  721. /agno/{reranker → utils/models}/__init__.py +0 -0
  722. /agno/{storage → utils/print_response}/__init__.py +0 -0
  723. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,140 @@
1
+ from pathlib import Path
2
+ from typing import Any, Dict, List, Optional, Sequence, Union
3
+
4
+ from agno.media import Image
5
+ from agno.utils.log import logger
6
+
7
+
8
+ def _process_bytes_image(image: bytes) -> Dict[str, Any]:
9
+ """Process bytes image data."""
10
+ import base64
11
+
12
+ base64_image = base64.b64encode(image).decode("utf-8")
13
+ image_url = f"data:image/jpeg;base64,{base64_image}"
14
+ return {"type": "input_image", "image_url": image_url}
15
+
16
+
17
+ def _process_image_path(image_path: Union[Path, str]) -> Dict[str, Any]:
18
+ """Process image ( file path)."""
19
+ # Process local file image
20
+ import base64
21
+ import mimetypes
22
+
23
+ path = image_path if isinstance(image_path, Path) else Path(image_path)
24
+ if not path.exists():
25
+ raise FileNotFoundError(f"Image file not found: {image_path}")
26
+
27
+ mime_type = mimetypes.guess_type(image_path)[0] or "image/jpeg"
28
+ with open(path, "rb") as image_file:
29
+ base64_image = base64.b64encode(image_file.read()).decode("utf-8")
30
+ image_url = f"data:{mime_type};base64,{base64_image}"
31
+ return {"type": "input_image", "image_url": image_url}
32
+
33
+
34
+ def _process_image_url(image_url: str) -> Dict[str, Any]:
35
+ """Process image (base64 or URL)."""
36
+
37
+ if image_url.startswith("data:image") or image_url.startswith(("http://", "https://")):
38
+ return {"type": "input_image", "image_url": image_url}
39
+ else:
40
+ raise ValueError("Image URL must start with 'data:image' or 'http(s)://'.")
41
+
42
+
43
+ def _process_image(image: Image) -> Optional[Dict[str, Any]]:
44
+ """Process an image based on the format."""
45
+
46
+ if image.url is not None:
47
+ image_payload = _process_image_url(image.url)
48
+
49
+ elif image.filepath is not None:
50
+ image_payload = _process_image_path(image.filepath)
51
+
52
+ elif image.content is not None:
53
+ image_payload = _process_bytes_image(image.content)
54
+
55
+ else:
56
+ logger.warning(f"Unsupported image format: {image}")
57
+ return None
58
+
59
+ if image.detail:
60
+ image_payload["image_url"]["detail"] = image.detail
61
+
62
+ return image_payload
63
+
64
+
65
+ def images_to_message(images: Sequence[Image]) -> List[Dict[str, Any]]:
66
+ """
67
+ Add images to a message for the model. By default, we use the OpenAI image format but other Models
68
+ can override this method to use a different image format.
69
+
70
+ Args:
71
+ images: Sequence of images in various formats:
72
+ - str: base64 encoded image, URL, or file path
73
+ - Dict: pre-formatted image data
74
+ - bytes: raw image data
75
+
76
+ Returns:
77
+ Message content with images added in the format expected by the model
78
+ """
79
+
80
+ # Create a default message content with text
81
+ image_messages: List[Dict[str, Any]] = []
82
+
83
+ # Add images to the message content
84
+ for image in images:
85
+ try:
86
+ image_data = _process_image(image)
87
+ if image_data:
88
+ image_messages.append(image_data)
89
+ except Exception as e:
90
+ logger.error(f"Failed to process image: {str(e)}")
91
+ continue
92
+
93
+ return image_messages
94
+
95
+
96
+ def sanitize_response_schema(schema: dict):
97
+ """
98
+ Recursively sanitize a Pydantic-generated JSON schema to comply with OpenAI's response_format rules:
99
+
100
+ - Sets "additionalProperties": false for all object types to disallow extra fields,
101
+ EXCEPT when additionalProperties is already defined with a schema (Dict types).
102
+ - Removes "default": null from optional fields.
103
+ - Ensures that all fields defined in "properties" are listed in "required",
104
+ making every property explicitly required as per OpenAI's expectations,
105
+ EXCEPT for Dict fields which should not be in the required array.
106
+ """
107
+ if isinstance(schema, dict):
108
+ # Enforce additionalProperties: false for object types, but preserve Dict schemas
109
+ if schema.get("type") == "object":
110
+ # Only set additionalProperties to False if it's not already defined with a schema
111
+ # This preserves Dict[str, T] fields which need additionalProperties to define value types
112
+ if "additionalProperties" not in schema:
113
+ schema["additionalProperties"] = False
114
+ elif schema.get("additionalProperties") is True:
115
+ # Convert True to False for strict mode, but preserve schema objects
116
+ schema["additionalProperties"] = False
117
+
118
+ # Ensure all properties are required, EXCEPT Dict fields
119
+ if "properties" in schema:
120
+ from agno.utils.models.schema_utils import is_dict_field
121
+
122
+ required_fields = []
123
+ for prop_name, prop_schema in schema["properties"].items():
124
+ # Use the utility function to check if this is a Dict field
125
+ if not is_dict_field(prop_schema):
126
+ required_fields.append(prop_name)
127
+
128
+ schema["required"] = required_fields
129
+
130
+ # Remove only default: null
131
+ if "default" in schema and schema["default"] is None:
132
+ schema.pop("default")
133
+
134
+ # Recurse into all values
135
+ for value in schema.values():
136
+ sanitize_response_schema(value)
137
+
138
+ elif isinstance(schema, list):
139
+ for item in schema:
140
+ sanitize_response_schema(item)
@@ -0,0 +1,153 @@
1
+ """
2
+ Utility functions for handling JSON schemas across different model providers.
3
+ This module provides model-agnostic schema transformations and validations.
4
+ """
5
+
6
+ from typing import Any, Dict, Type
7
+
8
+ from pydantic import BaseModel
9
+
10
+
11
+ def is_dict_field(schema: Dict[str, Any]) -> bool:
12
+ """
13
+ Check if a schema represents a Dict[str, T] field.
14
+
15
+ Args:
16
+ schema: JSON schema dictionary
17
+
18
+ Returns:
19
+ bool: True if the schema represents a Dict field
20
+ """
21
+ return (
22
+ schema.get("type") == "object"
23
+ and "additionalProperties" in schema
24
+ and isinstance(schema["additionalProperties"], dict)
25
+ and "type" in schema["additionalProperties"]
26
+ and "properties" not in schema
27
+ )
28
+
29
+
30
+ def get_dict_value_type(schema: Dict[str, Any]) -> str:
31
+ """
32
+ Extract the value type from a Dict field schema.
33
+
34
+ Args:
35
+ schema: JSON schema dictionary for a Dict field
36
+
37
+ Returns:
38
+ str: The type of values in the dictionary (e.g., "integer", "string")
39
+ """
40
+ if is_dict_field(schema):
41
+ return schema["additionalProperties"]["type"]
42
+ return "string"
43
+
44
+
45
+ def normalize_schema_for_provider(schema: Dict[str, Any], provider: str) -> Dict[str, Any]:
46
+ """
47
+ Normalize a Pydantic-generated schema for a specific model provider.
48
+
49
+ Args:
50
+ schema: Original Pydantic JSON schema
51
+ provider: Model provider name ("openai", "gemini", "anthropic", etc.)
52
+
53
+ Returns:
54
+ Dict[str, Any]: Normalized schema for the provider
55
+ """
56
+ # Make a deep copy to avoid modifying the original
57
+ import copy
58
+
59
+ normalized = copy.deepcopy(schema)
60
+
61
+ if provider.lower() == "openai":
62
+ return _normalize_for_openai(normalized)
63
+ elif provider.lower() == "gemini":
64
+ return _normalize_for_gemini(normalized)
65
+ else:
66
+ # Default normalization for other providers
67
+ return _normalize_generic(normalized)
68
+
69
+
70
+ def _normalize_for_openai(schema: Dict[str, Any]) -> Dict[str, Any]:
71
+ """Normalize schema for OpenAI structured outputs."""
72
+ from agno.utils.models.openai_responses import sanitize_response_schema
73
+
74
+ sanitize_response_schema(schema)
75
+ return schema
76
+
77
+
78
+ def _normalize_for_gemini(schema: Dict[str, Any]) -> Dict[str, Any]:
79
+ """
80
+ Normalize schema for Gemini.
81
+ Gemini has specific requirements for object types and doesn't support
82
+ additionalProperties in the same way as JSON Schema.
83
+ """
84
+
85
+ def _process_schema(s: Dict[str, Any]) -> None:
86
+ if isinstance(s, dict):
87
+ # Handle Dict fields - preserve additionalProperties info for convert_schema
88
+ if is_dict_field(s):
89
+ # For Gemini, we need to preserve the additionalProperties info
90
+ # so that convert_schema can create appropriate placeholder properties
91
+ value_type = get_dict_value_type(s)
92
+
93
+ # Update description to indicate this is a dictionary
94
+ current_desc = s.get("description", "")
95
+ if current_desc:
96
+ s["description"] = f"{current_desc} (Dictionary with {value_type} values)"
97
+ else:
98
+ s["description"] = f"Dictionary with {value_type} values"
99
+
100
+ # Keep additionalProperties for convert_schema to process
101
+ # Don't remove it here - let convert_schema handle the conversion
102
+
103
+ # Recursively process nested schemas
104
+ for value in s.values():
105
+ if isinstance(value, dict):
106
+ _process_schema(value)
107
+ elif isinstance(value, list):
108
+ for item in value:
109
+ if isinstance(item, dict):
110
+ _process_schema(item)
111
+
112
+ _process_schema(schema)
113
+ return schema
114
+
115
+
116
+ def _normalize_generic(schema: Dict[str, Any]) -> Dict[str, Any]:
117
+ """Generic normalization for other providers."""
118
+
119
+ def _process_schema(s: Dict[str, Any]) -> None:
120
+ if isinstance(s, dict):
121
+ # Remove null defaults
122
+ if "default" in s and s["default"] is None:
123
+ s.pop("default")
124
+
125
+ # Recursively process nested schemas
126
+ for value in s.values():
127
+ if isinstance(value, dict):
128
+ _process_schema(value)
129
+ elif isinstance(value, list):
130
+ for item in value:
131
+ if isinstance(item, dict):
132
+ _process_schema(item)
133
+
134
+ _process_schema(schema)
135
+ return schema
136
+
137
+
138
+ def get_response_schema_for_provider(output_schema: Type[BaseModel], provider: str) -> Dict[str, Any]:
139
+ """
140
+ Get a properly formatted response schema for a specific model provider.
141
+
142
+ Args:
143
+ output_schema: Pydantic BaseModel class
144
+ provider: Model provider name
145
+
146
+ Returns:
147
+ Dict[str, Any]: Provider-specific schema
148
+ """
149
+ # Generate the base schema
150
+ base_schema = output_schema.model_json_schema()
151
+
152
+ # Normalize for the specific provider
153
+ return normalize_schema_for_provider(base_schema, provider)
@@ -0,0 +1,41 @@
1
+ from typing import Any, Dict, List, Sequence
2
+
3
+ from agno.media import Image
4
+ from agno.models.message import Message
5
+ from agno.utils.log import log_error, log_warning
6
+
7
+
8
+ def format_images_for_message(message: Message, images: Sequence[Image]) -> Message:
9
+ """
10
+ Format an image into the format expected by WatsonX.
11
+ """
12
+
13
+ # Create a default message content with text
14
+ message_content_with_image: List[Dict[str, Any]] = [{"type": "text", "text": message.content}]
15
+
16
+ # Add images to the message content
17
+ for image in images:
18
+ try:
19
+ if image.content is not None:
20
+ image_content = image.content
21
+ elif image.url is not None:
22
+ image_content = image.get_content_bytes() # type: ignore
23
+ else:
24
+ log_warning(f"Unsupported image format: {image}")
25
+ continue
26
+
27
+ if image_content is not None:
28
+ import base64
29
+
30
+ base64_image = base64.b64encode(image_content).decode("utf-8")
31
+ image_url = f"data:image/jpeg;base64,{base64_image}"
32
+ image_payload = {"type": "image_url", "image_url": {"url": image_url}}
33
+ message_content_with_image.append(image_payload)
34
+
35
+ except Exception as e:
36
+ log_error(f"Failed to process image: {str(e)}")
37
+
38
+ # Update the message content with the images
39
+ if len(message_content_with_image) > 1:
40
+ message.content = message_content_with_image
41
+ return message
agno/utils/openai.py ADDED
@@ -0,0 +1,257 @@
1
+ import base64
2
+ import mimetypes
3
+ from pathlib import Path
4
+ from typing import Any, Dict, List, Optional, Sequence, Union
5
+
6
+ from agno.media import Audio, File, Image
7
+ from agno.utils.log import log_error, log_warning
8
+
9
+ # Ensure .webp is recognized
10
+ mimetypes.add_type("image/webp", ".webp")
11
+
12
+
13
+ def audio_to_message(audio: Sequence[Audio]) -> List[Dict[str, Any]]:
14
+ """
15
+ Add audio to a message for the model. By default, we use the OpenAI audio format but other Models
16
+ can override this method to use a different audio format.
17
+
18
+ Args:
19
+ audio: Pre-formatted audio data like {
20
+ "content": encoded_string,
21
+ "format": "wav"
22
+ }
23
+
24
+ Returns:
25
+ Message content with audio added in the format expected by the model
26
+ """
27
+ from urllib.parse import urlparse
28
+
29
+ audio_messages = []
30
+ for audio_snippet in audio:
31
+ encoded_string: Optional[str] = None
32
+ audio_format: Optional[str] = audio_snippet.format
33
+
34
+ # The audio is raw data
35
+ if audio_snippet.content:
36
+ encoded_string = base64.b64encode(audio_snippet.content).decode("utf-8")
37
+ if not audio_format:
38
+ audio_format = "wav" # Default format if not provided
39
+
40
+ # The audio is a URL
41
+ elif audio_snippet.url:
42
+ audio_bytes = audio_snippet.get_content_bytes()
43
+ if audio_bytes is not None:
44
+ encoded_string = base64.b64encode(audio_bytes).decode("utf-8")
45
+ if not audio_format:
46
+ # Try to guess format from URL extension
47
+ try:
48
+ # Parse the URL first to isolate the path
49
+ parsed_url = urlparse(audio_snippet.url)
50
+ # Get suffix from the path component only
51
+ audio_format = Path(parsed_url.path).suffix.lstrip(".")
52
+ if not audio_format: # Handle cases like URLs ending in /
53
+ log_warning(
54
+ f"Could not determine audio format from URL path: {parsed_url.path}. Defaulting."
55
+ )
56
+ audio_format = "wav"
57
+ except Exception as e:
58
+ log_warning(
59
+ f"Could not determine audio format from URL: {audio_snippet.url}. Error: {e}. Defaulting."
60
+ )
61
+ audio_format = "wav" # Default if guessing fails
62
+
63
+ # The audio is a file path
64
+ elif audio_snippet.filepath:
65
+ path = Path(audio_snippet.filepath)
66
+ if path.exists() and path.is_file():
67
+ try:
68
+ with open(path, "rb") as audio_file:
69
+ encoded_string = base64.b64encode(audio_file.read()).decode("utf-8")
70
+ if not audio_format:
71
+ audio_format = path.suffix.lstrip(".")
72
+ except Exception as e:
73
+ log_error(f"Failed to read audio file {path}: {e}")
74
+ continue # Skip this audio snippet if file reading fails
75
+ else:
76
+ log_error(f"Audio file not found or is not a file: {path}")
77
+ continue # Skip if file doesn't exist
78
+
79
+ # Append the message if we successfully processed the audio
80
+ if encoded_string and audio_format:
81
+ audio_messages.append(
82
+ {
83
+ "type": "input_audio",
84
+ "input_audio": {
85
+ "data": encoded_string,
86
+ "format": audio_format,
87
+ },
88
+ },
89
+ )
90
+ else:
91
+ log_error(f"Could not process audio snippet: {audio_snippet}")
92
+
93
+ return audio_messages
94
+
95
+
96
+ def _process_bytes_image(image: bytes, image_format: Optional[str] = None) -> Dict[str, Any]:
97
+ """Process bytes image data."""
98
+ base64_image = base64.b64encode(image).decode("utf-8")
99
+
100
+ # Use provided format or attempt detection, defaulting to JPEG
101
+ if image_format:
102
+ mime_type = f"image/{image_format.lower()}"
103
+ else:
104
+ # Try to detect the image format from the bytes
105
+ try:
106
+ import imghdr
107
+
108
+ detected_format = imghdr.what(None, h=image)
109
+ mime_type = f"image/{detected_format}" if detected_format else "image/jpeg"
110
+ except Exception:
111
+ mime_type = "image/jpeg"
112
+
113
+ image_url = f"data:{mime_type};base64,{base64_image}"
114
+ return {"type": "image_url", "image_url": {"url": image_url}}
115
+
116
+
117
+ def _process_image_path(image_path: Union[Path, str]) -> Dict[str, Any]:
118
+ """Process image ( file path)."""
119
+ # Process local file image
120
+ path = Path(image_path) # Ensure it's a Path object
121
+ if not path.exists():
122
+ raise FileNotFoundError(f"Image file not found: {image_path}")
123
+ if not path.is_file():
124
+ raise IsADirectoryError(f"Image path is not a file: {image_path}")
125
+
126
+ mime_type = mimetypes.guess_type(path)[0] or "image/jpeg" # Default to jpeg if guess fails
127
+ try:
128
+ with open(path, "rb") as image_file:
129
+ base64_image = base64.b64encode(image_file.read()).decode("utf-8")
130
+ image_url = f"data:{mime_type};base64,{base64_image}"
131
+ return {"type": "image_url", "image_url": {"url": image_url}}
132
+ except Exception as e:
133
+ log_error(f"Failed to read image file {path}: {e}")
134
+ raise # Re-raise the exception after logging
135
+
136
+
137
+ def _process_image_url(image_url: str) -> Dict[str, Any]:
138
+ """Process image (base64 or URL)."""
139
+
140
+ if image_url.startswith("data:image") or image_url.startswith(("http://", "https://")):
141
+ return {"type": "image_url", "image_url": {"url": image_url}}
142
+ else:
143
+ raise ValueError("Image URL must start with 'data:image' or 'http(s)://'.")
144
+
145
+
146
+ def process_image(image: Image) -> Optional[Dict[str, Any]]:
147
+ """Process an image based on the format."""
148
+ image_payload: Optional[Dict[str, Any]] = None # Initialize
149
+ try:
150
+ if image.url is not None:
151
+ image_payload = _process_image_url(image.url)
152
+
153
+ elif image.filepath is not None:
154
+ image_payload = _process_image_path(image.filepath)
155
+
156
+ elif image.content is not None:
157
+ # Pass the format from the Image object
158
+ image_payload = _process_bytes_image(image.content, image.format)
159
+
160
+ else:
161
+ log_warning(f"Unsupported image format or no data provided: {image}")
162
+ return None
163
+
164
+ if image_payload and image.detail: # Check if payload was created before adding detail
165
+ # Ensure image_url key exists before trying to access its sub-dictionary
166
+ if "image_url" not in image_payload:
167
+ # Initialize if missing (though unlikely based on helper funcs)
168
+ image_payload["image_url"] = {}
169
+ image_payload["image_url"]["detail"] = image.detail
170
+
171
+ return image_payload
172
+
173
+ except (FileNotFoundError, IsADirectoryError, ValueError) as e:
174
+ log_error(f"Failed to process image due to invalid input: {str(e)}")
175
+ return None # Return None for handled validation errors
176
+ except Exception as e:
177
+ log_error(f"An unexpected error occurred while processing image: {str(e)}")
178
+ # Depending on policy, you might want to return None or re-raise
179
+ return None # Return None for unexpected errors as well, preventing crashes
180
+
181
+
182
+ def images_to_message(images: Sequence[Image]) -> List[Dict[str, Any]]:
183
+ """
184
+ Add images to a message for the model. By default, we use the OpenAI image format but other Models
185
+ can override this method to use a different image format.
186
+
187
+ Args:
188
+ images: Sequence of images in various formats:
189
+ - str: base64 encoded image, URL, or file path
190
+ - Dict: pre-formatted image data
191
+ - bytes: raw image data
192
+
193
+ Returns:
194
+ Message content with images added in the format expected by the model
195
+ """
196
+
197
+ # Create a default message content with text
198
+ image_messages: List[Dict[str, Any]] = []
199
+
200
+ # Add images to the message content
201
+ for image in images:
202
+ try:
203
+ image_data = process_image(image)
204
+ if image_data:
205
+ image_messages.append(image_data)
206
+ except Exception as e:
207
+ log_error(f"Failed to process image: {str(e)}")
208
+ continue
209
+
210
+ return image_messages
211
+
212
+
213
+ def _format_file_for_message(file: File) -> Optional[Dict[str, Any]]:
214
+ """
215
+ Add a document url, base64 encoded content or OpenAI file to a message.
216
+ """
217
+ import base64
218
+ import mimetypes
219
+ from pathlib import Path
220
+
221
+ # Case 1: Document is a URL
222
+ if file.url is not None:
223
+ from urllib.parse import urlparse
224
+
225
+ result = file.file_url_content
226
+ if not result:
227
+ log_error(f"Failed to fetch file from URL: {file.url}")
228
+ return None
229
+ content_bytes, mime_type = result
230
+ name = Path(urlparse(file.url).path).name or "file"
231
+ _mime = mime_type or file.mime_type or mimetypes.guess_type(name)[0] or "application/pdf"
232
+ _encoded = base64.b64encode(content_bytes).decode("utf-8")
233
+ _data_url = f"data:{_mime};base64,{_encoded}"
234
+ return {"type": "file", "file": {"filename": name, "file_data": _data_url}}
235
+
236
+ # Case 2: Document is a local file path
237
+ if file.filepath is not None:
238
+ path = Path(file.filepath)
239
+ if not path.is_file():
240
+ log_error(f"File not found: {path}")
241
+ return None
242
+ data = path.read_bytes()
243
+
244
+ _mime = file.mime_type or mimetypes.guess_type(path.name)[0] or "application/pdf"
245
+ _encoded = base64.b64encode(data).decode("utf-8")
246
+ _data_url = f"data:{_mime};base64,{_encoded}"
247
+ return {"type": "file", "file": {"filename": path.name, "file_data": _data_url}}
248
+
249
+ # Case 3: Document is bytes content
250
+ if file.content is not None:
251
+ name = getattr(file, "filename", "file")
252
+ _mime = file.mime_type or mimetypes.guess_type(name)[0] or "application/pdf"
253
+ _encoded = base64.b64encode(file.content).decode("utf-8")
254
+ _data_url = f"data:{_mime};base64,{_encoded}"
255
+ return {"type": "file", "file": {"filename": name, "file_data": _data_url}}
256
+
257
+ return None
agno/utils/pickle.py CHANGED
@@ -21,7 +21,7 @@ def unpickle_object_from_file(file_path: Path, verify_class: Optional[Any] = Non
21
21
  import pickle
22
22
 
23
23
  _obj = None
24
- # logger.debug(f"Reading {file_path}")
24
+ # log_debug(f"Reading {file_path}")
25
25
  if file_path.exists() and file_path.is_file():
26
26
  _obj = pickle.load(file_path.open("rb"))
27
27