agno 2.3.4__py3-none-any.whl → 2.3.6__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 (116) hide show
  1. agno/agent/agent.py +184 -45
  2. agno/culture/manager.py +2 -2
  3. agno/db/base.py +330 -8
  4. agno/db/dynamo/dynamo.py +722 -2
  5. agno/db/dynamo/schemas.py +127 -0
  6. agno/db/firestore/firestore.py +573 -1
  7. agno/db/firestore/schemas.py +40 -0
  8. agno/db/gcs_json/gcs_json_db.py +446 -1
  9. agno/db/in_memory/in_memory_db.py +143 -1
  10. agno/db/json/json_db.py +438 -1
  11. agno/db/mongo/async_mongo.py +522 -0
  12. agno/db/mongo/mongo.py +523 -1
  13. agno/db/mongo/schemas.py +29 -0
  14. agno/db/mysql/mysql.py +536 -3
  15. agno/db/mysql/schemas.py +38 -0
  16. agno/db/postgres/async_postgres.py +553 -15
  17. agno/db/postgres/postgres.py +544 -5
  18. agno/db/postgres/schemas.py +38 -0
  19. agno/db/redis/redis.py +468 -1
  20. agno/db/redis/schemas.py +32 -0
  21. agno/db/singlestore/schemas.py +38 -0
  22. agno/db/singlestore/singlestore.py +523 -1
  23. agno/db/sqlite/async_sqlite.py +549 -10
  24. agno/db/sqlite/schemas.py +38 -0
  25. agno/db/sqlite/sqlite.py +540 -9
  26. agno/db/sqlite/utils.py +6 -8
  27. agno/db/surrealdb/models.py +25 -0
  28. agno/db/surrealdb/surrealdb.py +548 -1
  29. agno/eval/accuracy.py +18 -8
  30. agno/eval/performance.py +10 -4
  31. agno/eval/reliability.py +22 -13
  32. agno/exceptions.py +11 -0
  33. agno/hooks/__init__.py +3 -0
  34. agno/hooks/decorator.py +164 -0
  35. agno/integrations/discord/client.py +1 -1
  36. agno/knowledge/chunking/semantic.py +2 -2
  37. agno/models/aimlapi/aimlapi.py +2 -3
  38. agno/models/anthropic/claude.py +18 -13
  39. agno/models/aws/bedrock.py +3 -4
  40. agno/models/aws/claude.py +5 -1
  41. agno/models/azure/ai_foundry.py +2 -2
  42. agno/models/azure/openai_chat.py +8 -0
  43. agno/models/cerebras/cerebras.py +62 -11
  44. agno/models/cerebras/cerebras_openai.py +2 -3
  45. agno/models/cohere/chat.py +1 -5
  46. agno/models/cometapi/cometapi.py +2 -3
  47. agno/models/dashscope/dashscope.py +2 -3
  48. agno/models/deepinfra/deepinfra.py +2 -3
  49. agno/models/deepseek/deepseek.py +2 -3
  50. agno/models/fireworks/fireworks.py +2 -3
  51. agno/models/google/gemini.py +9 -7
  52. agno/models/groq/groq.py +2 -3
  53. agno/models/huggingface/huggingface.py +1 -5
  54. agno/models/ibm/watsonx.py +1 -5
  55. agno/models/internlm/internlm.py +2 -3
  56. agno/models/langdb/langdb.py +6 -4
  57. agno/models/litellm/chat.py +2 -2
  58. agno/models/litellm/litellm_openai.py +2 -3
  59. agno/models/meta/llama.py +1 -5
  60. agno/models/meta/llama_openai.py +4 -5
  61. agno/models/mistral/mistral.py +1 -5
  62. agno/models/nebius/nebius.py +2 -3
  63. agno/models/nvidia/nvidia.py +4 -5
  64. agno/models/openai/chat.py +14 -3
  65. agno/models/openai/responses.py +14 -3
  66. agno/models/openrouter/openrouter.py +4 -5
  67. agno/models/perplexity/perplexity.py +2 -3
  68. agno/models/portkey/portkey.py +7 -6
  69. agno/models/requesty/requesty.py +4 -5
  70. agno/models/response.py +2 -1
  71. agno/models/sambanova/sambanova.py +4 -5
  72. agno/models/siliconflow/siliconflow.py +3 -4
  73. agno/models/together/together.py +4 -5
  74. agno/models/vercel/v0.py +4 -5
  75. agno/models/vllm/vllm.py +19 -14
  76. agno/models/xai/xai.py +4 -5
  77. agno/os/app.py +104 -0
  78. agno/os/config.py +13 -0
  79. agno/os/interfaces/whatsapp/router.py +0 -1
  80. agno/os/interfaces/whatsapp/security.py +3 -1
  81. agno/os/mcp.py +1 -0
  82. agno/os/router.py +31 -0
  83. agno/os/routers/traces/__init__.py +3 -0
  84. agno/os/routers/traces/schemas.py +414 -0
  85. agno/os/routers/traces/traces.py +499 -0
  86. agno/os/schema.py +12 -2
  87. agno/os/utils.py +57 -0
  88. agno/run/agent.py +1 -0
  89. agno/run/base.py +17 -0
  90. agno/run/team.py +4 -0
  91. agno/table.py +10 -0
  92. agno/team/team.py +221 -69
  93. agno/tools/function.py +10 -8
  94. agno/tools/google_drive.py +4 -3
  95. agno/tools/nano_banana.py +1 -1
  96. agno/tools/spotify.py +922 -0
  97. agno/tracing/__init__.py +12 -0
  98. agno/tracing/exporter.py +157 -0
  99. agno/tracing/schemas.py +276 -0
  100. agno/tracing/setup.py +111 -0
  101. agno/utils/agent.py +6 -6
  102. agno/utils/hooks.py +56 -1
  103. agno/utils/mcp.py +1 -1
  104. agno/vectordb/qdrant/qdrant.py +22 -22
  105. agno/workflow/condition.py +8 -0
  106. agno/workflow/loop.py +8 -0
  107. agno/workflow/parallel.py +8 -0
  108. agno/workflow/router.py +8 -0
  109. agno/workflow/step.py +20 -0
  110. agno/workflow/steps.py +8 -0
  111. agno/workflow/workflow.py +88 -19
  112. {agno-2.3.4.dist-info → agno-2.3.6.dist-info}/METADATA +38 -33
  113. {agno-2.3.4.dist-info → agno-2.3.6.dist-info}/RECORD +116 -105
  114. {agno-2.3.4.dist-info → agno-2.3.6.dist-info}/WHEEL +0 -0
  115. {agno-2.3.4.dist-info → agno-2.3.6.dist-info}/licenses/LICENSE +0 -0
  116. {agno-2.3.4.dist-info → agno-2.3.6.dist-info}/top_level.txt +0 -0
