agno 2.0.0rc2__py3-none-any.whl → 2.3.0__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 (331) hide show
  1. agno/agent/agent.py +6009 -2874
  2. agno/api/api.py +2 -0
  3. agno/api/os.py +1 -1
  4. agno/culture/__init__.py +3 -0
  5. agno/culture/manager.py +956 -0
  6. agno/db/async_postgres/__init__.py +3 -0
  7. agno/db/base.py +385 -6
  8. agno/db/dynamo/dynamo.py +388 -81
  9. agno/db/dynamo/schemas.py +47 -10
  10. agno/db/dynamo/utils.py +63 -4
  11. agno/db/firestore/firestore.py +435 -64
  12. agno/db/firestore/schemas.py +11 -0
  13. agno/db/firestore/utils.py +102 -4
  14. agno/db/gcs_json/gcs_json_db.py +384 -42
  15. agno/db/gcs_json/utils.py +60 -26
  16. agno/db/in_memory/in_memory_db.py +351 -66
  17. agno/db/in_memory/utils.py +60 -2
  18. agno/db/json/json_db.py +339 -48
  19. agno/db/json/utils.py +60 -26
  20. agno/db/migrations/manager.py +199 -0
  21. agno/db/migrations/v1_to_v2.py +510 -37
  22. agno/db/migrations/versions/__init__.py +0 -0
  23. agno/db/migrations/versions/v2_3_0.py +938 -0
  24. agno/db/mongo/__init__.py +15 -1
  25. agno/db/mongo/async_mongo.py +2036 -0
  26. agno/db/mongo/mongo.py +653 -76
  27. agno/db/mongo/schemas.py +13 -0
  28. agno/db/mongo/utils.py +80 -8
  29. agno/db/mysql/mysql.py +687 -25
  30. agno/db/mysql/schemas.py +61 -37
  31. agno/db/mysql/utils.py +60 -2
  32. agno/db/postgres/__init__.py +2 -1
  33. agno/db/postgres/async_postgres.py +2001 -0
  34. agno/db/postgres/postgres.py +676 -57
  35. agno/db/postgres/schemas.py +43 -18
  36. agno/db/postgres/utils.py +164 -2
  37. agno/db/redis/redis.py +344 -38
  38. agno/db/redis/schemas.py +18 -0
  39. agno/db/redis/utils.py +60 -2
  40. agno/db/schemas/__init__.py +2 -1
  41. agno/db/schemas/culture.py +120 -0
  42. agno/db/schemas/memory.py +13 -0
  43. agno/db/singlestore/schemas.py +26 -1
  44. agno/db/singlestore/singlestore.py +687 -53
  45. agno/db/singlestore/utils.py +60 -2
  46. agno/db/sqlite/__init__.py +2 -1
  47. agno/db/sqlite/async_sqlite.py +2371 -0
  48. agno/db/sqlite/schemas.py +24 -0
  49. agno/db/sqlite/sqlite.py +774 -85
  50. agno/db/sqlite/utils.py +168 -5
  51. agno/db/surrealdb/__init__.py +3 -0
  52. agno/db/surrealdb/metrics.py +292 -0
  53. agno/db/surrealdb/models.py +309 -0
  54. agno/db/surrealdb/queries.py +71 -0
  55. agno/db/surrealdb/surrealdb.py +1361 -0
  56. agno/db/surrealdb/utils.py +147 -0
  57. agno/db/utils.py +50 -22
  58. agno/eval/accuracy.py +50 -43
  59. agno/eval/performance.py +6 -3
  60. agno/eval/reliability.py +6 -3
  61. agno/eval/utils.py +33 -16
  62. agno/exceptions.py +68 -1
  63. agno/filters.py +354 -0
  64. agno/guardrails/__init__.py +6 -0
  65. agno/guardrails/base.py +19 -0
  66. agno/guardrails/openai.py +144 -0
  67. agno/guardrails/pii.py +94 -0
  68. agno/guardrails/prompt_injection.py +52 -0
  69. agno/integrations/discord/client.py +1 -0
  70. agno/knowledge/chunking/agentic.py +13 -10
  71. agno/knowledge/chunking/fixed.py +1 -1
  72. agno/knowledge/chunking/semantic.py +40 -8
  73. agno/knowledge/chunking/strategy.py +59 -15
  74. agno/knowledge/embedder/aws_bedrock.py +9 -4
  75. agno/knowledge/embedder/azure_openai.py +54 -0
  76. agno/knowledge/embedder/base.py +2 -0
  77. agno/knowledge/embedder/cohere.py +184 -5
  78. agno/knowledge/embedder/fastembed.py +1 -1
  79. agno/knowledge/embedder/google.py +79 -1
  80. agno/knowledge/embedder/huggingface.py +9 -4
  81. agno/knowledge/embedder/jina.py +63 -0
  82. agno/knowledge/embedder/mistral.py +78 -11
  83. agno/knowledge/embedder/nebius.py +1 -1
  84. agno/knowledge/embedder/ollama.py +13 -0
  85. agno/knowledge/embedder/openai.py +37 -65
  86. agno/knowledge/embedder/sentence_transformer.py +8 -4
  87. agno/knowledge/embedder/vllm.py +262 -0
  88. agno/knowledge/embedder/voyageai.py +69 -16
  89. agno/knowledge/knowledge.py +595 -187
  90. agno/knowledge/reader/base.py +9 -2
  91. agno/knowledge/reader/csv_reader.py +8 -10
  92. agno/knowledge/reader/docx_reader.py +5 -6
  93. agno/knowledge/reader/field_labeled_csv_reader.py +290 -0
  94. agno/knowledge/reader/json_reader.py +6 -5
  95. agno/knowledge/reader/markdown_reader.py +13 -13
  96. agno/knowledge/reader/pdf_reader.py +43 -68
  97. agno/knowledge/reader/pptx_reader.py +101 -0
  98. agno/knowledge/reader/reader_factory.py +51 -6
  99. agno/knowledge/reader/s3_reader.py +3 -15
  100. agno/knowledge/reader/tavily_reader.py +194 -0
  101. agno/knowledge/reader/text_reader.py +13 -13
  102. agno/knowledge/reader/web_search_reader.py +2 -43
  103. agno/knowledge/reader/website_reader.py +43 -25
  104. agno/knowledge/reranker/__init__.py +3 -0
  105. agno/knowledge/types.py +9 -0
  106. agno/knowledge/utils.py +20 -0
  107. agno/media.py +339 -266
  108. agno/memory/manager.py +336 -82
  109. agno/models/aimlapi/aimlapi.py +2 -2
  110. agno/models/anthropic/claude.py +183 -37
  111. agno/models/aws/bedrock.py +52 -112
  112. agno/models/aws/claude.py +33 -1
  113. agno/models/azure/ai_foundry.py +33 -15
  114. agno/models/azure/openai_chat.py +25 -8
  115. agno/models/base.py +1011 -566
  116. agno/models/cerebras/cerebras.py +19 -13
  117. agno/models/cerebras/cerebras_openai.py +8 -5
  118. agno/models/cohere/chat.py +27 -1
  119. agno/models/cometapi/__init__.py +5 -0
  120. agno/models/cometapi/cometapi.py +57 -0
  121. agno/models/dashscope/dashscope.py +1 -0
  122. agno/models/deepinfra/deepinfra.py +2 -2
  123. agno/models/deepseek/deepseek.py +2 -2
  124. agno/models/fireworks/fireworks.py +2 -2
  125. agno/models/google/gemini.py +110 -37
  126. agno/models/groq/groq.py +28 -11
  127. agno/models/huggingface/huggingface.py +2 -1
  128. agno/models/internlm/internlm.py +2 -2
  129. agno/models/langdb/langdb.py +4 -4
  130. agno/models/litellm/chat.py +18 -1
  131. agno/models/litellm/litellm_openai.py +2 -2
  132. agno/models/llama_cpp/__init__.py +5 -0
  133. agno/models/llama_cpp/llama_cpp.py +22 -0
  134. agno/models/message.py +143 -4
  135. agno/models/meta/llama.py +27 -10
  136. agno/models/meta/llama_openai.py +5 -17
  137. agno/models/nebius/nebius.py +6 -6
  138. agno/models/nexus/__init__.py +3 -0
  139. agno/models/nexus/nexus.py +22 -0
  140. agno/models/nvidia/nvidia.py +2 -2
  141. agno/models/ollama/chat.py +60 -6
  142. agno/models/openai/chat.py +102 -43
  143. agno/models/openai/responses.py +103 -106
  144. agno/models/openrouter/openrouter.py +41 -3
  145. agno/models/perplexity/perplexity.py +4 -5
  146. agno/models/portkey/portkey.py +3 -3
  147. agno/models/requesty/__init__.py +5 -0
  148. agno/models/requesty/requesty.py +52 -0
  149. agno/models/response.py +81 -5
  150. agno/models/sambanova/sambanova.py +2 -2
  151. agno/models/siliconflow/__init__.py +5 -0
  152. agno/models/siliconflow/siliconflow.py +25 -0
  153. agno/models/together/together.py +2 -2
  154. agno/models/utils.py +254 -8
  155. agno/models/vercel/v0.py +2 -2
  156. agno/models/vertexai/__init__.py +0 -0
  157. agno/models/vertexai/claude.py +96 -0
  158. agno/models/vllm/vllm.py +1 -0
  159. agno/models/xai/xai.py +3 -2
  160. agno/os/app.py +543 -175
  161. agno/os/auth.py +24 -14
  162. agno/os/config.py +1 -0
  163. agno/os/interfaces/__init__.py +1 -0
  164. agno/os/interfaces/a2a/__init__.py +3 -0
  165. agno/os/interfaces/a2a/a2a.py +42 -0
  166. agno/os/interfaces/a2a/router.py +250 -0
  167. agno/os/interfaces/a2a/utils.py +924 -0
  168. agno/os/interfaces/agui/agui.py +23 -7
  169. agno/os/interfaces/agui/router.py +27 -3
  170. agno/os/interfaces/agui/utils.py +242 -142
  171. agno/os/interfaces/base.py +6 -2
  172. agno/os/interfaces/slack/router.py +81 -23
  173. agno/os/interfaces/slack/slack.py +29 -14
  174. agno/os/interfaces/whatsapp/router.py +11 -4
  175. agno/os/interfaces/whatsapp/whatsapp.py +14 -7
  176. agno/os/mcp.py +111 -54
  177. agno/os/middleware/__init__.py +7 -0
  178. agno/os/middleware/jwt.py +233 -0
  179. agno/os/router.py +556 -139
  180. agno/os/routers/evals/evals.py +71 -34
  181. agno/os/routers/evals/schemas.py +31 -31
  182. agno/os/routers/evals/utils.py +6 -5
  183. agno/os/routers/health.py +31 -0
  184. agno/os/routers/home.py +52 -0
  185. agno/os/routers/knowledge/knowledge.py +185 -38
  186. agno/os/routers/knowledge/schemas.py +82 -22
  187. agno/os/routers/memory/memory.py +158 -53
  188. agno/os/routers/memory/schemas.py +20 -16
  189. agno/os/routers/metrics/metrics.py +20 -8
  190. agno/os/routers/metrics/schemas.py +16 -16
  191. agno/os/routers/session/session.py +499 -38
  192. agno/os/schema.py +308 -198
  193. agno/os/utils.py +401 -41
  194. agno/reasoning/anthropic.py +80 -0
  195. agno/reasoning/azure_ai_foundry.py +2 -2
  196. agno/reasoning/deepseek.py +2 -2
  197. agno/reasoning/default.py +3 -1
  198. agno/reasoning/gemini.py +73 -0
  199. agno/reasoning/groq.py +2 -2
  200. agno/reasoning/ollama.py +2 -2
  201. agno/reasoning/openai.py +7 -2
  202. agno/reasoning/vertexai.py +76 -0
  203. agno/run/__init__.py +6 -0
  204. agno/run/agent.py +266 -112
  205. agno/run/base.py +53 -24
  206. agno/run/team.py +252 -111
  207. agno/run/workflow.py +156 -45
  208. agno/session/agent.py +105 -89
  209. agno/session/summary.py +65 -25
  210. agno/session/team.py +176 -96
  211. agno/session/workflow.py +406 -40
  212. agno/team/team.py +3854 -1692
  213. agno/tools/brightdata.py +3 -3
  214. agno/tools/cartesia.py +3 -5
  215. agno/tools/dalle.py +9 -8
  216. agno/tools/decorator.py +4 -2
  217. agno/tools/desi_vocal.py +2 -2
  218. agno/tools/duckduckgo.py +15 -11
  219. agno/tools/e2b.py +20 -13
  220. agno/tools/eleven_labs.py +26 -28
  221. agno/tools/exa.py +21 -16
  222. agno/tools/fal.py +4 -4
  223. agno/tools/file.py +153 -23
  224. agno/tools/file_generation.py +350 -0
  225. agno/tools/firecrawl.py +4 -4
  226. agno/tools/function.py +257 -37
  227. agno/tools/giphy.py +2 -2
  228. agno/tools/gmail.py +238 -14
  229. agno/tools/google_drive.py +270 -0
  230. agno/tools/googlecalendar.py +36 -8
  231. agno/tools/googlesheets.py +20 -5
  232. agno/tools/jira.py +20 -0
  233. agno/tools/knowledge.py +3 -3
  234. agno/tools/lumalab.py +3 -3
  235. agno/tools/mcp/__init__.py +10 -0
  236. agno/tools/mcp/mcp.py +331 -0
  237. agno/tools/mcp/multi_mcp.py +347 -0
  238. agno/tools/mcp/params.py +24 -0
  239. agno/tools/mcp_toolbox.py +284 -0
  240. agno/tools/mem0.py +11 -17
  241. agno/tools/memori.py +1 -53
  242. agno/tools/memory.py +419 -0
  243. agno/tools/models/azure_openai.py +2 -2
  244. agno/tools/models/gemini.py +3 -3
  245. agno/tools/models/groq.py +3 -5
  246. agno/tools/models/nebius.py +7 -7
  247. agno/tools/models_labs.py +25 -15
  248. agno/tools/notion.py +204 -0
  249. agno/tools/openai.py +4 -9
  250. agno/tools/opencv.py +3 -3
  251. agno/tools/parallel.py +314 -0
  252. agno/tools/replicate.py +7 -7
  253. agno/tools/scrapegraph.py +58 -31
  254. agno/tools/searxng.py +2 -2
  255. agno/tools/serper.py +2 -2
  256. agno/tools/slack.py +18 -3
  257. agno/tools/spider.py +2 -2
  258. agno/tools/tavily.py +146 -0
  259. agno/tools/whatsapp.py +1 -1
  260. agno/tools/workflow.py +278 -0
  261. agno/tools/yfinance.py +12 -11
  262. agno/utils/agent.py +820 -0
  263. agno/utils/audio.py +27 -0
  264. agno/utils/common.py +90 -1
  265. agno/utils/events.py +222 -7
  266. agno/utils/gemini.py +181 -23
  267. agno/utils/hooks.py +57 -0
  268. agno/utils/http.py +111 -0
  269. agno/utils/knowledge.py +12 -5
  270. agno/utils/log.py +1 -0
  271. agno/utils/mcp.py +95 -5
  272. agno/utils/media.py +188 -10
  273. agno/utils/merge_dict.py +22 -1
  274. agno/utils/message.py +60 -0
  275. agno/utils/models/claude.py +40 -11
  276. agno/utils/models/cohere.py +1 -1
  277. agno/utils/models/watsonx.py +1 -1
  278. agno/utils/openai.py +1 -1
  279. agno/utils/print_response/agent.py +105 -21
  280. agno/utils/print_response/team.py +103 -38
  281. agno/utils/print_response/workflow.py +251 -34
  282. agno/utils/reasoning.py +22 -1
  283. agno/utils/serialize.py +32 -0
  284. agno/utils/streamlit.py +16 -10
  285. agno/utils/string.py +41 -0
  286. agno/utils/team.py +98 -9
  287. agno/utils/tools.py +1 -1
  288. agno/vectordb/base.py +23 -4
  289. agno/vectordb/cassandra/cassandra.py +65 -9
  290. agno/vectordb/chroma/chromadb.py +182 -38
  291. agno/vectordb/clickhouse/clickhousedb.py +64 -11
  292. agno/vectordb/couchbase/couchbase.py +105 -10
  293. agno/vectordb/lancedb/lance_db.py +183 -135
  294. agno/vectordb/langchaindb/langchaindb.py +25 -7
  295. agno/vectordb/lightrag/lightrag.py +17 -3
  296. agno/vectordb/llamaindex/__init__.py +3 -0
  297. agno/vectordb/llamaindex/llamaindexdb.py +46 -7
  298. agno/vectordb/milvus/milvus.py +126 -9
  299. agno/vectordb/mongodb/__init__.py +7 -1
  300. agno/vectordb/mongodb/mongodb.py +112 -7
  301. agno/vectordb/pgvector/pgvector.py +142 -21
  302. agno/vectordb/pineconedb/pineconedb.py +80 -8
  303. agno/vectordb/qdrant/qdrant.py +125 -39
  304. agno/vectordb/redis/__init__.py +9 -0
  305. agno/vectordb/redis/redisdb.py +694 -0
  306. agno/vectordb/singlestore/singlestore.py +111 -25
  307. agno/vectordb/surrealdb/surrealdb.py +31 -5
  308. agno/vectordb/upstashdb/upstashdb.py +76 -8
  309. agno/vectordb/weaviate/weaviate.py +86 -15
  310. agno/workflow/__init__.py +2 -0
  311. agno/workflow/agent.py +299 -0
  312. agno/workflow/condition.py +112 -18
  313. agno/workflow/loop.py +69 -10
  314. agno/workflow/parallel.py +266 -118
  315. agno/workflow/router.py +110 -17
  316. agno/workflow/step.py +645 -136
  317. agno/workflow/steps.py +65 -6
  318. agno/workflow/types.py +71 -33
  319. agno/workflow/workflow.py +2113 -300
  320. agno-2.3.0.dist-info/METADATA +618 -0
  321. agno-2.3.0.dist-info/RECORD +577 -0
  322. agno-2.3.0.dist-info/licenses/LICENSE +201 -0
  323. agno/knowledge/reader/url_reader.py +0 -128
  324. agno/tools/googlesearch.py +0 -98
  325. agno/tools/mcp.py +0 -610
  326. agno/utils/models/aws_claude.py +0 -170
  327. agno-2.0.0rc2.dist-info/METADATA +0 -355
  328. agno-2.0.0rc2.dist-info/RECORD +0 -515
  329. agno-2.0.0rc2.dist-info/licenses/LICENSE +0 -375
  330. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/WHEEL +0 -0
  331. {agno-2.0.0rc2.dist-info → agno-2.3.0.dist-info}/top_level.txt +0 -0
