agno 1.8.1__py3-none-any.whl → 2.0.0a1__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 +19 -27
- agno/agent/agent.py +2778 -4123
- agno/api/agent.py +9 -65
- agno/api/api.py +5 -46
- agno/api/evals.py +6 -17
- 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 +9 -64
- 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 +1749 -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 +1438 -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 +888 -0
- agno/db/in_memory/utils.py +172 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1051 -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 +1417 -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 +298 -0
- agno/db/postgres/__init__.py +3 -0
- agno/db/postgres/postgres.py +1720 -0
- agno/db/postgres/schemas.py +124 -0
- agno/db/postgres/utils.py +281 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +1371 -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 +1722 -0
- agno/db/singlestore/utils.py +327 -0
- agno/db/sqlite/__init__.py +3 -0
- agno/db/sqlite/schemas.py +119 -0
- agno/db/sqlite/sqlite.py +1680 -0
- agno/db/sqlite/utils.py +269 -0
- agno/db/utils.py +88 -0
- agno/eval/__init__.py +14 -0
- agno/eval/accuracy.py +142 -43
- 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 +1515 -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 +68 -15
- agno/knowledge/reader/docx_reader.py +83 -0
- agno/{document → knowledge}/reader/firecrawl_reader.py +42 -21
- agno/knowledge/reader/gcs_reader.py +67 -0
- agno/{document → knowledge}/reader/json_reader.py +30 -9
- agno/{document → knowledge}/reader/markdown_reader.py +36 -9
- agno/{document → knowledge}/reader/pdf_reader.py +79 -21
- agno/knowledge/reader/reader_factory.py +275 -0
- agno/knowledge/reader/s3_reader.py +171 -0
- agno/{document → knowledge}/reader/text_reader.py +31 -10
- agno/knowledge/reader/url_reader.py +84 -0
- agno/knowledge/reader/web_search_reader.py +389 -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/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 +129 -82
- 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 +347 -287
- agno/models/cerebras/cerebras.py +84 -27
- agno/models/cohere/chat.py +106 -98
- agno/models/google/gemini.py +100 -42
- 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 +38 -144
- 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 +84 -46
- agno/models/openai/chat.py +121 -23
- 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 +14 -8
- 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 +393 -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 +33 -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 +30 -0
- agno/os/router.py +843 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +204 -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 +413 -0
- agno/os/routers/knowledge/schemas.py +118 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +179 -0
- agno/os/routers/memory/schemas.py +58 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +58 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +163 -0
- agno/os/schema.py +892 -0
- agno/{app/playground → os}/settings.py +8 -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/{response.py → agent.py} +144 -72
- agno/run/base.py +44 -58
- agno/run/cancel.py +83 -0
- agno/run/team.py +133 -77
- agno/run/workflow.py +537 -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 +2961 -4253
- 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 +42 -22
- agno/tools/browserbase.py +13 -4
- agno/tools/calcom.py +12 -10
- agno/tools/calculator.py +10 -27
- agno/tools/cartesia.py +18 -13
- 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 +18 -11
- agno/tools/daytona.py +13 -16
- agno/tools/decorator.py +6 -3
- agno/tools/desi_vocal.py +16 -7
- 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 +61 -61
- agno/tools/eleven_labs.py +35 -28
- agno/tools/email.py +4 -1
- agno/tools/evm.py +7 -1
- agno/tools/exa.py +19 -14
- agno/tools/fal.py +29 -29
- agno/tools/file.py +9 -8
- agno/tools/financial_datasets.py +25 -44
- agno/tools/firecrawl.py +22 -22
- agno/tools/function.py +68 -17
- agno/tools/giphy.py +22 -10
- 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 +31 -19
- 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 +32 -14
- agno/tools/models/gemini.py +58 -31
- agno/tools/models/groq.py +29 -20
- agno/tools/models/nebius.py +27 -11
- agno/tools/models_labs.py +39 -15
- 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 +57 -26
- agno/tools/openbb.py +12 -11
- agno/tools/opencv.py +62 -46
- 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 +54 -41
- 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 +95 -118
- agno/utils/knowledge.py +29 -0
- agno/utils/log.py +2 -2
- agno/utils/mcp.py +11 -5
- agno/utils/media.py +39 -0
- agno/utils/message.py +12 -1
- agno/utils/models/claude.py +6 -4
- agno/utils/models/mistral.py +8 -7
- agno/utils/models/schema_utils.py +3 -3
- agno/utils/pprint.py +33 -32
- agno/utils/print_response/agent.py +779 -0
- agno/utils/print_response/team.py +1565 -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/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} +200 -42
- agno/workflow/{v2/steps.py → steps.py} +147 -66
- agno/workflow/types.py +482 -0
- agno/workflow/workflow.py +2394 -696
- agno-2.0.0a1.dist-info/METADATA +355 -0
- agno-2.0.0a1.dist-info/RECORD +514 -0
- agno/agent/metrics.py +0 -107
- 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 -1050
- agno/app/playground/deploy.py +0 -249
- agno/app/playground/operator.py +0 -183
- agno/app/playground/schemas.py +0 -220
- agno/app/playground/serve.py +0 -55
- agno/app/playground/sync_router.py +0 -1042
- 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/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 -3312
- 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.1.dist-info/METADATA +0 -982
- agno-1.8.1.dist-info/RECORD +0 -566
- agno-1.8.1.dist-info/entry_points.txt +0 -3
- /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.1.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {agno-1.8.1.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from hashlib import md5
|
|
2
3
|
from math import sqrt
|
|
3
4
|
from typing import Any, Dict, List, Optional, Union, cast
|
|
4
5
|
|
|
5
6
|
try:
|
|
7
|
+
from sqlalchemy import update
|
|
6
8
|
from sqlalchemy.dialects import postgresql
|
|
7
9
|
from sqlalchemy.engine import Engine, create_engine
|
|
8
10
|
from sqlalchemy.inspection import inspect
|
|
@@ -10,6 +12,7 @@ try:
|
|
|
10
12
|
from sqlalchemy.schema import Column, Index, MetaData, Table
|
|
11
13
|
from sqlalchemy.sql.expression import bindparam, desc, func, select, text
|
|
12
14
|
from sqlalchemy.types import DateTime, String
|
|
15
|
+
|
|
13
16
|
except ImportError:
|
|
14
17
|
raise ImportError("`sqlalchemy` not installed. Please install using `pip install sqlalchemy psycopg`")
|
|
15
18
|
|
|
@@ -18,11 +21,10 @@ try:
|
|
|
18
21
|
except ImportError:
|
|
19
22
|
raise ImportError("`pgvector` not installed. Please install using `pip install pgvector`")
|
|
20
23
|
|
|
21
|
-
from agno.document import Document
|
|
22
|
-
from agno.embedder import Embedder
|
|
23
|
-
from agno.reranker.base import Reranker
|
|
24
|
+
from agno.knowledge.document import Document
|
|
25
|
+
from agno.knowledge.embedder import Embedder
|
|
26
|
+
from agno.knowledge.reranker.base import Reranker
|
|
24
27
|
from agno.utils.log import log_debug, log_info, logger
|
|
25
|
-
from agno.utils.string import safe_content_hash
|
|
26
28
|
from agno.vectordb.base import VectorDb
|
|
27
29
|
from agno.vectordb.distance import Distance
|
|
28
30
|
from agno.vectordb.pgvector.index import HNSW, Ivfflat
|
|
@@ -53,6 +55,7 @@ class PgVector(VectorDb):
|
|
|
53
55
|
schema_version: int = 1,
|
|
54
56
|
auto_upgrade_schema: bool = False,
|
|
55
57
|
reranker: Optional[Reranker] = None,
|
|
58
|
+
use_batch: bool = False,
|
|
56
59
|
):
|
|
57
60
|
"""
|
|
58
61
|
Initialize the PgVector instance.
|
|
@@ -93,10 +96,11 @@ class PgVector(VectorDb):
|
|
|
93
96
|
self.db_url: Optional[str] = db_url
|
|
94
97
|
self.db_engine: Engine = db_engine
|
|
95
98
|
self.metadata: MetaData = MetaData(schema=self.schema)
|
|
99
|
+
self.use_batch: bool = use_batch
|
|
96
100
|
|
|
97
101
|
# Embedder for embedding the document contents
|
|
98
102
|
if embedder is None:
|
|
99
|
-
from agno.embedder.openai import OpenAIEmbedder
|
|
103
|
+
from agno.knowledge.embedder.openai import OpenAIEmbedder
|
|
100
104
|
|
|
101
105
|
embedder = OpenAIEmbedder()
|
|
102
106
|
log_info("Embedder not provided, using OpenAIEmbedder as default.")
|
|
@@ -155,6 +159,7 @@ class PgVector(VectorDb):
|
|
|
155
159
|
Column("created_at", DateTime(timezone=True), server_default=func.now()),
|
|
156
160
|
Column("updated_at", DateTime(timezone=True), onupdate=func.now()),
|
|
157
161
|
Column("content_hash", String),
|
|
162
|
+
Column("content_id", String),
|
|
158
163
|
extend_existing=True,
|
|
159
164
|
)
|
|
160
165
|
|
|
@@ -162,7 +167,7 @@ class PgVector(VectorDb):
|
|
|
162
167
|
Index(f"idx_{self.table_name}_id", table.c.id)
|
|
163
168
|
Index(f"idx_{self.table_name}_name", table.c.name)
|
|
164
169
|
Index(f"idx_{self.table_name}_content_hash", table.c.content_hash)
|
|
165
|
-
|
|
170
|
+
Index(f"idx_{self.table_name}_content_id", table.c.content_id)
|
|
166
171
|
return table
|
|
167
172
|
|
|
168
173
|
def get_table(self) -> Table:
|
|
@@ -229,23 +234,6 @@ class PgVector(VectorDb):
|
|
|
229
234
|
logger.error(f"Error checking if record exists: {e}")
|
|
230
235
|
return False
|
|
231
236
|
|
|
232
|
-
def doc_exists(self, document: Document) -> bool:
|
|
233
|
-
"""
|
|
234
|
-
Check if a document with the same content hash exists in the table.
|
|
235
|
-
|
|
236
|
-
Args:
|
|
237
|
-
document (Document): The document to check.
|
|
238
|
-
|
|
239
|
-
Returns:
|
|
240
|
-
bool: True if the document exists, False otherwise.
|
|
241
|
-
"""
|
|
242
|
-
content_hash = safe_content_hash(document.content)
|
|
243
|
-
return self._record_exists(self.table.c.content_hash, content_hash)
|
|
244
|
-
|
|
245
|
-
async def async_doc_exists(self, document: Document) -> bool:
|
|
246
|
-
"""Check if document exists asynchronously by running in a thread."""
|
|
247
|
-
return await asyncio.to_thread(self.doc_exists, document)
|
|
248
|
-
|
|
249
237
|
def name_exists(self, name: str) -> bool:
|
|
250
238
|
"""
|
|
251
239
|
Check if a document with the given name exists in the table.
|
|
@@ -274,6 +262,12 @@ class PgVector(VectorDb):
|
|
|
274
262
|
"""
|
|
275
263
|
return self._record_exists(self.table.c.id, id)
|
|
276
264
|
|
|
265
|
+
def content_hash_exists(self, content_hash: str) -> bool:
|
|
266
|
+
"""
|
|
267
|
+
Check if a document with the given content hash exists in the table.
|
|
268
|
+
"""
|
|
269
|
+
return self._record_exists(self.table.c.content_hash, content_hash)
|
|
270
|
+
|
|
277
271
|
def _clean_content(self, content: str) -> str:
|
|
278
272
|
"""
|
|
279
273
|
Clean the content by replacing null characters.
|
|
@@ -288,6 +282,7 @@ class PgVector(VectorDb):
|
|
|
288
282
|
|
|
289
283
|
def insert(
|
|
290
284
|
self,
|
|
285
|
+
content_hash: str,
|
|
291
286
|
documents: List[Document],
|
|
292
287
|
filters: Optional[Dict[str, Any]] = None,
|
|
293
288
|
batch_size: int = 100,
|
|
@@ -296,6 +291,7 @@ class PgVector(VectorDb):
|
|
|
296
291
|
Insert documents into the database.
|
|
297
292
|
|
|
298
293
|
Args:
|
|
294
|
+
content_hash (str): The content hash to insert.
|
|
299
295
|
documents (List[Document]): List of documents to insert.
|
|
300
296
|
filters (Optional[Dict[str, Any]]): Filters to apply to the documents.
|
|
301
297
|
batch_size (int): Number of documents to insert in each batch.
|
|
@@ -310,7 +306,7 @@ class PgVector(VectorDb):
|
|
|
310
306
|
batch_records = []
|
|
311
307
|
for doc in batch_docs:
|
|
312
308
|
try:
|
|
313
|
-
batch_records.append(self._get_document_record(doc, filters))
|
|
309
|
+
batch_records.append(self._get_document_record(doc, filters, content_hash))
|
|
314
310
|
except Exception as e:
|
|
315
311
|
logger.error(f"Error processing document '{doc.name}': {e}")
|
|
316
312
|
|
|
@@ -327,9 +323,62 @@ class PgVector(VectorDb):
|
|
|
327
323
|
logger.error(f"Error inserting documents: {e}")
|
|
328
324
|
raise
|
|
329
325
|
|
|
330
|
-
async def async_insert(
|
|
331
|
-
|
|
332
|
-
|
|
326
|
+
async def async_insert(
|
|
327
|
+
self,
|
|
328
|
+
content_hash: str,
|
|
329
|
+
documents: List[Document],
|
|
330
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
331
|
+
batch_size: int = 100,
|
|
332
|
+
) -> None:
|
|
333
|
+
"""Insert documents asynchronously with parallel embedding."""
|
|
334
|
+
try:
|
|
335
|
+
with self.Session() as sess:
|
|
336
|
+
for i in range(0, len(documents), batch_size):
|
|
337
|
+
batch_docs = documents[i : i + batch_size]
|
|
338
|
+
log_debug(f"Processing batch starting at index {i}, size: {len(batch_docs)}")
|
|
339
|
+
try:
|
|
340
|
+
embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in batch_docs]
|
|
341
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
342
|
+
|
|
343
|
+
# Prepare documents for insertion
|
|
344
|
+
batch_records = []
|
|
345
|
+
for doc in batch_docs:
|
|
346
|
+
try:
|
|
347
|
+
cleaned_content = self._clean_content(doc.content)
|
|
348
|
+
record_id = doc.id or content_hash
|
|
349
|
+
|
|
350
|
+
meta_data = doc.meta_data or {}
|
|
351
|
+
if filters:
|
|
352
|
+
meta_data.update(filters)
|
|
353
|
+
|
|
354
|
+
record = {
|
|
355
|
+
"id": record_id,
|
|
356
|
+
"name": doc.name,
|
|
357
|
+
"meta_data": doc.meta_data,
|
|
358
|
+
"filters": filters,
|
|
359
|
+
"content": cleaned_content,
|
|
360
|
+
"embedding": doc.embedding,
|
|
361
|
+
"usage": doc.usage,
|
|
362
|
+
"content_hash": content_hash,
|
|
363
|
+
"content_id": doc.content_id,
|
|
364
|
+
}
|
|
365
|
+
batch_records.append(record)
|
|
366
|
+
except Exception as e:
|
|
367
|
+
logger.error(f"Error processing document '{doc.name}': {e}")
|
|
368
|
+
|
|
369
|
+
# Insert the batch of records
|
|
370
|
+
if batch_records:
|
|
371
|
+
insert_stmt = postgresql.insert(self.table)
|
|
372
|
+
sess.execute(insert_stmt, batch_records)
|
|
373
|
+
sess.commit() # Commit batch independently
|
|
374
|
+
log_info(f"Inserted batch of {len(batch_records)} documents.")
|
|
375
|
+
except Exception as e:
|
|
376
|
+
logger.error(f"Error with batch starting at index {i}: {e}")
|
|
377
|
+
sess.rollback() # Rollback the current batch if there's an error
|
|
378
|
+
raise
|
|
379
|
+
except Exception as e:
|
|
380
|
+
logger.error(f"Error inserting documents: {e}")
|
|
381
|
+
raise
|
|
333
382
|
|
|
334
383
|
def upsert_available(self) -> bool:
|
|
335
384
|
"""
|
|
@@ -342,6 +391,27 @@ class PgVector(VectorDb):
|
|
|
342
391
|
|
|
343
392
|
def upsert(
|
|
344
393
|
self,
|
|
394
|
+
content_hash: str,
|
|
395
|
+
documents: List[Document],
|
|
396
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
397
|
+
batch_size: int = 100,
|
|
398
|
+
) -> None:
|
|
399
|
+
"""
|
|
400
|
+
Upsert documents by content hash.
|
|
401
|
+
First delete all documents with the same content hash.
|
|
402
|
+
Then upsert the new documents.
|
|
403
|
+
"""
|
|
404
|
+
try:
|
|
405
|
+
if self.content_hash_exists(content_hash):
|
|
406
|
+
self._delete_by_content_hash(content_hash)
|
|
407
|
+
self._upsert(content_hash, documents, filters, batch_size)
|
|
408
|
+
except Exception as e:
|
|
409
|
+
logger.error(f"Error upserting documents by content hash: {e}")
|
|
410
|
+
raise
|
|
411
|
+
|
|
412
|
+
def _upsert(
|
|
413
|
+
self,
|
|
414
|
+
content_hash: str,
|
|
345
415
|
documents: List[Document],
|
|
346
416
|
filters: Optional[Dict[str, Any]] = None,
|
|
347
417
|
batch_size: int = 100,
|
|
@@ -358,16 +428,22 @@ class PgVector(VectorDb):
|
|
|
358
428
|
with self.Session() as sess:
|
|
359
429
|
for i in range(0, len(documents), batch_size):
|
|
360
430
|
batch_docs = documents[i : i + batch_size]
|
|
361
|
-
|
|
431
|
+
log_info(f"Processing batch starting at index {i}, size: {len(batch_docs)}")
|
|
362
432
|
try:
|
|
363
433
|
# Prepare documents for upserting
|
|
364
|
-
|
|
434
|
+
batch_records_dict: Dict[str, Dict[str, Any]] = {} # Use dict to deduplicate by ID
|
|
365
435
|
for doc in batch_docs:
|
|
366
436
|
try:
|
|
367
|
-
|
|
437
|
+
batch_records_dict[doc.id] = self._get_document_record(doc, filters, content_hash) # type: ignore
|
|
368
438
|
except Exception as e:
|
|
369
439
|
logger.error(f"Error processing document '{doc.name}': {e}")
|
|
370
440
|
|
|
441
|
+
# Convert dict to list for upsert
|
|
442
|
+
batch_records = list(batch_records_dict.values())
|
|
443
|
+
if not batch_records:
|
|
444
|
+
log_info("No valid records to upsert in this batch.")
|
|
445
|
+
continue
|
|
446
|
+
|
|
371
447
|
# Upsert the batch of records
|
|
372
448
|
insert_stmt = postgresql.insert(self.table).values(batch_records)
|
|
373
449
|
upsert_stmt = insert_stmt.on_conflict_do_update(
|
|
@@ -380,6 +456,7 @@ class PgVector(VectorDb):
|
|
|
380
456
|
"embedding": insert_stmt.excluded.embedding,
|
|
381
457
|
"usage": insert_stmt.excluded.usage,
|
|
382
458
|
"content_hash": insert_stmt.excluded.content_hash,
|
|
459
|
+
"content_id": insert_stmt.excluded.content_id,
|
|
383
460
|
},
|
|
384
461
|
)
|
|
385
462
|
sess.execute(upsert_stmt)
|
|
@@ -393,30 +470,155 @@ class PgVector(VectorDb):
|
|
|
393
470
|
logger.error(f"Error upserting documents: {e}")
|
|
394
471
|
raise
|
|
395
472
|
|
|
396
|
-
def _get_document_record(
|
|
473
|
+
def _get_document_record(
|
|
474
|
+
self, doc: Document, filters: Optional[Dict[str, Any]] = None, content_hash: str = ""
|
|
475
|
+
) -> Dict[str, Any]:
|
|
397
476
|
doc.embed(embedder=self.embedder)
|
|
398
477
|
cleaned_content = self._clean_content(doc.content)
|
|
399
|
-
|
|
400
|
-
_id = doc.id or content_hash
|
|
478
|
+
record_id = doc.id or content_hash
|
|
401
479
|
|
|
402
480
|
meta_data = doc.meta_data or {}
|
|
403
481
|
if filters:
|
|
404
482
|
meta_data.update(filters)
|
|
405
483
|
|
|
406
484
|
return {
|
|
407
|
-
"id":
|
|
485
|
+
"id": record_id,
|
|
408
486
|
"name": doc.name,
|
|
409
|
-
"meta_data": meta_data,
|
|
487
|
+
"meta_data": doc.meta_data,
|
|
410
488
|
"filters": filters,
|
|
411
489
|
"content": cleaned_content,
|
|
412
490
|
"embedding": doc.embedding,
|
|
413
491
|
"usage": doc.usage,
|
|
414
492
|
"content_hash": content_hash,
|
|
493
|
+
"content_id": doc.content_id,
|
|
415
494
|
}
|
|
416
495
|
|
|
417
|
-
async def async_upsert(
|
|
496
|
+
async def async_upsert(
|
|
497
|
+
self,
|
|
498
|
+
content_hash: str,
|
|
499
|
+
documents: List[Document],
|
|
500
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
501
|
+
batch_size: int = 100,
|
|
502
|
+
) -> None:
|
|
418
503
|
"""Upsert documents asynchronously by running in a thread."""
|
|
419
|
-
|
|
504
|
+
try:
|
|
505
|
+
if self.content_hash_exists(content_hash):
|
|
506
|
+
self._delete_by_content_hash(content_hash)
|
|
507
|
+
await self._async_upsert(content_hash, documents, filters, batch_size)
|
|
508
|
+
except Exception as e:
|
|
509
|
+
logger.error(f"Error upserting documents by content hash: {e}")
|
|
510
|
+
raise
|
|
511
|
+
|
|
512
|
+
async def _async_upsert(
|
|
513
|
+
self,
|
|
514
|
+
content_hash: str,
|
|
515
|
+
documents: List[Document],
|
|
516
|
+
filters: Optional[Dict[str, Any]] = None,
|
|
517
|
+
batch_size: int = 100,
|
|
518
|
+
) -> None:
|
|
519
|
+
"""
|
|
520
|
+
Upsert (insert or update) documents in the database.
|
|
521
|
+
|
|
522
|
+
Args:
|
|
523
|
+
documents (List[Document]): List of documents to upsert.
|
|
524
|
+
filters (Optional[Dict[str, Any]]): Filters to apply to the documents.
|
|
525
|
+
batch_size (int): Number of documents to upsert in each batch.
|
|
526
|
+
"""
|
|
527
|
+
try:
|
|
528
|
+
with self.Session() as sess:
|
|
529
|
+
for i in range(0, len(documents), batch_size):
|
|
530
|
+
batch_docs = documents[i : i + batch_size]
|
|
531
|
+
log_info(f"Processing batch starting at index {i}, size: {len(batch_docs)}")
|
|
532
|
+
try:
|
|
533
|
+
embed_tasks = [doc.async_embed(embedder=self.embedder) for doc in batch_docs]
|
|
534
|
+
await asyncio.gather(*embed_tasks, return_exceptions=True)
|
|
535
|
+
|
|
536
|
+
# Prepare documents for upserting
|
|
537
|
+
batch_records_dict = {} # Use dict to deduplicate by ID
|
|
538
|
+
for doc in batch_docs:
|
|
539
|
+
try:
|
|
540
|
+
cleaned_content = self._clean_content(doc.content)
|
|
541
|
+
record_id = md5(cleaned_content.encode()).hexdigest()
|
|
542
|
+
|
|
543
|
+
meta_data = doc.meta_data or {}
|
|
544
|
+
if filters:
|
|
545
|
+
meta_data.update(filters)
|
|
546
|
+
|
|
547
|
+
record = {
|
|
548
|
+
"id": record_id, # use record_id as a reproducible id to avoid duplicates while upsert
|
|
549
|
+
"name": doc.name,
|
|
550
|
+
"meta_data": doc.meta_data,
|
|
551
|
+
"filters": filters,
|
|
552
|
+
"content": cleaned_content,
|
|
553
|
+
"embedding": doc.embedding,
|
|
554
|
+
"usage": doc.usage,
|
|
555
|
+
"content_hash": content_hash,
|
|
556
|
+
"content_id": doc.content_id,
|
|
557
|
+
}
|
|
558
|
+
batch_records_dict[record_id] = record # This deduplicates by ID
|
|
559
|
+
except Exception as e:
|
|
560
|
+
logger.error(f"Error processing document '{doc.name}': {e}")
|
|
561
|
+
|
|
562
|
+
# Convert dict to list for upsert
|
|
563
|
+
batch_records = list(batch_records_dict.values())
|
|
564
|
+
if not batch_records:
|
|
565
|
+
log_info("No valid records to upsert in this batch.")
|
|
566
|
+
continue
|
|
567
|
+
|
|
568
|
+
# Upsert the batch of records
|
|
569
|
+
insert_stmt = postgresql.insert(self.table).values(batch_records)
|
|
570
|
+
upsert_stmt = insert_stmt.on_conflict_do_update(
|
|
571
|
+
index_elements=["id"],
|
|
572
|
+
set_={
|
|
573
|
+
"name": insert_stmt.excluded.name,
|
|
574
|
+
"meta_data": insert_stmt.excluded.meta_data,
|
|
575
|
+
"filters": insert_stmt.excluded.filters,
|
|
576
|
+
"content": insert_stmt.excluded.content,
|
|
577
|
+
"embedding": insert_stmt.excluded.embedding,
|
|
578
|
+
"usage": insert_stmt.excluded.usage,
|
|
579
|
+
"content_hash": insert_stmt.excluded.content_hash,
|
|
580
|
+
"content_id": insert_stmt.excluded.content_id,
|
|
581
|
+
},
|
|
582
|
+
)
|
|
583
|
+
sess.execute(upsert_stmt)
|
|
584
|
+
sess.commit() # Commit batch independently
|
|
585
|
+
log_info(f"Upserted batch of {len(batch_records)} documents.")
|
|
586
|
+
except Exception as e:
|
|
587
|
+
logger.error(f"Error with batch starting at index {i}: {e}")
|
|
588
|
+
sess.rollback() # Rollback the current batch if there's an error
|
|
589
|
+
raise
|
|
590
|
+
except Exception as e:
|
|
591
|
+
logger.error(f"Error upserting documents: {e}")
|
|
592
|
+
raise
|
|
593
|
+
|
|
594
|
+
def update_metadata(self, content_id: str, metadata: Dict[str, Any]) -> None:
|
|
595
|
+
"""
|
|
596
|
+
Update the metadata for a document.
|
|
597
|
+
|
|
598
|
+
Args:
|
|
599
|
+
id (str): The ID of the document.
|
|
600
|
+
metadata (Dict[str, Any]): The metadata to update.
|
|
601
|
+
"""
|
|
602
|
+
try:
|
|
603
|
+
with self.Session() as sess:
|
|
604
|
+
# Merge JSONB instead of overwriting: coalesce(existing, '{}') || :new
|
|
605
|
+
stmt = (
|
|
606
|
+
update(self.table)
|
|
607
|
+
.where(self.table.c.content_id == content_id)
|
|
608
|
+
.values(
|
|
609
|
+
meta_data=func.coalesce(self.table.c.meta_data, text("'{}'::jsonb")).op("||")(
|
|
610
|
+
bindparam("md", metadata, type_=postgresql.JSONB)
|
|
611
|
+
),
|
|
612
|
+
filters=func.coalesce(self.table.c.filters, text("'{}'::jsonb")).op("||")(
|
|
613
|
+
bindparam("ft", metadata, type_=postgresql.JSONB)
|
|
614
|
+
),
|
|
615
|
+
)
|
|
616
|
+
)
|
|
617
|
+
sess.execute(stmt)
|
|
618
|
+
sess.commit()
|
|
619
|
+
except Exception as e:
|
|
620
|
+
logger.error(f"Error updating metadata for document {content_id}: {e}")
|
|
621
|
+
raise
|
|
420
622
|
|
|
421
623
|
def search(self, query: str, limit: int = 5, filters: Optional[Dict[str, Any]] = None) -> List[Document]:
|
|
422
624
|
"""
|
|
@@ -1025,6 +1227,86 @@ class PgVector(VectorDb):
|
|
|
1025
1227
|
sess.rollback()
|
|
1026
1228
|
return False
|
|
1027
1229
|
|
|
1230
|
+
def delete_by_id(self, id: str) -> bool:
|
|
1231
|
+
"""
|
|
1232
|
+
Delete content by ID.
|
|
1233
|
+
"""
|
|
1234
|
+
try:
|
|
1235
|
+
with self.Session() as sess, sess.begin():
|
|
1236
|
+
stmt = self.table.delete().where(self.table.c.id == id)
|
|
1237
|
+
sess.execute(stmt)
|
|
1238
|
+
sess.commit()
|
|
1239
|
+
log_info(f"Deleted records with id '{id}' from table '{self.table.fullname}'.")
|
|
1240
|
+
return True
|
|
1241
|
+
except Exception as e:
|
|
1242
|
+
logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
|
|
1243
|
+
sess.rollback()
|
|
1244
|
+
return False
|
|
1245
|
+
|
|
1246
|
+
def delete_by_name(self, name: str) -> bool:
|
|
1247
|
+
"""
|
|
1248
|
+
Delete content by name.
|
|
1249
|
+
"""
|
|
1250
|
+
try:
|
|
1251
|
+
with self.Session() as sess, sess.begin():
|
|
1252
|
+
stmt = self.table.delete().where(self.table.c.name == name)
|
|
1253
|
+
sess.execute(stmt)
|
|
1254
|
+
sess.commit()
|
|
1255
|
+
log_info(f"Deleted records with name '{name}' from table '{self.table.fullname}'.")
|
|
1256
|
+
return True
|
|
1257
|
+
except Exception as e:
|
|
1258
|
+
logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
|
|
1259
|
+
sess.rollback()
|
|
1260
|
+
return False
|
|
1261
|
+
|
|
1262
|
+
def delete_by_metadata(self, metadata: Dict[str, Any]) -> bool:
|
|
1263
|
+
"""
|
|
1264
|
+
Delete content by metadata.
|
|
1265
|
+
"""
|
|
1266
|
+
try:
|
|
1267
|
+
with self.Session() as sess, sess.begin():
|
|
1268
|
+
stmt = self.table.delete().where(self.table.c.meta_data.contains(metadata))
|
|
1269
|
+
sess.execute(stmt)
|
|
1270
|
+
sess.commit()
|
|
1271
|
+
log_info(f"Deleted records with metadata '{metadata}' from table '{self.table.fullname}'.")
|
|
1272
|
+
return True
|
|
1273
|
+
except Exception as e:
|
|
1274
|
+
logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
|
|
1275
|
+
sess.rollback()
|
|
1276
|
+
return False
|
|
1277
|
+
|
|
1278
|
+
def delete_by_content_id(self, content_id: str) -> bool:
|
|
1279
|
+
"""
|
|
1280
|
+
Delete content by content ID.
|
|
1281
|
+
"""
|
|
1282
|
+
try:
|
|
1283
|
+
with self.Session() as sess, sess.begin():
|
|
1284
|
+
stmt = self.table.delete().where(self.table.c.content_id == content_id)
|
|
1285
|
+
sess.execute(stmt)
|
|
1286
|
+
sess.commit()
|
|
1287
|
+
log_info(f"Deleted records with content ID '{content_id}' from table '{self.table.fullname}'.")
|
|
1288
|
+
return True
|
|
1289
|
+
except Exception as e:
|
|
1290
|
+
logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
|
|
1291
|
+
sess.rollback()
|
|
1292
|
+
return False
|
|
1293
|
+
|
|
1294
|
+
def _delete_by_content_hash(self, content_hash: str) -> bool:
|
|
1295
|
+
"""
|
|
1296
|
+
Delete content by content hash.
|
|
1297
|
+
"""
|
|
1298
|
+
try:
|
|
1299
|
+
with self.Session() as sess, sess.begin():
|
|
1300
|
+
stmt = self.table.delete().where(self.table.c.content_hash == content_hash)
|
|
1301
|
+
sess.execute(stmt)
|
|
1302
|
+
sess.commit()
|
|
1303
|
+
log_info(f"Deleted records with content hash '{content_hash}' from table '{self.table.fullname}'.")
|
|
1304
|
+
return True
|
|
1305
|
+
except Exception as e:
|
|
1306
|
+
logger.error(f"Error deleting rows from table '{self.table.fullname}': {e}")
|
|
1307
|
+
sess.rollback()
|
|
1308
|
+
return False
|
|
1309
|
+
|
|
1028
1310
|
def __deepcopy__(self, memo):
|
|
1029
1311
|
"""
|
|
1030
1312
|
Create a deep copy of the PgVector instance, handling unpickleable attributes.
|