agno/agent/agent.py CHANGED
@@ -83,8 +83,8 @@ from agno.utils.agent import (
83
83
  aget_session_state_util,
84
84
  aset_session_name_util,
85
85
  aupdate_session_state_util,
86
- await_for_background_tasks,
87
- await_for_background_tasks_stream,
86
+ await_for_open_threads,
87
+ await_for_thread_tasks_stream,
88
88
  collect_joint_audios,
89
89
  collect_joint_files,
90
90
  collect_joint_images,
@@ -103,8 +103,8 @@ from agno.utils.agent import (
103
103
  store_media_util,
104
104
  update_session_state_util,
105
105
  validate_media_object_id,
106
- wait_for_background_tasks,
107
- wait_for_background_tasks_stream,
106
+ wait_for_open_threads,
107
+ wait_for_thread_tasks_stream,
108
108
  )
109
109
  from agno.utils.common import is_typed_dict, validate_typed_dict
110
110
  from agno.utils.events import (
@@ -131,7 +131,7 @@ from agno.utils.events import (
131
131
  create_tool_call_started_event,
132
132
  handle_event,
133
133
  )
134
- from agno.utils.hooks import filter_hook_args, normalize_hooks
134
+ from agno.utils.hooks import copy_args_for_background, filter_hook_args, normalize_hooks, should_run_hook_in_background
135
135
  from agno.utils.knowledge import get_agentic_or_user_search_filters
136
136
  from agno.utils.log import (
137
137
  log_debug,
@@ -273,6 +273,8 @@ class Agent:
273
273
  pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None
274
274
  # Functions called after output is generated but before the response is returned
275
275
  post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None
276
+ # If True, run hooks as FastAPI background tasks (non-blocking). Set by AgentOS.
277
+ _run_hooks_in_background: Optional[bool] = None
276
278
 
277
279
  # --- Agent Reasoning ---
278
280
  # Enable reasoning by working through the problem step by step.
@@ -594,7 +596,6 @@ class Agent:
594
596
  self.tool_choice = tool_choice
595
597
  self.tool_hooks = tool_hooks
596
598
 
597
- # Initialize hooks with backward compatibility
598
599
  self.pre_hooks = pre_hooks
599
600
  self.post_hooks = post_hooks
600
601
 
@@ -710,9 +711,13 @@ class Agent:
710
711
  self.id = generate_id_from_name(self.name)
711
712
 
712
713
  def _set_debug(self, debug_mode: Optional[bool] = None) -> None:
714
+ # Get the debug level from the environment variable or the default debug level
715
+ debug_level: Literal[1, 2] = (
716
+ cast(Literal[1, 2], int(env)) if (env := getenv("AGNO_DEBUG_LEVEL")) in ("1", "2") else self.debug_level
717
+ )
713
718
  # If the default debug mode is set, or passed on run, or via environment variable, set the debug mode to True
714
719
  if self.debug_mode or debug_mode or getenv("AGNO_DEBUG", "false").lower() == "true":
715
- set_log_level_to_debug(level=self.debug_level)
720
+ set_log_level_to_debug(level=debug_level)
716
721
  else:
717
722
  set_log_level_to_info()
718
723
 
@@ -967,6 +972,7 @@ class Agent:
967
972
  add_session_state_to_context: Optional[bool] = None,
968
973
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
969
974
  debug_mode: Optional[bool] = None,
975
+ background_tasks: Optional[Any] = None,
970
976
  **kwargs: Any,
971
977
  ) -> RunOutput:
972
978
  """Run the Agent and return the RunOutput.
@@ -987,9 +993,6 @@ class Agent:
987
993
  13. Cleanup and store the run response and session
988
994
  """
989
995
 
990
- # Register run for cancellation tracking
991
- register_run(run_response.run_id) # type: ignore
992
-
993
996
  # 1. Execute pre-hooks
994
997
  run_input = cast(RunInput, run_response.input)
995
998
  self.model = cast(Model, self.model)
@@ -1003,6 +1006,7 @@ class Agent:
1003
1006
  session=session,
1004
1007
  user_id=user_id,
1005
1008
  debug_mode=debug_mode,
1009
+ background_tasks=background_tasks,
1006
1010
  **kwargs,
1007
1011
  )
1008
1012
  # Consume the generator without yielding
@@ -1105,9 +1109,7 @@ class Agent:
1105
1109
 
1106
1110
  # We should break out of the run function
1107
1111
  if any(tool_call.is_paused for tool_call in run_response.tools or []):
1108
- wait_for_background_tasks(
1109
- memory_future=memory_future, cultural_knowledge_future=cultural_knowledge_future
1110
- )
1112
+ wait_for_open_threads(memory_future=memory_future, cultural_knowledge_future=cultural_knowledge_future)
1111
1113
 
1112
1114
  return self._handle_agent_run_paused(run_response=run_response, session=session, user_id=user_id)
1113
1115
 
@@ -1127,6 +1129,7 @@ class Agent:
1127
1129
  session=session,
1128
1130
  user_id=user_id,
1129
1131
  debug_mode=debug_mode,
1132
+ background_tasks=background_tasks,
1130
1133
  **kwargs,
1131
1134
  )
