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