agno 1.8.2__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 (589) hide show
  1. agno/agent/__init__.py +19 -27
  2. agno/agent/agent.py +3143 -4170
  3. agno/api/agent.py +11 -67
  4. agno/api/api.py +5 -46
  5. agno/api/evals.py +8 -19
  6. agno/api/os.py +17 -0
  7. agno/api/routes.py +6 -41
  8. agno/api/schemas/__init__.py +9 -0
  9. agno/api/schemas/agent.py +5 -21
  10. agno/api/schemas/evals.py +7 -16
  11. agno/api/schemas/os.py +14 -0
  12. agno/api/schemas/team.py +5 -21
  13. agno/api/schemas/utils.py +21 -0
  14. agno/api/schemas/workflows.py +11 -7
  15. agno/api/settings.py +53 -0
  16. agno/api/team.py +11 -66
  17. agno/api/workflow.py +28 -0
  18. agno/cloud/aws/base.py +214 -0
  19. agno/cloud/aws/s3/__init__.py +2 -0
  20. agno/cloud/aws/s3/api_client.py +43 -0
  21. agno/cloud/aws/s3/bucket.py +195 -0
  22. agno/cloud/aws/s3/object.py +57 -0
  23. agno/db/__init__.py +24 -0
  24. agno/db/base.py +245 -0
  25. agno/db/dynamo/__init__.py +3 -0
  26. agno/db/dynamo/dynamo.py +1743 -0
  27. agno/db/dynamo/schemas.py +278 -0
  28. agno/db/dynamo/utils.py +684 -0
  29. agno/db/firestore/__init__.py +3 -0
  30. agno/db/firestore/firestore.py +1432 -0
  31. agno/db/firestore/schemas.py +130 -0
  32. agno/db/firestore/utils.py +278 -0
  33. agno/db/gcs_json/__init__.py +3 -0
  34. agno/db/gcs_json/gcs_json_db.py +1001 -0
  35. agno/db/gcs_json/utils.py +194 -0
  36. agno/db/in_memory/__init__.py +3 -0
  37. agno/db/in_memory/in_memory_db.py +882 -0
  38. agno/db/in_memory/utils.py +172 -0
  39. agno/db/json/__init__.py +3 -0
  40. agno/db/json/json_db.py +1045 -0
  41. agno/db/json/utils.py +196 -0
  42. agno/db/migrations/v1_to_v2.py +162 -0
  43. agno/db/mongo/__init__.py +3 -0
  44. agno/db/mongo/mongo.py +1416 -0
  45. agno/db/mongo/schemas.py +77 -0
  46. agno/db/mongo/utils.py +204 -0
  47. agno/db/mysql/__init__.py +3 -0
  48. agno/db/mysql/mysql.py +1719 -0
  49. agno/db/mysql/schemas.py +124 -0
  50. agno/db/mysql/utils.py +297 -0
  51. agno/db/postgres/__init__.py +3 -0
  52. agno/db/postgres/postgres.py +1710 -0
  53. agno/db/postgres/schemas.py +124 -0
  54. agno/db/postgres/utils.py +280 -0
  55. agno/db/redis/__init__.py +3 -0
  56. agno/db/redis/redis.py +1367 -0
  57. agno/db/redis/schemas.py +109 -0
  58. agno/db/redis/utils.py +288 -0
  59. agno/db/schemas/__init__.py +3 -0
  60. agno/db/schemas/evals.py +33 -0
  61. agno/db/schemas/knowledge.py +40 -0
  62. agno/db/schemas/memory.py +46 -0
  63. agno/db/singlestore/__init__.py +3 -0
  64. agno/db/singlestore/schemas.py +116 -0
  65. agno/db/singlestore/singlestore.py +1712 -0
  66. agno/db/singlestore/utils.py +326 -0
  67. agno/db/sqlite/__init__.py +3 -0
  68. agno/db/sqlite/schemas.py +119 -0
  69. agno/db/sqlite/sqlite.py +1676 -0
  70. agno/db/sqlite/utils.py +268 -0
  71. agno/db/utils.py +88 -0
  72. agno/eval/__init__.py +14 -0
  73. agno/eval/accuracy.py +154 -48
  74. agno/eval/performance.py +88 -23
  75. agno/eval/reliability.py +73 -20
  76. agno/eval/utils.py +23 -13
  77. agno/integrations/discord/__init__.py +3 -0
  78. agno/{app → integrations}/discord/client.py +10 -10
  79. agno/knowledge/__init__.py +2 -2
  80. agno/{document → knowledge}/chunking/agentic.py +2 -2
  81. agno/{document → knowledge}/chunking/document.py +2 -2
  82. agno/{document → knowledge}/chunking/fixed.py +3 -3
  83. agno/{document → knowledge}/chunking/markdown.py +2 -2
  84. agno/{document → knowledge}/chunking/recursive.py +2 -2
  85. agno/{document → knowledge}/chunking/row.py +2 -2
  86. agno/knowledge/chunking/semantic.py +59 -0
  87. agno/knowledge/chunking/strategy.py +121 -0
  88. agno/knowledge/content.py +74 -0
  89. agno/knowledge/document/__init__.py +5 -0
  90. agno/{document → knowledge/document}/base.py +12 -2
  91. agno/knowledge/embedder/__init__.py +5 -0
  92. agno/{embedder → knowledge/embedder}/aws_bedrock.py +127 -1
  93. agno/{embedder → knowledge/embedder}/azure_openai.py +65 -1
  94. agno/{embedder → knowledge/embedder}/base.py +6 -0
  95. agno/{embedder → knowledge/embedder}/cohere.py +72 -1
  96. agno/{embedder → knowledge/embedder}/fastembed.py +17 -1
  97. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  98. agno/{embedder → knowledge/embedder}/google.py +74 -1
  99. agno/{embedder → knowledge/embedder}/huggingface.py +36 -2
  100. agno/{embedder → knowledge/embedder}/jina.py +48 -2
  101. agno/knowledge/embedder/langdb.py +22 -0
  102. agno/knowledge/embedder/mistral.py +139 -0
  103. agno/{embedder → knowledge/embedder}/nebius.py +1 -1
  104. agno/{embedder → knowledge/embedder}/ollama.py +54 -3
  105. agno/knowledge/embedder/openai.py +223 -0
  106. agno/{embedder → knowledge/embedder}/sentence_transformer.py +16 -1
  107. agno/{embedder → knowledge/embedder}/together.py +1 -1
  108. agno/{embedder → knowledge/embedder}/voyageai.py +49 -1
  109. agno/knowledge/knowledge.py +1551 -0
  110. agno/knowledge/reader/__init__.py +7 -0
  111. agno/{document → knowledge}/reader/arxiv_reader.py +32 -4
  112. agno/knowledge/reader/base.py +88 -0
  113. agno/{document → knowledge}/reader/csv_reader.py +47 -65
  114. agno/knowledge/reader/docx_reader.py +83 -0
  115. agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
  116. agno/{document → knowledge}/reader/json_reader.py +30 -9
  117. agno/{document → knowledge}/reader/markdown_reader.py +58 -9
  118. agno/{document → knowledge}/reader/pdf_reader.py +71 -126
  119. agno/knowledge/reader/reader_factory.py +268 -0
  120. agno/knowledge/reader/s3_reader.py +101 -0
  121. agno/{document → knowledge}/reader/text_reader.py +31 -10
  122. agno/knowledge/reader/url_reader.py +128 -0
  123. agno/knowledge/reader/web_search_reader.py +366 -0
  124. agno/{document → knowledge}/reader/website_reader.py +37 -10
  125. agno/knowledge/reader/wikipedia_reader.py +59 -0
  126. agno/knowledge/reader/youtube_reader.py +78 -0
  127. agno/knowledge/remote_content/remote_content.py +88 -0
  128. agno/{reranker → knowledge/reranker}/base.py +1 -1
  129. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  130. agno/{reranker → knowledge/reranker}/infinity.py +2 -2
  131. agno/{reranker → knowledge/reranker}/sentence_transformer.py +2 -2
  132. agno/knowledge/types.py +30 -0
  133. agno/knowledge/utils.py +169 -0
  134. agno/media.py +269 -268
  135. agno/memory/__init__.py +2 -10
  136. agno/memory/manager.py +1003 -148
  137. agno/models/aimlapi/__init__.py +2 -2
  138. agno/models/aimlapi/aimlapi.py +6 -6
  139. agno/models/anthropic/claude.py +128 -72
  140. agno/models/aws/bedrock.py +107 -175
  141. agno/models/aws/claude.py +64 -18
  142. agno/models/azure/ai_foundry.py +73 -23
  143. agno/models/base.py +346 -290
  144. agno/models/cerebras/cerebras.py +84 -27
  145. agno/models/cohere/chat.py +106 -98
  146. agno/models/google/gemini.py +105 -46
  147. agno/models/groq/groq.py +97 -35
  148. agno/models/huggingface/huggingface.py +92 -27
  149. agno/models/ibm/watsonx.py +72 -13
  150. agno/models/litellm/chat.py +85 -13
  151. agno/models/message.py +46 -151
  152. agno/models/meta/llama.py +85 -49
  153. agno/models/metrics.py +120 -0
  154. agno/models/mistral/mistral.py +90 -21
  155. agno/models/ollama/__init__.py +0 -2
  156. agno/models/ollama/chat.py +85 -47
  157. agno/models/openai/chat.py +154 -37
  158. agno/models/openai/responses.py +178 -105
  159. agno/models/perplexity/perplexity.py +26 -2
  160. agno/models/portkey/portkey.py +0 -7
  161. agno/models/response.py +15 -9
  162. agno/models/utils.py +20 -0
  163. agno/models/vercel/__init__.py +2 -2
  164. agno/models/vercel/v0.py +1 -1
  165. agno/models/vllm/__init__.py +2 -2
  166. agno/models/vllm/vllm.py +3 -3
  167. agno/models/xai/xai.py +10 -10
  168. agno/os/__init__.py +3 -0
  169. agno/os/app.py +497 -0
  170. agno/os/auth.py +47 -0
  171. agno/os/config.py +103 -0
  172. agno/os/interfaces/agui/__init__.py +3 -0
  173. agno/os/interfaces/agui/agui.py +31 -0
  174. agno/{app/agui/async_router.py → os/interfaces/agui/router.py} +16 -16
  175. agno/{app → os/interfaces}/agui/utils.py +65 -28
  176. agno/os/interfaces/base.py +21 -0
  177. agno/os/interfaces/slack/__init__.py +3 -0
  178. agno/{app/slack/async_router.py → os/interfaces/slack/router.py} +3 -5
  179. agno/os/interfaces/slack/slack.py +32 -0
  180. agno/os/interfaces/whatsapp/__init__.py +3 -0
  181. agno/{app/whatsapp/async_router.py → os/interfaces/whatsapp/router.py} +4 -7
  182. agno/os/interfaces/whatsapp/whatsapp.py +29 -0
  183. agno/os/mcp.py +235 -0
  184. agno/os/router.py +1400 -0
  185. agno/os/routers/__init__.py +3 -0
  186. agno/os/routers/evals/__init__.py +3 -0
  187. agno/os/routers/evals/evals.py +393 -0
  188. agno/os/routers/evals/schemas.py +142 -0
  189. agno/os/routers/evals/utils.py +161 -0
  190. agno/os/routers/knowledge/__init__.py +3 -0
  191. agno/os/routers/knowledge/knowledge.py +850 -0
  192. agno/os/routers/knowledge/schemas.py +118 -0
  193. agno/os/routers/memory/__init__.py +3 -0
  194. agno/os/routers/memory/memory.py +410 -0
  195. agno/os/routers/memory/schemas.py +58 -0
  196. agno/os/routers/metrics/__init__.py +3 -0
  197. agno/os/routers/metrics/metrics.py +178 -0
  198. agno/os/routers/metrics/schemas.py +47 -0
  199. agno/os/routers/session/__init__.py +3 -0
  200. agno/os/routers/session/session.py +536 -0
  201. agno/os/schema.py +945 -0
  202. agno/{app/playground → os}/settings.py +7 -15
  203. agno/os/utils.py +270 -0
  204. agno/reasoning/azure_ai_foundry.py +4 -4
  205. agno/reasoning/deepseek.py +4 -4
  206. agno/reasoning/default.py +6 -11
  207. agno/reasoning/groq.py +4 -4
  208. agno/reasoning/helpers.py +4 -6
  209. agno/reasoning/ollama.py +4 -4
  210. agno/reasoning/openai.py +4 -4
  211. agno/run/agent.py +633 -0
  212. agno/run/base.py +53 -77
  213. agno/run/cancel.py +81 -0
  214. agno/run/team.py +243 -96
  215. agno/run/workflow.py +550 -12
  216. agno/session/__init__.py +10 -0
  217. agno/session/agent.py +244 -0
  218. agno/session/summary.py +225 -0
  219. agno/session/team.py +262 -0
  220. agno/{storage/session/v2 → session}/workflow.py +47 -24
  221. agno/team/__init__.py +15 -16
  222. agno/team/team.py +3260 -4824
  223. agno/tools/agentql.py +14 -5
  224. agno/tools/airflow.py +9 -4
  225. agno/tools/api.py +7 -3
  226. agno/tools/apify.py +2 -46
  227. agno/tools/arxiv.py +8 -3
  228. agno/tools/aws_lambda.py +7 -5
  229. agno/tools/aws_ses.py +7 -1
  230. agno/tools/baidusearch.py +4 -1
  231. agno/tools/bitbucket.py +4 -4
  232. agno/tools/brandfetch.py +14 -11
  233. agno/tools/bravesearch.py +4 -1
  234. agno/tools/brightdata.py +43 -23
  235. agno/tools/browserbase.py +13 -4
  236. agno/tools/calcom.py +12 -10
  237. agno/tools/calculator.py +10 -27
  238. agno/tools/cartesia.py +20 -17
  239. agno/tools/{clickup_tool.py → clickup.py} +12 -25
  240. agno/tools/confluence.py +8 -8
  241. agno/tools/crawl4ai.py +7 -1
  242. agno/tools/csv_toolkit.py +9 -8
  243. agno/tools/dalle.py +22 -12
  244. agno/tools/daytona.py +13 -16
  245. agno/tools/decorator.py +6 -3
  246. agno/tools/desi_vocal.py +17 -8
  247. agno/tools/discord.py +11 -8
  248. agno/tools/docker.py +30 -42
  249. agno/tools/duckdb.py +34 -53
  250. agno/tools/duckduckgo.py +8 -7
  251. agno/tools/e2b.py +62 -62
  252. agno/tools/eleven_labs.py +36 -29
  253. agno/tools/email.py +4 -1
  254. agno/tools/evm.py +7 -1
  255. agno/tools/exa.py +19 -14
  256. agno/tools/fal.py +30 -30
  257. agno/tools/file.py +9 -8
  258. agno/tools/financial_datasets.py +25 -44
  259. agno/tools/firecrawl.py +17 -18
  260. agno/tools/function.py +127 -18
  261. agno/tools/giphy.py +23 -11
  262. agno/tools/github.py +48 -126
  263. agno/tools/gmail.py +45 -61
  264. agno/tools/google_bigquery.py +7 -6
  265. agno/tools/google_maps.py +11 -26
  266. agno/tools/googlesearch.py +7 -2
  267. agno/tools/googlesheets.py +21 -17
  268. agno/tools/hackernews.py +9 -5
  269. agno/tools/jina.py +5 -4
  270. agno/tools/jira.py +18 -9
  271. agno/tools/knowledge.py +31 -32
  272. agno/tools/linear.py +18 -33
  273. agno/tools/linkup.py +5 -1
  274. agno/tools/local_file_system.py +8 -5
  275. agno/tools/lumalab.py +32 -20
  276. agno/tools/mcp.py +1 -2
  277. agno/tools/mem0.py +18 -12
  278. agno/tools/memori.py +14 -10
  279. agno/tools/mlx_transcribe.py +3 -2
  280. agno/tools/models/azure_openai.py +33 -15
  281. agno/tools/models/gemini.py +59 -32
  282. agno/tools/models/groq.py +30 -23
  283. agno/tools/models/nebius.py +28 -12
  284. agno/tools/models_labs.py +40 -16
  285. agno/tools/moviepy_video.py +7 -6
  286. agno/tools/neo4j.py +10 -8
  287. agno/tools/newspaper.py +7 -2
  288. agno/tools/newspaper4k.py +8 -3
  289. agno/tools/openai.py +58 -32
  290. agno/tools/openbb.py +12 -11
  291. agno/tools/opencv.py +63 -47
  292. agno/tools/openweather.py +14 -12
  293. agno/tools/pandas.py +11 -3
  294. agno/tools/postgres.py +4 -12
  295. agno/tools/pubmed.py +4 -1
  296. agno/tools/python.py +9 -22
  297. agno/tools/reasoning.py +35 -27
  298. agno/tools/reddit.py +11 -26
  299. agno/tools/replicate.py +55 -42
  300. agno/tools/resend.py +4 -1
  301. agno/tools/scrapegraph.py +15 -14
  302. agno/tools/searxng.py +10 -23
  303. agno/tools/serpapi.py +6 -3
  304. agno/tools/serper.py +13 -4
  305. agno/tools/shell.py +9 -2
  306. agno/tools/slack.py +12 -11
  307. agno/tools/sleep.py +3 -2
  308. agno/tools/spider.py +24 -4
  309. agno/tools/sql.py +7 -6
  310. agno/tools/tavily.py +6 -4
  311. agno/tools/telegram.py +12 -4
  312. agno/tools/todoist.py +11 -31
  313. agno/tools/toolkit.py +1 -1
  314. agno/tools/trafilatura.py +22 -6
  315. agno/tools/trello.py +9 -22
  316. agno/tools/twilio.py +10 -3
  317. agno/tools/user_control_flow.py +6 -1
  318. agno/tools/valyu.py +34 -5
  319. agno/tools/visualization.py +19 -28
  320. agno/tools/webbrowser.py +4 -3
  321. agno/tools/webex.py +11 -7
  322. agno/tools/website.py +15 -46
  323. agno/tools/webtools.py +12 -4
  324. agno/tools/whatsapp.py +5 -9
  325. agno/tools/wikipedia.py +20 -13
  326. agno/tools/x.py +14 -13
  327. agno/tools/yfinance.py +13 -40
  328. agno/tools/youtube.py +26 -20
  329. agno/tools/zendesk.py +7 -2
  330. agno/tools/zep.py +10 -7
  331. agno/tools/zoom.py +10 -9
  332. agno/utils/common.py +1 -19
  333. agno/utils/events.py +100 -123
  334. agno/utils/gemini.py +1 -1
  335. agno/utils/knowledge.py +29 -0
  336. agno/utils/log.py +54 -4
  337. agno/utils/mcp.py +68 -10
  338. agno/utils/media.py +39 -0
  339. agno/utils/message.py +12 -1
  340. agno/utils/models/aws_claude.py +1 -1
  341. agno/utils/models/claude.py +6 -12
  342. agno/utils/models/cohere.py +1 -1
  343. agno/utils/models/mistral.py +8 -7
  344. agno/utils/models/schema_utils.py +3 -3
  345. agno/utils/models/watsonx.py +1 -1
  346. agno/utils/openai.py +1 -1
  347. agno/utils/pprint.py +33 -32
  348. agno/utils/print_response/agent.py +779 -0
  349. agno/utils/print_response/team.py +1669 -0
  350. agno/utils/print_response/workflow.py +1451 -0
  351. agno/utils/prompts.py +14 -14
  352. agno/utils/reasoning.py +87 -0
  353. agno/utils/response.py +42 -42
  354. agno/utils/streamlit.py +481 -0
  355. agno/utils/string.py +8 -22
  356. agno/utils/team.py +50 -0
  357. agno/utils/timer.py +2 -2
  358. agno/vectordb/base.py +33 -21
  359. agno/vectordb/cassandra/cassandra.py +287 -23
  360. agno/vectordb/chroma/chromadb.py +482 -59
  361. agno/vectordb/clickhouse/clickhousedb.py +270 -63
  362. agno/vectordb/couchbase/couchbase.py +309 -29
  363. agno/vectordb/lancedb/lance_db.py +360 -21
  364. agno/vectordb/langchaindb/__init__.py +5 -0
  365. agno/vectordb/langchaindb/langchaindb.py +145 -0
  366. agno/vectordb/lightrag/__init__.py +5 -0
  367. agno/vectordb/lightrag/lightrag.py +374 -0
  368. agno/vectordb/llamaindex/llamaindexdb.py +127 -0
  369. agno/vectordb/milvus/milvus.py +242 -32
  370. agno/vectordb/mongodb/mongodb.py +200 -24
  371. agno/vectordb/pgvector/pgvector.py +319 -37
  372. agno/vectordb/pineconedb/pineconedb.py +221 -27
  373. agno/vectordb/qdrant/qdrant.py +334 -14
  374. agno/vectordb/singlestore/singlestore.py +286 -29
  375. agno/vectordb/surrealdb/surrealdb.py +187 -7
  376. agno/vectordb/upstashdb/upstashdb.py +342 -26
  377. agno/vectordb/weaviate/weaviate.py +227 -165
  378. agno/workflow/__init__.py +17 -13
  379. agno/workflow/{v2/condition.py → condition.py} +135 -32
  380. agno/workflow/{v2/loop.py → loop.py} +115 -28
  381. agno/workflow/{v2/parallel.py → parallel.py} +138 -108
  382. agno/workflow/{v2/router.py → router.py} +133 -32
  383. agno/workflow/{v2/step.py → step.py} +207 -49
  384. agno/workflow/{v2/steps.py → steps.py} +147 -66
  385. agno/workflow/types.py +482 -0
  386. agno/workflow/workflow.py +2410 -696
  387. agno-2.0.0.dist-info/METADATA +494 -0
  388. agno-2.0.0.dist-info/RECORD +515 -0
  389. agno-2.0.0.dist-info/licenses/LICENSE +201 -0
  390. agno/agent/metrics.py +0 -110
  391. agno/api/app.py +0 -35
  392. agno/api/playground.py +0 -92
  393. agno/api/schemas/app.py +0 -12
  394. agno/api/schemas/playground.py +0 -22
  395. agno/api/schemas/user.py +0 -35
  396. agno/api/schemas/workspace.py +0 -46
  397. agno/api/user.py +0 -160
  398. agno/api/workflows.py +0 -33
  399. agno/api/workspace.py +0 -175
  400. agno/app/agui/__init__.py +0 -3
  401. agno/app/agui/app.py +0 -17
  402. agno/app/agui/sync_router.py +0 -120
  403. agno/app/base.py +0 -186
  404. agno/app/discord/__init__.py +0 -3
  405. agno/app/fastapi/__init__.py +0 -3
  406. agno/app/fastapi/app.py +0 -107
  407. agno/app/fastapi/async_router.py +0 -457
  408. agno/app/fastapi/sync_router.py +0 -448
  409. agno/app/playground/app.py +0 -228
  410. agno/app/playground/async_router.py +0 -1053
  411. agno/app/playground/deploy.py +0 -249
  412. agno/app/playground/operator.py +0 -183
  413. agno/app/playground/schemas.py +0 -223
  414. agno/app/playground/serve.py +0 -55
  415. agno/app/playground/sync_router.py +0 -1045
  416. agno/app/playground/utils.py +0 -46
  417. agno/app/settings.py +0 -15
  418. agno/app/slack/__init__.py +0 -3
  419. agno/app/slack/app.py +0 -19
  420. agno/app/slack/sync_router.py +0 -92
  421. agno/app/utils.py +0 -54
  422. agno/app/whatsapp/__init__.py +0 -3
  423. agno/app/whatsapp/app.py +0 -15
  424. agno/app/whatsapp/sync_router.py +0 -197
  425. agno/cli/auth_server.py +0 -249
  426. agno/cli/config.py +0 -274
  427. agno/cli/console.py +0 -88
  428. agno/cli/credentials.py +0 -23
  429. agno/cli/entrypoint.py +0 -571
  430. agno/cli/operator.py +0 -357
  431. agno/cli/settings.py +0 -96
  432. agno/cli/ws/ws_cli.py +0 -817
  433. agno/constants.py +0 -13
  434. agno/document/__init__.py +0 -5
  435. agno/document/chunking/semantic.py +0 -45
  436. agno/document/chunking/strategy.py +0 -31
  437. agno/document/reader/__init__.py +0 -5
  438. agno/document/reader/base.py +0 -47
  439. agno/document/reader/docx_reader.py +0 -60
  440. agno/document/reader/gcs/pdf_reader.py +0 -44
  441. agno/document/reader/s3/pdf_reader.py +0 -59
  442. agno/document/reader/s3/text_reader.py +0 -63
  443. agno/document/reader/url_reader.py +0 -59
  444. agno/document/reader/youtube_reader.py +0 -58
  445. agno/embedder/__init__.py +0 -5
  446. agno/embedder/langdb.py +0 -80
  447. agno/embedder/mistral.py +0 -82
  448. agno/embedder/openai.py +0 -78
  449. agno/file/__init__.py +0 -5
  450. agno/file/file.py +0 -16
  451. agno/file/local/csv.py +0 -32
  452. agno/file/local/txt.py +0 -19
  453. agno/infra/app.py +0 -240
  454. agno/infra/base.py +0 -144
  455. agno/infra/context.py +0 -20
  456. agno/infra/db_app.py +0 -52
  457. agno/infra/resource.py +0 -205
  458. agno/infra/resources.py +0 -55
  459. agno/knowledge/agent.py +0 -702
  460. agno/knowledge/arxiv.py +0 -33
  461. agno/knowledge/combined.py +0 -36
  462. agno/knowledge/csv.py +0 -144
  463. agno/knowledge/csv_url.py +0 -124
  464. agno/knowledge/document.py +0 -223
  465. agno/knowledge/docx.py +0 -137
  466. agno/knowledge/firecrawl.py +0 -34
  467. agno/knowledge/gcs/__init__.py +0 -0
  468. agno/knowledge/gcs/base.py +0 -39
  469. agno/knowledge/gcs/pdf.py +0 -125
  470. agno/knowledge/json.py +0 -137
  471. agno/knowledge/langchain.py +0 -71
  472. agno/knowledge/light_rag.py +0 -273
  473. agno/knowledge/llamaindex.py +0 -66
  474. agno/knowledge/markdown.py +0 -154
  475. agno/knowledge/pdf.py +0 -164
  476. agno/knowledge/pdf_bytes.py +0 -42
  477. agno/knowledge/pdf_url.py +0 -148
  478. agno/knowledge/s3/__init__.py +0 -0
  479. agno/knowledge/s3/base.py +0 -64
  480. agno/knowledge/s3/pdf.py +0 -33
  481. agno/knowledge/s3/text.py +0 -34
  482. agno/knowledge/text.py +0 -141
  483. agno/knowledge/url.py +0 -46
  484. agno/knowledge/website.py +0 -179
  485. agno/knowledge/wikipedia.py +0 -32
  486. agno/knowledge/youtube.py +0 -35
  487. agno/memory/agent.py +0 -423
  488. agno/memory/classifier.py +0 -104
  489. agno/memory/db/__init__.py +0 -5
  490. agno/memory/db/base.py +0 -42
  491. agno/memory/db/mongodb.py +0 -189
  492. agno/memory/db/postgres.py +0 -203
  493. agno/memory/db/sqlite.py +0 -193
  494. agno/memory/memory.py +0 -22
  495. agno/memory/row.py +0 -36
  496. agno/memory/summarizer.py +0 -201
  497. agno/memory/summary.py +0 -19
  498. agno/memory/team.py +0 -415
  499. agno/memory/v2/__init__.py +0 -2
  500. agno/memory/v2/db/__init__.py +0 -1
  501. agno/memory/v2/db/base.py +0 -42
  502. agno/memory/v2/db/firestore.py +0 -339
  503. agno/memory/v2/db/mongodb.py +0 -196
  504. agno/memory/v2/db/postgres.py +0 -214
  505. agno/memory/v2/db/redis.py +0 -187
  506. agno/memory/v2/db/schema.py +0 -54
  507. agno/memory/v2/db/sqlite.py +0 -209
  508. agno/memory/v2/manager.py +0 -437
  509. agno/memory/v2/memory.py +0 -1097
  510. agno/memory/v2/schema.py +0 -55
  511. agno/memory/v2/summarizer.py +0 -215
  512. agno/memory/workflow.py +0 -38
  513. agno/models/ollama/tools.py +0 -430
  514. agno/models/qwen/__init__.py +0 -5
  515. agno/playground/__init__.py +0 -10
  516. agno/playground/deploy.py +0 -3
  517. agno/playground/playground.py +0 -3
  518. agno/playground/serve.py +0 -3
  519. agno/playground/settings.py +0 -3
  520. agno/reranker/__init__.py +0 -0
  521. agno/run/response.py +0 -467
  522. agno/run/v2/__init__.py +0 -0
  523. agno/run/v2/workflow.py +0 -567
  524. agno/storage/__init__.py +0 -0
  525. agno/storage/agent/__init__.py +0 -0
  526. agno/storage/agent/dynamodb.py +0 -1
  527. agno/storage/agent/json.py +0 -1
  528. agno/storage/agent/mongodb.py +0 -1
  529. agno/storage/agent/postgres.py +0 -1
  530. agno/storage/agent/singlestore.py +0 -1
  531. agno/storage/agent/sqlite.py +0 -1
  532. agno/storage/agent/yaml.py +0 -1
  533. agno/storage/base.py +0 -60
  534. agno/storage/dynamodb.py +0 -673
  535. agno/storage/firestore.py +0 -297
  536. agno/storage/gcs_json.py +0 -261
  537. agno/storage/in_memory.py +0 -234
  538. agno/storage/json.py +0 -237
  539. agno/storage/mongodb.py +0 -328
  540. agno/storage/mysql.py +0 -685
  541. agno/storage/postgres.py +0 -682
  542. agno/storage/redis.py +0 -336
  543. agno/storage/session/__init__.py +0 -16
  544. agno/storage/session/agent.py +0 -64
  545. agno/storage/session/team.py +0 -63
  546. agno/storage/session/v2/__init__.py +0 -5
  547. agno/storage/session/workflow.py +0 -61
  548. agno/storage/singlestore.py +0 -606
  549. agno/storage/sqlite.py +0 -646
  550. agno/storage/workflow/__init__.py +0 -0
  551. agno/storage/workflow/mongodb.py +0 -1
  552. agno/storage/workflow/postgres.py +0 -1
  553. agno/storage/workflow/sqlite.py +0 -1
  554. agno/storage/yaml.py +0 -241
  555. agno/tools/thinking.py +0 -73
  556. agno/utils/defaults.py +0 -57
  557. agno/utils/filesystem.py +0 -39
  558. agno/utils/git.py +0 -52
  559. agno/utils/json_io.py +0 -30
  560. agno/utils/load_env.py +0 -19
  561. agno/utils/py_io.py +0 -19
  562. agno/utils/pyproject.py +0 -18
  563. agno/utils/resource_filter.py +0 -31
  564. agno/workflow/v2/__init__.py +0 -21
  565. agno/workflow/v2/types.py +0 -357
  566. agno/workflow/v2/workflow.py +0 -3313
  567. agno/workspace/__init__.py +0 -0
  568. agno/workspace/config.py +0 -325
  569. agno/workspace/enums.py +0 -6
  570. agno/workspace/helpers.py +0 -52
  571. agno/workspace/operator.py +0 -757
  572. agno/workspace/settings.py +0 -158
  573. agno-1.8.2.dist-info/METADATA +0 -982
  574. agno-1.8.2.dist-info/RECORD +0 -566
  575. agno-1.8.2.dist-info/entry_points.txt +0 -3
  576. agno-1.8.2.dist-info/licenses/LICENSE +0 -375
  577. /agno/{app → db/migrations}/__init__.py +0 -0
  578. /agno/{app/playground/__init__.py → db/schemas/metrics.py} +0 -0
  579. /agno/{cli → integrations}/__init__.py +0 -0
  580. /agno/{cli/ws → knowledge/chunking}/__init__.py +0 -0
  581. /agno/{document/chunking → knowledge/remote_content}/__init__.py +0 -0
  582. /agno/{document/reader/gcs → knowledge/reranker}/__init__.py +0 -0
  583. /agno/{document/reader/s3 → os/interfaces}/__init__.py +0 -0
  584. /agno/{app → os/interfaces}/slack/security.py +0 -0
  585. /agno/{app → os/interfaces}/whatsapp/security.py +0 -0
  586. /agno/{file/local → utils/print_response}/__init__.py +0 -0
  587. /agno/{infra → vectordb/llamaindex}/__init__.py +0 -0
  588. {agno-1.8.2.dist-info → agno-2.0.0.dist-info}/WHEEL +0 -0
  589. {agno-1.8.2.dist-info → agno-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import json
2
3
  from hashlib import md5
3
4
  from os import getenv
@@ -9,9 +10,9 @@ try:
9
10
  except ImportError:
10
11
  raise ImportError("`lancedb` not installed. Please install using `pip install lancedb`")
11
12
 
12
- from agno.document import Document
13
- from agno.embedder import Embedder
14
- from agno.reranker.base import Reranker
13
+ from agno.knowledge.document import Document
14
+ from agno.knowledge.embedder import Embedder
15
+ from agno.knowledge.reranker.base import Reranker
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
@@ -60,7 +61,7 @@ class LanceDb(VectorDb):
60
61
  ):
