agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (723) hide show
  1. agno/__init__.py +8 -0
  2. agno/agent/__init__.py +44 -5
  3. agno/agent/agent.py +10531 -2975
  4. agno/api/agent.py +14 -53
  5. agno/api/api.py +7 -46
  6. agno/api/evals.py +22 -0
  7. agno/api/os.py +17 -0
  8. agno/api/routes.py +6 -25
  9. agno/api/schemas/__init__.py +9 -0
  10. agno/api/schemas/agent.py +6 -9
  11. agno/api/schemas/evals.py +16 -0
  12. agno/api/schemas/os.py +14 -0
  13. agno/api/schemas/team.py +10 -10
  14. agno/api/schemas/utils.py +21 -0
  15. agno/api/schemas/workflows.py +16 -0
  16. agno/api/settings.py +53 -0
  17. agno/api/team.py +22 -26
  18. agno/api/workflow.py +28 -0
  19. agno/cloud/aws/base.py +214 -0
  20. agno/cloud/aws/s3/__init__.py +2 -0
  21. agno/cloud/aws/s3/api_client.py +43 -0
  22. agno/cloud/aws/s3/bucket.py +195 -0
  23. agno/cloud/aws/s3/object.py +57 -0
  24. agno/compression/__init__.py +3 -0
  25. agno/compression/manager.py +247 -0
  26. agno/culture/__init__.py +3 -0
  27. agno/culture/manager.py +956 -0
  28. agno/db/__init__.py +24 -0
  29. agno/db/async_postgres/__init__.py +3 -0
  30. agno/db/base.py +946 -0
  31. agno/db/dynamo/__init__.py +3 -0
  32. agno/db/dynamo/dynamo.py +2781 -0
  33. agno/db/dynamo/schemas.py +442 -0
  34. agno/db/dynamo/utils.py +743 -0
  35. agno/db/firestore/__init__.py +3 -0
  36. agno/db/firestore/firestore.py +2379 -0
  37. agno/db/firestore/schemas.py +181 -0
  38. agno/db/firestore/utils.py +376 -0
  39. agno/db/gcs_json/__init__.py +3 -0
  40. agno/db/gcs_json/gcs_json_db.py +1791 -0
  41. agno/db/gcs_json/utils.py +228 -0
  42. agno/db/in_memory/__init__.py +3 -0
  43. agno/db/in_memory/in_memory_db.py +1312 -0
  44. agno/db/in_memory/utils.py +230 -0
  45. agno/db/json/__init__.py +3 -0
  46. agno/db/json/json_db.py +1777 -0
  47. agno/db/json/utils.py +230 -0
  48. agno/db/migrations/manager.py +199 -0
  49. agno/db/migrations/v1_to_v2.py +635 -0
  50. agno/db/migrations/versions/v2_3_0.py +938 -0
  51. agno/db/mongo/__init__.py +17 -0
  52. agno/db/mongo/async_mongo.py +2760 -0
  53. agno/db/mongo/mongo.py +2597 -0
  54. agno/db/mongo/schemas.py +119 -0
  55. agno/db/mongo/utils.py +276 -0
  56. agno/db/mysql/__init__.py +4 -0
  57. agno/db/mysql/async_mysql.py +2912 -0
  58. agno/db/mysql/mysql.py +2923 -0
  59. agno/db/mysql/schemas.py +186 -0
  60. agno/db/mysql/utils.py +488 -0
  61. agno/db/postgres/__init__.py +4 -0
  62. agno/db/postgres/async_postgres.py +2579 -0
  63. agno/db/postgres/postgres.py +2870 -0
  64. agno/db/postgres/schemas.py +187 -0
  65. agno/db/postgres/utils.py +442 -0
  66. agno/db/redis/__init__.py +3 -0
  67. agno/db/redis/redis.py +2141 -0
  68. agno/db/redis/schemas.py +159 -0
  69. agno/db/redis/utils.py +346 -0
  70. agno/db/schemas/__init__.py +4 -0
  71. agno/db/schemas/culture.py +120 -0
  72. agno/db/schemas/evals.py +34 -0
  73. agno/db/schemas/knowledge.py +40 -0
  74. agno/db/schemas/memory.py +61 -0
  75. agno/db/singlestore/__init__.py +3 -0
  76. agno/db/singlestore/schemas.py +179 -0
  77. agno/db/singlestore/singlestore.py +2877 -0
  78. agno/db/singlestore/utils.py +384 -0
  79. agno/db/sqlite/__init__.py +4 -0
  80. agno/db/sqlite/async_sqlite.py +2911 -0
  81. agno/db/sqlite/schemas.py +181 -0
  82. agno/db/sqlite/sqlite.py +2908 -0
  83. agno/db/sqlite/utils.py +429 -0
  84. agno/db/surrealdb/__init__.py +3 -0
  85. agno/db/surrealdb/metrics.py +292 -0
  86. agno/db/surrealdb/models.py +334 -0
  87. agno/db/surrealdb/queries.py +71 -0
  88. agno/db/surrealdb/surrealdb.py +1908 -0
  89. agno/db/surrealdb/utils.py +147 -0
  90. agno/db/utils.py +118 -0
  91. agno/eval/__init__.py +24 -0
  92. agno/eval/accuracy.py +666 -276
  93. agno/eval/agent_as_judge.py +861 -0
  94. agno/eval/base.py +29 -0
  95. agno/eval/performance.py +779 -0
  96. agno/eval/reliability.py +241 -62
  97. agno/eval/utils.py +120 -0
  98. agno/exceptions.py +143 -1
  99. agno/filters.py +354 -0
  100. agno/guardrails/__init__.py +6 -0
  101. agno/guardrails/base.py +19 -0
  102. agno/guardrails/openai.py +144 -0
  103. agno/guardrails/pii.py +94 -0
  104. agno/guardrails/prompt_injection.py +52 -0
  105. agno/hooks/__init__.py +3 -0
  106. agno/hooks/decorator.py +164 -0
  107. agno/integrations/discord/__init__.py +3 -0
  108. agno/integrations/discord/client.py +203 -0
  109. agno/knowledge/__init__.py +5 -1
  110. agno/{document → knowledge}/chunking/agentic.py +22 -14
  111. agno/{document → knowledge}/chunking/document.py +2 -2
  112. agno/{document → knowledge}/chunking/fixed.py +7 -6
  113. agno/knowledge/chunking/markdown.py +151 -0
  114. agno/{document → knowledge}/chunking/recursive.py +15 -3
  115. agno/knowledge/chunking/row.py +39 -0
  116. agno/knowledge/chunking/semantic.py +91 -0
  117. agno/knowledge/chunking/strategy.py +165 -0
  118. agno/knowledge/content.py +74 -0
  119. agno/knowledge/document/__init__.py +5 -0
  120. agno/{document → knowledge/document}/base.py +12 -2
  121. agno/knowledge/embedder/__init__.py +5 -0
  122. agno/knowledge/embedder/aws_bedrock.py +343 -0
  123. agno/knowledge/embedder/azure_openai.py +210 -0
  124. agno/{embedder → knowledge/embedder}/base.py +8 -0
  125. agno/knowledge/embedder/cohere.py +323 -0
  126. agno/knowledge/embedder/fastembed.py +62 -0
  127. agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
  128. agno/knowledge/embedder/google.py +258 -0
  129. agno/knowledge/embedder/huggingface.py +94 -0
  130. agno/knowledge/embedder/jina.py +182 -0
  131. agno/knowledge/embedder/langdb.py +22 -0
  132. agno/knowledge/embedder/mistral.py +206 -0
  133. agno/knowledge/embedder/nebius.py +13 -0
  134. agno/knowledge/embedder/ollama.py +154 -0
  135. agno/knowledge/embedder/openai.py +195 -0
  136. agno/knowledge/embedder/sentence_transformer.py +63 -0
  137. agno/{embedder → knowledge/embedder}/together.py +1 -1
  138. agno/knowledge/embedder/vllm.py +262 -0
  139. agno/knowledge/embedder/voyageai.py +165 -0
  140. agno/knowledge/knowledge.py +3006 -0
  141. agno/knowledge/reader/__init__.py +7 -0
  142. agno/knowledge/reader/arxiv_reader.py +81 -0
  143. agno/knowledge/reader/base.py +95 -0
  144. agno/knowledge/reader/csv_reader.py +164 -0
  145. agno/knowledge/reader/docx_reader.py +82 -0
  146. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  147. agno/knowledge/reader/firecrawl_reader.py +201 -0
  148. agno/knowledge/reader/json_reader.py +88 -0
  149. agno/knowledge/reader/markdown_reader.py +137 -0
  150. agno/knowledge/reader/pdf_reader.py +431 -0
  151. agno/knowledge/reader/pptx_reader.py +101 -0
  152. agno/knowledge/reader/reader_factory.py +313 -0
  153. agno/knowledge/reader/s3_reader.py +89 -0
  154. agno/knowledge/reader/tavily_reader.py +193 -0
  155. agno/knowledge/reader/text_reader.py +127 -0
  156. agno/knowledge/reader/web_search_reader.py +325 -0
  157. agno/knowledge/reader/website_reader.py +455 -0
  158. agno/knowledge/reader/wikipedia_reader.py +91 -0
  159. agno/knowledge/reader/youtube_reader.py +78 -0
  160. agno/knowledge/remote_content/remote_content.py +88 -0
  161. agno/knowledge/reranker/__init__.py +3 -0
  162. agno/{reranker → knowledge/reranker}/base.py +1 -1
  163. agno/{reranker → knowledge/reranker}/cohere.py +2 -2
  164. agno/knowledge/reranker/infinity.py +195 -0
  165. agno/knowledge/reranker/sentence_transformer.py +54 -0
  166. agno/knowledge/types.py +39 -0
  167. agno/knowledge/utils.py +234 -0
  168. agno/media.py +439 -95
  169. agno/memory/__init__.py +16 -3
  170. agno/memory/manager.py +1474 -123
  171. agno/memory/strategies/__init__.py +15 -0
  172. agno/memory/strategies/base.py +66 -0
  173. agno/memory/strategies/summarize.py +196 -0
  174. agno/memory/strategies/types.py +37 -0
  175. agno/models/aimlapi/__init__.py +5 -0
  176. agno/models/aimlapi/aimlapi.py +62 -0
  177. agno/models/anthropic/__init__.py +4 -0
  178. agno/models/anthropic/claude.py +960 -496
  179. agno/models/aws/__init__.py +15 -0
  180. agno/models/aws/bedrock.py +686 -451
  181. agno/models/aws/claude.py +190 -183
  182. agno/models/azure/__init__.py +18 -1
  183. agno/models/azure/ai_foundry.py +489 -0
  184. agno/models/azure/openai_chat.py +89 -40
  185. agno/models/base.py +2477 -550
  186. agno/models/cerebras/__init__.py +12 -0
  187. agno/models/cerebras/cerebras.py +565 -0
  188. agno/models/cerebras/cerebras_openai.py +131 -0
  189. agno/models/cohere/__init__.py +4 -0
  190. agno/models/cohere/chat.py +306 -492
  191. agno/models/cometapi/__init__.py +5 -0
  192. agno/models/cometapi/cometapi.py +74 -0
  193. agno/models/dashscope/__init__.py +5 -0
  194. agno/models/dashscope/dashscope.py +90 -0
  195. agno/models/deepinfra/__init__.py +5 -0
  196. agno/models/deepinfra/deepinfra.py +45 -0
  197. agno/models/deepseek/__init__.py +4 -0
  198. agno/models/deepseek/deepseek.py +110 -9
  199. agno/models/fireworks/__init__.py +4 -0
  200. agno/models/fireworks/fireworks.py +19 -22
  201. agno/models/google/__init__.py +3 -7
  202. agno/models/google/gemini.py +1717 -662
  203. agno/models/google/utils.py +22 -0
  204. agno/models/groq/__init__.py +4 -0
  205. agno/models/groq/groq.py +391 -666
  206. agno/models/huggingface/__init__.py +4 -0
  207. agno/models/huggingface/huggingface.py +266 -538
  208. agno/models/ibm/__init__.py +5 -0
  209. agno/models/ibm/watsonx.py +432 -0
  210. agno/models/internlm/__init__.py +3 -0
  211. agno/models/internlm/internlm.py +20 -3
  212. agno/models/langdb/__init__.py +1 -0
  213. agno/models/langdb/langdb.py +60 -0
  214. agno/models/litellm/__init__.py +14 -0
  215. agno/models/litellm/chat.py +503 -0
  216. agno/models/litellm/litellm_openai.py +42 -0
  217. agno/models/llama_cpp/__init__.py +5 -0
  218. agno/models/llama_cpp/llama_cpp.py +22 -0
  219. agno/models/lmstudio/__init__.py +5 -0
  220. agno/models/lmstudio/lmstudio.py +25 -0
  221. agno/models/message.py +361 -39
  222. agno/models/meta/__init__.py +12 -0
  223. agno/models/meta/llama.py +502 -0
  224. agno/models/meta/llama_openai.py +79 -0
  225. agno/models/metrics.py +120 -0
  226. agno/models/mistral/__init__.py +4 -0
  227. agno/models/mistral/mistral.py +293 -393
  228. agno/models/nebius/__init__.py +3 -0
  229. agno/models/nebius/nebius.py +53 -0
  230. agno/models/nexus/__init__.py +3 -0
  231. agno/models/nexus/nexus.py +22 -0
  232. agno/models/nvidia/__init__.py +4 -0
  233. agno/models/nvidia/nvidia.py +22 -3
  234. agno/models/ollama/__init__.py +4 -2
  235. agno/models/ollama/chat.py +257 -492
  236. agno/models/openai/__init__.py +7 -0
  237. agno/models/openai/chat.py +725 -770
  238. agno/models/openai/like.py +16 -2
  239. agno/models/openai/responses.py +1121 -0
  240. agno/models/openrouter/__init__.py +4 -0
  241. agno/models/openrouter/openrouter.py +62 -5
  242. agno/models/perplexity/__init__.py +5 -0
  243. agno/models/perplexity/perplexity.py +203 -0
  244. agno/models/portkey/__init__.py +3 -0
  245. agno/models/portkey/portkey.py +82 -0
  246. agno/models/requesty/__init__.py +5 -0
  247. agno/models/requesty/requesty.py +69 -0
  248. agno/models/response.py +177 -7
  249. agno/models/sambanova/__init__.py +4 -0
  250. agno/models/sambanova/sambanova.py +23 -4
  251. agno/models/siliconflow/__init__.py +5 -0
  252. agno/models/siliconflow/siliconflow.py +42 -0
  253. agno/models/together/__init__.py +4 -0
  254. agno/models/together/together.py +21 -164
  255. agno/models/utils.py +266 -0
  256. agno/models/vercel/__init__.py +3 -0
  257. agno/models/vercel/v0.py +43 -0
  258. agno/models/vertexai/__init__.py +0 -1
  259. agno/models/vertexai/claude.py +190 -0
  260. agno/models/vllm/__init__.py +3 -0
  261. agno/models/vllm/vllm.py +83 -0
  262. agno/models/xai/__init__.py +2 -0
  263. agno/models/xai/xai.py +111 -7
  264. agno/os/__init__.py +3 -0
  265. agno/os/app.py +1027 -0
  266. agno/os/auth.py +244 -0
  267. agno/os/config.py +126 -0
  268. agno/os/interfaces/__init__.py +1 -0
  269. agno/os/interfaces/a2a/__init__.py +3 -0
  270. agno/os/interfaces/a2a/a2a.py +42 -0
  271. agno/os/interfaces/a2a/router.py +249 -0
  272. agno/os/interfaces/a2a/utils.py +924 -0
  273. agno/os/interfaces/agui/__init__.py +3 -0
  274. agno/os/interfaces/agui/agui.py +47 -0
  275. agno/os/interfaces/agui/router.py +147 -0
  276. agno/os/interfaces/agui/utils.py +574 -0
  277. agno/os/interfaces/base.py +25 -0
  278. agno/os/interfaces/slack/__init__.py +3 -0
  279. agno/os/interfaces/slack/router.py +148 -0
  280. agno/os/interfaces/slack/security.py +30 -0
  281. agno/os/interfaces/slack/slack.py +47 -0
  282. agno/os/interfaces/whatsapp/__init__.py +3 -0
  283. agno/os/interfaces/whatsapp/router.py +210 -0
  284. agno/os/interfaces/whatsapp/security.py +55 -0
  285. agno/os/interfaces/whatsapp/whatsapp.py +36 -0
  286. agno/os/mcp.py +293 -0
  287. agno/os/middleware/__init__.py +9 -0
  288. agno/os/middleware/jwt.py +797 -0
  289. agno/os/router.py +258 -0
  290. agno/os/routers/__init__.py +3 -0
  291. agno/os/routers/agents/__init__.py +3 -0
  292. agno/os/routers/agents/router.py +599 -0
  293. agno/os/routers/agents/schema.py +261 -0
  294. agno/os/routers/evals/__init__.py +3 -0
  295. agno/os/routers/evals/evals.py +450 -0
  296. agno/os/routers/evals/schemas.py +174 -0
  297. agno/os/routers/evals/utils.py +231 -0
  298. agno/os/routers/health.py +31 -0
  299. agno/os/routers/home.py +52 -0
  300. agno/os/routers/knowledge/__init__.py +3 -0
  301. agno/os/routers/knowledge/knowledge.py +1008 -0
  302. agno/os/routers/knowledge/schemas.py +178 -0
  303. agno/os/routers/memory/__init__.py +3 -0
  304. agno/os/routers/memory/memory.py +661 -0
  305. agno/os/routers/memory/schemas.py +88 -0
  306. agno/os/routers/metrics/__init__.py +3 -0
  307. agno/os/routers/metrics/metrics.py +190 -0
  308. agno/os/routers/metrics/schemas.py +47 -0
  309. agno/os/routers/session/__init__.py +3 -0
  310. agno/os/routers/session/session.py +997 -0
  311. agno/os/routers/teams/__init__.py +3 -0
  312. agno/os/routers/teams/router.py +512 -0
  313. agno/os/routers/teams/schema.py +257 -0
  314. agno/os/routers/traces/__init__.py +3 -0
  315. agno/os/routers/traces/schemas.py +414 -0
  316. agno/os/routers/traces/traces.py +499 -0
  317. agno/os/routers/workflows/__init__.py +3 -0
  318. agno/os/routers/workflows/router.py +624 -0
  319. agno/os/routers/workflows/schema.py +75 -0
  320. agno/os/schema.py +534 -0
  321. agno/os/scopes.py +469 -0
  322. agno/{playground → os}/settings.py +7 -15
  323. agno/os/utils.py +973 -0
  324. agno/reasoning/anthropic.py +80 -0
  325. agno/reasoning/azure_ai_foundry.py +67 -0
  326. agno/reasoning/deepseek.py +63 -0
  327. agno/reasoning/default.py +97 -0
  328. agno/reasoning/gemini.py +73 -0
  329. agno/reasoning/groq.py +71 -0
  330. agno/reasoning/helpers.py +24 -1
  331. agno/reasoning/ollama.py +67 -0
  332. agno/reasoning/openai.py +86 -0
  333. agno/reasoning/step.py +2 -1
  334. agno/reasoning/vertexai.py +76 -0
  335. agno/run/__init__.py +6 -0
  336. agno/run/agent.py +822 -0
  337. agno/run/base.py +247 -0
  338. agno/run/cancel.py +81 -0
  339. agno/run/requirement.py +181 -0
  340. agno/run/team.py +767 -0
  341. agno/run/workflow.py +708 -0
  342. agno/session/__init__.py +10 -0
  343. agno/session/agent.py +260 -0
  344. agno/session/summary.py +265 -0
  345. agno/session/team.py +342 -0
  346. agno/session/workflow.py +501 -0
  347. agno/table.py +10 -0
  348. agno/team/__init__.py +37 -0
  349. agno/team/team.py +9536 -0
  350. agno/tools/__init__.py +7 -0
  351. agno/tools/agentql.py +120 -0
  352. agno/tools/airflow.py +22 -12
  353. agno/tools/api.py +122 -0
  354. agno/tools/apify.py +276 -83
  355. agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
  356. agno/tools/aws_lambda.py +28 -7
  357. agno/tools/aws_ses.py +66 -0
  358. agno/tools/baidusearch.py +11 -4
  359. agno/tools/bitbucket.py +292 -0
  360. agno/tools/brandfetch.py +213 -0
  361. agno/tools/bravesearch.py +106 -0
  362. agno/tools/brightdata.py +367 -0
  363. agno/tools/browserbase.py +209 -0
  364. agno/tools/calcom.py +32 -23
  365. agno/tools/calculator.py +24 -37
  366. agno/tools/cartesia.py +187 -0
  367. agno/tools/{clickup_tool.py → clickup.py} +17 -28
  368. agno/tools/confluence.py +91 -26
  369. agno/tools/crawl4ai.py +139 -43
  370. agno/tools/csv_toolkit.py +28 -22
  371. agno/tools/dalle.py +36 -22
  372. agno/tools/daytona.py +475 -0
  373. agno/tools/decorator.py +169 -14
  374. agno/tools/desi_vocal.py +23 -11
  375. agno/tools/discord.py +32 -29
  376. agno/tools/docker.py +716 -0
  377. agno/tools/duckdb.py +76 -81
  378. agno/tools/duckduckgo.py +43 -40
  379. agno/tools/e2b.py +703 -0
  380. agno/tools/eleven_labs.py +65 -54
  381. agno/tools/email.py +13 -5
  382. agno/tools/evm.py +129 -0
  383. agno/tools/exa.py +324 -42
  384. agno/tools/fal.py +39 -35
  385. agno/tools/file.py +196 -30
  386. agno/tools/file_generation.py +356 -0
  387. agno/tools/financial_datasets.py +288 -0
  388. agno/tools/firecrawl.py +108 -33
  389. agno/tools/function.py +960 -122
  390. agno/tools/giphy.py +34 -12
  391. agno/tools/github.py +1294 -97
  392. agno/tools/gmail.py +922 -0
  393. agno/tools/google_bigquery.py +117 -0
  394. agno/tools/google_drive.py +271 -0
  395. agno/tools/google_maps.py +253 -0
  396. agno/tools/googlecalendar.py +607 -107
  397. agno/tools/googlesheets.py +377 -0
  398. agno/tools/hackernews.py +20 -12
  399. agno/tools/jina.py +24 -14
  400. agno/tools/jira.py +48 -19
  401. agno/tools/knowledge.py +218 -0
  402. agno/tools/linear.py +82 -43
  403. agno/tools/linkup.py +58 -0
  404. agno/tools/local_file_system.py +15 -7
  405. agno/tools/lumalab.py +41 -26
  406. agno/tools/mcp/__init__.py +10 -0
  407. agno/tools/mcp/mcp.py +331 -0
  408. agno/tools/mcp/multi_mcp.py +347 -0
  409. agno/tools/mcp/params.py +24 -0
  410. agno/tools/mcp_toolbox.py +284 -0
  411. agno/tools/mem0.py +193 -0
  412. agno/tools/memory.py +419 -0
  413. agno/tools/mlx_transcribe.py +11 -9
  414. agno/tools/models/azure_openai.py +190 -0
  415. agno/tools/models/gemini.py +203 -0
  416. agno/tools/models/groq.py +158 -0
  417. agno/tools/models/morph.py +186 -0
  418. agno/tools/models/nebius.py +124 -0
  419. agno/tools/models_labs.py +163 -82
  420. agno/tools/moviepy_video.py +18 -13
  421. agno/tools/nano_banana.py +151 -0
  422. agno/tools/neo4j.py +134 -0
  423. agno/tools/newspaper.py +15 -4
  424. agno/tools/newspaper4k.py +19 -6
  425. agno/tools/notion.py +204 -0
  426. agno/tools/openai.py +181 -17
  427. agno/tools/openbb.py +27 -20
  428. agno/tools/opencv.py +321 -0
  429. agno/tools/openweather.py +233 -0
  430. agno/tools/oxylabs.py +385 -0
  431. agno/tools/pandas.py +25 -15
  432. agno/tools/parallel.py +314 -0
  433. agno/tools/postgres.py +238 -185
  434. agno/tools/pubmed.py +125 -13
  435. agno/tools/python.py +48 -35
  436. agno/tools/reasoning.py +283 -0
  437. agno/tools/reddit.py +207 -29
  438. agno/tools/redshift.py +406 -0
  439. agno/tools/replicate.py +69 -26
  440. agno/tools/resend.py +11 -6
  441. agno/tools/scrapegraph.py +179 -19
  442. agno/tools/searxng.py +23 -31
  443. agno/tools/serpapi.py +15 -10
  444. agno/tools/serper.py +255 -0
  445. agno/tools/shell.py +23 -12
  446. agno/tools/shopify.py +1519 -0
  447. agno/tools/slack.py +56 -14
  448. agno/tools/sleep.py +8 -6
  449. agno/tools/spider.py +35 -11
  450. agno/tools/spotify.py +919 -0
  451. agno/tools/sql.py +34 -19
  452. agno/tools/tavily.py +158 -8
  453. agno/tools/telegram.py +18 -8
  454. agno/tools/todoist.py +218 -0
  455. agno/tools/toolkit.py +134 -9
  456. agno/tools/trafilatura.py +388 -0
  457. agno/tools/trello.py +25 -28
  458. agno/tools/twilio.py +18 -9
  459. agno/tools/user_control_flow.py +78 -0
  460. agno/tools/valyu.py +228 -0
  461. agno/tools/visualization.py +467 -0
  462. agno/tools/webbrowser.py +28 -0
  463. agno/tools/webex.py +76 -0
  464. agno/tools/website.py +23 -19
  465. agno/tools/webtools.py +45 -0
  466. agno/tools/whatsapp.py +286 -0
  467. agno/tools/wikipedia.py +28 -19
  468. agno/tools/workflow.py +285 -0
  469. agno/tools/{twitter.py → x.py} +142 -46
  470. agno/tools/yfinance.py +41 -39
  471. agno/tools/youtube.py +34 -17
  472. agno/tools/zendesk.py +15 -5
  473. agno/tools/zep.py +454 -0
  474. agno/tools/zoom.py +86 -37
  475. agno/tracing/__init__.py +12 -0
  476. agno/tracing/exporter.py +157 -0
  477. agno/tracing/schemas.py +276 -0
  478. agno/tracing/setup.py +111 -0
  479. agno/utils/agent.py +938 -0
  480. agno/utils/audio.py +37 -1
  481. agno/utils/certs.py +27 -0
  482. agno/utils/code_execution.py +11 -0
  483. agno/utils/common.py +103 -20
  484. agno/utils/cryptography.py +22 -0
  485. agno/utils/dttm.py +33 -0
  486. agno/utils/events.py +700 -0
  487. agno/utils/functions.py +107 -37
  488. agno/utils/gemini.py +426 -0
  489. agno/utils/hooks.py +171 -0
  490. agno/utils/http.py +185 -0
  491. agno/utils/json_schema.py +159 -37
  492. agno/utils/knowledge.py +36 -0
  493. agno/utils/location.py +19 -0
  494. agno/utils/log.py +221 -8
  495. agno/utils/mcp.py +214 -0
  496. agno/utils/media.py +335 -14
  497. agno/utils/merge_dict.py +22 -1
  498. agno/utils/message.py +77 -2
  499. agno/utils/models/ai_foundry.py +50 -0
  500. agno/utils/models/claude.py +373 -0
  501. agno/utils/models/cohere.py +94 -0
  502. agno/utils/models/llama.py +85 -0
  503. agno/utils/models/mistral.py +100 -0
  504. agno/utils/models/openai_responses.py +140 -0
  505. agno/utils/models/schema_utils.py +153 -0
  506. agno/utils/models/watsonx.py +41 -0
  507. agno/utils/openai.py +257 -0
  508. agno/utils/pickle.py +1 -1
  509. agno/utils/pprint.py +124 -8
  510. agno/utils/print_response/agent.py +930 -0
  511. agno/utils/print_response/team.py +1914 -0
  512. agno/utils/print_response/workflow.py +1668 -0
  513. agno/utils/prompts.py +111 -0
  514. agno/utils/reasoning.py +108 -0
  515. agno/utils/response.py +163 -0
  516. agno/utils/serialize.py +32 -0
  517. agno/utils/shell.py +4 -4
  518. agno/utils/streamlit.py +487 -0
  519. agno/utils/string.py +204 -51
  520. agno/utils/team.py +139 -0
  521. agno/utils/timer.py +9 -2
  522. agno/utils/tokens.py +657 -0
  523. agno/utils/tools.py +19 -1
  524. agno/utils/whatsapp.py +305 -0
  525. agno/utils/yaml_io.py +3 -3
  526. agno/vectordb/__init__.py +2 -0
  527. agno/vectordb/base.py +87 -9
  528. agno/vectordb/cassandra/__init__.py +5 -1
  529. agno/vectordb/cassandra/cassandra.py +383 -27
  530. agno/vectordb/chroma/__init__.py +4 -0
  531. agno/vectordb/chroma/chromadb.py +748 -83
  532. agno/vectordb/clickhouse/__init__.py +7 -1
  533. agno/vectordb/clickhouse/clickhousedb.py +554 -53
  534. agno/vectordb/couchbase/__init__.py +3 -0
  535. agno/vectordb/couchbase/couchbase.py +1446 -0
  536. agno/vectordb/lancedb/__init__.py +5 -0
  537. agno/vectordb/lancedb/lance_db.py +730 -98
  538. agno/vectordb/langchaindb/__init__.py +5 -0
  539. agno/vectordb/langchaindb/langchaindb.py +163 -0
  540. agno/vectordb/lightrag/__init__.py +5 -0
  541. agno/vectordb/lightrag/lightrag.py +388 -0
  542. agno/vectordb/llamaindex/__init__.py +3 -0
  543. agno/vectordb/llamaindex/llamaindexdb.py +166 -0
  544. agno/vectordb/milvus/__init__.py +3 -0
  545. agno/vectordb/milvus/milvus.py +966 -78
  546. agno/vectordb/mongodb/__init__.py +9 -1
  547. agno/vectordb/mongodb/mongodb.py +1175 -172
  548. agno/vectordb/pgvector/__init__.py +8 -0
  549. agno/vectordb/pgvector/pgvector.py +599 -115
  550. agno/vectordb/pineconedb/__init__.py +5 -1
  551. agno/vectordb/pineconedb/pineconedb.py +406 -43
  552. agno/vectordb/qdrant/__init__.py +4 -0
  553. agno/vectordb/qdrant/qdrant.py +914 -61
  554. agno/vectordb/redis/__init__.py +9 -0
  555. agno/vectordb/redis/redisdb.py +682 -0
  556. agno/vectordb/singlestore/__init__.py +8 -1
  557. agno/vectordb/singlestore/singlestore.py +771 -0
  558. agno/vectordb/surrealdb/__init__.py +3 -0
  559. agno/vectordb/surrealdb/surrealdb.py +663 -0
  560. agno/vectordb/upstashdb/__init__.py +5 -0
  561. agno/vectordb/upstashdb/upstashdb.py +718 -0
  562. agno/vectordb/weaviate/__init__.py +8 -0
  563. agno/vectordb/weaviate/index.py +15 -0
  564. agno/vectordb/weaviate/weaviate.py +1009 -0
  565. agno/workflow/__init__.py +23 -1
  566. agno/workflow/agent.py +299 -0
  567. agno/workflow/condition.py +759 -0
  568. agno/workflow/loop.py +756 -0
  569. agno/workflow/parallel.py +853 -0
  570. agno/workflow/router.py +723 -0
  571. agno/workflow/step.py +1564 -0
  572. agno/workflow/steps.py +613 -0
  573. agno/workflow/types.py +556 -0
  574. agno/workflow/workflow.py +4327 -514
  575. agno-2.3.13.dist-info/METADATA +639 -0
  576. agno-2.3.13.dist-info/RECORD +613 -0
  577. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
  578. agno-2.3.13.dist-info/licenses/LICENSE +201 -0
  579. agno/api/playground.py +0 -91
  580. agno/api/schemas/playground.py +0 -22
  581. agno/api/schemas/user.py +0 -22
  582. agno/api/schemas/workspace.py +0 -46
  583. agno/api/user.py +0 -160
  584. agno/api/workspace.py +0 -151
  585. agno/cli/auth_server.py +0 -118
  586. agno/cli/config.py +0 -275
  587. agno/cli/console.py +0 -88
  588. agno/cli/credentials.py +0 -23
  589. agno/cli/entrypoint.py +0 -571
  590. agno/cli/operator.py +0 -355
  591. agno/cli/settings.py +0 -85
  592. agno/cli/ws/ws_cli.py +0 -817
  593. agno/constants.py +0 -13
  594. agno/document/__init__.py +0 -1
  595. agno/document/chunking/semantic.py +0 -47
  596. agno/document/chunking/strategy.py +0 -31
  597. agno/document/reader/__init__.py +0 -1
  598. agno/document/reader/arxiv_reader.py +0 -41
  599. agno/document/reader/base.py +0 -22
  600. agno/document/reader/csv_reader.py +0 -84
  601. agno/document/reader/docx_reader.py +0 -46
  602. agno/document/reader/firecrawl_reader.py +0 -99
  603. agno/document/reader/json_reader.py +0 -43
  604. agno/document/reader/pdf_reader.py +0 -219
  605. agno/document/reader/s3/pdf_reader.py +0 -46
  606. agno/document/reader/s3/text_reader.py +0 -51
  607. agno/document/reader/text_reader.py +0 -41
  608. agno/document/reader/website_reader.py +0 -175
  609. agno/document/reader/youtube_reader.py +0 -50
  610. agno/embedder/__init__.py +0 -1
  611. agno/embedder/azure_openai.py +0 -86
  612. agno/embedder/cohere.py +0 -72
  613. agno/embedder/fastembed.py +0 -37
  614. agno/embedder/google.py +0 -73
  615. agno/embedder/huggingface.py +0 -54
  616. agno/embedder/mistral.py +0 -80
  617. agno/embedder/ollama.py +0 -57
  618. agno/embedder/openai.py +0 -74
  619. agno/embedder/sentence_transformer.py +0 -38
  620. agno/embedder/voyageai.py +0 -64
  621. agno/eval/perf.py +0 -201
  622. agno/file/__init__.py +0 -1
  623. agno/file/file.py +0 -16
  624. agno/file/local/csv.py +0 -32
  625. agno/file/local/txt.py +0 -19
  626. agno/infra/app.py +0 -240
  627. agno/infra/base.py +0 -144
  628. agno/infra/context.py +0 -20
  629. agno/infra/db_app.py +0 -52
  630. agno/infra/resource.py +0 -205
  631. agno/infra/resources.py +0 -55
  632. agno/knowledge/agent.py +0 -230
  633. agno/knowledge/arxiv.py +0 -22
  634. agno/knowledge/combined.py +0 -22
  635. agno/knowledge/csv.py +0 -28
  636. agno/knowledge/csv_url.py +0 -19
  637. agno/knowledge/document.py +0 -20
  638. agno/knowledge/docx.py +0 -30
  639. agno/knowledge/json.py +0 -28
  640. agno/knowledge/langchain.py +0 -71
  641. agno/knowledge/llamaindex.py +0 -66
  642. agno/knowledge/pdf.py +0 -28
  643. agno/knowledge/pdf_url.py +0 -26
  644. agno/knowledge/s3/base.py +0 -60
  645. agno/knowledge/s3/pdf.py +0 -21
  646. agno/knowledge/s3/text.py +0 -23
  647. agno/knowledge/text.py +0 -30
  648. agno/knowledge/website.py +0 -88
  649. agno/knowledge/wikipedia.py +0 -31
  650. agno/knowledge/youtube.py +0 -22
  651. agno/memory/agent.py +0 -392
  652. agno/memory/classifier.py +0 -104
  653. agno/memory/db/__init__.py +0 -1
  654. agno/memory/db/base.py +0 -42
  655. agno/memory/db/mongodb.py +0 -189
  656. agno/memory/db/postgres.py +0 -203
  657. agno/memory/db/sqlite.py +0 -193
  658. agno/memory/memory.py +0 -15
  659. agno/memory/row.py +0 -36
  660. agno/memory/summarizer.py +0 -192
  661. agno/memory/summary.py +0 -19
  662. agno/memory/workflow.py +0 -38
  663. agno/models/google/gemini_openai.py +0 -26
  664. agno/models/ollama/hermes.py +0 -221
  665. agno/models/ollama/tools.py +0 -362
  666. agno/models/vertexai/gemini.py +0 -595
  667. agno/playground/__init__.py +0 -3
  668. agno/playground/async_router.py +0 -421
  669. agno/playground/deploy.py +0 -249
  670. agno/playground/operator.py +0 -92
  671. agno/playground/playground.py +0 -91
  672. agno/playground/schemas.py +0 -76
  673. agno/playground/serve.py +0 -55
  674. agno/playground/sync_router.py +0 -405
  675. agno/reasoning/agent.py +0 -68
  676. agno/run/response.py +0 -112
  677. agno/storage/agent/__init__.py +0 -0
  678. agno/storage/agent/base.py +0 -38
  679. agno/storage/agent/dynamodb.py +0 -350
  680. agno/storage/agent/json.py +0 -92
  681. agno/storage/agent/mongodb.py +0 -228
  682. agno/storage/agent/postgres.py +0 -367
  683. agno/storage/agent/session.py +0 -79
  684. agno/storage/agent/singlestore.py +0 -303
  685. agno/storage/agent/sqlite.py +0 -357
  686. agno/storage/agent/yaml.py +0 -93
  687. agno/storage/workflow/__init__.py +0 -0
  688. agno/storage/workflow/base.py +0 -40
  689. agno/storage/workflow/mongodb.py +0 -233
  690. agno/storage/workflow/postgres.py +0 -366
  691. agno/storage/workflow/session.py +0 -60
  692. agno/storage/workflow/sqlite.py +0 -359
  693. agno/tools/googlesearch.py +0 -88
  694. agno/utils/defaults.py +0 -57
  695. agno/utils/filesystem.py +0 -39
  696. agno/utils/git.py +0 -52
  697. agno/utils/json_io.py +0 -30
  698. agno/utils/load_env.py +0 -19
  699. agno/utils/py_io.py +0 -19
  700. agno/utils/pyproject.py +0 -18
  701. agno/utils/resource_filter.py +0 -31
  702. agno/vectordb/singlestore/s2vectordb.py +0 -390
  703. agno/vectordb/singlestore/s2vectordb2.py +0 -355
  704. agno/workspace/__init__.py +0 -0
  705. agno/workspace/config.py +0 -325
  706. agno/workspace/enums.py +0 -6
  707. agno/workspace/helpers.py +0 -48
  708. agno/workspace/operator.py +0 -758
  709. agno/workspace/settings.py +0 -63
  710. agno-0.1.2.dist-info/LICENSE +0 -375
  711. agno-0.1.2.dist-info/METADATA +0 -502
  712. agno-0.1.2.dist-info/RECORD +0 -352
  713. agno-0.1.2.dist-info/entry_points.txt +0 -3
  714. /agno/{cli → db/migrations}/__init__.py +0 -0
  715. /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
  716. /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
  717. /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
  718. /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
  719. /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
  720. /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
  721. /agno/{reranker → utils/models}/__init__.py +0 -0
  722. /agno/{storage → utils/print_response}/__init__.py +0 -0
  723. {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,718 @@
1
+ import asyncio
2
+ from typing import Any, Dict, List, Optional, Union
3
+
4
+ try:
5
+ from upstash_vector import Index, Vector
6
+ from upstash_vector.types import InfoResult
7
+ except ImportError:
8
+ raise ImportError(
9
+ "The `upstash-vector` package is not installed, please install using `pip install upstash-vector`"
10
+ )
11
+
12
+ from agno.filters import FilterExpr
13
+ from agno.knowledge.document import Document
14
+ from agno.knowledge.embedder import Embedder
15
+ from agno.knowledge.reranker.base import Reranker
16
+ from agno.utils.log import log_info, log_warning, logger
17
+ from agno.vectordb.base import VectorDb
18
+
19
+ DEFAULT_NAMESPACE = ""
20
+
21
+
22
+ class UpstashVectorDb(VectorDb):
23
+ """
24
+ This class provides an interface to Upstash Vector database with support for both
25
+ custom embeddings and Upstash's hosted embedding models.
26
+
27
+ Args:
28
+ url (str): The Upstash Vector database URL.
29
+ token (str): The Upstash Vector API token.
30
+ retries (Optional[int], optional): Number of retry attempts for operations. Defaults to 3.
31
+ retry_interval (Optional[float], optional): Time interval between retries in seconds. Defaults to 1.0.
32
+ dimension (Optional[int], optional): The dimension of the embeddings. Defaults to None.
33
+ embedder (Optional[Embedder], optional): The embedder to use. If None, uses Upstash hosted embedding models.
34
+ namespace (Optional[str], optional): The namespace to use. Defaults to DEFAULT_NAMESPACE.
35
+ reranker (Optional[Reranker], optional): The reranker to use. Defaults to None.
36
+ name (Optional[str], optional): The name of the vector database. Defaults to None.
37
+ description (Optional[str], optional): The description of the vector database. Defaults to None.
38
+ **kwargs: Additional keyword arguments.
39
+ """
40
+
41
+ def __init__(
42
+ self,
43
+ url: str,
44
+ token: str,
45
+ retries: Optional[int] = 3,
46
+ retry_interval: Optional[float] = 1.0,
47
+ dimension: Optional[int] = None,
48
+ embedder: Optional[Embedder] = None,
49
+ namespace: Optional[str] = DEFAULT_NAMESPACE,
50
+ reranker: Optional[Reranker] = None,
51
+ name: Optional[str] = None,
52
+ description: Optional[str] = None,
53
+ id: Optional[str] = None,
54
+ **kwargs: Any,
55
+ ) -> None:
56
+ # Validate required parameters
57
+ if not url:
58
+ raise ValueError("URL must be provided.")
59
+ if not token:
60
+ raise ValueError("Token must be provided.")
61
+
62
+ # Dynamic ID generation based on unique identifiers
63
+ if id is None:
64
+ from agno.utils.string import generate_id
65
+
66
+ namespace_identifier = namespace or DEFAULT_NAMESPACE
67
+ seed = f"{url}#{namespace_identifier}"
68
+ id = generate_id(seed)
69
+
70
+ # Initialize base class with name, description, and generated ID
71
+ super().__init__(id=id, name=name, description=description)
72
+
73
+ self._index: Optional[Index] = None
74
+ self.url: str = url
75
+ self.token: str = token
76
+ self.retries: int = retries if retries is not None else 3
77
+ self.retry_interval: float = retry_interval if retry_interval is not None else 1.0
78
+ self.dimension: Optional[int] = dimension
79
+ self.namespace: str = namespace if namespace is not None else DEFAULT_NAMESPACE
80
+ self.kwargs: Dict[str, Any] = kwargs
81
+ self.use_upstash_embeddings: bool = embedder is None
82
+ if embedder is None:
83
+ logger.warning(
84
+ "You have not provided an embedder, using Upstash hosted embedding models. "
85
+ "Make sure you created your index with an embedding model."
86
+ )
87
+ self.embedder: Optional[Embedder] = embedder
88
+ self.reranker: Optional[Reranker] = reranker
89
+
90
+ @property
91
+ def index(self) -> Index:
92
+ """The Upstash Vector index.
93
+ Returns:
94
+ upstash_vector.Index: The Upstash Vector index.
95
+ """
96
+ if self._index is None:
97
+ self._index = Index(
98
+ url=self.url,
99
+ token=self.token,
100
+ retries=self.retries,
101
+ retry_interval=self.retry_interval,
102
+ )
103
+ if self._index is None:
104
+ raise ValueError("Failed to initialize Upstash index")
105
+
106
+ info = self._index.info()
107
+ if info is None:
108
+ raise ValueError("Failed to get index info")
109
+
110
+ index_dimension = info.dimension
111
+ if self.dimension is not None and index_dimension != self.dimension:
112
+ raise ValueError(
113
+ f"Index dimension {index_dimension} does not match provided dimension {self.dimension}"
114
+ )
115
+ return self._index
116
+
117
+ def exists(self) -> bool:
118
+ """Check if the index exists and is accessible.
119
+
120
+ Returns:
121
+ bool: True if the index exists and is accessible, False otherwise.
122
+
123
+ Raises:
124
+ Exception: If there's an error communicating with Upstash.
125
+ """
126
+ try:
127
+ self.index.info()
128
+ return True
129
+ except Exception as e:
130
+ logger.error(f"Error checking index existence: {str(e)}")
131
+ return False
132
+
133
+ def create(self) -> None:
134
+ """You can create indexes via Upstash Console."""
135
+ logger.warning(
136
+ "Indexes can only be created through the Upstash Console or the developer API. Please create an index before using this vector database."
137
+ )
138
+ pass
139
+
140
+ def drop(self) -> None:
141
+ """You can drop indexes via Upstash Console."""
142
+ logger.warning(
143
+ "Indexes can only be dropped through the Upstash Console. Make sure you have an existing index before performing operations."
144
+ )
145
+ pass
146
+
147
+ def drop_namespace(self, namespace: Optional[str] = None) -> None:
148
+ """Delete a namespace from the index.
149
+ Args:
150
+ namespace (Optional[str], optional): The namespace to drop. Defaults to None, which uses the instance namespace.
151
+ """
152
+ _namespace = self.namespace if namespace is None else namespace
153
+ if self.namespace_exists(_namespace):
154
+ self.index.delete_namespace(_namespace)
155
+ else:
156
+ logger.error(f"Namespace {_namespace} does not exist.")
157
+
158
+ def get_all_namespaces(self) -> List[str]:
159
+ """Get all namespaces in the index.
160
+ Returns:
161
+ List[str]: A list of namespaces.
162
+ """
163
+ return self.index.list_namespaces()
164
+
165
+ def content_hash_exists(self, content_hash: str) -> bool:
166
+ """Check if documents with the given content hash exist in the index.
167
+
168
+ Args:
169
+ content_hash (str): The content hash to check.
170
+
171
+ Returns:
172
+ bool: True if documents with the content hash exist, False otherwise.
173
+ """
174
+ try:
175
+ # Use query with a filter to check if any documents exist with this content_hash
176
+ # We only need to check existence, so limit to 1 result
177
+ filter_str = f'content_hash = "{content_hash}"'
178
+
179
+ if not self.use_upstash_embeddings and self.embedder is not None:
180
+ # For custom embeddings, we need a dummy vector for the query
181
+ # Use a zero vector as we only care about the filter match
182
+ info = self.index.info()
183
+ dimension = info.dimension
184
+ dummy_vector = [0.0] * dimension
185
+
186
+ response = self.index.query(
187
+ vector=dummy_vector,
188
+ namespace=self.namespace,
189
+ top_k=1,
190
+ filter=filter_str,
191
+ include_data=False,
192
+ include_metadata=False,
193
+ include_vectors=False,
194
+ )
195
+ else:
196
+ # For hosted embeddings, use a minimal text query
197
+ response = self.index.query(
198
+ data="", # Empty query since we only care about the filter
199
+ namespace=self.namespace,
200
+ top_k=1,
201
+ filter=filter_str,
202
+ include_data=False,
203
+ include_metadata=False,
204
+ include_vectors=False,
205
+ )
206
+
207
+ return response is not None and len(response) > 0
208
+ except Exception as e:
209
+ logger.error(f"Error checking if content_hash {content_hash} exists: {e}")
210
+ return False
211
+
212
+ def name_exists(self, name: str) -> bool:
213
+ """You can check if an index exists in Upstash Console.
214
+ Args:
215
+ name (str): The name of the index to check.
216
+ Returns:
217
+ bool: True if the index exists, False otherwise. (Name is not used.)
218
+ """
219
+ logger.warning(
220
+ f"You can check if an index with name {name} exists in Upstash Console."
221
+ "The token and url parameters you provided are used to connect to a specific index."
222
+ )
223
+ return self.exists()
224
+
225
+ def namespace_exists(self, namespace: str) -> bool:
226
+ """Check if an namespace exists.
227
+ Args:
228
+ namespace (str): The name of the namespace to check.
229
+ Returns:
230
+ bool: True if the namespace exists, False otherwise.
231
+ """
232
+ namespaces = self.index.list_namespaces()
233
+ return namespace in namespaces
234
+
235
+ def upsert(
236
+ self,
237
+ content_hash: str,
238
+ documents: List[Document],
239
+ filters: Optional[Dict[str, Any]] = None,
240
+ namespace: Optional[str] = None,
241
+ ) -> None:
242
+ """Upsert documents into the index.
243
+
244
+ Args:
245
+ documents (List[Document]): The documents to upsert.
246
+ filters (Optional[Dict[str, Any]], optional): The filters for the upsert. Defaults to None.
247
+ namespace (Optional[str], optional): The namespace for the documents. Defaults to None, which uses the instance namespace.
248
+ """
249
+ _namespace = self.namespace if namespace is None else namespace
250
+ vectors = []
251
+
252
+ for i, document in enumerate(documents):
253
+ if document.id is None:
254
+ logger.error(f"Document ID must not be None. Skipping document: {document.content[:100]}...")
255
+ continue
256
+
257
+ logger.debug(
258
+ f"Processing document {i + 1}: ID={document.id}, name={document.name}, "
259
+ f"content_id={getattr(document, 'content_id', 'N/A')}"
260
+ )
261
+
262
+ # Create a copy of metadata to avoid modifying the original document
263
+ meta_data = document.meta_data.copy() if document.meta_data else {}
264
+
265
+ # Add filters to document metadata if provided
266
+ if filters:
267
+ meta_data.update(filters)
268
+
269
+ meta_data["text"] = document.content
270
+
271
+ # Add content_id to metadata if it exists
272
+ if hasattr(document, "content_id") and document.content_id:
273
+ meta_data["content_id"] = document.content_id
274
+ else:
275
+ logger.warning(f"Document {document.id} has no content_id")
276
+
277
+ meta_data["content_hash"] = content_hash
278
+
279
+ # Add name to metadata if it exists
280
+ if document.name:
281
+ meta_data["name"] = document.name
282
+ else:
283
+ logger.warning(f"Document {document.id} has no name")
284
+
285
+ if not self.use_upstash_embeddings:
286
+ if self.embedder is None:
287
+ logger.error("Embedder is None but use_upstash_embeddings is False")
288
+ continue
289
+
290
+ document.embed(embedder=self.embedder)
291
+ if document.embedding is None:
292
+ logger.error(f"Failed to generate embedding for document: {document.id}")
293
+ continue
294
+
295
+ vector = Vector(id=document.id, vector=document.embedding, metadata=meta_data, data=document.content)
296
+ else:
297
+ vector = Vector(id=document.id, data=document.content, metadata=meta_data)
298
+ vectors.append(vector)
299
+
300
+ if not vectors:
301
+ logger.warning("No valid documents to upsert")
302
+ return
303
+
304
+ logger.info(f"Upserting {len(vectors)} vectors to Upstash with IDs: {[v.id for v in vectors[:5]]}...")
305
+ self.index.upsert(vectors, namespace=_namespace)
306
+
307
+ def upsert_available(self) -> bool:
308
+ """Check if upsert operation is available.
309
+ Returns:
310
+ True
311
+ """
312
+ return True
313
+
314
+ def insert(self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
315
+ """Insert documents into the index.
316
+ This method is not supported by Upstash. Use `upsert` instead.
317
+ Args:
318
+ documents (List[Document]): The documents to insert.
319
+ filters (Optional[Dict[str, Any]], optional): The filters for the insert. Defaults to None.
320
+ """
321
+ logger.warning("Upstash does not support insert operations. Using upsert instead.")
322
+ self.upsert(content_hash=content_hash, documents=documents, filters=filters)
323
+
324
+ def search(
325
+ self,
326
+ query: str,
327
+ limit: int = 5,
328
+ filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
329
+ namespace: Optional[str] = None,
330
+ ) -> List[Document]:
331
+ """Search for documents in the index.
332
+ Args:
333
+ query (str): The query string to search for.
334
+ limit (int, optional): Maximum number of results to return. Defaults to 5.
335
+ filters (Optional[Dict[str, Any]], optional): Metadata filters for the search.
336
+ namespace (Optional[str], optional): The namespace to search in. Defaults to None, which uses the instance namespace.
337
+ Returns:
338
+ List[Document]: List of matching documents.
339
+ """
340
+ _namespace = self.namespace if namespace is None else namespace
341
+ if isinstance(filters, List):
342
+ log_warning("Filters Expressions are not supported in UpstashDB. No filters will be applied.")
343
+ filters = None
344
+ filter_str = "" if filters is None else str(filters)
345
+
346
+ if not self.use_upstash_embeddings and self.embedder is not None:
347
+ dense_embedding = self.embedder.get_embedding(query)
348
+
349
+ if dense_embedding is None:
350
+ logger.error(f"Error getting embedding for Query: {query}")
351
+ return []
352
+
353
+ response = self.index.query(
354
+ vector=dense_embedding,
355
+ namespace=_namespace,
356
+ top_k=limit,
357
+ filter=filter_str,
358
+ include_data=True,
359
+ include_metadata=True,
360
+ include_vectors=True,
361
+ )
362
+ else:
363
+ response = self.index.query(
364
+ data=query,
365
+ namespace=_namespace,
366
+ top_k=limit,
367
+ filter=filter_str,
368
+ include_data=True,
369
+ include_metadata=True,
370
+ include_vectors=True,
371
+ )
372
+
373
+ if response is None:
374
+ log_info(f"No results found for query: {query}")
375
+ return []
376
+
377
+ search_results = []
378
+ for result in response:
379
+ if result.data is not None and result.id is not None and result.vector is not None:
380
+ search_results.append(
381
+ Document(
382
+ content=result.data,
383
+ id=result.id,
384
+ meta_data=result.metadata or {},
385
+ embedding=result.vector,
386
+ )
387
+ )
388
+
389
+ if self.reranker:
390
+ search_results = self.reranker.rerank(query=query, documents=search_results)
391
+
392
+ log_info(f"Found {len(search_results)} results")
393
+ return search_results
394
+
395
+ def delete(self, namespace: Optional[str] = None, delete_all: bool = False) -> bool:
396
+ """Clear the index.
397
+ Args:
398
+ namespace (Optional[str], optional): The namespace to clear. Defaults to None, which uses the instance namespace.
399
+ delete_all (bool, optional): Whether to delete all documents in the index. Defaults to False.
400
+ Returns:
401
+ bool: True if the index was deleted, False otherwise.
402
+ """
403
+ _namespace = self.namespace if namespace is None else namespace
404
+ response = self.index.reset(namespace=_namespace, all=delete_all)
405
+ return True if response.lower().strip() == "success" else False
406
+
407
+ def get_index_info(self) -> InfoResult:
408
+ """Get information about the index.
409
+ Returns:
410
+ InfoResult: Information about the index including size, vector count, etc.
411
+ """
412
+ return self.index.info()
413
+
414
+ def optimize(self) -> None:
415
+ """Optimize the index.
416
+ This method is empty as Upstash automatically optimizes indexes.
417
+ """
418
+ pass
419
+
420
+ def delete_by_id(self, id: str) -> bool:
421
+ """Delete document by ID.
422
+
423
+ Args:
424
+ id (str): The document ID to delete
425
+
426
+ Returns:
427
+ bool: True if deletion was successful, False otherwise
428
+ """
429
+ try:
430
+ response = self.index.delete(ids=[id], namespace=self.namespace)
431
+ deleted_count = getattr(response, "deleted", 0)
432
+ logger.info(f"Deleted {deleted_count} document(s) with ID: {id}")
433
+ return True
434
+ except Exception as e:
435
+ logger.error(f"Error deleting document by ID {id}: {e}")
436
+ return False
437
+
438
+ def delete_by_name(self, name: str) -> bool:
439
+ """Delete documents by name using metadata filter.
440
+
441
+ Args:
442
+ name (str): The document name to delete
443
+
444
+ Returns:
445
+ bool: True if deletion was successful, False otherwise
446
+ """
447
+ try:
448
+ # Use Upstash's delete with metadata filter
449
+ response = self.index.delete(filter=f'name = "{name}"', namespace=self.namespace)
450
+ deleted_count = getattr(response, "deleted", 0)
451
+ logger.info(f"Deleted {deleted_count} document(s) with name: {name}")
452
+ return True
453
+ except Exception as e:
454
+ logger.error(f"Error deleting documents by name {name}: {e}")
455
+ return False
456
+
457
+ def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
458
+ """Delete documents by metadata filter.
459
+
460
+ Args:
461
+ metadata (Dict[str, Any]): Metadata criteria for deletion
462
+
463
+ Returns:
464
+ bool: True if deletion was successful, False otherwise
465
+ """
466
+ try:
467
+ # Build filter string for Upstash metadata filtering
468
+ filter_parts = []
469
+ for key, value in metadata.items():
470
+ if isinstance(value, str):
471
+ filter_parts.append(f'{key} = "{value}"')
472
+ else:
473
+ filter_parts.append(f"{key} = {value}")
474
+
475
+ filter_str = " AND ".join(filter_parts)
476
+
477
+ response = self.index.delete(filter=filter_str, namespace=self.namespace)
478
+ deleted_count = getattr(response, "deleted", 0)
479
+ logger.info(f"Deleted {deleted_count} document(s) matching metadata: {metadata}")
480
+ return True
481
+ except Exception as e:
482
+ logger.error(f"Error deleting documents by metadata {metadata}: {e}")
483
+ return False
484
+
485
+ def delete_by_content_id(self, content_id: str) -> bool:
486
+ """Delete documents by content_id.
487
+
488
+ Args:
489
+ content_id (str): The content ID to delete
490
+
491
+ Returns:
492
+ bool: True if deletion was successful, False otherwise
493
+ """
494
+ return self.delete_by_metadata({"content_id": content_id})
495
+
496
+ async def async_insert(
497
+ self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
498
+ ) -> None:
499
+ logger.warning("Upstash does not support async insert operations. Using upsert instead.")
500
+ await self.async_upsert(content_hash=content_hash, documents=documents, filters=filters)
501
+
502
+ async def async_exists(self) -> bool:
503
+ raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
504
+
505
+ async def async_name_exists(self, name: str) -> bool:
506
+ raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
507
+
508
+ async def async_create(self) -> None:
509
+ raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
510
+
511
+ async def async_drop(self) -> None:
512
+ raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
513
+
514
+ async def async_upsert(
515
+ self,
516
+ content_hash: str,
517
+ documents: List[Document],
518
+ filters: Optional[Dict[str, Any]] = None,
519
+ namespace: Optional[str] = None,
520
+ ) -> None:
521
+ """Async Upsert documents into the index.
522
+
523
+ Args:
524
+ documents (List[Document]): The documents to upsert.
525
+ filters (Optional[Dict[str, Any]], optional): The filters for the upsert. Defaults to None.
526
+ namespace (Optional[str], optional): The namespace for the documents. Defaults to None, which uses the instance namespace.
527
+ """
528
+ _namespace = self.namespace if namespace is None else namespace
529
+ vectors = []
530
+
531
+ if (
532
+ self.embedder
533
+ and self.embedder.enable_batch
534
+ and hasattr(self.embedder, "async_get_embeddings_batch_and_usage")
535
+ ):
536
+ # Use batch embedding when enabled and supported
537
+ try:
538
+ # Extract content from all documents
539
+ doc_contents = [doc.content for doc in documents]
540
+
541
+ # Get batch embeddings and usage
542
+ embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
543
+
544
+ # Process documents with pre-computed embeddings
545
+ for j, doc in enumerate(documents):
546
+ try:
547
+ if j < len(embeddings):
548
+ doc.embedding = embeddings[j]
549
+ doc.usage = usages[j] if j < len(usages) else None
550
+ except Exception as e:
551
+ logger.error(f"Error assigning batch embedding to document '{doc.name}': {e}")
552
+
553
+ except Exception as e:
554
+ # Check if this is a rate limit error - don't fall back as it would make things worse
555
+ error_str = str(e).lower()
556
+ is_rate_limit = any(
557
+ phrase in error_str
558
+ for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
559
+ )
560
+
561
+ if is_rate_limit:
562
+ logger.error(f"Rate limit detected during batch embedding. {e}")
563
+ raise e
564
+ else:
565
+ logger.warning(f"Async batch embedding failed, falling back to individual embeddings: {e}")
566
+ # Fall back to individual embedding
567
+ embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in documents]
568
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
569
+ else:
570
+ # Use individual embedding
571
+ embed_tasks = [document.async_embed(embedder=self.embedder) for document in documents]
572
+ await asyncio.gather(*embed_tasks, return_exceptions=True)
573
+
574
+ for i, document in enumerate(documents):
575
+ if document.id is None:
576
+ logger.error(f"Document ID must not be None. Skipping document: {document.content[:100]}...")
577
+ continue
578
+
579
+ logger.debug(
580
+ f"Processing document {i + 1}: ID={document.id}, name={document.name}, "
581
+ f"content_id={getattr(document, 'content_id', 'N/A')}"
582
+ )
583
+
584
+ # Create a copy of metadata to avoid modifying the original document
585
+ meta_data = document.meta_data.copy() if document.meta_data else {}
586
+
587
+ # Add filters to document metadata if provided
588
+ if filters:
589
+ meta_data.update(filters)
590
+
591
+ meta_data["text"] = document.content
592
+
593
+ # Add content_id to metadata if it exists
594
+ if hasattr(document, "content_id") and document.content_id:
595
+ meta_data["content_id"] = document.content_id
596
+ else:
597
+ logger.warning(f"Document {document.id} has no content_id")
598
+
599
+ meta_data["content_hash"] = content_hash
600
+
601
+ # Add name to metadata if it exists
602
+ if document.name:
603
+ meta_data["name"] = document.name
604
+ else:
605
+ logger.warning(f"Document {document.id} has no name")
606
+
607
+ if not self.use_upstash_embeddings:
608
+ if self.embedder is None:
609
+ logger.error("Embedder is None but use_upstash_embeddings is False")
610
+ continue
611
+
612
+ if document.embedding is None:
613
+ logger.error(f"Failed to generate embedding for document: {document.id}")
614
+ continue
615
+
616
+ vector = Vector(id=document.id, vector=document.embedding, metadata=meta_data, data=document.content)
617
+ else:
618
+ vector = Vector(id=document.id, data=document.content, metadata=meta_data)
619
+ vectors.append(vector)
620
+
621
+ if not vectors:
622
+ logger.warning("No valid documents to upsert")
623
+ return
624
+
625
+ logger.info(f"Upserting {len(vectors)} vectors to Upstash with IDs: {[v.id for v in vectors[:5]]}...")
626
+ self.index.upsert(vectors, namespace=_namespace)
627
+
628
+ async def async_search(
629
+ self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
630
+ ) -> List[Document]:
631
+ raise NotImplementedError(f"Async not supported on {self.__class__.__name__}.")
632
+
633
+ def id_exists(self, id: str) -> bool:
634
+ """Check if a document with the given ID exists in the index.
635
+
636
+ Args:
637
+ id (str): The document ID to check.
638
+
639
+ Returns:
640
+ bool: True if the document exists, False otherwise.
641
+ """
642
+ try:
643
+ response = self.index.fetch(ids=[id], namespace=self.namespace)
644
+ return len(response) > 0
645
+ except Exception as e:
646
+ logger.error(f"Error checking if ID {id} exists: {e}")
647
+ return False
648
+
649
+ def _delete_by_content_hash(self, content_hash: str) -> bool:
650
+ """Delete documents by content hash using metadata filter.
651
+
652
+ Args:
653
+ content_hash (str): The content hash to delete.
654
+
655
+ Returns:
656
+ bool: True if deletion was successful, False otherwise.
657
+ """
658
+ try:
659
+ response = self.index.delete(filter=f'content_hash = "{content_hash}"', namespace=self.namespace)
660
+ deleted_count = getattr(response, "deleted", 0)
661
+ logger.info(f"Deleted {deleted_count} document(s) with content_hash: {content_hash}")
662
+ return True
663
+ except Exception as e:
664
+ logger.error(f"Error deleting documents by content_hash {content_hash}: {e}")
665
+ return False
666
+
667
+ def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
668
+ """
669
+ Update the metadata for documents with the given content_id.
670
+
671
+ Args:
672
+ content_id (str): The content ID to update
673
+ metadata (Dict[str, Any]): The metadata to update
674
+ """
675
+ try:
676
+ # Query for vectors with the given content_id
677
+ query_response = self.index.query(
678
+ filter=f'content_id = "{content_id}"',
679
+ top_k=1000, # Get all matching vectors
680
+ include_metadata=True,
681
+ namespace=self.namespace,
682
+ )
683
+
684
+ if not query_response or not hasattr(query_response, "__iter__"):
685
+ logger.debug(f"No documents found with content_id: {content_id}")
686
+ return
687
+
688
+ # Update each matching vector
689
+ updated_count = 0
690
+ for result in query_response:
691
+ if hasattr(result, "id") and hasattr(result, "metadata"):
692
+ vector_id = result.id
693
+ current_metadata = result.metadata or {}
694
+
695
+ # Merge existing metadata with new metadata
696
+ updated_metadata = current_metadata.copy()
697
+ updated_metadata.update(metadata)
698
+
699
+ if "filters" not in updated_metadata:
700
+ updated_metadata["filters"] = {}
701
+ if isinstance(updated_metadata["filters"], dict):
702
+ updated_metadata["filters"].update(metadata)
703
+ else:
704
+ updated_metadata["filters"] = metadata
705
+
706
+ # Update the vector metadata
707
+ self.index.update(id=vector_id, metadata=updated_metadata, namespace=self.namespace)
708
+ updated_count += 1
709
+
710
+ logger.debug(f"Updated metadata for {updated_count} documents with content_id: {content_id}")
711
+
712
+ except Exception as e:
713
+ logger.error(f"Error updating metadata for content_id '{content_id}': {e}")
714
+ raise
715
+
716
+ def get_supported_search_types(self) -> List[str]:
717
+ """Get the supported search types for this vector database."""
718
+ return [] # UpstashVectorDb doesn't use SearchType enum