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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (590) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +19 -27
  3. agno/agent/agent.py +3143 -4170
  4. agno/api/agent.py +11 -67
  5. agno/api/api.py +5 -46
  6. agno/api/evals.py +8 -19
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -41
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +5 -21
  11. agno/api/schemas/evals.py +7 -16
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +5 -21
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +11 -7
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +11 -66
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/db/__init__.py +24 -0
  25. agno/db/base.py +245 -0
  26. agno/db/dynamo/__init__.py +3 -0
  27. agno/db/dynamo/dynamo.py +1743 -0
  28. agno/db/dynamo/schemas.py +278 -0
  29. agno/db/dynamo/utils.py +684 -0
  30. agno/db/firestore/__init__.py +3 -0
  31. agno/db/firestore/firestore.py +1432 -0
  32. agno/db/firestore/schemas.py +130 -0
  33. agno/db/firestore/utils.py +278 -0
  34. agno/db/gcs_json/__init__.py +3 -0
  35. agno/db/gcs_json/gcs_json_db.py +1001 -0
  36. agno/db/gcs_json/utils.py +194 -0
  37. agno/db/in_memory/__init__.py +3 -0
  38. agno/db/in_memory/in_memory_db.py +882 -0
  39. agno/db/in_memory/utils.py +172 -0
  40. agno/db/json/__init__.py +3 -0
  41. agno/db/json/json_db.py +1045 -0
  42. agno/db/json/utils.py +196 -0
  43. agno/db/migrations/v1_to_v2.py +162 -0
  44. agno/db/mongo/__init__.py +3 -0
  45. agno/db/mongo/mongo.py +1416 -0
  46. agno/db/mongo/schemas.py +77 -0
  47. agno/db/mongo/utils.py +204 -0
  48. agno/db/mysql/__init__.py +3 -0
  49. agno/db/mysql/mysql.py +1719 -0
  50. agno/db/mysql/schemas.py +124 -0
  51. agno/db/mysql/utils.py +297 -0
  52. agno/db/postgres/__init__.py +3 -0
  53. agno/db/postgres/postgres.py +1710 -0
  54. agno/db/postgres/schemas.py +124 -0
  55. agno/db/postgres/utils.py +280 -0
  56. agno/db/redis/__init__.py +3 -0
  57. agno/db/redis/redis.py +1367 -0
  58. agno/db/redis/schemas.py +109 -0
  59. agno/db/redis/utils.py +288 -0
  60. agno/db/schemas/__init__.py +3 -0
  61. agno/db/schemas/evals.py +33 -0
  62. agno/db/schemas/knowledge.py +40 -0
  63. agno/db/schemas/memory.py +46 -0
  64. agno/db/singlestore/__init__.py +3 -0
  65. agno/db/singlestore/schemas.py +116 -0
  66. agno/db/singlestore/singlestore.py +1712 -0
  67. agno/db/singlestore/utils.py +326 -0
  68. agno/db/sqlite/__init__.py +3 -0
  69. agno/db/sqlite/schemas.py +119 -0
  70. agno/db/sqlite/sqlite.py +1676 -0
  71. agno/db/sqlite/utils.py +268 -0
  72. agno/db/utils.py +88 -0
  73. agno/eval/__init__.py +14 -0
  74. agno/eval/accuracy.py +154 -48
  75. agno/eval/performance.py +88 -23
  76. agno/eval/reliability.py +73 -20
  77. agno/eval/utils.py +23 -13
  78. agno/integrations/discord/__init__.py +3 -0
  79. agno/{app → integrations}/discord/client.py +15 -11
  80. agno/knowledge/__init__.py +2 -2
  81. agno/{document → knowledge}/chunking/agentic.py +2 -2
  82. agno/{document → knowledge}/chunking/document.py +2 -2
  83. agno/{document → knowledge}/chunking/fixed.py +3 -3
  84. agno/{document → knowledge}/chunking/markdown.py +2 -2
  85. agno/{document → knowledge}/chunking/recursive.py +2 -2
  86. agno/{document → knowledge}/chunking/row.py +2 -2
  87. agno/knowledge/chunking/semantic.py +59 -0
  88. agno/knowledge/chunking/strategy.py +121 -0
  89. agno/knowledge/content.py +74 -0
  90. agno/knowledge/document/__init__.py +5 -0
  91. agno/{document → knowledge/document}/base.py +12 -2
  92. agno/knowledge/embedder/__init__.py +5 -0
  93. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  94. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  95. agno/{embedder → knowledge/embedder}/base.py +6 -0
  96. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  97. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  98. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  99. agno/{embedder → knowledge/embedder}/google.py +74 -1
  100. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  101. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  102. agno/knowledge/embedder/langdb.py +22 -0
  103. agno/knowledge/embedder/mistral.py +139 -0
  104. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  105. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  106. agno/knowledge/embedder/openai.py +223 -0
  107. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  108. agno/{embedder → knowledge/embedder}/together.py +1 -1
  109. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  110. agno/knowledge/knowledge.py +1551 -0
  111. agno/knowledge/reader/__init__.py +7 -0
  112. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  113. agno/knowledge/reader/base.py +88 -0
  114. agno/{document → knowledge}/reader/csv_reader.py +47 -65
  115. agno/knowledge/reader/docx_reader.py +83 -0
  116. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  117. agno/{document → knowledge}/reader/json_reader.py +30 -9
  118. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  119. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  120. agno/knowledge/reader/reader_factory.py +268 -0
  121. agno/knowledge/reader/s3_reader.py +101 -0
  122. agno/{document → knowledge}/reader/text_reader.py +31 -10
  123. agno/knowledge/reader/url_reader.py +128 -0
  124. agno/knowledge/reader/web_search_reader.py +366 -0
  125. agno/{document → knowledge}/reader/website_reader.py +37 -10
  126. agno/knowledge/reader/wikipedia_reader.py +59 -0
  127. agno/knowledge/reader/youtube_reader.py +78 -0
  128. agno/knowledge/remote_content/remote_content.py +88 -0
  129. agno/{reranker → knowledge/reranker}/base.py +1 -1
  130. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  131. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  132. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  133. agno/knowledge/types.py +30 -0
  134. agno/knowledge/utils.py +169 -0
  135. agno/media.py +269 -268
  136. agno/memory/__init__.py +2 -10
  137. agno/memory/manager.py +1003 -148
  138. agno/models/aimlapi/__init__.py +2 -2
  139. agno/models/aimlapi/aimlapi.py +6 -6
  140. agno/models/anthropic/claude.py +131 -131
  141. agno/models/aws/bedrock.py +110 -182
  142. agno/models/aws/claude.py +64 -18
  143. agno/models/azure/ai_foundry.py +73 -23
  144. agno/models/base.py +346 -290
  145. agno/models/cerebras/cerebras.py +84 -27
  146. agno/models/cohere/chat.py +106 -98
  147. agno/models/google/gemini.py +105 -46
  148. agno/models/groq/groq.py +97 -35
  149. agno/models/huggingface/huggingface.py +92 -27
  150. agno/models/ibm/watsonx.py +72 -13
  151. agno/models/litellm/chat.py +85 -13
  152. agno/models/message.py +46 -151
  153. agno/models/meta/llama.py +85 -49
  154. agno/models/metrics.py +120 -0
  155. agno/models/mistral/mistral.py +90 -21
  156. agno/models/ollama/__init__.py +0 -2
  157. agno/models/ollama/chat.py +85 -47
  158. agno/models/openai/chat.py +154 -37
  159. agno/models/openai/responses.py +178 -105
  160. agno/models/perplexity/perplexity.py +26 -2
  161. agno/models/portkey/portkey.py +0 -7
  162. agno/models/response.py +15 -9
  163. agno/models/utils.py +20 -0
  164. agno/models/vercel/__init__.py +2 -2
  165. agno/models/vercel/v0.py +1 -1
  166. agno/models/vllm/__init__.py +2 -2
  167. agno/models/vllm/vllm.py +3 -3
  168. agno/models/xai/xai.py +10 -10
  169. agno/os/__init__.py +3 -0
  170. agno/os/app.py +497 -0
  171. agno/os/auth.py +47 -0
  172. agno/os/config.py +103 -0
  173. agno/os/interfaces/agui/__init__.py +3 -0
  174. agno/os/interfaces/agui/agui.py +31 -0
  175. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  176. agno/{app → os/interfaces}/agui/utils.py +77 -33
  177. agno/os/interfaces/base.py +21 -0
  178. agno/os/interfaces/slack/__init__.py +3 -0
  179. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  180. agno/os/interfaces/slack/slack.py +32 -0
  181. agno/os/interfaces/whatsapp/__init__.py +3 -0
  182. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  183. agno/os/interfaces/whatsapp/whatsapp.py +29 -0
  184. agno/os/mcp.py +235 -0
  185. agno/os/router.py +1400 -0
  186. agno/os/routers/__init__.py +3 -0
  187. agno/os/routers/evals/__init__.py +3 -0
  188. agno/os/routers/evals/evals.py +393 -0
  189. agno/os/routers/evals/schemas.py +142 -0
  190. agno/os/routers/evals/utils.py +161 -0
  191. agno/os/routers/knowledge/__init__.py +3 -0
  192. agno/os/routers/knowledge/knowledge.py +850 -0
  193. agno/os/routers/knowledge/schemas.py +118 -0
  194. agno/os/routers/memory/__init__.py +3 -0
  195. agno/os/routers/memory/memory.py +410 -0
  196. agno/os/routers/memory/schemas.py +58 -0
  197. agno/os/routers/metrics/__init__.py +3 -0
  198. agno/os/routers/metrics/metrics.py +178 -0
  199. agno/os/routers/metrics/schemas.py +47 -0
  200. agno/os/routers/session/__init__.py +3 -0
  201. agno/os/routers/session/session.py +536 -0
  202. agno/os/schema.py +945 -0
  203. agno/{app/playground → os}/settings.py +7 -15
  204. agno/os/utils.py +270 -0
  205. agno/reasoning/azure_ai_foundry.py +4 -4
  206. agno/reasoning/deepseek.py +4 -4
  207. agno/reasoning/default.py +6 -11
  208. agno/reasoning/groq.py +4 -4
  209. agno/reasoning/helpers.py +4 -6
  210. agno/reasoning/ollama.py +4 -4
  211. agno/reasoning/openai.py +4 -4
  212. agno/run/agent.py +633 -0
  213. agno/run/base.py +53 -77
  214. agno/run/cancel.py +81 -0
  215. agno/run/team.py +243 -96
  216. agno/run/workflow.py +550 -12
  217. agno/session/__init__.py +10 -0
  218. agno/session/agent.py +244 -0
  219. agno/session/summary.py +225 -0
  220. agno/session/team.py +262 -0
  221. agno/{storage/session/v2 → session}/workflow.py +47 -24
  222. agno/team/__init__.py +15 -16
  223. agno/team/team.py +3260 -4824
  224. agno/tools/agentql.py +14 -5
  225. agno/tools/airflow.py +9 -4
  226. agno/tools/api.py +7 -3
  227. agno/tools/apify.py +2 -46
  228. agno/tools/arxiv.py +8 -3
  229. agno/tools/aws_lambda.py +7 -5
  230. agno/tools/aws_ses.py +7 -1
  231. agno/tools/baidusearch.py +4 -1
  232. agno/tools/bitbucket.py +4 -4
  233. agno/tools/brandfetch.py +14 -11
  234. agno/tools/bravesearch.py +4 -1
  235. agno/tools/brightdata.py +43 -23
  236. agno/tools/browserbase.py +13 -4
  237. agno/tools/calcom.py +12 -10
  238. agno/tools/calculator.py +10 -27
  239. agno/tools/cartesia.py +20 -17
  240. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  241. agno/tools/confluence.py +8 -8
  242. agno/tools/crawl4ai.py +7 -1
  243. agno/tools/csv_toolkit.py +9 -8
  244. agno/tools/dalle.py +22 -12
  245. agno/tools/daytona.py +13 -16
  246. agno/tools/decorator.py +6 -3
  247. agno/tools/desi_vocal.py +17 -8
  248. agno/tools/discord.py +11 -8
  249. agno/tools/docker.py +30 -42
  250. agno/tools/duckdb.py +34 -53
  251. agno/tools/duckduckgo.py +8 -7
  252. agno/tools/e2b.py +62 -62
  253. agno/tools/eleven_labs.py +36 -29
  254. agno/tools/email.py +4 -1
  255. agno/tools/evm.py +7 -1
  256. agno/tools/exa.py +19 -14
  257. agno/tools/fal.py +30 -30
  258. agno/tools/file.py +9 -8
  259. agno/tools/financial_datasets.py +25 -44
  260. agno/tools/firecrawl.py +22 -22
  261. agno/tools/function.py +127 -18
  262. agno/tools/giphy.py +23 -11
  263. agno/tools/github.py +48 -126
  264. agno/tools/gmail.py +45 -61
  265. agno/tools/google_bigquery.py +7 -6
  266. agno/tools/google_maps.py +11 -26
  267. agno/tools/googlesearch.py +7 -2
  268. agno/tools/googlesheets.py +21 -17
  269. agno/tools/hackernews.py +9 -5
  270. agno/tools/jina.py +5 -4
  271. agno/tools/jira.py +18 -9
  272. agno/tools/knowledge.py +31 -32
  273. agno/tools/linear.py +19 -34
  274. agno/tools/linkup.py +5 -1
  275. agno/tools/local_file_system.py +8 -5
  276. agno/tools/lumalab.py +32 -20
  277. agno/tools/mcp.py +1 -2
  278. agno/tools/mem0.py +18 -12
  279. agno/tools/memori.py +14 -10
  280. agno/tools/mlx_transcribe.py +3 -2
  281. agno/tools/models/azure_openai.py +33 -15
  282. agno/tools/models/gemini.py +59 -32
  283. agno/tools/models/groq.py +30 -23
  284. agno/tools/models/nebius.py +28 -12
  285. agno/tools/models_labs.py +40 -16
  286. agno/tools/moviepy_video.py +7 -6
  287. agno/tools/neo4j.py +10 -8
  288. agno/tools/newspaper.py +7 -2
  289. agno/tools/newspaper4k.py +8 -3
  290. agno/tools/openai.py +58 -32
  291. agno/tools/openbb.py +12 -11
  292. agno/tools/opencv.py +63 -47
  293. agno/tools/openweather.py +14 -12
  294. agno/tools/pandas.py +11 -3
  295. agno/tools/postgres.py +4 -12
  296. agno/tools/pubmed.py +4 -1
  297. agno/tools/python.py +9 -22
  298. agno/tools/reasoning.py +35 -27
  299. agno/tools/reddit.py +11 -26
  300. agno/tools/replicate.py +55 -42
  301. agno/tools/resend.py +4 -1
  302. agno/tools/scrapegraph.py +15 -14
  303. agno/tools/searxng.py +10 -23
  304. agno/tools/serpapi.py +6 -3
  305. agno/tools/serper.py +13 -4
  306. agno/tools/shell.py +9 -2
  307. agno/tools/slack.py +12 -11
  308. agno/tools/sleep.py +3 -2
  309. agno/tools/spider.py +24 -4
  310. agno/tools/sql.py +7 -6
  311. agno/tools/tavily.py +6 -4
  312. agno/tools/telegram.py +12 -4
  313. agno/tools/todoist.py +11 -31
  314. agno/tools/toolkit.py +1 -1
  315. agno/tools/trafilatura.py +22 -6
  316. agno/tools/trello.py +9 -22
  317. agno/tools/twilio.py +10 -3
  318. agno/tools/user_control_flow.py +6 -1
  319. agno/tools/valyu.py +34 -5
  320. agno/tools/visualization.py +19 -28
  321. agno/tools/webbrowser.py +4 -3
  322. agno/tools/webex.py +11 -7
  323. agno/tools/website.py +15 -46
  324. agno/tools/webtools.py +12 -4
  325. agno/tools/whatsapp.py +5 -9
  326. agno/tools/wikipedia.py +20 -13
  327. agno/tools/x.py +14 -13
  328. agno/tools/yfinance.py +13 -40
  329. agno/tools/youtube.py +26 -20
  330. agno/tools/zendesk.py +7 -2
  331. agno/tools/zep.py +10 -7
  332. agno/tools/zoom.py +10 -9
  333. agno/utils/common.py +1 -19
  334. agno/utils/events.py +100 -123
  335. agno/utils/gemini.py +32 -2
  336. agno/utils/knowledge.py +29 -0
  337. agno/utils/log.py +54 -4
  338. agno/utils/mcp.py +68 -10
  339. agno/utils/media.py +39 -0
  340. agno/utils/message.py +12 -1
  341. agno/utils/models/aws_claude.py +1 -1
  342. agno/utils/models/claude.py +47 -4
  343. agno/utils/models/cohere.py +1 -1
  344. agno/utils/models/mistral.py +8 -7
  345. agno/utils/models/schema_utils.py +3 -3
  346. agno/utils/models/watsonx.py +1 -1
  347. agno/utils/openai.py +1 -1
  348. agno/utils/pprint.py +33 -32
  349. agno/utils/print_response/agent.py +779 -0
  350. agno/utils/print_response/team.py +1669 -0
  351. agno/utils/print_response/workflow.py +1451 -0
  352. agno/utils/prompts.py +14 -14
  353. agno/utils/reasoning.py +87 -0
  354. agno/utils/response.py +42 -42
  355. agno/utils/streamlit.py +481 -0
  356. agno/utils/string.py +8 -22
  357. agno/utils/team.py +50 -0
  358. agno/utils/timer.py +2 -2
  359. agno/vectordb/base.py +33 -21
  360. agno/vectordb/cassandra/cassandra.py +287 -23
  361. agno/vectordb/chroma/chromadb.py +482 -59
  362. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  363. agno/vectordb/couchbase/couchbase.py +309 -29
  364. agno/vectordb/lancedb/lance_db.py +360 -21
  365. agno/vectordb/langchaindb/__init__.py +5 -0
  366. agno/vectordb/langchaindb/langchaindb.py +145 -0
  367. agno/vectordb/lightrag/__init__.py +5 -0
  368. agno/vectordb/lightrag/lightrag.py +374 -0
  369. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  370. agno/vectordb/milvus/milvus.py +242 -32
  371. agno/vectordb/mongodb/mongodb.py +200 -24
  372. agno/vectordb/pgvector/pgvector.py +319 -37
  373. agno/vectordb/pineconedb/pineconedb.py +221 -27
  374. agno/vectordb/qdrant/qdrant.py +334 -14
  375. agno/vectordb/singlestore/singlestore.py +286 -29
  376. agno/vectordb/surrealdb/surrealdb.py +187 -7
  377. agno/vectordb/upstashdb/upstashdb.py +342 -26
  378. agno/vectordb/weaviate/weaviate.py +227 -165
  379. agno/workflow/__init__.py +17 -13
  380. agno/workflow/{v2/condition.py → condition.py} +135 -32
  381. agno/workflow/{v2/loop.py → loop.py} +115 -28
  382. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  383. agno/workflow/{v2/router.py → router.py} +133 -32
  384. agno/workflow/{v2/step.py → step.py} +207 -49
  385. agno/workflow/{v2/steps.py → steps.py} +147 -66
  386. agno/workflow/types.py +482 -0
  387. agno/workflow/workflow.py +2410 -696
  388. agno-2.0.0.dist-info/METADATA +494 -0
  389. agno-2.0.0.dist-info/RECORD +515 -0
  390. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  391. agno/agent/metrics.py +0 -107
  392. agno/api/app.py +0 -35
  393. agno/api/playground.py +0 -92
  394. agno/api/schemas/app.py +0 -12
  395. agno/api/schemas/playground.py +0 -22
  396. agno/api/schemas/user.py +0 -35
  397. agno/api/schemas/workspace.py +0 -46
  398. agno/api/user.py +0 -160
  399. agno/api/workflows.py +0 -33
  400. agno/api/workspace.py +0 -175
  401. agno/app/agui/__init__.py +0 -3
  402. agno/app/agui/app.py +0 -17
  403. agno/app/agui/sync_router.py +0 -120
  404. agno/app/base.py +0 -186
  405. agno/app/discord/__init__.py +0 -3
  406. agno/app/fastapi/__init__.py +0 -3
  407. agno/app/fastapi/app.py +0 -107
  408. agno/app/fastapi/async_router.py +0 -457
  409. agno/app/fastapi/sync_router.py +0 -448
  410. agno/app/playground/app.py +0 -228
  411. agno/app/playground/async_router.py +0 -1050
  412. agno/app/playground/deploy.py +0 -249
  413. agno/app/playground/operator.py +0 -183
  414. agno/app/playground/schemas.py +0 -220
  415. agno/app/playground/serve.py +0 -55
  416. agno/app/playground/sync_router.py +0 -1042
  417. agno/app/playground/utils.py +0 -46
  418. agno/app/settings.py +0 -15
  419. agno/app/slack/__init__.py +0 -3
  420. agno/app/slack/app.py +0 -19
  421. agno/app/slack/sync_router.py +0 -92
  422. agno/app/utils.py +0 -54
  423. agno/app/whatsapp/__init__.py +0 -3
  424. agno/app/whatsapp/app.py +0 -15
  425. agno/app/whatsapp/sync_router.py +0 -197
  426. agno/cli/auth_server.py +0 -249
  427. agno/cli/config.py +0 -274
  428. agno/cli/console.py +0 -88
  429. agno/cli/credentials.py +0 -23
  430. agno/cli/entrypoint.py +0 -571
  431. agno/cli/operator.py +0 -357
  432. agno/cli/settings.py +0 -96
  433. agno/cli/ws/ws_cli.py +0 -817
  434. agno/constants.py +0 -13
  435. agno/document/__init__.py +0 -5
  436. agno/document/chunking/semantic.py +0 -45
  437. agno/document/chunking/strategy.py +0 -31
  438. agno/document/reader/__init__.py +0 -5
  439. agno/document/reader/base.py +0 -47
  440. agno/document/reader/docx_reader.py +0 -60
  441. agno/document/reader/gcs/pdf_reader.py +0 -44
  442. agno/document/reader/s3/pdf_reader.py +0 -59
  443. agno/document/reader/s3/text_reader.py +0 -63
  444. agno/document/reader/url_reader.py +0 -59
  445. agno/document/reader/youtube_reader.py +0 -58
  446. agno/embedder/__init__.py +0 -5
  447. agno/embedder/langdb.py +0 -80
  448. agno/embedder/mistral.py +0 -82
  449. agno/embedder/openai.py +0 -78
  450. agno/file/__init__.py +0 -5
  451. agno/file/file.py +0 -16
  452. agno/file/local/csv.py +0 -32
  453. agno/file/local/txt.py +0 -19
  454. agno/infra/app.py +0 -240
  455. agno/infra/base.py +0 -144
  456. agno/infra/context.py +0 -20
  457. agno/infra/db_app.py +0 -52
  458. agno/infra/resource.py +0 -205
  459. agno/infra/resources.py +0 -55
  460. agno/knowledge/agent.py +0 -702
  461. agno/knowledge/arxiv.py +0 -33
  462. agno/knowledge/combined.py +0 -36
  463. agno/knowledge/csv.py +0 -144
  464. agno/knowledge/csv_url.py +0 -124
  465. agno/knowledge/document.py +0 -223
  466. agno/knowledge/docx.py +0 -137
  467. agno/knowledge/firecrawl.py +0 -34
  468. agno/knowledge/gcs/__init__.py +0 -0
  469. agno/knowledge/gcs/base.py +0 -39
  470. agno/knowledge/gcs/pdf.py +0 -125
  471. agno/knowledge/json.py +0 -137
  472. agno/knowledge/langchain.py +0 -71
  473. agno/knowledge/light_rag.py +0 -273
  474. agno/knowledge/llamaindex.py +0 -66
  475. agno/knowledge/markdown.py +0 -154
  476. agno/knowledge/pdf.py +0 -164
  477. agno/knowledge/pdf_bytes.py +0 -42
  478. agno/knowledge/pdf_url.py +0 -148
  479. agno/knowledge/s3/__init__.py +0 -0
  480. agno/knowledge/s3/base.py +0 -64
  481. agno/knowledge/s3/pdf.py +0 -33
  482. agno/knowledge/s3/text.py +0 -34
  483. agno/knowledge/text.py +0 -141
  484. agno/knowledge/url.py +0 -46
  485. agno/knowledge/website.py +0 -179
  486. agno/knowledge/wikipedia.py +0 -32
  487. agno/knowledge/youtube.py +0 -35
  488. agno/memory/agent.py +0 -423
  489. agno/memory/classifier.py +0 -104
  490. agno/memory/db/__init__.py +0 -5
  491. agno/memory/db/base.py +0 -42
  492. agno/memory/db/mongodb.py +0 -189
  493. agno/memory/db/postgres.py +0 -203
  494. agno/memory/db/sqlite.py +0 -193
  495. agno/memory/memory.py +0 -22
  496. agno/memory/row.py +0 -36
  497. agno/memory/summarizer.py +0 -201
  498. agno/memory/summary.py +0 -19
  499. agno/memory/team.py +0 -415
  500. agno/memory/v2/__init__.py +0 -2
  501. agno/memory/v2/db/__init__.py +0 -1
  502. agno/memory/v2/db/base.py +0 -42
  503. agno/memory/v2/db/firestore.py +0 -339
  504. agno/memory/v2/db/mongodb.py +0 -196
  505. agno/memory/v2/db/postgres.py +0 -214
  506. agno/memory/v2/db/redis.py +0 -187
  507. agno/memory/v2/db/schema.py +0 -54
  508. agno/memory/v2/db/sqlite.py +0 -209
  509. agno/memory/v2/manager.py +0 -437
  510. agno/memory/v2/memory.py +0 -1097
  511. agno/memory/v2/schema.py +0 -55
  512. agno/memory/v2/summarizer.py +0 -215
  513. agno/memory/workflow.py +0 -38
  514. agno/models/ollama/tools.py +0 -430
  515. agno/models/qwen/__init__.py +0 -5
  516. agno/playground/__init__.py +0 -10
  517. agno/playground/deploy.py +0 -3
  518. agno/playground/playground.py +0 -3
  519. agno/playground/serve.py +0 -3
  520. agno/playground/settings.py +0 -3
  521. agno/reranker/__init__.py +0 -0
  522. agno/run/response.py +0 -467
  523. agno/run/v2/__init__.py +0 -0
  524. agno/run/v2/workflow.py +0 -567
  525. agno/storage/__init__.py +0 -0
  526. agno/storage/agent/__init__.py +0 -0
  527. agno/storage/agent/dynamodb.py +0 -1
  528. agno/storage/agent/json.py +0 -1
  529. agno/storage/agent/mongodb.py +0 -1
  530. agno/storage/agent/postgres.py +0 -1
  531. agno/storage/agent/singlestore.py +0 -1
  532. agno/storage/agent/sqlite.py +0 -1
  533. agno/storage/agent/yaml.py +0 -1
  534. agno/storage/base.py +0 -60
  535. agno/storage/dynamodb.py +0 -673
  536. agno/storage/firestore.py +0 -297
  537. agno/storage/gcs_json.py +0 -261
  538. agno/storage/in_memory.py +0 -234
  539. agno/storage/json.py +0 -237
  540. agno/storage/mongodb.py +0 -328
  541. agno/storage/mysql.py +0 -685
  542. agno/storage/postgres.py +0 -682
  543. agno/storage/redis.py +0 -336
  544. agno/storage/session/__init__.py +0 -16
  545. agno/storage/session/agent.py +0 -64
  546. agno/storage/session/team.py +0 -63
  547. agno/storage/session/v2/__init__.py +0 -5
  548. agno/storage/session/workflow.py +0 -61
  549. agno/storage/singlestore.py +0 -606
  550. agno/storage/sqlite.py +0 -646
  551. agno/storage/workflow/__init__.py +0 -0
  552. agno/storage/workflow/mongodb.py +0 -1
  553. agno/storage/workflow/postgres.py +0 -1
  554. agno/storage/workflow/sqlite.py +0 -1
  555. agno/storage/yaml.py +0 -241
  556. agno/tools/thinking.py +0 -73
  557. agno/utils/defaults.py +0 -57
  558. agno/utils/filesystem.py +0 -39
  559. agno/utils/git.py +0 -52
  560. agno/utils/json_io.py +0 -30
  561. agno/utils/load_env.py +0 -19
  562. agno/utils/py_io.py +0 -19
  563. agno/utils/pyproject.py +0 -18
  564. agno/utils/resource_filter.py +0 -31
  565. agno/workflow/v2/__init__.py +0 -21
  566. agno/workflow/v2/types.py +0 -357
  567. agno/workflow/v2/workflow.py +0 -3312
  568. agno/workspace/__init__.py +0 -0
  569. agno/workspace/config.py +0 -325
  570. agno/workspace/enums.py +0 -6
  571. agno/workspace/helpers.py +0 -52
  572. agno/workspace/operator.py +0 -757
  573. agno/workspace/settings.py +0 -158
  574. agno-1.8.1.dist-info/METADATA +0 -982
  575. agno-1.8.1.dist-info/RECORD +0 -566
  576. agno-1.8.1.dist-info/entry_points.txt +0 -3
  577. agno-1.8.1.dist-info/licenses/LICENSE +0 -375
  578. /agno/{app → db/migrations}/__init__.py +0 -0
  579. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  580. /agno/{cli → integrations}/__init__.py +0 -0
  581. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  582. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  583. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  584. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  585. /agno/{app → os/interfaces}/slack/security.py +0 -0
  586. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  587. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  588. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  589. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  590. {agno-1.8.1.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
1
1
  import asyncio
2
+ from hashlib import md5
2
3
  from math import sqrt
3
4
  from typing import Any, Dict, List, Optional, Union, cast
4
5
 
5
6
  try:
7
+ from sqlalchemy import update
6
8
  from sqlalchemy.dialects import postgresql
7
9
  from sqlalchemy.engine import Engine, create_engine
8
10
  from sqlalchemy.inspection import inspect
@@ -10,6 +12,7 @@ try:
10
12
  from sqlalchemy.schema import Column, Index, MetaData, Table
11
13
  from sqlalchemy.sql.expression import bindparam, desc, func, select, text
12
14
  from sqlalchemy.types import DateTime, String
15
+
13
16
  except ImportError:
14
17
  raise ImportError("`sqlalchemy` not installed. Please install using `pip install sqlalchemy psycopg`")
15
18
 
@@ -18,11 +21,10 @@ try:
18
21
  except ImportError:
19
22
  raise ImportError("`pgvector` not installed. Please install using `pip install pgvector`")
20
23
 
21
- from agno.document import Document
22
- from agno.embedder import Embedder
23
- from agno.reranker.base import Reranker
24
+ from agno.knowledge.document import Document
25
+ from agno.knowledge.embedder import Embedder
26
+ from agno.knowledge.reranker.base import Reranker
24
27
  from agno.utils.log import log_debug, log_info, logger
25
- from agno.utils.string import safe_content_hash
26
28
  from agno.vectordb.base import VectorDb
27
29
  from agno.vectordb.distance import Distance
28
30
  from agno.vectordb.pgvector.index import HNSW, Ivfflat
@@ -53,6 +55,7 @@ class PgVector(VectorDb):
53
55
  schema_version: int = 1,
54
56
  auto_upgrade_schema: bool = False,
55
57
  reranker: Optional[Reranker] = None,
58
+ use_batch: bool = False,
56
59
  ):
