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,3 +1,4 @@
1
+ import asyncio
1
2
  from hashlib import md5
2
3
  from typing import Any, Dict, List, Optional
3
4
 
@@ -10,8 +11,8 @@ try:
10
11
  except ImportError:
11
12
  raise ImportError("`clickhouse-connect` not installed. Use `pip install clickhouse-connect` to install it")
12
13
 
13
- from agno.document import Document
14
- from agno.embedder import Embedder
14
+ from agno.knowledge.document import Document
15
+ from agno.knowledge.embedder import Embedder
15
16
  from agno.utils.log import log_debug, log_info, logger
16
17
  from agno.vectordb.base import VectorDb
17
18
  from agno.vectordb.distance import Distance
@@ -62,7 +63,7 @@ class Clickhouse(VectorDb):
62
63
  # Embedder for embedding the document contents
63
64
  _embedder = embedder
64
65
  if _embedder is None:
65
- from agno.embedder.openai import OpenAIEmbedder
66
+ from agno.knowledge.embedder.openai import OpenAIEmbedder
66
67
 
67
68
  _embedder = OpenAIEmbedder()
68
69
  log_info("Embedder not provided, using OpenAIEmbedder as default.")
@@ -157,6 +158,7 @@ class Clickhouse(VectorDb):
157
158
  meta_data JSON DEFAULT '{{}}',
158
159
  filters JSON DEFAULT '{{}}',
159
160
  content String,
161
+ content_id String,
160
162
  embedding Array(Float32),
161
163
  usage JSON,
162
164
  created_at DateTime('UTC') DEFAULT now(),
@@ -201,6 +203,7 @@ class Clickhouse(VectorDb):
201
203
  meta_data JSON DEFAULT '{{}}',
202
204
  filters JSON DEFAULT '{{}}',
203
205
  content String,
206
+ content_id String,
204
207
  embedding Array(Float32),
205
208
  usage JSON,
206
209
  created_at DateTime('UTC') DEFAULT now(),
@@ -211,37 +214,6 @@ class Clickhouse(VectorDb):
211
214
  parameters=parameters,
212
215
  )
213
216
 
214
- def doc_exists(self, document: Document) -> bool:
215
- """
216
- Validating if the document exists or not
217
-
218
- Args:
219
- document (Document): Document to validate
220
- """
221
- cleaned_content = document.content.replace("\x00", "\ufffd")
222
- parameters = self._get_base_parameters()
223
- parameters["content_hash"] = md5(cleaned_content.encode()).hexdigest()
224
-
225
- result = self.client.query(
226
- "SELECT content_hash FROM {database_name:Identifier}.{table_name:Identifier} WHERE content_hash = {content_hash:String}",
227
- parameters=parameters,
228
- )
229
- return bool(result.result_rows)
230
-
231
- async def async_doc_exists(self, document: Document) -> bool:
232
- """Check if a document exists asynchronously."""
233
- cleaned_content = document.content.replace("\x00", "\ufffd")
234
- async_client = await self._ensure_async_client()
235
-
236
- parameters = self._get_base_parameters()
237
- parameters["content_hash"] = md5(cleaned_content.encode()).hexdigest()
238
-
239
- result = await async_client.query(
240
- "SELECT content_hash FROM {database_name:Identifier}.{table_name:Identifier} WHERE content_hash = {content_hash:String}",
241
- parameters=parameters,
242
- )
243
- return bool(result.result_rows)
244
-
245
217
  def name_exists(self, name: str) -> bool:
246
218
  """
247
219
  Validate if a row with this name exists or not
@@ -289,6 +261,7 @@ class Clickhouse(VectorDb):
289
261
 
290
262
  def insert(
291
263
  self,
264
+ content_hash: str,
292
265
  documents: List[Document],
293
266
  filters: Optional[Dict[str, Any]] = None,
294
267
  ) -> None:
@@ -296,8 +269,7 @@ class Clickhouse(VectorDb):
296
269
  for document in documents:
297
270
  document.embed(embedder=self.embedder)
298
271
  cleaned_content = document.content.replace("\x00", "\ufffd")
299
- content_hash = md5(cleaned_content.encode()).hexdigest()
300
- _id = document.id or content_hash
272
+ _id = md5(cleaned_content.encode()).hexdigest()
301
273
 
302
274
  row: List[Any] = [
303
275
  _id,
@@ -305,6 +277,7 @@ class Clickhouse(VectorDb):
305
277
  document.meta_data,
306
278
  filters,
307
279
  cleaned_content,
280
+ document.content_id,
308
281
  document.embedding,
309
282
  document.usage,
310
283
  content_hash,
@@ -320,6 +293,7 @@ class Clickhouse(VectorDb):
320
293
  "meta_data",
321
294
  "filters",
322
295
  "content",
296
+ "content_id",
323
297
  "embedding",
324
298
  "usage",
325
299
  "content_hash",
@@ -327,16 +301,19 @@ class Clickhouse(VectorDb):
327
301
  )
328
302
  log_debug(f"Inserted {len(documents)} documents")
329
303
 
330
- async def async_insert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
304
+ async def async_insert(
305
+ self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
306
+ ) -> None:
331
307
  """Insert documents asynchronously."""
332
308
  rows: List[List[Any]] = []
333
309
  async_client = await self._ensure_async_client()
334
310
 
311
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
312
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
313
+
335
314
  for document in documents:
336
- document.embed(embedder=self.embedder)
337
315
  cleaned_content = document.content.replace("\x00", "\ufffd")
338
- content_hash = md5(cleaned_content.encode()).hexdigest()
339
- _id = document.id or content_hash
316
+ _id = md5(cleaned_content.encode()).hexdigest()
340
317
 
341
318
  row: List[Any] = [
342
319
  _id,
@@ -344,6 +321,7 @@ class Clickhouse(VectorDb):
344
321
  document.meta_data,
345
322
  filters,
346
323
  cleaned_content,
324
+ document.content_id,
347
325
  document.embedding,
348
326
  document.usage,
349
327
  content_hash,
@@ -359,6 +337,7 @@ class Clickhouse(VectorDb):
359
337
  "meta_data",
360
338
  "filters",
361
339
  "content",
340
+ "content_id",
362
341
  "embedding",
363
342
  "usage",
364
343
  "content_hash",
@@ -371,6 +350,20 @@ class Clickhouse(VectorDb):
371
350
 
372
351
  def upsert(
373
352
  self,
353
+ content_hash: str,
354
+ documents: List[Document],
355
+ filters: Optional[Dict[str, Any]] = None,
356
+ ) -> None:
357
+ """
358
+ Upsert documents into the database.
359
+ """
360
+ if self.content_hash_exists(content_hash):
361
+ self._delete_by_content_hash(content_hash)
362
+ self.insert(content_hash=content_hash, documents=documents, filters=filters)
363
+
364
+ def _upsert(
365
+ self,
366
+ content_hash: str,
374
367
  documents: List[Document],
375
368
  filters: Optional[Dict[str, Any]] = None,
376
369
  ) -> None:
@@ -384,7 +377,7 @@ class Clickhouse(VectorDb):
384
377
  """
385
378
  # We are using ReplacingMergeTree engine in our table, so we need to insert the documents,
386
379
  # then call SELECT with FINAL
387
- self.insert(documents=documents, filters=filters)
380
+ self.insert(content_hash=content_hash, documents=documents, filters=filters)
388
381
 
389
382
  parameters = self._get_base_parameters()
390
383
  self.client.query(
@@ -392,11 +385,21 @@ class Clickhouse(VectorDb):
392
385
  parameters=parameters,
393
386
  )
394
387
 
