agno 1.8.1__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (590) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +3143 -4170
  4. agno/api/agent.py +11 -67
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +8 -19
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -41
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +5 -21
  11. agno/api/schemas/evals.py +7 -16
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +5 -21
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +11 -7
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +11 -66
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/db/__init__.py +24 -0
  25. agno/db/base.py +245 -0
  26. agno/db/dynamo/__init__.py +3 -0
  27. agno/db/dynamo/dynamo.py +1743 -0
  28. agno/db/dynamo/schemas.py +278 -0
  29. agno/db/dynamo/utils.py +684 -0
  30. agno/db/firestore/__init__.py +3 -0
  31. agno/db/firestore/firestore.py +1432 -0
  32. agno/db/firestore/schemas.py +130 -0
  33. agno/db/firestore/utils.py +278 -0
  34. agno/db/gcs_json/__init__.py +3 -0
  35. agno/db/gcs_json/gcs_json_db.py +1001 -0
  36. agno/db/gcs_json/utils.py +194 -0
  37. agno/db/in_memory/__init__.py +3 -0
  38. agno/db/in_memory/in_memory_db.py +882 -0
  39. agno/db/in_memory/utils.py +172 -0
  40. agno/db/json/__init__.py +3 -0
  41. agno/db/json/json_db.py +1045 -0
  42. agno/db/json/utils.py +196 -0
  43. agno/db/migrations/v1_to_v2.py +162 -0
  44. agno/db/mongo/__init__.py +3 -0
  45. agno/db/mongo/mongo.py +1416 -0
  46. agno/db/mongo/schemas.py +77 -0
  47. agno/db/mongo/utils.py +204 -0
  48. agno/db/mysql/__init__.py +3 -0
  49. agno/db/mysql/mysql.py +1719 -0
  50. agno/db/mysql/schemas.py +124 -0
  51. agno/db/mysql/utils.py +297 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1710 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +280 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1367 -0
  58. agno/db/redis/schemas.py +109 -0
  59. agno/db/redis/utils.py +288 -0
  60. agno/db/schemas/__init__.py +3 -0
  61. agno/db/schemas/evals.py +33 -0
  62. agno/db/schemas/knowledge.py +40 -0
  63. agno/db/schemas/memory.py +46 -0
  64. agno/db/singlestore/__init__.py +3 -0
  65. agno/db/singlestore/schemas.py +116 -0
  66. agno/db/singlestore/singlestore.py +1712 -0
  67. agno/db/singlestore/utils.py +326 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1676 -0
  71. agno/db/sqlite/utils.py +268 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +154 -48
  75. agno/eval/performance.py +88 -23
  76. agno/eval/reliability.py +73 -20
  77. agno/eval/utils.py +23 -13
  78. agno/integrations/discord/__init__.py +3 -0
  79. agno/{app → integrations}/discord/client.py +15 -11
  80. agno/knowledge/__init__.py +2 -2
  81. agno/{document → knowledge}/chunking/agentic.py +2 -2
  82. agno/{document → knowledge}/chunking/document.py +2 -2
  83. agno/{document → knowledge}/chunking/fixed.py +3 -3
  84. agno/{document → knowledge}/chunking/markdown.py +2 -2
  85. agno/{document → knowledge}/chunking/recursive.py +2 -2
  86. agno/{document → knowledge}/chunking/row.py +2 -2
  87. agno/knowledge/chunking/semantic.py +59 -0
  88. agno/knowledge/chunking/strategy.py +121 -0
  89. agno/knowledge/content.py +74 -0
  90. agno/knowledge/document/__init__.py +5 -0
  91. agno/{document → knowledge/document}/base.py +12 -2
  92. agno/knowledge/embedder/__init__.py +5 -0
  93. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  94. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  95. agno/{embedder → knowledge/embedder}/base.py +6 -0
  96. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  97. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  98. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  99. agno/{embedder → knowledge/embedder}/google.py +74 -1
  100. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  101. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  102. agno/knowledge/embedder/langdb.py +22 -0
  103. agno/knowledge/embedder/mistral.py +139 -0
  104. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  105. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  106. agno/knowledge/embedder/openai.py +223 -0
  107. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  108. agno/{embedder → knowledge/embedder}/together.py +1 -1
  109. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  110. agno/knowledge/knowledge.py +1551 -0
  111. agno/knowledge/reader/__init__.py +7 -0
  112. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  113. agno/knowledge/reader/base.py +88 -0
  114. agno/{document → knowledge}/reader/csv_reader.py +47 -65
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/{document → knowledge}/reader/json_reader.py +30 -9
  118. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  119. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  120. agno/knowledge/reader/reader_factory.py +268 -0
  121. agno/knowledge/reader/s3_reader.py +101 -0
  122. agno/{document → knowledge}/reader/text_reader.py +31 -10
  123. agno/knowledge/reader/url_reader.py +128 -0
  124. agno/knowledge/reader/web_search_reader.py +366 -0
  125. agno/{document → knowledge}/reader/website_reader.py +37 -10
  126. agno/knowledge/reader/wikipedia_reader.py +59 -0
  127. agno/knowledge/reader/youtube_reader.py +78 -0
  128. agno/knowledge/remote_content/remote_content.py +88 -0
  129. agno/{reranker → knowledge/reranker}/base.py +1 -1
  130. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  131. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  132. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  133. agno/knowledge/types.py +30 -0
  134. agno/knowledge/utils.py +169 -0
  135. agno/media.py +269 -268
  136. agno/memory/__init__.py +2 -10
  137. agno/memory/manager.py +1003 -148
  138. agno/models/aimlapi/__init__.py +2 -2
  139. agno/models/aimlapi/aimlapi.py +6 -6
  140. agno/models/anthropic/claude.py +131 -131
  141. agno/models/aws/bedrock.py +110 -182
  142. agno/models/aws/claude.py +64 -18
  143. agno/models/azure/ai_foundry.py +73 -23
  144. agno/models/base.py +346 -290
  145. agno/models/cerebras/cerebras.py +84 -27
  146. agno/models/cohere/chat.py +106 -98
  147. agno/models/google/gemini.py +105 -46
  148. agno/models/groq/groq.py +97 -35
  149. agno/models/huggingface/huggingface.py +92 -27
  150. agno/models/ibm/watsonx.py +72 -13
  151. agno/models/litellm/chat.py +85 -13
  152. agno/models/message.py +46 -151
  153. agno/models/meta/llama.py +85 -49
  154. agno/models/metrics.py +120 -0
  155. agno/models/mistral/mistral.py +90 -21
  156. agno/models/ollama/__init__.py +0 -2
  157. agno/models/ollama/chat.py +85 -47
  158. agno/models/openai/chat.py +154 -37
  159. agno/models/openai/responses.py +178 -105
  160. agno/models/perplexity/perplexity.py +26 -2
  161. agno/models/portkey/portkey.py +0 -7
  162. agno/models/response.py +15 -9
  163. agno/models/utils.py +20 -0
  164. agno/models/vercel/__init__.py +2 -2
  165. agno/models/vercel/v0.py +1 -1
  166. agno/models/vllm/__init__.py +2 -2
  167. agno/models/vllm/vllm.py +3 -3
  168. agno/models/xai/xai.py +10 -10
  169. agno/os/__init__.py +3 -0
  170. agno/os/app.py +497 -0
  171. agno/os/auth.py +47 -0
  172. agno/os/config.py +103 -0
  173. agno/os/interfaces/agui/__init__.py +3 -0
  174. agno/os/interfaces/agui/agui.py +31 -0
  175. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  176. agno/{app → os/interfaces}/agui/utils.py +77 -33
  177. agno/os/interfaces/base.py +21 -0
  178. agno/os/interfaces/slack/__init__.py +3 -0
  179. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  180. agno/os/interfaces/slack/slack.py +32 -0
  181. agno/os/interfaces/whatsapp/__init__.py +3 -0
  182. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  183. agno/os/interfaces/whatsapp/whatsapp.py +29 -0
  184. agno/os/mcp.py +235 -0
  185. agno/os/router.py +1400 -0
  186. agno/os/routers/__init__.py +3 -0
  187. agno/os/routers/evals/__init__.py +3 -0
  188. agno/os/routers/evals/evals.py +393 -0
  189. agno/os/routers/evals/schemas.py +142 -0
  190. agno/os/routers/evals/utils.py +161 -0
  191. agno/os/routers/knowledge/__init__.py +3 -0
  192. agno/os/routers/knowledge/knowledge.py +850 -0
  193. agno/os/routers/knowledge/schemas.py +118 -0
  194. agno/os/routers/memory/__init__.py +3 -0
  195. agno/os/routers/memory/memory.py +410 -0
  196. agno/os/routers/memory/schemas.py +58 -0
  197. agno/os/routers/metrics/__init__.py +3 -0
  198. agno/os/routers/metrics/metrics.py +178 -0
  199. agno/os/routers/metrics/schemas.py +47 -0
  200. agno/os/routers/session/__init__.py +3 -0
  201. agno/os/routers/session/session.py +536 -0
  202. agno/os/schema.py +945 -0
  203. agno/{app/playground → os}/settings.py +7 -15
  204. agno/os/utils.py +270 -0
  205. agno/reasoning/azure_ai_foundry.py +4 -4
  206. agno/reasoning/deepseek.py +4 -4
  207. agno/reasoning/default.py +6 -11
  208. agno/reasoning/groq.py +4 -4
  209. agno/reasoning/helpers.py +4 -6
  210. agno/reasoning/ollama.py +4 -4
  211. agno/reasoning/openai.py +4 -4
  212. agno/run/agent.py +633 -0
  213. agno/run/base.py +53 -77
  214. agno/run/cancel.py +81 -0
  215. agno/run/team.py +243 -96
  216. agno/run/workflow.py +550 -12
  217. agno/session/__init__.py +10 -0
  218. agno/session/agent.py +244 -0
  219. agno/session/summary.py +225 -0
  220. agno/session/team.py +262 -0
  221. agno/{storage/session/v2 → session}/workflow.py +47 -24
  222. agno/team/__init__.py +15 -16
  223. agno/team/team.py +3260 -4824
  224. agno/tools/agentql.py +14 -5
  225. agno/tools/airflow.py +9 -4
  226. agno/tools/api.py +7 -3
  227. agno/tools/apify.py +2 -46
  228. agno/tools/arxiv.py +8 -3
  229. agno/tools/aws_lambda.py +7 -5
  230. agno/tools/aws_ses.py +7 -1
  231. agno/tools/baidusearch.py +4 -1
  232. agno/tools/bitbucket.py +4 -4
  233. agno/tools/brandfetch.py +14 -11
  234. agno/tools/bravesearch.py +4 -1
  235. agno/tools/brightdata.py +43 -23
  236. agno/tools/browserbase.py +13 -4
  237. agno/tools/calcom.py +12 -10
  238. agno/tools/calculator.py +10 -27
  239. agno/tools/cartesia.py +20 -17
  240. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  241. agno/tools/confluence.py +8 -8
  242. agno/tools/crawl4ai.py +7 -1
  243. agno/tools/csv_toolkit.py +9 -8
  244. agno/tools/dalle.py +22 -12
  245. agno/tools/daytona.py +13 -16
  246. agno/tools/decorator.py +6 -3
  247. agno/tools/desi_vocal.py +17 -8
  248. agno/tools/discord.py +11 -8
  249. agno/tools/docker.py +30 -42
  250. agno/tools/duckdb.py +34 -53
  251. agno/tools/duckduckgo.py +8 -7
  252. agno/tools/e2b.py +62 -62
  253. agno/tools/eleven_labs.py +36 -29
  254. agno/tools/email.py +4 -1
  255. agno/tools/evm.py +7 -1
  256. agno/tools/exa.py +19 -14
  257. agno/tools/fal.py +30 -30
  258. agno/tools/file.py +9 -8
  259. agno/tools/financial_datasets.py +25 -44
  260. agno/tools/firecrawl.py +22 -22
  261. agno/tools/function.py +127 -18
  262. agno/tools/giphy.py +23 -11
  263. agno/tools/github.py +48 -126
  264. agno/tools/gmail.py +45 -61
  265. agno/tools/google_bigquery.py +7 -6
  266. agno/tools/google_maps.py +11 -26
  267. agno/tools/googlesearch.py +7 -2
  268. agno/tools/googlesheets.py +21 -17
  269. agno/tools/hackernews.py +9 -5
  270. agno/tools/jina.py +5 -4
  271. agno/tools/jira.py +18 -9
  272. agno/tools/knowledge.py +31 -32
  273. agno/tools/linear.py +19 -34
  274. agno/tools/linkup.py +5 -1
  275. agno/tools/local_file_system.py +8 -5
  276. agno/tools/lumalab.py +32 -20
  277. agno/tools/mcp.py +1 -2
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +33 -15
  282. agno/tools/models/gemini.py +59 -32
  283. agno/tools/models/groq.py +30 -23
  284. agno/tools/models/nebius.py +28 -12
  285. agno/tools/models_labs.py +40 -16
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +10 -8
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +58 -32
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +63 -47
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +55 -42
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +100 -123
  335. agno/utils/gemini.py +32 -2
  336. agno/utils/knowledge.py +29 -0
  337. agno/utils/log.py +54 -4
  338. agno/utils/mcp.py +68 -10
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/aws_claude.py +1 -1
  342. agno/utils/models/claude.py +47 -4
  343. agno/utils/models/cohere.py +1 -1
  344. agno/utils/models/mistral.py +8 -7
  345. agno/utils/models/schema_utils.py +3 -3
  346. agno/utils/models/watsonx.py +1 -1
  347. agno/utils/openai.py +1 -1
  348. agno/utils/pprint.py +33 -32
  349. agno/utils/print_response/agent.py +779 -0
  350. agno/utils/print_response/team.py +1669 -0
  351. agno/utils/print_response/workflow.py +1451 -0
  352. agno/utils/prompts.py +14 -14
  353. agno/utils/reasoning.py +87 -0
  354. agno/utils/response.py +42 -42
  355. agno/utils/streamlit.py +481 -0
  356. agno/utils/string.py +8 -22
  357. agno/utils/team.py +50 -0
  358. agno/utils/timer.py +2 -2
  359. agno/vectordb/base.py +33 -21
  360. agno/vectordb/cassandra/cassandra.py +287 -23
  361. agno/vectordb/chroma/chromadb.py +482 -59
  362. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  363. agno/vectordb/couchbase/couchbase.py +309 -29
  364. agno/vectordb/lancedb/lance_db.py +360 -21
  365. agno/vectordb/langchaindb/__init__.py +5 -0
  366. agno/vectordb/langchaindb/langchaindb.py +145 -0
  367. agno/vectordb/lightrag/__init__.py +5 -0
  368. agno/vectordb/lightrag/lightrag.py +374 -0
  369. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  370. agno/vectordb/milvus/milvus.py +242 -32
  371. agno/vectordb/mongodb/mongodb.py +200 -24
  372. agno/vectordb/pgvector/pgvector.py +319 -37
  373. agno/vectordb/pineconedb/pineconedb.py +221 -27
  374. agno/vectordb/qdrant/qdrant.py +334 -14
  375. agno/vectordb/singlestore/singlestore.py +286 -29
  376. agno/vectordb/surrealdb/surrealdb.py +187 -7
  377. agno/vectordb/upstashdb/upstashdb.py +342 -26
  378. agno/vectordb/weaviate/weaviate.py +227 -165
  379. agno/workflow/__init__.py +17 -13
  380. agno/workflow/{v2/condition.py → condition.py} +135 -32
  381. agno/workflow/{v2/loop.py → loop.py} +115 -28
  382. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  383. agno/workflow/{v2/router.py → router.py} +133 -32
  384. agno/workflow/{v2/step.py → step.py} +207 -49
  385. agno/workflow/{v2/steps.py → steps.py} +147 -66
  386. agno/workflow/types.py +482 -0
  387. agno/workflow/workflow.py +2410 -696
  388. agno-2.0.0.dist-info/METADATA +494 -0
  389. agno-2.0.0.dist-info/RECORD +515 -0
  390. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  391. agno/agent/metrics.py +0 -107
  392. agno/api/app.py +0 -35
  393. agno/api/playground.py +0 -92
  394. agno/api/schemas/app.py +0 -12
  395. agno/api/schemas/playground.py +0 -22
  396. agno/api/schemas/user.py +0 -35
  397. agno/api/schemas/workspace.py +0 -46
  398. agno/api/user.py +0 -160
  399. agno/api/workflows.py +0 -33
  400. agno/api/workspace.py +0 -175
  401. agno/app/agui/__init__.py +0 -3
  402. agno/app/agui/app.py +0 -17
  403. agno/app/agui/sync_router.py +0 -120
  404. agno/app/base.py +0 -186
  405. agno/app/discord/__init__.py +0 -3
  406. agno/app/fastapi/__init__.py +0 -3
  407. agno/app/fastapi/app.py +0 -107
  408. agno/app/fastapi/async_router.py +0 -457
  409. agno/app/fastapi/sync_router.py +0 -448
  410. agno/app/playground/app.py +0 -228
  411. agno/app/playground/async_router.py +0 -1050
  412. agno/app/playground/deploy.py +0 -249
  413. agno/app/playground/operator.py +0 -183
  414. agno/app/playground/schemas.py +0 -220
  415. agno/app/playground/serve.py +0 -55
  416. agno/app/playground/sync_router.py +0 -1042
  417. agno/app/playground/utils.py +0 -46
  418. agno/app/settings.py +0 -15
  419. agno/app/slack/__init__.py +0 -3
  420. agno/app/slack/app.py +0 -19
  421. agno/app/slack/sync_router.py +0 -92
  422. agno/app/utils.py +0 -54
  423. agno/app/whatsapp/__init__.py +0 -3
  424. agno/app/whatsapp/app.py +0 -15
  425. agno/app/whatsapp/sync_router.py +0 -197
  426. agno/cli/auth_server.py +0 -249
  427. agno/cli/config.py +0 -274
  428. agno/cli/console.py +0 -88
  429. agno/cli/credentials.py +0 -23
  430. agno/cli/entrypoint.py +0 -571
  431. agno/cli/operator.py +0 -357
  432. agno/cli/settings.py +0 -96
  433. agno/cli/ws/ws_cli.py +0 -817
  434. agno/constants.py +0 -13
  435. agno/document/__init__.py +0 -5
  436. agno/document/chunking/semantic.py +0 -45
  437. agno/document/chunking/strategy.py +0 -31
  438. agno/document/reader/__init__.py +0 -5
  439. agno/document/reader/base.py +0 -47
  440. agno/document/reader/docx_reader.py +0 -60
  441. agno/document/reader/gcs/pdf_reader.py +0 -44
  442. agno/document/reader/s3/pdf_reader.py +0 -59
  443. agno/document/reader/s3/text_reader.py +0 -63
  444. agno/document/reader/url_reader.py +0 -59
  445. agno/document/reader/youtube_reader.py +0 -58
  446. agno/embedder/__init__.py +0 -5
  447. agno/embedder/langdb.py +0 -80
  448. agno/embedder/mistral.py +0 -82
  449. agno/embedder/openai.py +0 -78
  450. agno/file/__init__.py +0 -5
  451. agno/file/file.py +0 -16
  452. agno/file/local/csv.py +0 -32
  453. agno/file/local/txt.py +0 -19
  454. agno/infra/app.py +0 -240
  455. agno/infra/base.py +0 -144
  456. agno/infra/context.py +0 -20
  457. agno/infra/db_app.py +0 -52
  458. agno/infra/resource.py +0 -205
  459. agno/infra/resources.py +0 -55
  460. agno/knowledge/agent.py +0 -702
  461. agno/knowledge/arxiv.py +0 -33
  462. agno/knowledge/combined.py +0 -36
  463. agno/knowledge/csv.py +0 -144
  464. agno/knowledge/csv_url.py +0 -124
  465. agno/knowledge/document.py +0 -223
  466. agno/knowledge/docx.py +0 -137
  467. agno/knowledge/firecrawl.py +0 -34
  468. agno/knowledge/gcs/__init__.py +0 -0
  469. agno/knowledge/gcs/base.py +0 -39
  470. agno/knowledge/gcs/pdf.py +0 -125
  471. agno/knowledge/json.py +0 -137
  472. agno/knowledge/langchain.py +0 -71
  473. agno/knowledge/light_rag.py +0 -273
  474. agno/knowledge/llamaindex.py +0 -66
  475. agno/knowledge/markdown.py +0 -154
  476. agno/knowledge/pdf.py +0 -164
  477. agno/knowledge/pdf_bytes.py +0 -42
  478. agno/knowledge/pdf_url.py +0 -148
  479. agno/knowledge/s3/__init__.py +0 -0
  480. agno/knowledge/s3/base.py +0 -64
  481. agno/knowledge/s3/pdf.py +0 -33
  482. agno/knowledge/s3/text.py +0 -34
  483. agno/knowledge/text.py +0 -141
  484. agno/knowledge/url.py +0 -46
  485. agno/knowledge/website.py +0 -179
  486. agno/knowledge/wikipedia.py +0 -32
  487. agno/knowledge/youtube.py +0 -35
  488. agno/memory/agent.py +0 -423
  489. agno/memory/classifier.py +0 -104
  490. agno/memory/db/__init__.py +0 -5
  491. agno/memory/db/base.py +0 -42
  492. agno/memory/db/mongodb.py +0 -189
  493. agno/memory/db/postgres.py +0 -203
  494. agno/memory/db/sqlite.py +0 -193
  495. agno/memory/memory.py +0 -22
  496. agno/memory/row.py +0 -36
  497. agno/memory/summarizer.py +0 -201
  498. agno/memory/summary.py +0 -19
  499. agno/memory/team.py +0 -415
  500. agno/memory/v2/__init__.py +0 -2
  501. agno/memory/v2/db/__init__.py +0 -1
  502. agno/memory/v2/db/base.py +0 -42
  503. agno/memory/v2/db/firestore.py +0 -339
  504. agno/memory/v2/db/mongodb.py +0 -196
  505. agno/memory/v2/db/postgres.py +0 -214
  506. agno/memory/v2/db/redis.py +0 -187
  507. agno/memory/v2/db/schema.py +0 -54
  508. agno/memory/v2/db/sqlite.py +0 -209
  509. agno/memory/v2/manager.py +0 -437
  510. agno/memory/v2/memory.py +0 -1097
  511. agno/memory/v2/schema.py +0 -55
  512. agno/memory/v2/summarizer.py +0 -215
  513. agno/memory/workflow.py +0 -38
  514. agno/models/ollama/tools.py +0 -430
  515. agno/models/qwen/__init__.py +0 -5
  516. agno/playground/__init__.py +0 -10
  517. agno/playground/deploy.py +0 -3
  518. agno/playground/playground.py +0 -3
  519. agno/playground/serve.py +0 -3
  520. agno/playground/settings.py +0 -3
  521. agno/reranker/__init__.py +0 -0
  522. agno/run/response.py +0 -467
  523. agno/run/v2/__init__.py +0 -0
  524. agno/run/v2/workflow.py +0 -567
  525. agno/storage/__init__.py +0 -0
  526. agno/storage/agent/__init__.py +0 -0
  527. agno/storage/agent/dynamodb.py +0 -1
  528. agno/storage/agent/json.py +0 -1
  529. agno/storage/agent/mongodb.py +0 -1
  530. agno/storage/agent/postgres.py +0 -1
  531. agno/storage/agent/singlestore.py +0 -1
  532. agno/storage/agent/sqlite.py +0 -1
  533. agno/storage/agent/yaml.py +0 -1
  534. agno/storage/base.py +0 -60
  535. agno/storage/dynamodb.py +0 -673
  536. agno/storage/firestore.py +0 -297
  537. agno/storage/gcs_json.py +0 -261
  538. agno/storage/in_memory.py +0 -234
  539. agno/storage/json.py +0 -237
  540. agno/storage/mongodb.py +0 -328
  541. agno/storage/mysql.py +0 -685
  542. agno/storage/postgres.py +0 -682
  543. agno/storage/redis.py +0 -336
  544. agno/storage/session/__init__.py +0 -16
  545. agno/storage/session/agent.py +0 -64
  546. agno/storage/session/team.py +0 -63
  547. agno/storage/session/v2/__init__.py +0 -5
  548. agno/storage/session/workflow.py +0 -61
  549. agno/storage/singlestore.py +0 -606
  550. agno/storage/sqlite.py +0 -646
  551. agno/storage/workflow/__init__.py +0 -0
  552. agno/storage/workflow/mongodb.py +0 -1
  553. agno/storage/workflow/postgres.py +0 -1
  554. agno/storage/workflow/sqlite.py +0 -1
  555. agno/storage/yaml.py +0 -241
  556. agno/tools/thinking.py +0 -73
  557. agno/utils/defaults.py +0 -57
  558. agno/utils/filesystem.py +0 -39
  559. agno/utils/git.py +0 -52
  560. agno/utils/json_io.py +0 -30
  561. agno/utils/load_env.py +0 -19
  562. agno/utils/py_io.py +0 -19
  563. agno/utils/pyproject.py +0 -18
  564. agno/utils/resource_filter.py +0 -31
  565. agno/workflow/v2/__init__.py +0 -21
  566. agno/workflow/v2/types.py +0 -357
  567. agno/workflow/v2/workflow.py +0 -3312
  568. agno/workspace/__init__.py +0 -0
  569. agno/workspace/config.py +0 -325
  570. agno/workspace/enums.py +0 -6
  571. agno/workspace/helpers.py +0 -52
  572. agno/workspace/operator.py +0 -757
  573. agno/workspace/settings.py +0 -158
  574. agno-1.8.1.dist-info/METADATA +0 -982
  575. agno-1.8.1.dist-info/RECORD +0 -566
  576. agno-1.8.1.dist-info/entry_points.txt +0 -3
  577. agno-1.8.1.dist-info/licenses/LICENSE +0 -375
  578. /agno/{app → db/migrations}/__init__.py +0 -0
  579. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  580. /agno/{cli → integrations}/__init__.py +0 -0
  581. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  582. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  583. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  584. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  585. /agno/{app → os/interfaces}/slack/security.py +0 -0
  586. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  587. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  588. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  589. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  590. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
