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.
Files changed (383) hide show
  1. agno/agent/__init__.py +6 -0
  2. agno/agent/agent.py +5252 -3145
  3. agno/agent/remote.py +525 -0
  4. agno/api/api.py +2 -0
  5. agno/client/__init__.py +3 -0
  6. agno/client/a2a/__init__.py +10 -0
  7. agno/client/a2a/client.py +554 -0
  8. agno/client/a2a/schemas.py +112 -0
  9. agno/client/a2a/utils.py +369 -0
  10. agno/client/os.py +2669 -0
  11. agno/compression/__init__.py +3 -0
  12. agno/compression/manager.py +247 -0
  13. agno/culture/manager.py +2 -2
  14. agno/db/base.py +927 -6
  15. agno/db/dynamo/dynamo.py +788 -2
  16. agno/db/dynamo/schemas.py +128 -0
  17. agno/db/dynamo/utils.py +26 -3
  18. agno/db/firestore/firestore.py +674 -50
  19. agno/db/firestore/schemas.py +41 -0
  20. agno/db/firestore/utils.py +25 -10
  21. agno/db/gcs_json/gcs_json_db.py +506 -3
  22. agno/db/gcs_json/utils.py +14 -2
  23. agno/db/in_memory/in_memory_db.py +203 -4
  24. agno/db/in_memory/utils.py +14 -2
  25. agno/db/json/json_db.py +498 -2
  26. agno/db/json/utils.py +14 -2
  27. agno/db/migrations/manager.py +199 -0
  28. agno/db/migrations/utils.py +19 -0
  29. agno/db/migrations/v1_to_v2.py +54 -16
  30. agno/db/migrations/versions/__init__.py +0 -0
  31. agno/db/migrations/versions/v2_3_0.py +977 -0
  32. agno/db/mongo/async_mongo.py +1013 -39
  33. agno/db/mongo/mongo.py +684 -4
  34. agno/db/mongo/schemas.py +48 -0
  35. agno/db/mongo/utils.py +17 -0
  36. agno/db/mysql/__init__.py +2 -1
  37. agno/db/mysql/async_mysql.py +2958 -0
  38. agno/db/mysql/mysql.py +722 -53
  39. agno/db/mysql/schemas.py +77 -11
  40. agno/db/mysql/utils.py +151 -8
  41. agno/db/postgres/async_postgres.py +1254 -137
  42. agno/db/postgres/postgres.py +2316 -93
  43. agno/db/postgres/schemas.py +153 -21
  44. agno/db/postgres/utils.py +22 -7
  45. agno/db/redis/redis.py +531 -3
  46. agno/db/redis/schemas.py +36 -0
  47. agno/db/redis/utils.py +31 -15
  48. agno/db/schemas/evals.py +1 -0
  49. agno/db/schemas/memory.py +20 -9
  50. agno/db/singlestore/schemas.py +70 -1
  51. agno/db/singlestore/singlestore.py +737 -74
  52. agno/db/singlestore/utils.py +13 -3
  53. agno/db/sqlite/async_sqlite.py +1069 -89
  54. agno/db/sqlite/schemas.py +133 -1
  55. agno/db/sqlite/sqlite.py +2203 -165
  56. agno/db/sqlite/utils.py +21 -11
  57. agno/db/surrealdb/models.py +25 -0
  58. agno/db/surrealdb/surrealdb.py +603 -1
  59. agno/db/utils.py +60 -0
  60. agno/eval/__init__.py +26 -3
  61. agno/eval/accuracy.py +25 -12
  62. agno/eval/agent_as_judge.py +871 -0
  63. agno/eval/base.py +29 -0
  64. agno/eval/performance.py +10 -4
  65. agno/eval/reliability.py +22 -13
  66. agno/eval/utils.py +2 -1
  67. agno/exceptions.py +42 -0
  68. agno/hooks/__init__.py +3 -0
  69. agno/hooks/decorator.py +164 -0
  70. agno/integrations/discord/client.py +13 -2
  71. agno/knowledge/__init__.py +4 -0
  72. agno/knowledge/chunking/code.py +90 -0
  73. agno/knowledge/chunking/document.py +65 -4
  74. agno/knowledge/chunking/fixed.py +4 -1
  75. agno/knowledge/chunking/markdown.py +102 -11
  76. agno/knowledge/chunking/recursive.py +2 -2
  77. agno/knowledge/chunking/semantic.py +130 -48
  78. agno/knowledge/chunking/strategy.py +18 -0
  79. agno/knowledge/embedder/azure_openai.py +0 -1
  80. agno/knowledge/embedder/google.py +1 -1
  81. agno/knowledge/embedder/mistral.py +1 -1
  82. agno/knowledge/embedder/nebius.py +1 -1
  83. agno/knowledge/embedder/openai.py +16 -12
  84. agno/knowledge/filesystem.py +412 -0
  85. agno/knowledge/knowledge.py +4261 -1199
  86. agno/knowledge/protocol.py +134 -0
  87. agno/knowledge/reader/arxiv_reader.py +3 -2
  88. agno/knowledge/reader/base.py +9 -7
  89. agno/knowledge/reader/csv_reader.py +91 -42
  90. agno/knowledge/reader/docx_reader.py +9 -10
  91. agno/knowledge/reader/excel_reader.py +225 -0
  92. agno/knowledge/reader/field_labeled_csv_reader.py +38 -48
  93. agno/knowledge/reader/firecrawl_reader.py +3 -2
  94. agno/knowledge/reader/json_reader.py +16 -22
  95. agno/knowledge/reader/markdown_reader.py +15 -14
  96. agno/knowledge/reader/pdf_reader.py +33 -28
  97. agno/knowledge/reader/pptx_reader.py +9 -10
  98. agno/knowledge/reader/reader_factory.py +135 -1
  99. agno/knowledge/reader/s3_reader.py +8 -16
  100. agno/knowledge/reader/tavily_reader.py +3 -3
  101. agno/knowledge/reader/text_reader.py +15 -14
  102. agno/knowledge/reader/utils/__init__.py +17 -0
  103. agno/knowledge/reader/utils/spreadsheet.py +114 -0
  104. agno/knowledge/reader/web_search_reader.py +8 -65
  105. agno/knowledge/reader/website_reader.py +16 -13
  106. agno/knowledge/reader/wikipedia_reader.py +36 -3
  107. agno/knowledge/reader/youtube_reader.py +3 -2
  108. agno/knowledge/remote_content/__init__.py +33 -0
  109. agno/knowledge/remote_content/config.py +266 -0
  110. agno/knowledge/remote_content/remote_content.py +105 -17
  111. agno/knowledge/utils.py +76 -22
  112. agno/learn/__init__.py +71 -0
  113. agno/learn/config.py +463 -0
  114. agno/learn/curate.py +185 -0
  115. agno/learn/machine.py +725 -0
  116. agno/learn/schemas.py +1114 -0
  117. agno/learn/stores/__init__.py +38 -0
  118. agno/learn/stores/decision_log.py +1156 -0
  119. agno/learn/stores/entity_memory.py +3275 -0
  120. agno/learn/stores/learned_knowledge.py +1583 -0
  121. agno/learn/stores/protocol.py +117 -0
  122. agno/learn/stores/session_context.py +1217 -0
  123. agno/learn/stores/user_memory.py +1495 -0
  124. agno/learn/stores/user_profile.py +1220 -0
  125. agno/learn/utils.py +209 -0
  126. agno/media.py +22 -6
  127. agno/memory/__init__.py +14 -1
  128. agno/memory/manager.py +223 -8
  129. agno/memory/strategies/__init__.py +15 -0
  130. agno/memory/strategies/base.py +66 -0
  131. agno/memory/strategies/summarize.py +196 -0
  132. agno/memory/strategies/types.py +37 -0
  133. agno/models/aimlapi/aimlapi.py +17 -0
  134. agno/models/anthropic/claude.py +434 -59
  135. agno/models/aws/bedrock.py +121 -20
  136. agno/models/aws/claude.py +131 -274
  137. agno/models/azure/ai_foundry.py +10 -6
  138. agno/models/azure/openai_chat.py +33 -10
  139. agno/models/base.py +1162 -561
  140. agno/models/cerebras/cerebras.py +120 -24
  141. agno/models/cerebras/cerebras_openai.py +21 -2
  142. agno/models/cohere/chat.py +65 -6
  143. agno/models/cometapi/cometapi.py +18 -1
  144. agno/models/dashscope/dashscope.py +2 -3
  145. agno/models/deepinfra/deepinfra.py +18 -1
  146. agno/models/deepseek/deepseek.py +69 -3
  147. agno/models/fireworks/fireworks.py +18 -1
  148. agno/models/google/gemini.py +959 -89
  149. agno/models/google/utils.py +22 -0
  150. agno/models/groq/groq.py +48 -18
  151. agno/models/huggingface/huggingface.py +17 -6
  152. agno/models/ibm/watsonx.py +16 -6
  153. agno/models/internlm/internlm.py +18 -1
  154. agno/models/langdb/langdb.py +13 -1
  155. agno/models/litellm/chat.py +88 -9
  156. agno/models/litellm/litellm_openai.py +18 -1
  157. agno/models/message.py +24 -5
  158. agno/models/meta/llama.py +40 -13
  159. agno/models/meta/llama_openai.py +22 -21
  160. agno/models/metrics.py +12 -0
  161. agno/models/mistral/mistral.py +8 -4
  162. agno/models/n1n/__init__.py +3 -0
  163. agno/models/n1n/n1n.py +57 -0
  164. agno/models/nebius/nebius.py +6 -7
  165. agno/models/nvidia/nvidia.py +20 -3
  166. agno/models/ollama/__init__.py +2 -0
  167. agno/models/ollama/chat.py +17 -6
  168. agno/models/ollama/responses.py +100 -0
  169. agno/models/openai/__init__.py +2 -0
  170. agno/models/openai/chat.py +117 -26
  171. agno/models/openai/open_responses.py +46 -0
  172. agno/models/openai/responses.py +110 -32
  173. agno/models/openrouter/__init__.py +2 -0
  174. agno/models/openrouter/openrouter.py +67 -2
  175. agno/models/openrouter/responses.py +146 -0
  176. agno/models/perplexity/perplexity.py +19 -1
  177. agno/models/portkey/portkey.py +7 -6
  178. agno/models/requesty/requesty.py +19 -2
  179. agno/models/response.py +20 -2
  180. agno/models/sambanova/sambanova.py +20 -3
  181. agno/models/siliconflow/siliconflow.py +19 -2
  182. agno/models/together/together.py +20 -3
  183. agno/models/vercel/v0.py +20 -3
  184. agno/models/vertexai/claude.py +124 -4
  185. agno/models/vllm/vllm.py +19 -14
  186. agno/models/xai/xai.py +19 -2
  187. agno/os/app.py +467 -137
  188. agno/os/auth.py +253 -5
  189. agno/os/config.py +22 -0
  190. agno/os/interfaces/a2a/a2a.py +7 -6
  191. agno/os/interfaces/a2a/router.py +635 -26
  192. agno/os/interfaces/a2a/utils.py +32 -33
  193. agno/os/interfaces/agui/agui.py +5 -3
  194. agno/os/interfaces/agui/router.py +26 -16
  195. agno/os/interfaces/agui/utils.py +97 -57
  196. agno/os/interfaces/base.py +7 -7
  197. agno/os/interfaces/slack/router.py +16 -7
  198. agno/os/interfaces/slack/slack.py +7 -7
  199. agno/os/interfaces/whatsapp/router.py +35 -7
  200. agno/os/interfaces/whatsapp/security.py +3 -1
  201. agno/os/interfaces/whatsapp/whatsapp.py +11 -8
  202. agno/os/managers.py +326 -0
  203. agno/os/mcp.py +652 -79
  204. agno/os/middleware/__init__.py +4 -0
  205. agno/os/middleware/jwt.py +718 -115
  206. agno/os/middleware/trailing_slash.py +27 -0
  207. agno/os/router.py +105 -1558
  208. agno/os/routers/agents/__init__.py +3 -0
  209. agno/os/routers/agents/router.py +655 -0
  210. agno/os/routers/agents/schema.py +288 -0
  211. agno/os/routers/components/__init__.py +3 -0
  212. agno/os/routers/components/components.py +475 -0
  213. agno/os/routers/database.py +155 -0
  214. agno/os/routers/evals/evals.py +111 -18
  215. agno/os/routers/evals/schemas.py +38 -5
  216. agno/os/routers/evals/utils.py +80 -11
  217. agno/os/routers/health.py +3 -3
  218. agno/os/routers/knowledge/knowledge.py +284 -35
  219. agno/os/routers/knowledge/schemas.py +14 -2
  220. agno/os/routers/memory/memory.py +274 -11
  221. agno/os/routers/memory/schemas.py +44 -3
  222. agno/os/routers/metrics/metrics.py +30 -15
  223. agno/os/routers/metrics/schemas.py +10 -6
  224. agno/os/routers/registry/__init__.py +3 -0
  225. agno/os/routers/registry/registry.py +337 -0
  226. agno/os/routers/session/session.py +143 -14
  227. agno/os/routers/teams/__init__.py +3 -0
  228. agno/os/routers/teams/router.py +550 -0
  229. agno/os/routers/teams/schema.py +280 -0
  230. agno/os/routers/traces/__init__.py +3 -0
  231. agno/os/routers/traces/schemas.py +414 -0
  232. agno/os/routers/traces/traces.py +549 -0
  233. agno/os/routers/workflows/__init__.py +3 -0
  234. agno/os/routers/workflows/router.py +757 -0
  235. agno/os/routers/workflows/schema.py +139 -0
  236. agno/os/schema.py +157 -584
  237. agno/os/scopes.py +469 -0
  238. agno/os/settings.py +3 -0
  239. agno/os/utils.py +574 -185
  240. agno/reasoning/anthropic.py +85 -1
  241. agno/reasoning/azure_ai_foundry.py +93 -1
  242. agno/reasoning/deepseek.py +102 -2
  243. agno/reasoning/default.py +6 -7
  244. agno/reasoning/gemini.py +87 -3
  245. agno/reasoning/groq.py +109 -2
  246. agno/reasoning/helpers.py +6 -7
  247. agno/reasoning/manager.py +1238 -0
  248. agno/reasoning/ollama.py +93 -1
  249. agno/reasoning/openai.py +115 -1
  250. agno/reasoning/vertexai.py +85 -1
  251. agno/registry/__init__.py +3 -0
  252. agno/registry/registry.py +68 -0
  253. agno/remote/__init__.py +3 -0
  254. agno/remote/base.py +581 -0
  255. agno/run/__init__.py +2 -4
  256. agno/run/agent.py +134 -19
  257. agno/run/base.py +49 -1
  258. agno/run/cancel.py +65 -52
  259. agno/run/cancellation_management/__init__.py +9 -0
  260. agno/run/cancellation_management/base.py +78 -0
  261. agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
  262. agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
  263. agno/run/requirement.py +181 -0
  264. agno/run/team.py +111 -19
  265. agno/run/workflow.py +2 -1
  266. agno/session/agent.py +57 -92
  267. agno/session/summary.py +1 -1
  268. agno/session/team.py +62 -115
  269. agno/session/workflow.py +353 -57
  270. agno/skills/__init__.py +17 -0
  271. agno/skills/agent_skills.py +377 -0
  272. agno/skills/errors.py +32 -0
  273. agno/skills/loaders/__init__.py +4 -0
  274. agno/skills/loaders/base.py +27 -0
  275. agno/skills/loaders/local.py +216 -0
  276. agno/skills/skill.py +65 -0
  277. agno/skills/utils.py +107 -0
  278. agno/skills/validator.py +277 -0
  279. agno/table.py +10 -0
  280. agno/team/__init__.py +5 -1
  281. agno/team/remote.py +447 -0
  282. agno/team/team.py +3769 -2202
  283. agno/tools/brandfetch.py +27 -18
  284. agno/tools/browserbase.py +225 -16
  285. agno/tools/crawl4ai.py +3 -0
  286. agno/tools/duckduckgo.py +25 -71
  287. agno/tools/exa.py +0 -21
  288. agno/tools/file.py +14 -13
  289. agno/tools/file_generation.py +12 -6
  290. agno/tools/firecrawl.py +15 -7
  291. agno/tools/function.py +94 -113
  292. agno/tools/google_bigquery.py +11 -2
  293. agno/tools/google_drive.py +4 -3
  294. agno/tools/knowledge.py +9 -4
  295. agno/tools/mcp/mcp.py +301 -18
  296. agno/tools/mcp/multi_mcp.py +269 -14
  297. agno/tools/mem0.py +11 -10
  298. agno/tools/memory.py +47 -46
  299. agno/tools/mlx_transcribe.py +10 -7
  300. agno/tools/models/nebius.py +5 -5
  301. agno/tools/models_labs.py +20 -10
  302. agno/tools/nano_banana.py +151 -0
  303. agno/tools/parallel.py +0 -7
  304. agno/tools/postgres.py +76 -36
  305. agno/tools/python.py +14 -6
  306. agno/tools/reasoning.py +30 -23
  307. agno/tools/redshift.py +406 -0
  308. agno/tools/shopify.py +1519 -0
  309. agno/tools/spotify.py +919 -0
  310. agno/tools/tavily.py +4 -1
  311. agno/tools/toolkit.py +253 -18
  312. agno/tools/websearch.py +93 -0
  313. agno/tools/website.py +1 -1
  314. agno/tools/wikipedia.py +1 -1
  315. agno/tools/workflow.py +56 -48
  316. agno/tools/yfinance.py +12 -11
  317. agno/tracing/__init__.py +12 -0
  318. agno/tracing/exporter.py +161 -0
  319. agno/tracing/schemas.py +276 -0
  320. agno/tracing/setup.py +112 -0
  321. agno/utils/agent.py +251 -10
  322. agno/utils/cryptography.py +22 -0
  323. agno/utils/dttm.py +33 -0
  324. agno/utils/events.py +264 -7
  325. agno/utils/hooks.py +111 -3
  326. agno/utils/http.py +161 -2
  327. agno/utils/mcp.py +49 -8
  328. agno/utils/media.py +22 -1
  329. agno/utils/models/ai_foundry.py +9 -2
  330. agno/utils/models/claude.py +20 -5
  331. agno/utils/models/cohere.py +9 -2
  332. agno/utils/models/llama.py +9 -2
  333. agno/utils/models/mistral.py +4 -2
  334. agno/utils/os.py +0 -0
  335. agno/utils/print_response/agent.py +99 -16
  336. agno/utils/print_response/team.py +223 -24
  337. agno/utils/print_response/workflow.py +0 -2
  338. agno/utils/prompts.py +8 -6
  339. agno/utils/remote.py +23 -0
  340. agno/utils/response.py +1 -13
  341. agno/utils/string.py +91 -2
  342. agno/utils/team.py +62 -12
  343. agno/utils/tokens.py +657 -0
  344. agno/vectordb/base.py +15 -2
  345. agno/vectordb/cassandra/cassandra.py +1 -1
  346. agno/vectordb/chroma/__init__.py +2 -1
  347. agno/vectordb/chroma/chromadb.py +468 -23
  348. agno/vectordb/clickhouse/clickhousedb.py +1 -1
  349. agno/vectordb/couchbase/couchbase.py +6 -2
  350. agno/vectordb/lancedb/lance_db.py +7 -38
  351. agno/vectordb/lightrag/lightrag.py +7 -6
  352. agno/vectordb/milvus/milvus.py +118 -84
  353. agno/vectordb/mongodb/__init__.py +2 -1
  354. agno/vectordb/mongodb/mongodb.py +14 -31
  355. agno/vectordb/pgvector/pgvector.py +120 -66
  356. agno/vectordb/pineconedb/pineconedb.py +2 -19
  357. agno/vectordb/qdrant/__init__.py +2 -1
  358. agno/vectordb/qdrant/qdrant.py +33 -56
  359. agno/vectordb/redis/__init__.py +2 -1
  360. agno/vectordb/redis/redisdb.py +19 -31
  361. agno/vectordb/singlestore/singlestore.py +17 -9
  362. agno/vectordb/surrealdb/surrealdb.py +2 -38
  363. agno/vectordb/weaviate/__init__.py +2 -1
  364. agno/vectordb/weaviate/weaviate.py +7 -3
  365. agno/workflow/__init__.py +5 -1
  366. agno/workflow/agent.py +2 -2
  367. agno/workflow/condition.py +12 -10
  368. agno/workflow/loop.py +28 -9
  369. agno/workflow/parallel.py +21 -13
  370. agno/workflow/remote.py +362 -0
  371. agno/workflow/router.py +12 -9
  372. agno/workflow/step.py +261 -36
  373. agno/workflow/steps.py +12 -8
  374. agno/workflow/types.py +40 -77
  375. agno/workflow/workflow.py +939 -213
  376. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/METADATA +134 -181
  377. agno-2.4.3.dist-info/RECORD +677 -0
  378. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/WHEEL +1 -1
  379. agno/tools/googlesearch.py +0 -98
  380. agno/tools/memori.py +0 -339
  381. agno-2.2.13.dist-info/RECORD +0 -575
  382. {agno-2.2.13.dist-info → agno-2.4.3.dist-info}/licenses/LICENSE +0 -0
  383. {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 fastapi import Depends, HTTPException
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
@@ -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
  ):