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
@@ -0,0 +1,276 @@
1
+ """
2
+ Trace data models for Agno tracing.
3
+ """
4
+
5
+ from dataclasses import asdict, dataclass
6
+ from datetime import datetime, timezone
7
+ from typing import Any, Dict, List, Optional
8
+
9
+ from opentelemetry.sdk.trace import ReadableSpan # type: ignore
10
+ from opentelemetry.trace import SpanKind, StatusCode # type: ignore
11
+
12
+
13
+ @dataclass
14
+ class Trace:
15
+ """Represents a complete trace (one record per trace_id)"""
16
+
17
+ trace_id: str
18
+ name: str # Name from root span
19
+ status: str # Overall status: OK, ERROR, UNSET
20
+ start_time: datetime # Python datetime object
21
+ end_time: datetime # Python datetime object
22
+ duration_ms: int
23
+ total_spans: int
24
+ error_count: int
25
+
26
+ # Context from root span
27
+ run_id: Optional[str]
28
+ session_id: Optional[str]
29
+ user_id: Optional[str]
30
+ agent_id: Optional[str]
31
+ team_id: Optional[str]
32
+ workflow_id: Optional[str]
33
+
34
+ created_at: datetime # Python datetime object
35
+
36
+ def to_dict(self) -> Dict[str, Any]:
37
+ """Convert Trace to dictionary for database storage (datetime -> ISO string)"""
38
+ data = asdict(self)
39
+ # Convert datetime objects to ISO format strings for database storage
40
+ data["start_time"] = self.start_time.isoformat()
41
+ data["end_time"] = self.end_time.isoformat()
42
+ data["created_at"] = self.created_at.isoformat()
43
+ return data
44
+
45
+ @classmethod
46
+ def from_dict(cls, data: Dict[str, Any]) -> "Trace":
47
+ """Create Trace from dictionary (ISO string -> datetime)"""
48
+ # Convert ISO format strings to datetime objects
49
+ start_time = data["start_time"]
50
+ if isinstance(start_time, str):
51
+ start_time = datetime.fromisoformat(start_time.replace("Z", "+00:00"))
52
+ elif isinstance(start_time, int):
53
+ start_time = datetime.fromtimestamp(start_time / 1_000_000_000, tz=timezone.utc)
54
+
55
+ end_time = data["end_time"]
56
+ if isinstance(end_time, str):
57
+ end_time = datetime.fromisoformat(end_time.replace("Z", "+00:00"))
58
+ elif isinstance(end_time, int):
59
+ end_time = datetime.fromtimestamp(end_time / 1_000_000_000, tz=timezone.utc)
60
+
61
+ created_at = data["created_at"]
62
+ if isinstance(created_at, str):
63
+ created_at = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
64
+ elif isinstance(created_at, int):
65
+ created_at = datetime.fromtimestamp(created_at, tz=timezone.utc)
66
+
67
+ return cls(
68
+ trace_id=data["trace_id"],
69
+ name=data["name"],
70
+ status=data["status"],
71
+ start_time=start_time,
72
+ end_time=end_time,
73
+ duration_ms=data["duration_ms"],
74
+ total_spans=data["total_spans"],
75
+ error_count=data["error_count"],
76
+ run_id=data.get("run_id"),
77
+ session_id=data.get("session_id"),
78
+ user_id=data.get("user_id"),
79
+ agent_id=data.get("agent_id"),
80
+ team_id=data.get("team_id"),
81
+ workflow_id=data.get("workflow_id"),
82
+ created_at=created_at,
83
+ )
84
+
85
+
86
+ @dataclass
87
+ class Span:
88
+ """Represents a single span within a trace"""
89
+
90
+ span_id: str
91
+ trace_id: str
92
+ parent_span_id: Optional[str]
93
+ name: str
94
+ span_kind: str
95
+ status_code: str
96
+ status_message: Optional[str]
97
+ start_time: datetime # Python datetime object
98
+ end_time: datetime # Python datetime object
99
+ duration_ms: int
100
+ attributes: Dict[str, Any]
101
+ created_at: datetime # Python datetime object
102
+
103
+ def to_dict(self) -> Dict[str, Any]:
104
+ """Convert Span to dictionary for database storage (datetime -> ISO string)"""
105
+ data = asdict(self)
106
+ # Convert datetime objects to ISO format strings for database storage
107
+ data["start_time"] = self.start_time.isoformat()
108
+ data["end_time"] = self.end_time.isoformat()
109
+ data["created_at"] = self.created_at.isoformat()
110
+ return data
111
+
112
+ @classmethod
113
+ def from_dict(cls, data: Dict[str, Any]) -> "Span":
114
+ """Create Span from dictionary (ISO string -> datetime)"""
115
+ # Convert ISO format strings to datetime objects
116
+ start_time = data["start_time"]
117
+ if isinstance(start_time, str):
118
+ start_time = datetime.fromisoformat(start_time.replace("Z", "+00:00"))
119
+ elif isinstance(start_time, int):
120
+ start_time = datetime.fromtimestamp(start_time / 1_000_000_000, tz=timezone.utc)
121
+
122
+ end_time = data["end_time"]
123
+ if isinstance(end_time, str):
124
+ end_time = datetime.fromisoformat(end_time.replace("Z", "+00:00"))
125
+ elif isinstance(end_time, int):
126
+ end_time = datetime.fromtimestamp(end_time / 1_000_000_000, tz=timezone.utc)
127
+
128
+ created_at = data["created_at"]
129
+ if isinstance(created_at, str):
130
+ created_at = datetime.fromisoformat(created_at.replace("Z", "+00:00"))
131
+ elif isinstance(created_at, int):
132
+ created_at = datetime.fromtimestamp(created_at, tz=timezone.utc)
133
+
134
+ return cls(
135
+ span_id=data["span_id"],
136
+ trace_id=data["trace_id"],
137
+ parent_span_id=data.get("parent_span_id"),
138
+ name=data["name"],
139
+ span_kind=data["span_kind"],
140
+ status_code=data["status_code"],
141
+ status_message=data.get("status_message"),
142
+ start_time=start_time,
143
+ end_time=end_time,
144
+ duration_ms=data["duration_ms"],
145
+ attributes=data.get("attributes", {}),
146
+ created_at=created_at,
147
+ )
148
+
149
+ @classmethod
150
+ def from_otel_span(cls, otel_span: ReadableSpan) -> "Span":
151
+ """Convert OpenTelemetry ReadableSpan to Span"""
152
+ # Extract span context
153
+ span_context = otel_span.context
154
+ trace_id = format(span_context.trace_id, "032x") if span_context else "0" * 32
155
+ span_id = format(span_context.span_id, "016x") if span_context else "0" * 16
156
+
157
+ # Extract parent span ID if exists
158
+ parent_span_id = None
159
+ if otel_span.parent and otel_span.parent.span_id:
160
+ parent_span_id = format(otel_span.parent.span_id, "016x")
161
+
162
+ # Extract span kind
163
+ span_kind_map = {
164
+ SpanKind.INTERNAL: "INTERNAL",
165
+ SpanKind.SERVER: "SERVER",
166
+ SpanKind.CLIENT: "CLIENT",
167
+ SpanKind.PRODUCER: "PRODUCER",
168
+ SpanKind.CONSUMER: "CONSUMER",
169
+ }
170
+ span_kind = span_kind_map.get(otel_span.kind, "INTERNAL")
171
+
172
+ # Extract status
173
+ status_code_map = {
174
+ StatusCode.UNSET: "UNSET",
175
+ StatusCode.OK: "OK",
176
+ StatusCode.ERROR: "ERROR",
177
+ }
178
+ status_code = status_code_map.get(otel_span.status.status_code, "UNSET")
179
+ status_message = otel_span.status.description
180
+
181
+ # Calculate duration in milliseconds
182
+ start_time_ns = otel_span.start_time or 0
183
+ end_time_ns = otel_span.end_time or start_time_ns
184
+ duration_ms = int((end_time_ns - start_time_ns) / 1_000_000)
185
+
186
+ # Convert nanosecond timestamps to datetime objects
187
+ start_time = datetime.fromtimestamp(start_time_ns / 1_000_000_000, tz=timezone.utc)
188
+ end_time = datetime.fromtimestamp(end_time_ns / 1_000_000_000, tz=timezone.utc)
189
+
190
+ # Convert attributes to dictionary
191
+ attributes: Dict[str, Any] = {}
192
+ if otel_span.attributes:
193
+ for key, value in otel_span.attributes.items():
194
+ # Convert attribute values to JSON-serializable types
195
+ if isinstance(value, (str, int, float, bool, type(None))):
196
+ attributes[key] = value
197
+ elif isinstance(value, (list, tuple)):
198
+ attributes[key] = list(value)
199
+ else:
200
+ attributes[key] = str(value)
201
+
202
+ return cls(
203
+ span_id=span_id,
204
+ trace_id=trace_id,
205
+ parent_span_id=parent_span_id,
206
+ name=otel_span.name,
207
+ span_kind=span_kind,
208
+ status_code=status_code,
209
+ status_message=status_message,
210
+ start_time=start_time,
211
+ end_time=end_time,
212
+ duration_ms=duration_ms,
213
+ attributes=attributes,
214
+ created_at=datetime.now(timezone.utc),
215
+ )
216
+
217
+
218
+ def create_trace_from_spans(spans: List[Span]) -> Optional[Trace]:
219
+ """
220
+ Create a Trace object from a list of Span objects with the same trace_id.
221
+
222
+ Args:
223
+ spans: List of Span objects belonging to the same trace
224
+
225
+ Returns:
226
+ Trace object with aggregated information, or None if spans list is empty
227
+ """
228
+ if not spans:
229
+ return None
230
+
231
+ # Find root span (no parent)
232
+ root_span = next((s for s in spans if not s.parent_span_id), spans[0])
233
+
234
+ # Calculate aggregated metrics
235
+ trace_id = spans[0].trace_id
236
+ start_time = min(s.start_time for s in spans)
237
+ end_time = max(s.end_time for s in spans)
238
+ duration_ms = int((end_time - start_time).total_seconds() * 1000)
239
+ total_spans = len(spans)
240
+ error_count = sum(1 for s in spans if s.status_code == "ERROR")
241
+
242
+ # Determine overall status (ERROR if any span errored, OK otherwise)
243
+ status = "ERROR" if error_count > 0 else "OK"
244
+
245
+ # Extract context from root span's attributes
246
+ attrs = root_span.attributes
247
+ run_id = attrs.get("run_id") or attrs.get("agno.run.id")
248
+
249
+ session_id = attrs.get("session_id") or attrs.get("agno.session.id") or attrs.get("session.id")
250
+
251
+ user_id = attrs.get("user_id") or attrs.get("agno.user.id") or attrs.get("user.id")
252
+
253
+ # Try to extract agent_id from the span name or attributes
254
+ agent_id = attrs.get("agent_id") or attrs.get("agno.agent.id")
255
+
256
+ team_id = attrs.get("team_id") or attrs.get("agno.team.id")
257
+
258
+ workflow_id = attrs.get("workflow_id") or attrs.get("agno.workflow.id")
259
+
260
+ return Trace(
261
+ trace_id=trace_id,
262
+ name=root_span.name,
263
+ status=status,
264
+ start_time=start_time,
265
+ end_time=end_time,
266
+ duration_ms=duration_ms,
267
+ total_spans=total_spans,
268
+ error_count=error_count,
269
+ run_id=run_id,
270
+ session_id=session_id,
271
+ user_id=user_id,
272
+ agent_id=agent_id,
273
+ team_id=team_id,
274
+ workflow_id=workflow_id,
275
+ created_at=datetime.now(timezone.utc),
276
+ )
agno/tracing/setup.py ADDED
@@ -0,0 +1,112 @@
1
+ """
2
+ Setup helper functions for configuring Agno tracing.
3
+ """
4
+
5
+ from typing import Union
6
+
7
+ from agno.db.base import AsyncBaseDb, BaseDb
8
+ from agno.remote.base import RemoteDb
9
+ from agno.tracing.exporter import DatabaseSpanExporter
10
+ from agno.utils.log import logger
11
+
12
+ try:
13
+ from openinference.instrumentation.agno import AgnoInstrumentor # type: ignore
14
+ from opentelemetry import trace as trace_api # type: ignore
15
+ from opentelemetry.sdk.trace import TracerProvider # type: ignore
16
+ from opentelemetry.sdk.trace.export import BatchSpanProcessor, SimpleSpanProcessor, SpanProcessor # type: ignore
17
+
18
+ OPENTELEMETRY_AVAILABLE = True
19
+ except ImportError:
20
+ OPENTELEMETRY_AVAILABLE = False
21
+
22
+
23
+ def setup_tracing(
24
+ db: Union[BaseDb, AsyncBaseDb, RemoteDb],
25
+ batch_processing: bool = False,
26
+ max_queue_size: int = 2048,
27
+ max_export_batch_size: int = 512,
28
+ schedule_delay_millis: int = 5000,
29
+ ) -> None:
30
+ """
31
+ Set up OpenTelemetry tracing with database export for Agno agents.
32
+
33
+ This function configures automatic tracing for all Agno agents, teams, and workflows.
34
+ Traces are automatically captured for:
35
+ - Agent runs (agent.run, agent.arun)
36
+ - Model calls (model.response)
37
+ - Tool executions
38
+ - Team coordination
39
+ - Workflow steps
40
+
41
+ Args:
42
+ db: Database instance to store traces (sync or async)
43
+ batch_processing: If True, use BatchSpanProcessor for better performance
44
+ If False, use SimpleSpanProcessor (immediate export)
45
+ max_queue_size: Maximum queue size for batch processor
46
+ max_export_batch_size: Maximum batch size for export
47
+ schedule_delay_millis: Delay in milliseconds between batch exports
48
+
49
+ Raises:
50
+ ImportError: If OpenTelemetry packages are not installed
51
+
52
+ Example:
53
+ ```python
54
+ from agno.db.sqlite import SqliteDb
55
+ from agno.tracing import setup_tracing
56
+
57
+ db = SqliteDb(db_file="tmp/traces.db")
58
+ setup_tracing(db=db)
59
+
60
+ # Now all agents will be automatically traced
61
+ agent = Agent(...)
62
+ agent.run("Hello") # This will be traced automatically
63
+ ```
64
+ """
65
+ if not OPENTELEMETRY_AVAILABLE:
66
+ raise ImportError(
67
+ "OpenTelemetry packages are required for tracing. "
68
+ "Install with: pip install opentelemetry-api opentelemetry-sdk openinference-instrumentation-agno"
69
+ )
70
+
71
+ # Check if tracing is already set up (handles reload scenarios)
72
+ current_provider = trace_api.get_tracer_provider()
73
+ if isinstance(current_provider, TracerProvider):
74
+ # Already configured with a real TracerProvider, skip
75
+ return
76
+
77
+ try:
78
+ # Create tracer provider
79
+ tracer_provider = TracerProvider()
80
+
81
+ # Create database exporter
82
+ exporter = DatabaseSpanExporter(db=db)
83
+
84
+ # Configure span processor
85
+ processor: SpanProcessor
86
+ if batch_processing:
87
+ processor = BatchSpanProcessor(
88
+ exporter,
89
+ max_queue_size=max_queue_size,
90
+ max_export_batch_size=max_export_batch_size,
91
+ schedule_delay_millis=schedule_delay_millis,
92
+ )
93
+ logger.debug(
94
+ f"Tracing configured with BatchSpanProcessor "
95
+ f"(queue_size={max_queue_size}, batch_size={max_export_batch_size})"
96
+ )
97
+ else:
98
+ processor = SimpleSpanProcessor(exporter)
99
+ logger.debug("Tracing configured with SimpleSpanProcessor")
100
+
101
+ tracer_provider.add_span_processor(processor)
102
+
103
+ # Set the global tracer provider
104
+ trace_api.set_tracer_provider(tracer_provider)
105
+
106
+ # Instrument Agno with OpenInference
107
+ AgnoInstrumentor().instrument(tracer_provider=tracer_provider)
108
+
109
+ logger.info("Agno tracing successfully set up with database storage")
110
+ except Exception as e:
111
+ logger.error(f"Failed to set up tracing: {e}", exc_info=True)
112
+ raise