agno/media.py CHANGED
@@ -1,335 +1,336 @@
1
1
  from pathlib import Path
2
2
  from typing import Any, Dict, List, Optional, Tuple, Union
3
+ from uuid import uuid4
3
4
 
4
5
  from pydantic import BaseModel, field_validator, model_validator
5
6
 
6
7
 
7
- class Media(BaseModel):
8
- id: str
9
- original_prompt: Optional[str] = None
10
- revised_prompt: Optional[str] = None
11
-
12
-
13
- class VideoArtifact(Media):
14
- url: Optional[str] = None # Remote location for file (if no inline content)
15
- content: Optional[Union[str, bytes]] = None # type: ignore
16
- mime_type: Optional[str] = None # MIME type of the video content
17
- eta: Optional[str] = None
18
- length: Optional[str] = None
19
-
20
- def to_dict(self) -> Dict[str, Any]:
21
- response_dict = {
22
- "id": self.id,
23
- "url": self.url,
24
- "content": self.content
25
- if isinstance(self.content, str)
26
- else self.content.decode("utf-8")
27
- if self.content
28
- else None,
29
- "mime_type": self.mime_type,
30
- "eta": self.eta,
31
- }
32
- return {k: v for k, v in response_dict.items() if v is not None}
8
+ class Image(BaseModel):
9
+ """Unified Image class for all use cases (input, output, artifacts)"""
33
10
 