1132
1135
  deque(post_hook_iterator, maxlen=0)
@@ -1135,7 +1138,7 @@ class Agent:
1135
1138
  raise_if_cancelled(run_response.run_id) # type: ignore
1136
1139
 
1137
1140
  # 11. Wait for background memory creation and cultural knowledge creation
1138
- wait_for_background_tasks(memory_future=memory_future, cultural_knowledge_future=cultural_knowledge_future)
1141
+ wait_for_open_threads(memory_future=memory_future, cultural_knowledge_future=cultural_knowledge_future)
1139
1142
 
1140
1143
  # 12. Create session summary
1141
1144
  if self.session_summary_manager is not None:
@@ -1188,6 +1191,7 @@ class Agent:
1188
1191
  stream_events: bool = False,
1189
1192
  yield_run_output: Optional[bool] = None,
1190
1193
  debug_mode: Optional[bool] = None,
1194
+ background_tasks: Optional[Any] = None,
1191
1195
  **kwargs: Any,
1192
1196
  ) -> Iterator[Union[RunOutputEvent, RunOutput]]:
1193
1197
  """Run the Agent and yield the RunOutput.
@@ -1205,9 +1209,6 @@ class Agent:
1205
1209
  10. Cleanup and store the run response and session
1206
1210
  """
1207
1211
 
1208
- # Register run for cancellation tracking
1209
- register_run(run_response.run_id) # type: ignore
1210
-
1211
1212
  # 1. Execute pre-hooks
1212
1213
  run_input = cast(RunInput, run_response.input)
1213
1214
  self.model = cast(Model, self.model)
@@ -1222,6 +1223,7 @@ class Agent:
1222
1223
  user_id=user_id,
1223
1224
  debug_mode=debug_mode,
1224
1225
  stream_events=stream_events,
1226
+ background_tasks=background_tasks,
1225
1227
  **kwargs,
1226
1228
  )
1227
1229
  for event in pre_hook_iterator:
@@ -1365,7 +1367,7 @@ class Agent:
1365
1367
 
1366
1368
  # We should break out of the run function
1367
1369
  if any(tool_call.is_paused for tool_call in run_response.tools or []):