agno/run/agent.py CHANGED
@@ -1,17 +1,133 @@
1
1
  from dataclasses import asdict, dataclass, field
2
2
  from enum import Enum
3
3
  from time import time
4
- from typing import Any, Dict, List, Optional, Sequence, Union
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence, Union
5
5
 
6
6
  from pydantic import BaseModel
7
7
 
8
- from agno.media import AudioArtifact, AudioResponse, File, ImageArtifact, VideoArtifact
8
+ from agno.media import Audio, File, Image, Video
9
9
  from agno.models.message import Citations, Message
10
10
  from agno.models.metrics import Metrics
11
11
  from agno.models.response import ToolExecution
12
12
  from agno.reasoning.step import ReasoningStep
13
13
  from agno.run.base import BaseRunOutputEvent, MessageReferences, RunStatus
14
14
  from agno.utils.log import logger
15
+ from agno.utils.media import (
16
+ reconstruct_audio_list,
17
+ reconstruct_files,
18
+ reconstruct_images,
19
+ reconstruct_response_audio,
20
+ reconstruct_videos,
21
+ )
22
+
23
+ if TYPE_CHECKING:
24
+ from agno.session.summary import SessionSummary
25
+
26
+
27
+ @dataclass
28
+ class RunInput:
29
+ """Container for the raw input data passed to Agent.run().
30
+
31
+ This captures the original input exactly as provided by the user,
32
+ separate from the processed messages that go to the model.
33
+
34
+ Attributes:
35
+ input_content: The literal input message/content passed to run()
36
+ images: Images directly passed to run()
37
+ videos: Videos directly passed to run()
38
+ audios: Audio files directly passed to run()
39
+ files: Files directly passed to run()
40
+ """
41
+
42
+ input_content: Union[str, List, Dict, Message, BaseModel, List[Message]]
43
+ images: Optional[Sequence[Image]] = None
44
+ videos: Optional[Sequence[Video]] = None
45
+ audios: Optional[Sequence[Audio]] = None
46
+ files: Optional[Sequence[File]] = None
47
+
48
+ def input_content_string(self) -> str:
49
+ import json
50
+
51
+ if isinstance(self.input_content, (str)):
52
+ return self.input_content
53
+ elif isinstance(self.input_content, BaseModel):
54
+ return self.input_content.model_dump_json(exclude_none=True)
55
+ elif isinstance(self.input_content, Message):
56
+ return json.dumps(self.input_content.to_dict())
57
+ elif isinstance(self.input_content, list) and self.input_content and isinstance(self.input_content[0], Message):
58
+ return json.dumps([m.to_dict() for m in self.input_content])
59
+ else:
60
+ return str(self.input_content)
61
+
62
+ def to_dict(self) -> Dict[str, Any]:
63
+ """Convert to dictionary representation"""
64
+ result: Dict[str, Any] = {}
65
+
66
+ if self.input_content is not None:
67
+ if isinstance(self.input_content, (str)):
68
+ result["input_content"] = self.input_content
69
+ elif isinstance(self.input_content, BaseModel):
70
+ result["input_content"] = self.input_content.model_dump(exclude_none=True)
71
+ elif isinstance(self.input_content, Message):
72
+ result["input_content"] = self.input_content.to_dict()
73
+
74
+ # Handle input_content provided as a list of Message objects
75
+ elif (
76
+ isinstance(self.input_content, list)
77
+ and self.input_content
78
+ and isinstance(self.input_content[0], Message)
79
+ ):
80
+ result["input_content"] = [m.to_dict() for m in self.input_content]
81
+
82
+ # Handle input_content provided as a list of dicts
83
+ elif (
84
+ isinstance(self.input_content, list) and self.input_content and isinstance(self.input_content[0], dict)
85
+ ):
86
+ for content in self.input_content:
87
+ # Handle media input
88
+ if isinstance(content, dict):
89
+ if content.get("images"):
90
+ content["images"] = [
91
+ img.to_dict() if isinstance(img, Image) else img for img in content["images"]
92
+ ]
93
+ if content.get("videos"):
94
+ content["videos"] = [
95
+ vid.to_dict() if isinstance(vid, Video) else vid for vid in content["videos"]
96
+ ]
97
+ if content.get("audios"):
98
+ content["audios"] = [
99
+ aud.to_dict() if isinstance(aud, Audio) else aud for aud in content["audios"]
100
+ ]
101
+ if content.get("files"):
102
+ content["files"] = [
103
+ file.to_dict() if isinstance(file, File) else file for file in content["files"]
104
+ ]
105
+ result["input_content"] = self.input_content
106
+ else:
107
+ result["input_content"] = self.input_content
108
+
109
+ if self.images:
110
+ result["images"] = [img.to_dict() for img in self.images]
111
+ if self.videos:
112
+ result["videos"] = [vid.to_dict() for vid in self.videos]
113
+ if self.audios:
114
+ result["audios"] = [aud.to_dict() for aud in self.audios]
115
+ if self.files:
116
+ result["files"] = [file.to_dict() for file in self.files]
117
+
118
+ return result
119
+
120
+ @classmethod
121
+ def from_dict(cls, data: Dict[str, Any]) -> "RunInput":
122
+ """Create RunInput from dictionary"""
123
+ images = reconstruct_images(data.get("images"))
124
+ videos = reconstruct_videos(data.get("videos"))
125
+ audios = reconstruct_audio_list(data.get("audios"))
126
+ files = reconstruct_files(data.get("files"))
127
+
128
+ return cls(
129
+ input_content=data.get("input_content", ""), images=images, videos=videos, audios=audios, files=files
130
+ )
15
131
 