61
62
  # Embedder for embedding the document contents
62
63
  if embedder is None:
63
- from agno.embedder.openai import OpenAIEmbedder
64
+ from agno.knowledge.embedder.openai import OpenAIEmbedder
64
65
 
65
66
  embedder = OpenAIEmbedder()
66
67
  log_info("Embedder not provided, using OpenAIEmbedder as default.")
@@ -88,10 +89,18 @@ class LanceDb(VectorDb):
88
89
 
89
90
  if table_name and table_name in self.connection.table_names():
90
91
  # Open the table if it exists
91
- self.table = self.connection.open_table(name=table_name)
92
- self.table_name = self.table.name
93
- self._vector_col = self.table.schema.names[0]
94
- self._id = self.table.schema.names[1] # type: ignore
92
+ try:
93
+ self.table = self.connection.open_table(name=table_name)
94
+ self.table_name = self.table.name
95
+ self._vector_col = self.table.schema.names[0]
96
+ self._id = self.table.schema.names[1] # type: ignore
97
+ except ValueError as e:
98
+ # Table might have been dropped by async operations but sync connection hasn't updated
99
+ if "was not found" in str(e):
100
+ log_debug(f"Table {table_name} listed but not accessible, will create if needed")
101
+ self.table = None
102
+ else:
103
+ raise
95
104
 