57
60
  """
58
61
  Initialize the PgVector instance.
@@ -93,10 +96,11 @@ class PgVector(VectorDb):
93
96
  self.db_url: Optional[str] = db_url
94
97
  self.db_engine: Engine = db_engine
95
98
  self.metadata: MetaData = MetaData(schema=self.schema)
99
+ self.use_batch: bool = use_batch
96
100
 
97
101
  # Embedder for embedding the document contents
98
102
  if embedder is None:
99
- from agno.embedder.openai import OpenAIEmbedder
103
+ from agno.knowledge.embedder.openai import OpenAIEmbedder
100
104
 
101
105
  embedder = OpenAIEmbedder()
102
106
  log_info("Embedder not provided, using OpenAIEmbedder as default.")
@@ -155,6 +159,7 @@ class PgVector(VectorDb):
155
159
  Column("created_at", DateTime(timezone=True), server_default=func.now()),
156
160
  Column("updated_at", DateTime(timezone=True), onupdate=func.now()),
157
161
  Column("content_hash", String),
162
+ Column("content_id", String),
158
163
  extend_existing=True,
159
164
  )
160
165
 
@@ -162,7 +167,7 @@ class PgVector(VectorDb):
162
167
  Index(f"idx_{self.table_name}_id", table.c.id)
163
168
  Index(f"idx_{self.table_name}_name", table.c.name)
164
169
  Index(f"idx_{self.table_name}_content_hash", table.c.content_hash)
165
-
170
+ Index(f"idx_{self.table_name}_content_id", table.c.content_id)
166
171
  return table
167
172
 
168
173
  def get_table(self) -> Table:
@@ -229,23 +234,6 @@ class PgVector(VectorDb):
229
234
  logger.error(f"Error checking if record exists: {e}")
230
235
  return False
231
236
 
232
- def doc_exists(self, document: Document) -> bool:
233
- """
234
- Check if a document with the same content hash exists in the table.
235
-
236
- Args:
237
- document (Document): The document to check.
238
-
239
- Returns:
240
- bool: True if the document exists, False otherwise.
241
- """
242
- content_hash = safe_content_hash(document.content)
243
- return self._record_exists(self.table.c.content_hash, content_hash)
244
-
245
- async def async_doc_exists(self, document: Document) -> bool:
246
- """Check if document exists asynchronously by running in a thread."""
247
- return await asyncio.to_thread(self.doc_exists, document)
248
-
249
237
  def name_exists(self, name: str) -> bool:
250
238
  """