16
132
 
17
133
  class RunEvent(str, Enum):
@@ -19,6 +135,7 @@ class RunEvent(str, Enum):
19
135
 
20
136
  run_started = "RunStarted"
21
137
  run_content = "RunContent"
138
+ run_content_completed = "RunContentCompleted"
22
139
  run_intermediate_content = "RunIntermediateContent"
23
140
  run_completed = "RunCompleted"
24
141
  run_error = "RunError"
@@ -27,6 +144,12 @@ class RunEvent(str, Enum):
27
144
  run_paused = "RunPaused"
28
145
  run_continued = "RunContinued"
29
146
 
147
+ pre_hook_started = "PreHookStarted"
148
+ pre_hook_completed = "PreHookCompleted"
149
+
150
+ post_hook_started = "PostHookStarted"
151
+ post_hook_completed = "PostHookCompleted"
152
+
30
153
  tool_call_started = "ToolCallStarted"
31
154
  tool_call_completed = "ToolCallCompleted"
32
155
 
@@ -37,6 +160,9 @@ class RunEvent(str, Enum):
37
160
  memory_update_started = "MemoryUpdateStarted"
38
161
  memory_update_completed = "MemoryUpdateCompleted"
39
162
 
163
+ session_summary_started = "SessionSummaryStarted"
164
+ session_summary_completed = "SessionSummaryCompleted"
165
+
40
166
  parser_model_response_started = "ParserModelResponseStarted"