395
- async def async_upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
388
+ async def async_upsert(
389
+ self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
390
+ ) -> None:
391
+ """Upsert documents asynchronously."""
392
+ if self.content_hash_exists(content_hash):
393
+ self._delete_by_content_hash(content_hash)
394
+ await self._async_upsert(content_hash=content_hash, documents=documents, filters=filters)
395
+
396
+ async def _async_upsert(
397
+ self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
398
+ ) -> None:
396
399
  """Upsert documents asynchronously."""
397
400
  # We are using ReplacingMergeTree engine in our table, so we need to insert the documents,
398
401
  # then call SELECT with FINAL
399
- await self.async_insert(documents=documents, filters=filters)
402
+ await self.async_insert(content_hash=content_hash, documents=documents, filters=filters)
400
403
 
401
404
  parameters = self._get_base_parameters()
402
405
  await self.async_client.query( # type: ignore
@@ -412,13 +415,6 @@ class Clickhouse(VectorDb):
412
415
 
413
416
  parameters = self._get_base_parameters()
414
417
  where_query = ""
415
- # if filters:
416
- # query_filters: List[str] = []
417
- # for key, value in filters.values():
418
- # query_filters.append(f"{{{key}_key:String}} = {{{key}_value:String}}")
419
- # parameters[f"{key}_key"] = key
420
- # parameters[f"{key}_value"] = value
421
- # where_query = f"WHERE {' AND '.join(query_filters)}"
422
418
 
423
419
  order_by_query = ""
424
420
  if self.distance == Distance.l2 or self.distance == Distance.max_inner_product:
@@ -429,7 +425,7 @@ class Clickhouse(VectorDb):
429
425
  parameters["query_embedding"] = query_embedding
430
426
 
431
427
  clickhouse_query = (
432
- "SELECT name, meta_data, content, embedding, usage FROM "
428
+ "SELECT name, meta_data, content, content_id, embedding, usage FROM "
433
429
  "{database_name:Identifier}.{table_name:Identifier} "
434
430
  f"{where_query} {order_by_query} LIMIT {limit}"
435
431
  )
@@ -455,9 +451,10 @@ class Clickhouse(VectorDb):
455
451
  name=result[0],
456
452
  meta_data=result[1],
457
453
  content=result[2],
454
+ content_id=result[3],
458
455
  embedder=self.embedder,
459
- embedding=result[3],
460
- usage=result[4],
456
+ embedding=result[4],
457
+ usage=result[5],
461
458
  )
462
459
  )
463
460
 
@@ -476,13 +473,6 @@ class Clickhouse(VectorDb):
476
473
 
477
474
  parameters = self._get_base_parameters()
478
475
  where_query = ""
479
- # if filters:
480
- # query_filters: List[str] = []
481
- # for key, value in filters.values():
482
- # query_filters.append(f"{{{key}_key:String}} = {{{key}_value:String}}")
483
- # parameters[f"{key}_key"] = key
484
- # parameters[f"{key}_value"] = value
485
- # where_query = f"WHERE {' AND '.join(query_filters)}"
486
476
 
487
477
  order_by_query = ""
488
478
  if self.distance == Distance.l2 or self.distance == Distance.max_inner_product:
@@ -493,7 +483,7 @@ class Clickhouse(VectorDb):
493
483
  parameters["query_embedding"] = query_embedding
494
484
 
495
485
  clickhouse_query = (
496
- "SELECT name, meta_data, content, embedding, usage FROM "
486
+ "SELECT name, meta_data, content, content_id, embedding, usage FROM "
497
487
  "{database_name:Identifier}.{table_name:Identifier} "
498
488
  f"{where_query} {order_by_query} LIMIT {limit}"
499
489
  )
@@ -519,9 +509,10 @@ class Clickhouse(VectorDb):
519
509
  name=result[0],
520
510
  meta_data=result[1],
521
511
  content=result[2],
512
+ content_id=result[3],
522
513
  embedder=self.embedder,
523
- embedding=result[3],
524
- usage=result[4],
514
+ embedding=result[4],
515
+ usage=result[5],
525
516
  )
526
517
  )
527
518
 
@@ -573,3 +564,219 @@ class Clickhouse(VectorDb):
573
564
  parameters=parameters,
