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
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import math
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
from uuid import uuid4
|
|
6
|
+
|
|
7
|
+
from fastapi import APIRouter, BackgroundTasks, Depends, File, Form, HTTPException, Path, Query, UploadFile
|
|
8
|
+
|
|
9
|
+
from agno.knowledge.content import Content, FileData
|
|
10
|
+
from agno.knowledge.knowledge import Knowledge
|
|
11
|
+
from agno.knowledge.reader import ReaderFactory
|
|
12
|
+
from agno.knowledge.reader.base import Reader
|
|
13
|
+
from agno.knowledge.utils import get_all_chunkers_info, get_all_readers_info, get_content_types_to_readers_mapping
|
|
14
|
+
from agno.os.auth import get_authentication_dependency
|
|
15
|
+
from agno.os.routers.knowledge.schemas import (
|
|
16
|
+
ChunkerSchema,
|
|
17
|
+
ConfigResponseSchema,
|
|
18
|
+
ContentResponseSchema,
|
|
19
|
+
ContentStatus,
|
|
20
|
+
ContentStatusResponse,
|
|
21
|
+
ContentUpdateSchema,
|
|
22
|
+
ReaderSchema,
|
|
23
|
+
)
|
|
24
|
+
from agno.os.schema import PaginatedResponse, PaginationInfo, SortOrder
|
|
25
|
+
from agno.os.settings import AgnoAPISettings
|
|
26
|
+
from agno.os.utils import get_knowledge_instance_by_db_id
|
|
27
|
+
from agno.utils.log import log_debug, log_info
|
|
28
|
+
|
|
29
|
+
logger = logging.getLogger(__name__)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_knowledge_router(
|
|
33
|
+
knowledge_instances: List[Knowledge], settings: AgnoAPISettings = AgnoAPISettings()
|
|
34
|
+
) -> APIRouter:
|
|
35
|
+
router = APIRouter(dependencies=[Depends(get_authentication_dependency(settings))], tags=["Knowledge"])
|
|
36
|
+
return attach_routes(router=router, knowledge_instances=knowledge_instances)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def attach_routes(router: APIRouter, knowledge_instances: List[Knowledge]) -> APIRouter:
|
|
40
|
+
@router.post("/knowledge/content", response_model=ContentResponseSchema, status_code=202)
|
|
41
|
+
async def upload_content(
|
|
42
|
+
background_tasks: BackgroundTasks,
|
|
43
|
+
name: Optional[str] = Form(None),
|
|
44
|
+
description: Optional[str] = Form(None),
|
|
45
|
+
url: Optional[str] = Form(None),
|
|
46
|
+
metadata: Optional[str] = Form(None, description="JSON metadata"),
|
|
47
|
+
file: Optional[UploadFile] = File(None),
|
|
48
|
+
text_content: Optional[str] = Form(None),
|
|
49
|
+
reader_id: Optional[str] = Form(None),
|
|
50
|
+
chunker: Optional[str] = Form(None),
|
|
51
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
52
|
+
):
|
|
53
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
54
|
+
content_id = str(uuid4())
|
|
55
|
+
log_info(f"Adding content: {name}, {description}, {url}, {metadata} with ID: {content_id}")
|
|
56
|
+
|
|
57
|
+
parsed_metadata = None
|
|
58
|
+
if metadata:
|
|
59
|
+
try:
|
|
60
|
+
parsed_metadata = json.loads(metadata)
|
|
61
|
+
except json.JSONDecodeError:
|
|
62
|
+
# If it's not valid JSON, treat as a simple key-value pair
|
|
63
|
+
parsed_metadata = {"value": metadata} if metadata != "string" else None
|
|
64
|
+
if file:
|
|
65
|
+
content_bytes = await file.read()
|
|
66
|
+
elif text_content:
|
|
67
|
+
content_bytes = text_content.encode("utf-8")
|
|
68
|
+
else:
|
|
69
|
+
content_bytes = None
|
|
70
|
+
|
|
71
|
+
parsed_urls = None
|
|
72
|
+
if url and url.strip():
|
|
73
|
+
try:
|
|
74
|
+
parsed_urls = json.loads(url)
|
|
75
|
+
log_debug(f"Parsed URLs: {parsed_urls}")
|
|
76
|
+
except json.JSONDecodeError:
|
|
77
|
+
# If it's not valid JSON, treat as a single URL string
|
|
78
|
+
parsed_urls = url
|
|
79
|
+
|
|
80
|
+
# # Parse metadata with proper error handling
|
|
81
|
+
parsed_metadata = None
|
|
82
|
+
if metadata:
|
|
83
|
+
try:
|
|
84
|
+
parsed_metadata = json.loads(metadata)
|
|
85
|
+
except json.JSONDecodeError:
|
|
86
|
+
# If it's not valid JSON, treat as a simple key-value pair
|
|
87
|
+
parsed_metadata = {"value": metadata}
|
|
88
|
+
|
|
89
|
+
if text_content:
|
|
90
|
+
file_data = FileData(
|
|
91
|
+
content=content_bytes,
|
|
92
|
+
type="manual",
|
|
93
|
+
)
|
|
94
|
+
elif file:
|
|
95
|
+
file_data = FileData(
|
|
96
|
+
content=content_bytes,
|
|
97
|
+
type=file.content_type if file.content_type else None,
|
|
98
|
+
filename=file.filename,
|
|
99
|
+
size=file.size,
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
file_data = None
|
|
103
|
+
|
|
104
|
+
if not name:
|
|
105
|
+
if file and file.filename:
|
|
106
|
+
name = file.filename
|
|
107
|
+
elif url:
|
|
108
|
+
name = parsed_urls
|
|
109
|
+
|
|
110
|
+
content = Content(
|
|
111
|
+
name=name,
|
|
112
|
+
description=description,
|
|
113
|
+
url=parsed_urls,
|
|
114
|
+
metadata=parsed_metadata,
|
|
115
|
+
file_data=file_data,
|
|
116
|
+
size=file.size if file else None if text_content else None,
|
|
117
|
+
)
|
|
118
|
+
background_tasks.add_task(process_content, knowledge, content_id, content, reader_id, chunker)
|
|
119
|
+
|
|
120
|
+
response = ContentResponseSchema(
|
|
121
|
+
id=content_id,
|
|
122
|
+
name=name,
|
|
123
|
+
description=description,
|
|
124
|
+
metadata=parsed_metadata,
|
|
125
|
+
status=ContentStatus.PROCESSING,
|
|
126
|
+
)
|
|
127
|
+
return response
|
|
128
|
+
|
|
129
|
+
@router.patch("/knowledge/content/{content_id}", response_model=ContentResponseSchema, status_code=200)
|
|
130
|
+
async def update_content(
|
|
131
|
+
content_id: str = Path(..., description="Content ID"),
|
|
132
|
+
name: Optional[str] = Form(None, description="Content name"),
|
|
133
|
+
description: Optional[str] = Form(None, description="Content description"),
|
|
134
|
+
metadata: Optional[str] = Form(None, description="Content metadata as JSON string"),
|
|
135
|
+
reader_id: Optional[str] = Form(None, description="ID of the reader to use for processing"),
|
|
136
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
137
|
+
) -> Optional[ContentResponseSchema]:
|
|
138
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
139
|
+
|
|
140
|
+
# Parse metadata JSON string if provided
|
|
141
|
+
parsed_metadata = None
|
|
142
|
+
if metadata and metadata.strip():
|
|
143
|
+
try:
|
|
144
|
+
parsed_metadata = json.loads(metadata)
|
|
145
|
+
except json.JSONDecodeError:
|
|
146
|
+
raise HTTPException(status_code=400, detail="Invalid JSON format for metadata")
|
|
147
|
+
|
|
148
|
+
# Create ContentUpdateSchema object from form data
|
|
149
|
+
update_data = ContentUpdateSchema(
|
|
150
|
+
name=name if name and name.strip() else None,
|
|
151
|
+
description=description if description and description.strip() else None,
|
|
152
|
+
metadata=parsed_metadata,
|
|
153
|
+
reader_id=reader_id if reader_id and reader_id.strip() else None,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
content = Content(
|
|
157
|
+
id=content_id,
|
|
158
|
+
name=update_data.name,
|
|
159
|
+
description=update_data.description,
|
|
160
|
+
metadata=update_data.metadata,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if update_data.reader_id:
|
|
164
|
+
if knowledge.readers and update_data.reader_id in knowledge.readers:
|
|
165
|
+
content.reader = knowledge.readers[update_data.reader_id]
|
|
166
|
+
else:
|
|
167
|
+
raise HTTPException(status_code=400, detail=f"Invalid reader_id: {update_data.reader_id}")
|
|
168
|
+
|
|
169
|
+
updated_content_dict = knowledge.patch_content(content)
|
|
170
|
+
if not updated_content_dict:
|
|
171
|
+
raise HTTPException(status_code=404, detail=f"Content not found: {content_id}")
|
|
172
|
+
|
|
173
|
+
return ContentResponseSchema.from_dict(updated_content_dict)
|
|
174
|
+
|
|
175
|
+
@router.get("/knowledge/content", response_model=PaginatedResponse[ContentResponseSchema], status_code=200)
|
|
176
|
+
def get_content(
|
|
177
|
+
limit: Optional[int] = Query(default=20, description="Number of content entries to return"),
|
|
178
|
+
page: Optional[int] = Query(default=1, description="Page number"),
|
|
179
|
+
sort_by: Optional[str] = Query(default="created_at", description="Field to sort by"),
|
|
180
|
+
sort_order: Optional[SortOrder] = Query(default="desc", description="Sort order (asc or desc)"),
|
|
181
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
182
|
+
) -> PaginatedResponse[ContentResponseSchema]:
|
|
183
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
184
|
+
contents, count = knowledge.get_content(limit=limit, page=page, sort_by=sort_by, sort_order=sort_order)
|
|
185
|
+
|
|
186
|
+
return PaginatedResponse(
|
|
187
|
+
data=[
|
|
188
|
+
ContentResponseSchema.from_dict(
|
|
189
|
+
{
|
|
190
|
+
"id": content.id,
|
|
191
|
+
"name": content.name,
|
|
192
|
+
"description": content.description,
|
|
193
|
+
"file_type": content.file_type,
|
|
194
|
+
"size": content.size,
|
|
195
|
+
"metadata": content.metadata,
|
|
196
|
+
"status": content.status,
|
|
197
|
+
"status_message": content.status_message,
|
|
198
|
+
"created_at": content.created_at,
|
|
199
|
+
"updated_at": content.updated_at,
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
for content in contents
|
|
203
|
+
],
|
|
204
|
+
meta=PaginationInfo(
|
|
205
|
+
page=page,
|
|
206
|
+
limit=limit,
|
|
207
|
+
total_count=count,
|
|
208
|
+
total_pages=math.ceil(count / limit) if limit is not None and limit > 0 else 0,
|
|
209
|
+
),
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
@router.get("/knowledge/content/{content_id}", response_model=ContentResponseSchema, status_code=200)
|
|
213
|
+
def get_content_by_id(
|
|
214
|
+
content_id: str,
|
|
215
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
216
|
+
) -> ContentResponseSchema:
|
|
217
|
+
log_info(f"Getting content by id: {content_id}")
|
|
218
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
219
|
+
content = knowledge.get_content_by_id(content_id=content_id)
|
|
220
|
+
if not content:
|
|
221
|
+
raise HTTPException(status_code=404, detail=f"Content not found: {content_id}")
|
|
222
|
+
response = ContentResponseSchema.from_dict(
|
|
223
|
+
{
|
|
224
|
+
"id": content_id,
|
|
225
|
+
"name": content.name,
|
|
226
|
+
"description": content.description,
|
|
227
|
+
"file_type": content.file_type,
|
|
228
|
+
"size": len(content.file_data.content) if content.file_data and content.file_data.content else 0,
|
|
229
|
+
"metadata": content.metadata,
|
|
230
|
+
"status": content.status,
|
|
231
|
+
"status_message": content.status_message,
|
|
232
|
+
"created_at": content.created_at,
|
|
233
|
+
"updated_at": content.updated_at,
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return response
|
|
238
|
+
|
|
239
|
+
@router.delete(
|
|
240
|
+
"/knowledge/content/{content_id}",
|
|
241
|
+
response_model=ContentResponseSchema,
|
|
242
|
+
status_code=200,
|
|
243
|
+
response_model_exclude_none=True,
|
|
244
|
+
)
|
|
245
|
+
def delete_content_by_id(
|
|
246
|
+
content_id: str,
|
|
247
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
248
|
+
) -> ContentResponseSchema:
|
|
249
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
250
|
+
knowledge.remove_content_by_id(content_id=content_id)
|
|
251
|
+
log_info(f"Deleting content by id: {content_id}")
|
|
252
|
+
|
|
253
|
+
return ContentResponseSchema(
|
|
254
|
+
id=content_id,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
@router.delete("/knowledge/content", status_code=200)
|
|
258
|
+
def delete_all_content(
|
|
259
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
260
|
+
):
|
|
261
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
262
|
+
log_info("Deleting all content")
|
|
263
|
+
knowledge.remove_all_content()
|
|
264
|
+
return "success"
|
|
265
|
+
|
|
266
|
+
@router.get("/knowledge/content/{content_id}/status", status_code=200, response_model=ContentStatusResponse)
|
|
267
|
+
def get_content_status(
|
|
268
|
+
content_id: str,
|
|
269
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
270
|
+
) -> ContentStatusResponse:
|
|
271
|
+
log_info(f"Getting content status: {content_id}")
|
|
272
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
273
|
+
knowledge_status, status_message = knowledge.get_content_status(content_id=content_id)
|
|
274
|
+
|
|
275
|
+
# Handle the case where content is not found
|
|
276
|
+
if knowledge_status is None:
|
|
277
|
+
return ContentStatusResponse(
|
|
278
|
+
status=ContentStatus.FAILED, status_message=status_message or "Content not found"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Convert knowledge ContentStatus to schema ContentStatus (they have same values)
|
|
282
|
+
if hasattr(knowledge_status, "value"):
|
|
283
|
+
status_value = knowledge_status.value
|
|
284
|
+
else:
|
|
285
|
+
status_value = str(knowledge_status)
|
|
286
|
+
|
|
287
|
+
# Convert string status to ContentStatus enum if needed (for backward compatibility and mocks)
|
|
288
|
+
if isinstance(status_value, str):
|
|
289
|
+
try:
|
|
290
|
+
status = ContentStatus(status_value.lower())
|
|
291
|
+
except ValueError:
|
|
292
|
+
# Handle legacy or unknown statuses gracefully
|
|
293
|
+
if "failed" in status_value.lower():
|
|
294
|
+
status = ContentStatus.FAILED
|
|
295
|
+
elif "completed" in status_value.lower():
|
|
296
|
+
status = ContentStatus.COMPLETED
|
|
297
|
+
else:
|
|
298
|
+
status = ContentStatus.PROCESSING
|
|
299
|
+
else:
|
|
300
|
+
status = ContentStatus.PROCESSING
|
|
301
|
+
|
|
302
|
+
return ContentStatusResponse(status=status, status_message=status_message or "")
|
|
303
|
+
|
|
304
|
+
@router.get("/knowledge/config", status_code=200)
|
|
305
|
+
def get_config(
|
|
306
|
+
db_id: Optional[str] = Query(default=None, description="The ID of the database to use"),
|
|
307
|
+
) -> ConfigResponseSchema:
|
|
308
|
+
knowledge = get_knowledge_instance_by_db_id(knowledge_instances, db_id)
|
|
309
|
+
|
|
310
|
+
# Get factory readers info
|
|
311
|
+
readers_info = get_all_readers_info()
|
|
312
|
+
reader_schemas = {}
|
|
313
|
+
# Add factory readers
|
|
314
|
+
for reader_info in readers_info:
|
|
315
|
+
reader_schemas[reader_info["id"]] = ReaderSchema(
|
|
316
|
+
id=reader_info["id"],
|
|
317
|
+
name=reader_info["name"],
|
|
318
|
+
description=reader_info.get("description"),
|
|
319
|
+
chunkers=reader_info.get("chunking_strategies", []),
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
# Add custom readers from knowledge.readers
|
|
323
|
+
readers_dict: Dict[str, Reader] = knowledge.get_readers() or {}
|
|
324
|
+
if readers_dict:
|
|
325
|
+
for reader_id, reader in readers_dict.items():
|
|
326
|
+
# Get chunking strategies from the reader
|
|
327
|
+
chunking_strategies = []
|
|
328
|
+
try:
|
|
329
|
+
strategies = reader.get_supported_chunking_strategies()
|
|
330
|
+
chunking_strategies = [strategy.value for strategy in strategies]
|
|
331
|
+
except Exception:
|
|
332
|
+
chunking_strategies = []
|
|
333
|
+
|
|
334
|
+
# Check if this reader ID already exists in factory readers
|
|
335
|
+
if reader_id not in reader_schemas:
|
|
336
|
+
reader_schemas[reader_id] = ReaderSchema(
|
|
337
|
+
id=reader_id,
|
|
338
|
+
name=getattr(reader, "name", reader.__class__.__name__),
|
|
339
|
+
description=getattr(reader, "description", f"Custom {reader.__class__.__name__}"),
|
|
340
|
+
chunkers=chunking_strategies,
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Get content types to readers mapping
|
|
344
|
+
types_of_readers = get_content_types_to_readers_mapping()
|
|
345
|
+
chunkers_list = get_all_chunkers_info()
|
|
346
|
+
|
|
347
|
+
# Convert chunkers list to dictionary format expected by schema
|
|
348
|
+
chunkers_dict = {}
|
|
349
|
+
for chunker_info in chunkers_list:
|
|
350
|
+
chunker_key = chunker_info.get("key")
|
|
351
|
+
if chunker_key:
|
|
352
|
+
chunkers_dict[chunker_key] = ChunkerSchema(
|
|
353
|
+
key=chunker_key, name=chunker_info.get("name"), description=chunker_info.get("description")
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return ConfigResponseSchema(
|
|
357
|
+
readers=reader_schemas,
|
|
358
|
+
readersForType=types_of_readers,
|
|
359
|
+
chunkers=chunkers_dict,
|
|
360
|
+
filters=knowledge.get_filters(),
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
return router
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
async def process_content(
|
|
367
|
+
knowledge: Knowledge,
|
|
368
|
+
content_id: str,
|
|
369
|
+
content: Content,
|
|
370
|
+
reader_id: Optional[str] = None,
|
|
371
|
+
chunker: Optional[str] = None,
|
|
372
|
+
):
|
|
373
|
+
"""Background task to process the content"""
|
|
374
|
+
log_info(f"Processing content {content_id}")
|
|
375
|
+
try:
|
|
376
|
+
content.id = content_id
|
|
377
|
+
if reader_id:
|
|
378
|
+
reader = None
|
|
379
|
+
if knowledge.readers and reader_id in knowledge.readers:
|
|
380
|
+
reader = knowledge.readers[reader_id]
|
|
381
|
+
else:
|
|
382
|
+
key = reader_id.lower().strip().replace("-", "_").replace(" ", "_")
|
|
383
|
+
candidates = [key] + ([key[:-6]] if key.endswith("reader") else [])
|
|
384
|
+
for cand in candidates:
|
|
385
|
+
try:
|
|
386
|
+
reader = ReaderFactory.create_reader(cand)
|
|
387
|
+
log_debug(f"Resolved reader: {reader.__class__.__name__}")
|
|
388
|
+
break
|
|
389
|
+
except Exception:
|
|
390
|
+
continue
|
|
391
|
+
if reader:
|
|
392
|
+
content.reader = reader
|
|
393
|
+
if chunker and content.reader:
|
|
394
|
+
# Set the chunker name on the reader - let the reader handle it internally
|
|
395
|
+
content.reader.set_chunking_strategy_from_string(chunker)
|
|
396
|
+
log_debug(f"Set chunking strategy: {chunker}")
|
|
397
|
+
|
|
398
|
+
log_debug(f"Using reader: {content.reader.__class__.__name__}")
|
|
399
|
+
await knowledge._load_content(content, upsert=False, skip_if_exists=True)
|
|
400
|
+
log_info(f"Content {content_id} processed successfully")
|
|
401
|
+
except Exception as e:
|
|
402
|
+
log_info(f"Error processing content {content_id}: {e}")
|
|
403
|
+
# Mark content as failed in the contents DB
|
|
404
|
+
try:
|
|
405
|
+
from agno.knowledge.content import ContentStatus as KnowledgeContentStatus
|
|
406
|
+
|
|
407
|
+
content.status = KnowledgeContentStatus.FAILED
|
|
408
|
+
content.status_message = str(e)
|
|
409
|
+
content.id = content_id
|
|
410
|
+
knowledge.patch_content(content)
|
|
411
|
+
except Exception:
|
|
412
|
+
# Swallow any secondary errors to avoid crashing the background task
|
|
413
|
+
pass
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ContentStatus(str, Enum):
|
|
9
|
+
"""Enumeration of possible content processing statuses."""
|
|
10
|
+
|
|
11
|
+
PROCESSING = "processing"
|
|
12
|
+
COMPLETED = "completed"
|
|
13
|
+
FAILED = "failed"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ContentStatusResponse(BaseModel):
|
|
17
|
+
"""Response model for content status endpoint."""
|
|
18
|
+
|
|
19
|
+
status: ContentStatus
|
|
20
|
+
status_message: str = ""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ContentResponseSchema(BaseModel):
|
|
24
|
+
id: str
|
|
25
|
+
name: Optional[str] = None
|
|
26
|
+
description: Optional[str] = None
|
|
27
|
+
type: Optional[str] = None
|
|
28
|
+
size: Optional[str] = None
|
|
29
|
+
linked_to: Optional[str] = None
|
|
30
|
+
metadata: Optional[dict] = None
|
|
31
|
+
access_count: Optional[int] = None
|
|
32
|
+
status: Optional[ContentStatus] = None
|
|
33
|
+
status_message: Optional[str] = None
|
|
34
|
+
created_at: Optional[datetime] = None
|
|
35
|
+
updated_at: Optional[datetime] = None
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_dict(cls, content: Dict[str, Any]) -> "ContentResponseSchema":
|
|
39
|
+
status = content.get("status")
|
|
40
|
+
if isinstance(status, str):
|
|
41
|
+
try:
|
|
42
|
+
status = ContentStatus(status.lower())
|
|
43
|
+
except ValueError:
|
|
44
|
+
# Handle legacy or unknown statuses gracefully
|
|
45
|
+
if "failed" in status.lower():
|
|
46
|
+
status = ContentStatus.FAILED
|
|
47
|
+
elif "completed" in status.lower():
|
|
48
|
+
status = ContentStatus.COMPLETED
|
|
49
|
+
else:
|
|
50
|
+
status = ContentStatus.PROCESSING
|
|
51
|
+
elif status is None:
|
|
52
|
+
status = ContentStatus.PROCESSING # Default for None values
|
|
53
|
+
|
|
54
|
+
# Helper function to safely parse timestamps
|
|
55
|
+
def parse_timestamp(timestamp_value):
|
|
56
|
+
if timestamp_value is None:
|
|
57
|
+
return None
|
|
58
|
+
try:
|
|
59
|
+
# If it's already a datetime object, return it
|
|
60
|
+
if isinstance(timestamp_value, datetime):
|
|
61
|
+
return timestamp_value
|
|
62
|
+
# If it's a string, try to parse it as ISO format first
|
|
63
|
+
if isinstance(timestamp_value, str):
|
|
64
|
+
try:
|
|
65
|
+
return datetime.fromisoformat(timestamp_value.replace("Z", "+00:00"))
|
|
66
|
+
except ValueError:
|
|
67
|
+
# Try to parse as float/int timestamp
|
|
68
|
+
timestamp_value = float(timestamp_value)
|
|
69
|
+
# If it's a number, use fromtimestamp
|
|
70
|
+
return datetime.fromtimestamp(timestamp_value, tz=timezone.utc)
|
|
71
|
+
except (ValueError, TypeError, OSError):
|
|
72
|
+
# If all parsing fails, return None
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
return cls(
|
|
76
|
+
id=content.get("id"), # type: ignore
|
|
77
|
+
name=content.get("name"),
|
|
78
|
+
description=content.get("description"),
|
|
79
|
+
type=content.get("file_type"),
|
|
80
|
+
size=str(content.get("size")) if content.get("size") else "0",
|
|
81
|
+
metadata=content.get("metadata"),
|
|
82
|
+
status=status,
|
|
83
|
+
status_message=content.get("status_message"),
|
|
84
|
+
created_at=parse_timestamp(content.get("created_at")),
|
|
85
|
+
updated_at=parse_timestamp(content.get("updated_at")),
|
|
86
|
+
# TODO: These fields are not available in the Content class. Fix the inconsistency
|
|
87
|
+
access_count=None,
|
|
88
|
+
linked_to=None,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class ContentUpdateSchema(BaseModel):
|
|
93
|
+
"""Schema for updating content."""
|
|
94
|
+
|
|
95
|
+
name: Optional[str] = Field(None, description="Content name", min_length=1, max_length=255)
|
|
96
|
+
description: Optional[str] = Field(None, description="Content description", max_length=1000)
|
|
97
|
+
metadata: Optional[Dict[str, Any]] = Field(None, description="Content metadata as key-value pairs")
|
|
98
|
+
reader_id: Optional[str] = Field(None, description="ID of the reader to use for processing", min_length=1)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ReaderSchema(BaseModel):
|
|
102
|
+
id: str
|
|
103
|
+
name: Optional[str] = None
|
|
104
|
+
description: Optional[str] = None
|
|
105
|
+
chunkers: Optional[List[str]] = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class ChunkerSchema(BaseModel):
|
|
109
|
+
key: str
|
|
110
|
+
name: Optional[str] = None
|
|
111
|
+
description: Optional[str] = None
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class ConfigResponseSchema(BaseModel):
|
|
115
|
+
readers: Optional[Dict[str, ReaderSchema]] = None
|
|
116
|
+
readersForType: Optional[Dict[str, List[str]]] = None
|
|
117
|
+
chunkers: Optional[Dict[str, ChunkerSchema]] = None
|
|
118
|
+
filters: Optional[List[str]] = None
|