11
+ # Core content fields (exactly one required)
12
+ url: Optional[str] = None # Remote location
13
+ filepath: Optional[Union[Path, str]] = None # Local file path
14
+ content: Optional[bytes] = None # Raw image bytes (standardized to bytes)
34
15
 
35
- class ImageArtifact(Media):
36
- url: Optional[str] = None # Remote location for file
37
- content: Optional[bytes] = None # Actual image bytes content
38
- mime_type: Optional[str] = None
39
- alt_text: Optional[str] = None
40
-
41
- def _normalise_content(self) -> Optional[Union[str, bytes]]:
42
- if self.content is None:
43
- return None
44
- content_normalised: Union[str, bytes] = self.content
45
- if content_normalised and isinstance(content_normalised, bytes):
46
- from base64 import b64encode
47
-
48
- try:
49
- # First try to decode as UTF-8
50
- content_normalised = content_normalised.decode("utf-8") # type: ignore
51
- except UnicodeDecodeError:
52
- # Fallback to base64 encoding for binary content
53
- content_normalised = b64encode(bytes(content_normalised)).decode("utf-8") # type: ignore
54
- except Exception:
55
- # Last resort: try to convert to base64
56
- try:
57
- content_normalised = b64encode(bytes(content_normalised)).decode("utf-8") # type: ignore
58
- except Exception:
59
- pass
60
- return content_normalised
61
-
62
- def to_dict(self) -> Dict[str, Any]:
63
- content_normalised = self._normalise_content()
64
-
65
- response_dict = {
66
- "id": self.id,
67
- "url": self.url,
68
- "content": content_normalised,
69
- "mime_type": self.mime_type,
70
- "alt_text": self.alt_text,
71
- }
72
- return {k: v for k, v in response_dict.items() if v is not None}
16
+ # Metadata fields
17
+ id: Optional[str] = None # For tracking/referencing
18
+ format: Optional[str] = None # E.g. 'png', 'jpeg', 'webp', 'gif'
19
+ mime_type: Optional[str] = None # E.g. 'image/png', 'image/jpeg'
73
20
 