1368
- yield from wait_for_background_tasks_stream(
1370
+ yield from wait_for_thread_tasks_stream(
1369
1371
  memory_future=memory_future,
1370
1372
  cultural_knowledge_future=cultural_knowledge_future,
1371
1373
  stream_events=stream_events,
@@ -1399,11 +1401,12 @@ class Agent:
1399
1401
  user_id=user_id,
1400
1402
  debug_mode=debug_mode,
1401
1403
  stream_events=stream_events,
1404
+ background_tasks=background_tasks,
1402
1405
  **kwargs,
1403
1406
  )
1404
1407
 
1405
1408
  # 8. Wait for background memory creation and cultural knowledge creation
1406
- yield from wait_for_background_tasks_stream(
1409
+ yield from wait_for_thread_tasks_stream(
1407
1410
  memory_future=memory_future,
1408
1411
  cultural_knowledge_future=cultural_knowledge_future,
1409
1412
  stream_events=stream_events,
@@ -1585,6 +1588,10 @@ class Agent:
1585
1588
  "`run` method is not supported with an async database. Please use `arun` method instead."
1586
1589
  )
1587
1590
 
1591
+ # Create a run_id for this specific run and register immediately for cancellation tracking
1592
+ run_id = str(uuid4())
1593
+ register_run(run_id)
1594
+
1588
1595
  if (add_history_to_context or self.add_history_to_context) and not self.db and not self.team_id:
1589
1596
  log_warning(
1590
1597
  "add_history_to_context is True, but no database has been assigned to the agent. History will not be added to the context."
@@ -1597,8 +1604,11 @@ class Agent:
1597
1604
  stacklevel=2,
1598
1605
  )
1599
1606
 
1600
- # Create a run_id for this specific run
1601
- run_id = str(uuid4())
1607
+ background_tasks = kwargs.pop("background_tasks", None)
1608
+ if background_tasks is not None:
1609
+ from fastapi import BackgroundTasks
1610
+
1611
+ background_tasks: BackgroundTasks = background_tasks # type: ignore
1602
1612
 
1603
1613
  # Validate input against input_schema if provided
1604
1614
  validated_input = self._validate_input(input)
@@ -1635,7 +1645,10 @@ class Agent:
1635
1645
 
1636
1646
  # Initialize session state
1637
1647
  session_state = self._initialize_session_state(
1638
- session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_id
1648
+ session_state=session_state if session_state is not None else {},
1649
+ user_id=user_id,
1650
+ session_id=session_id,
1651
+ run_id=run_id,
1639
1652
  )
1640
1653
  # Update session state from DB
1641
1654
  session_state = self._load_session_state(session=agent_session, session_state=session_state)
@@ -1750,6 +1763,7 @@ class Agent:
1750
1763
  stream_events=stream_events,
1751
1764
  yield_run_output=yield_run_output,
1752
1765
  debug_mode=debug_mode,
1766
+ background_tasks=background_tasks,
1753
1767
  **kwargs,
1754
1768
  )
1755
1769
  return response_iterator
@@ -1764,6 +1778,7 @@ class Agent:
1764
1778
  add_session_state_to_context=add_session_state,
1765
1779
  response_format=response_format,
1766
1780
  debug_mode=debug_mode,
1781
+ background_tasks=background_tasks,
1767
1782
  **kwargs,
1768
1783
  )
1769
1784
  return response
@@ -1822,6 +1837,7 @@ class Agent:
1822
1837
  add_session_state_to_context: Optional[bool] = None,
1823
1838
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1824
1839
  debug_mode: Optional[bool] = None,
1840
+ background_tasks: Optional[Any] = None,
1825
1841
  **kwargs: Any,
1826
1842
  ) -> RunOutput:
1827
1843
  """Run the Agent and return the RunOutput.
@@ -1846,9 +1862,6 @@ class Agent:
1846
1862
  """
1847
1863
  log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
1848
1864
 
1849
- # Register run for cancellation tracking
1850
- register_run(run_response.run_id) # type: ignore
1851
-
1852
1865
  # 1. Read or create session. Reads from the database if provided.
1853
1866
  agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
1854
1867
 
@@ -1856,7 +1869,7 @@ class Agent:
1856
1869
  self._update_metadata(session=agent_session)
1857
1870
  # Initialize session state
1858
1871
  run_context.session_state = self._initialize_session_state(
1859
- session_state=run_context.session_state or {},
1872
+ session_state=run_context.session_state if run_context.session_state is not None else {},
1860
1873
  user_id=user_id,
1861
1874
  session_id=session_id,
1862
1875
  run_id=run_response.run_id,
@@ -1884,6 +1897,7 @@ class Agent:
1884
1897
  session=agent_session,
1885
1898
  user_id=user_id,
1886
1899
  debug_mode=debug_mode,
1900
+ background_tasks=background_tasks,
1887
1901
  **kwargs,
1888
1902
  )
1889
1903
  # Consume the async iterator without yielding
@@ -1986,9 +2000,7 @@ class Agent:
1986
2000
 
1987
2001
  # We should break out of the run function
1988
2002
  if any(tool_call.is_paused for tool_call in run_response.tools or []):
1989
- await await_for_background_tasks(
1990
- memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task
1991
- )
2003
+ await await_for_open_threads(memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task)
1992
2004
  return await self._ahandle_agent_run_paused(
1993
2005
  run_response=run_response, session=agent_session, user_id=user_id
1994
2006
  )
@@ -2009,6 +2021,7 @@ class Agent:
2009
2021
  session=agent_session,
2010
2022
  user_id=user_id,
2011
2023
  debug_mode=debug_mode,
2024
+ background_tasks=background_tasks,
2012
2025
  **kwargs,
2013
2026
  ):
2014
2027
  pass
@@ -2017,7 +2030,7 @@ class Agent:
2017
2030
  raise_if_cancelled(run_response.run_id) # type: ignore
2018
2031
 
2019
2032
  # 14. Wait for background memory creation
2020
- await await_for_background_tasks(memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task)
2033
+ await await_for_open_threads(memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task)
2021
2034
 
2022
2035
  # 15. Create session summary
2023
2036
  if self.session_summary_manager is not None:
@@ -2096,6 +2109,7 @@ class Agent:
2096
2109
  stream_events: bool = False,
2097
2110
  yield_run_output: Optional[bool] = None,
2098
2111
  debug_mode: Optional[bool] = None,
2112
+ background_tasks: Optional[Any] = None,
2099
2113
  **kwargs: Any,
2100
2114
  ) -> AsyncIterator[Union[RunOutputEvent, RunOutput]]:
2101
2115
  """Run the Agent and yield the RunOutput.
@@ -2133,7 +2147,7 @@ class Agent:
2133
2147
  self._update_metadata(session=agent_session)
2134
2148
  # Initialize session state
2135
2149
  run_context.session_state = self._initialize_session_state(
2136
- session_state=run_context.session_state or {},
2150
+ session_state=run_context.session_state if run_context.session_state is not None else {},
2137
2151
  user_id=user_id,
2138
2152
  session_id=session_id,
2139
2153
  run_id=run_response.run_id,
@@ -2162,6 +2176,7 @@ class Agent:
2162
2176
  user_id=user_id,
2163
2177
  debug_mode=debug_mode,
2164
2178
  stream_events=stream_events,
2179
+ background_tasks=background_tasks,
2165
2180
  **kwargs,
2166
2181
  )
2167
2182
  async for event in pre_hook_iterator:
@@ -2220,9 +2235,6 @@ class Agent:
2220
2235
  log_debug("Starting cultural knowledge creation in background task.")
2221
2236
  cultural_knowledge_task = create_task(self._acreate_cultural_knowledge(run_messages=run_messages))
2222
2237
 
2223
- # Register run for cancellation tracking
2224
- register_run(run_response.run_id) # type: ignore
2225
-
2226
2238
  try:
2227
2239
  # 8. Reason about the task if reasoning is enabled
2228
2240
  async for item in self._ahandle_reasoning_stream(
@@ -2304,7 +2316,7 @@ class Agent:
2304
2316
 
2305
2317
  # Break out of the run function if a tool call is paused
2306
2318
  if any(tool_call.is_paused for tool_call in run_response.tools or []):
2307
- async for item in await_for_background_tasks_stream(
2319
+ async for item in await_for_thread_tasks_stream(
2308
2320
  memory_task=memory_task,
2309
2321
  cultural_knowledge_task=cultural_knowledge_task,
2310
2322
  stream_events=stream_events,
@@ -2328,12 +2340,13 @@ class Agent:
2328
2340
  user_id=user_id,
2329
2341
  debug_mode=debug_mode,
2330
2342
  stream_events=stream_events,
2343
+ background_tasks=background_tasks,
2331
2344
  **kwargs,
2332
2345
  ):
2333
2346
  yield event
2334
2347
 
2335
2348
  # 11. Wait for background memory creation
2336
- async for item in await_for_background_tasks_stream(
2349
+ async for item in await_for_thread_tasks_stream(
2337
2350
  memory_task=memory_task,
2338
2351
  cultural_knowledge_task=cultural_knowledge_task,
2339
2352
  stream_events=stream_events,
@@ -2537,6 +2550,10 @@ class Agent:
2537
2550
  ) -> Union[RunOutput, AsyncIterator[RunOutputEvent]]:
2538
2551
  """Async Run the Agent and return the response."""
2539
2552
 
2553
+ # Create a run_id for this specific run and register immediately for cancellation tracking
2554
+ run_id = str(uuid4())
2555
+ register_run(run_id)
2556
+
2540
2557
  if (add_history_to_context or self.add_history_to_context) and not self.db and not self.team_id:
2541
2558
  log_warning(
2542
2559
  "add_history_to_context is True, but no database has been assigned to the agent. History will not be added to the context."
@@ -2549,8 +2566,11 @@ class Agent:
2549
2566
  stacklevel=2,
2550
2567
  )
2551
2568
 
2552
- # Create a run_id for this specific run
2553
- run_id = str(uuid4())
2569
+ background_tasks = kwargs.pop("background_tasks", None)
2570
+ if background_tasks is not None:
2571
+ from fastapi import BackgroundTasks
2572
+
2573
+ background_tasks: BackgroundTasks = background_tasks # type: ignore
2554
2574
 
2555
2575
  # 2. Validate input against input_schema if provided
2556
2576
  validated_input = self._validate_input(input)
@@ -2695,6 +2715,7 @@ class Agent:
2695
2715
  add_dependencies_to_context=add_dependencies,
2696
2716
  add_session_state_to_context=add_session_state,
2697
2717
  debug_mode=debug_mode,
2718
+ background_tasks=background_tasks,
2698
2719
  **kwargs,
2699
2720
  ) # type: ignore[assignment]
2700
2721
  else:
@@ -2708,6 +2729,7 @@ class Agent:
2708
2729
  add_dependencies_to_context=add_dependencies,
2709
2730
  add_session_state_to_context=add_session_state,
2710
2731
  debug_mode=debug_mode,
2732
+ background_tasks=background_tasks,
2711
2733
  **kwargs,
2712
2734
  )
2713
2735
 
@@ -2839,6 +2861,12 @@ class Agent:
2839
2861
  if self._has_async_db():
2840
2862
  raise Exception("continue_run() is not supported with an async DB. Please use acontinue_arun() instead.")
2841
2863
 
2864
+ background_tasks = kwargs.pop("background_tasks", None)
2865
+ if background_tasks is not None:
2866
+ from fastapi import BackgroundTasks
2867
+
2868
+ background_tasks: BackgroundTasks = background_tasks # type: ignore
2869
+
2842
2870
  session_id = run_response.session_id if run_response else session_id
2843
2871
  run_id: str = run_response.run_id if run_response else run_id # type: ignore
2844
2872
 
@@ -2982,6 +3010,7 @@ class Agent:
2982
3010
  response_format=response_format,
2983
3011
  stream_events=stream_events,
2984
3012
  debug_mode=debug_mode,
3013
+ background_tasks=background_tasks,
2985
3014
  **kwargs,
2986
3015
  )
2987
3016
  return response_iterator
@@ -2995,6 +3024,7 @@ class Agent:
2995
3024
  session=agent_session,
2996
3025
  response_format=response_format,
2997
3026
  debug_mode=debug_mode,
3027
+ background_tasks=background_tasks,
2998
3028
  **kwargs,
2999
3029
  )
3000
3030
  return response
@@ -3045,6 +3075,7 @@ class Agent:
3045
3075
  user_id: Optional[str] = None,
3046
3076
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3047
3077
  debug_mode: Optional[bool] = None,
3078
+ background_tasks: Optional[Any] = None,
3048
3079
  **kwargs,
3049
3080
  ) -> RunOutput:
3050
3081
  """Continue a previous run.
