agno 0.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/__init__.py +8 -0
- agno/agent/__init__.py +44 -5
- agno/agent/agent.py +10531 -2975
- agno/api/agent.py +14 -53
- agno/api/api.py +7 -46
- agno/api/evals.py +22 -0
- agno/api/os.py +17 -0
- agno/api/routes.py +6 -25
- agno/api/schemas/__init__.py +9 -0
- agno/api/schemas/agent.py +6 -9
- agno/api/schemas/evals.py +16 -0
- agno/api/schemas/os.py +14 -0
- agno/api/schemas/team.py +10 -10
- agno/api/schemas/utils.py +21 -0
- agno/api/schemas/workflows.py +16 -0
- agno/api/settings.py +53 -0
- agno/api/team.py +22 -26
- 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/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/__init__.py +3 -0
- agno/culture/manager.py +956 -0
- agno/db/__init__.py +24 -0
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/base.py +946 -0
- agno/db/dynamo/__init__.py +3 -0
- agno/db/dynamo/dynamo.py +2781 -0
- agno/db/dynamo/schemas.py +442 -0
- agno/db/dynamo/utils.py +743 -0
- agno/db/firestore/__init__.py +3 -0
- agno/db/firestore/firestore.py +2379 -0
- agno/db/firestore/schemas.py +181 -0
- agno/db/firestore/utils.py +376 -0
- agno/db/gcs_json/__init__.py +3 -0
- agno/db/gcs_json/gcs_json_db.py +1791 -0
- agno/db/gcs_json/utils.py +228 -0
- agno/db/in_memory/__init__.py +3 -0
- agno/db/in_memory/in_memory_db.py +1312 -0
- agno/db/in_memory/utils.py +230 -0
- agno/db/json/__init__.py +3 -0
- agno/db/json/json_db.py +1777 -0
- agno/db/json/utils.py +230 -0
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/v1_to_v2.py +635 -0
- agno/db/migrations/versions/v2_3_0.py +938 -0
- agno/db/mongo/__init__.py +17 -0
- agno/db/mongo/async_mongo.py +2760 -0
- agno/db/mongo/mongo.py +2597 -0
- agno/db/mongo/schemas.py +119 -0
- agno/db/mongo/utils.py +276 -0
- agno/db/mysql/__init__.py +4 -0
- agno/db/mysql/async_mysql.py +2912 -0
- agno/db/mysql/mysql.py +2923 -0
- agno/db/mysql/schemas.py +186 -0
- agno/db/mysql/utils.py +488 -0
- agno/db/postgres/__init__.py +4 -0
- agno/db/postgres/async_postgres.py +2579 -0
- agno/db/postgres/postgres.py +2870 -0
- agno/db/postgres/schemas.py +187 -0
- agno/db/postgres/utils.py +442 -0
- agno/db/redis/__init__.py +3 -0
- agno/db/redis/redis.py +2141 -0
- agno/db/redis/schemas.py +159 -0
- agno/db/redis/utils.py +346 -0
- agno/db/schemas/__init__.py +4 -0
- agno/db/schemas/culture.py +120 -0
- agno/db/schemas/evals.py +34 -0
- agno/db/schemas/knowledge.py +40 -0
- agno/db/schemas/memory.py +61 -0
- agno/db/singlestore/__init__.py +3 -0
- agno/db/singlestore/schemas.py +179 -0
- agno/db/singlestore/singlestore.py +2877 -0
- agno/db/singlestore/utils.py +384 -0
- agno/db/sqlite/__init__.py +4 -0
- agno/db/sqlite/async_sqlite.py +2911 -0
- agno/db/sqlite/schemas.py +181 -0
- agno/db/sqlite/sqlite.py +2908 -0
- agno/db/sqlite/utils.py +429 -0
- agno/db/surrealdb/__init__.py +3 -0
- agno/db/surrealdb/metrics.py +292 -0
- agno/db/surrealdb/models.py +334 -0
- agno/db/surrealdb/queries.py +71 -0
- agno/db/surrealdb/surrealdb.py +1908 -0
- agno/db/surrealdb/utils.py +147 -0
- agno/db/utils.py +118 -0
- agno/eval/__init__.py +24 -0
- agno/eval/accuracy.py +666 -276
- agno/eval/agent_as_judge.py +861 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +779 -0
- agno/eval/reliability.py +241 -62
- agno/eval/utils.py +120 -0
- agno/exceptions.py +143 -1
- agno/filters.py +354 -0
- agno/guardrails/__init__.py +6 -0
- agno/guardrails/base.py +19 -0
- agno/guardrails/openai.py +144 -0
- agno/guardrails/pii.py +94 -0
- agno/guardrails/prompt_injection.py +52 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/__init__.py +3 -0
- agno/integrations/discord/client.py +203 -0
- agno/knowledge/__init__.py +5 -1
- agno/{document → knowledge}/chunking/agentic.py +22 -14
- agno/{document → knowledge}/chunking/document.py +2 -2
- agno/{document → knowledge}/chunking/fixed.py +7 -6
- agno/knowledge/chunking/markdown.py +151 -0
- agno/{document → knowledge}/chunking/recursive.py +15 -3
- agno/knowledge/chunking/row.py +39 -0
- agno/knowledge/chunking/semantic.py +91 -0
- agno/knowledge/chunking/strategy.py +165 -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/knowledge/embedder/aws_bedrock.py +343 -0
- agno/knowledge/embedder/azure_openai.py +210 -0
- agno/{embedder → knowledge/embedder}/base.py +8 -0
- agno/knowledge/embedder/cohere.py +323 -0
- agno/knowledge/embedder/fastembed.py +62 -0
- agno/{embedder → knowledge/embedder}/fireworks.py +1 -1
- agno/knowledge/embedder/google.py +258 -0
- agno/knowledge/embedder/huggingface.py +94 -0
- agno/knowledge/embedder/jina.py +182 -0
- agno/knowledge/embedder/langdb.py +22 -0
- agno/knowledge/embedder/mistral.py +206 -0
- agno/knowledge/embedder/nebius.py +13 -0
- agno/knowledge/embedder/ollama.py +154 -0
- agno/knowledge/embedder/openai.py +195 -0
- agno/knowledge/embedder/sentence_transformer.py +63 -0
- agno/{embedder → knowledge/embedder}/together.py +1 -1
- agno/knowledge/embedder/vllm.py +262 -0
- agno/knowledge/embedder/voyageai.py +165 -0
- agno/knowledge/knowledge.py +3006 -0
- agno/knowledge/reader/__init__.py +7 -0
- agno/knowledge/reader/arxiv_reader.py +81 -0
- agno/knowledge/reader/base.py +95 -0
- agno/knowledge/reader/csv_reader.py +164 -0
- agno/knowledge/reader/docx_reader.py +82 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
- agno/knowledge/reader/firecrawl_reader.py +201 -0
- agno/knowledge/reader/json_reader.py +88 -0
- agno/knowledge/reader/markdown_reader.py +137 -0
- agno/knowledge/reader/pdf_reader.py +431 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +313 -0
- agno/knowledge/reader/s3_reader.py +89 -0
- agno/knowledge/reader/tavily_reader.py +193 -0
- agno/knowledge/reader/text_reader.py +127 -0
- agno/knowledge/reader/web_search_reader.py +325 -0
- agno/knowledge/reader/website_reader.py +455 -0
- agno/knowledge/reader/wikipedia_reader.py +91 -0
- agno/knowledge/reader/youtube_reader.py +78 -0
- agno/knowledge/remote_content/remote_content.py +88 -0
- agno/knowledge/reranker/__init__.py +3 -0
- agno/{reranker → knowledge/reranker}/base.py +1 -1
- agno/{reranker → knowledge/reranker}/cohere.py +2 -2
- agno/knowledge/reranker/infinity.py +195 -0
- agno/knowledge/reranker/sentence_transformer.py +54 -0
- agno/knowledge/types.py +39 -0
- agno/knowledge/utils.py +234 -0
- agno/media.py +439 -95
- agno/memory/__init__.py +16 -3
- agno/memory/manager.py +1474 -123
- agno/memory/strategies/__init__.py +15 -0
- agno/memory/strategies/base.py +66 -0
- agno/memory/strategies/summarize.py +196 -0
- agno/memory/strategies/types.py +37 -0
- agno/models/aimlapi/__init__.py +5 -0
- agno/models/aimlapi/aimlapi.py +62 -0
- agno/models/anthropic/__init__.py +4 -0
- agno/models/anthropic/claude.py +960 -496
- agno/models/aws/__init__.py +15 -0
- agno/models/aws/bedrock.py +686 -451
- agno/models/aws/claude.py +190 -183
- agno/models/azure/__init__.py +18 -1
- agno/models/azure/ai_foundry.py +489 -0
- agno/models/azure/openai_chat.py +89 -40
- agno/models/base.py +2477 -550
- agno/models/cerebras/__init__.py +12 -0
- agno/models/cerebras/cerebras.py +565 -0
- agno/models/cerebras/cerebras_openai.py +131 -0
- agno/models/cohere/__init__.py +4 -0
- agno/models/cohere/chat.py +306 -492
- agno/models/cometapi/__init__.py +5 -0
- agno/models/cometapi/cometapi.py +74 -0
- agno/models/dashscope/__init__.py +5 -0
- agno/models/dashscope/dashscope.py +90 -0
- agno/models/deepinfra/__init__.py +5 -0
- agno/models/deepinfra/deepinfra.py +45 -0
- agno/models/deepseek/__init__.py +4 -0
- agno/models/deepseek/deepseek.py +110 -9
- agno/models/fireworks/__init__.py +4 -0
- agno/models/fireworks/fireworks.py +19 -22
- agno/models/google/__init__.py +3 -7
- agno/models/google/gemini.py +1717 -662
- agno/models/google/utils.py +22 -0
- agno/models/groq/__init__.py +4 -0
- agno/models/groq/groq.py +391 -666
- agno/models/huggingface/__init__.py +4 -0
- agno/models/huggingface/huggingface.py +266 -538
- agno/models/ibm/__init__.py +5 -0
- agno/models/ibm/watsonx.py +432 -0
- agno/models/internlm/__init__.py +3 -0
- agno/models/internlm/internlm.py +20 -3
- agno/models/langdb/__init__.py +1 -0
- agno/models/langdb/langdb.py +60 -0
- agno/models/litellm/__init__.py +14 -0
- agno/models/litellm/chat.py +503 -0
- agno/models/litellm/litellm_openai.py +42 -0
- agno/models/llama_cpp/__init__.py +5 -0
- agno/models/llama_cpp/llama_cpp.py +22 -0
- agno/models/lmstudio/__init__.py +5 -0
- agno/models/lmstudio/lmstudio.py +25 -0
- agno/models/message.py +361 -39
- agno/models/meta/__init__.py +12 -0
- agno/models/meta/llama.py +502 -0
- agno/models/meta/llama_openai.py +79 -0
- agno/models/metrics.py +120 -0
- agno/models/mistral/__init__.py +4 -0
- agno/models/mistral/mistral.py +293 -393
- agno/models/nebius/__init__.py +3 -0
- agno/models/nebius/nebius.py +53 -0
- agno/models/nexus/__init__.py +3 -0
- agno/models/nexus/nexus.py +22 -0
- agno/models/nvidia/__init__.py +4 -0
- agno/models/nvidia/nvidia.py +22 -3
- agno/models/ollama/__init__.py +4 -2
- agno/models/ollama/chat.py +257 -492
- agno/models/openai/__init__.py +7 -0
- agno/models/openai/chat.py +725 -770
- agno/models/openai/like.py +16 -2
- agno/models/openai/responses.py +1121 -0
- agno/models/openrouter/__init__.py +4 -0
- agno/models/openrouter/openrouter.py +62 -5
- agno/models/perplexity/__init__.py +5 -0
- agno/models/perplexity/perplexity.py +203 -0
- agno/models/portkey/__init__.py +3 -0
- agno/models/portkey/portkey.py +82 -0
- agno/models/requesty/__init__.py +5 -0
- agno/models/requesty/requesty.py +69 -0
- agno/models/response.py +177 -7
- agno/models/sambanova/__init__.py +4 -0
- agno/models/sambanova/sambanova.py +23 -4
- agno/models/siliconflow/__init__.py +5 -0
- agno/models/siliconflow/siliconflow.py +42 -0
- agno/models/together/__init__.py +4 -0
- agno/models/together/together.py +21 -164
- agno/models/utils.py +266 -0
- agno/models/vercel/__init__.py +3 -0
- agno/models/vercel/v0.py +43 -0
- agno/models/vertexai/__init__.py +0 -1
- agno/models/vertexai/claude.py +190 -0
- agno/models/vllm/__init__.py +3 -0
- agno/models/vllm/vllm.py +83 -0
- agno/models/xai/__init__.py +2 -0
- agno/models/xai/xai.py +111 -7
- agno/os/__init__.py +3 -0
- agno/os/app.py +1027 -0
- agno/os/auth.py +244 -0
- agno/os/config.py +126 -0
- agno/os/interfaces/__init__.py +1 -0
- agno/os/interfaces/a2a/__init__.py +3 -0
- agno/os/interfaces/a2a/a2a.py +42 -0
- agno/os/interfaces/a2a/router.py +249 -0
- agno/os/interfaces/a2a/utils.py +924 -0
- agno/os/interfaces/agui/__init__.py +3 -0
- agno/os/interfaces/agui/agui.py +47 -0
- agno/os/interfaces/agui/router.py +147 -0
- agno/os/interfaces/agui/utils.py +574 -0
- agno/os/interfaces/base.py +25 -0
- agno/os/interfaces/slack/__init__.py +3 -0
- agno/os/interfaces/slack/router.py +148 -0
- agno/os/interfaces/slack/security.py +30 -0
- agno/os/interfaces/slack/slack.py +47 -0
- agno/os/interfaces/whatsapp/__init__.py +3 -0
- agno/os/interfaces/whatsapp/router.py +210 -0
- agno/os/interfaces/whatsapp/security.py +55 -0
- agno/os/interfaces/whatsapp/whatsapp.py +36 -0
- agno/os/mcp.py +293 -0
- agno/os/middleware/__init__.py +9 -0
- agno/os/middleware/jwt.py +797 -0
- agno/os/router.py +258 -0
- agno/os/routers/__init__.py +3 -0
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +599 -0
- agno/os/routers/agents/schema.py +261 -0
- agno/os/routers/evals/__init__.py +3 -0
- agno/os/routers/evals/evals.py +450 -0
- agno/os/routers/evals/schemas.py +174 -0
- agno/os/routers/evals/utils.py +231 -0
- agno/os/routers/health.py +31 -0
- agno/os/routers/home.py +52 -0
- agno/os/routers/knowledge/__init__.py +3 -0
- agno/os/routers/knowledge/knowledge.py +1008 -0
- agno/os/routers/knowledge/schemas.py +178 -0
- agno/os/routers/memory/__init__.py +3 -0
- agno/os/routers/memory/memory.py +661 -0
- agno/os/routers/memory/schemas.py +88 -0
- agno/os/routers/metrics/__init__.py +3 -0
- agno/os/routers/metrics/metrics.py +190 -0
- agno/os/routers/metrics/schemas.py +47 -0
- agno/os/routers/session/__init__.py +3 -0
- agno/os/routers/session/session.py +997 -0
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +512 -0
- agno/os/routers/teams/schema.py +257 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +499 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +624 -0
- agno/os/routers/workflows/schema.py +75 -0
- agno/os/schema.py +534 -0
- agno/os/scopes.py +469 -0
- agno/{playground → os}/settings.py +7 -15
- agno/os/utils.py +973 -0
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/azure_ai_foundry.py +67 -0
- agno/reasoning/deepseek.py +63 -0
- agno/reasoning/default.py +97 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/groq.py +71 -0
- agno/reasoning/helpers.py +24 -1
- agno/reasoning/ollama.py +67 -0
- agno/reasoning/openai.py +86 -0
- agno/reasoning/step.py +2 -1
- agno/reasoning/vertexai.py +76 -0
- agno/run/__init__.py +6 -0
- agno/run/agent.py +822 -0
- agno/run/base.py +247 -0
- agno/run/cancel.py +81 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +767 -0
- agno/run/workflow.py +708 -0
- agno/session/__init__.py +10 -0
- agno/session/agent.py +260 -0
- agno/session/summary.py +265 -0
- agno/session/team.py +342 -0
- agno/session/workflow.py +501 -0
- agno/table.py +10 -0
- agno/team/__init__.py +37 -0
- agno/team/team.py +9536 -0
- agno/tools/__init__.py +7 -0
- agno/tools/agentql.py +120 -0
- agno/tools/airflow.py +22 -12
- agno/tools/api.py +122 -0
- agno/tools/apify.py +276 -83
- agno/tools/{arxiv_toolkit.py → arxiv.py} +20 -12
- agno/tools/aws_lambda.py +28 -7
- agno/tools/aws_ses.py +66 -0
- agno/tools/baidusearch.py +11 -4
- agno/tools/bitbucket.py +292 -0
- agno/tools/brandfetch.py +213 -0
- agno/tools/bravesearch.py +106 -0
- agno/tools/brightdata.py +367 -0
- agno/tools/browserbase.py +209 -0
- agno/tools/calcom.py +32 -23
- agno/tools/calculator.py +24 -37
- agno/tools/cartesia.py +187 -0
- agno/tools/{clickup_tool.py → clickup.py} +17 -28
- agno/tools/confluence.py +91 -26
- agno/tools/crawl4ai.py +139 -43
- agno/tools/csv_toolkit.py +28 -22
- agno/tools/dalle.py +36 -22
- agno/tools/daytona.py +475 -0
- agno/tools/decorator.py +169 -14
- agno/tools/desi_vocal.py +23 -11
- agno/tools/discord.py +32 -29
- agno/tools/docker.py +716 -0
- agno/tools/duckdb.py +76 -81
- agno/tools/duckduckgo.py +43 -40
- agno/tools/e2b.py +703 -0
- agno/tools/eleven_labs.py +65 -54
- agno/tools/email.py +13 -5
- agno/tools/evm.py +129 -0
- agno/tools/exa.py +324 -42
- agno/tools/fal.py +39 -35
- agno/tools/file.py +196 -30
- agno/tools/file_generation.py +356 -0
- agno/tools/financial_datasets.py +288 -0
- agno/tools/firecrawl.py +108 -33
- agno/tools/function.py +960 -122
- agno/tools/giphy.py +34 -12
- agno/tools/github.py +1294 -97
- agno/tools/gmail.py +922 -0
- agno/tools/google_bigquery.py +117 -0
- agno/tools/google_drive.py +271 -0
- agno/tools/google_maps.py +253 -0
- agno/tools/googlecalendar.py +607 -107
- agno/tools/googlesheets.py +377 -0
- agno/tools/hackernews.py +20 -12
- agno/tools/jina.py +24 -14
- agno/tools/jira.py +48 -19
- agno/tools/knowledge.py +218 -0
- agno/tools/linear.py +82 -43
- agno/tools/linkup.py +58 -0
- agno/tools/local_file_system.py +15 -7
- agno/tools/lumalab.py +41 -26
- agno/tools/mcp/__init__.py +10 -0
- agno/tools/mcp/mcp.py +331 -0
- agno/tools/mcp/multi_mcp.py +347 -0
- agno/tools/mcp/params.py +24 -0
- agno/tools/mcp_toolbox.py +284 -0
- agno/tools/mem0.py +193 -0
- agno/tools/memory.py +419 -0
- agno/tools/mlx_transcribe.py +11 -9
- agno/tools/models/azure_openai.py +190 -0
- agno/tools/models/gemini.py +203 -0
- agno/tools/models/groq.py +158 -0
- agno/tools/models/morph.py +186 -0
- agno/tools/models/nebius.py +124 -0
- agno/tools/models_labs.py +163 -82
- agno/tools/moviepy_video.py +18 -13
- agno/tools/nano_banana.py +151 -0
- agno/tools/neo4j.py +134 -0
- agno/tools/newspaper.py +15 -4
- agno/tools/newspaper4k.py +19 -6
- agno/tools/notion.py +204 -0
- agno/tools/openai.py +181 -17
- agno/tools/openbb.py +27 -20
- agno/tools/opencv.py +321 -0
- agno/tools/openweather.py +233 -0
- agno/tools/oxylabs.py +385 -0
- agno/tools/pandas.py +25 -15
- agno/tools/parallel.py +314 -0
- agno/tools/postgres.py +238 -185
- agno/tools/pubmed.py +125 -13
- agno/tools/python.py +48 -35
- agno/tools/reasoning.py +283 -0
- agno/tools/reddit.py +207 -29
- agno/tools/redshift.py +406 -0
- agno/tools/replicate.py +69 -26
- agno/tools/resend.py +11 -6
- agno/tools/scrapegraph.py +179 -19
- agno/tools/searxng.py +23 -31
- agno/tools/serpapi.py +15 -10
- agno/tools/serper.py +255 -0
- agno/tools/shell.py +23 -12
- agno/tools/shopify.py +1519 -0
- agno/tools/slack.py +56 -14
- agno/tools/sleep.py +8 -6
- agno/tools/spider.py +35 -11
- agno/tools/spotify.py +919 -0
- agno/tools/sql.py +34 -19
- agno/tools/tavily.py +158 -8
- agno/tools/telegram.py +18 -8
- agno/tools/todoist.py +218 -0
- agno/tools/toolkit.py +134 -9
- agno/tools/trafilatura.py +388 -0
- agno/tools/trello.py +25 -28
- agno/tools/twilio.py +18 -9
- agno/tools/user_control_flow.py +78 -0
- agno/tools/valyu.py +228 -0
- agno/tools/visualization.py +467 -0
- agno/tools/webbrowser.py +28 -0
- agno/tools/webex.py +76 -0
- agno/tools/website.py +23 -19
- agno/tools/webtools.py +45 -0
- agno/tools/whatsapp.py +286 -0
- agno/tools/wikipedia.py +28 -19
- agno/tools/workflow.py +285 -0
- agno/tools/{twitter.py → x.py} +142 -46
- agno/tools/yfinance.py +41 -39
- agno/tools/youtube.py +34 -17
- agno/tools/zendesk.py +15 -5
- agno/tools/zep.py +454 -0
- agno/tools/zoom.py +86 -37
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +157 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +111 -0
- agno/utils/agent.py +938 -0
- agno/utils/audio.py +37 -1
- agno/utils/certs.py +27 -0
- agno/utils/code_execution.py +11 -0
- agno/utils/common.py +103 -20
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +700 -0
- agno/utils/functions.py +107 -37
- agno/utils/gemini.py +426 -0
- agno/utils/hooks.py +171 -0
- agno/utils/http.py +185 -0
- agno/utils/json_schema.py +159 -37
- agno/utils/knowledge.py +36 -0
- agno/utils/location.py +19 -0
- agno/utils/log.py +221 -8
- agno/utils/mcp.py +214 -0
- agno/utils/media.py +335 -14
- agno/utils/merge_dict.py +22 -1
- agno/utils/message.py +77 -2
- agno/utils/models/ai_foundry.py +50 -0
- agno/utils/models/claude.py +373 -0
- agno/utils/models/cohere.py +94 -0
- agno/utils/models/llama.py +85 -0
- agno/utils/models/mistral.py +100 -0
- agno/utils/models/openai_responses.py +140 -0
- agno/utils/models/schema_utils.py +153 -0
- agno/utils/models/watsonx.py +41 -0
- agno/utils/openai.py +257 -0
- agno/utils/pickle.py +1 -1
- agno/utils/pprint.py +124 -8
- agno/utils/print_response/agent.py +930 -0
- agno/utils/print_response/team.py +1914 -0
- agno/utils/print_response/workflow.py +1668 -0
- agno/utils/prompts.py +111 -0
- agno/utils/reasoning.py +108 -0
- agno/utils/response.py +163 -0
- agno/utils/serialize.py +32 -0
- agno/utils/shell.py +4 -4
- agno/utils/streamlit.py +487 -0
- agno/utils/string.py +204 -51
- agno/utils/team.py +139 -0
- agno/utils/timer.py +9 -2
- agno/utils/tokens.py +657 -0
- agno/utils/tools.py +19 -1
- agno/utils/whatsapp.py +305 -0
- agno/utils/yaml_io.py +3 -3
- agno/vectordb/__init__.py +2 -0
- agno/vectordb/base.py +87 -9
- agno/vectordb/cassandra/__init__.py +5 -1
- agno/vectordb/cassandra/cassandra.py +383 -27
- agno/vectordb/chroma/__init__.py +4 -0
- agno/vectordb/chroma/chromadb.py +748 -83
- agno/vectordb/clickhouse/__init__.py +7 -1
- agno/vectordb/clickhouse/clickhousedb.py +554 -53
- agno/vectordb/couchbase/__init__.py +3 -0
- agno/vectordb/couchbase/couchbase.py +1446 -0
- agno/vectordb/lancedb/__init__.py +5 -0
- agno/vectordb/lancedb/lance_db.py +730 -98
- agno/vectordb/langchaindb/__init__.py +5 -0
- agno/vectordb/langchaindb/langchaindb.py +163 -0
- agno/vectordb/lightrag/__init__.py +5 -0
- agno/vectordb/lightrag/lightrag.py +388 -0
- agno/vectordb/llamaindex/__init__.py +3 -0
- agno/vectordb/llamaindex/llamaindexdb.py +166 -0
- agno/vectordb/milvus/__init__.py +3 -0
- agno/vectordb/milvus/milvus.py +966 -78
- agno/vectordb/mongodb/__init__.py +9 -1
- agno/vectordb/mongodb/mongodb.py +1175 -172
- agno/vectordb/pgvector/__init__.py +8 -0
- agno/vectordb/pgvector/pgvector.py +599 -115
- agno/vectordb/pineconedb/__init__.py +5 -1
- agno/vectordb/pineconedb/pineconedb.py +406 -43
- agno/vectordb/qdrant/__init__.py +4 -0
- agno/vectordb/qdrant/qdrant.py +914 -61
- agno/vectordb/redis/__init__.py +9 -0
- agno/vectordb/redis/redisdb.py +682 -0
- agno/vectordb/singlestore/__init__.py +8 -1
- agno/vectordb/singlestore/singlestore.py +771 -0
- agno/vectordb/surrealdb/__init__.py +3 -0
- agno/vectordb/surrealdb/surrealdb.py +663 -0
- agno/vectordb/upstashdb/__init__.py +5 -0
- agno/vectordb/upstashdb/upstashdb.py +718 -0
- agno/vectordb/weaviate/__init__.py +8 -0
- agno/vectordb/weaviate/index.py +15 -0
- agno/vectordb/weaviate/weaviate.py +1009 -0
- agno/workflow/__init__.py +23 -1
- agno/workflow/agent.py +299 -0
- agno/workflow/condition.py +759 -0
- agno/workflow/loop.py +756 -0
- agno/workflow/parallel.py +853 -0
- agno/workflow/router.py +723 -0
- agno/workflow/step.py +1564 -0
- agno/workflow/steps.py +613 -0
- agno/workflow/types.py +556 -0
- agno/workflow/workflow.py +4327 -514
- agno-2.3.13.dist-info/METADATA +639 -0
- agno-2.3.13.dist-info/RECORD +613 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +1 -1
- agno-2.3.13.dist-info/licenses/LICENSE +201 -0
- agno/api/playground.py +0 -91
- agno/api/schemas/playground.py +0 -22
- agno/api/schemas/user.py +0 -22
- agno/api/schemas/workspace.py +0 -46
- agno/api/user.py +0 -160
- agno/api/workspace.py +0 -151
- agno/cli/auth_server.py +0 -118
- agno/cli/config.py +0 -275
- agno/cli/console.py +0 -88
- agno/cli/credentials.py +0 -23
- agno/cli/entrypoint.py +0 -571
- agno/cli/operator.py +0 -355
- agno/cli/settings.py +0 -85
- agno/cli/ws/ws_cli.py +0 -817
- agno/constants.py +0 -13
- agno/document/__init__.py +0 -1
- agno/document/chunking/semantic.py +0 -47
- agno/document/chunking/strategy.py +0 -31
- agno/document/reader/__init__.py +0 -1
- agno/document/reader/arxiv_reader.py +0 -41
- agno/document/reader/base.py +0 -22
- agno/document/reader/csv_reader.py +0 -84
- agno/document/reader/docx_reader.py +0 -46
- agno/document/reader/firecrawl_reader.py +0 -99
- agno/document/reader/json_reader.py +0 -43
- agno/document/reader/pdf_reader.py +0 -219
- agno/document/reader/s3/pdf_reader.py +0 -46
- agno/document/reader/s3/text_reader.py +0 -51
- agno/document/reader/text_reader.py +0 -41
- agno/document/reader/website_reader.py +0 -175
- agno/document/reader/youtube_reader.py +0 -50
- agno/embedder/__init__.py +0 -1
- agno/embedder/azure_openai.py +0 -86
- agno/embedder/cohere.py +0 -72
- agno/embedder/fastembed.py +0 -37
- agno/embedder/google.py +0 -73
- agno/embedder/huggingface.py +0 -54
- agno/embedder/mistral.py +0 -80
- agno/embedder/ollama.py +0 -57
- agno/embedder/openai.py +0 -74
- agno/embedder/sentence_transformer.py +0 -38
- agno/embedder/voyageai.py +0 -64
- agno/eval/perf.py +0 -201
- agno/file/__init__.py +0 -1
- 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 -230
- agno/knowledge/arxiv.py +0 -22
- agno/knowledge/combined.py +0 -22
- agno/knowledge/csv.py +0 -28
- agno/knowledge/csv_url.py +0 -19
- agno/knowledge/document.py +0 -20
- agno/knowledge/docx.py +0 -30
- agno/knowledge/json.py +0 -28
- agno/knowledge/langchain.py +0 -71
- agno/knowledge/llamaindex.py +0 -66
- agno/knowledge/pdf.py +0 -28
- agno/knowledge/pdf_url.py +0 -26
- agno/knowledge/s3/base.py +0 -60
- agno/knowledge/s3/pdf.py +0 -21
- agno/knowledge/s3/text.py +0 -23
- agno/knowledge/text.py +0 -30
- agno/knowledge/website.py +0 -88
- agno/knowledge/wikipedia.py +0 -31
- agno/knowledge/youtube.py +0 -22
- agno/memory/agent.py +0 -392
- agno/memory/classifier.py +0 -104
- agno/memory/db/__init__.py +0 -1
- 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 -15
- agno/memory/row.py +0 -36
- agno/memory/summarizer.py +0 -192
- agno/memory/summary.py +0 -19
- agno/memory/workflow.py +0 -38
- agno/models/google/gemini_openai.py +0 -26
- agno/models/ollama/hermes.py +0 -221
- agno/models/ollama/tools.py +0 -362
- agno/models/vertexai/gemini.py +0 -595
- agno/playground/__init__.py +0 -3
- agno/playground/async_router.py +0 -421
- agno/playground/deploy.py +0 -249
- agno/playground/operator.py +0 -92
- agno/playground/playground.py +0 -91
- agno/playground/schemas.py +0 -76
- agno/playground/serve.py +0 -55
- agno/playground/sync_router.py +0 -405
- agno/reasoning/agent.py +0 -68
- agno/run/response.py +0 -112
- agno/storage/agent/__init__.py +0 -0
- agno/storage/agent/base.py +0 -38
- agno/storage/agent/dynamodb.py +0 -350
- agno/storage/agent/json.py +0 -92
- agno/storage/agent/mongodb.py +0 -228
- agno/storage/agent/postgres.py +0 -367
- agno/storage/agent/session.py +0 -79
- agno/storage/agent/singlestore.py +0 -303
- agno/storage/agent/sqlite.py +0 -357
- agno/storage/agent/yaml.py +0 -93
- agno/storage/workflow/__init__.py +0 -0
- agno/storage/workflow/base.py +0 -40
- agno/storage/workflow/mongodb.py +0 -233
- agno/storage/workflow/postgres.py +0 -366
- agno/storage/workflow/session.py +0 -60
- agno/storage/workflow/sqlite.py +0 -359
- agno/tools/googlesearch.py +0 -88
- 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/vectordb/singlestore/s2vectordb.py +0 -390
- agno/vectordb/singlestore/s2vectordb2.py +0 -355
- agno/workspace/__init__.py +0 -0
- agno/workspace/config.py +0 -325
- agno/workspace/enums.py +0 -6
- agno/workspace/helpers.py +0 -48
- agno/workspace/operator.py +0 -758
- agno/workspace/settings.py +0 -63
- agno-0.1.2.dist-info/LICENSE +0 -375
- agno-0.1.2.dist-info/METADATA +0 -502
- agno-0.1.2.dist-info/RECORD +0 -352
- agno-0.1.2.dist-info/entry_points.txt +0 -3
- /agno/{cli → db/migrations}/__init__.py +0 -0
- /agno/{cli/ws → db/migrations/versions}/__init__.py +0 -0
- /agno/{document/chunking/__init__.py → db/schemas/metrics.py} +0 -0
- /agno/{document/reader/s3 → integrations}/__init__.py +0 -0
- /agno/{file/local → knowledge/chunking}/__init__.py +0 -0
- /agno/{infra → knowledge/remote_content}/__init__.py +0 -0
- /agno/{knowledge/s3 → tools/models}/__init__.py +0 -0
- /agno/{reranker → utils/models}/__init__.py +0 -0
- /agno/{storage → utils/print_response}/__init__.py +0 -0
- {agno-0.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
agno/os/auth.py
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
from typing import List, Set
|
|
2
|
+
|
|
3
|
+
from fastapi import Depends, HTTPException, Request
|
|
4
|
+
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
5
|
+
|
|
6
|
+
from agno.os.scopes import get_accessible_resource_ids
|
|
7
|
+
from agno.os.settings import AgnoAPISettings
|
|
8
|
+
|
|
9
|
+
# Create a global HTTPBearer instance
|
|
10
|
+
security = HTTPBearer(auto_error=False)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_authentication_dependency(settings: AgnoAPISettings):
|
|
14
|
+
"""
|
|
15
|
+
Create an authentication dependency function for FastAPI routes.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
settings: The API settings containing the security key
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
A dependency function that can be used with FastAPI's Depends()
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
async def auth_dependency(credentials: HTTPAuthorizationCredentials = Depends(security)) -> bool:
|
|
25
|
+
# If no security key is set, skip authentication entirely
|
|
26
|
+
if not settings or not settings.os_security_key:
|
|
27
|
+
return True
|
|
28
|
+
|
|
29
|
+
# If security is enabled but no authorization header provided, fail
|
|
30
|
+
if not credentials:
|
|
31
|
+
raise HTTPException(status_code=401, detail="Authorization header required")
|
|
32
|
+
|
|
33
|
+
token = credentials.credentials
|
|
34
|
+
|
|
35
|
+
# Verify the token
|
|
36
|
+
if token != settings.os_security_key:
|
|
37
|
+
raise HTTPException(status_code=401, detail="Invalid authentication token")
|
|
38
|
+
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
return auth_dependency
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def validate_websocket_token(token: str, settings: AgnoAPISettings) -> bool:
|
|
45
|
+
"""
|
|
46
|
+
Validate a bearer token for WebSocket authentication (legacy os_security_key method).
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
token: The bearer token to validate
|
|
50
|
+
settings: The API settings containing the security key
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
True if the token is valid or authentication is disabled, False otherwise
|
|
54
|
+
"""
|
|
55
|
+
# If no security key is set, skip authentication entirely
|
|
56
|
+
if not settings or not settings.os_security_key:
|
|
57
|
+
return True
|
|
58
|
+
|
|
59
|
+
# Verify the token matches the configured security key
|
|
60
|
+
return token == settings.os_security_key
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def get_accessible_resources(request: Request, resource_type: str) -> Set[str]:
|
|
64
|
+
"""
|
|
65
|
+
Get the set of resource IDs the user has access to based on their scopes.
|
|
66
|
+
|
|
67
|
+
This function is used to filter lists of resources (agents, teams, workflows)
|
|
68
|
+
based on the user's scopes from their JWT token.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
request: The FastAPI request object (contains request.state.scopes)
|
|
72
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Set of resource IDs the user can access. Returns {"*"} for wildcard access.
|
|
76
|
+
|
|
77
|
+
Usage:
|
|
78
|
+
accessible_ids = get_accessible_resources(request, "agents")
|
|
79
|
+
if "*" not in accessible_ids:
|
|
80
|
+
agents = [a for a in agents if a.id in accessible_ids]
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
>>> # User with specific agent access
|
|
84
|
+
>>> # Token scopes: ["agent-os:my-os:agents:my-agent:read"]
|
|
85
|
+
>>> get_accessible_resources(request, "agents")
|
|
86
|
+
{'my-agent'}
|
|
87
|
+
|
|
88
|
+
>>> # User with wildcard access
|
|
89
|
+
>>> # Token scopes: ["agent-os:my-os:agents:*:read"] or ["admin"]
|
|
90
|
+
>>> get_accessible_resources(request, "agents")
|
|
91
|
+
{'*'}
|
|
92
|
+
|
|
93
|
+
>>> # User with agent-os level access (global resource scope)
|
|
94
|
+
>>> # Token scopes: ["agent-os:my-os:agents:read"]
|
|
95
|
+
>>> get_accessible_resources(request, "agents")
|
|
96
|
+
{'*'}
|
|
97
|
+
"""
|
|
98
|
+
# Check if accessible_resource_ids is already cached in request state (set by JWT middleware)
|
|
99
|
+
# This happens when user doesn't have global scope but has specific resource scopes
|
|
100
|
+
cached_ids = getattr(request.state, "accessible_resource_ids", None)
|
|
101
|
+
if cached_ids is not None:
|
|
102
|
+
return cached_ids
|
|
103
|
+
|
|
104
|
+
# Get user's scopes from request state (set by JWT middleware)
|
|
105
|
+
user_scopes = getattr(request.state, "scopes", [])
|
|
106
|
+
|
|
107
|
+
# Get accessible resource IDs
|
|
108
|
+
accessible_ids = get_accessible_resource_ids(user_scopes=user_scopes, resource_type=resource_type)
|
|
109
|
+
|
|
110
|
+
return accessible_ids
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def filter_resources_by_access(request: Request, resources: List, resource_type: str) -> List:
|
|
114
|
+
"""
|
|
115
|
+
Filter a list of resources based on user's access permissions.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
request: The FastAPI request object
|
|
119
|
+
resources: List of resource objects (agents, teams, or workflows) with 'id' attribute
|
|
120
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Filtered list of resources the user has access to
|
|
124
|
+
|
|
125
|
+
Usage:
|
|
126
|
+
agents = filter_resources_by_access(request, all_agents, "agents")
|
|
127
|
+
teams = filter_resources_by_access(request, all_teams, "teams")
|
|
128
|
+
workflows = filter_resources_by_access(request, all_workflows, "workflows")
|
|
129
|
+
|
|
130
|
+
Examples:
|
|
131
|
+
>>> # User with specific access
|
|
132
|
+
>>> agents = [Agent(id="agent-1"), Agent(id="agent-2"), Agent(id="agent-3")]
|
|
133
|
+
>>> # Token scopes: ["agent-os:my-os:agents:agent-1:read", "agent-os:my-os:agents:agent-2:read"]
|
|
134
|
+
>>> filter_resources_by_access(request, agents, "agents")
|
|
135
|
+
[Agent(id="agent-1"), Agent(id="agent-2")]
|
|
136
|
+
|
|
137
|
+
>>> # User with wildcard access
|
|
138
|
+
>>> # Token scopes: ["admin"]
|
|
139
|
+
>>> filter_resources_by_access(request, agents, "agents")
|
|
140
|
+
[Agent(id="agent-1"), Agent(id="agent-2"), Agent(id="agent-3")]
|
|
141
|
+
"""
|
|
142
|
+
accessible_ids = get_accessible_resources(request, resource_type)
|
|
143
|
+
|
|
144
|
+
# Wildcard access - return all resources
|
|
145
|
+
if "*" in accessible_ids:
|
|
146
|
+
return resources
|
|
147
|
+
|
|
148
|
+
# Filter to only accessible resources
|
|
149
|
+
return [r for r in resources if r.id in accessible_ids]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def check_resource_access(request: Request, resource_id: str, resource_type: str, action: str = "read") -> bool:
|
|
153
|
+
"""
|
|
154
|
+
Check if user has access to a specific resource.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
request: The FastAPI request object
|
|
158
|
+
resource_id: ID of the resource to check
|
|
159
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
160
|
+
action: Action to check ("read", "run", etc.)
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
True if user has access, False otherwise
|
|
164
|
+
|
|
165
|
+
Usage:
|
|
166
|
+
if not check_resource_access(request, agent_id, "agents", "run"):
|
|
167
|
+
raise HTTPException(status_code=403, detail="Access denied")
|
|
168
|
+
|
|
169
|
+
Examples:
|
|
170
|
+
>>> # Token scopes: ["agent-os:my-os:agents:my-agent:read", "agent-os:my-os:agents:my-agent:run"]
|
|
171
|
+
>>> check_resource_access(request, "my-agent", "agents", "run")
|
|
172
|
+
True
|
|
173
|
+
|
|
174
|
+
>>> check_resource_access(request, "other-agent", "agents", "run")
|
|
175
|
+
False
|
|
176
|
+
"""
|
|
177
|
+
accessible_ids = get_accessible_resources(request, resource_type)
|
|
178
|
+
|
|
179
|
+
# Wildcard access grants all permissions
|
|
180
|
+
if "*" in accessible_ids:
|
|
181
|
+
return True
|
|
182
|
+
|
|
183
|
+
# Check if user has access to this specific resource
|
|
184
|
+
return resource_id in accessible_ids
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def require_resource_access(resource_type: str, action: str, resource_id_param: str):
|
|
188
|
+
"""
|
|
189
|
+
Create a dependency that checks if the user has access to a specific resource.
|
|
190
|
+
|
|
191
|
+
This dependency factory creates a FastAPI dependency that automatically checks
|
|
192
|
+
authorization when authorization is enabled. It extracts the resource ID from
|
|
193
|
+
the path parameters and verifies the user has the required access.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
197
|
+
action: Action to check ("read", "run")
|
|
198
|
+
resource_id_param: Name of the path parameter containing the resource ID
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
A dependency function for use with FastAPI's Depends()
|
|
202
|
+
|
|
203
|
+
Usage:
|
|
204
|
+
@router.post("/agents/{agent_id}/runs")
|
|
205
|
+
async def create_agent_run(
|
|
206
|
+
agent_id: str,
|
|
207
|
+
request: Request,
|
|
208
|
+
_: None = Depends(require_resource_access("agents", "run", "agent_id")),
|
|
209
|
+
):
|
|
210
|
+
...
|
|
211
|
+
|
|
212
|
+
@router.get("/agents/{agent_id}")
|
|
213
|
+
async def get_agent(
|
|
214
|
+
agent_id: str,
|
|
215
|
+
request: Request,
|
|
216
|
+
_: None = Depends(require_resource_access("agents", "read", "agent_id")),
|
|
217
|
+
):
|
|
218
|
+
...
|
|
219
|
+
|
|
220
|
+
Examples:
|
|
221
|
+
>>> # Creates dependency for checking agent run access
|
|
222
|
+
>>> dep = require_resource_access("agents", "run", "agent_id")
|
|
223
|
+
|
|
224
|
+
>>> # Creates dependency for checking team read access
|
|
225
|
+
>>> dep = require_resource_access("teams", "read", "team_id")
|
|
226
|
+
"""
|
|
227
|
+
# Map resource_type to singular form for error messages
|
|
228
|
+
resource_singular = {
|
|
229
|
+
"agents": "agent",
|
|
230
|
+
"teams": "team",
|
|
231
|
+
"workflows": "workflow",
|
|
232
|
+
}.get(resource_type, resource_type.rstrip("s"))
|
|
233
|
+
|
|
234
|
+
async def dependency(request: Request):
|
|
235
|
+
# Only check authorization if it's enabled
|
|
236
|
+
if not getattr(request.state, "authorization_enabled", False):
|
|
237
|
+
return
|
|
238
|
+
|
|
239
|
+
# Get the resource_id from path parameters
|
|
240
|
+
resource_id = request.path_params.get(resource_id_param)
|
|
241
|
+
if resource_id and not check_resource_access(request, resource_id, resource_type, action):
|
|
242
|
+
raise HTTPException(status_code=403, detail=f"Access denied to {action} this {resource_singular}")
|
|
243
|
+
|
|
244
|
+
return dependency
|
agno/os/config.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""Schemas related to the AgentOS configuration"""
|
|
2
|
+
|
|
3
|
+
from typing import Generic, List, Optional, TypeVar
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, field_validator
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AuthorizationConfig(BaseModel):
|
|
9
|
+
"""Configuration for the JWT middleware"""
|
|
10
|
+
|
|
11
|
+
verification_keys: Optional[List[str]] = None
|
|
12
|
+
jwks_file: Optional[str] = None
|
|
13
|
+
algorithm: Optional[str] = None
|
|
14
|
+
verify_audience: Optional[bool] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class EvalsDomainConfig(BaseModel):
|
|
18
|
+
"""Configuration for the Evals domain of the AgentOS"""
|
|
19
|
+
|
|
20
|
+
display_name: Optional[str] = None
|
|
21
|
+
available_models: Optional[List[str]] = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SessionDomainConfig(BaseModel):
|
|
25
|
+
"""Configuration for the Session domain of the AgentOS"""
|
|
26
|
+
|
|
27
|
+
display_name: Optional[str] = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class KnowledgeDomainConfig(BaseModel):
|
|
31
|
+
"""Configuration for the Knowledge domain of the AgentOS"""
|
|
32
|
+
|
|
33
|
+
display_name: Optional[str] = None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MetricsDomainConfig(BaseModel):
|
|
37
|
+
"""Configuration for the Metrics domain of the AgentOS"""
|
|
38
|
+
|
|
39
|
+
display_name: Optional[str] = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class MemoryDomainConfig(BaseModel):
|
|
43
|
+
"""Configuration for the Memory domain of the AgentOS"""
|
|
44
|
+
|
|
45
|
+
display_name: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TracesDomainConfig(BaseModel):
|
|
49
|
+
"""Configuration for the Traces domain of the AgentOS"""
|
|
50
|
+
|
|
51
|
+
display_name: Optional[str] = None
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
DomainConfigType = TypeVar("DomainConfigType")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class DatabaseConfig(BaseModel, Generic[DomainConfigType]):
|
|
58
|
+
"""Configuration for a domain when used with the contextual database"""
|
|
59
|
+
|
|
60
|
+
db_id: str
|
|
61
|
+
domain_config: Optional[DomainConfigType] = None
|
|
62
|
+
tables: Optional[List[str]] = None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class EvalsConfig(EvalsDomainConfig):
|
|
66
|
+
"""Configuration for the Evals domain of the AgentOS"""
|
|
67
|
+
|
|
68
|
+
dbs: Optional[List[DatabaseConfig[EvalsDomainConfig]]] = None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class SessionConfig(SessionDomainConfig):
|
|
72
|
+
"""Configuration for the Session domain of the AgentOS"""
|
|
73
|
+
|
|
74
|
+
dbs: Optional[List[DatabaseConfig[SessionDomainConfig]]] = None
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class MemoryConfig(MemoryDomainConfig):
|
|
78
|
+
"""Configuration for the Memory domain of the AgentOS"""
|
|
79
|
+
|
|
80
|
+
dbs: Optional[List[DatabaseConfig[MemoryDomainConfig]]] = None
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class KnowledgeConfig(KnowledgeDomainConfig):
|
|
84
|
+
"""Configuration for the Knowledge domain of the AgentOS"""
|
|
85
|
+
|
|
86
|
+
dbs: Optional[List[DatabaseConfig[KnowledgeDomainConfig]]] = None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class MetricsConfig(MetricsDomainConfig):
|
|
90
|
+
"""Configuration for the Metrics domain of the AgentOS"""
|
|
91
|
+
|
|
92
|
+
dbs: Optional[List[DatabaseConfig[MetricsDomainConfig]]] = None
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TracesConfig(TracesDomainConfig):
|
|
96
|
+
"""Configuration for the Traces domain of the AgentOS"""
|
|
97
|
+
|
|
98
|
+
dbs: Optional[List[DatabaseConfig[TracesDomainConfig]]] = None
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ChatConfig(BaseModel):
|
|
102
|
+
"""Configuration for the Chat page of the AgentOS"""
|
|
103
|
+
|
|
104
|
+
quick_prompts: dict[str, list[str]]
|
|
105
|
+
|
|
106
|
+
# Limit the number of quick prompts to 3 (per agent/team/workflow)
|
|
107
|
+
@field_validator("quick_prompts")
|
|
108
|
+
@classmethod
|
|
109
|
+
def limit_lists(cls, v):
|
|
110
|
+
for key, lst in v.items():
|
|
111
|
+
if len(lst) > 3:
|
|
112
|
+
raise ValueError(f"Too many quick prompts for '{key}', maximum allowed is 3")
|
|
113
|
+
return v
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class AgentOSConfig(BaseModel):
|
|
117
|
+
"""General configuration for an AgentOS instance"""
|
|
118
|
+
|
|
119
|
+
available_models: Optional[List[str]] = None
|
|
120
|
+
chat: Optional[ChatConfig] = None
|
|
121
|
+
evals: Optional[EvalsConfig] = None
|
|
122
|
+
knowledge: Optional[KnowledgeConfig] = None
|
|
123
|
+
memory: Optional[MemoryConfig] = None
|
|
124
|
+
session: Optional[SessionConfig] = None
|
|
125
|
+
metrics: Optional[MetricsConfig] = None
|
|
126
|
+
traces: Optional[TracesConfig] = None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Main class for the A2A app, used to expose an Agno Agent, Team, or Workflow in an A2A compatible format."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from fastapi.routing import APIRouter
|
|
6
|
+
from typing_extensions import List
|
|
7
|
+
|
|
8
|
+
from agno.agent import Agent
|
|
9
|
+
from agno.os.interfaces.a2a.router import attach_routes
|
|
10
|
+
from agno.os.interfaces.base import BaseInterface
|
|
11
|
+
from agno.team import Team
|
|
12
|
+
from agno.workflow import Workflow
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class A2A(BaseInterface):
|
|
16
|
+
type = "a2a"
|
|
17
|
+
|
|
18
|
+
router: APIRouter
|
|
19
|
+
|
|
20
|
+
def __init__(
|
|
21
|
+
self,
|
|
22
|
+
agents: Optional[List[Agent]] = None,
|
|
23
|
+
teams: Optional[List[Team]] = None,
|
|
24
|
+
workflows: Optional[List[Workflow]] = None,
|
|
25
|
+
prefix: str = "/a2a",
|
|
26
|
+
tags: Optional[List[str]] = None,
|
|
27
|
+
):
|
|
28
|
+
self.agents = agents
|
|
29
|
+
self.teams = teams
|
|
30
|
+
self.workflows = workflows
|
|
31
|
+
self.prefix = prefix
|
|
32
|
+
self.tags = tags or ["A2A"]
|
|
33
|
+
|
|
34
|
+
if not (self.agents or self.teams or self.workflows):
|
|
35
|
+
raise ValueError("Agents, Teams, or Workflows are required to setup the A2A interface.")
|
|
36
|
+
|
|
37
|
+
def get_router(self, **kwargs) -> APIRouter:
|
|
38
|
+
self.router = APIRouter(prefix=self.prefix, tags=self.tags) # type: ignore
|
|
39
|
+
|
|
40
|
+
self.router = attach_routes(router=self.router, agents=self.agents, teams=self.teams, workflows=self.workflows)
|
|
41
|
+
|
|
42
|
+
return self.router
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Async router handling exposing an Agno Agent or Team in an A2A compatible format."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Union
|
|
4
|
+
from uuid import uuid4
|
|
5
|
+
|
|
6
|
+
from fastapi import HTTPException, Request
|
|
7
|
+
from fastapi.responses import StreamingResponse
|
|
8
|
+
from fastapi.routing import APIRouter
|
|
9
|
+
from typing_extensions import List
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from a2a.types import SendMessageSuccessResponse, Task, TaskState, TaskStatus
|
|
13
|
+
except ImportError as e:
|
|
14
|
+
raise ImportError("`a2a` not installed. Please install it with `pip install -U a2a-sdk`") from e
|
|
15
|
+
|
|
16
|
+
from agno.agent import Agent
|
|
17
|
+
from agno.os.interfaces.a2a.utils import (
|
|
18
|
+
map_a2a_request_to_run_input,
|
|
19
|
+
map_run_output_to_a2a_task,
|
|
20
|
+
stream_a2a_response_with_error_handling,
|
|
21
|
+
)
|
|
22
|
+
from agno.os.utils import get_agent_by_id, get_request_kwargs, get_team_by_id, get_workflow_by_id
|
|
23
|
+
from agno.team import Team
|
|
24
|
+
from agno.workflow import Workflow
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def attach_routes(
|
|
28
|
+
router: APIRouter,
|
|
29
|
+
agents: Optional[List[Agent]] = None,
|
|
30
|
+
teams: Optional[List[Team]] = None,
|
|
31
|
+
workflows: Optional[List[Workflow]] = None,
|
|
32
|
+
) -> APIRouter:
|
|
33
|
+
if agents is None and teams is None and workflows is None:
|
|
34
|
+
raise ValueError("Agents, Teams, or Workflows are required to setup the A2A interface.")
|
|
35
|
+
|
|
36
|
+
@router.post(
|
|
37
|
+
"/message/send",
|
|
38
|
+
operation_id="send_message",
|
|
39
|
+
name="send_message",
|
|
40
|
+
description="Send a message to an Agno Agent, Team, or Workflow. "
|
|
41
|
+
"The Agent, Team or Workflow is identified via the 'agentId' field in params.message or X-Agent-ID header. "
|
|
42
|
+
"Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata.",
|
|
43
|
+
response_model_exclude_none=True,
|
|
44
|
+
responses={
|
|
45
|
+
200: {
|
|
46
|
+
"description": "Message sent successfully",
|
|
47
|
+
"content": {
|
|
48
|
+
"application/json": {
|
|
49
|
+
"example": {
|
|
50
|
+
"jsonrpc": "2.0",
|
|
51
|
+
"id": "request-123",
|
|
52
|
+
"result": {
|
|
53
|
+
"task": {
|
|
54
|
+
"id": "task-456",
|
|
55
|
+
"context_id": "context-789",
|
|
56
|
+
"status": "completed",
|
|
57
|
+
"history": [
|
|
58
|
+
{
|
|
59
|
+
"message_id": "msg-1",
|
|
60
|
+
"role": "agent",
|
|
61
|
+
"parts": [{"kind": "text", "text": "Response from agent"}],
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
400: {"description": "Invalid request or unsupported method"},
|
|
71
|
+
404: {"description": "Agent, Team, or Workflow not found"},
|
|
72
|
+
},
|
|
73
|
+
response_model=SendMessageSuccessResponse,
|
|
74
|
+
)
|
|
75
|
+
async def a2a_send_message(request: Request):
|
|
76
|
+
request_body = await request.json()
|
|
77
|
+
kwargs = await get_request_kwargs(request, a2a_send_message)
|
|
78
|
+
|
|
79
|
+
# 1. Get the Agent, Team, or Workflow to run
|
|
80
|
+
agent_id = request_body.get("params", {}).get("message", {}).get("agentId") or request.headers.get("X-Agent-ID")
|
|
81
|
+
if not agent_id:
|
|
82
|
+
raise HTTPException(
|
|
83
|
+
status_code=400,
|
|
84
|
+
detail="Entity ID required. Provide it via 'agentId' in params.message or 'X-Agent-ID' header.",
|
|
85
|
+
)
|
|
86
|
+
entity: Optional[Union[Agent, Team, Workflow]] = None
|
|
87
|
+
if agents:
|
|
88
|
+
entity = get_agent_by_id(agent_id, agents)
|
|
89
|
+
if not entity and teams:
|
|
90
|
+
entity = get_team_by_id(agent_id, teams)
|
|
91
|
+
if not entity and workflows:
|
|
92
|
+
entity = get_workflow_by_id(agent_id, workflows)
|
|
93
|
+
if entity is None:
|
|
94
|
+
raise HTTPException(status_code=404, detail=f"Agent, Team, or Workflow with ID '{agent_id}' not found")
|
|
95
|
+
|
|
96
|
+
# 2. Map the request to our run_input and run variables
|
|
97
|
+
run_input = await map_a2a_request_to_run_input(request_body, stream=False)
|
|
98
|
+
context_id = request_body.get("params", {}).get("message", {}).get("contextId")
|
|
99
|
+
user_id = request.headers.get("X-User-ID")
|
|
100
|
+
if not user_id:
|
|
101
|
+
user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
|
|
102
|
+
|
|
103
|
+
# 3. Run the agent, team, or workflow
|
|
104
|
+
try:
|
|
105
|
+
if isinstance(entity, Workflow):
|
|
106
|
+
response = await entity.arun(
|
|
107
|
+
input=run_input.input_content,
|
|
108
|
+
images=list(run_input.images) if run_input.images else None,
|
|
109
|
+
videos=list(run_input.videos) if run_input.videos else None,
|
|
110
|
+
audio=list(run_input.audios) if run_input.audios else None,
|
|
111
|
+
files=list(run_input.files) if run_input.files else None,
|
|
112
|
+
session_id=context_id,
|
|
113
|
+
user_id=user_id,
|
|
114
|
+
**kwargs,
|
|
115
|
+
)
|
|
116
|
+
else:
|
|
117
|
+
response = await entity.arun(
|
|
118
|
+
input=run_input.input_content,
|
|
119
|
+
images=run_input.images,
|
|
120
|
+
videos=run_input.videos,
|
|
121
|
+
audio=run_input.audios,
|
|
122
|
+
files=run_input.files,
|
|
123
|
+
session_id=context_id,
|
|
124
|
+
user_id=user_id,
|
|
125
|
+
**kwargs,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# 4. Send the response
|
|
129
|
+
a2a_task = map_run_output_to_a2a_task(response)
|
|
130
|
+
return SendMessageSuccessResponse(
|
|
131
|
+
id=request_body.get("id", "unknown"),
|
|
132
|
+
result=a2a_task,
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
# Handle all critical errors
|
|
136
|
+
except Exception as e:
|
|
137
|
+
from a2a.types import Message as A2AMessage
|
|
138
|
+
from a2a.types import Part, Role, TextPart
|
|
139
|
+
|
|
140
|
+
error_message = A2AMessage(
|
|
141
|
+
message_id=str(uuid4()),
|
|
142
|
+
role=Role.agent,
|
|
143
|
+
parts=[Part(root=TextPart(text=f"Error: {str(e)}"))],
|
|
144
|
+
context_id=context_id or str(uuid4()),
|
|
145
|
+
)
|
|
146
|
+
failed_task = Task(
|
|
147
|
+
id=str(uuid4()),
|
|
148
|
+
context_id=context_id or str(uuid4()),
|
|
149
|
+
status=TaskStatus(state=TaskState.failed),
|
|
150
|
+
history=[error_message],
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
return SendMessageSuccessResponse(
|
|
154
|
+
id=request_body.get("id", "unknown"),
|
|
155
|
+
result=failed_task,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
@router.post(
|
|
159
|
+
"/message/stream",
|
|
160
|
+
operation_id="stream_message",
|
|
161
|
+
name="stream_message",
|
|
162
|
+
description="Stream a message to an Agno Agent, Team, or Workflow."
|
|
163
|
+
"The Agent, Team or Workflow is identified via the 'agentId' field in params.message or X-Agent-ID header. "
|
|
164
|
+
"Optional: Pass user ID via X-User-ID header (recommended) or 'userId' in params.message.metadata. "
|
|
165
|
+
"Returns real-time updates as newline-delimited JSON (NDJSON).",
|
|
166
|
+
response_model_exclude_none=True,
|
|
167
|
+
responses={
|
|
168
|
+
200: {
|
|
169
|
+
"description": "Streaming response with task updates",
|
|
170
|
+
"content": {
|
|
171
|
+
"application/x-ndjson": {
|
|
172
|
+
"example": '{"jsonrpc":"2.0","id":"request-123","result":{"taskId":"task-456","status":"working"}}\n'
|
|
173
|
+
'{"jsonrpc":"2.0","id":"request-123","result":{"messageId":"msg-1","role":"agent","parts":[{"kind":"text","text":"Response"}]}}\n'
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
400: {"description": "Invalid request or unsupported method"},
|
|
178
|
+
404: {"description": "Agent, Team, or Workflow not found"},
|
|
179
|
+
},
|
|
180
|
+
)
|
|
181
|
+
async def a2a_stream_message(request: Request):
|
|
182
|
+
request_body = await request.json()
|
|
183
|
+
kwargs = await get_request_kwargs(request, a2a_stream_message)
|
|
184
|
+
|
|
185
|
+
# 1. Get the Agent, Team, or Workflow to run
|
|
186
|
+
agent_id = request_body.get("params", {}).get("message", {}).get("agentId")
|
|
187
|
+
if not agent_id:
|
|
188
|
+
agent_id = request.headers.get("X-Agent-ID")
|
|
189
|
+
if not agent_id:
|
|
190
|
+
raise HTTPException(
|
|
191
|
+
status_code=400,
|
|
192
|
+
detail="Entity ID required. Provide 'agentId' in params.message or 'X-Agent-ID' header.",
|
|
193
|
+
)
|
|
194
|
+
entity: Optional[Union[Agent, Team, Workflow]] = None
|
|
195
|
+
if agents:
|
|
196
|
+
entity = get_agent_by_id(agent_id, agents)
|
|
197
|
+
if not entity and teams:
|
|
198
|
+
entity = get_team_by_id(agent_id, teams)
|
|
199
|
+
if not entity and workflows:
|
|
200
|
+
entity = get_workflow_by_id(agent_id, workflows)
|
|
201
|
+
if entity is None:
|
|
202
|
+
raise HTTPException(status_code=404, detail=f"Agent, Team, or Workflow with ID '{agent_id}' not found")
|
|
203
|
+
|
|
204
|
+
# 2. Map the request to our run_input and run variables
|
|
205
|
+
run_input = await map_a2a_request_to_run_input(request_body, stream=True)
|
|
206
|
+
context_id = request_body.get("params", {}).get("message", {}).get("contextId")
|
|
207
|
+
user_id = request.headers.get("X-User-ID")
|
|
208
|
+
if not user_id:
|
|
209
|
+
user_id = request_body.get("params", {}).get("message", {}).get("metadata", {}).get("userId")
|
|
210
|
+
|
|
211
|
+
# 3. Run the Agent, Team, or Workflow and stream the response
|
|
212
|
+
try:
|
|
213
|
+
if isinstance(entity, Workflow):
|
|
214
|
+
event_stream = entity.arun(
|
|
215
|
+
input=run_input.input_content,
|
|
216
|
+
images=list(run_input.images) if run_input.images else None,
|
|
217
|
+
videos=list(run_input.videos) if run_input.videos else None,
|
|
218
|
+
audio=list(run_input.audios) if run_input.audios else None,
|
|
219
|
+
files=list(run_input.files) if run_input.files else None,
|
|
220
|
+
session_id=context_id,
|
|
221
|
+
user_id=user_id,
|
|
222
|
+
stream=True,
|
|
223
|
+
stream_events=True,
|
|
224
|
+
**kwargs,
|
|
225
|
+
)
|
|
226
|
+
else:
|
|
227
|
+
event_stream = entity.arun( # type: ignore[assignment]
|
|
228
|
+
input=run_input.input_content,
|
|
229
|
+
images=run_input.images,
|
|
230
|
+
videos=run_input.videos,
|
|
231
|
+
audio=run_input.audios,
|
|
232
|
+
files=run_input.files,
|
|
233
|
+
session_id=context_id,
|
|
234
|
+
user_id=user_id,
|
|
235
|
+
stream=True,
|
|
236
|
+
stream_events=True,
|
|
237
|
+
**kwargs,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
# 4. Stream the response
|
|
241
|
+
return StreamingResponse(
|
|
242
|
+
stream_a2a_response_with_error_handling(event_stream=event_stream, request_id=request_body["id"]), # type: ignore[arg-type]
|
|
243
|
+
media_type="application/x-ndjson",
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
except Exception as e:
|
|
247
|
+
raise HTTPException(status_code=500, detail=f"Failed to start run: {str(e)}")
|
|
248
|
+
|
|
249
|
+
return router
|