21
+ # Input-specific fields
22
+ detail: Optional[str] = (
23
+ None # low, medium, high or auto (per OpenAI spec https://platform.openai.com/docs/guides/vision?lang=node#low-or-high-fidelity-image-understanding)
24
+ )
74
25
 
75
- class AudioArtifact(Media):
76
- url: Optional[str] = None # Remote location for file
77
- base64_audio: Optional[str] = None # Base64-encoded audio data
78
- length: Optional[str] = None
79
- mime_type: Optional[str] = None
26
+ # Output-specific fields (from tools/LLMs)
27
+ original_prompt: Optional[str] = None # Original generation prompt
28
+ revised_prompt: Optional[str] = None # Revised generation prompt
29
+ alt_text: Optional[str] = None # Alt text description
80
30
 
81
31
  @model_validator(mode="before")
82
- def validate_exclusive_audio(cls, data: Any):
83
- """
84
- Ensure that either `url` or `base64_audio` is provided, but not both.
85
- """
86
- if data.get("url") and data.get("base64_audio"):
87
- raise ValueError("Provide either `url` or `base64_audio`, not both.")
88
- if not data.get("url") and not data.get("base64_audio"):
89
- raise ValueError("Either `url` or `base64_audio` must be provided.")
90
- return data
91
-
92
- def to_dict(self) -> Dict[str, Any]:
93
- response_dict = {
94
- "id": self.id,
95
- "url": self.url,
96
- "content": self.base64_audio,
97
- "mime_type": self.mime_type,
98
- "length": self.length,
99
- }
100
- return {k: v for k, v in response_dict.items() if v is not None}
32
+ def validate_and_normalize_content(cls, data: Any):
33
+ """Ensure exactly one content source and normalize to bytes"""
34
+ if isinstance(data, dict):
35
+ url = data.get("url")
36
+ filepath = data.get("filepath")
37
+ content = data.get("content")
38
+
39
+ # Count non-None sources
40
+ sources = [x for x in [url, filepath, content] if x is not None]
41
+ if len(sources) == 0:
42
+ raise ValueError("One of 'url', 'filepath', or 'content' must be provided")
43
+ elif len(sources) > 1:
44
+ raise ValueError("Only one of 'url', 'filepath', or 'content' should be provided")
45
+
46
+ # Auto-generate ID if not provided
47
+ if data.get("id") is None:
48
+ data["id"] = str(uuid4())
101
49
 
