agno 2.2.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 +51 -0
- agno/agent/agent.py +10405 -0
- agno/api/__init__.py +0 -0
- agno/api/agent.py +28 -0
- agno/api/api.py +40 -0
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +13 -0
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +16 -0
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/response.py +6 -0
- agno/api/schemas/team.py +16 -0
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +30 -0
- 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/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 +598 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2042 -0
- agno/db/dynamo/schemas.py +314 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +1795 -0
- agno/db/firestore/schemas.py +140 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1335 -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 +1160 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1328 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/__init__.py +0 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2026 -0
- agno/db/mongo/mongo.py +1982 -0
- agno/db/mongo/schemas.py +87 -0
- agno/db/mongo/utils.py +259 -0
- agno/db/mysql/__init__.py +3 -0
- agno/db/mysql/mysql.py +2308 -0
- agno/db/mysql/schemas.py +138 -0
- agno/db/mysql/utils.py +355 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +1927 -0
- agno/db/postgres/postgres.py +2260 -0
- agno/db/postgres/schemas.py +139 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1660 -0
- agno/db/redis/schemas.py +123 -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 +33 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +46 -0
- agno/db/schemas/metrics.py +0 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +130 -0
- agno/db/singlestore/singlestore.py +2272 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2293 -0
- agno/db/sqlite/schemas.py +133 -0
- agno/db/sqlite/sqlite.py +2288 -0
- agno/db/sqlite/utils.py +431 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +309 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1353 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +116 -0
- agno/debug.py +18 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +834 -0
- agno/eval/performance.py +773 -0
- agno/eval/reliability.py +306 -0
- agno/eval/utils.py +119 -0
- agno/exceptions.py +161 -0
- 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/integrations/__init__.py +0 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -0
- agno/knowledge/chunking/__init__.py +0 -0
- agno/knowledge/chunking/agentic.py +79 -0
- agno/knowledge/chunking/document.py +91 -0
- agno/knowledge/chunking/fixed.py +57 -0
- agno/knowledge/chunking/markdown.py +151 -0
- agno/knowledge/chunking/recursive.py +63 -0
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +86 -0
- agno/knowledge/chunking/strategy.py +165 -0
- agno/knowledge/content.py +74 -0
- agno/knowledge/document/__init__.py +5 -0
- agno/knowledge/document/base.py +58 -0
- agno/knowledge/embedder/__init__.py +5 -0
- agno/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/knowledge/embedder/base.py +23 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/knowledge/embedder/fireworks.py +13 -0
- 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/knowledge/embedder/together.py +13 -0
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +1988 -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 +166 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +292 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +87 -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 +194 -0
- agno/knowledge/reader/text_reader.py +115 -0
- agno/knowledge/reader/web_search_reader.py +372 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +59 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/__init__.py +0 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/knowledge/reranker/base.py +14 -0
- agno/knowledge/reranker/cohere.py +64 -0
- 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 +189 -0
- agno/media.py +462 -0
- agno/memory/__init__.py +3 -0
- agno/memory/manager.py +1327 -0
- agno/models/__init__.py +0 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +45 -0
- agno/models/anthropic/__init__.py +5 -0
- agno/models/anthropic/claude.py +757 -0
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +701 -0
- agno/models/aws/claude.py +378 -0
- agno/models/azure/__init__.py +18 -0
- agno/models/azure/ai_foundry.py +485 -0
- agno/models/azure/openai_chat.py +131 -0
- agno/models/base.py +2175 -0
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +501 -0
- agno/models/cerebras/cerebras_openai.py +112 -0
- agno/models/cohere/__init__.py +5 -0
- agno/models/cohere/chat.py +389 -0
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +57 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +91 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +28 -0
- agno/models/deepseek/__init__.py +5 -0
- agno/models/deepseek/deepseek.py +61 -0
- agno/models/defaults.py +1 -0
- agno/models/fireworks/__init__.py +5 -0
- agno/models/fireworks/fireworks.py +26 -0
- agno/models/google/__init__.py +5 -0
- agno/models/google/gemini.py +1085 -0
- agno/models/groq/__init__.py +5 -0
- agno/models/groq/groq.py +556 -0
- agno/models/huggingface/__init__.py +5 -0
- agno/models/huggingface/huggingface.py +491 -0
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +422 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +26 -0
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +48 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +468 -0
- agno/models/litellm/litellm_openai.py +25 -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 +434 -0
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +475 -0
- agno/models/meta/llama_openai.py +78 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +5 -0
- agno/models/mistral/mistral.py +432 -0
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +54 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +5 -0
- agno/models/nvidia/nvidia.py +28 -0
- agno/models/ollama/__init__.py +5 -0
- agno/models/ollama/chat.py +441 -0
- agno/models/openai/__init__.py +9 -0
- agno/models/openai/chat.py +883 -0
- agno/models/openai/like.py +27 -0
- agno/models/openai/responses.py +1050 -0
- agno/models/openrouter/__init__.py +5 -0
- agno/models/openrouter/openrouter.py +66 -0
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +187 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +81 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +52 -0
- agno/models/response.py +199 -0
- agno/models/sambanova/__init__.py +5 -0
- agno/models/sambanova/sambanova.py +28 -0
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +25 -0
- agno/models/together/__init__.py +5 -0
- agno/models/together/together.py +25 -0
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +26 -0
- agno/models/vertexai/__init__.py +0 -0
- agno/models/vertexai/claude.py +70 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +78 -0
- agno/models/xai/__init__.py +3 -0
- agno/models/xai/xai.py +113 -0
- agno/os/__init__.py +3 -0
- agno/os/app.py +876 -0
- agno/os/auth.py +57 -0
- agno/os/config.py +104 -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 +250 -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 +144 -0
- agno/os/interfaces/agui/utils.py +534 -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 +211 -0
- agno/os/interfaces/whatsapp/security.py +53 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +292 -0
- agno/os/middleware/__init__.py +7 -0
- agno/os/middleware/jwt.py +233 -0
- agno/os/router.py +1763 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +430 -0
- agno/os/routers/evals/schemas.py +142 -0
- agno/os/routers/evals/utils.py +162 -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 +997 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +515 -0
- agno/os/routers/memory/schemas.py +62 -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/schema.py +1055 -0
- agno/os/settings.py +43 -0
- agno/os/utils.py +630 -0
- agno/py.typed +0 -0
- agno/reasoning/__init__.py +0 -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 +63 -0
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +31 -0
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +787 -0
- agno/run/base.py +229 -0
- agno/run/cancel.py +81 -0
- agno/run/messages.py +32 -0
- agno/run/team.py +753 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +295 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +392 -0
- agno/session/workflow.py +205 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +8793 -0
- agno/tools/__init__.py +10 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +69 -0
- agno/tools/api.py +122 -0
- agno/tools/apify.py +314 -0
- agno/tools/arxiv.py +127 -0
- agno/tools/aws_lambda.py +53 -0
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +89 -0
- 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 +255 -0
- agno/tools/calculator.py +151 -0
- agno/tools/cartesia.py +187 -0
- agno/tools/clickup.py +244 -0
- agno/tools/confluence.py +240 -0
- agno/tools/crawl4ai.py +158 -0
- agno/tools/csv_toolkit.py +185 -0
- agno/tools/dalle.py +110 -0
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +262 -0
- agno/tools/desi_vocal.py +108 -0
- agno/tools/discord.py +161 -0
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +379 -0
- agno/tools/duckduckgo.py +91 -0
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +196 -0
- agno/tools/email.py +67 -0
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +396 -0
- agno/tools/fal.py +127 -0
- agno/tools/file.py +240 -0
- agno/tools/file_generation.py +350 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +143 -0
- agno/tools/function.py +1187 -0
- agno/tools/giphy.py +93 -0
- agno/tools/github.py +1760 -0
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +270 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +674 -0
- agno/tools/googlesearch.py +98 -0
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +77 -0
- agno/tools/jina.py +101 -0
- agno/tools/jira.py +170 -0
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +426 -0
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +90 -0
- agno/tools/lumalab.py +183 -0
- 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/memori.py +339 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +139 -0
- agno/tools/models/__init__.py +0 -0
- 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 +195 -0
- agno/tools/moviepy_video.py +349 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +46 -0
- agno/tools/newspaper4k.py +93 -0
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +202 -0
- agno/tools/openbb.py +160 -0
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +102 -0
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +257 -0
- agno/tools/pubmed.py +188 -0
- agno/tools/python.py +205 -0
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +467 -0
- agno/tools/replicate.py +117 -0
- agno/tools/resend.py +62 -0
- agno/tools/scrapegraph.py +222 -0
- agno/tools/searxng.py +152 -0
- agno/tools/serpapi.py +116 -0
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +53 -0
- agno/tools/slack.py +136 -0
- agno/tools/sleep.py +20 -0
- agno/tools/spider.py +116 -0
- agno/tools/sql.py +154 -0
- agno/tools/streamlit/__init__.py +0 -0
- agno/tools/streamlit/components.py +113 -0
- agno/tools/tavily.py +254 -0
- agno/tools/telegram.py +48 -0
- agno/tools/todoist.py +218 -0
- agno/tools/tool_registry.py +1 -0
- agno/tools/toolkit.py +146 -0
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +274 -0
- agno/tools/twilio.py +186 -0
- 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 +54 -0
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +63 -0
- agno/tools/workflow.py +278 -0
- agno/tools/x.py +335 -0
- agno/tools/yfinance.py +257 -0
- agno/tools/youtube.py +184 -0
- agno/tools/zendesk.py +82 -0
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +382 -0
- agno/utils/__init__.py +0 -0
- agno/utils/agent.py +820 -0
- agno/utils/audio.py +49 -0
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +132 -0
- agno/utils/dttm.py +13 -0
- agno/utils/enum.py +22 -0
- agno/utils/env.py +11 -0
- agno/utils/events.py +696 -0
- agno/utils/format_str.py +16 -0
- agno/utils/functions.py +166 -0
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +57 -0
- agno/utils/http.py +74 -0
- agno/utils/json_schema.py +234 -0
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +255 -0
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +352 -0
- agno/utils/merge_dict.py +41 -0
- agno/utils/message.py +118 -0
- agno/utils/models/__init__.py +0 -0
- agno/utils/models/ai_foundry.py +43 -0
- agno/utils/models/claude.py +358 -0
- agno/utils/models/cohere.py +87 -0
- agno/utils/models/llama.py +78 -0
- agno/utils/models/mistral.py +98 -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 +32 -0
- agno/utils/pprint.py +178 -0
- agno/utils/print_response/__init__.py +0 -0
- agno/utils/print_response/agent.py +842 -0
- agno/utils/print_response/team.py +1724 -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/response_iterator.py +17 -0
- agno/utils/safe_formatter.py +24 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +22 -0
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +231 -0
- agno/utils/team.py +139 -0
- agno/utils/timer.py +41 -0
- agno/utils/tools.py +102 -0
- agno/utils/web.py +23 -0
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +25 -0
- agno/vectordb/__init__.py +3 -0
- agno/vectordb/base.py +127 -0
- agno/vectordb/cassandra/__init__.py +5 -0
- agno/vectordb/cassandra/cassandra.py +501 -0
- agno/vectordb/cassandra/extra_param_mixin.py +11 -0
- agno/vectordb/cassandra/index.py +13 -0
- agno/vectordb/chroma/__init__.py +5 -0
- agno/vectordb/chroma/chromadb.py +929 -0
- agno/vectordb/clickhouse/__init__.py +9 -0
- agno/vectordb/clickhouse/clickhousedb.py +835 -0
- agno/vectordb/clickhouse/index.py +9 -0
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1442 -0
- agno/vectordb/distance.py +7 -0
- agno/vectordb/lancedb/__init__.py +6 -0
- agno/vectordb/lancedb/lance_db.py +995 -0
- 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 +4 -0
- agno/vectordb/milvus/milvus.py +1182 -0
- agno/vectordb/mongodb/__init__.py +9 -0
- agno/vectordb/mongodb/mongodb.py +1417 -0
- agno/vectordb/pgvector/__init__.py +12 -0
- agno/vectordb/pgvector/index.py +23 -0
- agno/vectordb/pgvector/pgvector.py +1462 -0
- agno/vectordb/pineconedb/__init__.py +5 -0
- agno/vectordb/pineconedb/pineconedb.py +747 -0
- agno/vectordb/qdrant/__init__.py +5 -0
- agno/vectordb/qdrant/qdrant.py +1134 -0
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +694 -0
- agno/vectordb/search.py +7 -0
- agno/vectordb/singlestore/__init__.py +10 -0
- agno/vectordb/singlestore/index.py +41 -0
- agno/vectordb/singlestore/singlestore.py +763 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +699 -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 +1005 -0
- agno/workflow/__init__.py +23 -0
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +738 -0
- agno/workflow/loop.py +735 -0
- agno/workflow/parallel.py +824 -0
- agno/workflow/router.py +702 -0
- agno/workflow/step.py +1432 -0
- agno/workflow/steps.py +592 -0
- agno/workflow/types.py +520 -0
- agno/workflow/workflow.py +4321 -0
- agno-2.2.13.dist-info/METADATA +614 -0
- agno-2.2.13.dist-info/RECORD +575 -0
- agno-2.2.13.dist-info/WHEEL +5 -0
- agno-2.2.13.dist-info/licenses/LICENSE +201 -0
- agno-2.2.13.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any, Dict, Iterable, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from agno.filters import FilterExpr
|
|
5
|
+
from agno.knowledge.document import Document
|
|
6
|
+
from agno.knowledge.embedder import Embedder
|
|
7
|
+
from agno.utils.log import log_debug, log_error, log_info, log_warning
|
|
8
|
+
from agno.vectordb.base import VectorDb
|
|
9
|
+
from agno.vectordb.cassandra.index import AgnoMetadataVectorCassandraTable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Cassandra(VectorDb):
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
table_name: str,
|
|
16
|
+
keyspace: str,
|
|
17
|
+
embedder: Optional[Embedder] = None,
|
|
18
|
+
session=None,
|
|
19
|
+
name: Optional[str] = None,
|
|
20
|
+
description: Optional[str] = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
if not table_name:
|
|
23
|
+
raise ValueError("Table name must be provided.")
|
|
24
|
+
|
|
25
|
+
if not session:
|
|
26
|
+
raise ValueError("Session is not provided")
|
|
27
|
+
|
|
28
|
+
if not keyspace:
|
|
29
|
+
raise ValueError("Keyspace must be provided")
|
|
30
|
+
|
|
31
|
+
if embedder is None:
|
|
32
|
+
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
33
|
+
|
|
34
|
+
embedder = OpenAIEmbedder()
|
|
35
|
+
log_info("Embedder not provided, using OpenAIEmbedder as default.")
|
|
36
|
+
# Initialize base class with name and description
|
|
37
|
+
super().__init__(name=name, description=description)
|
|
38
|
+
|
|
39
|
+
self.table_name: str = table_name
|
|
40
|
+
self.embedder: Embedder = embedder
|
|
41
|
+
self.session = session
|
|
42
|
+
self.keyspace: str = keyspace
|
|
43
|
+
self.initialize_table()
|
|
44
|
+
|
|
45
|
+
def initialize_table(self):
|
|
46
|
+
self.table = AgnoMetadataVectorCassandraTable(
|
|
47
|
+
session=self.session,
|
|
48
|
+
keyspace=self.keyspace,
|
|
49
|
+
vector_dimension=1024,
|
|
50
|
+
table=self.table_name,
|
|
51
|
+
primary_key_type="TEXT",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def create(self) -> None:
|
|
55
|
+
"""Create the table in Cassandra for storing vectors and metadata."""
|
|
56
|
+
if not self.exists():
|
|
57
|
+
log_debug(f"Cassandra VectorDB : Creating table {self.table_name}")
|
|
58
|
+
self.initialize_table()
|
|
59
|
+
|
|
60
|
+
async def async_create(self) -> None:
|
|
61
|
+
"""Create the table asynchronously by running in a thread."""
|
|
62
|
+
await asyncio.to_thread(self.create)
|
|
63
|
+
|
|
64
|
+
def _row_to_document(self, row: Dict[str, Any]) -> Document:
|
|
65
|
+
metadata = row["metadata"]
|
|
66
|
+
return Document(
|
|
67
|
+
id=row["row_id"],
|
|
68
|
+
content=row["body_blob"],
|
|
69
|
+
meta_data=metadata,
|
|
70
|
+
embedding=row["vector"],
|
|
71
|
+
name=row["document_name"],
|
|
72
|
+
content_id=metadata.get("content_id"),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def name_exists(self, name: str) -> bool:
|
|
76
|
+
"""Check if a document exists by name."""
|
|
77
|
+
query = f"SELECT COUNT(*) FROM {self.keyspace}.{self.table_name} WHERE document_name = %s ALLOW FILTERING"
|
|
78
|
+
result = self.session.execute(query, (name,))
|
|
79
|
+
return result.one()[0] > 0
|
|
80
|
+
|
|
81
|
+
async def async_name_exists(self, name: str) -> bool:
|
|
82
|
+
"""Check if a document with given name exists asynchronously."""
|
|
83
|
+
return await asyncio.to_thread(self.name_exists, name)
|
|
84
|
+
|
|
85
|
+
def id_exists(self, id: str) -> bool:
|
|
86
|
+
"""Check if a document exists by ID."""
|
|
87
|
+
query = f"SELECT COUNT(*) FROM {self.keyspace}.{self.table_name} WHERE row_id = %s ALLOW FILTERING"
|
|
88
|
+
result = self.session.execute(query, (id,))
|
|
89
|
+
return result.one()[0] > 0
|
|
90
|
+
|
|
91
|
+
def content_hash_exists(self, content_hash: str) -> bool:
|
|
92
|
+
"""Check if a document exists by content hash."""
|
|
93
|
+
query = f"SELECT COUNT(*) FROM {self.keyspace}.{self.table_name} WHERE metadata_s['content_hash'] = %s ALLOW FILTERING"
|
|
94
|
+
result = self.session.execute(query, (content_hash,))
|
|
95
|
+
return result.one()[0] > 0
|
|
96
|
+
|
|
97
|
+
def insert(self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
|
|
98
|
+
log_info(f"Cassandra VectorDB : Inserting Documents to the table {self.table_name}")
|
|
99
|
+
futures = []
|
|
100
|
+
for doc in documents:
|
|
101
|
+
doc.embed(embedder=self.embedder)
|
|
102
|
+
metadata = {key: str(value) for key, value in doc.meta_data.items()}
|
|
103
|
+
metadata.update(filters or {})
|
|
104
|
+
metadata["content_id"] = doc.content_id or ""
|
|
105
|
+
metadata["content_hash"] = content_hash
|
|
106
|
+
futures.append(
|
|
107
|
+
self.table.put_async(
|
|
108
|
+
row_id=doc.id,
|
|
109
|
+
vector=doc.embedding,
|
|
110
|
+
metadata=metadata or {},
|
|
111
|
+
body_blob=doc.content,
|
|
112
|
+
document_name=doc.name,
|
|
113
|
+
)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
for f in futures:
|
|
117
|
+
f.result()
|
|
118
|
+
|
|
119
|
+
async def async_insert(
|
|
120
|
+
self,
|
|
121
|
+
content_hash: str,
|
|
122
|
+
documents: List[Document],
|
|
123
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
124
|
+
) -> None:
|
|
125
|
+
"""Insert documents asynchronously by running in a thread."""
|
|
126
|
+
log_info(f"Cassandra VectorDB : Inserting Documents to the table {self.table_name}")
|
|
127
|
+
|
|
128
|
+
if self.embedder.enable_batch and hasattr(self.embedder, "async_get_embeddings_batch_and_usage"):
|
|
129
|
+
# Use batch embedding when enabled and supported
|
|
130
|
+
try:
|
|
131
|
+
# Extract content from all documents
|
|
132
|
+
doc_contents = [doc.content for doc in documents]
|
|
133
|
+
|
|
134
|
+
# Get batch embeddings and usage
|
|
135
|
+
embeddings, usages = await self.embedder.async_get_embeddings_batch_and_usage(doc_contents)
|
|
136
|
+
|
|
137
|
+
# Process documents with pre-computed embeddings
|
|
138
|
+
for j, doc in enumerate(documents):
|
|
139
|
+
try:
|
|
140
|
+
if j < len(embeddings):
|
|
141
|
+
doc.embedding = embeddings[j]
|
|
142
|
+
doc.usage = usages[j] if j < len(usages) else None
|
|
143
|
+
except Exception as e:
|
|
144
|
+
log_error(f"Error assigning batch embedding to document '{doc.name}': {e}")
|
|
145
|
+
|
|
146
|
+
except Exception as e:
|
|
147
|
+
# Check if this is a rate limit error - don't fall back as it would make things worse
|
|
148
|
+
error_str = str(e).lower()
|
|
149
|
+
is_rate_limit = any(
|
|
150
|
+
phrase in error_str
|
|
151
|
+
for phrase in ["rate limit", "too many requests", "429", "trial key", "api calls / minute"]
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if is_rate_limit:
|
|
155
|
+
log_error(f"Rate limit detected during batch embedding. {e}")
|
|
156
|
+
raise e
|
|
157
|
+
else:
|
|
158
|
+
log_error(f"Async batch embedding failed, falling back to individual embeddings: {e}")
|
|
159
|
+
# Fall back to individual embedding
|
|
160
|
+
for doc in documents:
|
|
161
|
+
try:
|
|
162
|
+
embed_tasks = [doc.async_embed(embedder=self.embedder)]
|
|
163
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
164
|
+
except Exception as e:
|
|
165
|
+
log_error(f"Error processing document '{doc.name}': {e}")
|
|
166
|
+
else:
|
|
167
|
+
# Use individual embedding (original behavior)
|
|
168
|
+
for doc in documents:
|
|
169
|
+
try:
|
|
170
|
+
embed_tasks = [doc.async_embed(embedder=self.embedder)]
|
|
171
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
log_error(f"Error processing document '{doc.name}': {e}")
|
|
174
|
+
|
|
175
|
+
futures = []
|
|
176
|
+
for doc in documents:
|
|
177
|
+
metadata = {key: str(value) for key, value in doc.meta_data.items()}
|
|
178
|
+
metadata.update(filters or {})
|
|
179
|
+
metadata["content_id"] = doc.content_id or ""
|
|
180
|
+
metadata["content_hash"] = content_hash
|
|
181
|
+
futures.append(
|
|
182
|
+
self.table.put_async(
|
|
183
|
+
row_id=doc.id,
|
|
184
|
+
vector=doc.embedding,
|
|
185
|
+
metadata=metadata or {},
|
|
186
|
+
body_blob=doc.content,
|
|
187
|
+
document_name=doc.name,
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
for f in futures:
|
|
192
|
+
f.result()
|
|
193
|
+
|
|
194
|
+
def upsert(self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None) -> None:
|
|
195
|
+
"""Insert or update documents based on primary key."""
|
|
196
|
+
if self.content_hash_exists(content_hash):
|
|
197
|
+
self.delete_by_content_hash(content_hash)
|
|
198
|
+
self.insert(content_hash, documents, filters)
|
|
199
|
+
|
|
200
|
+
async def async_upsert(
|
|
201
|
+
self, content_hash: str, documents: List[Document], filters: Optional[Dict[str, Any]] = None
|
|
202
|
+
) -> None:
|
|
203
|
+
"""Upsert documents asynchronously by running in a thread."""
|
|
204
|
+
if self.content_hash_exists(content_hash):
|
|
205
|
+
self.delete_by_content_hash(content_hash)
|
|
206
|
+
await self.async_insert(content_hash, documents, filters)
|
|
207
|
+
|
|
208
|
+
def search(
|
|
209
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
210
|
+
) -> List[Document]:
|
|
211
|
+
"""Keyword-based search on document metadata."""
|
|
212
|
+
log_debug(f"Cassandra VectorDB : Performing Vector Search on {self.table_name} with query {query}")
|
|
213
|
+
if filters is not None:
|
|
214
|
+
log_warning("Filters are not yet supported in Cassandra. No filters will be applied.")
|
|
215
|
+
return self.vector_search(query=query, limit=limit)
|
|
216
|
+
|
|
217
|
+
async def async_search(
|
|
218
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
219
|
+
) -> List[Document]:
|
|
220
|
+
"""Search asynchronously by running in a thread."""
|
|
221
|
+
return await asyncio.to_thread(self.search, query, limit, filters)
|
|
222
|
+
|
|
223
|
+
def _search_to_documents(
|
|
224
|
+
self,
|
|
225
|
+
hits: Iterable[Dict[str, Any]],
|
|
226
|
+
) -> List[Document]:
|
|
227
|
+
return [self._row_to_document(row=hit) for hit in hits]
|
|
228
|
+
|
|
229
|
+
def vector_search(
|
|
230
|
+
self, query: str, limit: int = 5, filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None
|
|
231
|
+
) -> List[Document]:
|
|
232
|
+
"""Vector similarity search implementation."""
|
|
233
|
+
query_embedding = self.embedder.get_embedding(query)
|
|
234
|
+
hits = list(
|
|
235
|
+
self.table.metric_ann_search(
|
|
236
|
+
vector=query_embedding,
|
|
237
|
+
n=limit,
|
|
238
|
+
metric="cos",
|
|
239
|
+
)
|
|
240
|
+
)
|
|
241
|
+
d = self._search_to_documents(hits)
|
|
242
|
+
return d
|
|
243
|
+
|
|
244
|
+
def drop(self) -> None:
|
|
245
|
+
"""Drop the vector table in Cassandra."""
|
|
246
|
+
log_debug(f"Cassandra VectorDB : Dropping Table {self.table_name}")
|
|
247
|
+
drop_table_query = f"DROP TABLE IF EXISTS {self.keyspace}.{self.table_name}"
|
|
248
|
+
self.session.execute(drop_table_query)
|
|
249
|
+
|
|
250
|
+
async def async_drop(self) -> None:
|
|
251
|
+
"""Drop the table asynchronously by running in a thread."""
|
|
252
|
+
await asyncio.to_thread(self.drop)
|
|
253
|
+
|
|
254
|
+
def exists(self) -> bool:
|
|
255
|
+
"""Check if the table exists in Cassandra."""
|
|
256
|
+
check_table_query = """
|
|
257
|
+
SELECT * FROM system_schema.tables
|
|
258
|
+
WHERE keyspace_name = %s AND table_name = %s
|
|
259
|
+
"""
|
|
260
|
+
result = self.session.execute(check_table_query, (self.keyspace, self.table_name))
|
|
261
|
+
return bool(result.one())
|
|
262
|
+
|
|
263
|
+
async def async_exists(self) -> bool:
|
|
264
|
+
"""Check if table exists asynchronously by running in a thread."""
|
|
265
|
+
return await asyncio.to_thread(self.exists)
|
|
266
|
+
|
|
267
|
+
def delete(self) -> bool:
|
|
268
|
+
"""Delete all documents in the table."""
|
|
269
|
+
log_debug(f"Cassandra VectorDB : Clearing the table {self.table_name}")
|
|
270
|
+
self.table.clear()
|
|
271
|
+
return True
|
|
272
|
+
|
|
273
|
+
def delete_by_id(self, id: str) -> bool:
|
|
274
|
+
"""
|
|
275
|
+
Delete a document by its ID.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
id (str): The document ID to delete
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
bool: True if document was deleted, False otherwise
|
|
282
|
+
"""
|
|
283
|
+
try:
|
|
284
|
+
log_debug(f"Cassandra VectorDB : Deleting document with ID {id}")
|
|
285
|
+
# Check if document exists before deletion
|
|
286
|
+
if not self.id_exists(id):
|
|
287
|
+
return False
|
|
288
|
+
|
|
289
|
+
query = f"DELETE FROM {self.keyspace}.{self.table_name} WHERE row_id = %s"
|
|
290
|
+
self.session.execute(query, (id,))
|
|
291
|
+
return True
|
|
292
|
+
except Exception as e:
|
|
293
|
+
log_info(f"Error deleting document with ID {id}: {e}")
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
def delete_by_name(self, name: str) -> bool:
|
|
297
|
+
"""
|
|
298
|
+
Delete documents by name.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
name (str): The document name to delete
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
bool: True if documents were deleted, False otherwise
|
|
305
|
+
"""
|
|
306
|
+
try:
|
|
307
|
+
log_debug(f"Cassandra VectorDB : Deleting documents with name {name}")
|
|
308
|
+
# Check if document exists before deletion
|
|
309
|
+
if not self.name_exists(name):
|
|
310
|
+
return False
|
|
311
|
+
|
|
312
|
+
# Query to find documents with matching name
|
|
313
|
+
query = f"SELECT row_id, document_name FROM {self.keyspace}.{self.table_name} ALLOW FILTERING"
|
|
314
|
+
result = self.session.execute(query)
|
|
315
|
+
|
|
316
|
+
deleted_count = 0
|
|
317
|
+
for row in result:
|
|
318
|
+
# Check if the row's document_name matches our criteria
|
|
319
|
+
# Use attribute access for Row objects
|
|
320
|
+
row_name = getattr(row, "document_name", None)
|
|
321
|
+
if row_name == name:
|
|
322
|
+
# Delete this specific document
|
|
323
|
+
delete_query = f"DELETE FROM {self.keyspace}.{self.table_name} WHERE row_id = %s"
|
|
324
|
+
self.session.execute(delete_query, (getattr(row, "row_id"),))
|
|
325
|
+
deleted_count += 1
|
|
326
|
+
|
|
327
|
+
return deleted_count > 0
|
|
328
|
+
except Exception as e:
|
|
329
|
+
log_info(f"Error deleting documents with name {name}: {e}")
|
|
330
|
+
return False
|
|
331
|
+
|
|
332
|
+
def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
|
|
333
|
+
"""
|
|
334
|
+
Delete documents by metadata.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
metadata (Dict[str, Any]): The metadata to match for deletion
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
bool: True if documents were deleted, False otherwise
|
|
341
|
+
"""
|
|
342
|
+
try:
|
|
343
|
+
log_debug(f"Cassandra VectorDB : Deleting documents with metadata {metadata}")
|
|
344
|
+
# For metadata deletion, we need to query first to find matching documents
|
|
345
|
+
# Then delete them by their IDs
|
|
346
|
+
query = f"SELECT row_id, metadata_s FROM {self.keyspace}.{self.table_name} ALLOW FILTERING"
|
|
347
|
+
result = self.session.execute(query)
|
|
348
|
+
|
|
349
|
+
deleted_count = 0
|
|
350
|
+
for row in result:
|
|
351
|
+
# Check if the row's metadata matches our criteria
|
|
352
|
+
# Use attribute access for Row objects
|
|
353
|
+
row_metadata = getattr(row, "metadata_s", {})
|
|
354
|
+
if self._metadata_matches(row_metadata, metadata):
|
|
355
|
+
# Delete this specific document
|
|
356
|
+
delete_query = f"DELETE FROM {self.keyspace}.{self.table_name} WHERE row_id = %s"
|
|
357
|
+
self.session.execute(delete_query, (getattr(row, "row_id"),))
|
|
358
|
+
deleted_count += 1
|
|
359
|
+
|
|
360
|
+
return deleted_count > 0
|
|
361
|
+
except Exception as e:
|
|
362
|
+
log_debug(f"Error deleting documents with metadata {metadata}: {e}")
|
|
363
|
+
return False
|
|
364
|
+
|
|
365
|
+
def delete_by_content_id(self, content_id: str) -> bool:
|
|
366
|
+
"""
|
|
367
|
+
Delete documents by content ID.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
content_id (str): The content ID to delete
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
bool: True if documents were deleted, False otherwise
|
|
374
|
+
"""
|
|
375
|
+
try:
|
|
376
|
+
log_debug(f"Cassandra VectorDB : Deleting documents with content_id {content_id}")
|
|
377
|
+
# Query to find documents with matching content_id in metadata
|
|
378
|
+
query = f"SELECT row_id, metadata_s FROM {self.keyspace}.{self.table_name} ALLOW FILTERING"
|
|
379
|
+
result = self.session.execute(query)
|
|
380
|
+
deleted_count = 0
|
|
381
|
+
for row in result:
|
|
382
|
+
# Check if the row's metadata contains the content_id
|
|
383
|
+
# Use attribute access for Row objects
|
|
384
|
+
row_metadata = getattr(row, "metadata_s", {})
|
|
385
|
+
if row_metadata.get("content_id") == content_id:
|
|
386
|
+
# Delete this specific document
|
|
387
|
+
delete_query = f"DELETE FROM {self.keyspace}.{self.table_name} WHERE row_id = %s"
|
|
388
|
+
self.session.execute(delete_query, (getattr(row, "row_id"),))
|
|
389
|
+
deleted_count += 1
|
|
390
|
+
|
|
391
|
+
return deleted_count > 0
|
|
392
|
+
except Exception as e:
|
|
393
|
+
log_info(f"Error deleting documents with content_id {content_id}: {e}")
|
|
394
|
+
return False
|
|
395
|
+
|
|
396
|
+
def delete_by_content_hash(self, content_hash: str) -> bool:
|
|
397
|
+
"""
|
|
398
|
+
Delete documents by content hash.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
content_hash (str): The content hash to delete
|
|
402
|
+
|
|
403
|
+
Returns:
|
|
404
|
+
bool: True if documents were deleted, False otherwise
|
|
405
|
+
"""
|
|
406
|
+
try:
|
|
407
|
+
log_debug(f"Cassandra VectorDB : Deleting documents with content_hash {content_hash}")
|
|
408
|
+
# Query to find documents with matching content_hash in metadata
|
|
409
|
+
query = f"SELECT row_id, metadata_s FROM {self.keyspace}.{self.table_name} ALLOW FILTERING"
|
|
410
|
+
result = self.session.execute(query)
|
|
411
|
+
deleted_count = 0
|
|
412
|
+
for row in result:
|
|
413
|
+
# Check if the row's metadata contains the content_hash
|
|
414
|
+
# Use attribute access for Row objects
|
|
415
|
+
row_metadata = getattr(row, "metadata_s", {})
|
|
416
|
+
if row_metadata.get("content_hash") == content_hash:
|
|
417
|
+
# Delete this specific document
|
|
418
|
+
delete_query = f"DELETE FROM {self.keyspace}.{self.table_name} WHERE row_id = %s"
|
|
419
|
+
self.session.execute(delete_query, (getattr(row, "row_id"),))
|
|
420
|
+
deleted_count += 1
|
|
421
|
+
|
|
422
|
+
return deleted_count > 0
|
|
423
|
+
except Exception as e:
|
|
424
|
+
log_info(f"Error deleting documents with content_hash {content_hash}: {e}")
|
|
425
|
+
return False
|
|
426
|
+
|
|
427
|
+
def _metadata_matches(self, row_metadata: Dict[str, Any], target_metadata: Dict[str, Any]) -> bool:
|
|
428
|
+
"""
|
|
429
|
+
Check if row metadata matches target metadata criteria.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
row_metadata (Dict[str, Any]): The metadata from the database row
|
|
433
|
+
target_metadata (Dict[str, Any]): The target metadata to match against
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
bool: True if metadata matches, False otherwise
|
|
437
|
+
"""
|
|
438
|
+
try:
|
|
439
|
+
for key, value in target_metadata.items():
|
|
440
|
+
if key not in row_metadata:
|
|
441
|
+
return False
|
|
442
|
+
|
|
443
|
+
# Handle boolean values specially
|
|
444
|
+
if isinstance(value, bool):
|
|
445
|
+
if row_metadata[key] != value:
|
|
446
|
+
return False
|
|
447
|
+
else:
|
|
448
|
+
# For non-boolean values, convert to string for comparison
|
|
449
|
+
if row_metadata[key] != str(value):
|
|
450
|
+
return False
|
|
451
|
+
return True
|
|
452
|
+
except Exception:
|
|
453
|
+
return False
|
|
454
|
+
|
|
455
|
+
def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
|
|
456
|
+
"""
|
|
457
|
+
Update the metadata for a document.
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
content_id (str): The content ID to update
|
|
461
|
+
metadata (Dict[str, Any]): The metadata to update
|
|
462
|
+
"""
|
|
463
|
+
try:
|
|
464
|
+
log_debug(f"Cassandra VectorDB : Updating metadata for content_id {content_id}")
|
|
465
|
+
|
|
466
|
+
# First, find all documents with the given content_id
|
|
467
|
+
query = f"SELECT row_id, metadata_s FROM {self.keyspace}.{self.table_name} ALLOW FILTERING"
|
|
468
|
+
result = self.session.execute(query)
|
|
469
|
+
|
|
470
|
+
updated_count = 0
|
|
471
|
+
for row in result:
|
|
472
|
+
row_metadata = getattr(row, "metadata_s", {})
|
|
473
|
+
if row_metadata.get("content_id") == content_id:
|
|
474
|
+
# Merge existing metadata with new metadata
|
|
475
|
+
updated_metadata = row_metadata.copy()
|
|
476
|
+
# Convert new metadata values to strings (Cassandra requirement)
|
|
477
|
+
string_metadata = {key: str(value) for key, value in metadata.items()}
|
|
478
|
+
updated_metadata.update(string_metadata)
|
|
479
|
+
|
|
480
|
+
# Update the document with merged metadata
|
|
481
|
+
row_id = getattr(row, "row_id")
|
|
482
|
+
update_query = f"""
|
|
483
|
+
UPDATE {self.keyspace}.{self.table_name}
|
|
484
|
+
SET metadata_s = %s
|
|
485
|
+
WHERE row_id = %s
|
|
486
|
+
"""
|
|
487
|
+
self.session.execute(update_query, (updated_metadata, row_id))
|
|
488
|
+
updated_count += 1
|
|
489
|
+
|
|
490
|
+
if updated_count == 0:
|
|
491
|
+
log_debug(f"No documents found with content_id {content_id}")
|
|
492
|
+
else:
|
|
493
|
+
log_debug(f"Updated metadata for {updated_count} documents with content_id {content_id}")
|
|
494
|
+
|
|
495
|
+
except Exception as e:
|
|
496
|
+
log_error(f"Error updating metadata for content_id {content_id}: {e}")
|
|
497
|
+
raise
|
|
498
|
+
|
|
499
|
+
def get_supported_search_types(self) -> List[str]:
|
|
500
|
+
"""Get the supported search types for this vector database."""
|
|
501
|
+
return [] # Cassandra doesn't use SearchType enum
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from cassio.table.mixins.base_table import BaseTableMixin
|
|
4
|
+
from cassio.table.table_types import ColumnSpecType
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExtraParamMixin(BaseTableMixin):
|
|
8
|
+
def _schema_da(self) -> List[ColumnSpecType]:
|
|
9
|
+
return super()._schema_da() + [
|
|
10
|
+
("document_name", "TEXT"),
|
|
11
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from cassio.table.base_table import BaseTable
|
|
3
|
+
from cassio.table.mixins.metadata import MetadataMixin
|
|
4
|
+
from cassio.table.mixins.type_normalizer import TypeNormalizerMixin
|
|
5
|
+
from cassio.table.mixins.vector import VectorMixin
|
|
6
|
+
|
|
7
|
+
from .extra_param_mixin import ExtraParamMixin
|
|
8
|
+
except (ImportError, ModuleNotFoundError):
|
|
9
|
+
raise ImportError("Could not import cassio python package. Please install it with pip install cassio.")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgnoMetadataVectorCassandraTable(ExtraParamMixin, TypeNormalizerMixin, MetadataMixin, VectorMixin, BaseTable):
|
|
13
|
+
pass
|