@@ -3109,6 +3140,7 @@ class Agent:
3109
3140
  session=session,
3110
3141
  user_id=user_id,
3111
3142
  debug_mode=debug_mode,
3143
+ background_tasks=background_tasks,
3112
3144
  **kwargs,
3113
3145
  )
3114
3146
  deque(post_hook_iterator, maxlen=0)
@@ -3164,6 +3196,7 @@ class Agent:
3164
3196
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3165
3197
  stream_events: bool = False,
3166
3198
  debug_mode: Optional[bool] = None,
3199
+ background_tasks: Optional[Any] = None,
3167
3200
  **kwargs,
3168
3201
  ) -> Iterator[RunOutputEvent]:
3169
3202
  """Continue a previous run.
@@ -3240,6 +3273,7 @@ class Agent:
3240
3273
  user_id=user_id,
3241
3274
  debug_mode=debug_mode,
3242
3275
  stream_events=stream_events,
3276
+ background_tasks=background_tasks,
3243
3277
  **kwargs,
3244
3278
  )
3245
3279
 
@@ -3341,6 +3375,7 @@ class Agent:
3341
3375
  dependencies: Optional[Dict[str, Any]] = None,
3342
3376
  metadata: Optional[Dict[str, Any]] = None,
3343
3377
  debug_mode: Optional[bool] = None,
3378
+ **kwargs: Any,
3344
3379
  ) -> RunOutput: ...
3345
3380
 
3346
3381
  @overload
@@ -3360,6 +3395,7 @@ class Agent:
3360
3395
  dependencies: Optional[Dict[str, Any]] = None,
3361
3396
  metadata: Optional[Dict[str, Any]] = None,
3362
3397
  debug_mode: Optional[bool] = None,
3398
+ **kwargs: Any,
3363
3399
  ) -> AsyncIterator[Union[RunOutputEvent, RunOutput]]: ...
3364
3400
 
3365
3401
  def acontinue_run( # type: ignore
@@ -3407,6 +3443,12 @@ class Agent:
3407
3443
  if run_response is None and (run_id is not None and (session_id is None and self.session_id is None)):
3408
3444
  raise ValueError("Session ID is required to continue a run from a run_id.")
3409
3445
 
3446
+ background_tasks = kwargs.pop("background_tasks", None)
3447
+ if background_tasks is not None:
3448
+ from fastapi import BackgroundTasks
3449
+
3450
+ background_tasks: BackgroundTasks = background_tasks # type: ignore
3451
+
3410
3452
  session_id, user_id = self._initialize_session(
3411
3453
  session_id=session_id,
3412
3454
  user_id=user_id,
@@ -3491,6 +3533,7 @@ class Agent:
3491
3533
  stream_events=stream_events,
3492
3534
  yield_run_output=yield_run_output,
3493
3535
  debug_mode=debug_mode,
3536
+ background_tasks=background_tasks,
3494
3537
  **kwargs,
3495
3538
  )
3496
3539
  else:
@@ -3503,6 +3546,7 @@ class Agent:
3503
3546
  user_id=user_id,
3504
3547
  response_format=response_format,
3505
3548
  debug_mode=debug_mode,
3549
+ background_tasks=background_tasks,
3506
3550
  **kwargs,
3507
3551
  )
3508
3552
  except ModelProviderError as e:
@@ -3552,6 +3596,7 @@ class Agent:
3552
3596
  user_id: Optional[str] = None,
3553
3597
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3554
3598
  debug_mode: Optional[bool] = None,
3599
+ background_tasks: Optional[Any] = None,
3555
3600
  **kwargs,
3556
3601
  ) -> RunOutput:
3557
3602
  """Continue a previous run.
@@ -3694,6 +3739,7 @@ class Agent:
3694
3739
  session=agent_session,
3695
3740
  user_id=user_id,
3696
3741
  debug_mode=debug_mode,
3742
+ background_tasks=background_tasks,
3697
3743
  **kwargs,
3698
3744
  ):
3699
3745
  pass
@@ -3763,6 +3809,7 @@ class Agent:
3763
3809
  stream_events: bool = False,
3764
3810
  yield_run_output: bool = False,
3765
3811
  debug_mode: Optional[bool] = None,