41
167
  parser_model_response_completed = "ParserModelResponseCompleted"
42
168
 
@@ -53,6 +179,7 @@ class BaseAgentRunEvent(BaseRunOutputEvent):
53
179
  agent_id: str = ""
54
180
  agent_name: str = ""
55
181
  run_id: Optional[str] = None
182
+ parent_run_id: Optional[str] = None
56
183
  session_id: Optional[str] = None
57
184
 
58
185
  # Step context for workflow execution
@@ -94,17 +221,26 @@ class RunContentEvent(BaseAgentRunEvent):
94
221
 
95
222
  event: str = RunEvent.run_content.value
96
223
  content: Optional[Any] = None
224
+ workflow_agent: bool = (
225
+ False # Used by consumers of the events to distinguish between workflow agent and regular agent
226
+ )
97
227
  content_type: str = "str"
98
228
  reasoning_content: Optional[str] = None
229
+ model_provider_data: Optional[Dict[str, Any]] = None
99
230
  citations: Optional[Citations] = None
100
- response_audio: Optional[AudioResponse] = None # Model audio response
101
- image: Optional[ImageArtifact] = None # Image attached to the response
231
+ response_audio: Optional[Audio] = None # Model audio response
232
+ image: Optional[Image] = None # Image attached to the response
102
233
  references: Optional[List[MessageReferences]] = None
