agno 2.1.2__py3-none-any.whl → 2.3.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (314) hide show
  1. agno/agent/agent.py +5540 -2273
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/compression/__init__.py +3 -0
  5. agno/compression/manager.py +247 -0
  6. agno/culture/__init__.py +3 -0
  7. agno/culture/manager.py +956 -0
  8. agno/db/async_postgres/__init__.py +3 -0
  9. agno/db/base.py +689 -6
  10. agno/db/dynamo/dynamo.py +933 -37
  11. agno/db/dynamo/schemas.py +174 -10
  12. agno/db/dynamo/utils.py +63 -4
  13. agno/db/firestore/firestore.py +831 -9
  14. agno/db/firestore/schemas.py +51 -0
  15. agno/db/firestore/utils.py +102 -4
  16. agno/db/gcs_json/gcs_json_db.py +660 -12
  17. agno/db/gcs_json/utils.py +60 -26
  18. agno/db/in_memory/in_memory_db.py +287 -14
  19. agno/db/in_memory/utils.py +60 -2
  20. agno/db/json/json_db.py +590 -14
  21. agno/db/json/utils.py +60 -26
  22. agno/db/migrations/manager.py +199 -0
  23. agno/db/migrations/v1_to_v2.py +43 -13
  24. agno/db/migrations/versions/__init__.py +0 -0
  25. agno/db/migrations/versions/v2_3_0.py +938 -0
  26. agno/db/mongo/__init__.py +15 -1
  27. agno/db/mongo/async_mongo.py +2760 -0
  28. agno/db/mongo/mongo.py +879 -11
  29. agno/db/mongo/schemas.py +42 -0
  30. agno/db/mongo/utils.py +80 -8
  31. agno/db/mysql/__init__.py +2 -1
  32. agno/db/mysql/async_mysql.py +2912 -0
  33. agno/db/mysql/mysql.py +946 -68
  34. agno/db/mysql/schemas.py +72 -10
  35. agno/db/mysql/utils.py +198 -7
  36. agno/db/postgres/__init__.py +2 -1
  37. agno/db/postgres/async_postgres.py +2579 -0
  38. agno/db/postgres/postgres.py +942 -57
  39. agno/db/postgres/schemas.py +81 -18
  40. agno/db/postgres/utils.py +164 -2
  41. agno/db/redis/redis.py +671 -7
  42. agno/db/redis/schemas.py +50 -0
  43. agno/db/redis/utils.py +65 -7
  44. agno/db/schemas/__init__.py +2 -1
  45. agno/db/schemas/culture.py +120 -0
  46. agno/db/schemas/evals.py +1 -0
  47. agno/db/schemas/memory.py +17 -2
  48. agno/db/singlestore/schemas.py +63 -0
  49. agno/db/singlestore/singlestore.py +949 -83
  50. agno/db/singlestore/utils.py +60 -2
  51. agno/db/sqlite/__init__.py +2 -1
  52. agno/db/sqlite/async_sqlite.py +2911 -0
  53. agno/db/sqlite/schemas.py +62 -0
  54. agno/db/sqlite/sqlite.py +965 -46
  55. agno/db/sqlite/utils.py +169 -8
  56. agno/db/surrealdb/__init__.py +3 -0
  57. agno/db/surrealdb/metrics.py +292 -0
  58. agno/db/surrealdb/models.py +334 -0
  59. agno/db/surrealdb/queries.py +71 -0
  60. agno/db/surrealdb/surrealdb.py +1908 -0
  61. agno/db/surrealdb/utils.py +147 -0
  62. agno/db/utils.py +2 -0
  63. agno/eval/__init__.py +10 -0
  64. agno/eval/accuracy.py +75 -55
  65. agno/eval/agent_as_judge.py +861 -0
  66. agno/eval/base.py +29 -0
  67. agno/eval/performance.py +16 -7
  68. agno/eval/reliability.py +28 -16
  69. agno/eval/utils.py +35 -17
  70. agno/exceptions.py +27 -2
  71. agno/filters.py +354 -0
  72. agno/guardrails/prompt_injection.py +1 -0
  73. agno/hooks/__init__.py +3 -0
  74. agno/hooks/decorator.py +164 -0
  75. agno/integrations/discord/client.py +1 -1
  76. agno/knowledge/chunking/agentic.py +13 -10
  77. agno/knowledge/chunking/fixed.py +4 -1
  78. agno/knowledge/chunking/semantic.py +9 -4
  79. agno/knowledge/chunking/strategy.py +59 -15
  80. agno/knowledge/embedder/fastembed.py +1 -1
  81. agno/knowledge/embedder/nebius.py +1 -1
  82. agno/knowledge/embedder/ollama.py +8 -0
  83. agno/knowledge/embedder/openai.py +8 -8
  84. agno/knowledge/embedder/sentence_transformer.py +6 -2
  85. agno/knowledge/embedder/vllm.py +262 -0
  86. agno/knowledge/knowledge.py +1618 -318
  87. agno/knowledge/reader/base.py +6 -2
  88. agno/knowledge/reader/csv_reader.py +8 -10
  89. agno/knowledge/reader/docx_reader.py +5 -6
  90. agno/knowledge/reader/field_labeled_csv_reader.py +16 -20
  91. agno/knowledge/reader/json_reader.py +5 -4
  92. agno/knowledge/reader/markdown_reader.py +8 -8
  93. agno/knowledge/reader/pdf_reader.py +17 -19
  94. agno/knowledge/reader/pptx_reader.py +101 -0
  95. agno/knowledge/reader/reader_factory.py +32 -3
  96. agno/knowledge/reader/s3_reader.py +3 -3
  97. agno/knowledge/reader/tavily_reader.py +193 -0
  98. agno/knowledge/reader/text_reader.py +22 -10
  99. agno/knowledge/reader/web_search_reader.py +1 -48
  100. agno/knowledge/reader/website_reader.py +10 -10
  101. agno/knowledge/reader/wikipedia_reader.py +33 -1
  102. agno/knowledge/types.py +1 -0
  103. agno/knowledge/utils.py +72 -7
  104. agno/media.py +22 -6
  105. agno/memory/__init__.py +14 -1
  106. agno/memory/manager.py +544 -83
  107. agno/memory/strategies/__init__.py +15 -0
  108. agno/memory/strategies/base.py +66 -0
  109. agno/memory/strategies/summarize.py +196 -0
  110. agno/memory/strategies/types.py +37 -0
  111. agno/models/aimlapi/aimlapi.py +17 -0
  112. agno/models/anthropic/claude.py +515 -40
  113. agno/models/aws/bedrock.py +102 -21
  114. agno/models/aws/claude.py +131 -274
  115. agno/models/azure/ai_foundry.py +41 -19
  116. agno/models/azure/openai_chat.py +39 -8
  117. agno/models/base.py +1249 -525
  118. agno/models/cerebras/cerebras.py +91 -21
  119. agno/models/cerebras/cerebras_openai.py +21 -2
  120. agno/models/cohere/chat.py +40 -6
  121. agno/models/cometapi/cometapi.py +18 -1
  122. agno/models/dashscope/dashscope.py +2 -3
  123. agno/models/deepinfra/deepinfra.py +18 -1
  124. agno/models/deepseek/deepseek.py +69 -3
  125. agno/models/fireworks/fireworks.py +18 -1
  126. agno/models/google/gemini.py +877 -80
  127. agno/models/google/utils.py +22 -0
  128. agno/models/groq/groq.py +51 -18
  129. agno/models/huggingface/huggingface.py +17 -6
  130. agno/models/ibm/watsonx.py +16 -6
  131. agno/models/internlm/internlm.py +18 -1
  132. agno/models/langdb/langdb.py +13 -1
  133. agno/models/litellm/chat.py +44 -9
  134. agno/models/litellm/litellm_openai.py +18 -1
  135. agno/models/message.py +28 -5
  136. agno/models/meta/llama.py +47 -14
  137. agno/models/meta/llama_openai.py +22 -17
  138. agno/models/mistral/mistral.py +8 -4
  139. agno/models/nebius/nebius.py +6 -7
  140. agno/models/nvidia/nvidia.py +20 -3
  141. agno/models/ollama/chat.py +24 -8
  142. agno/models/openai/chat.py +104 -29
  143. agno/models/openai/responses.py +101 -81
  144. agno/models/openrouter/openrouter.py +60 -3
  145. agno/models/perplexity/perplexity.py +17 -1
  146. agno/models/portkey/portkey.py +7 -6
  147. agno/models/requesty/requesty.py +24 -4
  148. agno/models/response.py +73 -2
  149. agno/models/sambanova/sambanova.py +20 -3
  150. agno/models/siliconflow/siliconflow.py +19 -2
  151. agno/models/together/together.py +20 -3
  152. agno/models/utils.py +254 -8
  153. agno/models/vercel/v0.py +20 -3
  154. agno/models/vertexai/__init__.py +0 -0
  155. agno/models/vertexai/claude.py +190 -0
  156. agno/models/vllm/vllm.py +19 -14
  157. agno/models/xai/xai.py +19 -2
  158. agno/os/app.py +549 -152
  159. agno/os/auth.py +190 -3
  160. agno/os/config.py +23 -0
  161. agno/os/interfaces/a2a/router.py +8 -11
  162. agno/os/interfaces/a2a/utils.py +1 -1
  163. agno/os/interfaces/agui/router.py +18 -3
  164. agno/os/interfaces/agui/utils.py +152 -39
  165. agno/os/interfaces/slack/router.py +55 -37
  166. agno/os/interfaces/slack/slack.py +9 -1
  167. agno/os/interfaces/whatsapp/router.py +0 -1
  168. agno/os/interfaces/whatsapp/security.py +3 -1
  169. agno/os/mcp.py +110 -52
  170. agno/os/middleware/__init__.py +2 -0
  171. agno/os/middleware/jwt.py +676 -112
  172. agno/os/router.py +40 -1478
  173. agno/os/routers/agents/__init__.py +3 -0
  174. agno/os/routers/agents/router.py +599 -0
  175. agno/os/routers/agents/schema.py +261 -0
  176. agno/os/routers/evals/evals.py +96 -39
  177. agno/os/routers/evals/schemas.py +65 -33
  178. agno/os/routers/evals/utils.py +80 -10
  179. agno/os/routers/health.py +10 -4
  180. agno/os/routers/knowledge/knowledge.py +196 -38
  181. agno/os/routers/knowledge/schemas.py +82 -22
  182. agno/os/routers/memory/memory.py +279 -52
  183. agno/os/routers/memory/schemas.py +46 -17
  184. agno/os/routers/metrics/metrics.py +20 -8
  185. agno/os/routers/metrics/schemas.py +16 -16
  186. agno/os/routers/session/session.py +462 -34
  187. agno/os/routers/teams/__init__.py +3 -0
  188. agno/os/routers/teams/router.py +512 -0
  189. agno/os/routers/teams/schema.py +257 -0
  190. agno/os/routers/traces/__init__.py +3 -0
  191. agno/os/routers/traces/schemas.py +414 -0
  192. agno/os/routers/traces/traces.py +499 -0
  193. agno/os/routers/workflows/__init__.py +3 -0
  194. agno/os/routers/workflows/router.py +624 -0
  195. agno/os/routers/workflows/schema.py +75 -0
  196. agno/os/schema.py +256 -693
  197. agno/os/scopes.py +469 -0
  198. agno/os/utils.py +514 -36
  199. agno/reasoning/anthropic.py +80 -0
  200. agno/reasoning/gemini.py +73 -0
  201. agno/reasoning/openai.py +5 -0
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +155 -32
  205. agno/run/base.py +55 -3
  206. agno/run/requirement.py +181 -0
  207. agno/run/team.py +125 -38
  208. agno/run/workflow.py +72 -18
  209. agno/session/agent.py +102 -89
  210. agno/session/summary.py +56 -15
  211. agno/session/team.py +164 -90
  212. agno/session/workflow.py +405 -40
  213. agno/table.py +10 -0
  214. agno/team/team.py +3974 -1903
  215. agno/tools/dalle.py +2 -4
  216. agno/tools/eleven_labs.py +23 -25
  217. agno/tools/exa.py +21 -16
  218. agno/tools/file.py +153 -23
  219. agno/tools/file_generation.py +16 -10
  220. agno/tools/firecrawl.py +15 -7
  221. agno/tools/function.py +193 -38
  222. agno/tools/gmail.py +238 -14
  223. agno/tools/google_drive.py +271 -0
  224. agno/tools/googlecalendar.py +36 -8
  225. agno/tools/googlesheets.py +20 -5
  226. agno/tools/jira.py +20 -0
  227. agno/tools/mcp/__init__.py +10 -0
  228. agno/tools/mcp/mcp.py +331 -0
  229. agno/tools/mcp/multi_mcp.py +347 -0
  230. agno/tools/mcp/params.py +24 -0
  231. agno/tools/mcp_toolbox.py +3 -3
  232. agno/tools/models/nebius.py +5 -5
  233. agno/tools/models_labs.py +20 -10
  234. agno/tools/nano_banana.py +151 -0
  235. agno/tools/notion.py +204 -0
  236. agno/tools/parallel.py +314 -0
  237. agno/tools/postgres.py +76 -36
  238. agno/tools/redshift.py +406 -0
  239. agno/tools/scrapegraph.py +1 -1
  240. agno/tools/shopify.py +1519 -0
  241. agno/tools/slack.py +18 -3
  242. agno/tools/spotify.py +919 -0
  243. agno/tools/tavily.py +146 -0
  244. agno/tools/toolkit.py +25 -0
  245. agno/tools/workflow.py +8 -1
  246. agno/tools/yfinance.py +12 -11
  247. agno/tracing/__init__.py +12 -0
  248. agno/tracing/exporter.py +157 -0
  249. agno/tracing/schemas.py +276 -0
  250. agno/tracing/setup.py +111 -0
  251. agno/utils/agent.py +938 -0
  252. agno/utils/cryptography.py +22 -0
  253. agno/utils/dttm.py +33 -0
  254. agno/utils/events.py +151 -3
  255. agno/utils/gemini.py +15 -5
  256. agno/utils/hooks.py +118 -4
  257. agno/utils/http.py +113 -2
  258. agno/utils/knowledge.py +12 -5
  259. agno/utils/log.py +1 -0
  260. agno/utils/mcp.py +92 -2
  261. agno/utils/media.py +187 -1
  262. agno/utils/merge_dict.py +3 -3
  263. agno/utils/message.py +60 -0
  264. agno/utils/models/ai_foundry.py +9 -2
  265. agno/utils/models/claude.py +49 -14
  266. agno/utils/models/cohere.py +9 -2
  267. agno/utils/models/llama.py +9 -2
  268. agno/utils/models/mistral.py +4 -2
  269. agno/utils/print_response/agent.py +109 -16
  270. agno/utils/print_response/team.py +223 -30
  271. agno/utils/print_response/workflow.py +251 -34
  272. agno/utils/streamlit.py +1 -1
  273. agno/utils/team.py +98 -9
  274. agno/utils/tokens.py +657 -0
  275. agno/vectordb/base.py +39 -7
  276. agno/vectordb/cassandra/cassandra.py +21 -5
  277. agno/vectordb/chroma/chromadb.py +43 -12
  278. agno/vectordb/clickhouse/clickhousedb.py +21 -5
  279. agno/vectordb/couchbase/couchbase.py +29 -5
  280. agno/vectordb/lancedb/lance_db.py +92 -181
  281. agno/vectordb/langchaindb/langchaindb.py +24 -4
  282. agno/vectordb/lightrag/lightrag.py +17 -3
  283. agno/vectordb/llamaindex/llamaindexdb.py +25 -5
  284. agno/vectordb/milvus/milvus.py +50 -37
  285. agno/vectordb/mongodb/__init__.py +7 -1
  286. agno/vectordb/mongodb/mongodb.py +36 -30
  287. agno/vectordb/pgvector/pgvector.py +201 -77
  288. agno/vectordb/pineconedb/pineconedb.py +41 -23
  289. agno/vectordb/qdrant/qdrant.py +67 -54
  290. agno/vectordb/redis/__init__.py +9 -0
  291. agno/vectordb/redis/redisdb.py +682 -0
  292. agno/vectordb/singlestore/singlestore.py +50 -29
  293. agno/vectordb/surrealdb/surrealdb.py +31 -41
  294. agno/vectordb/upstashdb/upstashdb.py +34 -6
  295. agno/vectordb/weaviate/weaviate.py +53 -14
  296. agno/workflow/__init__.py +2 -0
  297. agno/workflow/agent.py +299 -0
  298. agno/workflow/condition.py +120 -18
  299. agno/workflow/loop.py +77 -10
  300. agno/workflow/parallel.py +231 -143
  301. agno/workflow/router.py +118 -17
  302. agno/workflow/step.py +609 -170
  303. agno/workflow/steps.py +73 -6
  304. agno/workflow/types.py +96 -21
  305. agno/workflow/workflow.py +2039 -262
  306. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/METADATA +201 -66
  307. agno-2.3.13.dist-info/RECORD +613 -0
  308. agno/tools/googlesearch.py +0 -98
  309. agno/tools/mcp.py +0 -679
  310. agno/tools/memori.py +0 -339
  311. agno-2.1.2.dist-info/RECORD +0 -543
  312. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/WHEEL +0 -0
  313. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/licenses/LICENSE +0 -0
  314. {agno-2.1.2.dist-info → agno-2.3.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,257 @@
1
+ from typing import Any, Dict, List, Optional, Union
2
+ from uuid import uuid4
3
+
4
+ from pydantic import BaseModel
5
+
6
+ from agno.agent import Agent
7
+ from agno.os.routers.agents.schema import AgentResponse
8
+ from agno.os.schema import ModelResponse
9
+ from agno.os.utils import (
10
+ format_team_tools,
11
+ get_team_input_schema_dict,
12
+ )
13
+ from agno.run import RunContext
14
+ from agno.run.team import TeamRunOutput
15
+ from agno.session import TeamSession
16
+ from agno.team.team import Team
17
+ from agno.utils.agent import aexecute_instructions, aexecute_system_message
18
+
19
+
20
+ class TeamResponse(BaseModel):
21
+ id: Optional[str] = None
22
+ name: Optional[str] = None
23
+ db_id: Optional[str] = None
24
+ description: Optional[str] = None
25
+ model: Optional[ModelResponse] = None
26
+ tools: Optional[Dict[str, Any]] = None
27
+ sessions: Optional[Dict[str, Any]] = None
28
+ knowledge: Optional[Dict[str, Any]] = None
29
+ memory: Optional[Dict[str, Any]] = None
30
+ reasoning: Optional[Dict[str, Any]] = None
31
+ default_tools: Optional[Dict[str, Any]] = None
32
+ system_message: Optional[Dict[str, Any]] = None
33
+ response_settings: Optional[Dict[str, Any]] = None
34
+ introduction: Optional[str] = None
35
+ streaming: Optional[Dict[str, Any]] = None
36
+ members: Optional[List[Union[AgentResponse, "TeamResponse"]]] = None
37
+ metadata: Optional[Dict[str, Any]] = None
38
+ input_schema: Optional[Dict[str, Any]] = None
39
+
40
+ @classmethod
41
+ async def from_team(cls, team: Team) -> "TeamResponse":
42
+ def filter_meaningful_config(d: Dict[str, Any], defaults: Dict[str, Any]) -> Optional[Dict[str, Any]]:
43
+ """Filter out fields that match their default values, keeping only meaningful user configurations"""
44
+ filtered = {}
45
+ for key, value in d.items():
46
+ if value is None:
47
+ continue
48
+ # Skip if value matches the default exactly
49
+ if key in defaults and value == defaults[key]:
50
+ continue
51
+ # Keep non-default values
52
+ filtered[key] = value
53
+ return filtered if filtered else None
54
+
55
+ # Define default values for filtering (similar to agent defaults)
56
+ team_defaults = {
57
+ # Sessions defaults
58
+ "add_history_to_context": False,
59
+ "num_history_runs": 3,
60
+ "enable_session_summaries": False,
61
+ "cache_session": False,
62
+ # Knowledge defaults
63
+ "add_references": False,
64
+ "references_format": "json",
65
+ "enable_agentic_knowledge_filters": False,
66
+ # Memory defaults
67
+ "enable_agentic_memory": False,
68
+ "enable_user_memories": False,
69
+ # Reasoning defaults
70
+ "reasoning": False,
71
+ "reasoning_min_steps": 1,
72
+ "reasoning_max_steps": 10,
73
+ # Default tools defaults
74
+ "search_knowledge": True,
75
+ "read_chat_history": False,
76
+ "get_member_information_tool": False,
77
+ # System message defaults
78
+ "system_message_role": "system",
79
+ "markdown": False,
80
+ "add_datetime_to_context": False,
81
+ "add_location_to_context": False,
82
+ "resolve_in_context": True,
83
+ # Response settings defaults
84
+ "parse_response": True,
85
+ "use_json_mode": False,
86
+ # Streaming defaults
87
+ "stream_events": False,
88
+ "stream_intermediate_steps": False,
89
+ "stream_member_events": False,
90
+ }
91
+
92
+ run_id = str(uuid4())
93
+ session_id = str(uuid4())
94
+ _tools = team._determine_tools_for_model(
95
+ model=team.model, # type: ignore
96
+ session=TeamSession(session_id=session_id, session_data={}),
97
+ run_response=TeamRunOutput(run_id=run_id),
98
+ run_context=RunContext(run_id=run_id, session_id=session_id, session_state={}),
99
+ async_mode=True,
100
+ team_run_context={},
101
+ check_mcp_tools=False,
102
+ )
103
+ team_tools = _tools
104
+ formatted_tools = format_team_tools(team_tools) if team_tools else None
105
+
106
+ model_name = team.model.name or team.model.__class__.__name__ if team.model else None
107
+ model_provider = team.model.provider or team.model.__class__.__name__ if team.model else ""
108
+ model_id = team.model.id if team.model else None
109
+
110
+ if model_provider and model_id:
111
+ model_provider = f"{model_provider} {model_id}"
112
+ elif model_name and model_id:
113
+ model_provider = f"{model_name} {model_id}"
114
+ elif model_id:
115
+ model_provider = model_id
116
+
117
+ session_table = team.db.session_table_name if team.db else None
118
+ knowledge_table = team.db.knowledge_table_name if team.db and team.knowledge else None
119
+
120
+ tools_info = {
121
+ "tools": formatted_tools,
122
+ "tool_call_limit": team.tool_call_limit,
123
+ "tool_choice": team.tool_choice,
124
+ }
125
+
126
+ sessions_info = {
127
+ "session_table": session_table,
128
+ "add_history_to_context": team.add_history_to_context,
129
+ "enable_session_summaries": team.enable_session_summaries,
130
+ "num_history_runs": team.num_history_runs,
131
+ "cache_session": team.cache_session,
132
+ }
133
+
134
+ knowledge_info = {
135
+ "knowledge_table": knowledge_table,
136
+ "enable_agentic_knowledge_filters": team.enable_agentic_knowledge_filters,
137
+ "knowledge_filters": team.knowledge_filters,
138
+ "references_format": team.references_format,
139
+ }
140
+
141
+ memory_info: Optional[Dict[str, Any]] = None
142
+ if team.memory_manager is not None:
143
+ memory_info = {
144
+ "enable_agentic_memory": team.enable_agentic_memory,
145
+ "enable_user_memories": team.enable_user_memories,
146
+ "metadata": team.metadata,
147
+ "memory_table": team.db.memory_table_name if team.db and team.enable_user_memories else None,
148
+ }
149
+
150
+ if team.memory_manager.model is not None:
151
+ memory_info["model"] = ModelResponse(
152
+ name=team.memory_manager.model.name,
153
+ model=team.memory_manager.model.id,
154
+ provider=team.memory_manager.model.provider,
155
+ ).model_dump()
156
+
157
+ reasoning_info: Dict[str, Any] = {
158
+ "reasoning": team.reasoning,
159
+ "reasoning_agent_id": team.reasoning_agent.id if team.reasoning_agent else None,
160
+ "reasoning_min_steps": team.reasoning_min_steps,
161
+ "reasoning_max_steps": team.reasoning_max_steps,
162
+ }
163
+
164
+ if team.reasoning_model:
165
+ reasoning_info["reasoning_model"] = ModelResponse(
166
+ name=team.reasoning_model.name,
167
+ model=team.reasoning_model.id,
168
+ provider=team.reasoning_model.provider,
169
+ ).model_dump()
170
+
171
+ default_tools_info = {
172
+ "search_knowledge": team.search_knowledge,
173
+ "read_chat_history": team.read_chat_history,
174
+ "get_member_information_tool": team.get_member_information_tool,
175
+ }
176
+
177
+ team_instructions = team.instructions if team.instructions else None
178
+ if team_instructions and callable(team_instructions):
179
+ team_instructions = await aexecute_instructions(instructions=team_instructions, agent=team, team=team)
180
+
181
+ team_system_message = team.system_message if team.system_message else None
182
+ if team_system_message and callable(team_system_message):
183
+ team_system_message = await aexecute_system_message(
184
+ system_message=team_system_message, agent=team, team=team
185
+ )
186
+
187
+ system_message_info = {
188
+ "system_message": team_system_message,
189
+ "system_message_role": team.system_message_role,
190
+ "description": team.description,
191
+ "instructions": team_instructions,
192
+ "expected_output": team.expected_output,
193
+ "additional_context": team.additional_context,
194
+ "markdown": team.markdown,
195
+ "add_datetime_to_context": team.add_datetime_to_context,
196
+ "add_location_to_context": team.add_location_to_context,
197
+ "resolve_in_context": team.resolve_in_context,
198
+ }
199
+
200
+ response_settings_info: Dict[str, Any] = {
201
+ "output_schema_name": team.output_schema.__name__ if team.output_schema else None,
202
+ "parser_model_prompt": team.parser_model_prompt,
203
+ "parse_response": team.parse_response,
204
+ "use_json_mode": team.use_json_mode,
205
+ }
206
+
207
+ if team.parser_model:
208
+ response_settings_info["parser_model"] = ModelResponse(
209
+ name=team.parser_model.name,
210
+ model=team.parser_model.id,
211
+ provider=team.parser_model.provider,
212
+ ).model_dump()
213
+
214
+ streaming_info = {
215
+ "stream": team.stream,
216
+ "stream_events": team.stream_events,
217
+ "stream_intermediate_steps": team.stream_intermediate_steps,
218
+ "stream_member_events": team.stream_member_events,
219
+ }
220
+
221
+ # Build team model only if it has at least one non-null field
222
+ _team_model_data: Dict[str, Any] = {}
223
+ if team.model and team.model.name is not None:
224
+ _team_model_data["name"] = team.model.name
225
+ if team.model and team.model.id is not None:
226
+ _team_model_data["model"] = team.model.id
227
+ if team.model and team.model.provider is not None:
228
+ _team_model_data["provider"] = team.model.provider
229
+
230
+ members: List[Union[AgentResponse, TeamResponse]] = []
231
+ for member in team.members:
232
+ if isinstance(member, Agent):
233
+ agent_response = await AgentResponse.from_agent(member)
234
+ members.append(agent_response)
235
+ if isinstance(member, Team):
236
+ team_response = await TeamResponse.from_team(member)
237
+ members.append(team_response)
238
+
239
+ return TeamResponse(
240
+ id=team.id,
241
+ name=team.name,
242
+ db_id=team.db.id if team.db else None,
243
+ model=ModelResponse(**_team_model_data) if _team_model_data else None,
244
+ tools=filter_meaningful_config(tools_info, {}),
245
+ sessions=filter_meaningful_config(sessions_info, team_defaults),
246
+ knowledge=filter_meaningful_config(knowledge_info, team_defaults),
247
+ memory=filter_meaningful_config(memory_info, team_defaults) if memory_info else None,
248
+ reasoning=filter_meaningful_config(reasoning_info, team_defaults),
249
+ default_tools=filter_meaningful_config(default_tools_info, team_defaults),
250
+ system_message=filter_meaningful_config(system_message_info, team_defaults),
251
+ response_settings=filter_meaningful_config(response_settings_info, team_defaults),
252
+ introduction=team.introduction,
253
+ streaming=filter_meaningful_config(streaming_info, team_defaults),
254
+ members=members if members else None,
255
+ metadata=team.metadata,
256
+ input_schema=get_team_input_schema_dict(team),
257
+ )
@@ -0,0 +1,3 @@
1
+ from agno.os.routers.traces.traces import get_traces_router
2
+
3
+ __all__ = ["get_traces_router"]
@@ -0,0 +1,414 @@
1
+ from datetime import datetime
2
+ from typing import Any, Dict, List, Optional
3
+
4
+ from pydantic import BaseModel, Field
5
+
6
+ from agno.os.utils import format_duration_ms
7
+
8
+
9
+ def _derive_span_type(span: Any) -> str:
10
+ """
11
+ Derive the correct span type from span attributes.
12
+
13
+ OpenInference sets span_kind to:
14
+ - AGENT for both agents and teams
15
+ - CHAIN for workflows
16
+
17
+ We use additional context (agno.team.id, agno.workflow.id) to differentiate:
18
+ - WORKFLOW: CHAIN spans or spans with agno.workflow.id
19
+ - TEAM: AGENT spans with agno.team.id
20
+ - AGENT: AGENT spans without agno.team.id
21
+ - LLM, TOOL, etc.: unchanged
22
+ """
23
+ span_kind = span.attributes.get("openinference.span.kind", "UNKNOWN")
24
+
25
+ # Check for workflow (CHAIN kind or has workflow.id)
26
+ if span_kind == "CHAIN":
27
+ return "WORKFLOW"
28
+
29
+ # Check for team vs agent
30
+ if span_kind == "AGENT":
31
+ # If it has a team.id attribute, it's a TEAM span
32
+ if span.attributes.get("agno.team.id") or span.attributes.get("team.id"):
33
+ return "TEAM"
34
+ return "AGENT"
35
+
36
+ # Return original span kind for LLM, TOOL, etc.
37
+ return span_kind
38
+
39
+
40
+ class TraceNode(BaseModel):
41
+ """Recursive node structure for rendering trace hierarchy in the frontend"""
42
+
43
+ id: str = Field(..., description="Span ID")
44
+ name: str = Field(..., description="Span name (e.g., 'agent.run', 'llm.invoke')")
45
+ type: str = Field(..., description="Span kind (AGENT, TEAM, WORKFLOW, LLM, TOOL)")
46
+ duration: str = Field(..., description="Human-readable duration (e.g., '123ms', '1.5s')")
47
+ start_time: datetime = Field(..., description="Start time (Pydantic auto-serializes to ISO 8601)")
48
+ end_time: datetime = Field(..., description="End time (Pydantic auto-serializes to ISO 8601)")
49
+ status: str = Field(..., description="Status code (OK, ERROR)")
50
+ input: Optional[str] = Field(None, description="Input to the span")
51
+ output: Optional[str] = Field(None, description="Output from the span")
52
+ error: Optional[str] = Field(None, description="Error message if status is ERROR")
53
+ spans: Optional[List["TraceNode"]] = Field(None, description="Child spans in the trace hierarchy")
54
+ step_type: Optional[str] = Field(None, description="Workflow step type (Step, Condition, function, Agent, Team)")
55
+ metadata: Optional[Dict[str, Any]] = Field(None, description="Additional span attributes and data")
56
+ extra_data: Optional[Dict[str, Any]] = Field(
57
+ None, description="Flexible field for custom attributes and additional data"
58
+ )
59
+
60
+ @classmethod
61
+ def from_span(cls, span: Any, spans: Optional[List["TraceNode"]] = None) -> "TraceNode":
62
+ """Create TraceNode from a Span object"""
63
+ # Derive the correct span type (AGENT, TEAM, WORKFLOW, LLM, TOOL, etc.)
64
+ span_type = _derive_span_type(span)
65
+
66
+ # Also get the raw span_kind for metadata extraction logic
67
+ span_kind = span.attributes.get("openinference.span.kind", "UNKNOWN")
68
+
69
+ # Extract input/output at root level (for all span types)
70
+ input_val = span.attributes.get("input.value")
71
+ output_val = span.attributes.get("output.value")
72
+
73
+ # Extract error information
74
+ error_val = None
75
+ if span.status_code == "ERROR":
76
+ error_val = span.status_message or span.attributes.get("exception.message")
77
+ output_val = None
78
+
79
+ # Build metadata with key attributes based on span kind
80
+ metadata: Dict[str, Any] = {}
81
+
82
+ if span_kind == "AGENT":
83
+ if run_id := span.attributes.get("agno.run.id"):
84
+ metadata["run_id"] = run_id
85
+
86
+ elif span_kind == "LLM":
87
+ if model_name := span.attributes.get("llm.model_name"):
88
+ metadata["model"] = model_name
89
+ if input_tokens := span.attributes.get("llm.token_count.prompt"):
90
+ metadata["input_tokens"] = input_tokens
91
+ if output_tokens := span.attributes.get("llm.token_count.completion"):
92
+ metadata["output_tokens"] = output_tokens
93
+
94
+ elif span_kind == "TOOL":
95
+ if tool_name := span.attributes.get("tool.name"):
96
+ metadata["tool_name"] = tool_name
97
+ if tool_params := span.attributes.get("tool.parameters"):
98
+ metadata["parameters"] = tool_params
99
+
100
+ elif span_kind == "CHAIN":
101
+ if workflow_description := span.attributes.get("agno.workflow.description"):
102
+ metadata["description"] = workflow_description
103
+ if steps_count := span.attributes.get("agno.workflow.steps_count"):
104
+ metadata["steps_count"] = steps_count
105
+ if steps := span.attributes.get("agno.workflow.steps"):
106
+ metadata["steps"] = steps
107
+ if step_types := span.attributes.get("agno.workflow.step_types"):
108
+ metadata["step_types"] = step_types
109
+
110
+ # Add session/user context if present
111
+ if session_id := span.attributes.get("session.id"):
112
+ metadata["session_id"] = session_id
113
+ if user_id := span.attributes.get("user.id"):
114
+ metadata["user_id"] = user_id
115
+
116
+ # Use datetime objects directly
117
+ return cls(
118
+ id=span.span_id,
119
+ name=span.name,
120
+ type=span_type,
121
+ duration=format_duration_ms(span.duration_ms),
122
+ start_time=span.start_time,
123
+ end_time=span.end_time,
124
+ status=span.status_code,
125
+ input=input_val,
126
+ output=output_val,
127
+ error=error_val,
128
+ spans=spans,
129
+ step_type=None, # Set by _build_span_tree for workflow steps
130
+ metadata=metadata if metadata else None,
131
+ extra_data=None,
132
+ )
133
+
134
+
135
+ class TraceSummary(BaseModel):
136
+ """Summary information for trace list view"""
137
+
138
+ trace_id: str = Field(..., description="Unique trace identifier")
139
+ name: str = Field(..., description="Trace name (usually root span name)")
140
+ status: str = Field(..., description="Overall status (OK, ERROR, UNSET)")
141
+ duration: str = Field(..., description="Human-readable total duration")
142
+ start_time: datetime = Field(..., description="Trace start time (Pydantic auto-serializes to ISO 8601)")
143
+ end_time: datetime = Field(..., description="Trace end time (Pydantic auto-serializes to ISO 8601)")
144
+ total_spans: int = Field(..., description="Total number of spans in this trace")
145
+ error_count: int = Field(..., description="Number of spans with errors")
146
+ input: Optional[str] = Field(None, description="Input to the agent")
147
+ run_id: Optional[str] = Field(None, description="Associated run ID")
148
+ session_id: Optional[str] = Field(None, description="Associated session ID")
149
+ user_id: Optional[str] = Field(None, description="Associated user ID")
150
+ agent_id: Optional[str] = Field(None, description="Associated agent ID")
151
+ team_id: Optional[str] = Field(None, description="Associated team ID")
152
+ workflow_id: Optional[str] = Field(None, description="Associated workflow ID")
153
+ created_at: datetime = Field(..., description="Time when trace was created (Pydantic auto-serializes to ISO 8601)")
154
+
155
+ @classmethod
156
+ def from_trace(cls, trace: Any, input: Optional[str] = None) -> "TraceSummary":
157
+ # Use datetime objects directly (Pydantic will auto-serialize to ISO 8601)
158
+ return cls(
159
+ trace_id=trace.trace_id,
160
+ name=trace.name,
161
+ status=trace.status,
162
+ duration=format_duration_ms(trace.duration_ms),
163
+ start_time=trace.start_time,
164
+ end_time=trace.end_time,
165
+ total_spans=trace.total_spans,
166
+ error_count=trace.error_count,
167
+ input=input,
168
+ run_id=trace.run_id,
169
+ session_id=trace.session_id,
170
+ user_id=trace.user_id,
171
+ agent_id=trace.agent_id,
172
+ team_id=trace.team_id,
173
+ workflow_id=trace.workflow_id,
174
+ created_at=trace.created_at,
175
+ )
176
+
177
+
178
+ class TraceSessionStats(BaseModel):
179
+ """Aggregated trace statistics grouped by session"""
180
+
181
+ session_id: str = Field(..., description="Session identifier")
182
+ user_id: Optional[str] = Field(None, description="User ID associated with the session")
183
+ agent_id: Optional[str] = Field(None, description="Agent ID(s) used in the session")
184
+ team_id: Optional[str] = Field(None, description="Team ID associated with the session")
185
+ workflow_id: Optional[str] = Field(None, description="Workflow ID associated with the session")
186
+ total_traces: int = Field(..., description="Total number of traces in this session")
187
+ first_trace_at: datetime = Field(..., description="Time of first trace (Pydantic auto-serializes to ISO 8601)")
188
+ last_trace_at: datetime = Field(..., description="Time of last trace (Pydantic auto-serializes to ISO 8601)")
189
+
190
+
191
+ class TraceDetail(BaseModel):
192
+ """Detailed trace information with hierarchical span tree"""
193
+
194
+ trace_id: str = Field(..., description="Unique trace identifier")
195
+ name: str = Field(..., description="Trace name (usually root span name)")
196
+ status: str = Field(..., description="Overall status (OK, ERROR)")
197
+ duration: str = Field(..., description="Human-readable total duration")
198
+ start_time: datetime = Field(..., description="Trace start time (Pydantic auto-serializes to ISO 8601)")
199
+ end_time: datetime = Field(..., description="Trace end time (Pydantic auto-serializes to ISO 8601)")
200
+ total_spans: int = Field(..., description="Total number of spans in this trace")
201
+ error_count: int = Field(..., description="Number of spans with errors")
202
+ input: Optional[str] = Field(None, description="Input to the agent/workflow")
203
+ output: Optional[str] = Field(None, description="Output from the agent/workflow")
204
+ error: Optional[str] = Field(None, description="Error message if status is ERROR")
205
+ run_id: Optional[str] = Field(None, description="Associated run ID")
206
+ session_id: Optional[str] = Field(None, description="Associated session ID")
207
+ user_id: Optional[str] = Field(None, description="Associated user ID")
208
+ agent_id: Optional[str] = Field(None, description="Associated agent ID")
209
+ team_id: Optional[str] = Field(None, description="Associated team ID")
210
+ workflow_id: Optional[str] = Field(None, description="Associated workflow ID")
211
+ created_at: datetime = Field(..., description="Time when trace was created (Pydantic auto-serializes to ISO 8601)")
212
+ tree: List[TraceNode] = Field(..., description="Hierarchical tree of spans (root nodes)")
213
+
214
+ @classmethod
215
+ def from_trace_and_spans(cls, trace: Any, spans: List[Any]) -> "TraceDetail":
216
+ """Create TraceDetail from a Trace and its Spans, building the tree structure"""
217
+ # Find root span to extract input/output/error
218
+ root_span = next((s for s in spans if not s.parent_span_id), None)
219
+ trace_input = None
220
+ trace_output = None
221
+ trace_error = None
222
+
223
+ if root_span:
224
+ trace_input = root_span.attributes.get("input.value")
225
+ output_val = root_span.attributes.get("output.value")
226
+
227
+ # If trace status is ERROR, extract error and set output to None
228
+ if trace.status == "ERROR" or root_span.status_code == "ERROR":
229
+ trace_error = root_span.status_message or root_span.attributes.get("exception.message")
230
+ trace_output = None
231
+ else:
232
+ trace_output = output_val
233
+
234
+ span_kind = root_span.attributes.get("openinference.span.kind", "")
235
+ output_is_empty = not trace_output or trace_output == "None" or str(trace_output).strip() == "None"
236
+ if span_kind == "CHAIN" and output_is_empty and trace.status != "ERROR":
237
+ # Find direct children of root span (workflow steps)
238
+ root_span_id = root_span.span_id
239
+ direct_children = [s for s in spans if s.parent_span_id == root_span_id]
240
+ if direct_children:
241
+ # Sort by end_time to get the last executed step
242
+ direct_children.sort(key=lambda s: s.end_time, reverse=True)
243
+ last_step = direct_children[0]
244
+ # Get output from the last step
245
+ trace_output = last_step.attributes.get("output.value")
246
+
247
+ # Calculate total tokens from all LLM spans
248
+ total_input_tokens = 0
249
+ total_output_tokens = 0
250
+ for span in spans:
251
+ if span.attributes.get("openinference.span.kind") == "LLM":
252
+ input_tokens = span.attributes.get("llm.token_count.prompt", 0)
253
+ output_tokens = span.attributes.get("llm.token_count.completion", 0)
254
+ if input_tokens:
255
+ total_input_tokens += input_tokens
256
+ if output_tokens:
257
+ total_output_tokens += output_tokens
258
+
259
+ # Build span tree with token totals
260
+ span_tree = cls._build_span_tree(
261
+ spans,
262
+ total_input_tokens,
263
+ total_output_tokens,
264
+ trace_start_time=trace.start_time,
265
+ trace_end_time=trace.end_time,
266
+ trace_duration_ms=trace.duration_ms,
267
+ )
268
+
269
+ # Use datetime objects directly (Pydantic will auto-serialize to ISO 8601)
270
+ return cls(
271
+ trace_id=trace.trace_id,
272
+ name=trace.name,
273
+ status=trace.status,
274
+ duration=format_duration_ms(trace.duration_ms),
275
+ start_time=trace.start_time,
276
+ end_time=trace.end_time,
277
+ total_spans=trace.total_spans,
278
+ error_count=trace.error_count,
279
+ input=trace_input,
280
+ output=trace_output,
281
+ error=trace_error,
282
+ run_id=trace.run_id,
283
+ session_id=trace.session_id,
284
+ user_id=trace.user_id,
285
+ agent_id=trace.agent_id,
286
+ team_id=trace.team_id,
287
+ workflow_id=trace.workflow_id,
288
+ created_at=trace.created_at,
289
+ tree=span_tree,
290
+ )
291
+
292
+ @staticmethod
293
+ def _build_span_tree(
294
+ spans: List[Any],
295
+ total_input_tokens: int,
296
+ total_output_tokens: int,
297
+ trace_start_time: Optional[datetime] = None,
298
+ trace_end_time: Optional[datetime] = None,
299
+ trace_duration_ms: Optional[int] = None,
300
+ ) -> List[TraceNode]:
301
+ """Build hierarchical tree from flat list of spans
302
+
303
+ Args:
304
+ spans: List of span objects
305
+ total_input_tokens: Total input tokens across all spans
306
+ total_output_tokens: Total output tokens across all spans
307
+ trace_start_time: Corrected start time from trace aggregation
308
+ trace_end_time: Corrected end time from trace aggregation
309
+ trace_duration_ms: Corrected duration from trace aggregation
310
+ """
311
+ if not spans:
312
+ return []
313
+
314
+ # Create a map of parent_id -> list of spans
315
+ spans_map: Dict[Optional[str], List[Any]] = {}
316
+ for span in spans:
317
+ parent_id = span.parent_span_id
318
+ if parent_id not in spans_map:
319
+ spans_map[parent_id] = []
320
+ spans_map[parent_id].append(span)
321
+
322
+ # Extract step_types list from workflow root span for index-based matching
323
+ step_types_list: List[str] = []
324
+ root_spans = spans_map.get(None, [])
325
+ for root_span in root_spans:
326
+ span_kind = root_span.attributes.get("openinference.span.kind", "")
327
+ if span_kind == "CHAIN":
328
+ step_types = root_span.attributes.get("agno.workflow.step_types", [])
329
+ if step_types:
330
+ step_types_list = list(step_types)
331
+ break # Use first workflow root span's step_types
332
+
333
+ # Recursive function to build tree for a span
334
+ # step_index is used to track position within direct children of root (workflow steps)
335
+ def build_node(span: Any, is_root: bool = False, step_index: Optional[int] = None) -> TraceNode:
336
+ span_id = span.span_id
337
+ children_spans = spans_map.get(span_id, [])
338
+
339
+ # Sort children spans by start time
340
+ if children_spans:
341
+ children_spans.sort(key=lambda s: s.start_time)
342
+
343
+ # Recursively build spans
344
+ # For root span's direct children (workflow steps), pass the index
345
+ children_nodes: Optional[List[TraceNode]] = None
346
+ if is_root and step_types_list:
347
+ children_nodes = []
348
+ for idx, child in enumerate(children_spans):
349
+ children_nodes.append(build_node(child, step_index=idx))
350
+ elif children_spans:
351
+ children_nodes = [build_node(child) for child in children_spans]
352
+
353
+ # For root span, create custom metadata with token totals
354
+ if is_root:
355
+ # Build simplified metadata for root with token totals
356
+ root_metadata: Dict[str, Any] = {}
357
+ if total_input_tokens > 0:
358
+ root_metadata["total_input_tokens"] = total_input_tokens
359
+ if total_output_tokens > 0:
360
+ root_metadata["total_output_tokens"] = total_output_tokens
361
+
362
+ # Use trace-level timing if available
363
+ start_time = trace_start_time if trace_start_time else span.start_time
364
+ end_time = trace_end_time if trace_end_time else span.end_time
365
+ duration_ms = trace_duration_ms if trace_duration_ms is not None else span.duration_ms
366
+
367
+ # Derive the correct span type (AGENT, TEAM, WORKFLOW, etc.)
368
+ span_type = _derive_span_type(span)
369
+ span_kind = span.attributes.get("openinference.span.kind", "UNKNOWN")
370
+
371
+ # Add workflow-specific metadata for CHAIN/WORKFLOW spans
372
+ if span_kind == "CHAIN":
373
+ if workflow_description := span.attributes.get("agno.workflow.description"):
374
+ root_metadata["description"] = workflow_description
375
+ if steps_count := span.attributes.get("agno.workflow.steps_count"):
376
+ root_metadata["steps_count"] = steps_count
377
+ if steps := span.attributes.get("agno.workflow.steps"):
378
+ root_metadata["steps"] = steps
379
+ if step_types := span.attributes.get("agno.workflow.step_types"):
380
+ root_metadata["step_types"] = step_types
381
+
382
+ # Use datetime objects directly (Pydantic will auto-serialize to ISO 8601)
383
+ # Skip input/output/error for root span (already at top level of TraceDetail)
384
+
385
+ return TraceNode(
386
+ id=span.span_id,
387
+ name=span.name,
388
+ type=span_type,
389
+ duration=format_duration_ms(duration_ms),
390
+ start_time=start_time,
391
+ end_time=end_time,
392
+ status=span.status_code,
393
+ input=None, # Skip for root span (already at TraceDetail level)
394
+ output=None, # Skip for root span (already at TraceDetail level)
395
+ error=None, # Skip for root span (already at TraceDetail level)
396
+ spans=children_nodes if children_nodes else None,
397
+ metadata=root_metadata if root_metadata else None,
398
+ extra_data=None,
399
+ )
400
+ else:
401
+ # Create node from span
402
+ node = TraceNode.from_span(span, spans=children_nodes)
403
+
404
+ # For workflow step spans (direct children of root), assign step_type by index
405
+ if step_index is not None and step_types_list and step_index < len(step_types_list):
406
+ node.step_type = step_types_list[step_index]
407
+
408
+ return node
409
+
410
+ # Sort root spans by start time
411
+ root_spans.sort(key=lambda s: s.start_time)
412
+
413
+ # Build tree starting from roots
414
+ return [build_node(root, is_root=True) for root in root_spans]