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.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- agno/api/workflow.py +28 -0
- agno/cloud/aws/base.py +214 -0
- agno/cloud/aws/s3/__init__.py +2 -0
- agno/cloud/aws/s3/api_client.py +43 -0
- agno/cloud/aws/s3/bucket.py +195 -0
- agno/cloud/aws/s3/object.py +57 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/{document → knowledge/document}/base.py +12 -2
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- agno/file/file.py +0 -16
- agno/file/local/csv.py +0 -32
- agno/file/local/txt.py +0 -19
- agno/infra/app.py +0 -240
- agno/infra/base.py +0 -144
- agno/infra/context.py +0 -20
- agno/infra/db_app.py +0 -52
- agno/infra/resource.py +0 -205
- agno/infra/resources.py +0 -55
- agno/knowledge/agent.py +0 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- agno/memory/db/base.py +0 -42
- agno/memory/db/mongodb.py +0 -189
- agno/memory/db/postgres.py +0 -203
- agno/memory/db/sqlite.py +0 -193
- agno/memory/memory.py +0 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- agno/utils/defaults.py +0 -57
- agno/utils/filesystem.py +0 -39
- agno/utils/git.py +0 -52
- agno/utils/json_io.py +0 -30
- agno/utils/load_env.py +0 -19
- agno/utils/py_io.py +0 -19
- agno/utils/pyproject.py +0 -18
- agno/utils/resource_filter.py +0 -31
- agno/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {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
|