574
565
  )
575
566
  return True
567
+
568
+ def delete_by_id(self, id: str) -> bool:
569
+ """
570
+
571
+ Delete a document by its ID.
572
+
573
+ Args:
574
+ id (str): The document ID to delete
575
+
576
+ Returns:
577
+ bool: True if document was deleted, False otherwise
578
+ """
579
+ try:
580
+ log_debug(f"ClickHouse VectorDB : Deleting document with ID {id}")
581
+ if not self.id_exists(id):
582
+ return False
583
+
584
+ parameters = self._get_base_parameters()
585
+ parameters["id"] = id
586
+
587
+ self.client.command(
588
+ "DELETE FROM {database_name:Identifier}.{table_name:Identifier} WHERE id = {id:String}",
589
+ parameters=parameters,
590
+ )
591
+ return True
592
+ except Exception as e:
593
+ log_info(f"Error deleting document with ID {id}: {e}")
594
+ return False
595
+
596
+ def delete_by_name(self, name: str) -> bool:
597
+ """
598
+ Delete documents by name.
599
+
600
+ Args:
601
+ name (str): The document name to delete
602
+
603
+ Returns:
604
+ bool: True if documents were deleted, False otherwise
605
+ """
606
+ try:
607
+ log_debug(f"ClickHouse VectorDB : Deleting documents with name {name}")
608
+ if not self.name_exists(name):
609
+ return False
610
+
611
+ parameters = self._get_base_parameters()
612
+ parameters["name"] = name
613
+
614
+ self.client.command(
615
+ "DELETE FROM {database_name:Identifier}.{table_name:Identifier} WHERE name = {name:String}",
616
+ parameters=parameters,
617
+ )
618
+ return True
619
+ except Exception as e:
620
+ log_info(f"Error deleting documents with name {name}: {e}")
621
+ return False
622
+
623
+ def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
624
+ """
625
+ Delete documents by metadata.
626
+
627
+ Args:
628
+ metadata (Dict[str, Any]): The metadata to match for deletion
629
+
630
+ Returns:
631
+ bool: True if documents were deleted, False otherwise
632
+ """
633
+ try:
634
+ log_debug(f"ClickHouse VectorDB : Deleting documents with metadata {metadata}")
635
+ parameters = self._get_base_parameters()
636
+
637
+ # Build WHERE clause for metadata matching using proper ClickHouse JSON syntax
638
+ where_conditions = []
639
+ for key, value in metadata.items():
640
+ if isinstance(value, bool):
641
+ where_conditions.append(f"JSONExtractBool(toString(filters), '{key}') = {str(value).lower()}")
642
+ elif isinstance(value, (int, float)):
643
+ where_conditions.append(f"JSONExtractFloat(toString(filters), '{key}') = {value}")
644
+ else:
645
+ where_conditions.append(f"JSONExtractString(toString(filters), '{key}') = '{value}'")
646
+
647
+ if not where_conditions:
648
+ return False
649
+
650
+ where_clause = " AND ".join(where_conditions)
651
+
652
+ self.client.command(
653
+ f"DELETE FROM {{database_name:Identifier}}.{{table_name:Identifier}} WHERE {where_clause}",
654
+ parameters=parameters,
655
+ )
656
+ return True
657
+ except Exception as e:
658
+ log_info(f"Error deleting documents with metadata {metadata}: {e}")
659
+ return False
660
+
661
+ def delete_by_content_id(self, content_id: str) -> bool:
662
+ """
663
+ Delete documents by content ID.
664
+
665
+ Args:
666
+ content_id (str): The content ID to delete
667
+
668
+ Returns:
669
+ bool: True if documents were deleted, False otherwise
670
+ """
671
+ try:
672
+ log_debug(f"ClickHouse VectorDB : Deleting documents with content_id {content_id}")
673
+ parameters = self._get_base_parameters()
674
+ parameters["content_id"] = content_id
675
+
676
+ self.client.command(
677
+ "DELETE FROM {database_name:Identifier}.{table_name:Identifier} WHERE content_id = {content_id:String}",
678
+ parameters=parameters,
679
+ )
680
+ return True
681
+ except Exception as e:
682
+ log_info(f"Error deleting documents with content_id {content_id}: {e}")
683
+ return False
684
+
685
+ def content_hash_exists(self, content_hash: str) -> bool:
686
+ """
687
+ Validate if a row with this content_hash exists or not
688
+
689
+ Args:
690
+ content_hash (str): Content hash to check
691
+ """
692
+ parameters = self._get_base_parameters()
693
+ parameters["content_hash"] = content_hash
694
+
695
+ result = self.client.query(
696
+ "SELECT content_hash FROM {database_name:Identifier}.{table_name:Identifier} WHERE content_hash = {content_hash:String}",
697
+ parameters=parameters,
698
+ )
699
+ return bool(result)
700
+
701
+ def _delete_by_content_hash(self, content_hash: str) -> bool:
702
+ """
703
+ Delete documents by content hash.
704
+ """
705
+ try:
706
+ parameters = self._get_base_parameters()
707
+ parameters["content_hash"] = content_hash
708
+
709
+ self.client.command(
710
+ "DELETE FROM {database_name:Identifier}.{table_name:Identifier} WHERE content_hash = {content_hash:String}",
711
+ parameters=parameters,
712
+ )
713
+ return True
714
+ except Exception:
715
+ return False
716
+
717
+ def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
718
+ """
719
+ Update the metadata for documents with the given content_id.
720
+
721
+ Args:
722
+ content_id (str): The content ID to update
723
+ metadata (Dict[str, Any]): The metadata to update
724
+ """
725
+ import json
726
+
727
+ try:
728
+ parameters = self._get_base_parameters()
729
+ parameters["content_id"] = content_id
730
+
731
+ # First, get existing documents with their current metadata and filters
732
+ result = self.client.query(
733
+ "SELECT id, meta_data, filters FROM {database_name:Identifier}.{table_name:Identifier} WHERE content_id = {content_id:String}",
734
+ parameters=parameters,
735
+ )
736
+
737
+ if not result.result_rows:
738
+ logger.debug(f"No documents found with content_id: {content_id}")
739
+ return
740
+
741
+ # Update each document
742
+ updated_count = 0
743
+ for row in result.result_rows:
744
+ doc_id, current_meta_json, current_filters_json = row
745
+
746
+ # Parse existing metadata
747
+ try:
748
+ current_metadata = json.loads(current_meta_json) if current_meta_json else {}
749
+ except (json.JSONDecodeError, TypeError):
750
+ current_metadata = {}
751
+
752
+ # Parse existing filters
753
+ try:
754
+ current_filters = json.loads(current_filters_json) if current_filters_json else {}
755
+ except (json.JSONDecodeError, TypeError):
756
+ current_filters = {}
757
+
758
+ # Merge existing metadata with new metadata
759
+ updated_metadata = current_metadata.copy()
760
+ updated_metadata.update(metadata)
761
+
762
+ # Merge existing filters with new metadata
763
+ updated_filters = current_filters.copy()
764
+ updated_filters.update(metadata)
765
+
766
+ # Update the document
767
+ update_params = parameters.copy()
768
+ update_params["doc_id"] = doc_id
769
+ update_params["metadata_json"] = json.dumps(updated_metadata)
770
+ update_params["filters_json"] = json.dumps(updated_filters)
771
+
772
+ self.client.command(
773
+ "ALTER TABLE {database_name:Identifier}.{table_name:Identifier} UPDATE meta_data = {metadata_json:String}, filters = {filters_json:String} WHERE id = {doc_id:String}",
774
+ parameters=update_params,
775
+ )
776
+ updated_count += 1
777
+
778
+ logger.debug(f"Updated metadata for {updated_count} documents with content_id: {content_id}")
779
+
780
+ except Exception as e:
781
+ logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
782
+ raise