103
234
  additional_input: Optional[List[Message]] = None
104
235
  reasoning_steps: Optional[List[ReasoningStep]] = None
105
236
  reasoning_messages: Optional[List[Message]] = None
106
237
 
107
238
 
239
+ @dataclass
240
+ class RunContentCompletedEvent(BaseAgentRunEvent):
241
+ event: str = RunEvent.run_content_completed.value
242
+
243
+
108
244
  @dataclass
109
245
  class IntermediateRunContentEvent(BaseAgentRunEvent):
110
246
  event: str = RunEvent.run_intermediate_content.value
@@ -119,16 +255,18 @@ class RunCompletedEvent(BaseAgentRunEvent):
119
255
  content_type: str = "str"
120
256
  reasoning_content: Optional[str] = None
121
257
  citations: Optional[Citations] = None
122
- images: Optional[List[ImageArtifact]] = None # Images attached to the response
123
- videos: Optional[List[VideoArtifact]] = None # Videos attached to the response
124
- audio: Optional[List[AudioArtifact]] = None # Audio attached to the response
125
- response_audio: Optional[AudioResponse] = None # Model audio response
258
+ model_provider_data: Optional[Dict[str, Any]] = None
259
+ images: Optional[List[Image]] = None # Images attached to the response
260
+ videos: Optional[List[Video]] = None # Videos attached to the response
261
+ audio: Optional[List[Audio]] = None # Audio attached to the response
262
+ response_audio: Optional[Audio] = None # Model audio response
126
263
  references: Optional[List[MessageReferences]] = None
127
264
  additional_input: Optional[List[Message]] = None
128
265
  reasoning_steps: Optional[List[ReasoningStep]] = None
129
266
  reasoning_messages: Optional[List[Message]] = None
130
267
  metadata: Optional[Dict[str, Any]] = None
131
268
  metrics: Optional[Metrics] = None
269
+ session_state: Optional[Dict[str, Any]] = None
132
270
 
133
271
 
134
272
  @dataclass
@@ -151,6 +289,11 @@ class RunErrorEvent(BaseAgentRunEvent):
151
289
  event: str = RunEvent.run_error.value
152
290
  content: Optional[str] = None
153
291
 
292
+ # From exceptions
293
+ error_type: Optional[str] = None
294
+ error_id: Optional[str] = None
295
+ additional_data: Optional[Dict[str, Any]] = None
296
+
154
297
 
155
298
  @dataclass
156
299
  class RunCancelledEvent(BaseAgentRunEvent):
@@ -162,6 +305,32 @@ class RunCancelledEvent(BaseAgentRunEvent):
162
305
  return True
163
306
 
164
307
 