251
239
  Check if a document with the given name exists in the table.
@@ -274,6 +262,12 @@ class PgVector(VectorDb):
274
262
  """
275
263
  return self._record_exists(self.table.c.id, id)
276
264
 
265
+ def content_hash_exists(self, content_hash: str) -> bool:
266
+ """
267
+ Check if a document with the given content hash exists in the table.
268
+ """
269
+ return self._record_exists(self.table.c.content_hash, content_hash)
270
+
277
271
  def _clean_content(self, content: str) -> str:
278
272
  """
279
273
  Clean the content by replacing null characters.
@@ -288,6 +282,7 @@ class PgVector(VectorDb):
288
282
 
289
283
  def insert(
290
284
  self,
285
+ content_hash: str,
291
286
  documents: List[Document],
292
287
  filters: Optional[Dict[str, Any]] = None,
293
288
  batch_size: int = 100,
@@ -296,6 +291,7 @@ class PgVector(VectorDb):
296
291
  Insert documents into the database.
297
292
 
298
293
  Args:
294
+ content_hash (str): The content hash to insert.
299
295
  documents (List[Document]): List of documents to insert.
300
296
  filters (Optional[Dict[str, Any]]): Filters to apply to the documents.
301
297
  batch_size (int): Number of documents to insert in each batch.
@@ -310,7 +306,7 @@ class PgVector(VectorDb):
310
306
  batch_records = []
311
307
  for doc in batch_docs:
312
308
  try:
313
- batch_records.append(self._get_document_record(doc, filters))
309
+ batch_records.append(self._get_document_record(doc, filters, content_hash))
314
310
  except Exception as e:
315
311
  logger.error(f"Error processing document '{doc.name}': {e}")
316
312
 
@@ -327,9 +323,62 @@ class PgVector(VectorDb):
327
323
  logger.error(f"Error inserting documents: {e}")
328
324
  raise
329
325
 
330
- async def async_insert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
331
- """Insert documents asynchronously by running in a thread."""
332
- await asyncio.to_thread(self.insert, documents, filters)
326
+ async def async_insert(
327
+ self,
328
+ content_hash: str,
329
+ documents: List[Document],
330
+ filters: Optional[Dict[str, Any]] = None,
331
+ batch_size: int = 100,
332
+ ) -> None:
333
+ """Insert documents asynchronously with parallel embedding."""
334
+ try:
335
+ with self.Session() as sess:
336
+ for i in range(0, len(documents), batch_size):
337
+ batch_docs = documents[i : i + batch_size]
338
+ log_debug(f"Processing batch starting at index {i}, size: {len(batch_docs)}")
339
+ try:
340
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in batch_docs]
341
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
342
+
343
+ # Prepare documents for insertion
344
+ batch_records = []
345
+ for doc in batch_docs:
346
+ try:
347
+ cleaned_content = self._clean_content(doc.content)
348
+ record_id = doc.id or content_hash
349
+
350
+ meta_data = doc.meta_data or {}
351
+ if filters:
352
+ meta_data.update(filters)
353
+
354
+ record = {
355
+ "id": record_id,
356
+ "name": doc.name,
357
+ "meta_data": doc.meta_data,
358
+ "filters": filters,
359
+ "content": cleaned_content,
360
+ "embedding": doc.embedding,
361
+ "usage": doc.usage,
362
+ "content_hash": content_hash,
363
+ "content_id": doc.content_id,
364
+ }
365
+ batch_records.append(record)
366
+ except Exception as e:
367
+ logger.error(f"Error processing document '{doc.name}': {e}")
368
+
369
+ # Insert the batch of records
370
+ if batch_records:
371
+ insert_stmt = postgresql.insert(self.table)
372
+ sess.execute(insert_stmt, batch_records)
373
+ sess.commit() # Commit batch independently
374
+ log_info(f"Inserted batch of {len(batch_records)} documents.")
375
+ except Exception as e:
376
+ logger.error(f"Error with batch starting at index {i}: {e}")
377
+ sess.rollback() # Rollback the current batch if there's an error
378
+ raise
379
+ except Exception as e:
380
+ logger.error(f"Error inserting documents: {e}")
381
+ raise
333
382
 
