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
agno/utils/agent.py ADDED
@@ -0,0 +1,938 @@
1
+ from asyncio import Future, Task
2
+ from typing import TYPE_CHECKING, Any, AsyncIterator, Callable, Dict, Iterator, List, Optional, Sequence, Union
3
+
4
+ from agno.media import Audio, File, Image, Video
5
+ from agno.models.message import Message
6
+ from agno.models.metrics import Metrics
7
+ from agno.models.response import ModelResponse
8
+ from agno.run import RunContext
9
+ from agno.run.agent import RunEvent, RunInput, RunOutput, RunOutputEvent
10
+ from agno.run.team import RunOutputEvent as TeamRunOutputEvent
11
+ from agno.run.team import TeamRunOutput
12
+ from agno.session import AgentSession, TeamSession, WorkflowSession
13
+ from agno.utils.events import (
14
+ create_memory_update_completed_event,
15
+ create_memory_update_started_event,
16
+ create_team_memory_update_completed_event,
17
+ create_team_memory_update_started_event,
18
+ handle_event,
19
+ )
20
+ from agno.utils.log import log_debug, log_warning
21
+
22
+ if TYPE_CHECKING:
23
+ from agno.agent.agent import Agent
24
+ from agno.team.team import Team
25
+
26
+
27
+ async def await_for_open_threads(
28
+ memory_task: Optional[Task] = None,
29
+ cultural_knowledge_task: Optional[Task] = None,
30
+ ) -> None:
31
+ if memory_task is not None:
32
+ try:
33
+ await memory_task
34
+ except Exception as e:
35
+ log_warning(f"Error in memory creation: {str(e)}")
36
+
37
+ if cultural_knowledge_task is not None:
38
+ try:
39
+ await cultural_knowledge_task
40
+ except Exception as e:
41
+ log_warning(f"Error in cultural knowledge creation: {str(e)}")
42
+
43
+
44
+ def wait_for_open_threads(
45
+ memory_future: Optional[Future] = None, cultural_knowledge_future: Optional[Future] = None
46
+ ) -> None:
47
+ if memory_future is not None:
48
+ try:
49
+ memory_future.result()
50
+ except Exception as e:
51
+ log_warning(f"Error in memory creation: {str(e)}")
52
+
53
+ # Wait for cultural knowledge creation
54
+ if cultural_knowledge_future is not None:
55
+ try:
56
+ cultural_knowledge_future.result()
57
+ except Exception as e:
58
+ log_warning(f"Error in cultural knowledge creation: {str(e)}")
59
+
60
+
61
+ async def await_for_thread_tasks_stream(
62
+ run_response: Union[RunOutput, TeamRunOutput],
63
+ memory_task: Optional[Task] = None,
64
+ cultural_knowledge_task: Optional[Task] = None,
65
+ stream_events: bool = False,
66
+ events_to_skip: Optional[List[RunEvent]] = None,
67
+ store_events: bool = False,
68
+ ) -> AsyncIterator[RunOutputEvent]:
69
+ if memory_task is not None:
70
+ if stream_events:
71
+ if isinstance(run_response, TeamRunOutput):
72
+ yield handle_event( # type: ignore
73
+ create_team_memory_update_started_event(from_run_response=run_response),
74
+ run_response,
75
+ events_to_skip=events_to_skip, # type: ignore
76
+ store_events=store_events,
77
+ )
78
+ else:
79
+ yield handle_event( # type: ignore
80
+ create_memory_update_started_event(from_run_response=run_response),
81
+ run_response,
82
+ events_to_skip=events_to_skip, # type: ignore
83
+ store_events=store_events,
84
+ )
85
+ try:
86
+ await memory_task
87
+ except Exception as e:
88
+ log_warning(f"Error in memory creation: {str(e)}")
89
+ if stream_events:
90
+ if isinstance(run_response, TeamRunOutput):
91
+ yield handle_event( # type: ignore
92
+ create_team_memory_update_completed_event(from_run_response=run_response),
93
+ run_response,
94
+ events_to_skip=events_to_skip, # type: ignore
95
+ store_events=store_events,
96
+ )
97
+ else:
98
+ yield handle_event( # type: ignore
99
+ create_memory_update_completed_event(from_run_response=run_response),
100
+ run_response,
101
+ events_to_skip=events_to_skip, # type: ignore
102
+ store_events=store_events,
103
+ )
104
+
105
+ if cultural_knowledge_task is not None:
106
+ try:
107
+ await cultural_knowledge_task
108
+ except Exception as e:
109
+ log_warning(f"Error in cultural knowledge creation: {str(e)}")
110
+
111
+
112
+ def wait_for_thread_tasks_stream(
113
+ run_response: Union[TeamRunOutput, RunOutput],
114
+ memory_future: Optional[Future] = None,
115
+ cultural_knowledge_future: Optional[Future] = None,
116
+ stream_events: bool = False,
117
+ events_to_skip: Optional[List[RunEvent]] = None,
118
+ store_events: bool = False,
119
+ ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent]]:
120
+ if memory_future is not None:
121
+ if stream_events:
122
+ if isinstance(run_response, TeamRunOutput):
123
+ yield handle_event( # type: ignore
124
+ create_team_memory_update_started_event(from_run_response=run_response),
125
+ run_response,
126
+ events_to_skip=events_to_skip, # type: ignore
127
+ store_events=store_events,
128
+ )
129
+ else:
130
+ yield handle_event( # type: ignore
131
+ create_memory_update_started_event(from_run_response=run_response),
132
+ run_response,
133
+ events_to_skip=events_to_skip, # type: ignore
134
+ store_events=store_events,
135
+ )
136
+ try:
137
+ memory_future.result()
138
+ except Exception as e:
139
+ log_warning(f"Error in memory creation: {str(e)}")
140
+ if stream_events:
141
+ if isinstance(run_response, TeamRunOutput):
142
+ yield handle_event( # type: ignore
143
+ create_team_memory_update_completed_event(from_run_response=run_response),
144
+ run_response,
145
+ events_to_skip=events_to_skip, # type: ignore
146
+ store_events=store_events,
147
+ )
148
+ else:
149
+ yield handle_event( # type: ignore
150
+ create_memory_update_completed_event(from_run_response=run_response),
151
+ run_response,
152
+ events_to_skip=events_to_skip, # type: ignore
153
+ store_events=store_events,
154
+ )
155
+
156
+ # Wait for cultural knowledge creation
157
+ if cultural_knowledge_future is not None:
158
+ # TODO: Add events
159
+ try:
160
+ cultural_knowledge_future.result()
161
+ except Exception as e:
162
+ log_warning(f"Error in cultural knowledge creation: {str(e)}")
163
+
164
+
165
+ def collect_joint_images(
166
+ run_input: Optional[RunInput] = None,
167
+ session: Optional[Union[AgentSession, TeamSession]] = None,
168
+ ) -> Optional[Sequence[Image]]:
169
+ """Collect images from input, session history, and current run response."""
170
+ joint_images: List[Image] = []
171
+
172
+ # 1. Add images from current input
173
+ if run_input and run_input.images:
174
+ joint_images.extend(run_input.images)
175
+ log_debug(f"Added {len(run_input.images)} input images to joint list")
176
+
177
+ # 2. Add images from session history (from both input and generated sources)
178
+ try:
179
+ if session and session.runs:
180
+ for historical_run in session.runs:
181
+ # Add generated images from previous runs
182
+ if historical_run.images:
183
+ joint_images.extend(historical_run.images)
184
+ log_debug(
185
+ f"Added {len(historical_run.images)} generated images from historical run {historical_run.run_id}"
186
+ )
187
+
188
+ # Add input images from previous runs
189
+ if historical_run.input and historical_run.input.images:
190
+ joint_images.extend(historical_run.input.images)
191
+ log_debug(
192
+ f"Added {len(historical_run.input.images)} input images from historical run {historical_run.run_id}"
193
+ )
194
+ except Exception as e:
195
+ log_debug(f"Could not access session history for images: {e}")
196
+
197
+ if joint_images:
198
+ log_debug(f"Images Available to Model: {len(joint_images)} images")
199
+ return joint_images if joint_images else None
200
+
201
+
202
+ def collect_joint_videos(
203
+ run_input: Optional[RunInput] = None,
204
+ session: Optional[Union[AgentSession, TeamSession]] = None,
205
+ ) -> Optional[Sequence[Video]]:
206
+ """Collect videos from input, session history, and current run response."""
207
+ joint_videos: List[Video] = []
208
+
209
+ # 1. Add videos from current input
210
+ if run_input and run_input.videos:
211
+ joint_videos.extend(run_input.videos)
212
+ log_debug(f"Added {len(run_input.videos)} input videos to joint list")
213
+
214
+ # 2. Add videos from session history (from both input and generated sources)
215
+ try:
216
+ if session and session.runs:
217
+ for historical_run in session.runs:
218
+ # Add generated videos from previous runs
219
+ if historical_run.videos:
220
+ joint_videos.extend(historical_run.videos)
221
+ log_debug(
222
+ f"Added {len(historical_run.videos)} generated videos from historical run {historical_run.run_id}"
223
+ )
224
+
225
+ # Add input videos from previous runs
226
+ if historical_run.input and historical_run.input.videos:
227
+ joint_videos.extend(historical_run.input.videos)
228
+ log_debug(
229
+ f"Added {len(historical_run.input.videos)} input videos from historical run {historical_run.run_id}"
230
+ )
231
+ except Exception as e:
232
+ log_debug(f"Could not access session history for videos: {e}")
233
+
234
+ if joint_videos:
235
+ log_debug(f"Videos Available to Model: {len(joint_videos)} videos")
236
+ return joint_videos if joint_videos else None
237
+
238
+
239
+ def collect_joint_audios(
240
+ run_input: Optional[RunInput] = None,
241
+ session: Optional[Union[AgentSession, TeamSession]] = None,
242
+ ) -> Optional[Sequence[Audio]]:
243
+ """Collect audios from input, session history, and current run response."""
244
+ joint_audios: List[Audio] = []
245
+
246
+ # 1. Add audios from current input
247
+ if run_input and run_input.audios:
248
+ joint_audios.extend(run_input.audios)
249
+ log_debug(f"Added {len(run_input.audios)} input audios to joint list")
250
+
251
+ # 2. Add audios from session history (from both input and generated sources)
252
+ try:
253
+ if session and session.runs:
254
+ for historical_run in session.runs:
255
+ # Add generated audios from previous runs
256
+ if historical_run.audio:
257
+ joint_audios.extend(historical_run.audio)
258
+ log_debug(
259
+ f"Added {len(historical_run.audio)} generated audios from historical run {historical_run.run_id}"
260
+ )
261
+
262
+ # Add input audios from previous runs
263
+ if historical_run.input and historical_run.input.audios:
264
+ joint_audios.extend(historical_run.input.audios)
265
+ log_debug(
266
+ f"Added {len(historical_run.input.audios)} input audios from historical run {historical_run.run_id}"
267
+ )
268
+ except Exception as e:
269
+ log_debug(f"Could not access session history for audios: {e}")
270
+
271
+ if joint_audios:
272
+ log_debug(f"Audios Available to Model: {len(joint_audios)} audios")
273
+ return joint_audios if joint_audios else None
274
+
275
+
276
+ def collect_joint_files(
277
+ run_input: Optional[RunInput] = None,
278
+ ) -> Optional[Sequence[File]]:
279
+ """Collect files from input and session history."""
280
+ from agno.utils.log import log_debug
281
+
282
+ joint_files: List[File] = []
283
+
284
+ # 1. Add files from current input
285
+ if run_input and run_input.files:
286
+ joint_files.extend(run_input.files)
287
+
288
+ # TODO: Files aren't stored in session history yet and dont have a FileArtifact
289
+
290
+ if joint_files:
291
+ log_debug(f"Files Available to Model: {len(joint_files)} files")
292
+
293
+ return joint_files if joint_files else None
294
+
295
+
296
+ def store_media_util(run_response: Union[RunOutput, TeamRunOutput], model_response: ModelResponse):
297
+ """Store media from model response in run_response for persistence"""
298
+ # Handle generated media fields from ModelResponse (generated media)
299
+ if model_response.images is not None:
300
+ for image in model_response.images:
301
+ if run_response.images is None:
302
+ run_response.images = []
303
+ run_response.images.append(image) # Generated images go to run_response.images
304
+
305
+ if model_response.videos is not None:
306
+ for video in model_response.videos:
307
+ if run_response.videos is None:
308
+ run_response.videos = []
309
+ run_response.videos.append(video) # Generated videos go to run_response.videos
310
+
311
+ if model_response.audios is not None:
312
+ for audio in model_response.audios:
313
+ if run_response.audio is None:
314
+ run_response.audio = []
315
+ run_response.audio.append(audio) # Generated audio go to run_response.audio
316
+
317
+ if model_response.files is not None:
318
+ for file in model_response.files:
319
+ if run_response.files is None:
320
+ run_response.files = []
321
+ run_response.files.append(file) # Generated files go to run_response.files
322
+
323
+
324
+ def validate_media_object_id(
325
+ images: Optional[Sequence[Image]] = None,
326
+ videos: Optional[Sequence[Video]] = None,
327
+ audios: Optional[Sequence[Audio]] = None,
328
+ files: Optional[Sequence[File]] = None,
329
+ ) -> tuple:
330
+ image_list = None
331
+ if images:
332
+ image_list = []
333
+ for img in images:
334
+ if not img.id:
335
+ from uuid import uuid4
336
+
337
+ img.id = str(uuid4())
338
+ image_list.append(img)
339
+
340
+ video_list = None
341
+ if videos:
342
+ video_list = []
343
+ for vid in videos:
344
+ if not vid.id:
345
+ from uuid import uuid4
346
+
347
+ vid.id = str(uuid4())
348
+ video_list.append(vid)
349
+
350
+ audio_list = None
351
+ if audios:
352
+ audio_list = []
353
+ for aud in audios:
354
+ if not aud.id:
355
+ from uuid import uuid4
356
+
357
+ aud.id = str(uuid4())
358
+ audio_list.append(aud)
359
+
360
+ file_list = None
361
+ if files:
362
+ file_list = []
363
+ for file in files:
364
+ if not file.id:
365
+ from uuid import uuid4
366
+
367
+ file.id = str(uuid4())
368
+ file_list.append(file)
369
+
370
+ return image_list, video_list, audio_list, file_list
371
+
372
+
373
+ def scrub_media_from_run_output(run_response: Union[RunOutput, TeamRunOutput]) -> None:
374
+ """
375
+ Completely remove all media from RunOutput when store_media=False.
376
+ This includes media in input, output artifacts, and all messages.
377
+ """
378
+ # 1. Scrub RunInput media
379
+ if run_response.input is not None:
380
+ run_response.input.images = []
381
+ run_response.input.videos = []
382
+ run_response.input.audios = []
383
+ run_response.input.files = []
384
+
385
+ # 3. Scrub media from all messages
386
+ if run_response.messages:
387
+ for message in run_response.messages:
388
+ scrub_media_from_message(message)
389
+
390
+ # 4. Scrub media from additional_input messages if any
391
+ if run_response.additional_input:
392
+ for message in run_response.additional_input:
393
+ scrub_media_from_message(message)
394
+
395
+ # 5. Scrub media from reasoning_messages if any
396
+ if run_response.reasoning_messages:
397
+ for message in run_response.reasoning_messages:
398
+ scrub_media_from_message(message)
399
+
400
+
401
+ def scrub_media_from_message(message: Message) -> None:
402
+ """Remove all media from a Message object."""
403
+ # Input media
404
+ message.images = None
405
+ message.videos = None
406
+ message.audio = None
407
+ message.files = None
408
+
409
+ # Output media
410
+ message.audio_output = None
411
+ message.image_output = None
412
+ message.video_output = None
413
+
414
+
415
+ def scrub_tool_results_from_run_output(run_response: Union[RunOutput, TeamRunOutput]) -> None:
416
+ """
417
+ Remove all tool-related data from RunOutput when store_tool_messages=False.
418
+ This removes both the tool call and its corresponding result to maintain API consistency.
419
+ """
420
+ if not run_response.messages:
421
+ return
422
+
423
+ # Step 1: Collect all tool_call_ids from tool result messages
424
+ tool_call_ids_to_remove = set()
425
+ for message in run_response.messages:
426
+ if message.role == "tool" and message.tool_call_id:
427
+ tool_call_ids_to_remove.add(message.tool_call_id)
428
+
429
+ # Step 2: Remove tool result messages (role="tool")
430
+ run_response.messages = [msg for msg in run_response.messages if msg.role != "tool"]
431
+
432
+ # Step 3: Remove assistant messages that made those tool calls
433
+ filtered_messages = []
434
+ for message in run_response.messages:
435
+ # Check if this assistant message made any of the tool calls we're removing
436
+ should_remove = False
437
+ if message.role == "assistant" and message.tool_calls:
438
+ for tool_call in message.tool_calls:
439
+ if tool_call.get("id") in tool_call_ids_to_remove:
440
+ should_remove = True
441
+ break
442
+
443
+ if not should_remove:
444
+ filtered_messages.append(message)
445
+
446
+ run_response.messages = filtered_messages
447
+
448
+
449
+ def scrub_history_messages_from_run_output(run_response: Union[RunOutput, TeamRunOutput]) -> None:
450
+ """
451
+ Remove all history messages from TeamRunOutput when store_history_messages=False.
452
+ This removes messages that were loaded from the team's memory.
453
+ """
454
+ # Remove messages with from_history=True
455
+ if run_response.messages:
456
+ run_response.messages = [msg for msg in run_response.messages if not msg.from_history]
457
+
458
+
459
+ def get_run_output_util(
460
+ entity: Union["Agent", "Team"], run_id: str, session_id: Optional[str] = None
461
+ ) -> Optional[
462
+ Union[
463
+ RunOutput,
464
+ TeamRunOutput,
465
+ ]
466
+ ]:
467
+ """
468
+ Get a RunOutput from the database.
469
+
470
+ Args:
471
+ run_id (str): The run_id to load from storage.
472
+ session_id (Optional[str]): The session_id to load from storage.
473
+ """
474
+ if session_id is not None:
475
+ if entity._has_async_db():
476
+ raise ValueError("Async database not supported for sync functions")
477
+
478
+ session = entity.get_session(session_id=session_id)
479
+ if session is not None:
480
+ run_response = session.get_run(run_id=run_id)
481
+ if run_response is not None:
482
+ return run_response # type: ignore
483
+ else:
484
+ log_warning(f"RunOutput {run_id} not found in Session {session_id}")
485
+ elif entity.cached_session is not None:
486
+ run_response = entity.cached_session.get_run(run_id=run_id)
487
+ if run_response is not None:
488
+ return run_response # type: ignore
489
+ else:
490
+ log_warning(f"RunOutput {run_id} not found in Session {entity.cached_session.session_id}")
491
+ return None
492
+ return None
493
+
494
+
495
+ async def aget_run_output_util(
496
+ entity: Union["Agent", "Team"], run_id: str, session_id: Optional[str] = None
497
+ ) -> Optional[Union[RunOutput, TeamRunOutput]]:
498
+ """
499
+ Get a RunOutput from the database.
500
+
501
+ Args:
502
+ run_id (str): The run_id to load from storage.
503
+ session_id (Optional[str]): The session_id to load from storage.
504
+ """
505
+ if session_id is not None:
506
+ session = await entity.aget_session(session_id=session_id)
507
+ if session is not None:
508
+ run_response = session.get_run(run_id=run_id)
509
+ if run_response is not None:
510
+ return run_response # type: ignore
511
+ else:
512
+ log_warning(f"RunOutput {run_id} not found in Session {session_id}")
513
+ elif entity.cached_session is not None:
514
+ run_response = entity.cached_session.get_run(run_id=run_id)
515
+ if run_response is not None:
516
+ return run_response
517
+ else:
518
+ log_warning(f"RunOutput {run_id} not found in Session {entity.cached_session.session_id}")
519
+ return None
520
+ return None
521
+
522
+
523
+ def get_last_run_output_util(
524
+ entity: Union["Agent", "Team"], session_id: Optional[str] = None
525
+ ) -> Optional[Union[RunOutput, TeamRunOutput]]:
526
+ """
527
+ Get the last run response from the database.
528
+
529
+ Args:
530
+ session_id (Optional[str]): The session_id to load from storage.
531
+
532
+ Returns:
533
+ RunOutput: The last run response from the database.
534
+ """
535
+ if session_id is not None:
536
+ if entity._has_async_db():
537
+ raise ValueError("Async database not supported for sync functions")
538
+
539
+ session = entity.get_session(session_id=session_id)
540
+ if session is not None and session.runs is not None and len(session.runs) > 0:
541
+ for run_output in reversed(session.runs):
542
+ if entity.__class__.__name__ == "Agent":
543
+ if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
544
+ return run_output # type: ignore
545
+ elif entity.__class__.__name__ == "Team":
546
+ if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
547
+ return run_output # type: ignore
548
+ else:
549
+ log_warning(f"No run responses found in Session {session_id}")
550
+
551
+ elif (
552
+ entity.cached_session is not None
553
+ and entity.cached_session.runs is not None
554
+ and len(entity.cached_session.runs) > 0
555
+ ):
556
+ for run_output in reversed(entity.cached_session.runs):
557
+ if entity.__class__.__name__ == "Agent":
558
+ if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
559
+ return run_output # type: ignore
560
+ elif entity.__class__.__name__ == "Team":
561
+ if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
562
+ return run_output # type: ignore
563
+ return None
564
+
565
+
566
+ async def aget_last_run_output_util(
567
+ entity: Union["Agent", "Team"], session_id: Optional[str] = None
568
+ ) -> Optional[Union[RunOutput, TeamRunOutput]]:
569
+ """
570
+ Get the last run response from the database.
571
+
572
+ Args:
573
+ session_id (Optional[str]): The session_id to load from storage.
574
+
575
+ Returns:
576
+ RunOutput: The last run response from the database.
577
+ """
578
+ if session_id is not None:
579
+ session = await entity.aget_session(session_id=session_id)
580
+ if session is not None and session.runs is not None and len(session.runs) > 0:
581
+ for run_output in reversed(session.runs):
582
+ if entity.__class__.__name__ == "Agent":
583
+ if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
584
+ return run_output # type: ignore
585
+ elif entity.__class__.__name__ == "Team":
586
+ if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
587
+ return run_output # type: ignore
588
+ else:
589
+ log_warning(f"No run responses found in Session {session_id}")
590
+
591
+ elif (
592
+ entity.cached_session is not None
593
+ and entity.cached_session.runs is not None
594
+ and len(entity.cached_session.runs) > 0
595
+ ):
596
+ for run_output in reversed(entity.cached_session.runs):
597
+ if entity.__class__.__name__ == "Agent":
598
+ if hasattr(run_output, "agent_id") and run_output.agent_id == entity.id:
599
+ return run_output # type: ignore
600
+ elif entity.__class__.__name__ == "Team":
601
+ if hasattr(run_output, "team_id") and run_output.team_id == entity.id:
602
+ return run_output # type: ignore
603
+ return None
604
+
605
+
606
+ def set_session_name_util(
607
+ entity: Union["Agent", "Team"], session_id: str, autogenerate: bool = False, session_name: Optional[str] = None
608
+ ) -> Union[AgentSession, TeamSession, WorkflowSession]:
609
+ """Set the session name and save to storage"""
610
+ if entity._has_async_db():
611
+ raise ValueError("Async database not supported for sync functions")
612
+
613
+ session = entity.get_session(session_id=session_id) # type: ignore
614
+
615
+ if session is None:
616
+ raise Exception("No session found")
617
+
618
+ # -*- Generate name for session
619
+ if autogenerate:
620
+ session_name = entity.generate_session_name(session=session) # type: ignore
621
+ log_debug(f"Generated Session Name: {session_name}")
622
+ elif session_name is None:
623
+ raise Exception("No session name provided")
624
+
625
+ # -*- Rename session
626
+ if session.session_data is None:
627
+ session.session_data = {"session_name": session_name}
628
+ else:
629
+ session.session_data["session_name"] = session_name
630
+ # -*- Save to storage
631
+ entity.save_session(session=session) # type: ignore
632
+
633
+ return session
634
+
635
+
636
+ async def aset_session_name_util(
637
+ entity: Union["Agent", "Team"], session_id: str, autogenerate: bool = False, session_name: Optional[str] = None
638
+ ) -> Union[AgentSession, TeamSession, WorkflowSession]:
639
+ """Set the session name and save to storage"""
640
+ session = await entity.aget_session(session_id=session_id) # type: ignore
641
+
642
+ if session is None:
643
+ raise Exception("Session not found")
644
+
645
+ # -*- Generate name for session
646
+ if autogenerate:
647
+ session_name = entity.generate_session_name(session=session) # type: ignore
648
+ log_debug(f"Generated Session Name: {session_name}")
649
+ elif session_name is None:
650
+ raise Exception("No session name provided")
651
+
652
+ # -*- Rename session
653
+ if session.session_data is None:
654
+ session.session_data = {"session_name": session_name}
655
+ else:
656
+ session.session_data["session_name"] = session_name
657
+
658
+ # -*- Save to storage
659
+ await entity.asave_session(session=session) # type: ignore
660
+
661
+ return session
662
+
663
+
664
+ def get_session_name_util(entity: Union["Agent", "Team"], session_id: str) -> str:
665
+ """Get the session name for the given session ID and user ID."""
666
+
667
+ if entity._has_async_db():
668
+ raise ValueError("Async database not supported for sync functions")
669
+
670
+ session = entity.get_session(session_id=session_id) # type: ignore
671
+ if session is None:
672
+ raise Exception("Session not found")
673
+ return session.session_data.get("session_name", "") if session.session_data is not None else "" # type: ignore
674
+
675
+
676
+ async def aget_session_name_util(entity: Union["Agent", "Team"], session_id: str) -> str:
677
+ """Get the session name for the given session ID and user ID."""
678
+ session = await entity.aget_session(session_id=session_id) # type: ignore
679
+ if session is None:
680
+ raise Exception("Session not found")
681
+ return session.session_data.get("session_name", "") if session.session_data is not None else "" # type: ignore
682
+
683
+
684
+ def get_session_state_util(entity: Union["Agent", "Team"], session_id: str) -> Dict[str, Any]:
685
+ """Get the session state for the given session ID and user ID."""
686
+ if entity._has_async_db():
687
+ raise ValueError("Async database not supported for sync functions")
688
+
689
+ session = entity.get_session(session_id=session_id) # type: ignore
690
+ if session is None:
691
+ raise Exception("Session not found")
692
+ return session.session_data.get("session_state", {}) if session.session_data is not None else {} # type: ignore
693
+
694
+
695
+ async def aget_session_state_util(entity: Union["Agent", "Team"], session_id: str) -> Dict[str, Any]:
696
+ """Get the session state for the given session ID and user ID."""
697
+ session = await entity.aget_session(session_id=session_id) # type: ignore
698
+ if session is None:
699
+ raise Exception("Session not found")
700
+ return session.session_data.get("session_state", {}) if session.session_data is not None else {} # type: ignore
701
+
702
+
703
+ def update_session_state_util(
704
+ entity: Union["Agent", "Team"], session_state_updates: Dict[str, Any], session_id: str
705
+ ) -> str:
706
+ """
707
+ Update the session state for the given session ID and user ID.
708
+ Args:
709
+ session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
710
+ session_id: The session ID to update. If not provided, the current cached session ID is used.
711
+ Returns:
712
+ dict: The updated session state.
713
+ """
714
+ if entity._has_async_db():
715
+ raise ValueError("Async database not supported for sync functions")
716
+
717
+ session = entity.get_session(session_id=session_id) # type: ignore
718
+ if session is None:
719
+ raise Exception("Session not found")
720
+
721
+ if session.session_data is not None and "session_state" not in session.session_data:
722
+ session.session_data["session_state"] = {}
723
+
724
+ for key, value in session_state_updates.items():
725
+ session.session_data["session_state"][key] = value # type: ignore
726
+
727
+ entity.save_session(session=session) # type: ignore
728
+
729
+ return session.session_data["session_state"] # type: ignore
730
+
731
+
732
+ async def aupdate_session_state_util(
733
+ entity: Union["Agent", "Team"], session_state_updates: Dict[str, Any], session_id: str
734
+ ) -> str:
735
+ """
736
+ Update the session state for the given session ID and user ID.
737
+ Args:
738
+ session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
739
+ session_id: The session ID to update. If not provided, the current cached session ID is used.
740
+ Returns:
741
+ dict: The updated session state.
742
+ """
743
+ session = await entity.aget_session(session_id=session_id) # type: ignore
744
+ if session is None:
745
+ raise Exception("Session not found")
746
+
747
+ if session.session_data is not None and "session_state" not in session.session_data:
748
+ session.session_data["session_state"] = {}
749
+
750
+ for key, value in session_state_updates.items():
751
+ session.session_data["session_state"][key] = value # type: ignore
752
+
753
+ await entity.asave_session(session=session) # type: ignore
754
+
755
+ return session.session_data["session_state"] # type: ignore
756
+
757
+
758
+ def get_session_metrics_util(entity: Union["Agent", "Team"], session_id: str) -> Optional[Metrics]:
759
+ """Get the session metrics for the given session ID and user ID."""
760
+ if entity._has_async_db():
761
+ raise ValueError("Async database not supported for sync functions")
762
+
763
+ session = entity.get_session(session_id=session_id) # type: ignore
764
+ if session is None:
765
+ raise Exception("Session not found")
766
+
767
+ if session.session_data is not None:
768
+ if isinstance(session.session_data.get("session_metrics"), dict):
769
+ return Metrics(**session.session_data.get("session_metrics", {}))
770
+ elif isinstance(session.session_data.get("session_metrics"), Metrics):
771
+ return session.session_data.get("session_metrics")
772
+ return None
773
+
774
+
775
+ async def aget_session_metrics_util(entity: Union["Agent", "Team"], session_id: str) -> Optional[Metrics]:
776
+ """Get the session metrics for the given session ID and user ID."""
777
+ session = await entity.aget_session(session_id=session_id) # type: ignore
778
+ if session is None:
779
+ raise Exception("Session not found")
780
+
781
+ if session.session_data is not None:
782
+ if isinstance(session.session_data.get("session_metrics"), dict):
783
+ return Metrics(**session.session_data.get("session_metrics", {}))
784
+ elif isinstance(session.session_data.get("session_metrics"), Metrics):
785
+ return session.session_data.get("session_metrics")
786
+ return None
787
+
788
+
789
+ def get_chat_history_util(entity: Union["Agent", "Team"], session_id: str) -> List[Message]:
790
+ """Read the chat history from the session
791
+
792
+ Args:
793
+ session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
794
+ Returns:
795
+ List[Message]: The chat history from the session.
796
+ """
797
+ if entity._has_async_db():
798
+ raise ValueError("Async database not supported for sync functions")
799
+
800
+ session = entity.get_session(session_id=session_id) # type: ignore
801
+
802
+ if session is None:
803
+ raise Exception("Session not found")
804
+
805
+ return session.get_chat_history() # type: ignore
806
+
807
+
808
+ async def aget_chat_history_util(entity: Union["Agent", "Team"], session_id: str) -> List[Message]:
809
+ """Read the chat history from the session
810
+
811
+ Args:
812
+ session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
813
+ Returns:
814
+ List[Message]: The chat history from the session.
815
+ """
816
+ session = await entity.aget_session(session_id=session_id) # type: ignore
817
+
818
+ if session is None:
819
+ raise Exception("Session not found")
820
+
821
+ return session.get_chat_history() # type: ignore
822
+
823
+
824
+ def execute_instructions(
825
+ instructions: Callable,
826
+ agent: Optional[Union["Agent", "Team"]] = None,
827
+ team: Optional["Team"] = None,
828
+ session_state: Optional[Dict[str, Any]] = None,
829
+ run_context: Optional[RunContext] = None,
830
+ ) -> Union[str, List[str]]:
831
+ """Execute the instructions function."""
832
+ import inspect
833
+
834
+ signature = inspect.signature(instructions)
835
+ instruction_args: Dict[str, Any] = {}
836
+
837
+ # Check for agent parameter
838
+ if "agent" in signature.parameters:
839
+ instruction_args["agent"] = agent
840
+
841
+ if "team" in signature.parameters:
842
+ instruction_args["team"] = team
843
+
844
+ # Check for session_state parameter
845
+ if "session_state" in signature.parameters:
846
+ instruction_args["session_state"] = session_state if session_state is not None else {}
847
+
848
+ # Check for run_context parameter
849
+ if "run_context" in signature.parameters:
850
+ instruction_args["run_context"] = run_context or None
851
+
852
+ # Run the instructions function, await if it's awaitable, otherwise run directly (in thread)
853
+ if inspect.iscoroutinefunction(instructions):
854
+ raise Exception("Instructions function is async, use `agent.arun()` instead")
855
+
856
+ # Run the instructions function
857
+ return instructions(**instruction_args)
858
+
859
+
860
+ def execute_system_message(
861
+ system_message: Callable,
862
+ agent: Optional[Union["Agent", "Team"]] = None,
863
+ team: Optional["Team"] = None,
864
+ session_state: Optional[Dict[str, Any]] = None,
865
+ run_context: Optional[RunContext] = None,
866
+ ) -> str:
867
+ """Execute the system message function."""
868
+ import inspect
869
+
870
+ signature = inspect.signature(system_message)
871
+ system_message_args: Dict[str, Any] = {}
872
+
873
+ # Check for agent parameter
874
+ if "agent" in signature.parameters:
875
+ system_message_args["agent"] = agent
876
+ if "team" in signature.parameters:
877
+ system_message_args["team"] = team
878
+ if inspect.iscoroutinefunction(system_message):
879
+ raise ValueError("System message function is async, use `agent.arun()` instead")
880
+
881
+ return system_message(**system_message_args)
882
+
883
+
884
+ async def aexecute_instructions(
885
+ instructions: Callable,
886
+ agent: Optional[Union["Agent", "Team"]] = None,
887
+ team: Optional["Team"] = None,
888
+ session_state: Optional[Dict[str, Any]] = None,
889
+ run_context: Optional[RunContext] = None,
890
+ ) -> Union[str, List[str]]:
891
+ """Execute the instructions function."""
892
+ import inspect
893
+
894
+ signature = inspect.signature(instructions)
895
+ instruction_args: Dict[str, Any] = {}
896
+
897
+ # Check for agent parameter
898
+ if "agent" in signature.parameters:
899
+ instruction_args["agent"] = agent
900
+ if "team" in signature.parameters:
901
+ instruction_args["team"] = team
902
+
903
+ # Check for session_state parameter
904
+ if "session_state" in signature.parameters:
905
+ instruction_args["session_state"] = session_state if session_state is not None else {}
906
+
907
+ # Check for run_context parameter
908
+ if "run_context" in signature.parameters:
909
+ instruction_args["run_context"] = run_context or None
910
+
911
+ if inspect.iscoroutinefunction(instructions):
912
+ return await instructions(**instruction_args)
913
+ else:
914
+ return instructions(**instruction_args)
915
+
916
+
917
+ async def aexecute_system_message(
918
+ system_message: Callable,
919
+ agent: Optional[Union["Agent", "Team"]] = None,
920
+ team: Optional["Team"] = None,
921
+ session_state: Optional[Dict[str, Any]] = None,
922
+ run_context: Optional[RunContext] = None,
923
+ ) -> str:
924
+ import inspect
925
+
926
+ signature = inspect.signature(system_message)
927
+ system_message_args: Dict[str, Any] = {}
928
+
929
+ # Check for agent parameter
930
+ if "agent" in signature.parameters:
931
+ system_message_args["agent"] = agent
932
+ if "team" in signature.parameters:
933
+ system_message_args["team"] = team
934
+
935
+ if inspect.iscoroutinefunction(system_message):
936
+ return await system_message(**system_message_args)
937
+ else:
938
+ return system_message(**system_message_args)