308
+ @dataclass
309
+ class PreHookStartedEvent(BaseAgentRunEvent):
310
+ event: str = RunEvent.pre_hook_started.value
311
+ pre_hook_name: Optional[str] = None
312
+ run_input: Optional[RunInput] = None
313
+
314
+
315
+ @dataclass
316
+ class PreHookCompletedEvent(BaseAgentRunEvent):
317
+ event: str = RunEvent.pre_hook_completed.value
318
+ pre_hook_name: Optional[str] = None
319
+ run_input: Optional[RunInput] = None
320
+
321
+
322
+ @dataclass
323
+ class PostHookStartedEvent(BaseAgentRunEvent):
324
+ event: str = RunEvent.post_hook_started.value
325
+ post_hook_name: Optional[str] = None
326
+
327
+
328
+ @dataclass
329
+ class PostHookCompletedEvent(BaseAgentRunEvent):
330
+ event: str = RunEvent.post_hook_completed.value
331
+ post_hook_name: Optional[str] = None
332
+
333
+
165
334
  @dataclass
166
335
  class MemoryUpdateStartedEvent(BaseAgentRunEvent):
167
336
  event: str = RunEvent.memory_update_started.value
@@ -172,6 +341,17 @@ class MemoryUpdateCompletedEvent(BaseAgentRunEvent):
172
341
  event: str = RunEvent.memory_update_completed.value
173
342
 
174
343
 
344
+ @dataclass
345
+ class SessionSummaryStartedEvent(BaseAgentRunEvent):
346
+ event: str = RunEvent.session_summary_started.value
347
+
348
+
349
+ @dataclass
350
+ class SessionSummaryCompletedEvent(BaseAgentRunEvent):
351
+ event: str = RunEvent.session_summary_completed.value
352
+ session_summary: Optional["SessionSummary"] = None
353
+
354
+
175
355
  @dataclass
176
356
  class ReasoningStartedEvent(BaseAgentRunEvent):
177
357
  event: str = RunEvent.reasoning_started.value
@@ -203,9 +383,9 @@ class ToolCallCompletedEvent(BaseAgentRunEvent):
203
383
  event: str = RunEvent.tool_call_completed.value
204
384
  tool: Optional[ToolExecution] = None
205
385
  content: Optional[Any] = None
206
- images: Optional[List[ImageArtifact]] = None # Images produced by the tool call
207
- videos: Optional[List[VideoArtifact]] = None # Videos produced by the tool call
208
- audio: Optional[List[AudioArtifact]] = None # Audio produced by the tool call
386
+ images: Optional[List[Image]] = None # Images produced by the tool call
387
+ videos: Optional[List[Video]] = None # Videos produced by the tool call
388
+ audio: Optional[List[Audio]] = None # Audio produced by the tool call
209
389
 
210
390
 
211
391
  @dataclass
@@ -232,21 +412,33 @@ class OutputModelResponseCompletedEvent(BaseAgentRunEvent):
232
412
  class CustomEvent(BaseAgentRunEvent):
233
413
  event: str = RunEvent.custom_event.value
234
414
 
415
+ def __init__(self, **kwargs):
416
+ # Store arbitrary attributes directly on the instance
417
+ for key, value in kwargs.items():
418
+ setattr(self, key, value)
419
+
235
420
 