3812
+ background_tasks: Optional[Any] = None,
3766
3813
  **kwargs,
3767
3814
  ) -> AsyncIterator[Union[RunOutputEvent, RunOutput]]:
3768
3815
  """Continue a previous run.
@@ -3948,6 +3995,7 @@ class Agent:
3948
3995
  user_id=user_id,
3949
3996
  debug_mode=debug_mode,
3950
3997
  stream_events=stream_events,
3998
+ background_tasks=background_tasks,
3951
3999
  **kwargs,
3952
4000
  ):
3953
4001
  yield event
@@ -4049,13 +4097,13 @@ class Agent:
4049
4097
  user_id: Optional[str] = None,
4050
4098
  debug_mode: Optional[bool] = None,
4051
4099
  stream_events: bool = False,
4100
+ background_tasks: Optional[Any] = None,
4052
4101
  **kwargs: Any,
4053
4102
  ) -> Iterator[RunOutputEvent]:
4054
4103
  """Execute multiple pre-hook functions in succession."""
4055
4104
  if hooks is None:
4056
4105
  return
4057
-
4058
- # Prepare all possible arguments once
4106
+ # Prepare arguments for this hook
4059
4107
  all_args = {
4060
4108
  "run_input": run_input,
4061
4109
  "run_context": run_context,
@@ -4067,9 +4115,32 @@ class Agent:
4067
4115
  "user_id": user_id,
4068
4116
  "debug_mode": debug_mode or self.debug_mode,
4069
4117
  }
4118
+
4119
+ # Check if background_tasks is available and ALL hooks should run in background
4120
+ # Note: Pre-hooks running in background may not be able to modify run_input
4121
+ if self._run_hooks_in_background is True and background_tasks is not None:
4122
+ # Schedule ALL pre_hooks as background tasks
4123
+ # Copy args to prevent race conditions
4124
+ bg_args = copy_args_for_background(all_args)
4125
+ for hook in hooks:
4126
+ # Filter arguments to only include those that the hook accepts
4127
+ filtered_args = filter_hook_args(hook, bg_args)
4128
+
4129
+ # Add to background tasks
4130
+ background_tasks.add_task(hook, **filtered_args)
4131
+ return
4132
+
4070
4133
  all_args.update(kwargs)
4071
4134
 
4072
4135
  for i, hook in enumerate(hooks):
4136
+ # Check if this specific hook should run in background (via @hook decorator)
4137
+ if should_run_hook_in_background(hook) and background_tasks is not None:
4138
+ # Copy args to prevent race conditions
4139
+ bg_args = copy_args_for_background(all_args)
4140
+ filtered_args = filter_hook_args(hook, bg_args)
4141
+ background_tasks.add_task(hook, **filtered_args)
4142
+ continue
4143
+
4073
4144
  if stream_events:
4074
4145
  yield handle_event( # type: ignore
4075
4146
  run_response=run_response,
@@ -4121,13 +4192,13 @@ class Agent:
4121
4192
  user_id: Optional[str] = None,
4122
4193
  debug_mode: Optional[bool] = None,
4123
4194
  stream_events: bool = False,
4195
+ background_tasks: Optional[Any] = None,
4124
4196
  **kwargs: Any,
4125
4197
  ) -> AsyncIterator[RunOutputEvent]:
4126
4198
  """Execute multiple pre-hook functions in succession (async version)."""
4127
4199
  if hooks is None:
4128
4200
  return
4129
-
4130
- # Prepare all possible arguments once
4201
+ # Prepare arguments for this hook
4131
4202
  all_args = {
4132
4203
  "run_input": run_input,
4133
4204
  "agent": self,
@@ -4139,9 +4210,32 @@ class Agent:
4139
4210
  "user_id": user_id,
4140
4211
  "debug_mode": debug_mode or self.debug_mode,
4141
4212
  }
4213
+
4214
+ # Check if background_tasks is available and ALL hooks should run in background
4215
+ # Note: Pre-hooks running in background may not be able to modify run_input
4216
+ if self._run_hooks_in_background is True and background_tasks is not None:
4217
+ # Schedule ALL pre_hooks as background tasks
4218
+ # Copy args to prevent race conditions
4219
+ bg_args = copy_args_for_background(all_args)
4220
+ for hook in hooks:
4221
+ # Filter arguments to only include those that the hook accepts
4222
+ filtered_args = filter_hook_args(hook, bg_args)
4223
+
4224
+ # Add to background tasks (both sync and async hooks supported)
4225
+ background_tasks.add_task(hook, **filtered_args)
4226
+ return
4227
+
4142
4228
  all_args.update(kwargs)
4143
4229
 
4144
4230
  for i, hook in enumerate(hooks):
4231
+ # Check if this specific hook should run in background (via @hook decorator)
4232
+ if should_run_hook_in_background(hook) and background_tasks is not None:
4233
+ # Copy args to prevent race conditions
4234
+ bg_args = copy_args_for_background(all_args)
4235
+ filtered_args = filter_hook_args(hook, bg_args)
4236
+ background_tasks.add_task(hook, **filtered_args)
4237
+ continue
4238
+
4145
4239
  if stream_events:
4146
4240
  yield handle_event( # type: ignore
4147
4241
  run_response=run_response,
@@ -4196,13 +4290,14 @@ class Agent:
4196
4290
  user_id: Optional[str] = None,
4197
4291
  debug_mode: Optional[bool] = None,
4198
4292
  stream_events: bool = False,
4293
+ background_tasks: Optional[Any] = None,
4199
4294
  **kwargs: Any,
4200
4295
  ) -> Iterator[RunOutputEvent]:
4201
4296
  """Execute multiple post-hook functions in succession."""
4202
4297
  if hooks is None:
4203
4298
  return
4204
4299
 
4205
- # Prepare all possible arguments once
4300
+ # Prepare arguments for this hook
4206
4301
  all_args = {
4207
4302
  "run_output": run_output,
4208
4303
  "agent": self,
@@ -4214,9 +4309,31 @@ class Agent:
4214
4309
  "run_context": run_context,
4215
4310
  "debug_mode": debug_mode or self.debug_mode,
4216
4311
  }
4312
+
4313
+ # Check if background_tasks is available and ALL hooks should run in background
4314
+ if self._run_hooks_in_background is True and background_tasks is not None:
4315
+ # Schedule ALL post_hooks as background tasks
4316
+ # Copy args to prevent race conditions
4317
+ bg_args = copy_args_for_background(all_args)
4318
+ for hook in hooks:
4319
+ # Filter arguments to only include those that the hook accepts
4320
+ filtered_args = filter_hook_args(hook, bg_args)
4321
+
4322
+ # Add to background tasks
4323
+ background_tasks.add_task(hook, **filtered_args)
4324
+ return
4325
+
4217
4326
  all_args.update(kwargs)
4218
4327
 
4219
4328
  for i, hook in enumerate(hooks):
4329
+ # Check if this specific hook should run in background (via @hook decorator)
4330
+ if should_run_hook_in_background(hook) and background_tasks is not None:
4331
+ # Copy args to prevent race conditions
4332
+ bg_args = copy_args_for_background(all_args)
4333
+ filtered_args = filter_hook_args(hook, bg_args)
4334
+ background_tasks.add_task(hook, **filtered_args)
4335
+ continue
4336
+
4220
4337
  if stream_events:
4221
4338
  yield handle_event( # type: ignore
4222
4339
  run_response=run_output,
@@ -4261,13 +4378,14 @@ class Agent:
4261
4378
  user_id: Optional[str] = None,
4262
4379
  debug_mode: Optional[bool] = None,
4263
4380
  stream_events: bool = False,
4381
+ background_tasks: Optional[Any] = None,
4264
4382
  **kwargs: Any,
4265
4383
  ) -> AsyncIterator[RunOutputEvent]:
4266
4384
  """Execute multiple post-hook functions in succession (async version)."""
4267
4385
  if hooks is None:
4268
4386
  return
4269
4387
 
4270
- # Prepare all possible arguments once
4388
+ # Prepare arguments for this hook
4271
4389
  all_args = {
4272
4390
  "run_output": run_output,
4273
4391
  "agent": self,
@@ -4279,9 +4397,29 @@ class Agent:
4279
4397
  "user_id": user_id,
4280
4398
  "debug_mode": debug_mode or self.debug_mode,
4281
4399
  }
4400
+ # Check if background_tasks is available and ALL hooks should run in background
4401
+ if self._run_hooks_in_background is True and background_tasks is not None:
4402
+ # Copy args to prevent race conditions
4403
+ bg_args = copy_args_for_background(all_args)
4404
+ for hook in hooks:
4405
+ # Filter arguments to only include those that the hook accepts
4406
+ filtered_args = filter_hook_args(hook, bg_args)
4407
+
4408
+ # Add to background tasks (both sync and async hooks supported)
4409
+ background_tasks.add_task(hook, **filtered_args)
4410
+ return
4411
+
4282
4412
  all_args.update(kwargs)
4283
4413
 
4284
4414
  for i, hook in enumerate(hooks):
4415
+ # Check if this specific hook should run in background (via @hook decorator)
4416
+ if should_run_hook_in_background(hook) and background_tasks is not None:
4417
+ # Copy args to prevent race conditions
4418
+ bg_args = copy_args_for_background(all_args)
4419
+ filtered_args = filter_hook_args(hook, bg_args)
4420
+ background_tasks.add_task(hook, **filtered_args)
4421
+ continue
4422
+
4285
4423
  if stream_events:
4286
4424
  yield handle_event( # type: ignore
4287
4425
  run_response=run_output,
@@ -6853,7 +6991,7 @@ class Agent:
6853
6991
 
6854
6992
  # Should already be resolved and passed from run() method
6855
6993
  format_variables = ChainMap(
6856
- session_state or {},
6994
+ session_state if session_state is not None else {},
6857
6995
  dependencies or {},
6858
6996
  metadata or {},
6859
6997
  {"user_id": user_id} if user_id is not None else {},
@@ -8664,6 +8802,7 @@ class Agent:
8664
8802
  # Update fields if provided
8665
8803
  if update:
8666
8804
  fields_for_new_agent.update(update)
8805
+
8667
8806
  # Create a new Agent
8668
8807
  new_agent = self.__class__(**fields_for_new_agent)
8669
8808
  log_debug(f"Created new {self.__class__.__name__}")