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/scopes.py
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
"""AgentOS RBAC Scopes
|
|
2
|
+
|
|
3
|
+
This module defines all available permission scopes for AgentOS RBAC (Role-Based Access Control).
|
|
4
|
+
|
|
5
|
+
Scope Format:
|
|
6
|
+
- Global resource scopes: `resource:action`
|
|
7
|
+
- Per-resource scopes: `resource:<resource-id>:action`
|
|
8
|
+
- Wildcards: `resource:*:action` for any resource
|
|
9
|
+
|
|
10
|
+
The AgentOS ID is verified via the JWT `aud` (audience) claim.
|
|
11
|
+
|
|
12
|
+
Examples:
|
|
13
|
+
- `system:read` - Read system config
|
|
14
|
+
- `agents:read` - List all agents
|
|
15
|
+
- `agents:web-agent:read` - Read specific agent
|
|
16
|
+
- `agents:web-agent:run` - Run specific agent
|
|
17
|
+
- `agents:*:run` - Run any agent (wildcard)
|
|
18
|
+
- `agent_os:admin` - Full access to everything
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from enum import Enum
|
|
23
|
+
from typing import Dict, List, Optional, Set
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class AgentOSScope(str, Enum):
|
|
27
|
+
"""
|
|
28
|
+
Enum of all available AgentOS permission scopes.
|
|
29
|
+
|
|
30
|
+
Special Scopes:
|
|
31
|
+
- ADMIN: Grants full access to all endpoints (agent_os:admin)
|
|
32
|
+
|
|
33
|
+
Scope format:
|
|
34
|
+
|
|
35
|
+
Global Resource Scopes:
|
|
36
|
+
- system:read - System configuration and model information
|
|
37
|
+
- agents:read - List all agents
|
|
38
|
+
- teams:read - List all teams
|
|
39
|
+
- workflows:read - List all workflows
|
|
40
|
+
- sessions:read - View session data
|
|
41
|
+
- sessions:write - Create and update sessions
|
|
42
|
+
- sessions:delete - Delete sessions
|
|
43
|
+
- memories:read - View memories
|
|
44
|
+
- memories:write - Create and update memories
|
|
45
|
+
- memories:delete - Delete memories
|
|
46
|
+
- knowledge:read - View and search knowledge
|
|
47
|
+
- knowledge:write - Add and update knowledge
|
|
48
|
+
- knowledge:delete - Delete knowledge
|
|
49
|
+
- metrics:read - View metrics
|
|
50
|
+
- metrics:write - Refresh metrics
|
|
51
|
+
- evals:read - View evaluation runs
|
|
52
|
+
- evals:write - Create and update evaluation runs
|
|
53
|
+
- evals:delete - Delete evaluation runs
|
|
54
|
+
- traces:read - View traces and trace statistics
|
|
55
|
+
|
|
56
|
+
Per-Resource Scopes (with resource ID):
|
|
57
|
+
- agents:<agent-id>:read - Read specific agent
|
|
58
|
+
- agents:<agent-id>:run - Run specific agent
|
|
59
|
+
- teams:<team-id>:read - Read specific team
|
|
60
|
+
- teams:<team-id>:run - Run specific team
|
|
61
|
+
- workflows:<workflow-id>:read - Read specific workflow
|
|
62
|
+
- workflows:<workflow-id>:run - Run specific workflow
|
|
63
|
+
|
|
64
|
+
Wildcards:
|
|
65
|
+
- agents:*:run - Run any agent
|
|
66
|
+
- teams:*:run - Run any team
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
# Special scopes
|
|
70
|
+
ADMIN = "agent_os:admin"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class ParsedScope:
|
|
75
|
+
"""Represents a parsed scope with its components."""
|
|
76
|
+
|
|
77
|
+
raw: str
|
|
78
|
+
scope_type: str # "admin", "global", "per_resource", or "unknown"
|
|
79
|
+
resource: Optional[str] = None
|
|
80
|
+
resource_id: Optional[str] = None
|
|
81
|
+
action: Optional[str] = None
|
|
82
|
+
is_wildcard_resource: bool = False
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def is_global_resource_scope(self) -> bool:
|
|
86
|
+
"""Check if this scope targets all resources of a type (no resource_id)."""
|
|
87
|
+
return self.scope_type == "global"
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def is_per_resource_scope(self) -> bool:
|
|
91
|
+
"""Check if this scope targets a specific resource (has resource_id)."""
|
|
92
|
+
return self.scope_type == "per_resource"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def parse_scope(scope: str, admin_scope: Optional[str] = None) -> ParsedScope:
|
|
96
|
+
"""
|
|
97
|
+
Parse a scope string into its components.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
scope: The scope string to parse
|
|
101
|
+
admin_scope: The scope string that grants admin access (default: "agent_os:admin")
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
ParsedScope object with parsed components
|
|
105
|
+
|
|
106
|
+
Examples:
|
|
107
|
+
>>> parse_scope("agent_os:admin")
|
|
108
|
+
ParsedScope(raw="agent_os:admin", scope_type="admin")
|
|
109
|
+
|
|
110
|
+
>>> parse_scope("system:read")
|
|
111
|
+
ParsedScope(raw="system:read", scope_type="global", resource="system", action="read")
|
|
112
|
+
|
|
113
|
+
>>> parse_scope("agents:web-agent:read")
|
|
114
|
+
ParsedScope(raw="...", scope_type="per_resource", resource="agents", resource_id="web-agent", action="read")
|
|
115
|
+
|
|
116
|
+
>>> parse_scope("agents:*:run")
|
|
117
|
+
ParsedScope(raw="...", scope_type="per_resource", resource="agents", resource_id="*", action="run", is_wildcard_resource=True)
|
|
118
|
+
"""
|
|
119
|
+
effective_admin_scope = admin_scope or AgentOSScope.ADMIN.value
|
|
120
|
+
if scope == effective_admin_scope:
|
|
121
|
+
return ParsedScope(raw=scope, scope_type="admin")
|
|
122
|
+
|
|
123
|
+
parts = scope.split(":")
|
|
124
|
+
|
|
125
|
+
# Global resource scope: resource:action (2 parts)
|
|
126
|
+
if len(parts) == 2:
|
|
127
|
+
return ParsedScope(
|
|
128
|
+
raw=scope,
|
|
129
|
+
scope_type="global",
|
|
130
|
+
resource=parts[0],
|
|
131
|
+
action=parts[1],
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
# Per-resource scope: resource:<resource-id>:action (3 parts)
|
|
135
|
+
if len(parts) == 3:
|
|
136
|
+
resource_id = parts[1]
|
|
137
|
+
is_wildcard_resource = resource_id == "*"
|
|
138
|
+
|
|
139
|
+
return ParsedScope(
|
|
140
|
+
raw=scope,
|
|
141
|
+
scope_type="per_resource",
|
|
142
|
+
resource=parts[0],
|
|
143
|
+
resource_id=resource_id,
|
|
144
|
+
action=parts[2],
|
|
145
|
+
is_wildcard_resource=is_wildcard_resource,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# Invalid format
|
|
149
|
+
return ParsedScope(raw=scope, scope_type="unknown")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def matches_scope(
|
|
153
|
+
user_scope: ParsedScope,
|
|
154
|
+
required_scope: ParsedScope,
|
|
155
|
+
resource_id: Optional[str] = None,
|
|
156
|
+
) -> bool:
|
|
157
|
+
"""
|
|
158
|
+
Check if a user's scope matches a required scope.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
user_scope: The user's parsed scope
|
|
162
|
+
required_scope: The required parsed scope
|
|
163
|
+
resource_id: The specific resource ID being accessed
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
True if the user's scope satisfies the required scope
|
|
167
|
+
|
|
168
|
+
Examples:
|
|
169
|
+
>>> user = parse_scope("system:read")
|
|
170
|
+
>>> required = parse_scope("system:read")
|
|
171
|
+
>>> matches_scope(user, required)
|
|
172
|
+
True
|
|
173
|
+
|
|
174
|
+
>>> user = parse_scope("agents:web-agent:run")
|
|
175
|
+
>>> required = parse_scope("agents:<id>:run")
|
|
176
|
+
>>> matches_scope(user, required, resource_id="web-agent")
|
|
177
|
+
True
|
|
178
|
+
|
|
179
|
+
>>> user = parse_scope("agents:*:run")
|
|
180
|
+
>>> required = parse_scope("agents:<id>:run")
|
|
181
|
+
>>> matches_scope(user, required, resource_id="web-agent")
|
|
182
|
+
True
|
|
183
|
+
"""
|
|
184
|
+
# Admin always matches
|
|
185
|
+
if user_scope.scope_type == "admin":
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
# Unknown scopes don't match anything
|
|
189
|
+
if user_scope.scope_type == "unknown" or required_scope.scope_type == "unknown":
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
# Resource type must match
|
|
193
|
+
if user_scope.resource != required_scope.resource:
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
# Action must match
|
|
197
|
+
if user_scope.action != required_scope.action:
|
|
198
|
+
return False
|
|
199
|
+
|
|
200
|
+
# If required scope has a resource_id, check it
|
|
201
|
+
if required_scope.resource_id:
|
|
202
|
+
# User has wildcard resource access
|
|
203
|
+
if user_scope.is_wildcard_resource:
|
|
204
|
+
return True
|
|
205
|
+
# User has global resource access (no resource_id in user scope)
|
|
206
|
+
if not user_scope.resource_id:
|
|
207
|
+
return True
|
|
208
|
+
# User has specific resource access - must match
|
|
209
|
+
return user_scope.resource_id == resource_id
|
|
210
|
+
|
|
211
|
+
# Required scope is global (no resource_id), user scope matches if:
|
|
212
|
+
# - User has global scope (no resource_id), OR
|
|
213
|
+
# - User has wildcard resource scope
|
|
214
|
+
return not user_scope.resource_id or user_scope.is_wildcard_resource
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def has_required_scopes(
|
|
218
|
+
user_scopes: List[str],
|
|
219
|
+
required_scopes: List[str],
|
|
220
|
+
resource_type: Optional[str] = None,
|
|
221
|
+
resource_id: Optional[str] = None,
|
|
222
|
+
admin_scope: Optional[str] = None,
|
|
223
|
+
) -> bool:
|
|
224
|
+
"""
|
|
225
|
+
Check if user has all required scopes.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
user_scopes: List of scope strings the user has
|
|
229
|
+
required_scopes: List of scope strings required
|
|
230
|
+
resource_type: Type of resource being accessed ("agents", "teams", "workflows")
|
|
231
|
+
resource_id: Specific resource ID being accessed
|
|
232
|
+
admin_scope: The scope string that grants admin access (default: "agent_os:admin")
|
|
233
|
+
|
|
234
|
+
Returns:
|
|
235
|
+
True if user has all required scopes
|
|
236
|
+
|
|
237
|
+
Examples:
|
|
238
|
+
>>> has_required_scopes(
|
|
239
|
+
... ["agents:read"],
|
|
240
|
+
... ["agents:read"],
|
|
241
|
+
... )
|
|
242
|
+
True
|
|
243
|
+
|
|
244
|
+
>>> has_required_scopes(
|
|
245
|
+
... ["agents:web-agent:run"],
|
|
246
|
+
... ["agents:run"],
|
|
247
|
+
... resource_type="agents",
|
|
248
|
+
... resource_id="web-agent"
|
|
249
|
+
... )
|
|
250
|
+
True
|
|
251
|
+
|
|
252
|
+
>>> has_required_scopes(
|
|
253
|
+
... ["agents:*:run"],
|
|
254
|
+
... ["agents:run"],
|
|
255
|
+
... resource_type="agents",
|
|
256
|
+
... resource_id="any-agent"
|
|
257
|
+
... )
|
|
258
|
+
True
|
|
259
|
+
"""
|
|
260
|
+
if not required_scopes:
|
|
261
|
+
return True
|
|
262
|
+
|
|
263
|
+
# Parse user scopes once
|
|
264
|
+
parsed_user_scopes = [parse_scope(scope, admin_scope=admin_scope) for scope in user_scopes]
|
|
265
|
+
|
|
266
|
+
# Check for admin scope
|
|
267
|
+
if any(s.scope_type == "admin" for s in parsed_user_scopes):
|
|
268
|
+
return True
|
|
269
|
+
|
|
270
|
+
# Check each required scope
|
|
271
|
+
for required_scope_str in required_scopes:
|
|
272
|
+
parts = required_scope_str.split(":")
|
|
273
|
+
if len(parts) == 2:
|
|
274
|
+
resource, action = parts
|
|
275
|
+
# Build the required scope based on context
|
|
276
|
+
if resource_id and resource_type:
|
|
277
|
+
# Per-resource scope required
|
|
278
|
+
full_required_scope = f"{resource_type}:<resource-id>:{action}"
|
|
279
|
+
else:
|
|
280
|
+
# Global resource scope required
|
|
281
|
+
full_required_scope = required_scope_str
|
|
282
|
+
|
|
283
|
+
required = parse_scope(full_required_scope, admin_scope=admin_scope)
|
|
284
|
+
else:
|
|
285
|
+
required = parse_scope(required_scope_str, admin_scope=admin_scope)
|
|
286
|
+
|
|
287
|
+
scope_matched = False
|
|
288
|
+
for user_scope in parsed_user_scopes:
|
|
289
|
+
if matches_scope(user_scope, required, resource_id=resource_id):
|
|
290
|
+
scope_matched = True
|
|
291
|
+
break
|
|
292
|
+
|
|
293
|
+
if not scope_matched:
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
return True
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def get_accessible_resource_ids(
|
|
300
|
+
user_scopes: List[str],
|
|
301
|
+
resource_type: str,
|
|
302
|
+
admin_scope: Optional[str] = None,
|
|
303
|
+
) -> Set[str]:
|
|
304
|
+
"""
|
|
305
|
+
Get the set of resource IDs the user has access to.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
user_scopes: List of scope strings the user has
|
|
309
|
+
resource_type: Type of resource ("agents", "teams", "workflows")
|
|
310
|
+
admin_scope: The scope string that grants admin access (default: "agent_os:admin")
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
Set of resource IDs the user can access. Returns {"*"} for wildcard access.
|
|
314
|
+
|
|
315
|
+
Examples:
|
|
316
|
+
>>> get_accessible_resource_ids(
|
|
317
|
+
... ["agents:agent-1:read", "agents:agent-2:read"],
|
|
318
|
+
... "agents"
|
|
319
|
+
... )
|
|
320
|
+
{'agent-1', 'agent-2'}
|
|
321
|
+
|
|
322
|
+
>>> get_accessible_resource_ids(["agents:*:read"], "agents")
|
|
323
|
+
{'*'}
|
|
324
|
+
|
|
325
|
+
>>> get_accessible_resource_ids(["agents:read"], "agents")
|
|
326
|
+
{'*'}
|
|
327
|
+
|
|
328
|
+
>>> get_accessible_resource_ids(["admin"], "agents")
|
|
329
|
+
{'*'}
|
|
330
|
+
"""
|
|
331
|
+
parsed_scopes = [parse_scope(scope, admin_scope=admin_scope) for scope in user_scopes]
|
|
332
|
+
|
|
333
|
+
# Check for admin or global wildcard access
|
|
334
|
+
for scope in parsed_scopes:
|
|
335
|
+
if scope.scope_type == "admin":
|
|
336
|
+
return {"*"}
|
|
337
|
+
|
|
338
|
+
# Check if resource type matches
|
|
339
|
+
if scope.resource == resource_type:
|
|
340
|
+
# Global resource scope (no resource_id) grants access to all
|
|
341
|
+
if not scope.resource_id and scope.action in ["read", "run"]:
|
|
342
|
+
return {"*"}
|
|
343
|
+
# Wildcard resource scope grants access to all
|
|
344
|
+
if scope.is_wildcard_resource and scope.action in ["read", "run"]:
|
|
345
|
+
return {"*"}
|
|
346
|
+
|
|
347
|
+
# Collect specific resource IDs
|
|
348
|
+
accessible_ids: Set[str] = set()
|
|
349
|
+
for scope in parsed_scopes:
|
|
350
|
+
# Check if resource type matches
|
|
351
|
+
if scope.resource == resource_type:
|
|
352
|
+
# Specific resource ID
|
|
353
|
+
if scope.resource_id and not scope.is_wildcard_resource and scope.action in ["read", "run"]:
|
|
354
|
+
accessible_ids.add(scope.resource_id)
|
|
355
|
+
|
|
356
|
+
return accessible_ids
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def get_default_scope_mappings() -> Dict[str, List[str]]:
|
|
360
|
+
"""
|
|
361
|
+
Get default scope mappings for AgentOS endpoints.
|
|
362
|
+
|
|
363
|
+
Returns a dictionary mapping route patterns (with HTTP methods) to required scope templates.
|
|
364
|
+
Format: "METHOD /path/pattern": ["resource:action"]
|
|
365
|
+
"""
|
|
366
|
+
return {
|
|
367
|
+
# System endpoints
|
|
368
|
+
"GET /config": ["system:read"],
|
|
369
|
+
"GET /models": ["system:read"],
|
|
370
|
+
# Agent endpoints
|
|
371
|
+
"GET /agents": ["agents:read"],
|
|
372
|
+
"GET /agents/*": ["agents:read"],
|
|
373
|
+
"POST /agents": ["agents:write"],
|
|
374
|
+
"PATCH /agents/*": ["agents:write"],
|
|
375
|
+
"DELETE /agents/*": ["agents:delete"],
|
|
376
|
+
"POST /agents/*/runs": ["agents:run"],
|
|
377
|
+
"POST /agents/*/runs/*/continue": ["agents:run"],
|
|
378
|
+
"POST /agents/*/runs/*/cancel": ["agents:run"],
|
|
379
|
+
# Team endpoints
|
|
380
|
+
"GET /teams": ["teams:read"],
|
|
381
|
+
"GET /teams/*": ["teams:read"],
|
|
382
|
+
"POST /teams": ["teams:write"],
|
|
383
|
+
"PATCH /teams/*": ["teams:write"],
|
|
384
|
+
"DELETE /teams/*": ["teams:delete"],
|
|
385
|
+
"POST /teams/*/runs": ["teams:run"],
|
|
386
|
+
"POST /teams/*/runs/*/continue": ["teams:run"],
|
|
387
|
+
"POST /teams/*/runs/*/cancel": ["teams:run"],
|
|
388
|
+
# Workflow endpoints
|
|
389
|
+
"GET /workflows": ["workflows:read"],
|
|
390
|
+
"GET /workflows/*": ["workflows:read"],
|
|
391
|
+
"POST /workflows": ["workflows:write"],
|
|
392
|
+
"PATCH /workflows/*": ["workflows:write"],
|
|
393
|
+
"DELETE /workflows/*": ["workflows:delete"],
|
|
394
|
+
"POST /workflows/*/runs": ["workflows:run"],
|
|
395
|
+
"POST /workflows/*/runs/*/continue": ["workflows:run"],
|
|
396
|
+
"POST /workflows/*/runs/*/cancel": ["workflows:run"],
|
|
397
|
+
# Session endpoints
|
|
398
|
+
"GET /sessions": ["sessions:read"],
|
|
399
|
+
"GET /sessions/*": ["sessions:read"],
|
|
400
|
+
"POST /sessions": ["sessions:write"],
|
|
401
|
+
"POST /sessions/*/rename": ["sessions:write"],
|
|
402
|
+
"PATCH /sessions/*": ["sessions:write"],
|
|
403
|
+
"DELETE /sessions": ["sessions:delete"],
|
|
404
|
+
"DELETE /sessions/*": ["sessions:delete"],
|
|
405
|
+
# Memory endpoints
|
|
406
|
+
"GET /memories": ["memories:read"],
|
|
407
|
+
"GET /memories/*": ["memories:read"],
|
|
408
|
+
"GET /memory_topics": ["memories:read"],
|
|
409
|
+
"GET /user_memory_stats": ["memories:read"],
|
|
410
|
+
"POST /memories": ["memories:write"],
|
|
411
|
+
"PATCH /memories/*": ["memories:write"],
|
|
412
|
+
"DELETE /memories": ["memories:delete"],
|
|
413
|
+
"DELETE /memories/*": ["memories:delete"],
|
|
414
|
+
"POST /optimize-memories": ["memories:write"],
|
|
415
|
+
# Knowledge endpoints
|
|
416
|
+
"GET /knowledge/content": ["knowledge:read"],
|
|
417
|
+
"GET /knowledge/content/*": ["knowledge:read"],
|
|
418
|
+
"GET /knowledge/config": ["knowledge:read"],
|
|
419
|
+
"POST /knowledge/content": ["knowledge:write"],
|
|
420
|
+
"PATCH /knowledge/content/*": ["knowledge:write"],
|
|
421
|
+
"POST /knowledge/search": ["knowledge:read"],
|
|
422
|
+
"DELETE /knowledge/content": ["knowledge:delete"],
|
|
423
|
+
"DELETE /knowledge/content/*": ["knowledge:delete"],
|
|
424
|
+
# Metrics endpoints
|
|
425
|
+
"GET /metrics": ["metrics:read"],
|
|
426
|
+
"POST /metrics/refresh": ["metrics:write"],
|
|
427
|
+
# Evaluation endpoints
|
|
428
|
+
"GET /eval-runs": ["evals:read"],
|
|
429
|
+
"GET /eval-runs/*": ["evals:read"],
|
|
430
|
+
"POST /eval-runs": ["evals:write"],
|
|
431
|
+
"PATCH /eval-runs/*": ["evals:write"],
|
|
432
|
+
"DELETE /eval-runs": ["evals:delete"],
|
|
433
|
+
# Trace endpoints
|
|
434
|
+
"GET /traces": ["traces:read"],
|
|
435
|
+
"GET /traces/*": ["traces:read"],
|
|
436
|
+
"GET /trace_session_stats": ["traces:read"],
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def get_scope_value(scope: AgentOSScope) -> str:
|
|
441
|
+
"""
|
|
442
|
+
Get the string value of a scope.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
scope: The AgentOSScope enum value
|
|
446
|
+
|
|
447
|
+
Returns:
|
|
448
|
+
The string value of the scope
|
|
449
|
+
|
|
450
|
+
Example:
|
|
451
|
+
>>> get_scope_value(AgentOSScope.ADMIN)
|
|
452
|
+
'admin'
|
|
453
|
+
"""
|
|
454
|
+
return scope.value
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def get_all_scopes() -> list[str]:
|
|
458
|
+
"""
|
|
459
|
+
Get a list of all available scope strings.
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
List of all scope string values
|
|
463
|
+
|
|
464
|
+
Example:
|
|
465
|
+
>>> scopes = get_all_scopes()
|
|
466
|
+
>>> 'admin' in scopes
|
|
467
|
+
True
|
|
468
|
+
"""
|
|
469
|
+
return [scope.value for scope in AgentOSScope]
|
agno/os/settings.py
CHANGED
|
@@ -20,6 +20,9 @@ class AgnoAPISettings(BaseSettings):
|
|
|
20
20
|
# Authentication settings
|
|
21
21
|
os_security_key: Optional[str] = Field(default=None, description="Bearer token for API authentication")
|
|
22
22
|
|
|
23
|
+
# Authorization flag - when True, JWT middleware handles auth and security key validation is skipped
|
|
24
|
+
authorization_enabled: bool = Field(default=False, description="Whether JWT authorization is enabled")
|
|
25
|
+
|
|
23
26
|
# Cors origin list to allow requests from.
|
|
24
27
|
# This list is set using the set_cors_origin_list validator
|
|
25
28
|
cors_origin_list: Optional[List[str]] = Field(default=None, validate_default=True)
|