agno 1.8.0__py3-none-any.whl → 2.0.0a1__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 (583) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +2781 -4126
  4. agno/api/agent.py +9 -65
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +6 -17
  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 +9 -64
  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 +1749 -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 +1438 -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 +888 -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 +1051 -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 +1417 -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 +298 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1720 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +281 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1371 -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 +1722 -0
  67. agno/db/singlestore/utils.py +327 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1680 -0
  71. agno/db/sqlite/utils.py +269 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +142 -43
  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 +10 -10
  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 +1515 -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 +68 -15
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/knowledge/reader/gcs_reader.py +67 -0
  118. agno/{document → knowledge}/reader/json_reader.py +30 -9
  119. agno/{document → knowledge}/reader/markdown_reader.py +36 -9
  120. agno/{document → knowledge}/reader/pdf_reader.py +79 -21
  121. agno/knowledge/reader/reader_factory.py +275 -0
  122. agno/knowledge/reader/s3_reader.py +171 -0
  123. agno/{document → knowledge}/reader/text_reader.py +31 -10
  124. agno/knowledge/reader/url_reader.py +84 -0
  125. agno/knowledge/reader/web_search_reader.py +389 -0
  126. agno/{document → knowledge}/reader/website_reader.py +37 -10
  127. agno/knowledge/reader/wikipedia_reader.py +59 -0
  128. agno/knowledge/reader/youtube_reader.py +78 -0
  129. agno/knowledge/remote_content/remote_content.py +88 -0
  130. agno/{reranker → knowledge/reranker}/base.py +1 -1
  131. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  132. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  133. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  134. agno/knowledge/types.py +30 -0
  135. agno/knowledge/utils.py +169 -0
  136. agno/media.py +2 -2
  137. agno/memory/__init__.py +2 -10
  138. agno/memory/manager.py +1003 -148
  139. agno/models/aimlapi/__init__.py +2 -2
  140. agno/models/aimlapi/aimlapi.py +6 -6
  141. agno/models/anthropic/claude.py +129 -82
  142. agno/models/aws/bedrock.py +107 -175
  143. agno/models/aws/claude.py +64 -18
  144. agno/models/azure/ai_foundry.py +73 -23
  145. agno/models/base.py +347 -287
  146. agno/models/cerebras/cerebras.py +84 -27
  147. agno/models/cohere/chat.py +106 -98
  148. agno/models/dashscope/dashscope.py +14 -5
  149. agno/models/google/gemini.py +123 -53
  150. agno/models/groq/groq.py +97 -35
  151. agno/models/huggingface/huggingface.py +92 -27
  152. agno/models/ibm/watsonx.py +72 -13
  153. agno/models/litellm/chat.py +85 -13
  154. agno/models/message.py +38 -144
  155. agno/models/meta/llama.py +85 -49
  156. agno/models/metrics.py +120 -0
  157. agno/models/mistral/mistral.py +90 -21
  158. agno/models/ollama/__init__.py +0 -2
  159. agno/models/ollama/chat.py +84 -46
  160. agno/models/openai/chat.py +135 -27
  161. agno/models/openai/responses.py +233 -115
  162. agno/models/perplexity/perplexity.py +26 -2
  163. agno/models/portkey/portkey.py +0 -7
  164. agno/models/response.py +14 -8
  165. agno/models/utils.py +20 -0
  166. agno/models/vercel/__init__.py +2 -2
  167. agno/models/vercel/v0.py +1 -1
  168. agno/models/vllm/__init__.py +2 -2
  169. agno/models/vllm/vllm.py +3 -3
  170. agno/models/xai/xai.py +10 -10
  171. agno/os/__init__.py +3 -0
  172. agno/os/app.py +393 -0
  173. agno/os/auth.py +47 -0
  174. agno/os/config.py +103 -0
  175. agno/os/interfaces/agui/__init__.py +3 -0
  176. agno/os/interfaces/agui/agui.py +31 -0
  177. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  178. agno/{app → os/interfaces}/agui/utils.py +65 -28
  179. agno/os/interfaces/base.py +21 -0
  180. agno/os/interfaces/slack/__init__.py +3 -0
  181. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  182. agno/os/interfaces/slack/slack.py +33 -0
  183. agno/os/interfaces/whatsapp/__init__.py +3 -0
  184. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  185. agno/os/interfaces/whatsapp/whatsapp.py +30 -0
  186. agno/os/router.py +843 -0
  187. agno/os/routers/__init__.py +3 -0
  188. agno/os/routers/evals/__init__.py +3 -0
  189. agno/os/routers/evals/evals.py +204 -0
  190. agno/os/routers/evals/schemas.py +142 -0
  191. agno/os/routers/evals/utils.py +161 -0
  192. agno/os/routers/knowledge/__init__.py +3 -0
  193. agno/os/routers/knowledge/knowledge.py +413 -0
  194. agno/os/routers/knowledge/schemas.py +118 -0
  195. agno/os/routers/memory/__init__.py +3 -0
  196. agno/os/routers/memory/memory.py +179 -0
  197. agno/os/routers/memory/schemas.py +58 -0
  198. agno/os/routers/metrics/__init__.py +3 -0
  199. agno/os/routers/metrics/metrics.py +58 -0
  200. agno/os/routers/metrics/schemas.py +47 -0
  201. agno/os/routers/session/__init__.py +3 -0
  202. agno/os/routers/session/session.py +163 -0
  203. agno/os/schema.py +892 -0
  204. agno/{app/playground → os}/settings.py +8 -15
  205. agno/os/utils.py +270 -0
  206. agno/reasoning/azure_ai_foundry.py +4 -4
  207. agno/reasoning/deepseek.py +4 -4
  208. agno/reasoning/default.py +6 -11
  209. agno/reasoning/groq.py +4 -4
  210. agno/reasoning/helpers.py +4 -6
  211. agno/reasoning/ollama.py +4 -4
  212. agno/reasoning/openai.py +4 -4
  213. agno/run/{response.py → agent.py} +144 -72
  214. agno/run/base.py +44 -58
  215. agno/run/cancel.py +83 -0
  216. agno/run/team.py +133 -77
  217. agno/run/workflow.py +537 -12
  218. agno/session/__init__.py +10 -0
  219. agno/session/agent.py +244 -0
  220. agno/session/summary.py +225 -0
  221. agno/session/team.py +262 -0
  222. agno/{storage/session/v2 → session}/workflow.py +47 -24
  223. agno/team/__init__.py +15 -16
  224. agno/team/team.py +2967 -4243
  225. agno/tools/agentql.py +14 -5
  226. agno/tools/airflow.py +9 -4
  227. agno/tools/api.py +7 -3
  228. agno/tools/apify.py +2 -46
  229. agno/tools/arxiv.py +8 -3
  230. agno/tools/aws_lambda.py +7 -5
  231. agno/tools/aws_ses.py +7 -1
  232. agno/tools/baidusearch.py +4 -1
  233. agno/tools/bitbucket.py +4 -4
  234. agno/tools/brandfetch.py +14 -11
  235. agno/tools/bravesearch.py +4 -1
  236. agno/tools/brightdata.py +42 -22
  237. agno/tools/browserbase.py +13 -4
  238. agno/tools/calcom.py +12 -10
  239. agno/tools/calculator.py +10 -27
  240. agno/tools/cartesia.py +18 -13
  241. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  242. agno/tools/confluence.py +71 -18
  243. agno/tools/crawl4ai.py +7 -1
  244. agno/tools/csv_toolkit.py +9 -8
  245. agno/tools/dalle.py +18 -11
  246. agno/tools/daytona.py +13 -16
  247. agno/tools/decorator.py +6 -3
  248. agno/tools/desi_vocal.py +16 -7
  249. agno/tools/discord.py +11 -8
  250. agno/tools/docker.py +30 -42
  251. agno/tools/duckdb.py +34 -53
  252. agno/tools/duckduckgo.py +8 -7
  253. agno/tools/e2b.py +62 -62
  254. agno/tools/eleven_labs.py +35 -28
  255. agno/tools/email.py +4 -1
  256. agno/tools/evm.py +7 -1
  257. agno/tools/exa.py +19 -14
  258. agno/tools/fal.py +29 -29
  259. agno/tools/file.py +9 -8
  260. agno/tools/financial_datasets.py +25 -44
  261. agno/tools/firecrawl.py +22 -22
  262. agno/tools/function.py +68 -17
  263. agno/tools/giphy.py +22 -10
  264. agno/tools/github.py +48 -126
  265. agno/tools/gmail.py +46 -62
  266. agno/tools/google_bigquery.py +7 -6
  267. agno/tools/google_maps.py +11 -26
  268. agno/tools/googlesearch.py +7 -2
  269. agno/tools/googlesheets.py +21 -17
  270. agno/tools/hackernews.py +9 -5
  271. agno/tools/jina.py +5 -4
  272. agno/tools/jira.py +18 -9
  273. agno/tools/knowledge.py +31 -32
  274. agno/tools/linear.py +18 -33
  275. agno/tools/linkup.py +5 -1
  276. agno/tools/local_file_system.py +8 -5
  277. agno/tools/lumalab.py +31 -19
  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 +32 -14
  282. agno/tools/models/gemini.py +58 -31
  283. agno/tools/models/groq.py +29 -20
  284. agno/tools/models/nebius.py +27 -11
  285. agno/tools/models_labs.py +39 -15
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +134 -0
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +57 -26
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +62 -46
  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 +54 -41
  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 +95 -118
  335. agno/utils/knowledge.py +29 -0
  336. agno/utils/location.py +2 -2
  337. agno/utils/log.py +2 -2
  338. agno/utils/mcp.py +11 -5
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/claude.py +6 -4
  342. agno/utils/models/mistral.py +8 -7
  343. agno/utils/models/schema_utils.py +3 -3
  344. agno/utils/pprint.py +33 -32
  345. agno/utils/print_response/agent.py +779 -0
  346. agno/utils/print_response/team.py +1565 -0
  347. agno/utils/print_response/workflow.py +1451 -0
  348. agno/utils/prompts.py +14 -14
  349. agno/utils/reasoning.py +87 -0
  350. agno/utils/response.py +42 -42
  351. agno/utils/string.py +8 -22
  352. agno/utils/team.py +50 -0
  353. agno/utils/timer.py +2 -2
  354. agno/vectordb/base.py +33 -21
  355. agno/vectordb/cassandra/cassandra.py +287 -23
  356. agno/vectordb/chroma/chromadb.py +482 -59
  357. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  358. agno/vectordb/couchbase/couchbase.py +309 -29
  359. agno/vectordb/lancedb/lance_db.py +360 -21
  360. agno/vectordb/langchaindb/__init__.py +5 -0
  361. agno/vectordb/langchaindb/langchaindb.py +145 -0
  362. agno/vectordb/lightrag/__init__.py +5 -0
  363. agno/vectordb/lightrag/lightrag.py +374 -0
  364. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  365. agno/vectordb/milvus/milvus.py +242 -32
  366. agno/vectordb/mongodb/mongodb.py +200 -24
  367. agno/vectordb/pgvector/pgvector.py +319 -37
  368. agno/vectordb/pineconedb/pineconedb.py +221 -27
  369. agno/vectordb/qdrant/qdrant.py +356 -14
  370. agno/vectordb/singlestore/singlestore.py +286 -29
  371. agno/vectordb/surrealdb/surrealdb.py +187 -7
  372. agno/vectordb/upstashdb/upstashdb.py +342 -26
  373. agno/vectordb/weaviate/weaviate.py +227 -165
  374. agno/workflow/__init__.py +17 -13
  375. agno/workflow/{v2/condition.py → condition.py} +135 -32
  376. agno/workflow/{v2/loop.py → loop.py} +115 -28
  377. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  378. agno/workflow/{v2/router.py → router.py} +133 -32
  379. agno/workflow/{v2/step.py → step.py} +200 -42
  380. agno/workflow/{v2/steps.py → steps.py} +147 -66
  381. agno/workflow/types.py +482 -0
  382. agno/workflow/workflow.py +2394 -696
  383. agno-2.0.0a1.dist-info/METADATA +355 -0
  384. agno-2.0.0a1.dist-info/RECORD +514 -0
  385. agno/agent/metrics.py +0 -107
  386. agno/api/app.py +0 -35
  387. agno/api/playground.py +0 -92
  388. agno/api/schemas/app.py +0 -12
  389. agno/api/schemas/playground.py +0 -22
  390. agno/api/schemas/user.py +0 -35
  391. agno/api/schemas/workspace.py +0 -46
  392. agno/api/user.py +0 -160
  393. agno/api/workflows.py +0 -33
  394. agno/api/workspace.py +0 -175
  395. agno/app/agui/__init__.py +0 -3
  396. agno/app/agui/app.py +0 -17
  397. agno/app/agui/sync_router.py +0 -120
  398. agno/app/base.py +0 -186
  399. agno/app/discord/__init__.py +0 -3
  400. agno/app/fastapi/__init__.py +0 -3
  401. agno/app/fastapi/app.py +0 -107
  402. agno/app/fastapi/async_router.py +0 -457
  403. agno/app/fastapi/sync_router.py +0 -448
  404. agno/app/playground/app.py +0 -228
  405. agno/app/playground/async_router.py +0 -1050
  406. agno/app/playground/deploy.py +0 -249
  407. agno/app/playground/operator.py +0 -183
  408. agno/app/playground/schemas.py +0 -220
  409. agno/app/playground/serve.py +0 -55
  410. agno/app/playground/sync_router.py +0 -1042
  411. agno/app/playground/utils.py +0 -46
  412. agno/app/settings.py +0 -15
  413. agno/app/slack/__init__.py +0 -3
  414. agno/app/slack/app.py +0 -19
  415. agno/app/slack/sync_router.py +0 -92
  416. agno/app/utils.py +0 -54
  417. agno/app/whatsapp/__init__.py +0 -3
  418. agno/app/whatsapp/app.py +0 -15
  419. agno/app/whatsapp/sync_router.py +0 -197
  420. agno/cli/auth_server.py +0 -249
  421. agno/cli/config.py +0 -274
  422. agno/cli/console.py +0 -88
  423. agno/cli/credentials.py +0 -23
  424. agno/cli/entrypoint.py +0 -571
  425. agno/cli/operator.py +0 -357
  426. agno/cli/settings.py +0 -96
  427. agno/cli/ws/ws_cli.py +0 -817
  428. agno/constants.py +0 -13
  429. agno/document/__init__.py +0 -5
  430. agno/document/chunking/semantic.py +0 -45
  431. agno/document/chunking/strategy.py +0 -31
  432. agno/document/reader/__init__.py +0 -5
  433. agno/document/reader/base.py +0 -47
  434. agno/document/reader/docx_reader.py +0 -60
  435. agno/document/reader/gcs/pdf_reader.py +0 -44
  436. agno/document/reader/s3/pdf_reader.py +0 -59
  437. agno/document/reader/s3/text_reader.py +0 -63
  438. agno/document/reader/url_reader.py +0 -59
  439. agno/document/reader/youtube_reader.py +0 -58
  440. agno/embedder/__init__.py +0 -5
  441. agno/embedder/langdb.py +0 -80
  442. agno/embedder/mistral.py +0 -82
  443. agno/embedder/openai.py +0 -78
  444. agno/file/__init__.py +0 -5
  445. agno/file/file.py +0 -16
  446. agno/file/local/csv.py +0 -32
  447. agno/file/local/txt.py +0 -19
  448. agno/infra/app.py +0 -240
  449. agno/infra/base.py +0 -144
  450. agno/infra/context.py +0 -20
  451. agno/infra/db_app.py +0 -52
  452. agno/infra/resource.py +0 -205
  453. agno/infra/resources.py +0 -55
  454. agno/knowledge/agent.py +0 -698
  455. agno/knowledge/arxiv.py +0 -33
  456. agno/knowledge/combined.py +0 -36
  457. agno/knowledge/csv.py +0 -144
  458. agno/knowledge/csv_url.py +0 -124
  459. agno/knowledge/document.py +0 -223
  460. agno/knowledge/docx.py +0 -137
  461. agno/knowledge/firecrawl.py +0 -34
  462. agno/knowledge/gcs/__init__.py +0 -0
  463. agno/knowledge/gcs/base.py +0 -39
  464. agno/knowledge/gcs/pdf.py +0 -125
  465. agno/knowledge/json.py +0 -137
  466. agno/knowledge/langchain.py +0 -71
  467. agno/knowledge/light_rag.py +0 -273
  468. agno/knowledge/llamaindex.py +0 -66
  469. agno/knowledge/markdown.py +0 -154
  470. agno/knowledge/pdf.py +0 -164
  471. agno/knowledge/pdf_bytes.py +0 -42
  472. agno/knowledge/pdf_url.py +0 -148
  473. agno/knowledge/s3/__init__.py +0 -0
  474. agno/knowledge/s3/base.py +0 -64
  475. agno/knowledge/s3/pdf.py +0 -33
  476. agno/knowledge/s3/text.py +0 -34
  477. agno/knowledge/text.py +0 -141
  478. agno/knowledge/url.py +0 -46
  479. agno/knowledge/website.py +0 -179
  480. agno/knowledge/wikipedia.py +0 -32
  481. agno/knowledge/youtube.py +0 -35
  482. agno/memory/agent.py +0 -423
  483. agno/memory/classifier.py +0 -104
  484. agno/memory/db/__init__.py +0 -5
  485. agno/memory/db/base.py +0 -42
  486. agno/memory/db/mongodb.py +0 -189
  487. agno/memory/db/postgres.py +0 -203
  488. agno/memory/db/sqlite.py +0 -193
  489. agno/memory/memory.py +0 -22
  490. agno/memory/row.py +0 -36
  491. agno/memory/summarizer.py +0 -201
  492. agno/memory/summary.py +0 -19
  493. agno/memory/team.py +0 -415
  494. agno/memory/v2/__init__.py +0 -2
  495. agno/memory/v2/db/__init__.py +0 -1
  496. agno/memory/v2/db/base.py +0 -42
  497. agno/memory/v2/db/firestore.py +0 -339
  498. agno/memory/v2/db/mongodb.py +0 -196
  499. agno/memory/v2/db/postgres.py +0 -214
  500. agno/memory/v2/db/redis.py +0 -187
  501. agno/memory/v2/db/schema.py +0 -54
  502. agno/memory/v2/db/sqlite.py +0 -209
  503. agno/memory/v2/manager.py +0 -437
  504. agno/memory/v2/memory.py +0 -1097
  505. agno/memory/v2/schema.py +0 -55
  506. agno/memory/v2/summarizer.py +0 -215
  507. agno/memory/workflow.py +0 -38
  508. agno/models/ollama/tools.py +0 -430
  509. agno/models/qwen/__init__.py +0 -5
  510. agno/playground/__init__.py +0 -10
  511. agno/playground/deploy.py +0 -3
  512. agno/playground/playground.py +0 -3
  513. agno/playground/serve.py +0 -3
  514. agno/playground/settings.py +0 -3
  515. agno/reranker/__init__.py +0 -0
  516. agno/run/v2/__init__.py +0 -0
  517. agno/run/v2/workflow.py +0 -567
  518. agno/storage/__init__.py +0 -0
  519. agno/storage/agent/__init__.py +0 -0
  520. agno/storage/agent/dynamodb.py +0 -1
  521. agno/storage/agent/json.py +0 -1
  522. agno/storage/agent/mongodb.py +0 -1
  523. agno/storage/agent/postgres.py +0 -1
  524. agno/storage/agent/singlestore.py +0 -1
  525. agno/storage/agent/sqlite.py +0 -1
  526. agno/storage/agent/yaml.py +0 -1
  527. agno/storage/base.py +0 -60
  528. agno/storage/dynamodb.py +0 -673
  529. agno/storage/firestore.py +0 -297
  530. agno/storage/gcs_json.py +0 -261
  531. agno/storage/in_memory.py +0 -234
  532. agno/storage/json.py +0 -237
  533. agno/storage/mongodb.py +0 -328
  534. agno/storage/mysql.py +0 -685
  535. agno/storage/postgres.py +0 -682
  536. agno/storage/redis.py +0 -336
  537. agno/storage/session/__init__.py +0 -16
  538. agno/storage/session/agent.py +0 -64
  539. agno/storage/session/team.py +0 -63
  540. agno/storage/session/v2/__init__.py +0 -5
  541. agno/storage/session/workflow.py +0 -61
  542. agno/storage/singlestore.py +0 -606
  543. agno/storage/sqlite.py +0 -646
  544. agno/storage/workflow/__init__.py +0 -0
  545. agno/storage/workflow/mongodb.py +0 -1
  546. agno/storage/workflow/postgres.py +0 -1
  547. agno/storage/workflow/sqlite.py +0 -1
  548. agno/storage/yaml.py +0 -241
  549. agno/tools/thinking.py +0 -73
  550. agno/utils/defaults.py +0 -57
  551. agno/utils/filesystem.py +0 -39
  552. agno/utils/git.py +0 -52
  553. agno/utils/json_io.py +0 -30
  554. agno/utils/load_env.py +0 -19
  555. agno/utils/py_io.py +0 -19
  556. agno/utils/pyproject.py +0 -18
  557. agno/utils/resource_filter.py +0 -31
  558. agno/workflow/v2/__init__.py +0 -21
  559. agno/workflow/v2/types.py +0 -357
  560. agno/workflow/v2/workflow.py +0 -3312
  561. agno/workspace/__init__.py +0 -0
  562. agno/workspace/config.py +0 -325
  563. agno/workspace/enums.py +0 -6
  564. agno/workspace/helpers.py +0 -52
  565. agno/workspace/operator.py +0 -757
  566. agno/workspace/settings.py +0 -158
  567. agno-1.8.0.dist-info/METADATA +0 -979
  568. agno-1.8.0.dist-info/RECORD +0 -565
  569. agno-1.8.0.dist-info/entry_points.txt +0 -3
  570. /agno/{app → db/migrations}/__init__.py +0 -0
  571. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  572. /agno/{cli → integrations}/__init__.py +0 -0
  573. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  574. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  575. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  576. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  577. /agno/{app → os/interfaces}/slack/security.py +0 -0
  578. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  579. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  580. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  581. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
  582. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
  583. {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
agno/storage/postgres.py DELETED
@@ -1,682 +0,0 @@
1
- import time
2
- from typing import List, Literal, Optional
3
-
4
- from agno.storage.base import Storage
5
- from agno.storage.session import Session
6
- from agno.storage.session.agent import AgentSession
7
- from agno.storage.session.team import TeamSession
8
- from agno.storage.session.v2.workflow import WorkflowSession as WorkflowSessionV2
9
- from agno.storage.session.workflow import WorkflowSession
10
- from agno.utils.log import log_debug, log_info, log_warning, logger
11
-
12
- try:
13
- from sqlalchemy.dialects import postgresql
14
- from sqlalchemy.engine import Engine, create_engine
15
- from sqlalchemy.inspection import inspect
16
- from sqlalchemy.orm import scoped_session, sessionmaker
17
- from sqlalchemy.schema import Column, MetaData, Table
18
- from sqlalchemy.sql.expression import select, text
19
- from sqlalchemy.types import BigInteger, String
20
- except ImportError:
21
- raise ImportError("`sqlalchemy` not installed. Please install it using `pip install sqlalchemy`")
22
-
23
-
24
- class PostgresStorage(Storage):
25
- def __init__(
26
- self,
27
- table_name: str,
28
- schema: Optional[str] = "ai",
29
- db_url: Optional[str] = None,
30
- db_engine: Optional[Engine] = None,
31
- schema_version: int = 1,
32
- auto_upgrade_schema: bool = False,
33
- mode: Optional[Literal["agent", "team", "workflow", "workflow_v2"]] = "agent",
34
- ):
35
- """
36
- This class provides agent storage using a PostgreSQL table.
37
-
38
- The following order is used to determine the database connection:
39
- 1. Use the db_engine if provided
40
- 2. Use the db_url
41
- 3. Raise an error if neither is provided
42
-
43
- Args:
44
- table_name (str): Name of the table to store Agent sessions.
45
- schema (Optional[str]): The schema to use for the table. Defaults to "ai".
46
- db_url (Optional[str]): The database URL to connect to.
47
- db_engine (Optional[Engine]): The SQLAlchemy database engine to use.
48
- schema_version (int): Version of the schema. Defaults to 1.
49
- auto_upgrade_schema (bool): Whether to automatically upgrade the schema.
50
- mode (Optional[Literal["agent", "team", "workflow"]]): The mode of the storage.
51
- Raises:
52
- ValueError: If neither db_url nor db_engine is provided.
53
- """
54
- super().__init__(mode)
55
- _engine: Optional[Engine] = db_engine
56
- if _engine is None and db_url is not None:
57
- _engine = create_engine(db_url)
58
-
59
- if _engine is None:
60
- raise ValueError("Must provide either db_url or db_engine")
61
-
62
- # Database attributes
63
- self.table_name: str = table_name
64
- self.schema: Optional[str] = schema
65
- self.db_url: Optional[str] = db_url
66
- self.db_engine: Engine = _engine
67
- self.metadata: MetaData = MetaData(schema=self.schema)
68
- self.inspector = inspect(self.db_engine)
69
-
70
- # Table schema version
71
- self.schema_version: int = schema_version
72
- # Automatically upgrade schema if True
73
- self.auto_upgrade_schema: bool = auto_upgrade_schema
74
- self._schema_up_to_date: bool = False
75
-
76
- # Database session
77
- self.Session: scoped_session = scoped_session(sessionmaker(bind=self.db_engine))
78
- # Database table for storage
79
- self.table: Table = self.get_table()
80
- log_debug(f"Created PostgresStorage: '{self.schema}.{self.table_name}'")
81
-
82
- @property
83
- def mode(self) -> Literal["agent", "team", "workflow", "workflow_v2"]:
84
- """Get the mode of the storage."""
85
- return super().mode
86
-
87
- @mode.setter
88
- def mode(self, value: Optional[Literal["agent", "team", "workflow", "workflow_v2"]]) -> None:
89
- """Set the mode and refresh the table if mode changes."""
90
- super(PostgresStorage, type(self)).mode.fset(self, value) # type: ignore
91
- if value is not None:
92
- self.table = self.get_table()
93
-
94
- def get_table_v1(self) -> Table:
95
- """
96
- Define the table schema for version 1.
97
-
98
- Returns:
99
- Table: SQLAlchemy Table object representing the schema.
100
- """
101
- # Common columns for both agent and workflow modes
102
- common_columns = [
103
- Column("session_id", String, primary_key=True),
104
- Column("user_id", String, index=True),
105
- Column("memory", postgresql.JSONB),
106
- Column("session_data", postgresql.JSONB),
107
- Column("extra_data", postgresql.JSONB),
108
- Column("created_at", BigInteger, server_default=text("(extract(epoch from now()))::bigint")),
109
- Column("updated_at", BigInteger, server_onupdate=text("(extract(epoch from now()))::bigint")),
110
- ]
111
-
112
- # Mode-specific columns
113
- specific_columns = []
114
- if self.mode == "agent":
115
- specific_columns = [
116
- Column("agent_id", String, index=True),
117
- Column("team_session_id", String, index=True, nullable=True),
118
- Column("agent_data", postgresql.JSONB),
119
- ]
120
- elif self.mode == "team":
121
- specific_columns = [
122
- Column("team_id", String, index=True),
123
- Column("team_session_id", String, index=True, nullable=True),
124
- Column("team_data", postgresql.JSONB),
125
- ]
126
- elif self.mode == "workflow":
127
- specific_columns = [
128
- Column("workflow_id", String, index=True),
129
- Column("workflow_data", postgresql.JSONB),
130
- ]
131
- elif self.mode == "workflow_v2":
132
- specific_columns = [
133
- Column("workflow_id", String, index=True),
134
- Column("workflow_name", String, index=True),
135
- Column("workflow_data", postgresql.JSONB),
136
- Column("runs", postgresql.JSONB),
137
- ]
138
-
139
- # Create table with all columns
140
- table = Table(
141
- self.table_name,
142
- self.metadata,
143
- *common_columns,
144
- *specific_columns,
145
- extend_existing=True,
146
- schema=self.schema, # type: ignore
147
- )
148
-
149
- return table
150
-
151
- def get_table(self) -> Table:
152
- """
153
- Get the table schema based on the schema version.
154
-
155
- Returns:
156
- Table: SQLAlchemy Table object for the current schema version.
157
-
158
- Raises:
159
- ValueError: If an unsupported schema version is specified.
160
- """
161
- if self.schema_version == 1:
162
- return self.get_table_v1()
163
- else:
164
- raise ValueError(f"Unsupported schema version: {self.schema_version}")
165
-
166
- def table_exists(self) -> bool:
167
- """
168
- Check if the table exists in the database.
169
-
170
- Returns:
171
- bool: True if the table exists, False otherwise.
172
- """
173
- try:
174
- # Use a direct SQL query to check if the table exists
175
- with self.Session() as sess:
176
- if self.schema is not None:
177
- exists_query = text(
178
- "SELECT 1 FROM information_schema.tables WHERE table_schema = :schema AND table_name = :table"
179
- )
180
- exists = (
181
- sess.execute(exists_query, {"schema": self.schema, "table": self.table_name}).scalar()
182
- is not None
183
- )
184
- else:
185
- exists_query = text("SELECT 1 FROM information_schema.tables WHERE table_name = :table")
186
- exists = sess.execute(exists_query, {"table": self.table_name}).scalar() is not None
187
-
188
- log_debug(f"Table '{self.table.fullname}' does{' not ' if not exists else ' '}exist")
189
- return exists
190
-
191
- except Exception as e:
192
- logger.error(f"Error checking if table exists: {e}")
193
- return False
194
-
195
- def create(self) -> None:
196
- """
197
- Create the table if it does not exist.
198
- """
199
- self.table = self.get_table()
200
- if not self.table_exists():
201
- try:
202
- with self.Session() as sess, sess.begin():
203
- if self.schema is not None:
204
- log_debug(f"Creating schema: {self.schema}")
205
- sess.execute(text(f"CREATE SCHEMA IF NOT EXISTS {self.schema};"))
206
-
207
- log_debug(f"Creating table: {self.table_name}")
208
-
209
- # First create the table without indexes
210
- table_without_indexes = Table(
211
- self.table_name,
212
- MetaData(schema=self.schema),
213
- *[c.copy() for c in self.table.columns],
214
- schema=self.schema,
215
- )
216
- table_without_indexes.create(self.db_engine, checkfirst=True)
217
-
218
- # Then create each index individually with error handling
219
- for idx in self.table.indexes:
220
- try:
221
- idx_name = idx.name
222
- log_debug(f"Creating index: {idx_name}")
223
-
224
- # Check if index already exists
225
- with self.Session() as sess:
226
- if self.schema:
227
- exists_query = text(
228
- "SELECT 1 FROM pg_indexes WHERE schemaname = :schema AND indexname = :index_name"
229
- )
230
- exists = (
231
- sess.execute(exists_query, {"schema": self.schema, "index_name": idx_name}).scalar()
232
- is not None
233
- )
234
- else:
235
- exists_query = text("SELECT 1 FROM pg_indexes WHERE indexname = :index_name")
236
- exists = sess.execute(exists_query, {"index_name": idx_name}).scalar() is not None
237
-
238
- if not exists:
239
- idx.create(self.db_engine)
240
- else:
241
- log_debug(f"Index {idx_name} already exists, skipping creation")
242
-
243
- except Exception as e:
244
- # Log the error but continue with other indexes
245
- logger.warning(f"Error creating index {idx.name}: {e}")
246
-
247
- except Exception as e:
248
- logger.error(f"Could not create table: '{self.table.fullname}': {e}")
249
- raise
250
-
251
- def read(self, session_id: str, user_id: Optional[str] = None) -> Optional[Session]:
252
- """
253
- Read an Session from the database.
254
-
255
- Args:
256
- session_id (str): ID of the session to read.
257
- user_id (Optional[str]): User ID to filter by. Defaults to None.
258
-
259
- Returns:
260
- Optional[Session]: Session object if found, None otherwise.
261
- """
262
- try:
263
- with self.Session() as sess:
264
- stmt = select(self.table).where(self.table.c.session_id == session_id)
265
- if user_id:
266
- stmt = stmt.where(self.table.c.user_id == user_id)
267
- result = sess.execute(stmt).fetchone()
268
- if self.mode == "agent":
269
- return AgentSession.from_dict(result._mapping) if result is not None else None
270
- elif self.mode == "team":
271
- return TeamSession.from_dict(result._mapping) if result is not None else None
272
- elif self.mode == "workflow":
273
- return WorkflowSession.from_dict(result._mapping) if result is not None else None
274
- elif self.mode == "workflow_v2":
275
- return WorkflowSessionV2.from_dict(result._mapping) if result is not None else None
276
- except Exception as e:
277
- if "does not exist" in str(e):
278
- log_debug(f"Table does not exist: {self.table.name}")
279
- log_debug("Creating table for future transactions")
280
- self.create()
281
- else:
282
- log_debug(f"Exception reading from table: {e}")
283
- return None
284
-
285
- def get_all_session_ids(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[str]:
286
- """
287
- Get all session IDs, optionally filtered by user_id and/or entity_id.
288
-
289
- Args:
290
- user_id (Optional[str]): The ID of the user to filter by.
291
- entity_id (Optional[str]): The ID of the agent / workflow to filter by.
292
-
293
- Returns:
294
- List[str]: List of session IDs matching the criteria.
295
- """
296
- try:
297
- with self.Session() as sess, sess.begin():
298
- # get all session_ids
299
- stmt = select(self.table.c.session_id)
300
- if user_id is not None:
301
- stmt = stmt.where(self.table.c.user_id == user_id)
302
- if entity_id is not None:
303
- if self.mode == "agent":
304
- stmt = stmt.where(self.table.c.agent_id == entity_id)
305
- elif self.mode == "team":
306
- stmt = stmt.where(self.table.c.team_id == entity_id)
307
- elif self.mode == "workflow":
308
- stmt = stmt.where(self.table.c.workflow_id == entity_id)
309
- elif self.mode == "workflow_v2":
310
- stmt = stmt.where(self.table.c.workflow_id == entity_id)
311
- # order by created_at desc
312
- stmt = stmt.order_by(self.table.c.created_at.desc())
313
- # execute query
314
- rows = sess.execute(stmt).fetchall()
315
- return [row[0] for row in rows] if rows is not None else []
316
- except Exception as e:
317
- log_debug(f"Exception reading from table: {e}")
318
- log_debug(f"Table does not exist: {self.table.name}")
319
- log_debug("Creating table for future transactions")
320
- self.create()
321
- return []
322
-
323
- def get_all_sessions(self, user_id: Optional[str] = None, entity_id: Optional[str] = None) -> List[Session]:
324
- """
325
- Get all sessions, optionally filtered by user_id and/or entity_id.
326
-
327
- Args:
328
- user_id (Optional[str]): The ID of the user to filter by.
329
- entity_id (Optional[str]): The ID of the agent / workflow to filter by.
330
-
331
- Returns:
332
- List[Session]: List of Session objects matching the criteria.
333
- """
334
- try:
335
- with self.Session() as sess, sess.begin():
336
- # get all sessions
337
- stmt = select(self.table)
338
- if user_id is not None:
339
- stmt = stmt.where(self.table.c.user_id == user_id)
340
- if entity_id is not None:
341
- if self.mode == "agent":
342
- stmt = stmt.where(self.table.c.agent_id == entity_id)
343
- elif self.mode == "team":
344
- stmt = stmt.where(self.table.c.team_id == entity_id)
345
- elif self.mode == "workflow":
346
- stmt = stmt.where(self.table.c.workflow_id == entity_id)
347
- elif self.mode == "workflow_v2":
348
- stmt = stmt.where(self.table.c.workflow_id == entity_id)
349
- # order by created_at desc
350
- stmt = stmt.order_by(self.table.c.created_at.desc())
351
- # execute query
352
- rows = sess.execute(stmt).fetchall()
353
- if rows is not None:
354
- if self.mode == "agent":
355
- return [AgentSession.from_dict(row._mapping) for row in rows] # type: ignore
356
- elif self.mode == "team":
357
- return [TeamSession.from_dict(row._mapping) for row in rows] # type: ignore
358
- else:
359
- return [WorkflowSession.from_dict(row._mapping) for row in rows] # type: ignore
360
- else:
361
- return []
362
- except Exception as e:
363
- log_debug(f"Exception reading from table: {e}")
364
- log_debug(f"Table does not exist: {self.table.name}")
365
- log_debug("Creating table for future transactions")
366
- self.create()
367
- return []
368
-
369
- def get_recent_sessions(
370
- self,
371
- user_id: Optional[str] = None,
372
- entity_id: Optional[str] = None,
373
- limit: Optional[int] = 2,
374
- ) -> List[Session]:
375
- """Get the last N sessions, ordered by created_at descending.
376
-
377
- Args:
378
- num_history_sessions: Number of most recent sessions to return
379
- user_id: Filter by user ID
380
- entity_id: Filter by entity ID (agent_id, team_id, or workflow_id)
381
-
382
- Returns:
383
- List[Session]: List of most recent sessions
384
- """
385
- try:
386
- with self.Session() as sess, sess.begin():
387
- # Build the base query
388
- stmt = select(self.table)
389
-
390
- # Add filters
391
- if user_id is not None:
392
- stmt = stmt.where(self.table.c.user_id == user_id)
393
- if entity_id is not None:
394
- if self.mode == "agent":
395
- stmt = stmt.where(self.table.c.agent_id == entity_id)
396
- elif self.mode == "team":
397
- stmt = stmt.where(self.table.c.team_id == entity_id)
398
- elif self.mode == "workflow":
399
- stmt = stmt.where(self.table.c.workflow_id == entity_id)
400
- elif self.mode == "workflow_v2":
401
- stmt = stmt.where(self.table.c.workflow_id == entity_id)
402
- # Order by created_at desc and limit results
403
- stmt = stmt.order_by(self.table.c.created_at.desc())
404
- if limit is not None:
405
- stmt = stmt.limit(limit)
406
-
407
- # Execute query
408
- rows = sess.execute(stmt).fetchall()
409
- if rows is not None:
410
- sessions: List[Session] = []
411
- for row in rows:
412
- session: Optional[Session] = None
413
- if self.mode == "agent":
414
- session = AgentSession.from_dict(row._mapping) # type: ignore
415
- elif self.mode == "team":
416
- session = TeamSession.from_dict(row._mapping) # type: ignore
417
- elif self.mode == "workflow":
418
- session = WorkflowSession.from_dict(row._mapping) # type: ignore
419
- elif self.mode == "workflow_v2":
420
- session = WorkflowSessionV2.from_dict(row._mapping) # type: ignore
421
- if session is not None:
422
- sessions.append(session)
423
- return sessions
424
- return []
425
-
426
- except Exception as e:
427
- if "does not exist" in str(e):
428
- log_debug(f"Table does not exist: {self.table.name}")
429
- log_debug("Creating table for future transactions")
430
- self.create()
431
- else:
432
- log_debug(f"Exception reading from table: {e}")
433
- return []
434
-
435
- def upgrade_schema(self) -> None:
436
- """
437
- Upgrade the schema to the latest version.
438
- Currently handles adding the team_session_id column for agent mode.
439
- """
440
- if not self.auto_upgrade_schema:
441
- log_debug("Auto schema upgrade disabled. Skipping upgrade.")
442
- return
443
-
444
- try:
445
- if self.mode == "agent" and self.table_exists():
446
- with self.Session() as sess:
447
- # Check if team_session_id column exists
448
- column_exists_query = text(
449
- """
450
- SELECT 1 FROM information_schema.columns
451
- WHERE table_schema = :schema AND table_name = :table
452
- AND column_name = 'team_session_id'
453
- """
454
- )
455
- column_exists = (
456
- sess.execute(column_exists_query, {"schema": self.schema, "table": self.table_name}).scalar()
457
- is not None
458
- )
459
-
460
- if not column_exists:
461
- log_info(f"Adding 'team_session_id' column to {self.schema}.{self.table_name}")
462
- alter_table_query = text(
463
- f"ALTER TABLE {self.schema}.{self.table_name} ADD COLUMN team_session_id TEXT"
464
- )
465
- sess.execute(alter_table_query)
466
- sess.commit()
467
- self._schema_up_to_date = True
468
- log_info("Schema upgrade completed successfully")
469
- except Exception as e:
470
- logger.error(f"Error during schema upgrade: {e}")
471
- raise
472
-
473
- def upsert(self, session: Session, create_and_retry: bool = True) -> Optional[Session]:
474
- """
475
- Insert or update an Session in the database.
476
-
477
- Args:
478
- session (Session): The session data to upsert.
479
- create_and_retry (bool): Retry upsert if table does not exist.
480
-
481
- Returns:
482
- Optional[Session]: The upserted Session, or None if operation failed.
483
- """
484
- # Perform schema upgrade if auto_upgrade_schema is enabled
485
- if self.auto_upgrade_schema and not self._schema_up_to_date:
486
- self.upgrade_schema()
487
-
488
- try:
489
- with self.Session() as sess, sess.begin():
490
- # Create an insert statement
491
- if self.mode == "agent":
492
- stmt = postgresql.insert(self.table).values(
493
- session_id=session.session_id,
494
- agent_id=session.agent_id, # type: ignore
495
- team_session_id=session.team_session_id, # type: ignore
496
- user_id=session.user_id,
497
- memory=getattr(session, "memory", None),
498
- agent_data=session.agent_data, # type: ignore
499
- session_data=session.session_data,
500
- extra_data=session.extra_data,
501
- )
502
- # Define the upsert if the session_id already exists
503
- # See: https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#postgresql-insert-on-conflict
504
- stmt = stmt.on_conflict_do_update(
505
- index_elements=["session_id"],
506
- set_=dict(
507
- agent_id=session.agent_id, # type: ignore
508
- team_session_id=session.team_session_id, # type: ignore
509
- user_id=session.user_id,
510
- memory=getattr(session, "memory", None),
511
- agent_data=session.agent_data, # type: ignore
512
- session_data=session.session_data,
513
- extra_data=session.extra_data,
514
- updated_at=int(time.time()),
515
- ), # The updated value for each column
516
- )
517
- elif self.mode == "team":
518
- stmt = postgresql.insert(self.table).values(
519
- session_id=session.session_id,
520
- team_id=session.team_id, # type: ignore
521
- user_id=session.user_id,
522
- team_session_id=session.team_session_id, # type: ignore
523
- memory=getattr(session, "memory", None),
524
- team_data=session.team_data, # type: ignore
525
- session_data=session.session_data,
526
- extra_data=session.extra_data,
527
- )
528
- # Define the upsert if the session_id already exists
529
- # See: https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#postgresql-insert-on-conflict
530
- stmt = stmt.on_conflict_do_update(
531
- index_elements=["session_id"],
532
- set_=dict(
533
- team_id=session.team_id, # type: ignore
534
- user_id=session.user_id,
535
- team_session_id=session.team_session_id, # type: ignore
536
- memory=getattr(session, "memory", None),
537
- team_data=session.team_data, # type: ignore
538
- session_data=session.session_data,
539
- extra_data=session.extra_data,
540
- updated_at=int(time.time()),
541
- ), # The updated value for each column
542
- )
543
- elif self.mode == "workflow":
544
- stmt = postgresql.insert(self.table).values(
545
- session_id=session.session_id,
546
- workflow_id=session.workflow_id, # type: ignore
547
- user_id=session.user_id,
548
- memory=getattr(session, "memory", None),
549
- workflow_data=session.workflow_data, # type: ignore
550
- session_data=session.session_data,
551
- extra_data=session.extra_data,
552
- )
553
- # Define the upsert if the session_id already exists
554
- # See: https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#postgresql-insert-on-conflict
555
- stmt = stmt.on_conflict_do_update(
556
- index_elements=["session_id"],
557
- set_=dict(
558
- workflow_id=session.workflow_id, # type: ignore
559
- user_id=session.user_id,
560
- memory=getattr(session, "memory", None),
561
- workflow_data=session.workflow_data, # type: ignore
562
- session_data=session.session_data,
563
- extra_data=session.extra_data,
564
- updated_at=int(time.time()),
565
- ), # The updated value for each column
566
- )
567
- elif self.mode == "workflow_v2":
568
- # Convert session to dict to ensure proper serialization
569
- session_dict = session.to_dict()
570
-
571
- stmt = postgresql.insert(self.table).values(
572
- session_id=session.session_id,
573
- workflow_id=session.workflow_id, # type: ignore
574
- user_id=session.user_id,
575
- runs=session_dict.get("runs"),
576
- workflow_name=session.workflow_name, # type: ignore
577
- workflow_data=session.workflow_data, # type: ignore
578
- session_data=session.session_data,
579
- extra_data=session.extra_data,
580
- )
581
- # Define the upsert if the session_id already exists
582
- # See: https://docs.sqlalchemy.org/en/20/dialects/postgresql.html#postgresql-insert-on-conflict
583
- stmt = stmt.on_conflict_do_update(
584
- index_elements=["session_id"],
585
- set_=dict(
586
- workflow_id=session.workflow_id, # type: ignore
587
- user_id=session.user_id,
588
- runs=session_dict.get("runs"),
589
- workflow_name=session.workflow_name, # type: ignore
590
- workflow_data=session.workflow_data, # type: ignore
591
- session_data=session.session_data,
592
- extra_data=session.extra_data,
593
- updated_at=int(time.time()),
594
- ),
595
- )
596
-
597
- sess.execute(stmt)
598
- except Exception as e:
599
- if create_and_retry and not self.table_exists():
600
- log_debug(f"Table does not exist: {self.table.name}")
601
- log_debug("Creating table and retrying upsert")
602
- self.create()
603
- return self.upsert(session, create_and_retry=False)
604
- else:
605
- log_warning(f"Exception upserting into table: {e}")
606
- log_warning(
607
- "A table upgrade might be required, please review these docs for more information: https://agno.link/upgrade-schema"
608
- )
609
- return None
610
- return self.read(session_id=session.session_id)
611
-
612
- def delete_session(self, session_id: Optional[str] = None):
613
- """
614
- Delete a session from the database.
615
-
616
- Args:
617
- session_id (Optional[str], optional): ID of the session to delete. Defaults to None.
618
-
619
- Raises:
620
- Exception: If an error occurs during deletion.
621
- """
622
- if session_id is None:
623
- logger.warning("No session_id provided for deletion.")
624
- return
625
-
626
- try:
627
- with self.Session() as sess, sess.begin():
628
- # Delete the session with the given session_id
629
- delete_stmt = self.table.delete().where(self.table.c.session_id == session_id)
630
- result = sess.execute(delete_stmt)
631
- if result.rowcount == 0:
632
- log_debug(f"No session found with session_id: {session_id}")
633
- else:
634
- log_debug(f"Successfully deleted session with session_id: {session_id}")
635
- except Exception as e:
636
- logger.error(f"Error deleting session: {e}")
637
-
638
- def drop(self) -> None:
639
- """
640
- Drop the table from the database if it exists.
641
- """
642
- if self.table_exists():
643
- log_debug(f"Deleting table: {self.table_name}")
644
- # Drop with checkfirst=True to avoid errors if the table doesn't exist
645
- self.table.drop(self.db_engine, checkfirst=True)
646
- # Clear metadata to ensure indexes are recreated properly
647
- self.metadata = MetaData(schema=self.schema)
648
- self.table = self.get_table()
649
-
650
- def __deepcopy__(self, memo):
651
- """
652
- Create a deep copy of the PostgresStorage instance, handling unpickleable attributes.
653
-
654
- Args:
655
- memo (dict): A dictionary of objects already copied during the current copying pass.
656
-
657
- Returns:
658
- PostgresStorage: A deep-copied instance of PostgresStorage.
659
- """
660
- from copy import deepcopy
661
-
662
- # Create a new instance without calling __init__
663
- cls = self.__class__
664
- copied_obj = cls.__new__(cls)
665
- memo[id(self)] = copied_obj
666
-
667
- # Deep copy attributes
668
- for k, v in self.__dict__.items():
669
- if k in {"metadata", "table", "inspector"}:
670
- continue
671
- # Reuse db_engine and Session without copying
672
- elif k in {"db_engine", "SqlSession"}:
673
- setattr(copied_obj, k, v)
674
- else:
675
- setattr(copied_obj, k, deepcopy(v, memo))
676
-
677
- # Recreate metadata and table for the copied instance
678
- copied_obj.metadata = MetaData(schema=copied_obj.schema)
679
- copied_obj.inspector = inspect(copied_obj.db_engine)
680
- copied_obj.table = copied_obj.get_table()
681
-
682
- return copied_obj