334
383
  def upsert_available(self) -> bool:
335
384
  """
@@ -342,6 +391,27 @@ class PgVector(VectorDb):
342
391
 
343
392
  def upsert(
344
393
  self,
394
+ content_hash: str,
395
+ documents: List[Document],
396
+ filters: Optional[Dict[str, Any]] = None,
397
+ batch_size: int = 100,
398
+ ) -> None:
399
+ """
400
+ Upsert documents by content hash.
401
+ First delete all documents with the same content hash.
402
+ Then upsert the new documents.
403
+ """
404
+ try:
405
+ if self.content_hash_exists(content_hash):
406
+ self._delete_by_content_hash(content_hash)
407
+ self._upsert(content_hash, documents, filters, batch_size)
408
+ except Exception as e:
409
+ logger.error(f"Error upserting documents by content hash: {e}")
410
+ raise
411
+
412
+ def _upsert(
413
+ self,
414
+ content_hash: str,
345
415
  documents: List[Document],
346
416
  filters: Optional[Dict[str, Any]] = None,
347
417
  batch_size: int = 100,
@@ -358,16 +428,22 @@ class PgVector(VectorDb):
358
428
  with self.Session() as sess:
359
429
  for i in range(0, len(documents), batch_size):
360
430
  batch_docs = documents[i : i + batch_size]
361
- log_debug(f"Processing batch starting at index {i}, size: {len(batch_docs)}")
431
+ log_info(f"Processing batch starting at index {i}, size: {len(batch_docs)}")
362
432
  try:
363
433
  # Prepare documents for upserting
364
- batch_records = []
434
+ batch_records_dict: Dict[str, Dict[str, Any]] = {} # Use dict to deduplicate by ID
365
435
  for doc in batch_docs:
366
436
  try:
367
- batch_records.append(self._get_document_record(doc, filters))
437
+ batch_records_dict[doc.id] = self._get_document_record(doc, filters, content_hash) # type: ignore
368
438
  except Exception as e:
369
439
  logger.error(f"Error processing document '{doc.name}': {e}")
370
440
 
441
+ # Convert dict to list for upsert
442
+ batch_records = list(batch_records_dict.values())
443
+ if not batch_records:
444
+ log_info("No valid records to upsert in this batch.")
445
+ continue
446
+
371
447
  # Upsert the batch of records
372
448
  insert_stmt = postgresql.insert(self.table).values(batch_records)
373
449
  upsert_stmt = insert_stmt.on_conflict_do_update(
@@ -380,6 +456,7 @@ class PgVector(VectorDb):
380
456
  "embedding": insert_stmt.excluded.embedding,
381
457
  "usage": insert_stmt.excluded.usage,
382
458
  "content_hash": insert_stmt.excluded.content_hash,
459
+ "content_id": insert_stmt.excluded.content_id,
383
460
  },
384
461
  )
385
462
  sess.execute(upsert_stmt)
@@ -393,30 +470,155 @@ class PgVector(VectorDb):
393
470
  logger.error(f"Error upserting documents: {e}")
394
471
  raise
395
472
 
396
- def _get_document_record(self, doc: Document, filters: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
473
+ def _get_document_record(
474
+ self, doc: Document, filters: Optional[Dict[str, Any]] = None, content_hash: str = ""
475
+ ) -> Dict[str, Any]:
397
476
  doc.embed(embedder=self.embedder)
398
477
  cleaned_content = self._clean_content(doc.content)
399
- content_hash = safe_content_hash(doc.content)
400
- _id = doc.id or content_hash
478
+ record_id = doc.id or content_hash
401
479
 
402
480
  meta_data = doc.meta_data or {}
403
481
  if filters:
404
482
  meta_data.update(filters)
405
483
 
406
484
  return {
407
- "id": _id,
485
+ "id": record_id,
408
486
  "name": doc.name,
409
- "meta_data": meta_data,
487
+ "meta_data": doc.meta_data,
410
488
  "filters": filters,
411
489
  "content": cleaned_content,
412
490
  "embedding": doc.embedding,
413
491
  "usage": doc.usage,
414
492
  "content_hash": content_hash,
493
+ "content_id": doc.content_id,
415
494
  }
416
495
 
417
- async def async_upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
496
+ async def async_upsert(
497
+ self,
498
+ content_hash: str,
499
+ documents: List[Document],
500
+ filters: Optional[Dict[str, Any]] = None,
501
+ batch_size: int = 100,
502
+ ) -> None:
418
503
  """Upsert documents asynchronously by running in a thread."""
419
- await asyncio.to_thread(self.upsert, documents, filters)
504
+ try:
505
+ if self.content_hash_exists(content_hash):
506
+ self._delete_by_content_hash(content_hash)
507
+ await self._async_upsert(content_hash, documents, filters, batch_size)
508
+ except Exception as e:
509
+ logger.error(f"Error upserting documents by content hash: {e}")
510
+ raise
511
+
512
+ async def _async_upsert(
513
+ self,
514
+ content_hash: str,
515
+ documents: List[Document],
516
+ filters: Optional[Dict[str, Any]] = None,
517
+ batch_size: int = 100,
518
+ ) -> None:
519
+ """
520
+ Upsert (insert or update) documents in the database.
521
+
522
+ Args:
523
+ documents (List[Document]): List of documents to upsert.
524
+ filters (Optional[Dict[str, Any]]): Filters to apply to the documents.
525
+ batch_size (int): Number of documents to upsert in each batch.
526
+ """
527
+ try:
528
+ with self.Session() as sess:
529
+ for i in range(0, len(documents), batch_size):
530
+ batch_docs = documents[i : i + batch_size]
531
+ log_info(f"Processing batch starting at index {i}, size: {len(batch_docs)}")
532
+ try:
533
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in batch_docs]
534
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
535
+
536
+ # Prepare documents for upserting
537
+ batch_records_dict = {} # Use dict to deduplicate by ID
538
+ for doc in batch_docs:
539
+ try:
540
+ cleaned_content = self._clean_content(doc.content)
541
+ record_id = md5(cleaned_content.encode()).hexdigest()
542
+
543
+ meta_data = doc.meta_data or {}
544
+ if filters:
545
+ meta_data.update(filters)
546
+
547
+ record = {
548
+ "id": record_id, # use record_id as a reproducible id to avoid duplicates while upsert
549
+ "name": doc.name,
550
+ "meta_data": doc.meta_data,
551
+ "filters": filters,
552
+ "content": cleaned_content,
553
+ "embedding": doc.embedding,
554
+ "usage": doc.usage,
555
+ "content_hash": content_hash,
556
+ "content_id": doc.content_id,
557
+ }
558
+ batch_records_dict[record_id] = record # This deduplicates by ID
559
+ except Exception as e:
560
+ logger.error(f"Error processing document '{doc.name}': {e}")
561
+
562
+ # Convert dict to list for upsert
563
+ batch_records = list(batch_records_dict.values())
564
+ if not batch_records:
565
+ log_info("No valid records to upsert in this batch.")
566
+ continue
567
+
568
+ # Upsert the batch of records
569
+ insert_stmt = postgresql.insert(self.table).values(batch_records)
570
+ upsert_stmt = insert_stmt.on_conflict_do_update(
571
+ index_elements=["id"],
572
+ set_={
573
+ "name": insert_stmt.excluded.name,
574
+ "meta_data": insert_stmt.excluded.meta_data,
575
+ "filters": insert_stmt.excluded.filters,
576
+ "content": insert_stmt.excluded.content,
577
+ "embedding": insert_stmt.excluded.embedding,
578
+ "usage": insert_stmt.excluded.usage,
579
+ "content_hash": insert_stmt.excluded.content_hash,
580
+ "content_id": insert_stmt.excluded.content_id,
581
+ },
582
+ )
583
+ sess.execute(upsert_stmt)
584
+ sess.commit() # Commit batch independently
585
+ log_info(f"Upserted batch of {len(batch_records)} documents.")
586
+ except Exception as e:
587
+ logger.error(f"Error with batch starting at index {i}: {e}")
588
+ sess.rollback() # Rollback the current batch if there's an error
589
+ raise
590
+ except Exception as e:
591
+ logger.error(f"Error upserting documents: {e}")
592
+ raise
593
+
594
+ def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
595
+ """
596
+ Update the metadata for a document.
597
+
598
+ Args:
599
+ id (str): The ID of the document.
600
+ metadata (Dict[str, Any]): The metadata to update.
601
+ """
602
+ try:
603
+ with self.Session() as sess:
604
+ # Merge JSONB instead of overwriting: coalesce(existing, '{}') || :new
605
+ stmt = (
606
+ update(self.table)
607
+ .where(self.table.c.content_id == content_id)
608
+ .values(
609
+ meta_data=func.coalesce(self.table.c.meta_data, text("'{}'::jsonb")).op("||")(
610
+ bindparam("md", metadata, type_=postgresql.JSONB)
611
+ ),
612
+ filters=func.coalesce(self.table.c.filters, text("'{}'::jsonb")).op("||")(
613
+ bindparam("ft", metadata, type_=postgresql.JSONB)
614
+ ),
615
+ )
616
+ )
617
+ sess.execute(stmt)
618
+ sess.commit()
619
+ except Exception as e:
620
+ logger.error(f"Error updating metadata for document {content_id}: {e}")
621
+ raise
420
622
 
421
623
  def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
422
624
  """
