agno 2.2.13__py3-none-any.whl → 2.4.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/__init__.py +6 -0
- agno/agent/agent.py +5252 -3145
- agno/agent/remote.py +525 -0
- agno/api/api.py +2 -0
- agno/client/__init__.py +3 -0
- agno/client/a2a/__init__.py +10 -0
- agno/client/a2a/client.py +554 -0
- agno/client/a2a/schemas.py +112 -0
- agno/client/a2a/utils.py +369 -0
- agno/client/os.py +2669 -0
- agno/compression/__init__.py +3 -0
- agno/compression/manager.py +247 -0
- agno/culture/manager.py +2 -2
- agno/db/base.py +927 -6
- agno/db/dynamo/dynamo.py +788 -2
- agno/db/dynamo/schemas.py +128 -0
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/firestore.py +674 -50
- agno/db/firestore/schemas.py +41 -0
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/gcs_json_db.py +506 -3
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/in_memory_db.py +203 -4
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/json_db.py +498 -2
- agno/db/json/utils.py +14 -2
- agno/db/migrations/manager.py +199 -0
- agno/db/migrations/utils.py +19 -0
- agno/db/migrations/v1_to_v2.py +54 -16
- agno/db/migrations/versions/__init__.py +0 -0
- agno/db/migrations/versions/v2_3_0.py +977 -0
- agno/db/mongo/async_mongo.py +1013 -39
- agno/db/mongo/mongo.py +684 -4
- agno/db/mongo/schemas.py +48 -0
- agno/db/mongo/utils.py +17 -0
- agno/db/mysql/__init__.py +2 -1
- agno/db/mysql/async_mysql.py +2958 -0
- agno/db/mysql/mysql.py +722 -53
- agno/db/mysql/schemas.py +77 -11
- agno/db/mysql/utils.py +151 -8
- agno/db/postgres/async_postgres.py +1254 -137
- agno/db/postgres/postgres.py +2316 -93
- agno/db/postgres/schemas.py +153 -21
- agno/db/postgres/utils.py +22 -7
- agno/db/redis/redis.py +531 -3
- agno/db/redis/schemas.py +36 -0
- agno/db/redis/utils.py +31 -15
- agno/db/schemas/evals.py +1 -0
- agno/db/schemas/memory.py +20 -9
- agno/db/singlestore/schemas.py +70 -1
- agno/db/singlestore/singlestore.py +737 -74
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/async_sqlite.py +1069 -89
- agno/db/sqlite/schemas.py +133 -1
- agno/db/sqlite/sqlite.py +2203 -165
- agno/db/sqlite/utils.py +21 -11
- agno/db/surrealdb/models.py +25 -0
- agno/db/surrealdb/surrealdb.py +603 -1
- agno/db/utils.py +60 -0
- agno/eval/__init__.py +26 -3
- agno/eval/accuracy.py +25 -12
- agno/eval/agent_as_judge.py +871 -0
- agno/eval/base.py +29 -0
- agno/eval/performance.py +10 -4
- agno/eval/reliability.py +22 -13
- agno/eval/utils.py +2 -1
- agno/exceptions.py +42 -0
- agno/hooks/__init__.py +3 -0
- agno/hooks/decorator.py +164 -0
- agno/integrations/discord/client.py +13 -2
- agno/knowledge/__init__.py +4 -0
- agno/knowledge/chunking/code.py +90 -0
- agno/knowledge/chunking/document.py +65 -4
- agno/knowledge/chunking/fixed.py +4 -1
- agno/knowledge/chunking/markdown.py +102 -11
- agno/knowledge/chunking/recursive.py +2 -2
- agno/knowledge/chunking/semantic.py +130 -48
- agno/knowledge/chunking/strategy.py +18 -0
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/knowledge/embedder/mistral.py +1 -1
- agno/knowledge/embedder/nebius.py +1 -1
- agno/knowledge/embedder/openai.py +16 -12
- agno/knowledge/filesystem.py +412 -0
- agno/knowledge/knowledge.py +4261 -1199
- agno/knowledge/protocol.py +134 -0
- agno/knowledge/reader/arxiv_reader.py +3 -2
- agno/knowledge/reader/base.py +9 -7
- agno/knowledge/reader/csv_reader.py +91 -42
- agno/knowledge/reader/docx_reader.py +9 -10
- agno/knowledge/reader/excel_reader.py +225 -0
- agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
- agno/knowledge/reader/firecrawl_reader.py +3 -2
- agno/knowledge/reader/json_reader.py +16 -22
- agno/knowledge/reader/markdown_reader.py +15 -14
- agno/knowledge/reader/pdf_reader.py +33 -28
- agno/knowledge/reader/pptx_reader.py +9 -10
- agno/knowledge/reader/reader_factory.py +135 -1
- agno/knowledge/reader/s3_reader.py +8 -16
- agno/knowledge/reader/tavily_reader.py +3 -3
- agno/knowledge/reader/text_reader.py +15 -14
- agno/knowledge/reader/utils/__init__.py +17 -0
- agno/knowledge/reader/utils/spreadsheet.py +114 -0
- agno/knowledge/reader/web_search_reader.py +8 -65
- agno/knowledge/reader/website_reader.py +16 -13
- agno/knowledge/reader/wikipedia_reader.py +36 -3
- agno/knowledge/reader/youtube_reader.py +3 -2
- agno/knowledge/remote_content/__init__.py +33 -0
- agno/knowledge/remote_content/config.py +266 -0
- agno/knowledge/remote_content/remote_content.py +105 -17
- agno/knowledge/utils.py +76 -22
- agno/learn/__init__.py +71 -0
- agno/learn/config.py +463 -0
- agno/learn/curate.py +185 -0
- agno/learn/machine.py +725 -0
- agno/learn/schemas.py +1114 -0
- agno/learn/stores/__init__.py +38 -0
- agno/learn/stores/decision_log.py +1156 -0
- agno/learn/stores/entity_memory.py +3275 -0
- agno/learn/stores/learned_knowledge.py +1583 -0
- agno/learn/stores/protocol.py +117 -0
- agno/learn/stores/session_context.py +1217 -0
- agno/learn/stores/user_memory.py +1495 -0
- agno/learn/stores/user_profile.py +1220 -0
- agno/learn/utils.py +209 -0
- agno/media.py +22 -6
- agno/memory/__init__.py +14 -1
- agno/memory/manager.py +223 -8
- 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/aimlapi.py +17 -0
- agno/models/anthropic/claude.py +434 -59
- agno/models/aws/bedrock.py +121 -20
- agno/models/aws/claude.py +131 -274
- agno/models/azure/ai_foundry.py +10 -6
- agno/models/azure/openai_chat.py +33 -10
- agno/models/base.py +1162 -561
- agno/models/cerebras/cerebras.py +120 -24
- agno/models/cerebras/cerebras_openai.py +21 -2
- agno/models/cohere/chat.py +65 -6
- agno/models/cometapi/cometapi.py +18 -1
- agno/models/dashscope/dashscope.py +2 -3
- agno/models/deepinfra/deepinfra.py +18 -1
- agno/models/deepseek/deepseek.py +69 -3
- agno/models/fireworks/fireworks.py +18 -1
- agno/models/google/gemini.py +959 -89
- agno/models/google/utils.py +22 -0
- agno/models/groq/groq.py +48 -18
- agno/models/huggingface/huggingface.py +17 -6
- agno/models/ibm/watsonx.py +16 -6
- agno/models/internlm/internlm.py +18 -1
- agno/models/langdb/langdb.py +13 -1
- agno/models/litellm/chat.py +88 -9
- agno/models/litellm/litellm_openai.py +18 -1
- agno/models/message.py +24 -5
- agno/models/meta/llama.py +40 -13
- agno/models/meta/llama_openai.py +22 -21
- agno/models/metrics.py +12 -0
- agno/models/mistral/mistral.py +8 -4
- agno/models/n1n/__init__.py +3 -0
- agno/models/n1n/n1n.py +57 -0
- agno/models/nebius/nebius.py +6 -7
- agno/models/nvidia/nvidia.py +20 -3
- agno/models/ollama/__init__.py +2 -0
- agno/models/ollama/chat.py +17 -6
- agno/models/ollama/responses.py +100 -0
- agno/models/openai/__init__.py +2 -0
- agno/models/openai/chat.py +117 -26
- agno/models/openai/open_responses.py +46 -0
- agno/models/openai/responses.py +110 -32
- agno/models/openrouter/__init__.py +2 -0
- agno/models/openrouter/openrouter.py +67 -2
- agno/models/openrouter/responses.py +146 -0
- agno/models/perplexity/perplexity.py +19 -1
- agno/models/portkey/portkey.py +7 -6
- agno/models/requesty/requesty.py +19 -2
- agno/models/response.py +20 -2
- agno/models/sambanova/sambanova.py +20 -3
- agno/models/siliconflow/siliconflow.py +19 -2
- agno/models/together/together.py +20 -3
- agno/models/vercel/v0.py +20 -3
- agno/models/vertexai/claude.py +124 -4
- agno/models/vllm/vllm.py +19 -14
- agno/models/xai/xai.py +19 -2
- agno/os/app.py +467 -137
- agno/os/auth.py +253 -5
- agno/os/config.py +22 -0
- agno/os/interfaces/a2a/a2a.py +7 -6
- agno/os/interfaces/a2a/router.py +635 -26
- agno/os/interfaces/a2a/utils.py +32 -33
- agno/os/interfaces/agui/agui.py +5 -3
- agno/os/interfaces/agui/router.py +26 -16
- agno/os/interfaces/agui/utils.py +97 -57
- agno/os/interfaces/base.py +7 -7
- agno/os/interfaces/slack/router.py +16 -7
- agno/os/interfaces/slack/slack.py +7 -7
- agno/os/interfaces/whatsapp/router.py +35 -7
- agno/os/interfaces/whatsapp/security.py +3 -1
- agno/os/interfaces/whatsapp/whatsapp.py +11 -8
- agno/os/managers.py +326 -0
- agno/os/mcp.py +652 -79
- agno/os/middleware/__init__.py +4 -0
- agno/os/middleware/jwt.py +718 -115
- agno/os/middleware/trailing_slash.py +27 -0
- agno/os/router.py +105 -1558
- agno/os/routers/agents/__init__.py +3 -0
- agno/os/routers/agents/router.py +655 -0
- agno/os/routers/agents/schema.py +288 -0
- agno/os/routers/components/__init__.py +3 -0
- agno/os/routers/components/components.py +475 -0
- agno/os/routers/database.py +155 -0
- agno/os/routers/evals/evals.py +111 -18
- agno/os/routers/evals/schemas.py +38 -5
- agno/os/routers/evals/utils.py +80 -11
- agno/os/routers/health.py +3 -3
- agno/os/routers/knowledge/knowledge.py +284 -35
- agno/os/routers/knowledge/schemas.py +14 -2
- agno/os/routers/memory/memory.py +274 -11
- agno/os/routers/memory/schemas.py +44 -3
- agno/os/routers/metrics/metrics.py +30 -15
- agno/os/routers/metrics/schemas.py +10 -6
- agno/os/routers/registry/__init__.py +3 -0
- agno/os/routers/registry/registry.py +337 -0
- agno/os/routers/session/session.py +143 -14
- agno/os/routers/teams/__init__.py +3 -0
- agno/os/routers/teams/router.py +550 -0
- agno/os/routers/teams/schema.py +280 -0
- agno/os/routers/traces/__init__.py +3 -0
- agno/os/routers/traces/schemas.py +414 -0
- agno/os/routers/traces/traces.py +549 -0
- agno/os/routers/workflows/__init__.py +3 -0
- agno/os/routers/workflows/router.py +757 -0
- agno/os/routers/workflows/schema.py +139 -0
- agno/os/schema.py +157 -584
- agno/os/scopes.py +469 -0
- agno/os/settings.py +3 -0
- agno/os/utils.py +574 -185
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +102 -2
- agno/reasoning/default.py +6 -7
- agno/reasoning/gemini.py +87 -3
- agno/reasoning/groq.py +109 -2
- agno/reasoning/helpers.py +6 -7
- agno/reasoning/manager.py +1238 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +115 -1
- agno/reasoning/vertexai.py +85 -1
- agno/registry/__init__.py +3 -0
- agno/registry/registry.py +68 -0
- agno/remote/__init__.py +3 -0
- agno/remote/base.py +581 -0
- agno/run/__init__.py +2 -4
- agno/run/agent.py +134 -19
- agno/run/base.py +49 -1
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/run/requirement.py +181 -0
- agno/run/team.py +111 -19
- agno/run/workflow.py +2 -1
- agno/session/agent.py +57 -92
- agno/session/summary.py +1 -1
- agno/session/team.py +62 -115
- agno/session/workflow.py +353 -57
- agno/skills/__init__.py +17 -0
- agno/skills/agent_skills.py +377 -0
- agno/skills/errors.py +32 -0
- agno/skills/loaders/__init__.py +4 -0
- agno/skills/loaders/base.py +27 -0
- agno/skills/loaders/local.py +216 -0
- agno/skills/skill.py +65 -0
- agno/skills/utils.py +107 -0
- agno/skills/validator.py +277 -0
- agno/table.py +10 -0
- agno/team/__init__.py +5 -1
- agno/team/remote.py +447 -0
- agno/team/team.py +3769 -2202
- agno/tools/brandfetch.py +27 -18
- agno/tools/browserbase.py +225 -16
- agno/tools/crawl4ai.py +3 -0
- agno/tools/duckduckgo.py +25 -71
- agno/tools/exa.py +0 -21
- agno/tools/file.py +14 -13
- agno/tools/file_generation.py +12 -6
- agno/tools/firecrawl.py +15 -7
- agno/tools/function.py +94 -113
- agno/tools/google_bigquery.py +11 -2
- agno/tools/google_drive.py +4 -3
- agno/tools/knowledge.py +9 -4
- agno/tools/mcp/mcp.py +301 -18
- agno/tools/mcp/multi_mcp.py +269 -14
- agno/tools/mem0.py +11 -10
- agno/tools/memory.py +47 -46
- agno/tools/mlx_transcribe.py +10 -7
- agno/tools/models/nebius.py +5 -5
- agno/tools/models_labs.py +20 -10
- agno/tools/nano_banana.py +151 -0
- agno/tools/parallel.py +0 -7
- agno/tools/postgres.py +76 -36
- agno/tools/python.py +14 -6
- agno/tools/reasoning.py +30 -23
- agno/tools/redshift.py +406 -0
- agno/tools/shopify.py +1519 -0
- agno/tools/spotify.py +919 -0
- agno/tools/tavily.py +4 -1
- agno/tools/toolkit.py +253 -18
- agno/tools/websearch.py +93 -0
- agno/tools/website.py +1 -1
- agno/tools/wikipedia.py +1 -1
- agno/tools/workflow.py +56 -48
- agno/tools/yfinance.py +12 -11
- agno/tracing/__init__.py +12 -0
- agno/tracing/exporter.py +161 -0
- agno/tracing/schemas.py +276 -0
- agno/tracing/setup.py +112 -0
- agno/utils/agent.py +251 -10
- agno/utils/cryptography.py +22 -0
- agno/utils/dttm.py +33 -0
- agno/utils/events.py +264 -7
- agno/utils/hooks.py +111 -3
- agno/utils/http.py +161 -2
- agno/utils/mcp.py +49 -8
- agno/utils/media.py +22 -1
- agno/utils/models/ai_foundry.py +9 -2
- agno/utils/models/claude.py +20 -5
- agno/utils/models/cohere.py +9 -2
- agno/utils/models/llama.py +9 -2
- agno/utils/models/mistral.py +4 -2
- agno/utils/os.py +0 -0
- agno/utils/print_response/agent.py +99 -16
- agno/utils/print_response/team.py +223 -24
- agno/utils/print_response/workflow.py +0 -2
- agno/utils/prompts.py +8 -6
- agno/utils/remote.py +23 -0
- agno/utils/response.py +1 -13
- agno/utils/string.py +91 -2
- agno/utils/team.py +62 -12
- agno/utils/tokens.py +657 -0
- agno/vectordb/base.py +15 -2
- agno/vectordb/cassandra/cassandra.py +1 -1
- agno/vectordb/chroma/__init__.py +2 -1
- agno/vectordb/chroma/chromadb.py +468 -23
- agno/vectordb/clickhouse/clickhousedb.py +1 -1
- agno/vectordb/couchbase/couchbase.py +6 -2
- agno/vectordb/lancedb/lance_db.py +7 -38
- agno/vectordb/lightrag/lightrag.py +7 -6
- agno/vectordb/milvus/milvus.py +118 -84
- agno/vectordb/mongodb/__init__.py +2 -1
- agno/vectordb/mongodb/mongodb.py +14 -31
- agno/vectordb/pgvector/pgvector.py +120 -66
- agno/vectordb/pineconedb/pineconedb.py +2 -19
- agno/vectordb/qdrant/__init__.py +2 -1
- agno/vectordb/qdrant/qdrant.py +33 -56
- agno/vectordb/redis/__init__.py +2 -1
- agno/vectordb/redis/redisdb.py +19 -31
- agno/vectordb/singlestore/singlestore.py +17 -9
- agno/vectordb/surrealdb/surrealdb.py +2 -38
- agno/vectordb/weaviate/__init__.py +2 -1
- agno/vectordb/weaviate/weaviate.py +7 -3
- agno/workflow/__init__.py +5 -1
- agno/workflow/agent.py +2 -2
- agno/workflow/condition.py +12 -10
- agno/workflow/loop.py +28 -9
- agno/workflow/parallel.py +21 -13
- agno/workflow/remote.py +362 -0
- agno/workflow/router.py +12 -9
- agno/workflow/step.py +261 -36
- agno/workflow/steps.py +12 -8
- agno/workflow/types.py +40 -77
- agno/workflow/workflow.py +939 -213
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
- agno-2.4.3.dist-info/RECORD +677 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
- agno/tools/googlesearch.py +0 -98
- agno/tools/memori.py +0 -339
- agno-2.2.13.dist-info/RECORD +0 -575
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
- {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/top_level.txt +0 -0
agno/os/auth.py
CHANGED
|
@@ -1,24 +1,77 @@
|
|
|
1
|
-
from
|
|
1
|
+
from os import getenv
|
|
2
|
+
from typing import List, Optional, Set
|
|
3
|
+
|
|
4
|
+
from fastapi import Depends, HTTPException, Request
|
|
2
5
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
|
3
6
|
|
|
7
|
+
from agno.os.scopes import get_accessible_resource_ids
|
|
4
8
|
from agno.os.settings import AgnoAPISettings
|
|
5
9
|
|
|
6
10
|
# Create a global HTTPBearer instance
|
|
7
11
|
security = HTTPBearer(auto_error=False)
|
|
8
12
|
|
|
9
13
|
|
|
14
|
+
def get_auth_token_from_request(request: Request) -> Optional[str]:
|
|
15
|
+
"""
|
|
16
|
+
Extract the JWT/Bearer token from the Authorization header.
|
|
17
|
+
|
|
18
|
+
This is used to forward the auth token to remote agents/teams/workflows
|
|
19
|
+
when making requests through the gateway.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
request: The FastAPI request object
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
The bearer token string if present, None otherwise
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
auth_token = get_auth_token_from_request(request)
|
|
29
|
+
if auth_token and isinstance(agent, RemoteAgent):
|
|
30
|
+
await agent.arun(message, auth_token=auth_token)
|
|
31
|
+
"""
|
|
32
|
+
auth_header = request.headers.get("Authorization")
|
|
33
|
+
if auth_header and auth_header.lower().startswith("bearer "):
|
|
34
|
+
return auth_header[7:] # Remove "Bearer " prefix
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _is_jwt_configured() -> bool:
|
|
39
|
+
"""Check if JWT authentication is configured via environment variables.
|
|
40
|
+
|
|
41
|
+
This covers cases where JWT middleware is set up manually (not via authorization=True).
|
|
42
|
+
"""
|
|
43
|
+
return bool(getenv("JWT_VERIFICATION_KEY") or getenv("JWT_JWKS_FILE"))
|
|
44
|
+
|
|
45
|
+
|
|
10
46
|
def get_authentication_dependency(settings: AgnoAPISettings):
|
|
11
47
|
"""
|
|
12
48
|
Create an authentication dependency function for FastAPI routes.
|
|
13
49
|
|
|
50
|
+
This handles security key authentication (OS_SECURITY_KEY).
|
|
51
|
+
When JWT authorization is enabled (via authorization=True, JWT environment variables,
|
|
52
|
+
or manually added JWT middleware), this dependency is skipped as JWT middleware
|
|
53
|
+
handles authentication.
|
|
54
|
+
|
|
14
55
|
Args:
|
|
15
|
-
settings: The API settings containing the security key
|
|
56
|
+
settings: The API settings containing the security key and authorization flag
|
|
16
57
|
|
|
17
58
|
Returns:
|
|
18
59
|
A dependency function that can be used with FastAPI's Depends()
|
|
19
60
|
"""
|
|
20
61
|
|
|
21
|
-
def auth_dependency(credentials: HTTPAuthorizationCredentials = Depends(security)) -> bool:
|
|
62
|
+
async def auth_dependency(request: Request, credentials: HTTPAuthorizationCredentials = Depends(security)) -> bool:
|
|
63
|
+
# If JWT authorization is enabled via settings (authorization=True on AgentOS)
|
|
64
|
+
if settings and settings.authorization_enabled:
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
# Check if JWT middleware has already handled authentication
|
|
68
|
+
if getattr(request.state, "authenticated", False):
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
# Also skip if JWT is configured via environment variables
|
|
72
|
+
if _is_jwt_configured():
|
|
73
|
+
return True
|
|
74
|
+
|
|
22
75
|
# If no security key is set, skip authentication entirely
|
|
23
76
|
if not settings or not settings.os_security_key:
|
|
24
77
|
return True
|
|
@@ -40,18 +93,213 @@ def get_authentication_dependency(settings: AgnoAPISettings):
|
|
|
40
93
|
|
|
41
94
|
def validate_websocket_token(token: str, settings: AgnoAPISettings) -> bool:
|
|
42
95
|
"""
|
|
43
|
-
Validate a bearer token for WebSocket authentication.
|
|
96
|
+
Validate a bearer token for WebSocket authentication (legacy os_security_key method).
|
|
97
|
+
|
|
98
|
+
When JWT authorization is enabled (via authorization=True or JWT environment variables),
|
|
99
|
+
this validation is skipped as JWT middleware handles authentication.
|
|
44
100
|
|
|
45
101
|
Args:
|
|
46
102
|
token: The bearer token to validate
|
|
47
|
-
settings: The API settings containing the security key
|
|
103
|
+
settings: The API settings containing the security key and authorization flag
|
|
48
104
|
|
|
49
105
|
Returns:
|
|
50
106
|
True if the token is valid or authentication is disabled, False otherwise
|
|
51
107
|
"""
|
|
108
|
+
# If JWT authorization is enabled, skip security key validation
|
|
109
|
+
if settings and settings.authorization_enabled:
|
|
110
|
+
return True
|
|
111
|
+
|
|
112
|
+
# Also skip if JWT is configured via environment variables (manual JWT middleware setup)
|
|
113
|
+
if _is_jwt_configured():
|
|
114
|
+
return True
|
|
115
|
+
|
|
52
116
|
# If no security key is set, skip authentication entirely
|
|
53
117
|
if not settings or not settings.os_security_key:
|
|
54
118
|
return True
|
|
55
119
|
|
|
56
120
|
# Verify the token matches the configured security key
|
|
57
121
|
return token == settings.os_security_key
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_accessible_resources(request: Request, resource_type: str) -> Set[str]:
|
|
125
|
+
"""
|
|
126
|
+
Get the set of resource IDs the user has access to based on their scopes.
|
|
127
|
+
|
|
128
|
+
This function is used to filter lists of resources (agents, teams, workflows)
|
|
129
|
+
based on the user's scopes from their JWT token.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
request: The FastAPI request object (contains request.state.scopes)
|
|
133
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Set of resource IDs the user can access. Returns {"*"} for wildcard access.
|
|
137
|
+
|
|
138
|
+
Usage:
|
|
139
|
+
accessible_ids = get_accessible_resources(request, "agents")
|
|
140
|
+
if "*" not in accessible_ids:
|
|
141
|
+
agents = [a for a in agents if a.id in accessible_ids]
|
|
142
|
+
|
|
143
|
+
Examples:
|
|
144
|
+
>>> # User with specific agent access
|
|
145
|
+
>>> # Token scopes: ["agent-os:my-os:agents:my-agent:read"]
|
|
146
|
+
>>> get_accessible_resources(request, "agents")
|
|
147
|
+
{'my-agent'}
|
|
148
|
+
|
|
149
|
+
>>> # User with wildcard access
|
|
150
|
+
>>> # Token scopes: ["agent-os:my-os:agents:*:read"] or ["admin"]
|
|
151
|
+
>>> get_accessible_resources(request, "agents")
|
|
152
|
+
{'*'}
|
|
153
|
+
|
|
154
|
+
>>> # User with agent-os level access (global resource scope)
|
|
155
|
+
>>> # Token scopes: ["agent-os:my-os:agents:read"]
|
|
156
|
+
>>> get_accessible_resources(request, "agents")
|
|
157
|
+
{'*'}
|
|
158
|
+
"""
|
|
159
|
+
# Check if accessible_resource_ids is already cached in request state (set by JWT middleware)
|
|
160
|
+
# This happens when user doesn't have global scope but has specific resource scopes
|
|
161
|
+
cached_ids = getattr(request.state, "accessible_resource_ids", None)
|
|
162
|
+
if cached_ids is not None:
|
|
163
|
+
return cached_ids
|
|
164
|
+
|
|
165
|
+
# Get user's scopes from request state (set by JWT middleware)
|
|
166
|
+
user_scopes = getattr(request.state, "scopes", [])
|
|
167
|
+
|
|
168
|
+
# Get accessible resource IDs
|
|
169
|
+
accessible_ids = get_accessible_resource_ids(user_scopes=user_scopes, resource_type=resource_type)
|
|
170
|
+
|
|
171
|
+
return accessible_ids
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def filter_resources_by_access(request: Request, resources: List, resource_type: str) -> List:
|
|
175
|
+
"""
|
|
176
|
+
Filter a list of resources based on user's access permissions.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
request: The FastAPI request object
|
|
180
|
+
resources: List of resource objects (agents, teams, or workflows) with 'id' attribute
|
|
181
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Filtered list of resources the user has access to
|
|
185
|
+
|
|
186
|
+
Usage:
|
|
187
|
+
agents = filter_resources_by_access(request, all_agents, "agents")
|
|
188
|
+
teams = filter_resources_by_access(request, all_teams, "teams")
|
|
189
|
+
workflows = filter_resources_by_access(request, all_workflows, "workflows")
|
|
190
|
+
|
|
191
|
+
Examples:
|
|
192
|
+
>>> # User with specific access
|
|
193
|
+
>>> agents = [Agent(id="agent-1"), Agent(id="agent-2"), Agent(id="agent-3")]
|
|
194
|
+
>>> # Token scopes: ["agent-os:my-os:agents:agent-1:read", "agent-os:my-os:agents:agent-2:read"]
|
|
195
|
+
>>> filter_resources_by_access(request, agents, "agents")
|
|
196
|
+
[Agent(id="agent-1"), Agent(id="agent-2")]
|
|
197
|
+
|
|
198
|
+
>>> # User with wildcard access
|
|
199
|
+
>>> # Token scopes: ["admin"]
|
|
200
|
+
>>> filter_resources_by_access(request, agents, "agents")
|
|
201
|
+
[Agent(id="agent-1"), Agent(id="agent-2"), Agent(id="agent-3")]
|
|
202
|
+
"""
|
|
203
|
+
accessible_ids = get_accessible_resources(request, resource_type)
|
|
204
|
+
|
|
205
|
+
# Wildcard access - return all resources
|
|
206
|
+
if "*" in accessible_ids:
|
|
207
|
+
return resources
|
|
208
|
+
|
|
209
|
+
# Filter to only accessible resources
|
|
210
|
+
return [r for r in resources if r.id in accessible_ids]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def check_resource_access(request: Request, resource_id: str, resource_type: str, action: str = "read") -> bool:
|
|
214
|
+
"""
|
|
215
|
+
Check if user has access to a specific resource.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
request: The FastAPI request object
|
|
219
|
+
resource_id: ID of the resource to check
|
|
220
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
221
|
+
action: Action to check ("read", "run", etc.)
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
True if user has access, False otherwise
|
|
225
|
+
|
|
226
|
+
Usage:
|
|
227
|
+
if not check_resource_access(request, agent_id, "agents", "run"):
|
|
228
|
+
raise HTTPException(status_code=403, detail="Access denied")
|
|
229
|
+
|
|
230
|
+
Examples:
|
|
231
|
+
>>> # Token scopes: ["agent-os:my-os:agents:my-agent:read", "agent-os:my-os:agents:my-agent:run"]
|
|
232
|
+
>>> check_resource_access(request, "my-agent", "agents", "run")
|
|
233
|
+
True
|
|
234
|
+
|
|
235
|
+
>>> check_resource_access(request, "other-agent", "agents", "run")
|
|
236
|
+
False
|
|
237
|
+
"""
|
|
238
|
+
accessible_ids = get_accessible_resources(request, resource_type)
|
|
239
|
+
|
|
240
|
+
# Wildcard access grants all permissions
|
|
241
|
+
if "*" in accessible_ids:
|
|
242
|
+
return True
|
|
243
|
+
|
|
244
|
+
# Check if user has access to this specific resource
|
|
245
|
+
return resource_id in accessible_ids
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def require_resource_access(resource_type: str, action: str, resource_id_param: str):
|
|
249
|
+
"""
|
|
250
|
+
Create a dependency that checks if the user has access to a specific resource.
|
|
251
|
+
|
|
252
|
+
This dependency factory creates a FastAPI dependency that automatically checks
|
|
253
|
+
authorization when authorization is enabled. It extracts the resource ID from
|
|
254
|
+
the path parameters and verifies the user has the required access.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
258
|
+
action: Action to check ("read", "run")
|
|
259
|
+
resource_id_param: Name of the path parameter containing the resource ID
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
A dependency function for use with FastAPI's Depends()
|
|
263
|
+
|
|
264
|
+
Usage:
|
|
265
|
+
@router.post("/agents/{agent_id}/runs")
|
|
266
|
+
async def create_agent_run(
|
|
267
|
+
agent_id: str,
|
|
268
|
+
request: Request,
|
|
269
|
+
_: None = Depends(require_resource_access("agents", "run", "agent_id")),
|
|
270
|
+
):
|
|
271
|
+
...
|
|
272
|
+
|
|
273
|
+
@router.get("/agents/{agent_id}")
|
|
274
|
+
async def get_agent(
|
|
275
|
+
agent_id: str,
|
|
276
|
+
request: Request,
|
|
277
|
+
_: None = Depends(require_resource_access("agents", "read", "agent_id")),
|
|
278
|
+
):
|
|
279
|
+
...
|
|
280
|
+
|
|
281
|
+
Examples:
|
|
282
|
+
>>> # Creates dependency for checking agent run access
|
|
283
|
+
>>> dep = require_resource_access("agents", "run", "agent_id")
|
|
284
|
+
|
|
285
|
+
>>> # Creates dependency for checking team read access
|
|
286
|
+
>>> dep = require_resource_access("teams", "read", "team_id")
|
|
287
|
+
"""
|
|
288
|
+
# Map resource_type to singular form for error messages
|
|
289
|
+
resource_singular = {
|
|
290
|
+
"agents": "agent",
|
|
291
|
+
"teams": "team",
|
|
292
|
+
"workflows": "workflow",
|
|
293
|
+
}.get(resource_type, resource_type.rstrip("s"))
|
|
294
|
+
|
|
295
|
+
async def dependency(request: Request):
|
|
296
|
+
# Only check authorization if it's enabled
|
|
297
|
+
if not getattr(request.state, "authorization_enabled", False):
|
|
298
|
+
return
|
|
299
|
+
|
|
300
|
+
# Get the resource_id from path parameters
|
|
301
|
+
resource_id = request.path_params.get(resource_id_param)
|
|
302
|
+
if resource_id and not check_resource_access(request, resource_id, resource_type, action):
|
|
303
|
+
raise HTTPException(status_code=403, detail=f"Access denied to {action} this {resource_singular}")
|
|
304
|
+
|
|
305
|
+
return dependency
|
agno/os/config.py
CHANGED
|
@@ -5,6 +5,15 @@ from typing import Generic, List, Optional, TypeVar
|
|
|
5
5
|
from pydantic import BaseModel, field_validator
|
|
6
6
|
|
|
7
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
|
+
|
|
8
17
|
class EvalsDomainConfig(BaseModel):
|
|
9
18
|
"""Configuration for the Evals domain of the AgentOS"""
|
|
10
19
|
|
|
@@ -36,6 +45,12 @@ class MemoryDomainConfig(BaseModel):
|
|
|
36
45
|
display_name: Optional[str] = None
|
|
37
46
|
|
|
38
47
|
|
|
48
|
+
class TracesDomainConfig(BaseModel):
|
|
49
|
+
"""Configuration for the Traces domain of the AgentOS"""
|
|
50
|
+
|
|
51
|
+
display_name: Optional[str] = None
|
|
52
|
+
|
|
53
|
+
|
|
39
54
|
DomainConfigType = TypeVar("DomainConfigType")
|
|
40
55
|
|
|
41
56
|
|
|
@@ -77,6 +92,12 @@ class MetricsConfig(MetricsDomainConfig):
|
|
|
77
92
|
dbs: Optional[List[DatabaseConfig[MetricsDomainConfig]]] = None
|
|
78
93
|
|
|
79
94
|
|
|
95
|
+
class TracesConfig(TracesDomainConfig):
|
|
96
|
+
"""Configuration for the Traces domain of the AgentOS"""
|
|
97
|
+
|
|
98
|
+
dbs: Optional[List[DatabaseConfig[TracesDomainConfig]]] = None
|
|
99
|
+
|
|
100
|
+
|
|
80
101
|
class ChatConfig(BaseModel):
|
|
81
102
|
"""Configuration for the Chat page of the AgentOS"""
|
|
82
103
|
|
|
@@ -102,3 +123,4 @@ class AgentOSConfig(BaseModel):
|
|
|
102
123
|
memory: Optional[MemoryConfig] = None
|
|
103
124
|
session: Optional[SessionConfig] = None
|
|
104
125
|
metrics: Optional[MetricsConfig] = None
|
|
126
|
+
traces: Optional[TracesConfig] = None
|
agno/os/interfaces/a2a/a2a.py
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
"""Main class for the A2A app, used to expose an Agno Agent, Team, or Workflow in an A2A compatible format."""
|
|
2
2
|
|
|
3
|
-
from typing import Optional
|
|
3
|
+
from typing import Optional, Union
|
|
4
4
|
|
|
5
5
|
from fastapi.routing import APIRouter
|
|
6
6
|
from typing_extensions import List
|
|
7
7
|
|
|
8
8
|
from agno.agent import Agent
|
|
9
|
+
from agno.agent.remote import RemoteAgent
|
|
9
10
|
from agno.os.interfaces.a2a.router import attach_routes
|
|
10
11
|
from agno.os.interfaces.base import BaseInterface
|
|
11
|
-
from agno.team import Team
|
|
12
|
-
from agno.workflow import Workflow
|
|
12
|
+
from agno.team import RemoteTeam, Team
|
|
13
|
+
from agno.workflow import RemoteWorkflow, Workflow
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
class A2A(BaseInterface):
|
|
@@ -19,9 +20,9 @@ class A2A(BaseInterface):
|
|
|
19
20
|
|
|
20
21
|
def __init__(
|
|
21
22
|
self,
|
|
22
|
-
agents: Optional[List[Agent]] = None,
|
|
23
|
-
teams: Optional[List[Team]] = None,
|
|
24
|
-
workflows: Optional[List[Workflow]] = None,
|
|
23
|
+
agents: Optional[List[Union[Agent, RemoteAgent]]] = None,
|
|
24
|
+
teams: Optional[List[Union[Team, RemoteTeam]]] = None,
|
|
25
|
+
workflows: Optional[List[Union[Workflow, RemoteWorkflow]]] = None,
|
|
25
26
|
prefix: str = "/a2a",
|
|
26
27
|
tags: Optional[List[str]] = None,
|
|
27
28
|
):
|