50
+ return data
102
51
 
103
- class Video(BaseModel):
104
- filepath: Optional[Union[Path, str]] = None # Absolute local location for video
105
- content: Optional[Any] = None # Actual video bytes content
106
- url: Optional[str] = None # Remote location for video
107
- format: Optional[str] = None # E.g. `mp4`, `mov`, `avi`, `mkv`, `webm`, `flv`, `mpeg`, `mpg`, `wmv`, `three_gp`
52
+ def get_content_bytes(self) -> Optional[bytes]:
53
+ """Get image content as raw bytes, loading from URL/file if needed"""
54
+ if self.content:
55
+ return self.content
56
+ elif self.url:
57
+ import httpx
108
58
 
109
- @model_validator(mode="before")
110
- def validate_data(cls, data: Any):
111
- """
112
- Ensure that exactly one of `filepath`, or `content` or `url` is provided.
113
- Also converts content to bytes if it's a string.
114
- """
115
- # Extract the values from the input data
116
- filepath = data.get("filepath")
117
- content = data.get("content")
118
- url = data.get("url")
119
-
120
- # Convert and decompress content to bytes if it's a string
121
- if content and isinstance(content, str):
59
+ return httpx.get(self.url).content
60
+ elif self.filepath:
61
+ with open(self.filepath, "rb") as f:
62
+ return f.read()
63
+ return None
64
+
65
+ def to_base64(self) -> Optional[str]:
66
+ """Convert content to base64 string for transmission/storage"""
67
+ content_bytes = self.get_content_bytes()
68
+ if content_bytes:
122
69
  import base64
123
70
 
124
- try:
125
- import zlib
126
-
127
- decoded_content = base64.b64decode(content)
128
- content = zlib.decompress(decoded_content)
129
- except Exception:
130
- content = base64.b64decode(content).decode("utf-8")
131
- data["content"] = content
71
+ return base64.b64encode(content_bytes).decode("utf-8")
72
+ return None
132
73
 
133
- # Count how many fields are set (not None)
134
- count = len([field for field in [filepath, content, url] if field is not None])
74
+ @classmethod
75
+ def from_base64(
76
+ cls,
77
+ base64_content: str,
78
+ id: Optional[str] = None,
79
+ mime_type: Optional[str] = None,
80
+ format: Optional[str] = None,
81
+ **kwargs,
82
+ ) -> "Image":
83
+ """Create Image from base64 content"""
84
+ import base64
135
85
 
136
- if count == 0:
137
- raise ValueError("One of `filepath` or `content` or `url` must be provided.")
138
- elif count > 1:
139
- raise ValueError("Only one of `filepath` or `content` or `url` should be provided.")
86
+ try:
87
+ content_bytes = base64.b64decode(base64_content)
88
+ except Exception:
89
+ content_bytes = base64_content.encode("utf-8")
140
90
 
141
- return data
91
+ return cls(content=content_bytes, id=id or str(uuid4()), mime_type=mime_type, format=format, **kwargs)
142
92
 
143
- def to_dict(self) -> Dict[str, Any]:
144
- import base64
145
- import zlib
146
-
147
- response_dict = {
148
- "content": base64.b64encode(
149
- zlib.compress(self.content) if isinstance(self.content, bytes) else self.content.encode("utf-8")
150
- ).decode("utf-8")
151
- if self.content
152
- else None,
153
- "filepath": self.filepath,
93
+ def to_dict(self, include_base64_content: bool = True) -> Dict[str, Any]:
94
+ """Convert to dict, optionally including base64-encoded content"""
95
+ result = {
96
+ "id": self.id,
97
+ "url": self.url,
98
+ "filepath": str(self.filepath) if self.filepath else None,
154
99
  "format": self.format,
100
+ "mime_type": self.mime_type,
101
+ "detail": self.detail,
102
+ "original_prompt": self.original_prompt,
103
+ "revised_prompt": self.revised_prompt,
104
+ "alt_text": self.alt_text,
155
105
  }
156
- return {k: v for k, v in response_dict.items() if v is not None}
157
106
 
158
- @classmethod
159
- def from_artifact(cls, artifact: VideoArtifact) -> "Video":
160
- return cls(url=artifact.url, content=artifact.content, format=artifact.mime_type)
107
+ if include_base64_content and self.content:
108
+ result["content"] = self.to_base64()
109
+
110
+ return {k: v for k, v in result.items() if v is not None}
161
111
 
162
112
 
163
113
  class Audio(BaseModel):
164
- content: Optional[Any] = None # Actual audio bytes content
165
- filepath: Optional[Union[Path, str]] = None # Absolute local location for audio
166
- url: Optional[str] = None # Remote location for audio
167
- format: Optional[str] = None
114
+ """Unified Audio class for all use cases (input, output, artifacts)"""
168
115
 
169
- @model_validator(mode="before")
170
- def validate_data(cls, data: Any):
171
- """
172
- Ensure that exactly one of `filepath`, or `content` is provided.
173
- Also converts content to bytes if it's a string.
174
- """
175
- # Extract the values from the input data
176
- filepath = data.get("filepath")
177
- content = data.get("content")
178
- url = data.get("url")
179
-
180
- # Convert and decompress content to bytes if it's a string
181
- if content and isinstance(content, str):
182
- import base64
116
+ # Core content fields (exactly one required)
117
+ url: Optional[str] = None
118
+ filepath: Optional[Union[Path, str]] = None
119
+ content: Optional[bytes] = None # Raw audio bytes (standardized to bytes)
183
120
 
184
- try:
185
- import zlib
121
+ # Metadata fields
122
+ id: Optional[str] = None
123
+ format: Optional[str] = None # E.g. 'mp3', 'wav', 'ogg'
124
+ mime_type: Optional[str] = None # E.g. 'audio/mpeg', 'audio/wav'
186
125
 
187
- decoded_content = base64.b64decode(content)
188
- content = zlib.decompress(decoded_content)
189
- except Exception:
190
- content = base64.b64decode(content).decode("utf-8")
191
- data["content"] = content
126
+ # Audio-specific metadata
127
+ duration: Optional[float] = None # Duration in seconds
128
+ sample_rate: Optional[int] = 24000 # Sample rate in Hz
129
+ channels: Optional[int] = 1 # Number of audio channels
192
130
 
193
- # Count how many fields are set (not None)
194
- count = len([field for field in [filepath, content, url] if field is not None])
131
+ # Output-specific fields (from LLMs)
132
+ transcript: Optional[str] = None # Text transcript of audio
133
+ expires_at: Optional[int] = None # Expiration timestamp for temporary URLs
195
134
 
196
- if count == 0:
197
- raise ValueError("One of `filepath` or `content` or `url` must be provided.")
198
- elif count > 1:
199
- raise ValueError("Only one of `filepath` or `content` or `url` should be provided.")
135
+ @model_validator(mode="before")
136
+ def validate_and_normalize_content(cls, data: Any):
137
+ """Ensure exactly one content source and normalize to bytes"""
138
+ if isinstance(data, dict):
139
+ url = data.get("url")
140
+ filepath = data.get("filepath")
141
+ content = data.get("content")
142
+
143
+ sources = [x for x in [url, filepath, content] if x is not None]
144
+ if len(sources) == 0:
145
+ raise ValueError("One of 'url', 'filepath', or 'content' must be provided")
146
+ elif len(sources) > 1:
147
+ raise ValueError("Only one of 'url', 'filepath', or 'content' should be provided")
148
+
149
+ if data.get("id") is None:
150
+ data["id"] = str(uuid4())
200
151
 
201
152
  return data
202
153
 
203
- @property
204
- def audio_url_content(self) -> Optional[bytes]:
205
- import httpx
154
+ def get_content_bytes(self) -> Optional[bytes]:
155
+ """Get audio content as raw bytes"""
156
+ if self.content:
157
+ return self.content
158
+ elif self.url:
159
+ import httpx
206
160
 
207
- if self.url:
208
161
  return httpx.get(self.url).content
209
- else:
210
- return None
211
-
212
- def to_dict(self) -> Dict[str, Any]:
213
- import base64
214
- import zlib
215
-
216
- response_dict = {
217
- "content": base64.b64encode(
218
- zlib.compress(self.content) if isinstance(self.content, bytes) else self.content.encode("utf-8")
219
- ).decode("utf-8")
220
- if self.content
221
- else None,
222
- "filepath": self.filepath,
223
- "format": self.format,
224
- }
162
+ elif self.filepath:
163
+ with open(self.filepath, "rb") as f:
164
+ return f.read()
165
+ return None
166
+
167
+ def to_base64(self) -> Optional[str]:
168
+ """Convert content to base64 string"""
169
+ content_bytes = self.get_content_bytes()
170
+ if content_bytes:
171
+ import base64
225
172
 
226
- return {k: v for k, v in response_dict.items() if v is not None}
173
+ return base64.b64encode(content_bytes).decode("utf-8")
174
+ return None
227
175
 
228
176
  @classmethod
229
- def from_artifact(cls, artifact: AudioArtifact) -> "Audio":
230
- return cls(url=artifact.url, content=artifact.base64_audio, format=artifact.mime_type)
231
-
232
-
233
- class AudioResponse(BaseModel):
234
- id: Optional[str] = None
235
- content: Optional[str] = None # Base64 encoded
236
- expires_at: Optional[int] = None
237
- transcript: Optional[str] = None
238
-
239
- mime_type: Optional[str] = None
240
- sample_rate: Optional[int] = 24000
241
- channels: Optional[int] = 1
242
-
243
- def to_dict(self) -> Dict[str, Any]:
177
+ def from_base64(
178
+ cls,
179
+ base64_content: str,
180
+ id: Optional[str] = None,
181
+ mime_type: Optional[str] = None,
182
+ transcript: Optional[str] = None,
183
+ expires_at: Optional[int] = None,
184
+ sample_rate: Optional[int] = 24000,
185
+ channels: Optional[int] = 1,
186
+ **kwargs,
187
+ ) -> "Audio":
188
+ """Create Audio from base64 content (useful for API responses)"""
244
189
  import base64
245
190
 
246
- response_dict = {
191
+ try:
192
+ content_bytes = base64.b64decode(base64_content)
193
+ except Exception:
194
+ # If not valid base64, encode as UTF-8 bytes
195
+ content_bytes = base64_content.encode("utf-8")
196
+
197
+ return cls(
198
+ content=content_bytes,
199
+ id=id or str(uuid4()),
200
+ mime_type=mime_type,
201
+ transcript=transcript,
202
+ expires_at=expires_at,
203
+ sample_rate=sample_rate,
204
+ channels=channels,
205
+ **kwargs,
206
+ )
207
+
208
+ def to_dict(self, include_base64_content: bool = True) -> Dict[str, Any]:
209
+ """Convert to dict, optionally including base64-encoded content"""
210
+ result = {
247
211
  "id": self.id,
248
- "content": base64.b64encode(self.content).decode("utf-8")
249
- if isinstance(self.content, bytes)
250
- else self.content,
251
- "expires_at": self.expires_at,
252
- "transcript": self.transcript,
212
+ "url": self.url,
213
+ "filepath": str(self.filepath) if self.filepath else None,
214
+ "format": self.format,
253
215
  "mime_type": self.mime_type,
216
+ "duration": self.duration,
254
217
  "sample_rate": self.sample_rate,
255
218
  "channels": self.channels,
219
+ "transcript": self.transcript,
220
+ "expires_at": self.expires_at,
256
221
  }
257
- return {k: v for k, v in response_dict.items() if v is not None}
258
222
 
223
+ if include_base64_content and self.content:
224
+ result["content"] = self.to_base64()
259
225
 
260
- class Image(BaseModel):
261
- url: Optional[str] = None # Remote location for image
262
- filepath: Optional[Union[Path, str]] = None # Absolute local location for image
263
- content: Optional[Any] = None # Actual image bytes content
264
- format: Optional[str] = None # E.g. `png`, `jpeg`, `webp`, `gif`
265
- detail: Optional[str] = (
266
- None # low, medium, high or auto (per OpenAI spec https://platform.openai.com/docs/guides/vision?lang=node#low-or-high-fidelity-image-understanding)
267
- )
268
- id: Optional[str] = None
226
+ return {k: v for k, v in result.items() if v is not None}
269
227
 
270
- @property
271
- def image_url_content(self) -> Optional[bytes]:
272
- import httpx
273
228
 
274
- if self.url:
275
- return httpx.get(self.url).content
276
- else:
277
- return None
229
+ class Video(BaseModel):
230
+ """Unified Video class for all use cases (input, output, artifacts)"""
278
231
 
279
- @model_validator(mode="before")
280
- def validate_data(cls, data: Any):
281
- """
282
- Ensure that exactly one of `url`, `filepath`, or `content` is provided.
283
- Also converts content to bytes if it's a string.
284
- """
285
- # Extract the values from the input data
286
- url = data.get("url")
287
- filepath = data.get("filepath")
288
- content = data.get("content")
289
-
290
- # Convert and decompress content to bytes if it's a string
291
- if content and isinstance(content, str):
292
- import base64
232
+ # Core content fields (exactly one required)
233
+ url: Optional[str] = None
234
+ filepath: Optional[Union[Path, str]] = None
235
+ content: Optional[bytes] = None # Raw video bytes (standardized to bytes)
293
236
 
294
- try:
295
- import zlib
237
+ # Metadata fields
238
+ id: Optional[str] = None
239
+ format: Optional[str] = None # E.g. 'mp4', 'mov', 'avi', 'webm'
240
+ mime_type: Optional[str] = None # E.g. 'video/mp4', 'video/quicktime'
296
241
 
297
- decoded_content = base64.b64decode(content)
298
- content = zlib.decompress(decoded_content)
299
- except Exception:
300
- content = base64.b64decode(content).decode("utf-8")
301
- data["content"] = content
242
+ # Video-specific metadata
243
+ duration: Optional[float] = None # Duration in seconds
244
+ width: Optional[int] = None # Video width in pixels
245
+ height: Optional[int] = None # Video height in pixels
246
+ fps: Optional[float] = None # Frames per second
302
247
 
303
- # Count how many fields are set (not None)
304
- count = len([field for field in [url, filepath, content] if field is not None])
248
+ # Output-specific fields (from tools)
249
+ eta: Optional[str] = None # Estimated time for generation
250
+ original_prompt: Optional[str] = None
251
+ revised_prompt: Optional[str] = None
305
252
 
306
- if count == 0:
307
- raise ValueError("One of `url`, `filepath`, or `content` must be provided.")
308
- elif count > 1:
309
- raise ValueError("Only one of `url`, `filepath`, or `content` should be provided.")
253
+ @model_validator(mode="before")
254
+ def validate_and_normalize_content(cls, data: Any):
255
+ """Ensure exactly one content source and normalize to bytes"""
256
+ if isinstance(data, dict):
257
+ url = data.get("url")
258
+ filepath = data.get("filepath")
259
+ content = data.get("content")
260
+
261
+ sources = [x for x in [url, filepath, content] if x is not None]
262
+ if len(sources) == 0:
263
+ raise ValueError("One of 'url', 'filepath', or 'content' must be provided")
264
+ elif len(sources) > 1:
265
+ raise ValueError("Only one of 'url', 'filepath', or 'content' should be provided")
266
+
267
+ if data.get("id") is None:
268
+ data["id"] = str(uuid4())
310
269
 
311
270
  return data
312
271
 
313
- def to_dict(self) -> Dict[str, Any]:
272
+ def get_content_bytes(self) -> Optional[bytes]:
273
+ """Get video content as raw bytes"""
274
+ if self.content:
275
+ return self.content
276
+ elif self.url:
277
+ import httpx
278
+
279
+ return httpx.get(self.url).content
280
+ elif self.filepath:
281
+ with open(self.filepath, "rb") as f:
282
+ return f.read()
283
+ return None
284
+
285
+ def to_base64(self) -> Optional[str]:
286
+ """Convert content to base64 string"""
287
+ content_bytes = self.get_content_bytes()
288
+ if content_bytes:
289
+ import base64
290
+
291
+ return base64.b64encode(content_bytes).decode("utf-8")
292
+ return None
293
+
294
+ @classmethod
295
+ def from_base64(
296
+ cls,
297
+ base64_content: str,
298
+ id: Optional[str] = None,
299
+ mime_type: Optional[str] = None,
300
+ format: Optional[str] = None,
301
+ **kwargs,
302
+ ) -> "Video":
303
+ """Create Image from base64 content"""
314
304
  import base64
315
- import zlib
316
-
317
- response_dict = {
318
- "content": base64.b64encode(
319
- zlib.compress(self.content) if isinstance(self.content, bytes) else self.content.encode("utf-8")
320
- ).decode("utf-8")
321
- if self.content
322
- else None,
323
- "filepath": self.filepath,
305
+
306
+ try:
307
+ content_bytes = base64.b64decode(base64_content)
308
+ except Exception:
309
+ content_bytes = base64_content.encode("utf-8")
310
+
311
+ return cls(content=content_bytes, id=id or str(uuid4()), mime_type=mime_type, format=format, **kwargs)
312
+
313
+ def to_dict(self, include_base64_content: bool = True) -> Dict[str, Any]:
314
+ """Convert to dict, optionally including base64-encoded content"""
315
+ result = {
316
+ "id": self.id,
324
317
  "url": self.url,
325
- "detail": self.detail,
318
+ "filepath": str(self.filepath) if self.filepath else None,
319
+ "format": self.format,
320
+ "mime_type": self.mime_type,
321
+ "duration": self.duration,
322
+ "width": self.width,
323
+ "height": self.height,
324
+ "fps": self.fps,
325
+ "eta": self.eta,
326
+ "original_prompt": self.original_prompt,
327
+ "revised_prompt": self.revised_prompt,
326
328
  }
327
329
 
328
- return {k: v for k, v in response_dict.items() if v is not None}
330
+ if include_base64_content and self.content:
331
+ result["content"] = self.to_base64()
329
332
 
330
- @classmethod
331
- def from_artifact(cls, artifact: ImageArtifact) -> "Image":
332
- return cls(url=artifact.url, content=artifact.content, format=artifact.mime_type)
333
+ return {k: v for k, v in result.items() if v is not None}
333
334
 
334
335
 
335
336
  class File(BaseModel):
agno/memory/__init__.py CHANGED
@@ -1,11 +1,3 @@
1
- from agno.memory.agent import AgentMemory
2
- from agno.memory.memory import Memory
3
- from agno.memory.row import MemoryRow
4
- from agno.memory.team import TeamMemory
1
+ from agno.memory.manager import MemoryManager, UserMemory
5
2
 
6
- __all__ = [
7
- "AgentMemory",
8
- "Memory",
9
- "MemoryRow",
10
- "TeamMemory",
11
- ]
3
+ __all__ = ["MemoryManager", "UserMemory"]