@@ -1025,6 +1227,86 @@ class PgVector(VectorDb):
1025
1227
  sess.rollback()
1026
1228
  return False
1027
1229
 
1230
+ def delete_by_id(self, id: str) -> bool:
1231
+ """
1232
+ Delete content by ID.
1233
+ """
1234
+ try:
1235
+ with self.Session() as sess, sess.begin():
1236
+ stmt = self.table.delete().where(self.table.c.id == id)
1237
+ sess.execute(stmt)
1238
+ sess.commit()
1239
+ log_info(f"Deleted records with id '{id}' from table '{self.table.fullname}'.")
1240
+ return True
1241
+ except Exception as e:
1242
+ logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
1243
+ sess.rollback()
1244
+ return False
1245
+
1246
+ def delete_by_name(self, name: str) -> bool:
1247
+ """
1248
+ Delete content by name.
1249
+ """
1250
+ try:
1251
+ with self.Session() as sess, sess.begin():
1252
+ stmt = self.table.delete().where(self.table.c.name == name)
1253
+ sess.execute(stmt)
1254
+ sess.commit()
1255
+ log_info(f"Deleted records with name '{name}' from table '{self.table.fullname}'.")
1256
+ return True
1257
+ except Exception as e:
1258
+ logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
1259
+ sess.rollback()
1260
+ return False
1261
+
1262
+ def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
1263
+ """
1264
+ Delete content by metadata.
1265
+ """
1266
+ try:
1267
+ with self.Session() as sess, sess.begin():
1268
+ stmt = self.table.delete().where(self.table.c.meta_data.contains(metadata))
1269
+ sess.execute(stmt)
1270
+ sess.commit()
1271
+ log_info(f"Deleted records with metadata '{metadata}' from table '{self.table.fullname}'.")
1272
+ return True
1273
+ except Exception as e:
1274
+ logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
1275
+ sess.rollback()
1276
+ return False
1277
+
1278
+ def delete_by_content_id(self, content_id: str) -> bool:
1279
+ """
1280
+ Delete content by content ID.
1281
+ """
1282
+ try:
1283
+ with self.Session() as sess, sess.begin():
1284
+ stmt = self.table.delete().where(self.table.c.content_id == content_id)
1285
+ sess.execute(stmt)
1286
+ sess.commit()
1287
+ log_info(f"Deleted records with content ID '{content_id}' from table '{self.table.fullname}'.")
1288
+ return True
1289
+ except Exception as e:
1290
+ logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
1291
+ sess.rollback()
1292
+ return False
1293
+
1294
+ def _delete_by_content_hash(self, content_hash: str) -> bool:
1295
+ """
1296
+ Delete content by content hash.
1297
+ """
1298
+ try:
1299
+ with self.Session() as sess, sess.begin():
1300
+ stmt = self.table.delete().where(self.table.c.content_hash == content_hash)
1301
+ sess.execute(stmt)
1302
+ sess.commit()
1303
+ log_info(f"Deleted records with content hash '{content_hash}' from table '{self.table.fullname}'.")
1304
+ return True
1305
+ except Exception as e:
1306
+ logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
1307
+ sess.rollback()
1308
+ return False
1309
+
1028
1310
  def __deepcopy__(self, memo):
1029
1311
  """
1030
1312
  Create a deep copy of the PgVector instance, handling unpickleable attributes.