96
105
  # LanceDB table details
97
106
  if self.table is None:
@@ -105,7 +114,7 @@ class LanceDb(VectorDb):
105
114
  self.table = table
106
115
  self.table_name = self.table.name
107
116
  self._vector_col = self.table.schema.names[0]
108
- self._id = self.tbl.schema.names[1] # type: ignore
117
+ self._id = self.table.schema.names[1] # type: ignore
109
118
  else:
110
119
  if not table_name:
111
120
  raise ValueError("Either table or table_name should be provided.")
@@ -135,10 +144,28 @@ class LanceDb(VectorDb):
135
144
  """Get or create an async connection to LanceDB."""
136
145
  if self.async_connection is None:
137
146
  self.async_connection = await lancedb.connect_async(self.uri)
147
+ # Only try to open table if it exists and we don't have it already
138
148
  if self.async_table is None:
139
- self.async_table = await self.async_connection.open_table(self.table_name)
149
+ table_names = await self.async_connection.table_names()
150
+ if self.table_name in table_names:
151
+ try:
152
+ self.async_table = await self.async_connection.open_table(self.table_name)
153
+ except ValueError:
154
+ # Table might have been dropped by another operation
155
+ pass
140
156
  return self.async_connection
141
157
 
158
+ def _refresh_sync_connection(self) -> None:
159
+ """Refresh the sync connection to see changes made by async operations."""
160
+ try:
161
+ # Re-establish sync connection to see async changes
162
+ if self.connection and self.table_name in self.connection.table_names():
163
+ self.table = self.connection.open_table(self.table_name)
164
+ log_debug(f"Refreshed sync connection for table: {self.table_name}")
165
+ except Exception as e:
166
+ log_debug(f"Could not refresh sync connection: {e}")
167
+ # If refresh fails, we can still function but sync methods might not see async changes
168
+
142
169
  def create(self) -> None:
143
170
  """Create the table if it does not exist."""
144
171
  if not self.exists():
@@ -146,7 +173,7 @@ class LanceDb(VectorDb):
146
173
 
147
174
  async def async_create(self) -> None:
148
175
  """Create the table asynchronously if it does not exist."""
149
- if not self.exists():
176
+ if not await self.async_exists():
150
177
  conn = await self._get_async_connection()
151
178
  schema = self._base_schema()
152
179
 
@@ -212,7 +239,7 @@ class LanceDb(VectorDb):
212
239
  self.table = self.connection.open_table(name=self.table_name)
213
240
  return self.doc_exists(document)
214
241
 
215
- def insert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
242
+ def insert(self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
216
243
  """
217
244
  Insert documents into the database.
218
245
 
@@ -245,6 +272,8 @@ class LanceDb(VectorDb):
245
272
  "meta_data": document.meta_data,
246
273
  "content": cleaned_content,
247
274
  "usage": document.usage,
275
+ "content_id": document.content_id,
276
+ "content_hash": content_hash,
248
277
  }
249
278
  data.append(
250
279
  {
@@ -270,7 +299,9 @@ class LanceDb(VectorDb):
270
299
 
271
300
  log_debug(f"Inserted {len(data)} documents")
272
301
 
273
- async def async_insert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
302
+ async def async_insert(
303
+ self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
304
+ ) -> None:
274
305
  """
275
306
  Asynchronously insert documents into the database.
276
307
 
@@ -285,7 +316,10 @@ class LanceDb(VectorDb):
285
316
  log_debug(f"Inserting {len(documents)} documents")
286
317
  data = []
287
318
 
288
- # Prepare documents for insertion
319
+ # Prepare documents for insertion.
320
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
321
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
322
+
289
323
  for document in documents:
290
324
  if await self.async_doc_exists(document):
291
325
  continue
@@ -296,7 +330,6 @@ class LanceDb(VectorDb):
296
330
  meta_data.update(filters)
297
331
  document.meta_data = meta_data
298
332
 
299
- document.embed(embedder=self.embedder)
300
333
  cleaned_content = document.content.replace("\x00", "\ufffd")
301
334
  doc_id = str(md5(cleaned_content.encode()).hexdigest())
302
335
  payload = {
@@ -304,6 +337,8 @@ class LanceDb(VectorDb):
304
337
  "meta_data": document.meta_data,
305
338
  "content": cleaned_content,
306
339
  "usage": document.usage,
340
+ "content_id": document.content_id,
341
+ "content_hash": content_hash,
307
342
  }
308
343
  data.append(
309
344
  {
@@ -327,11 +362,18 @@ class LanceDb(VectorDb):
327
362
  await self.async_table.add(data) # type: ignore
328
363
 
329
364
  log_debug(f"Asynchronously inserted {len(data)} documents")
365
+
366
+ # Refresh sync connection to see async changes
367
+ self._refresh_sync_connection()
330
368
  except Exception as e:
331
369
  logger.error(f"Error during async document insertion: {e}")
332
370
  raise
333
371
 
334
- def upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
372
+ def upsert_available(self) -> bool:
373
+ """Check if upsert is available in LanceDB."""
374
+ return True
375
+
376
+ def upsert(self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
335
377
  """
336
378
  Upsert documents into the database.
337
379
 
@@ -339,10 +381,16 @@ class LanceDb(VectorDb):
339
381
  documents (List[Document]): List of documents to upsert
340
382
  filters (Optional[Dict[str, Any]]): Filters to apply while upserting
341
383
  """
342
- self.insert(documents, filters=filters)
384
+ if self.content_hash_exists(content_hash):
385
+ self._delete_by_content_hash(content_hash)
386
+ self.insert(content_hash=content_hash, documents=documents, filters=filters)
343
387
 
344
- async def async_upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
345
- await self.async_insert(documents, filters)
388
+ async def async_upsert(
389
+ self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
390
+ ) -> None:
391
+ if self.content_hash_exists(content_hash):
392
+ self._delete_by_content_hash(content_hash)
393
+ await self.async_insert(content_hash=content_hash, documents=documents, filters=filters)
346
394
 
347
395
  def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
348
396
  """
@@ -539,6 +587,7 @@ class LanceDb(VectorDb):
539
587
  embedder=self.embedder,
540
588
  embedding=item["vector"],
541
589
  usage=payload["usage"],
590
+ content_id=payload.get("content_id"),
542
591
  )
543
592
  )
544
593
 
@@ -551,6 +600,8 @@ class LanceDb(VectorDb):
551
600
  if self.exists():
552
601
  log_debug(f"Deleting collection: {self.table_name}")
553
602
  self.connection.drop_table(self.table_name) # type: ignore
603
+ # Clear the table reference after dropping
604
+ self.table = None
554
605
 
555
606
  async def async_drop(self) -> None:
556
607
  """Drop the table asynchronously."""
@@ -558,16 +609,26 @@ class LanceDb(VectorDb):
558
609
  log_debug(f"Deleting collection: {self.table_name}")
559
610
  conn = await self._get_async_connection()
560
611
  await conn.drop_table(self.table_name)
612
+ # Clear the async table reference after dropping
613
+ self.async_table = None
561
614
 
562
615
  def exists(self) -> bool:
616
+ # If we have an async table that was created, the table exists
617
+ if self.async_table is not None:
618
+ return True
563
619
  if self.connection:
564
620
  return self.table_name in self.connection.table_names()
565
621
  return False
566
622
 
567
623
  async def async_exists(self) -> bool:
568
624
  """Check if the table exists asynchronously."""
569
- conn = await self._get_async_connection()
570
- table_names = await conn.table_names()
625
+ # If we have an async table that was created, the table exists
626
+ if self.async_table is not None:
627
+ return True
628
+ # Check if table exists in database without trying to open it
629
+ if self.async_connection is None:
630
+ self.async_connection = await lancedb.connect_async(self.uri)
631
+ table_names = await self.async_connection.table_names()
571
632
  return self.table_name in table_names
572
633
 
573
634
  async def async_get_count(self) -> int:
@@ -577,7 +638,27 @@ class LanceDb(VectorDb):
577
638
  return await self.async_table.count_rows()
578
639
  return 0
579
640
 
641
+ def _async_get_count_sync(self) -> int:
642
+ """Helper method to run async_get_count in a new thread with its own event loop"""
643
+ import asyncio
644
+
645
+ return asyncio.run(self.async_get_count())
646
+
580
647
  def get_count(self) -> int:
648
+ # If we have data in the async table but sync table isn't available, try to get count from async table
649
+ if self.async_table is not None:
650
+ try:
651
+ import asyncio
652
+
653
+ # Check if we're already in an async context
654
+ try:
655
+ return self._async_get_count_sync()
656
+ except RuntimeError:
657
+ # No event loop running, safe to use asyncio.run
658
+ return asyncio.run(self.async_get_count())
659
+ except Exception:
660
+ pass
661
+
581
662
  if self.exists() and self.table:
582
663
  return self.table.count_rows()
583
664
  return 0
@@ -606,3 +687,261 @@ class LanceDb(VectorDb):
606
687
 
607
688
  async def async_name_exists(self, name: str) -> bool:
608
689
  raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
690
+
691
+ def id_exists(self, id: str) -> bool:
692
+ """Check if a document with the given ID exists in the database"""
693
+ if self.table is None:
694
+ logger.error("Table not initialized")
695
+ return False
696
+
697
+ try:
698
+ # Search for the document with the specific ID
699
+ result = self.table.search().where(f"{self._id} = '{id}'").to_pandas()
700
+ return len(result) > 0
701
+ except Exception as e:
702
+ logger.error(f"Error checking id existence: {e}")
703
+ return False
704
+
705
+ def delete_by_id(self, id: str) -> bool:
706
+ """Delete content by ID."""
707
+ if self.table is None:
708
+ logger.error("Table not initialized")
709
+ return False
710
+
711
+ try:
712
+ # Delete rows where the id matches
713
+ self.table.delete(f"{self._id} = '{id}'")
714
+ log_info(f"Deleted records with id '{id}' from table '{self.table_name}'.")
715
+ return True
716
+ except Exception as e:
717
+ logger.error(f"Error deleting rows by id '{id}': {e}")
718
+ return False
719
+
720
+ def delete_by_name(self, name: str) -> bool:
721
+ """Delete content by name."""
722
+ if self.table is None:
723
+ logger.error("Table not initialized")
724
+ return False
725
+
726
+ try:
727
+ total_count = self.table.count_rows()
728
+ result = self.table.search().select(["id", "payload"]).limit(total_count).to_pandas()
729
+
730
+ # Find matching IDs
731
+ ids_to_delete = []
732
+ for _, row in result.iterrows():
733
+ payload = json.loads(row["payload"])
734
+ if payload.get("name") == name:
735
+ ids_to_delete.append(row["id"])
736
+
737
+ # Delete matching records
738
+ if ids_to_delete:
739
+ for doc_id in ids_to_delete:
740
+ self.table.delete(f"{self._id} = '{doc_id}'")
741
+ log_info(f"Deleted {len(ids_to_delete)} records with name '{name}' from table '{self.table_name}'.")
742
+ return True
743
+ else:
744
+ log_info(f"No records found with name '{name}' to delete.")
745
+ return False
746
+
747
+ except Exception as e:
748
+ logger.error(f"Error deleting rows by name '{name}': {e}")
749
+ return False
750
+
751
+ def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
752
+ """Delete content by metadata."""
753
+ if self.table is None:
754
+ logger.error("Table not initialized")
755
+ return False
756
+
757
+ try:
758
+ total_count = self.table.count_rows()
759
+ result = self.table.search().select(["id", "payload"]).limit(total_count).to_pandas()
760
+
761
+ # Find matching IDs
762
+ ids_to_delete = []
763
+ for _, row in result.iterrows():
764
+ payload = json.loads(row["payload"])
765
+ doc_metadata = payload.get("meta_data", {})
766
+
767
+ # Check if all metadata key-value pairs match
768
+ match = True
769
+ for key, value in metadata.items():
770
+ if key not in doc_metadata or doc_metadata[key] != value:
771
+ match = False
772
+ break
773
+
774
+ if match:
775
+ ids_to_delete.append(row["id"])
776
+
777
+ # Delete matching records
778
+ if ids_to_delete:
779
+ for doc_id in ids_to_delete:
780
+ self.table.delete(f"{self._id} = '{doc_id}'")
781
+ log_info(
782
+ f"Deleted {len(ids_to_delete)} records with metadata '{metadata}' from table '{self.table_name}'."
783
+ )
784
+ return True
785
+ else:
786
+ log_info(f"No records found with metadata '{metadata}' to delete.")
787
+ return False
788
+
789
+ except Exception as e:
790
+ logger.error(f"Error deleting rows by metadata '{metadata}': {e}")
791
+ return False
792
+
793
+ def delete_by_content_id(self, content_id: str) -> bool:
794
+ """Delete content by content ID."""
795
+ if self.table is None:
796
+ logger.error("Table not initialized")
797
+ return False
798
+
799
+ try:
800
+ total_count = self.table.count_rows()
801
+ result = self.table.search().select(["id", "payload"]).limit(total_count).to_pandas()
802
+
803
+ # Find matching IDs
804
+ ids_to_delete = []
805
+ for _, row in result.iterrows():
806
+ payload = json.loads(row["payload"])
807
+ if payload.get("content_id") == content_id:
808
+ ids_to_delete.append(row["id"])
809
+
810
+ # Delete matching records
811
+ if ids_to_delete:
812
+ for doc_id in ids_to_delete:
813
+ self.table.delete(f"{self._id} = '{doc_id}'")
814
+ log_info(
815
+ f"Deleted {len(ids_to_delete)} records with content_id '{content_id}' from table '{self.table_name}'."
816
+ )
817
+ return True
818
+ else:
819
+ log_info(f"No records found with content_id '{content_id}' to delete.")
820
+ return False
821
+
822
+ except Exception as e:
823
+ logger.error(f"Error deleting rows by content_id '{content_id}': {e}")
824
+ return False
825
+
826
+ def _delete_by_content_hash(self, content_hash: str) -> bool:
827
+ """Delete content by content hash."""
828
+ if self.table is None:
829
+ logger.error("Table not initialized")
830
+ return False
831
+
832
+ try:
833
+ total_count = self.table.count_rows()
834
+ result = self.table.search().select(["id", "payload"]).limit(total_count).to_pandas()
835
+
836
+ # Find matching IDs
837
+ ids_to_delete = []
838
+ for _, row in result.iterrows():
839
+ payload = json.loads(row["payload"])
840
+ if payload.get("content_hash") == content_hash:
841
+ ids_to_delete.append(row["id"])
842
+
843
+ # Delete matching records
844
+ if ids_to_delete:
845
+ for doc_id in ids_to_delete:
846
+ self.table.delete(f"{self._id} = '{doc_id}'")
847
+ log_info(
848
+ f"Deleted {len(ids_to_delete)} records with content_hash '{content_hash}' from table '{self.table_name}'."
849
+ )
850
+ return True
851
+ else:
852
+ log_info(f"No records found with content_hash '{content_hash}' to delete.")
853
+ return False
854
+
855
+ except Exception as e:
856
+ logger.error(f"Error deleting rows by content_hash '{content_hash}': {e}")
857
+ return False
858
+
859
+ def content_hash_exists(self, content_hash: str) -> bool:
860
+ """Check if documents with the given content hash exist."""
861
+ if self.table is None:
862
+ logger.error("Table not initialized")
863
+ return False
864
+
865
+ try:
866
+ total_count = self.table.count_rows()
867
+ result = self.table.search().select(["id", "payload"]).limit(total_count).to_pandas()
868
+
869
+ # Check if any records match the content_hash
870
+ for _, row in result.iterrows():
871
+ payload = json.loads(row["payload"])
872
+ if payload.get("content_hash") == content_hash:
873
+ return True
874
+
875
+ return False
876
+
877
+ except Exception as e:
878
+ logger.error(f"Error checking content_hash existence '{content_hash}': {e}")
879
+ return False
880
+
881
+ def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
882
+ """
883
+ Update the metadata for documents with the given content_id.
884
+
885
+ Args:
886
+ content_id (str): The content ID to update
887
+ metadata (Dict[str, Any]): The metadata to update
888
+ """
889
+ import json
890
+
891
+ try:
892
+ if self.table is None:
893
+ logger.error("Table not initialized")
894
+ return
895
+
896
+ # Search for documents with the given content_id
897
+ query_filter = f"payload->>'content_id' = '{content_id}'"
898
+ results = self.table.search().where(query_filter).to_pandas()
899
+
900
+ if results.empty:
901
+ logger.debug(f"No documents found with content_id: {content_id}")
902
+ return
903
+
904
+ # Update each matching document
905
+ updated_count = 0
906
+ for _, row in results.iterrows():
907
+ row_id = row["id"]
908
+ current_payload = json.loads(row["payload"])
909
+
910
+ # Merge existing metadata with new metadata
911
+ if "meta_data" in current_payload:
912
+ current_payload["meta_data"].update(metadata)
913
+ else:
914
+ current_payload["meta_data"] = metadata
915
+
916
+ if "filters" in current_payload:
917
+ if isinstance(current_payload["filters"], dict):
918
+ current_payload["filters"].update(metadata)
919
+ else:
920
+ current_payload["filters"] = metadata
921
+ else:
922
+ current_payload["filters"] = metadata
923
+
924
+ # Update the document
925
+ update_data = {"id": row_id, "payload": json.dumps(current_payload)}
926
+
927
+ # LanceDB doesn't have a direct update, so we need to delete and re-insert
928
+ # First, get all the existing data
929
+ vector_data = row["vector"] if "vector" in row else None
930
+ text_data = row["text"] if "text" in row else None
931
+
932
+ # Create complete update record
933
+ if vector_data is not None:
934
+ update_data["vector"] = vector_data
935
+ if text_data is not None:
936
+ update_data["text"] = text_data
937
+
938
+ # Delete old record and insert updated one
939
+ self.table.delete(f"id = '{row_id}'")
940
+ self.table.add([update_data])
941
+ updated_count += 1
942
+
943
+ logger.debug(f"Updated metadata for {updated_count} documents with content_id: {content_id}")
944
+
945
+ except Exception as e:
946
+ logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
947
+ raise
@@ -0,0 +1,5 @@
1
+ from agno.vectordb.langchaindb.langchaindb import LangChainVectorDb
2
+
3
+ __all__ = [
4
+ "LangChainVectorDb",
5
+ ]
@@ -0,0 +1,145 @@
1
+ from typing import Any, Dict, List, Optional
2
+
3
+ from agno.knowledge.document import Document
4
+ from agno.utils.log import log_debug, logger
5
+ from agno.vectordb.base import VectorDb
6
+
7
+
8
+ class LangChainVectorDb(VectorDb):
9
+ def __init__(
10
+ self,
11
+ vectorstore: Optional[Any] = None,
12
+ search_kwargs: Optional[dict] = None,
13
+ knowledge_retriever: Optional[Any] = None,
14
+ ):
15
+ """
16
+ Initialize LangChainVectorDb.
17
+
18
+ Args:
19
+ vectorstore: The LangChain vectorstore instance
20
+ search_kwargs: Additional search parameters for the retriever
21
+ knowledge_retriever: An optional LangChain retriever instance
22
+ """
23
+ self.vectorstore = vectorstore
24
+ self.search_kwargs = search_kwargs
25
+ self.knowledge_retriever = knowledge_retriever
26
+
27
+ def create(self) -> None:
28
+ raise NotImplementedError
29
+
30
+ async def async_create(self) -> None:
31
+ raise NotImplementedError
32
+
33
+ def name_exists(self, name: str) -> bool:
34
+ raise NotImplementedError
35
+
36
+ def async_name_exists(self, name: str) -> bool:
37
+ raise NotImplementedError
38
+
39
+ def id_exists(self, id: str) -> bool:
40
+ raise NotImplementedError
41
+
42
+ def content_hash_exists(self, content_hash: str) -> bool:
43
+ raise NotImplementedError
44
+
45
+ def delete_by_content_id(self, content_id: str) -> None:
46
+ raise NotImplementedError
47
+
48
+ def insert(self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
49
+ logger.warning("LangChainKnowledgeBase.insert() not supported - please check the vectorstore manually.")
50
+ raise NotImplementedError
51
+
52
+ async def async_insert(
53
+ self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
54
+ ) -> None:
55
+ logger.warning("LangChainKnowledgeBase.async_insert() not supported - please check the vectorstore manually.")
56
+ raise NotImplementedError
57
+
58
+ def upsert(self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
59
+ logger.warning("LangChainKnowledgeBase.upsert() not supported - please check the vectorstore manually.")
60
+ raise NotImplementedError
61
+
62
+ async def async_upsert(self, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
63
+ logger.warning("LangChainKnowledgeBase.async_upsert() not supported - please check the vectorstore manually.")
64
+ raise NotImplementedError
65
+
66
+ def search(
67
+ self, query: str, num_documents: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
68
+ ) -> List[Document]:
69
+ """Returns relevant documents matching the query"""
70
+
71
+ try:
72
+ from langchain_core.documents import Document as LangChainDocument
73
+ from langchain_core.retrievers import BaseRetriever
74
+ except ImportError:
75
+ raise ImportError(
76
+ "The `langchain` package is not installed. Please install it via `pip install langchain`."
77
+ )
78
+
79
+ if self.vectorstore is not None and self.knowledge_retriever is None:
80
+ log_debug("Creating knowledge retriever")
81
+ if self.search_kwargs is None:
82
+ self.search_kwargs = {"k": num_documents}
83
+ if filters is not None:
84
+ self.search_kwargs.update(filters)
85
+ self.knowledge_retriever = self.vectorstore.as_retriever(search_kwargs=self.search_kwargs)
86
+
87
+ if self.knowledge_retriever is None:
88
+ logger.error("No knowledge retriever provided")
89
+ return []
90
+
91
+ if not isinstance(self.knowledge_retriever, BaseRetriever):
92
+ raise ValueError(f"Knowledge retriever is not of type BaseRetriever: {self.knowledge_retriever}")
93
+
94
+ log_debug(f"Getting {num_documents} relevant documents for query: {query}")
95
+ lc_documents: List[LangChainDocument] = self.knowledge_retriever.invoke(input=query)
96
+ documents = []
97
+ for lc_doc in lc_documents:
98
+ documents.append(
99
+ Document(
100
+ content=lc_doc.page_content,
101
+ meta_data=lc_doc.metadata,
102
+ )
103
+ )
104
+ return documents
105
+
106
+ async def async_search(
107
+ self, query: str, num_documents: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
108
+ ) -> List[Document]:
109
+ return self.search(query, num_documents, filters)
110
+
111
+ def drop(self) -> None:
112
+ raise NotImplementedError
113
+
114
+ async def async_drop(self) -> None:
115
+ raise NotImplementedError
116
+
117
+ async def async_exists(self) -> bool:
118
+ raise NotImplementedError
119
+
120
+ def delete(self) -> bool:
121
+ raise NotImplementedError
122
+
123
+ def delete_by_id(self, id: str) -> bool:
124
+ raise NotImplementedError
125
+
126
+ def delete_by_name(self, name: str) -> bool:
127
+ raise NotImplementedError
128
+
129
+ def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
130
+ raise NotImplementedError
131
+
132
+ def exists(self) -> bool:
133
+ logger.warning("LangChainKnowledgeBase.exists() not supported - please check the vectorstore manually.")
134
+ return True
135
+
136
+ def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
137
+ """
138
+ Update the metadata for documents with the given content_id.
139
+ Not implemented for LangChain wrapper.
140
+
141
+ Args:
142
+ content_id (str): The content ID to update
143
+ metadata (Dict[str, Any]): The metadata to update
144
+ """
145
+ raise NotImplementedError("update_metadata not supported for LangChain vectorstores")
@@ -0,0 +1,5 @@
1
+ from agno.vectordb.lightrag.lightrag import LightRag
2
+
3
+ __all__ = [
4
+ "LightRag",
5
+ ]