agno 1.8.0__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 +2781 -4126
- 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/media.py +2 -2
- 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/dashscope/dashscope.py +14 -5
- agno/models/google/gemini.py +123 -53
- 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 +135 -27
- agno/models/openai/responses.py +233 -115
- 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 +2967 -4243
- 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 +71 -18
- 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 +62 -62
- 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 +46 -62
- 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 +134 -0
- 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/location.py +2 -2
- 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 +356 -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 -698
- 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.0.dist-info/METADATA +0 -979
- agno-1.8.0.dist-info/RECORD +0 -565
- agno-1.8.0.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.0.dist-info → agno-2.0.0a1.dist-info}/WHEEL +0 -0
- {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/licenses/LICENSE +0 -0
- {agno-1.8.0.dist-info → agno-2.0.0a1.dist-info}/top_level.txt +0 -0
agno/knowledge/light_rag.py
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import Any, ClassVar, Dict, Iterator, List, Optional, Union
|
|
5
|
-
|
|
6
|
-
import textract
|
|
7
|
-
from pydantic import Field
|
|
8
|
-
|
|
9
|
-
from agno.document import Document
|
|
10
|
-
from agno.document.reader.markdown_reader import MarkdownReader
|
|
11
|
-
from agno.document.reader.pdf_reader import PDFUrlReader
|
|
12
|
-
from agno.document.reader.url_reader import URLReader
|
|
13
|
-
from agno.knowledge.agent import AgentKnowledge
|
|
14
|
-
from agno.utils.log import log_debug, log_info, logger
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class LightRagKnowledgeBase(AgentKnowledge):
|
|
18
|
-
"""LightRAG-based knowledge base for document processing and retrieval."""
|
|
19
|
-
|
|
20
|
-
# Constants
|
|
21
|
-
DEFAULT_SERVER_URL: ClassVar[str] = "http://localhost:9621"
|
|
22
|
-
SUPPORTED_EXTENSIONS: ClassVar[List[str]] = [".pdf", ".md", ".txt"]
|
|
23
|
-
|
|
24
|
-
lightrag_server_url: str = DEFAULT_SERVER_URL
|
|
25
|
-
path: Optional[Union[str, Path, List[Dict[str, Union[str, Dict[str, Any]]]]]] = None
|
|
26
|
-
urls: Optional[Union[List[str], List[Dict[str, Union[str, Dict[str, Any]]]]]] = None
|
|
27
|
-
exclude_files: List[str] = Field(default_factory=list)
|
|
28
|
-
|
|
29
|
-
pdf_url_reader: PDFUrlReader = PDFUrlReader()
|
|
30
|
-
markdown_reader: MarkdownReader = MarkdownReader()
|
|
31
|
-
url_reader: URLReader = URLReader()
|
|
32
|
-
|
|
33
|
-
@property
|
|
34
|
-
def document_lists(self) -> Iterator[List[Document]]:
|
|
35
|
-
"""Iterate over documents and yield lists of Document objects."""
|
|
36
|
-
# Convert text lists to Document objects to match parent class signature
|
|
37
|
-
for text_list in self._text_document_lists():
|
|
38
|
-
documents = [Document(content=text) for text in text_list]
|
|
39
|
-
yield documents
|
|
40
|
-
|
|
41
|
-
def _text_document_lists(self) -> Iterator[List[str]]:
|
|
42
|
-
"""Internal method to iterate over documents and yield lists of text content."""
|
|
43
|
-
if self.path is not None:
|
|
44
|
-
yield from self._process_paths()
|
|
45
|
-
|
|
46
|
-
if self.urls is not None:
|
|
47
|
-
yield from self._process_urls()
|
|
48
|
-
|
|
49
|
-
if self.urls is None and self.path is None:
|
|
50
|
-
raise ValueError("Path or URLs are not set")
|
|
51
|
-
|
|
52
|
-
def _process_paths(self) -> Iterator[List[str]]:
|
|
53
|
-
"""Process path-based documents."""
|
|
54
|
-
if self.path is None:
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
if isinstance(self.path, list):
|
|
58
|
-
for item in self.path:
|
|
59
|
-
if isinstance(item, dict) and "path" in item:
|
|
60
|
-
path_value = item["path"]
|
|
61
|
-
if isinstance(path_value, (str, Path)):
|
|
62
|
-
yield from self._process_single_path(Path(path_value))
|
|
63
|
-
else:
|
|
64
|
-
yield from self._process_single_path(Path(self.path))
|
|
65
|
-
|
|
66
|
-
def _process_single_path(self, path: Path) -> Iterator[List[str]]:
|
|
67
|
-
"""Process a single path (file or directory)."""
|
|
68
|
-
if path.is_dir():
|
|
69
|
-
for file_path in path.glob("**/*"):
|
|
70
|
-
if file_path.is_file():
|
|
71
|
-
text_str = textract.process(str(file_path)).decode("utf-8")
|
|
72
|
-
yield [text_str]
|
|
73
|
-
elif path.exists() and path.is_file():
|
|
74
|
-
if path.suffix == ".md":
|
|
75
|
-
documents = self.markdown_reader.read(file=path)
|
|
76
|
-
text_contents = [doc.content for doc in documents]
|
|
77
|
-
yield text_contents
|
|
78
|
-
elif path.suffix == ".pdf":
|
|
79
|
-
text_str = textract.process(str(path)).decode("utf-8")
|
|
80
|
-
yield [text_str]
|
|
81
|
-
else:
|
|
82
|
-
text_str = textract.process(str(path)).decode("utf-8")
|
|
83
|
-
yield [text_str]
|
|
84
|
-
|
|
85
|
-
def _process_urls(self) -> Iterator[List[str]]:
|
|
86
|
-
"""Process URL-based documents."""
|
|
87
|
-
if self.urls is None:
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
log_info(f"Processing URLs: {self.urls}")
|
|
91
|
-
for item in self.urls:
|
|
92
|
-
if isinstance(item, dict) and "url" in item:
|
|
93
|
-
url = item["url"]
|
|
94
|
-
config = item.get("metadata", {})
|
|
95
|
-
if isinstance(url, str) and isinstance(config, dict):
|
|
96
|
-
yield from self._process_url_with_metadata(url, config)
|
|
97
|
-
elif isinstance(item, str):
|
|
98
|
-
yield from self._process_simple_url(item)
|
|
99
|
-
|
|
100
|
-
def _process_url_with_metadata(self, url: str, config: Dict[str, Any]) -> Iterator[List[str]]:
|
|
101
|
-
"""Process URL with metadata configuration."""
|
|
102
|
-
log_debug(f"Processing URL with metadata - URL: {url}, Config: {config}")
|
|
103
|
-
if self._is_valid_url(url):
|
|
104
|
-
if url.endswith(".pdf"):
|
|
105
|
-
log_info(f"READING PDF URL: {url}")
|
|
106
|
-
documents = self.pdf_url_reader.read(url=url)
|
|
107
|
-
text_contents = [doc.content for doc in documents]
|
|
108
|
-
yield text_contents
|
|
109
|
-
else:
|
|
110
|
-
log_debug(f"URL is valid, reading documents from: {url}")
|
|
111
|
-
documents = self.url_reader.read(url=url)
|
|
112
|
-
text_contents = []
|
|
113
|
-
for doc in documents:
|
|
114
|
-
if config:
|
|
115
|
-
log_debug(f"Adding metadata {config} to document from URL: {url}")
|
|
116
|
-
doc.meta_data.update(config)
|
|
117
|
-
text_contents.append(doc.content)
|
|
118
|
-
yield text_contents
|
|
119
|
-
|
|
120
|
-
def _process_simple_url(self, url: str) -> Iterator[List[str]]:
|
|
121
|
-
"""Process a simple URL without metadata."""
|
|
122
|
-
log_info(f"Processing simple URL: {url}")
|
|
123
|
-
if self._is_valid_url(url):
|
|
124
|
-
log_info(f"Simple URL is valid, reading documents from: {url}")
|
|
125
|
-
if url.endswith(".pdf"):
|
|
126
|
-
documents = self.pdf_url_reader.read(url=url)
|
|
127
|
-
text_contents = [doc.content for doc in documents]
|
|
128
|
-
yield text_contents
|
|
129
|
-
else:
|
|
130
|
-
documents = self.url_reader.read(url=url)
|
|
131
|
-
text_contents = [doc.content for doc in documents]
|
|
132
|
-
yield text_contents
|
|
133
|
-
|
|
134
|
-
async def load(
|
|
135
|
-
self,
|
|
136
|
-
recreate: bool = False,
|
|
137
|
-
upsert: bool = False,
|
|
138
|
-
skip_existing: bool = True,
|
|
139
|
-
) -> None:
|
|
140
|
-
"""Load the knowledge base to the LightRAG server asynchronously.
|
|
141
|
-
|
|
142
|
-
Note: The LightRAG implementation is inherently async.
|
|
143
|
-
"""
|
|
144
|
-
logger.debug("Loading LightRagKnowledgeBase")
|
|
145
|
-
for text_list in self._text_document_lists():
|
|
146
|
-
for text in text_list:
|
|
147
|
-
await self._insert_text(text)
|
|
148
|
-
|
|
149
|
-
async def aload(
|
|
150
|
-
self,
|
|
151
|
-
recreate: bool = False,
|
|
152
|
-
upsert: bool = False,
|
|
153
|
-
skip_existing: bool = True,
|
|
154
|
-
) -> None:
|
|
155
|
-
"""Load all documents into the LightRAG server asynchronously."""
|
|
156
|
-
# Delegate to load() since both are async for LightRAG
|
|
157
|
-
await self.load(recreate=recreate, upsert=upsert, skip_existing=skip_existing)
|
|
158
|
-
|
|
159
|
-
async def load_text(
|
|
160
|
-
self, text: str, upsert: bool = False, skip_existing: bool = True, filters: Optional[Dict[str, Any]] = None
|
|
161
|
-
) -> None:
|
|
162
|
-
"""Load a single text into the LightRAG server asynchronously."""
|
|
163
|
-
await self._insert_text(text)
|
|
164
|
-
|
|
165
|
-
async def async_search(
|
|
166
|
-
self, query: str, num_documents: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
|
|
167
|
-
) -> List[Document]:
|
|
168
|
-
"""Override the async_search method from AgentKnowledge to query the LightRAG server."""
|
|
169
|
-
import httpx
|
|
170
|
-
|
|
171
|
-
logger.info(f"Querying LightRAG server with query: {query}")
|
|
172
|
-
mode = "hybrid" # Default mode, can be "local", "global", or "hybrid"
|
|
173
|
-
|
|
174
|
-
async with httpx.AsyncClient() as client:
|
|
175
|
-
response = await client.post(
|
|
176
|
-
f"{self.lightrag_server_url}/query",
|
|
177
|
-
json={"query": query, "mode": mode},
|
|
178
|
-
headers={"Content-Type": "application/json"},
|
|
179
|
-
)
|
|
180
|
-
response.raise_for_status()
|
|
181
|
-
result = response.json()
|
|
182
|
-
logger.info(f"Query result: {result}")
|
|
183
|
-
|
|
184
|
-
# Convert result to Document objects to match parent class signature
|
|
185
|
-
if isinstance(result, dict) and "response" in result:
|
|
186
|
-
return [Document(content=result["response"], meta_data={"query": query, "mode": mode})]
|
|
187
|
-
elif isinstance(result, list):
|
|
188
|
-
return [Document(content=str(item), meta_data={"query": query, "mode": mode}) for item in result]
|
|
189
|
-
else:
|
|
190
|
-
return [Document(content=str(result), meta_data={"query": query, "mode": mode})]
|
|
191
|
-
|
|
192
|
-
async def _insert_text(self, text: str) -> Dict[str, Any]:
|
|
193
|
-
"""Insert text into the LightRAG server."""
|
|
194
|
-
import httpx
|
|
195
|
-
|
|
196
|
-
async with httpx.AsyncClient() as client:
|
|
197
|
-
response = await client.post(
|
|
198
|
-
f"{self.lightrag_server_url}/documents/text",
|
|
199
|
-
json={"text": text},
|
|
200
|
-
headers={"Content-Type": "application/json"},
|
|
201
|
-
)
|
|
202
|
-
response.raise_for_status()
|
|
203
|
-
result = response.json()
|
|
204
|
-
logger.debug(f"Text insertion result: {result}")
|
|
205
|
-
return result
|
|
206
|
-
|
|
207
|
-
def _is_valid_url(self, url: str) -> bool:
|
|
208
|
-
"""Helper to check if URL is valid."""
|
|
209
|
-
if not any(url.endswith(ext) for ext in self.SUPPORTED_EXTENSIONS):
|
|
210
|
-
logger.error(f"Unsupported URL: {url}. Supported file types: {', '.join(self.SUPPORTED_EXTENSIONS)}")
|
|
211
|
-
return False
|
|
212
|
-
return True
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
async def lightrag_retriever(
|
|
216
|
-
query: str,
|
|
217
|
-
num_documents: int = 5,
|
|
218
|
-
mode: str = "hybrid", # Default mode, can be "local", "global", or "hybrid"
|
|
219
|
-
lightrag_server_url: str = "http://localhost:9621",
|
|
220
|
-
) -> Optional[List[Dict[str, Any]]]:
|
|
221
|
-
"""
|
|
222
|
-
Custom retriever function to search the LightRAG server for relevant documents.
|
|
223
|
-
|
|
224
|
-
Args:
|
|
225
|
-
query: The search query string
|
|
226
|
-
num_documents: Number of documents to retrieve (currently unused by LightRAG)
|
|
227
|
-
mode: Query mode - "local", "global", or "hybrid"
|
|
228
|
-
lightrag_server_url: URL of the LightRAG server
|
|
229
|
-
|
|
230
|
-
Returns:
|
|
231
|
-
List of retrieved documents or None if search fails
|
|
232
|
-
"""
|
|
233
|
-
try:
|
|
234
|
-
import httpx
|
|
235
|
-
|
|
236
|
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
237
|
-
response = await client.post(
|
|
238
|
-
f"{lightrag_server_url}/query",
|
|
239
|
-
json={"query": query, "mode": mode},
|
|
240
|
-
headers={"Content-Type": "application/json"},
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
response.raise_for_status()
|
|
244
|
-
result = response.json()
|
|
245
|
-
|
|
246
|
-
return _format_lightrag_response(result, query, mode)
|
|
247
|
-
|
|
248
|
-
except httpx.RequestError as e:
|
|
249
|
-
logger.error(f"HTTP Request Error: {type(e).__name__}: {str(e)}")
|
|
250
|
-
return None
|
|
251
|
-
except httpx.HTTPStatusError as e:
|
|
252
|
-
logger.error(f"HTTP Status Error: {e.response.status_code} - {e.response.text}")
|
|
253
|
-
return None
|
|
254
|
-
except Exception as e:
|
|
255
|
-
logger.error(f"Unexpected error during LightRAG server search: {type(e).__name__}: {str(e)}")
|
|
256
|
-
import traceback
|
|
257
|
-
|
|
258
|
-
logger.error(f"Full traceback: {traceback.format_exc()}")
|
|
259
|
-
return None
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
def _format_lightrag_response(result: Any, query: str, mode: str) -> List[Dict[str, Any]]:
|
|
263
|
-
"""Format LightRAG server response to expected document format."""
|
|
264
|
-
# LightRAG server returns a dict with 'response' key, but we expect a list of documents
|
|
265
|
-
# Convert the response to the expected format
|
|
266
|
-
if isinstance(result, dict) and "response" in result:
|
|
267
|
-
# Wrap the response in a document-like structure
|
|
268
|
-
return [{"content": result["response"], "source": "lightrag", "metadata": {"query": query, "mode": mode}}]
|
|
269
|
-
elif isinstance(result, list):
|
|
270
|
-
return result
|
|
271
|
-
else:
|
|
272
|
-
# If it's a string or other format, wrap it
|
|
273
|
-
return [{"content": str(result), "source": "lightrag", "metadata": {"query": query, "mode": mode}}]
|
agno/knowledge/llamaindex.py
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
from typing import Any, Callable, Dict, List, Optional
|
|
2
|
-
|
|
3
|
-
from agno.document import Document
|
|
4
|
-
from agno.knowledge.agent import AgentKnowledge
|
|
5
|
-
from agno.utils.log import logger
|
|
6
|
-
|
|
7
|
-
try:
|
|
8
|
-
from llama_index.core.retrievers import BaseRetriever
|
|
9
|
-
from llama_index.core.schema import NodeWithScore
|
|
10
|
-
except ImportError:
|
|
11
|
-
raise ImportError(
|
|
12
|
-
"The `llama-index-core` package is not installed. Please install it via `pip install llama-index-core`."
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class LlamaIndexKnowledgeBase(AgentKnowledge):
|
|
17
|
-
retriever: BaseRetriever
|
|
18
|
-
loader: Optional[Callable] = None
|
|
19
|
-
|
|
20
|
-
def search(
|
|
21
|
-
self, query: str, num_documents: Optional[int] = None, filters: Optional[Dict[str, Any]] = None
|
|
22
|
-
) -> List[Document]:
|
|
23
|
-
"""
|
|
24
|
-
Returns relevant documents matching the query.
|
|
25
|
-
|
|
26
|
-
Args:
|
|
27
|
-
query (str): The query string to search for.
|
|
28
|
-
num_documents (Optional[int]): The maximum number of documents to return. Defaults to None.
|
|
29
|
-
filters (Optional[Dict[str, Any]]): Filters to apply to the search. Defaults to None.
|
|
30
|
-
|
|
31
|
-
Returns:
|
|
32
|
-
List[Document]: A list of relevant documents matching the query.
|
|
33
|
-
Raises:
|
|
34
|
-
ValueError: If the retriever is not of type BaseRetriever.
|
|
35
|
-
"""
|
|
36
|
-
if not isinstance(self.retriever, BaseRetriever):
|
|
37
|
-
raise ValueError(f"Retriever is not of type BaseRetriever: {self.retriever}")
|
|
38
|
-
|
|
39
|
-
lc_documents: List[NodeWithScore] = self.retriever.retrieve(query)
|
|
40
|
-
if num_documents is not None:
|
|
41
|
-
lc_documents = lc_documents[:num_documents]
|
|
42
|
-
documents = []
|
|
43
|
-
for lc_doc in lc_documents:
|
|
44
|
-
documents.append(
|
|
45
|
-
Document(
|
|
46
|
-
content=lc_doc.text,
|
|
47
|
-
meta_data=lc_doc.metadata,
|
|
48
|
-
)
|
|
49
|
-
)
|
|
50
|
-
return documents
|
|
51
|
-
|
|
52
|
-
def load(
|
|
53
|
-
self,
|
|
54
|
-
recreate: bool = False,
|
|
55
|
-
upsert: bool = True,
|
|
56
|
-
skip_existing: bool = True,
|
|
57
|
-
filters: Optional[Dict[str, Any]] = None,
|
|
58
|
-
) -> None:
|
|
59
|
-
if self.loader is None:
|
|
60
|
-
logger.error("No loader provided for LlamaIndexKnowledgeBase")
|
|
61
|
-
return
|
|
62
|
-
self.loader()
|
|
63
|
-
|
|
64
|
-
def exists(self) -> bool:
|
|
65
|
-
logger.warning("LlamaIndexKnowledgeBase.exists() not supported - please check the vectorstore manually.")
|
|
66
|
-
return True
|
agno/knowledge/markdown.py
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Union, cast
|
|
3
|
-
|
|
4
|
-
from pydantic import model_validator
|
|
5
|
-
|
|
6
|
-
from agno.document import Document
|
|
7
|
-
from agno.document.chunking.markdown import MarkdownChunking
|
|
8
|
-
from agno.document.reader.markdown_reader import MarkdownReader
|
|
9
|
-
from agno.knowledge.agent import AgentKnowledge
|
|
10
|
-
from agno.utils.log import log_info, logger
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class MarkdownKnowledgeBase(AgentKnowledge):
|
|
14
|
-
path: Optional[Union[str, Path, List[Dict[str, Union[str, Dict[str, Any]]]]]] = None
|
|
15
|
-
formats: List[str] = [".md"]
|
|
16
|
-
reader: Optional[MarkdownReader] = None
|
|
17
|
-
|
|
18
|
-
@model_validator(mode="after")
|
|
19
|
-
def set_reader(self) -> "MarkdownKnowledgeBase":
|
|
20
|
-
if self.reader is None:
|
|
21
|
-
self.reader = MarkdownReader(chunking_strategy=self.chunking_strategy or MarkdownChunking())
|
|
22
|
-
return self
|
|
23
|
-
|
|
24
|
-
@property
|
|
25
|
-
def document_lists(self) -> Iterator[List[Document]]:
|
|
26
|
-
"""Iterate over text files and yield lists of documents."""
|
|
27
|
-
self.reader = cast(MarkdownReader, self.reader)
|
|
28
|
-
if self.path is None:
|
|
29
|
-
raise ValueError("Path is not set")
|
|
30
|
-
|
|
31
|
-
if isinstance(self.path, list):
|
|
32
|
-
for item in self.path:
|
|
33
|
-
if isinstance(item, dict) and "path" in item:
|
|
34
|
-
# Handle path with metadata
|
|
35
|
-
file_path = item["path"]
|
|
36
|
-
config = item.get("metadata", {})
|
|
37
|
-
_file_path = Path(file_path) # type: ignore
|
|
38
|
-
if self._is_valid_text(_file_path):
|
|
39
|
-
documents = self.reader.read(file=_file_path)
|
|
40
|
-
if config:
|
|
41
|
-
for doc in documents:
|
|
42
|
-
log_info(f"Adding metadata {config} to document: {doc.name}")
|
|
43
|
-
doc.meta_data.update(config) # type: ignore
|
|
44
|
-
yield documents
|
|
45
|
-
else:
|
|
46
|
-
# Handle single path
|
|
47
|
-
_file_path = Path(self.path)
|
|
48
|
-
if _file_path.is_dir():
|
|
49
|
-
for _file in _file_path.glob("**/*"):
|
|
50
|
-
if self._is_valid_text(_file):
|
|
51
|
-
yield self.reader.read(file=_file)
|
|
52
|
-
elif self._is_valid_text(_file_path):
|
|
53
|
-
yield self.reader.read(file=_file_path)
|
|
54
|
-
|
|
55
|
-
def _is_valid_text(self, path: Path) -> bool:
|
|
56
|
-
"""Helper to check if path is a valid text file."""
|
|
57
|
-
return path.exists() and path.is_file() and path.suffix in self.formats
|
|
58
|
-
|
|
59
|
-
@property
|
|
60
|
-
async def async_document_lists(self) -> AsyncIterator[List[Document]]:
|
|
61
|
-
"""Iterate over text files and yield lists of documents asynchronously."""
|
|
62
|
-
self.reader = cast(MarkdownReader, self.reader)
|
|
63
|
-
if self.path is None:
|
|
64
|
-
raise ValueError("Path is not set")
|
|
65
|
-
|
|
66
|
-
if isinstance(self.path, list):
|
|
67
|
-
for item in self.path:
|
|
68
|
-
if isinstance(item, dict) and "path" in item:
|
|
69
|
-
# Handle path with metadata
|
|
70
|
-
file_path = item["path"]
|
|
71
|
-
config = item.get("metadata", {})
|
|
72
|
-
_file_path = Path(file_path) # type: ignore
|
|
73
|
-
if self._is_valid_text(_file_path):
|
|
74
|
-
documents = await self.reader.async_read(file=_file_path)
|
|
75
|
-
if config:
|
|
76
|
-
for doc in documents:
|
|
77
|
-
log_info(f"Adding metadata {config} to document: {doc.name}")
|
|
78
|
-
doc.meta_data.update(config) # type: ignore
|
|
79
|
-
yield documents
|
|
80
|
-
else:
|
|
81
|
-
# Handle single path
|
|
82
|
-
_file_path = Path(self.path)
|
|
83
|
-
if _file_path.is_dir():
|
|
84
|
-
for _file in _file_path.glob("**/*"):
|
|
85
|
-
if self._is_valid_text(_file):
|
|
86
|
-
yield await self.reader.async_read(file=_file)
|
|
87
|
-
elif self._is_valid_text(_file_path):
|
|
88
|
-
yield await self.reader.async_read(file=_file_path)
|
|
89
|
-
|
|
90
|
-
def load_document(
|
|
91
|
-
self,
|
|
92
|
-
path: Union[str, Path],
|
|
93
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
94
|
-
recreate: bool = False,
|
|
95
|
-
upsert: bool = False,
|
|
96
|
-
skip_existing: bool = True,
|
|
97
|
-
) -> None:
|
|
98
|
-
"""Load documents from a single text file with specific metadata into the vector DB."""
|
|
99
|
-
self.reader = cast(MarkdownReader, self.reader)
|
|
100
|
-
|
|
101
|
-
_file_path = Path(path) if isinstance(path, str) else path
|
|
102
|
-
|
|
103
|
-
# Validate file and prepare collection in one step
|
|
104
|
-
if not self.prepare_load(_file_path, self.formats, metadata, recreate):
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
# Read documents
|
|
108
|
-
try:
|
|
109
|
-
documents = self.reader.read(file=_file_path)
|
|
110
|
-
except Exception as e:
|
|
111
|
-
logger.exception(f"Failed to read documents from file {_file_path}: {e}")
|
|
112
|
-
return
|
|
113
|
-
|
|
114
|
-
# Process documents
|
|
115
|
-
self.process_documents(
|
|
116
|
-
documents=documents,
|
|
117
|
-
metadata=metadata,
|
|
118
|
-
upsert=upsert,
|
|
119
|
-
skip_existing=skip_existing,
|
|
120
|
-
source_info=str(_file_path),
|
|
121
|
-
)
|
|
122
|
-
|
|
123
|
-
async def aload_document(
|
|
124
|
-
self,
|
|
125
|
-
path: Union[str, Path],
|
|
126
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
127
|
-
recreate: bool = False,
|
|
128
|
-
upsert: bool = False,
|
|
129
|
-
skip_existing: bool = True,
|
|
130
|
-
) -> None:
|
|
131
|
-
"""Load documents from a single text file with specific metadata into the vector DB."""
|
|
132
|
-
self.reader = cast(MarkdownReader, self.reader)
|
|
133
|
-
|
|
134
|
-
_file_path = Path(path) if isinstance(path, str) else path
|
|
135
|
-
|
|
136
|
-
# Validate file and prepare collection in one step
|
|
137
|
-
if not await self.aprepare_load(_file_path, self.formats, metadata, recreate):
|
|
138
|
-
return
|
|
139
|
-
|
|
140
|
-
# Read documents
|
|
141
|
-
try:
|
|
142
|
-
documents = await self.reader.async_read(file=_file_path)
|
|
143
|
-
except Exception as e:
|
|
144
|
-
logger.exception(f"Failed to read documents from file {_file_path}: {e}")
|
|
145
|
-
return
|
|
146
|
-
|
|
147
|
-
# Process documents
|
|
148
|
-
await self.aprocess_documents(
|
|
149
|
-
documents=documents,
|
|
150
|
-
metadata=metadata,
|
|
151
|
-
upsert=upsert,
|
|
152
|
-
skip_existing=skip_existing,
|
|
153
|
-
source_info=str(_file_path),
|
|
154
|
-
)
|
agno/knowledge/pdf.py
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
from pathlib import Path
|
|
2
|
-
from typing import Any, AsyncIterator, Dict, Iterator, List, Optional, Union
|
|
3
|
-
|
|
4
|
-
from pydantic import Field
|
|
5
|
-
from typing_extensions import TypedDict
|
|
6
|
-
|
|
7
|
-
from agno.document import Document
|
|
8
|
-
from agno.document.reader.pdf_reader import PDFImageReader, PDFReader
|
|
9
|
-
from agno.knowledge.agent import AgentKnowledge
|
|
10
|
-
from agno.utils.log import log_error, log_info, logger
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class PDFConfig(TypedDict, total=False):
|
|
14
|
-
path: str
|
|
15
|
-
password: Optional[str]
|
|
16
|
-
metadata: Optional[Dict[str, Any]]
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class PDFKnowledgeBase(AgentKnowledge):
|
|
20
|
-
path: Optional[Union[str, Path, List[PDFConfig]]] = None
|
|
21
|
-
formats: List[str] = [".pdf"]
|
|
22
|
-
exclude_files: List[str] = Field(default_factory=list)
|
|
23
|
-
reader: Union[PDFReader, PDFImageReader] = PDFReader()
|
|
24
|
-
|
|
25
|
-
@property
|
|
26
|
-
def document_lists(self) -> Iterator[List[Document]]:
|
|
27
|
-
"""Iterate over PDFs and yield lists of documents."""
|
|
28
|
-
if self.path is None:
|
|
29
|
-
raise ValueError("Path is not set")
|
|
30
|
-
|
|
31
|
-
if isinstance(self.path, list):
|
|
32
|
-
for item in self.path:
|
|
33
|
-
if isinstance(item, dict) and "path" in item:
|
|
34
|
-
file_path = item["path"]
|
|
35
|
-
config = item.get("metadata", {})
|
|
36
|
-
file_password = item.get("password")
|
|
37
|
-
if file_password is not None and not isinstance(file_password, str):
|
|
38
|
-
file_password = None
|
|
39
|
-
|
|
40
|
-
_pdf_path = Path(file_path) # type: ignore
|
|
41
|
-
if self._is_valid_pdf(_pdf_path):
|
|
42
|
-
documents = self.reader.read(pdf=_pdf_path, password=file_password)
|
|
43
|
-
if config:
|
|
44
|
-
for doc in documents:
|
|
45
|
-
log_info(f"Adding metadata {config} to document: {doc.name}")
|
|
46
|
-
doc.meta_data.update(config) # type: ignore
|
|
47
|
-
yield documents
|
|
48
|
-
else:
|
|
49
|
-
_pdf_path = Path(self.path)
|
|
50
|
-
if _pdf_path.is_dir():
|
|
51
|
-
for _pdf in _pdf_path.glob("**/*.pdf"):
|
|
52
|
-
if _pdf.name not in self.exclude_files:
|
|
53
|
-
yield self.reader.read(pdf=_pdf)
|
|
54
|
-
elif self._is_valid_pdf(_pdf_path):
|
|
55
|
-
yield self.reader.read(pdf=_pdf_path)
|
|
56
|
-
|
|
57
|
-
def _is_valid_pdf(self, path: Path) -> bool:
|
|
58
|
-
"""Helper to check if path is a valid PDF file."""
|
|
59
|
-
if not path.exists():
|
|
60
|
-
log_error(f"PDF file not found: {path}")
|
|
61
|
-
return False
|
|
62
|
-
if not path.is_file():
|
|
63
|
-
log_error(f"Path is not a file: {path}")
|
|
64
|
-
return False
|
|
65
|
-
if path.suffix != ".pdf":
|
|
66
|
-
log_error(f"File is not a PDF: {path}")
|
|
67
|
-
return False
|
|
68
|
-
if path.name in self.exclude_files:
|
|
69
|
-
log_error(f"PDF file excluded: {path}")
|
|
70
|
-
return False
|
|
71
|
-
return True
|
|
72
|
-
|
|
73
|
-
@property
|
|
74
|
-
async def async_document_lists(self) -> AsyncIterator[List[Document]]:
|
|
75
|
-
"""Iterate over PDFs and yield lists of documents asynchronously."""
|
|
76
|
-
if self.path is None:
|
|
77
|
-
raise ValueError("Path is not set")
|
|
78
|
-
|
|
79
|
-
if isinstance(self.path, list):
|
|
80
|
-
for item in self.path:
|
|
81
|
-
if isinstance(item, dict) and "path" in item:
|
|
82
|
-
file_path = item["path"]
|
|
83
|
-
config = item.get("metadata", {})
|
|
84
|
-
file_password = item.get("password")
|
|
85
|
-
if file_password is not None and not isinstance(file_password, str):
|
|
86
|
-
file_password = None
|
|
87
|
-
|
|
88
|
-
_pdf_path = Path(file_path) # type: ignore
|
|
89
|
-
if self._is_valid_pdf(_pdf_path):
|
|
90
|
-
documents = await self.reader.async_read(pdf=_pdf_path, password=file_password)
|
|
91
|
-
if config:
|
|
92
|
-
for doc in documents:
|
|
93
|
-
log_info(f"Adding metadata {config} to document: {doc.name}")
|
|
94
|
-
doc.meta_data.update(config) # type: ignore
|
|
95
|
-
yield documents
|
|
96
|
-
else:
|
|
97
|
-
# Handle single path
|
|
98
|
-
_pdf_path = Path(self.path)
|
|
99
|
-
if _pdf_path.is_dir():
|
|
100
|
-
for _pdf in _pdf_path.glob("**/*.pdf"):
|
|
101
|
-
if _pdf.name not in self.exclude_files:
|
|
102
|
-
yield await self.reader.async_read(pdf=_pdf)
|
|
103
|
-
elif self._is_valid_pdf(_pdf_path):
|
|
104
|
-
yield await self.reader.async_read(pdf=_pdf_path)
|
|
105
|
-
|
|
106
|
-
def load_document(
|
|
107
|
-
self,
|
|
108
|
-
path: Union[str, Path],
|
|
109
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
110
|
-
recreate: bool = False,
|
|
111
|
-
upsert: bool = False,
|
|
112
|
-
skip_existing: bool = True,
|
|
113
|
-
) -> None:
|
|
114
|
-
_file_path = Path(path) if isinstance(path, str) else path
|
|
115
|
-
|
|
116
|
-
# Validate file and prepare collection in one step
|
|
117
|
-
if not self.prepare_load(_file_path, self.formats, metadata, recreate):
|
|
118
|
-
return
|
|
119
|
-
|
|
120
|
-
# Read documents
|
|
121
|
-
try:
|
|
122
|
-
documents = self.reader.read(pdf=_file_path)
|
|
123
|
-
except Exception as e:
|
|
124
|
-
logger.exception(f"Failed to read documents from file {_file_path}: {e}")
|
|
125
|
-
return
|
|
126
|
-
|
|
127
|
-
# Process documents
|
|
128
|
-
self.process_documents(
|
|
129
|
-
documents=documents,
|
|
130
|
-
metadata=metadata,
|
|
131
|
-
upsert=upsert,
|
|
132
|
-
skip_existing=skip_existing,
|
|
133
|
-
source_info=str(_file_path),
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
async def aload_document(
|
|
137
|
-
self,
|
|
138
|
-
path: Union[str, Path],
|
|
139
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
140
|
-
recreate: bool = False,
|
|
141
|
-
upsert: bool = False,
|
|
142
|
-
skip_existing: bool = True,
|
|
143
|
-
) -> None:
|
|
144
|
-
_file_path = Path(path) if isinstance(path, str) else path
|
|
145
|
-
|
|
146
|
-
# Validate file and prepare collection in one step
|
|
147
|
-
if not await self.aprepare_load(_file_path, self.formats, metadata, recreate):
|
|
148
|
-
return
|
|
149
|
-
|
|
150
|
-
# Read documents
|
|
151
|
-
try:
|
|
152
|
-
documents = await self.reader.async_read(pdf=_file_path)
|
|
153
|
-
except Exception as e:
|
|
154
|
-
logger.exception(f"Failed to read documents from file {_file_path}: {e}")
|
|
155
|
-
return
|
|
156
|
-
|
|
157
|
-
# Process documents
|
|
158
|
-
await self.aprocess_documents(
|
|
159
|
-
documents=documents,
|
|
160
|
-
metadata=metadata,
|
|
161
|
-
upsert=upsert,
|
|
162
|
-
skip_existing=skip_existing,
|
|
163
|
-
source_info=str(_file_path),
|
|
164
|
-
)
|