236
421
  RunOutputEvent = Union[
237
422
  RunStartedEvent,
238
423
  RunContentEvent,
239
424
  IntermediateRunContentEvent,
425
+ RunContentCompletedEvent,
240
426
  RunCompletedEvent,
241
427
  RunErrorEvent,
242
428
  RunCancelledEvent,
243
429
  RunPausedEvent,
244
430
  RunContinuedEvent,
431
+ PreHookStartedEvent,
432
+ PreHookCompletedEvent,
433
+ PostHookStartedEvent,
434
+ PostHookCompletedEvent,
245
435
  ReasoningStartedEvent,
246
436
  ReasoningStepEvent,
247
437
  ReasoningCompletedEvent,
248
438
  MemoryUpdateStartedEvent,
249
439
  MemoryUpdateCompletedEvent,
440
+ SessionSummaryStartedEvent,
441
+ SessionSummaryCompletedEvent,
250
442
  ToolCallStartedEvent,
251
443
  ToolCallCompletedEvent,
252
444
  ParserModelResponseStartedEvent,
@@ -261,17 +453,24 @@ RunOutputEvent = Union[
261
453
  RUN_EVENT_TYPE_REGISTRY = {
262
454
  RunEvent.run_started.value: RunStartedEvent,
263
455
  RunEvent.run_content.value: RunContentEvent,
456
+ RunEvent.run_content_completed.value: RunContentCompletedEvent,
264
457
  RunEvent.run_intermediate_content.value: IntermediateRunContentEvent,
265
458
  RunEvent.run_completed.value: RunCompletedEvent,
266
459
  RunEvent.run_error.value: RunErrorEvent,
267
460
  RunEvent.run_cancelled.value: RunCancelledEvent,
268
461
  RunEvent.run_paused.value: RunPausedEvent,
269
462
  RunEvent.run_continued.value: RunContinuedEvent,
463
+ RunEvent.pre_hook_started.value: PreHookStartedEvent,
464
+ RunEvent.pre_hook_completed.value: PreHookCompletedEvent,
465
+ RunEvent.post_hook_started.value: PostHookStartedEvent,
466
+ RunEvent.post_hook_completed.value: PostHookCompletedEvent,
270
467
  RunEvent.reasoning_started.value: ReasoningStartedEvent,
271
468
  RunEvent.reasoning_step.value: ReasoningStepEvent,
272
469
  RunEvent.reasoning_completed.value: ReasoningCompletedEvent,
273
470
  RunEvent.memory_update_started.value: MemoryUpdateStartedEvent,
274
471
  RunEvent.memory_update_completed.value: MemoryUpdateCompletedEvent,
472
+ RunEvent.session_summary_started.value: SessionSummaryStartedEvent,
473
+ RunEvent.session_summary_completed.value: SessionSummaryCompletedEvent,
275
474
  RunEvent.tool_call_started.value: ToolCallStartedEvent,
276
475
  RunEvent.tool_call_completed.value: ToolCallCompletedEvent,
277
476
  RunEvent.parser_model_response_started.value: ParserModelResponseStartedEvent,
@@ -290,78 +489,6 @@ def run_output_event_from_dict(data: dict) -> BaseRunOutputEvent:
290
489
  return cls.from_dict(data) # type: ignore
291
490
 
292
491
 
293
- @dataclass
294
- class RunInput:
295
- """Container for the raw input data passed to Agent.run().
296
-
297
- This captures the original input exactly as provided by the user,
298
- separate from the processed messages that go to the model.
299
-
300
- Attributes:
301
- input_content: The literal input message/content passed to run()
302
- images: Images directly passed to run()
303
- videos: Videos directly passed to run()
304
- audios: Audio files directly passed to run()
305
- files: Files directly passed to run()
306
- """
307
-
308
- input_content: Optional[Union[str, List, Dict, Message, BaseModel, List[Message]]] = None
309
- images: Optional[Sequence[ImageArtifact]] = None
310
- videos: Optional[Sequence[VideoArtifact]] = None
311
- audios: Optional[Sequence[AudioArtifact]] = None
312
- files: Optional[Sequence[File]] = None
313
-
314
- def to_dict(self) -> Dict[str, Any]:
315
- """Convert to dictionary representation"""
316
- result: Dict[str, Any] = {}
317
-
318
- if self.input_content is not None:
319
- if isinstance(self.input_content, (str)):
320
- result["input_content"] = self.input_content
321
- elif isinstance(self.input_content, BaseModel):
322
- result["input_content"] = self.input_content.model_dump(exclude_none=True)
323
- elif isinstance(self.input_content, Message):
324
- result["input_content"] = self.input_content.to_dict()
325
- elif (
326
- isinstance(self.input_content, list)
327
- and self.input_content
328
- and isinstance(self.input_content[0], Message)
329
- ):
330
- result["input_content"] = [m.to_dict() for m in self.input_content]
331
- else:
332
- result["input_content"] = self.input_content
333
-
334
- if self.images:
335
- result["images"] = [img.to_dict() for img in self.images]
336
- if self.videos:
337
- result["videos"] = [vid.to_dict() for vid in self.videos]
338
- if self.audios:
339
- result["audios"] = [aud.to_dict() for aud in self.audios]
340
-
341
- return result
342
-
343
- @classmethod
344
- def from_dict(cls, data: Dict[str, Any]) -> "RunInput":
345
- """Create RunInput from dictionary"""
346
- images = None
347
- if data.get("images"):
348
- images = [ImageArtifact.model_validate(img_data) for img_data in data["images"]]
349
-
350
- videos = None
351
- if data.get("videos"):
352
- videos = [VideoArtifact.model_validate(vid_data) for vid_data in data["videos"]]
353
-
354
- audios = None
355
- if data.get("audios"):
356
- audios = [AudioArtifact.model_validate(aud_data) for aud_data in data["audios"]]
357
-
358
- files = None
359
- if data.get("files"):
360
- files = [File.model_validate(file_data) for file_data in data["files"]]
361
-
362
- return cls(input_content=data.get("input_content"), images=images, videos=videos, audios=audios, files=files)
363
-
364
-
365
492
  @dataclass
366
493
  class RunOutput:
367
494
  """Response returned by Agent.run() or Workflow.run() functions"""
@@ -374,6 +501,9 @@ class RunOutput:
374
501
  workflow_id: Optional[str] = None
375
502
  user_id: Optional[str] = None
376
503
 
504
+ # Input media and messages from user
505
+ input: Optional[RunInput] = None
506
+
377
507
  content: Optional[Any] = None
378
508
  content_type: str = "str"
379
509
 
@@ -381,6 +511,8 @@ class RunOutput:
381
511
  reasoning_steps: Optional[List[ReasoningStep]] = None
382
512
  reasoning_messages: Optional[List[Message]] = None
383
513
 
514
+ model_provider_data: Optional[Dict[str, Any]] = None
515
+
384
516
  model: Optional[str] = None
385
517
  model_provider: Optional[str] = None
386
518
  messages: Optional[List[Message]] = None
@@ -389,18 +521,17 @@ class RunOutput:
389
521
 
390
522
  tools: Optional[List[ToolExecution]] = None
391
523
 
392
- images: Optional[List[ImageArtifact]] = None # Images attached to the response
393
- videos: Optional[List[VideoArtifact]] = None # Videos attached to the response
394
- audio: Optional[List[AudioArtifact]] = None # Audio attached to the response
395
- response_audio: Optional[AudioResponse] = None # Model audio response
396
-
397
- # Input media and messages from user
398
- input: Optional[RunInput] = None
524
+ images: Optional[List[Image]] = None # Images attached to the response
525
+ videos: Optional[List[Video]] = None # Videos attached to the response
526
+ audio: Optional[List[Audio]] = None # Audio attached to the response
527
+ files: Optional[List[File]] = None # Files attached to the response
528
+ response_audio: Optional[Audio] = None # Model audio response
399
529
 
400
530
  citations: Optional[Citations] = None
401
531
  references: Optional[List[MessageReferences]] = None
402
532
 
403
533
  metadata: Optional[Dict[str, Any]] = None
534
+ session_state: Optional[Dict[str, Any]] = None
404
535
 
405
536
  created_at: int = field(default_factory=lambda: int(time()))
406
537
 
@@ -446,6 +577,7 @@ class RunOutput:
446
577
  "images",
447
578
  "videos",
448
579
  "audio",
580
+ "files",
449
581
  "response_audio",
450
582
  "input",
451
583
  "citations",
@@ -487,7 +619,7 @@ class RunOutput:
487
619
  if self.images is not None:
488
620
  _dict["images"] = []
489
621
  for img in self.images:
490
- if isinstance(img, ImageArtifact):
622
+ if isinstance(img, Image):
491
623
  _dict["images"].append(img.to_dict())
492
624
  else:
493
625
  _dict["images"].append(img)
@@ -495,7 +627,7 @@ class RunOutput:
495
627
  if self.videos is not None:
496
628
  _dict["videos"] = []
497
629
  for vid in self.videos:
498
- if isinstance(vid, VideoArtifact):
630
+ if isinstance(vid, Video):
499
631
  _dict["videos"].append(vid.to_dict())
500
632
  else:
501
633
  _dict["videos"].append(vid)
@@ -503,13 +635,21 @@ class RunOutput:
503
635
  if self.audio is not None:
504
636
  _dict["audio"] = []
505
637
  for aud in self.audio:
506
- if isinstance(aud, AudioArtifact):
638
+ if isinstance(aud, Audio):
507
639
  _dict["audio"].append(aud.to_dict())
508
640
  else:
509
641
  _dict["audio"].append(aud)
510
642
 
643
+ if self.files is not None:
644
+ _dict["files"] = []
645
+ for file in self.files:
646
+ if isinstance(file, File):
647
+ _dict["files"].append(file.to_dict())
648
+ else:
649
+ _dict["files"].append(file)
650
+
511
651
  if self.response_audio is not None:
512
- if isinstance(self.response_audio, AudioResponse):
652
+ if isinstance(self.response_audio, Audio):
513
653
  _dict["response_audio"] = self.response_audio.to_dict()
514
654
  else:
515
655
  _dict["response_audio"] = self.response_audio
@@ -536,7 +676,7 @@ class RunOutput:
536
676
 
537
677
  return _dict
538
678
 
539
- def to_json(self) -> str:
679
+ def to_json(self, separators=(", ", ": "), indent: Optional[int] = 2) -> str:
540
680
  import json
541
681
 
542
682
  try:
@@ -545,7 +685,10 @@ class RunOutput:
545
685
  logger.error("Failed to convert response to json", exc_info=True)
546
686
  raise
547
687
 
548
- return json.dumps(_dict, indent=2)
688
+ if indent is None:
689
+ return json.dumps(_dict, separators=separators)
690
+ else:
691
+ return json.dumps(_dict, indent=indent, separators=separators)
549
692
 
550
693
  @classmethod
551
694
  def from_dict(cls, data: Dict[str, Any]) -> "RunOutput":
@@ -553,10 +696,20 @@ class RunOutput:
553
696
  data = data.pop("run")
554
697
 
555
698
  events = data.pop("events", None)
556
- events = [run_output_event_from_dict(event) for event in events] if events else None
699
+ final_events = []
700
+ for event in events or []:
701
+ if "agent_id" in event:
702
+ event = run_output_event_from_dict(event)
703
+ else:
704
+ # Use the factory from response.py for agent events
705
+ from agno.run.team import team_run_output_event_from_dict
706
+
707
+ event = team_run_output_event_from_dict(event)
708
+ final_events.append(event)
709
+ events = final_events
557
710
 
558
711
  messages = data.pop("messages", None)
559
- messages = [Message.model_validate(message) for message in messages] if messages else None
712
+ messages = [Message.from_dict(message) for message in messages] if messages else None
560
713
 
561
714
  citations = data.pop("citations", None)
562
715
  citations = Citations.model_validate(citations) if citations else None
@@ -564,17 +717,11 @@ class RunOutput:
564
717
  tools = data.pop("tools", [])
565
718
  tools = [ToolExecution.from_dict(tool) for tool in tools] if tools else None
566
719
 
567
- images = data.pop("images", [])
568
- images = [ImageArtifact.model_validate(image) for image in images] if images else None
569
-
570
- videos = data.pop("videos", [])
571
- videos = [VideoArtifact.model_validate(video) for video in videos] if videos else None
572
-
573
- audio = data.pop("audio", [])
574
- audio = [AudioArtifact.model_validate(audio) for audio in audio] if audio else None
575
-
576
- response_audio = data.pop("response_audio", None)
577
- response_audio = AudioResponse.model_validate(response_audio) if response_audio else None
720
+ images = reconstruct_images(data.pop("images", []))
721
+ videos = reconstruct_videos(data.pop("videos", []))
722
+ audio = reconstruct_audio_list(data.pop("audio", []))
723
+ files = reconstruct_files(data.pop("files", []))
724
+ response_audio = reconstruct_response_audio(data.pop("response_audio", None))
578
725
 
579
726
  input_data = data.pop("input", None)
580
727
  input_obj = None
@@ -588,7 +735,7 @@ class RunOutput:
588
735
  additional_input = data.pop("additional_input", None)
589
736
 
590
737
  if additional_input is not None:
591
- additional_input = [Message.model_validate(message) for message in additional_input]
738
+ additional_input = [Message.from_dict(message) for message in additional_input]
592
739
 
593
740
  reasoning_steps = data.pop("reasoning_steps", None)
594
741
  if reasoning_steps is not None:
@@ -596,12 +743,18 @@ class RunOutput:
596
743
 
597
744
  reasoning_messages = data.pop("reasoning_messages", None)
598
745
  if reasoning_messages is not None:
599
- reasoning_messages = [Message.model_validate(message) for message in reasoning_messages]
746
+ reasoning_messages = [Message.from_dict(message) for message in reasoning_messages]
600
747
 
601
748
  references = data.pop("references", None)
602
749
  if references is not None:
603
750
  references = [MessageReferences.model_validate(reference) for reference in references]
604
751
 
752
+ # Filter data to only include fields that are actually defined in the RunOutput dataclass
753
+ from dataclasses import fields
754
+
755
+ supported_fields = {f.name for f in fields(cls)}
756
+ filtered_data = {k: v for k, v in data.items() if k in supported_fields}
757
+
605
758
  return cls(
606
759
  messages=messages,
607
760
  metrics=metrics,
@@ -610,6 +763,7 @@ class RunOutput:
610
763
  images=images,
611
764
  audio=audio,
612
765
  videos=videos,
766
+ files=files,
613
767
  response_audio=response_audio,
614
768
  input=input_obj,
615
769
  events=events,
@@ -617,7 +771,7 @@ class RunOutput:
617
771
  reasoning_steps=reasoning_steps,
618
772
  reasoning_messages=reasoning_messages,
619
773
  references=references,
620
- **data,
774
+ **filtered_data,
621
775
  )
622
776
 
623
777
  def get_content_as_string(self, **kwargs) -> str: