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,635 @@
1
+ """Migration utility to migrate your Agno tables from v1 to v2"""
2
+
3
+ import gc
4
+ import json
5
+ from typing import Any, Dict, List, Optional, Union, cast
6
+
7
+ from sqlalchemy import text
8
+
9
+ from agno.db.base import BaseDb
10
+ from agno.db.schemas.memory import UserMemory
11
+ from agno.session import AgentSession, TeamSession, WorkflowSession
12
+ from agno.utils.log import log_error, log_info, log_warning
13
+
14
+
15
+ def convert_v1_metrics_to_v2(metrics_dict: Dict[str, Any]) -> Dict[str, Any]:
16
+ """Convert v1 metrics dictionary to v2 format by mapping old field names to new ones."""
17
+ if not isinstance(metrics_dict, dict):
18
+ return metrics_dict
19
+
20
+ # Create a copy to avoid modifying the original
21
+ v2_metrics = metrics_dict.copy()
22
+
23
+ # Map v1 field names to v2 field names
24
+ field_mappings = {
25
+ "time": "duration",
26
+ "audio_tokens": "audio_total_tokens",
27
+ "input_audio_tokens": "audio_input_tokens",
28
+ "output_audio_tokens": "audio_output_tokens",
29
+ "cached_tokens": "cache_read_tokens",
30
+ }
31
+
32
+ # Fields to remove (deprecated in v2)
33
+ deprecated_fields = ["prompt_tokens", "completion_tokens", "prompt_tokens_details", "completion_tokens_details"]
34
+
35
+ # Apply field mappings
36
+ for old_field, new_field in field_mappings.items():
37
+ if old_field in v2_metrics:
38
+ v2_metrics[new_field] = v2_metrics.pop(old_field)
39
+
40
+ # Remove deprecated fields
41
+ for field in deprecated_fields:
42
+ v2_metrics.pop(field, None)
43
+
44
+ return v2_metrics
45
+
46
+
47
+ def convert_any_metrics_in_data(data: Any) -> Any:
48
+ """Recursively find and convert any metrics dictionaries and handle v1 to v2 field conversion."""
49
+ if isinstance(data, dict):
50
+ # First apply v1 to v2 field conversion (handles extra_data extraction, thinking/reasoning_content consolidation, etc.)
51
+ data = convert_v1_fields_to_v2(data)
52
+
53
+ # Check if this looks like a metrics dictionary
54
+ if _is_metrics_dict(data):
55
+ return convert_v1_metrics_to_v2(data)
56
+
57
+ # Otherwise, recursively process all values
58
+ converted_dict = {}
59
+ for key, value in data.items():
60
+ # Special handling for 'metrics' keys - always convert their values
61
+ if key == "metrics" and isinstance(value, dict):
62
+ converted_dict[key] = convert_v1_metrics_to_v2(value)
63
+ else:
64
+ converted_dict[key] = convert_any_metrics_in_data(value)
65
+ return converted_dict
66
+
67
+ elif isinstance(data, list):
68
+ return [convert_any_metrics_in_data(item) for item in data]
69
+
70
+ else:
71
+ # Not a dict or list, return as-is
72
+ return data
73
+
74
+
75
+ def _is_metrics_dict(data: Dict[str, Any]) -> bool:
76
+ """Check if a dictionary looks like a metrics dictionary based on common field names."""
77
+ if not isinstance(data, dict):
78
+ return False
79
+
80
+ # Common metrics field names (both v1 and v2)
81
+ metrics_indicators = {
82
+ "input_tokens",
83
+ "output_tokens",
84
+ "total_tokens",
85
+ "time",
86
+ "duration",
87
+ "audio_tokens",
88
+ "audio_total_tokens",
89
+ "audio_input_tokens",
90
+ "audio_output_tokens",
91
+ "cached_tokens",
92
+ "cache_read_tokens",
93
+ "cache_write_tokens",
94
+ "reasoning_tokens",
95
+ "prompt_tokens",
96
+ "completion_tokens",
97
+ "time_to_first_token",
98
+ "provider_metrics",
99
+ "additional_metrics",
100
+ }
101
+
102
+ # Deprecated v1 fields that are strong indicators this is a metrics dict
103
+ deprecated_v1_indicators = {"time", "audio_tokens", "cached_tokens", "prompt_tokens", "completion_tokens"}
104
+
105
+ # If we find any deprecated v1 field, it's definitely a metrics dict that needs conversion
106
+ if any(field in data for field in deprecated_v1_indicators):
107
+ return True
108
+
109
+ # Otherwise, if the dict has at least 2 metrics-related fields, consider it a metrics dict
110
+ matching_fields = sum(1 for field in data.keys() if field in metrics_indicators)
111
+ return matching_fields >= 2
112
+
113
+
114
+ def convert_session_data_comprehensively(session_data: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
115
+ """Comprehensively convert session data from v1 to v2 format, including metrics conversion and field mapping."""
116
+ if not session_data:
117
+ return session_data
118
+
119
+ # Use the recursive converter to handle all v1 to v2 conversions (metrics, field mapping, extra_data extraction, etc.)
120
+ return convert_any_metrics_in_data(session_data)
121
+
122
+
123
+ def safe_get_runs_from_memory(memory_data: Any) -> Any:
124
+ """Safely extract runs data from memory field, handling various data types."""
125
+ if memory_data is None:
126
+ return None
127
+
128
+ runs: Any = []
129
+
130
+ # If memory_data is a string, try to parse it as JSON
131
+ if isinstance(memory_data, str):
132
+ try:
133
+ memory_dict = json.loads(memory_data)
134
+ if isinstance(memory_dict, dict):
135
+ runs = memory_dict.get("runs")
136
+ except (json.JSONDecodeError, AttributeError):
137
+ # If JSON parsing fails, memory_data might just be a string value
138
+ return None
139
+
140
+ # If memory_data is already a dict, access runs directly
141
+ elif isinstance(memory_data, dict):
142
+ runs = memory_data.get("runs")
143
+
144
+ for run in runs or []:
145
+ # Adjust fields mapping for Agent sessions
146
+ if run.get("agent_id") is not None:
147
+ if run.get("team_id") is not None:
148
+ run.pop("team_id")
149
+ if run.get("team_session_id") is not None:
150
+ run["session_id"] = run.pop("team_session_id")
151
+ if run.get("event"):
152
+ run["events"] = [run.pop("event")]
153
+
154
+ # Adjust fields mapping for Team sessions
155
+ if run.get("team_id") is not None:
156
+ if run.get("agent_id") is not None:
157
+ run.pop("agent_id")
158
+ if member_responses := run.get("member_responses"):
159
+ for response in member_responses:
160
+ if response.get("agent_id") is not None and response.get("team_id") is not None:
161
+ response.pop("team_id")
162
+ if response.get("agent_id") is not None and response.get("team_session_id") is not None:
163
+ response["session_id"] = response.pop("team_session_id")
164
+ run["member_responses"] = member_responses
165
+
166
+ return runs
167
+
168
+
169
+ def convert_v1_media_to_v2(media_data: Dict[str, Any]) -> Dict[str, Any]:
170
+ """Convert v1 media objects to v2 format."""
171
+ if not isinstance(media_data, dict):
172
+ return media_data
173
+
174
+ # Create a copy to avoid modifying the original
175
+ v2_media = media_data.copy()
176
+
177
+ # Add id if missing (required in v2)
178
+ if "id" not in v2_media or v2_media["id"] is None:
179
+ from uuid import uuid4
180
+
181
+ v2_media["id"] = str(uuid4())
182
+
183
+ # Handle VideoArtifact → Video conversion
184
+ if "eta" in v2_media or "length" in v2_media:
185
+ # Convert length to duration if it's numeric
186
+ length = v2_media.pop("length", None)
187
+ if length and isinstance(length, (int, float)):
188
+ v2_media["duration"] = length
189
+ elif length and isinstance(length, str):
190
+ try:
191
+ v2_media["duration"] = float(length)
192
+ except ValueError:
193
+ pass # Keep as is if not convertible
194
+
195
+ # Handle AudioArtifact → Audio conversion
196
+ if "base64_audio" in v2_media:
197
+ # Map base64_audio to content
198
+ base64_audio = v2_media.pop("base64_audio", None)
199
+ if base64_audio:
200
+ v2_media["content"] = base64_audio
201
+
202
+ # Handle AudioResponse content conversion (base64 string to bytes if needed)
203
+ if "transcript" in v2_media and "content" in v2_media:
204
+ content = v2_media.get("content")
205
+ if content and isinstance(content, str):
206
+ # Try to decode base64 content to bytes for v2
207
+ try:
208
+ import base64
209
+
210
+ v2_media["content"] = base64.b64decode(content)
211
+ except Exception:
212
+ # If not valid base64, keep as string
213
+ pass
214
+
215
+ # Ensure format and mime_type are set appropriately
216
+ if "format" in v2_media and "mime_type" not in v2_media:
217
+ format_val = v2_media["format"]
218
+ if format_val:
219
+ # Set mime_type based on format for common types
220
+ mime_type_map = {
221
+ "mp4": "video/mp4",
222
+ "mov": "video/quicktime",
223
+ "avi": "video/x-msvideo",
224
+ "webm": "video/webm",
225
+ "mp3": "audio/mpeg",
226
+ "wav": "audio/wav",
227
+ "ogg": "audio/ogg",
228
+ "png": "image/png",
229
+ "jpg": "image/jpeg",
230
+ "jpeg": "image/jpeg",
231
+ "gif": "image/gif",
232
+ "webp": "image/webp",
233
+ }
234
+ if format_val.lower() in mime_type_map:
235
+ v2_media["mime_type"] = mime_type_map[format_val.lower()]
236
+
237
+ return v2_media
238
+
239
+
240
+ def convert_v1_fields_to_v2(data: Dict[str, Any]) -> Dict[str, Any]:
241
+ """Convert v1 fields to v2 format with proper field mapping and extraction."""
242
+ if not isinstance(data, dict):
243
+ return data
244
+
245
+ # Create a copy to avoid modifying the original
246
+ v2_data = data.copy()
247
+
248
+ # Fields that should be completely ignored/removed in v2
249
+ deprecated_fields = {
250
+ "team_session_id", # RunOutput v1 field, removed in v2
251
+ "formatted_tool_calls", # RunOutput v1 field, removed in v2
252
+ "event", # Remove event field
253
+ "events", # Remove events field
254
+ # Add other deprecated fields here as needed
255
+ }
256
+
257
+ # Extract and map fields from extra_data before removing it
258
+ extra_data = v2_data.get("extra_data")
259
+ if extra_data and isinstance(extra_data, dict):
260
+ # Map extra_data fields to their v2 locations
261
+ if "add_messages" in extra_data:
262
+ v2_data["additional_input"] = extra_data["add_messages"]
263
+ if "references" in extra_data:
264
+ v2_data["references"] = extra_data["references"]
265
+ if "reasoning_steps" in extra_data:
266
+ v2_data["reasoning_steps"] = extra_data["reasoning_steps"]
267
+ if "reasoning_content" in extra_data:
268
+ # reasoning_content from extra_data also goes to reasoning_content
269
+ v2_data["reasoning_content"] = extra_data["reasoning_content"]
270
+ if "reasoning_messages" in extra_data:
271
+ v2_data["reasoning_messages"] = extra_data["reasoning_messages"]
272
+
273
+ # Handle thinking and reasoning_content consolidation
274
+ # Both thinking and reasoning_content from v1 should become reasoning_content in v2
275
+ thinking = v2_data.get("thinking")
276
+ reasoning_content = v2_data.get("reasoning_content")
277
+
278
+ # Consolidate thinking and reasoning_content into reasoning_content
279
+ if thinking and reasoning_content:
280
+ # Both exist, combine them (thinking first, then reasoning_content)
281
+ v2_data["reasoning_content"] = f"{thinking}\n{reasoning_content}"
282
+ elif thinking and not reasoning_content:
283
+ # Only thinking exists, move it to reasoning_content
284
+ v2_data["reasoning_content"] = thinking
285
+ # If only reasoning_content exists, keep it as is
286
+
287
+ # Remove thinking field since it's now consolidated into reasoning_content
288
+ if "thinking" in v2_data:
289
+ del v2_data["thinking"]
290
+
291
+ # Handle media object conversions
292
+ media_fields = ["images", "videos", "audio", "response_audio"]
293
+ for field in media_fields:
294
+ if field in v2_data and v2_data[field]:
295
+ if isinstance(v2_data[field], list):
296
+ # Handle list of media objects
297
+ v2_data[field] = [
298
+ convert_v1_media_to_v2(item) if isinstance(item, dict) else item for item in v2_data[field]
299
+ ]
300
+ elif isinstance(v2_data[field], dict):
301
+ # Handle single media object
302
+ v2_data[field] = convert_v1_media_to_v2(v2_data[field])
303
+
304
+ # Remove extra_data after extraction
305
+ if "extra_data" in v2_data:
306
+ del v2_data["extra_data"]
307
+
308
+ # Remove other deprecated fields
309
+ for field in deprecated_fields:
310
+ v2_data.pop(field, None)
311
+
312
+ return v2_data
313
+
314
+
315
+ def migrate(
316
+ db: BaseDb,
317
+ v1_db_schema: str,
318
+ agent_sessions_table_name: Optional[str] = None,
319
+ team_sessions_table_name: Optional[str] = None,
320
+ workflow_sessions_table_name: Optional[str] = None,
321
+ memories_table_name: Optional[str] = None,
322
+ batch_size: int = 5000,
323
+ ):
324
+ """Given a database connection and table/collection names, parse and migrate the content to corresponding v2 tables/collections.
325
+
326
+ Args:
327
+ db: The database to migrate (PostgresDb, MySQLDb, SqliteDb, or MongoDb)
328
+ v1_db_schema: The schema of the v1 tables (leave empty for SQLite and MongoDB)
329
+ agent_sessions_table_name: The name of the agent sessions table/collection. If not provided, agent sessions will not be migrated.
330
+ team_sessions_table_name: The name of the team sessions table/collection. If not provided, team sessions will not be migrated.
331
+ workflow_sessions_table_name: The name of the workflow sessions table/collection. If not provided, workflow sessions will not be migrated.
332
+ memories_table_name: The name of the memories table/collection. If not provided, memories will not be migrated.
333
+ batch_size: Number of records to process in each batch (default: 5000)
334
+ """
335
+ if agent_sessions_table_name:
336
+ migrate_table_in_batches(
337
+ db=db,
338
+ v1_db_schema=v1_db_schema,
339
+ v1_table_name=agent_sessions_table_name,
340
+ v1_table_type="agent_sessions",
341
+ batch_size=batch_size,
342
+ )
343
+
344
+ if team_sessions_table_name:
345
+ migrate_table_in_batches(
346
+ db=db,
347
+ v1_db_schema=v1_db_schema,
348
+ v1_table_name=team_sessions_table_name,
349
+ v1_table_type="team_sessions",
350
+ batch_size=batch_size,
351
+ )
352
+
353
+ if workflow_sessions_table_name:
354
+ migrate_table_in_batches(
355
+ db=db,
356
+ v1_db_schema=v1_db_schema,
357
+ v1_table_name=workflow_sessions_table_name,
358
+ v1_table_type="workflow_sessions",
359
+ batch_size=batch_size,
360
+ )
361
+
362
+ if memories_table_name:
363
+ migrate_table_in_batches(
364
+ db=db,
365
+ v1_db_schema=v1_db_schema,
366
+ v1_table_name=memories_table_name,
367
+ v1_table_type="memories",
368
+ batch_size=batch_size,
369
+ )
370
+
371
+
372
+ def migrate_table_in_batches(
373
+ db: BaseDb,
374
+ v1_db_schema: str,
375
+ v1_table_name: str,
376
+ v1_table_type: str,
377
+ batch_size: int = 5000,
378
+ ):
379
+ log_info(f"Starting migration of table {v1_table_name} (type: {v1_table_type}) with batch size {batch_size}")
380
+
381
+ total_migrated = 0
382
+ batch_count = 0
383
+
384
+ for batch_content in get_table_content_in_batches(db, v1_db_schema, v1_table_name, batch_size):
385
+ batch_count += 1
386
+ batch_size_actual = len(batch_content)
387
+ log_info(f"Processing batch {batch_count} with {batch_size_actual} records from table {v1_table_name}")
388
+
389
+ # Parse the content into the new format
390
+ memories: List[UserMemory] = []
391
+ sessions: Union[List[AgentSession], List[TeamSession], List[WorkflowSession]] = []
392
+
393
+ if v1_table_type == "agent_sessions":
394
+ sessions = parse_agent_sessions(batch_content)
395
+ elif v1_table_type == "team_sessions":
396
+ sessions = parse_team_sessions(batch_content)
397
+ elif v1_table_type == "workflow_sessions":
398
+ sessions = parse_workflow_sessions(batch_content)
399
+ elif v1_table_type == "memories":
400
+ memories = parse_memories(batch_content)
401
+ else:
402
+ raise ValueError(f"Invalid table type: {v1_table_type}")
403
+
404
+ # Insert the batch into the new table
405
+ if v1_table_type in ["agent_sessions", "team_sessions", "workflow_sessions"]:
406
+ if sessions:
407
+ # Clear any existing scoped session state for SQL databases to prevent transaction conflicts
408
+ if hasattr(db, "Session"):
409
+ db.Session.remove() # type: ignore
410
+
411
+ db.upsert_sessions(sessions, preserve_updated_at=True) # type: ignore
412
+ total_migrated += len(sessions)
413
+ log_info(f"Bulk upserted {len(sessions)} sessions in batch {batch_count}")
414
+
415
+ elif v1_table_type == "memories":
416
+ if memories:
417
+ # Clear any existing scoped session state for SQL databases to prevent transaction conflicts
418
+ if hasattr(db, "Session"):
419
+ db.Session.remove() # type: ignore
420
+
421
+ db.upsert_memories(memories, preserve_updated_at=True)
422
+ total_migrated += len(memories)
423
+ log_info(f"Bulk upserted {len(memories)} memories in batch {batch_count}")
424
+
425
+ log_info(f"Completed batch {batch_count}: migrated {batch_size_actual} records")
426
+
427
+ # Explicit cleanup to free memory before next batch
428
+ del batch_content
429
+ if v1_table_type in ["agent_sessions", "team_sessions", "workflow_sessions"]:
430
+ del sessions
431
+ elif v1_table_type == "memories":
432
+ del memories
433
+
434
+ # Force garbage collection to return memory to OS
435
+ # This is necessary because Python's memory allocator retains memory after large operations
436
+ # See: https://github.com/sqlalchemy/sqlalchemy/issues/4616
437
+ gc.collect()
438
+
439
+ log_info(f"✅ Migration completed for table {v1_table_name}: {total_migrated} total records migrated")
440
+
441
+
442
+ def get_table_content_in_batches(db: BaseDb, db_schema: str, table_name: str, batch_size: int = 5000):
443
+ """Get table content in batches to avoid memory issues with large tables"""
444
+ try:
445
+ if type(db).__name__ == "MongoDb":
446
+ from agno.db.mongo.mongo import MongoDb
447
+
448
+ db = cast(MongoDb, db)
449
+
450
+ # MongoDB implementation with cursor and batching
451
+ collection = db.database[table_name]
452
+ cursor = collection.find({}).batch_size(batch_size)
453
+
454
+ batch = []
455
+ for doc in cursor:
456
+ # Convert ObjectId to string for compatibility
457
+ if "_id" in doc:
458
+ doc["_id"] = str(doc["_id"])
459
+ batch.append(doc)
460
+
461
+ if len(batch) >= batch_size:
462
+ yield batch
463
+ batch = []
464
+
465
+ # Yield remaining items
466
+ if batch:
467
+ yield batch
468
+ else:
469
+ # SQL database implementations (PostgresDb, MySQLDb, SqliteDb)
470
+ if type(db).__name__ == "PostgresDb":
471
+ from agno.db.postgres.postgres import PostgresDb
472
+
473
+ db = cast(PostgresDb, db)
474
+
475
+ elif type(db).__name__ == "MySQLDb":
476
+ from agno.db.mysql.mysql import MySQLDb
477
+
478
+ db = cast(MySQLDb, db)
479
+
480
+ elif type(db).__name__ == "SqliteDb":
481
+ from agno.db.sqlite.sqlite import SqliteDb
482
+
483
+ db = cast(SqliteDb, db)
484
+
485
+ else:
486
+ raise ValueError(f"Invalid database type: {type(db).__name__}")
487
+
488
+ offset = 0
489
+ while True:
490
+ # Create a new session for each batch to avoid transaction conflicts
491
+ with db.Session() as sess:
492
+ # Handle empty schema by omitting the schema prefix (needed for SQLite)
493
+ if db_schema and db_schema.strip():
494
+ sql_query = f"SELECT * FROM {db_schema}.{table_name} LIMIT {batch_size} OFFSET {offset}"
495
+ else:
496
+ sql_query = f"SELECT * FROM {table_name} LIMIT {batch_size} OFFSET {offset}"
497
+
498
+ result = sess.execute(text(sql_query))
499
+ batch = [row._asdict() for row in result]
500
+
501
+ if not batch:
502
+ break
503
+
504
+ yield batch
505
+ offset += batch_size
506
+
507
+ # If batch is smaller than batch_size, we've reached the end
508
+ if len(batch) < batch_size:
509
+ break
510
+
511
+ except Exception as e:
512
+ log_error(f"Error getting batched content from table/collection {table_name}: {e}")
513
+ return
514
+
515
+
516
+ def get_all_table_content(db, db_schema: str, table_name: str) -> list[dict[str, Any]]:
517
+ """Get all content from the given table/collection (legacy method kept for backward compatibility)
518
+
519
+ WARNING: This method loads all data into memory and should not be used for large tables.
520
+ Use get_table_content_in_batches() for large datasets.
521
+ """
522
+ log_warning(
523
+ f"Loading entire table {table_name} into memory. Consider using get_table_content_in_batches() for large tables, or if you experience any complication."
524
+ )
525
+
526
+ all_content = []
527
+ for batch in get_table_content_in_batches(db, db_schema, table_name):
528
+ all_content.extend(batch)
529
+ return all_content
530
+
531
+
532
+ def parse_agent_sessions(v1_content: List[Dict[str, Any]]) -> List[AgentSession]:
533
+ """Parse v1 Agent sessions into v2 Agent sessions and Memories"""
534
+ sessions_v2 = []
535
+
536
+ for item in v1_content:
537
+ session = {
538
+ "agent_id": item.get("agent_id"),
539
+ "agent_data": item.get("agent_data"),
540
+ "session_id": item.get("session_id"),
541
+ "user_id": item.get("user_id"),
542
+ "session_data": convert_session_data_comprehensively(item.get("session_data")),
543
+ "metadata": convert_any_metrics_in_data(item.get("extra_data")),
544
+ "runs": convert_any_metrics_in_data(safe_get_runs_from_memory(item.get("memory"))),
545
+ "created_at": item.get("created_at"),
546
+ "updated_at": item.get("updated_at"),
547
+ }
548
+
549
+ try:
550
+ agent_session = AgentSession.from_dict(session)
551
+ except Exception as e:
552
+ log_error(f"Error parsing agent session: {e}. This is the complete session that failed: {session}")
553
+ continue
554
+
555
+ if agent_session is not None:
556
+ sessions_v2.append(agent_session)
557
+
558
+ return sessions_v2
559
+
560
+
561
+ def parse_team_sessions(v1_content: List[Dict[str, Any]]) -> List[TeamSession]:
562
+ """Parse v1 Team sessions into v2 Team sessions and Memories"""
563
+ sessions_v2 = []
564
+
565
+ for item in v1_content:
566
+ session = {
567
+ "team_id": item.get("team_id"),
568
+ "team_data": item.get("team_data"),
569
+ "session_id": item.get("session_id"),
570
+ "user_id": item.get("user_id"),
571
+ "session_data": convert_session_data_comprehensively(item.get("session_data")),
572
+ "metadata": convert_any_metrics_in_data(item.get("extra_data")),
573
+ "runs": convert_any_metrics_in_data(safe_get_runs_from_memory(item.get("memory"))),
574
+ "created_at": item.get("created_at"),
575
+ "updated_at": item.get("updated_at"),
576
+ }
577
+ try:
578
+ team_session = TeamSession.from_dict(session)
579
+ except Exception as e:
580
+ log_error(f"Error parsing team session: {e}. This is the complete session that failed: {session}")
581
+ continue
582
+
583
+ if team_session is not None:
584
+ sessions_v2.append(team_session)
585
+
586
+ return sessions_v2
587
+
588
+
589
+ def parse_workflow_sessions(v1_content: List[Dict[str, Any]]) -> List[WorkflowSession]:
590
+ """Parse v1 Workflow sessions into v2 Workflow sessions"""
591
+ sessions_v2 = []
592
+
593
+ for item in v1_content:
594
+ session = {
595
+ "workflow_id": item.get("workflow_id"),
596
+ "workflow_data": item.get("workflow_data"),
597
+ "session_id": item.get("session_id"),
598
+ "user_id": item.get("user_id"),
599
+ "session_data": convert_session_data_comprehensively(item.get("session_data")),
600
+ "metadata": convert_any_metrics_in_data(item.get("extra_data")),
601
+ "created_at": item.get("created_at"),
602
+ "updated_at": item.get("updated_at"),
603
+ # Workflow v2 specific fields
604
+ "workflow_name": item.get("workflow_name"),
605
+ "runs": convert_any_metrics_in_data(item.get("runs")),
606
+ }
607
+ try:
608
+ workflow_session = WorkflowSession.from_dict(session)
609
+ except Exception as e:
610
+ log_error(f"Error parsing workflow session: {e}. This is the complete session that failed: {session}")
611
+ continue
612
+
613
+ if workflow_session is not None:
614
+ sessions_v2.append(workflow_session)
615
+
616
+ return sessions_v2
617
+
618
+
619
+ def parse_memories(v1_content: List[Dict[str, Any]]) -> List[UserMemory]:
620
+ """Parse v1 Memories into v2 Memories"""
621
+ memories_v2 = []
622
+
623
+ for item in v1_content:
624
+ memory = {
625
+ "memory_id": item.get("memory_id"),
626
+ "memory": item.get("memory"),
627
+ "input": item.get("input"),
628
+ "updated_at": item.get("updated_at"),
629
+ "agent_id": item.get("agent_id"),
630
+ "team_id": item.get("team_id"),
631
+ "user_id": item.get("user_id"),
632
+ }
633
+ memories_v2.append(UserMemory.from_dict(memory))
634
+
635
+ return memories_v2