agno 2.3.6__py3-none-any.whl → 2.3.8__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.
agno/agent/agent.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import time
3
4
  import warnings
4
5
  from asyncio import CancelledError, create_task
5
6
  from collections import ChainMap, deque
@@ -35,10 +36,8 @@ from agno.db.base import AsyncBaseDb, BaseDb, SessionType, UserMemory
35
36
  from agno.db.schemas.culture import CulturalKnowledge
36
37
  from agno.exceptions import (
37
38
  InputCheckError,
38
- ModelProviderError,
39
39
  OutputCheckError,
40
40
  RunCancelledException,
41
- StopAgentRun,
42
41
  )
43
42
  from agno.filters import FilterExpr
44
43
  from agno.guardrails import BaseGuardrail
@@ -68,6 +67,7 @@ from agno.run.cancel import (
68
67
  register_run,
69
68
  )
70
69
  from agno.run.messages import RunMessages
70
+ from agno.run.requirement import RunRequirement
71
71
  from agno.run.team import TeamRunOutputEvent
72
72
  from agno.session import AgentSession, SessionSummaryManager, TeamSession, WorkflowSession
73
73
  from agno.session.summary import SessionSummary
@@ -557,8 +557,12 @@ class Agent:
557
557
  self.enable_user_memories = enable_user_memories
558
558
  self.add_memories_to_context = add_memories_to_context
559
559
 
560
- self.session_summary_manager = session_summary_manager
561
560
  self.enable_session_summaries = enable_session_summaries
561
+
562
+ if session_summary_manager is not None:
563
+ self.session_summary_manager = session_summary_manager
564
+ self.enable_session_summaries = True
565
+
562
566
  self.add_session_summary_to_context = add_session_summary_to_context
563
567
 
564
568
  # Context compression settings
@@ -683,6 +687,7 @@ class Agent:
683
687
  self._hooks_normalised = False
684
688
 
685
689
  self._mcp_tools_initialized_on_run: List[Any] = []
690
+ self._connectable_tools_initialized_on_run: List[Any] = []
686
691
 
687
692
  # Lazy-initialized shared thread pool executor for background tasks (memory, cultural knowledge, etc.)
688
693
  self._background_executor: Optional[Any] = None
@@ -912,16 +917,48 @@ class Agent:
912
917
  and any(c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__)
913
918
  and not tool.initialized # type: ignore
914
919
  ):
915
- # Connect the MCP server
916
- await tool.connect() # type: ignore
917
- self._mcp_tools_initialized_on_run.append(tool) # type: ignore
920
+ try:
921
+ # Connect the MCP server
922
+ await tool.connect() # type: ignore
923
+ self._mcp_tools_initialized_on_run.append(tool) # type: ignore
924
+ except Exception as e:
925
+ log_warning(f"Error connecting tool: {str(e)}")
918
926
 
919
927
  async def _disconnect_mcp_tools(self) -> None:
920
928
  """Disconnect the MCP tools from the agent."""
921
929
  for tool in self._mcp_tools_initialized_on_run:
922
- await tool.close()
930
+ try:
931
+ await tool.close()
932
+ except Exception as e:
933
+ log_warning(f"Error disconnecting tool: {str(e)}")
923
934
  self._mcp_tools_initialized_on_run = []
924
935
 
936
+ def _connect_connectable_tools(self) -> None:
937
+ """Connect tools that require connection management (e.g., database connections)."""
938
+ if self.tools:
939
+ for tool in self.tools:
940
+ if (
941
+ hasattr(tool, "requires_connect")
942
+ and tool.requires_connect
943
+ and hasattr(tool, "connect")
944
+ and tool not in self._connectable_tools_initialized_on_run
945
+ ):
946
+ try:
947
+ tool.connect() # type: ignore
948
+ self._connectable_tools_initialized_on_run.append(tool)
949
+ except Exception as e:
950
+ log_warning(f"Error connecting tool: {str(e)}")
951
+
952
+ def _disconnect_connectable_tools(self) -> None:
953
+ """Disconnect tools that require connection management."""
954
+ for tool in self._connectable_tools_initialized_on_run:
955
+ if hasattr(tool, "close"):
956
+ try:
957
+ tool.close() # type: ignore
958
+ except Exception as e:
959
+ log_warning(f"Error disconnecting tool: {str(e)}")
960
+ self._connectable_tools_initialized_on_run = []
961
+
925
962
  def _initialize_session(
926
963
  self,
927
964
  session_id: Optional[str] = None,
@@ -1052,7 +1089,12 @@ class Agent:
1052
1089
  # Start memory creation on a separate thread (runs concurrently with the main execution loop)
1053
1090
  memory_future = None
1054
1091
  # 4. Start memory creation in background thread if memory manager is enabled and agentic memory is disabled
1055
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
1092
+ if (
1093
+ run_messages.user_message is not None
1094
+ and self.memory_manager is not None
1095
+ and self.enable_user_memories
1096
+ and not self.enable_agentic_memory
1097
+ ):
1056
1098
  log_debug("Starting memory creation in background thread.")
1057
1099
  memory_future = self.background_executor.submit(
1058
1100
  self._make_memories, run_messages=run_messages, user_id=user_id
@@ -1141,7 +1183,7 @@ class Agent:
1141
1183
  wait_for_open_threads(memory_future=memory_future, cultural_knowledge_future=cultural_knowledge_future)
1142
1184
 
1143
1185
  # 12. Create session summary
1144
- if self.session_summary_manager is not None:
1186
+ if self.session_summary_manager is not None and self.enable_session_summaries:
1145
1187
  # Upsert the RunOutput to Agent Session before creating the session summary
1146
1188
  session.upsert_run(run=run_response)
1147
1189
  try:
@@ -1175,6 +1217,8 @@ class Agent:
1175
1217
 
1176
1218
  return run_response
1177
1219
  finally:
1220
+ # Always disconnect connectable tools
1221
+ self._disconnect_connectable_tools()
1178
1222
  # Always clean up the run tracking
1179
1223
  cleanup_run(run_response.run_id) # type: ignore
1180
1224
 
@@ -1269,7 +1313,12 @@ class Agent:
1269
1313
  # Start memory creation on a separate thread (runs concurrently with the main execution loop)
1270
1314
  memory_future = None
1271
1315
  # 4. Start memory creation in background thread if memory manager is enabled and agentic memory is disabled
1272
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
1316
+ if (
1317
+ run_messages.user_message is not None
1318
+ and self.memory_manager is not None
1319
+ and self.enable_user_memories
1320
+ and not self.enable_agentic_memory
1321
+ ):
1273
1322
  log_debug("Starting memory creation in background thread.")
1274
1323
  memory_future = self.background_executor.submit(
1275
1324
  self._make_memories, run_messages=run_messages, user_id=user_id
@@ -1414,7 +1463,7 @@ class Agent:
1414
1463
  )
1415
1464
 
1416
1465
  # 9. Create session summary
1417
- if self.session_summary_manager is not None:
1466
+ if self.session_summary_manager is not None and self.enable_session_summaries:
1418
1467
  # Upsert the RunOutput to Agent Session before creating the session summary
1419
1468
  session.upsert_run(run=run_response)
1420
1469
 
@@ -1493,6 +1542,8 @@ class Agent:
1493
1542
  run_response=run_response, session=session, run_context=run_context, user_id=user_id
1494
1543
  )
1495
1544
  finally:
1545
+ # Always disconnect connectable tools
1546
+ self._disconnect_connectable_tools()
1496
1547
  # Always clean up the run tracking
1497
1548
  cleanup_run(run_response.run_id) # type: ignore
1498
1549
 
@@ -1512,7 +1563,6 @@ class Agent:
1512
1563
  images: Optional[Sequence[Image]] = None,
1513
1564
  videos: Optional[Sequence[Video]] = None,
1514
1565
  files: Optional[Sequence[File]] = None,
1515
- retries: Optional[int] = None,
1516
1566
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
1517
1567
  add_history_to_context: Optional[bool] = None,
1518
1568
  add_dependencies_to_context: Optional[bool] = None,
@@ -1540,7 +1590,6 @@ class Agent:
1540
1590
  images: Optional[Sequence[Image]] = None,
1541
1591
  videos: Optional[Sequence[Video]] = None,
1542
1592
  files: Optional[Sequence[File]] = None,
1543
- retries: Optional[int] = None,
1544
1593
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
1545
1594
  add_history_to_context: Optional[bool] = None,
1546
1595
  add_dependencies_to_context: Optional[bool] = None,
@@ -1569,7 +1618,6 @@ class Agent:
1569
1618
  images: Optional[Sequence[Image]] = None,
1570
1619
  videos: Optional[Sequence[Video]] = None,
1571
1620
  files: Optional[Sequence[File]] = None,
1572
- retries: Optional[int] = None,
1573
1621
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
1574
1622
  add_history_to_context: Optional[bool] = None,
1575
1623
  add_dependencies_to_context: Optional[bool] = None,
@@ -1672,159 +1720,163 @@ class Agent:
1672
1720
  # output_schema parameter takes priority, even if run_context was provided
1673
1721
  run_context.output_schema = output_schema
1674
1722
 
1675
- # Resolve dependencies
1676
- if run_context.dependencies is not None:
1677
- self._resolve_run_dependencies(run_context=run_context)
1723
+ # Set up retry logic
1724
+ num_attempts = self.retries + 1
1678
1725
 
1679
- add_dependencies = (
1680
- add_dependencies_to_context if add_dependencies_to_context is not None else self.add_dependencies_to_context
1681
- )
1682
- add_session_state = (
1683
- add_session_state_to_context
1684
- if add_session_state_to_context is not None
1685
- else self.add_session_state_to_context
1686
- )
1687
- add_history = add_history_to_context if add_history_to_context is not None else self.add_history_to_context
1726
+ for attempt in range(num_attempts):
1727
+ log_debug(f"Retrying Agent run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
1688
1728
 
1689
- # When filters are passed manually
1690
- if self.knowledge_filters or knowledge_filters:
1691
- run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
1729
+ try:
1730
+ # Resolve dependencies
1731
+ if run_context.dependencies is not None:
1732
+ self._resolve_run_dependencies(run_context=run_context)
1733
+
1734
+ add_dependencies = (
1735
+ add_dependencies_to_context
1736
+ if add_dependencies_to_context is not None
1737
+ else self.add_dependencies_to_context
1738
+ )
1739
+ add_session_state = (
1740
+ add_session_state_to_context
1741
+ if add_session_state_to_context is not None
1742
+ else self.add_session_state_to_context
1743
+ )
1744
+ add_history = (
1745
+ add_history_to_context if add_history_to_context is not None else self.add_history_to_context
1746
+ )
1692
1747
 
1693
- # Use stream override value when necessary
1694
- if stream is None:
1695
- stream = False if self.stream is None else self.stream
1748
+ # When filters are passed manually
1749
+ if self.knowledge_filters or knowledge_filters:
1750
+ run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
1696
1751
 
1697
- # Considering both stream_events and stream_intermediate_steps (deprecated)
1698
- if stream_intermediate_steps is not None:
1699
- warnings.warn(
1700
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
1701
- DeprecationWarning,
1702
- stacklevel=2,
1703
- )
1704
- stream_events = stream_events or stream_intermediate_steps
1752
+ # Use stream override value when necessary
1753
+ if stream is None:
1754
+ stream = False if self.stream is None else self.stream
1705
1755
 
1706
- # Can't stream events if streaming is disabled
1707
- if stream is False:
1708
- stream_events = False
1709
-
1710
- if stream_events is None:
1711
- stream_events = False if self.stream_events is None else self.stream_events
1756
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
1757
+ if stream_intermediate_steps is not None:
1758
+ warnings.warn(
1759
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
1760
+ DeprecationWarning,
1761
+ stacklevel=2,
1762
+ )
1763
+ stream_events = stream_events or stream_intermediate_steps
1712
1764
 
1713
- self.stream = self.stream or stream
1714
- self.stream_events = self.stream_events or stream_events
1765
+ # Can't stream events if streaming is disabled
1766
+ if stream is False:
1767
+ stream_events = False
1715
1768
 
1716
- # Prepare arguments for the model
1717
- response_format = self._get_response_format(run_context=run_context) if self.parser_model is None else None
1718
- self.model = cast(Model, self.model)
1769
+ if stream_events is None:
1770
+ stream_events = False if self.stream_events is None else self.stream_events
1719
1771
 
1720
- # Merge agent metadata with run metadata
1721
- if self.metadata is not None and metadata is not None:
1722
- merge_dictionaries(metadata, self.metadata)
1772
+ self.stream = self.stream or stream
1773
+ self.stream_events = self.stream_events or stream_events
1723
1774
 
1724
- # Create a new run_response for this attempt
1725
- run_response = RunOutput(
1726
- run_id=run_id,
1727
- session_id=session_id,
1728
- agent_id=self.id,
1729
- user_id=user_id,
1730
- agent_name=self.name,
1731
- metadata=run_context.metadata,
1732
- session_state=run_context.session_state,
1733
- input=run_input,
1734
- )
1775
+ # Prepare arguments for the model
1776
+ response_format = (
1777
+ self._get_response_format(run_context=run_context) if self.parser_model is None else None
1778
+ )
1779
+ self.model = cast(Model, self.model)
1735
1780
 
1736
- run_response.model = self.model.id if self.model is not None else None
1737
- run_response.model_provider = self.model.provider if self.model is not None else None
1781
+ # Merge agent metadata with run metadata
1782
+ if self.metadata is not None and metadata is not None:
1783
+ merge_dictionaries(metadata, self.metadata)
1738
1784
 
1739
- # Start the run metrics timer, to calculate the run duration
1740
- run_response.metrics = Metrics()
1741
- run_response.metrics.start_timer()
1785
+ # Create a new run_response for this attempt
1786
+ run_response = RunOutput(
1787
+ run_id=run_id,
1788
+ session_id=session_id,
1789
+ agent_id=self.id,
1790
+ user_id=user_id,
1791
+ agent_name=self.name,
1792
+ metadata=run_context.metadata,
1793
+ session_state=run_context.session_state,
1794
+ input=run_input,
1795
+ )
1742
1796
 
1743
- # If no retries are set, use the agent's default retries
1744
- retries = retries if retries is not None else self.retries
1797
+ run_response.model = self.model.id if self.model is not None else None
1798
+ run_response.model_provider = self.model.provider if self.model is not None else None
1745
1799
 
1746
- last_exception = None
1747
- num_attempts = retries + 1
1800
+ # Start the run metrics timer, to calculate the run duration
1801
+ run_response.metrics = Metrics()
1802
+ run_response.metrics.start_timer()
1748
1803
 
1749
- yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
1804
+ yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
1750
1805
 
1751
- for attempt in range(num_attempts):
1752
- try:
1753
- if stream:
1754
- response_iterator = self._run_stream(
1755
- run_response=run_response,
1756
- run_context=run_context,
1757
- session=agent_session,
1758
- user_id=user_id,
1759
- add_history_to_context=add_history,
1760
- add_dependencies_to_context=add_dependencies,
1761
- add_session_state_to_context=add_session_state,
1762
- response_format=response_format,
1763
- stream_events=stream_events,
1764
- yield_run_output=yield_run_output,
1765
- debug_mode=debug_mode,
1766
- background_tasks=background_tasks,
1767
- **kwargs,
1768
- )
1769
- return response_iterator
1770
- else:
1771
- response = self._run(
1772
- run_response=run_response,
1773
- run_context=run_context,
1774
- session=agent_session,
1775
- user_id=user_id,
1776
- add_history_to_context=add_history,
1777
- add_dependencies_to_context=add_dependencies,
1778
- add_session_state_to_context=add_session_state,
1779
- response_format=response_format,
1780
- debug_mode=debug_mode,
1781
- background_tasks=background_tasks,
1782
- **kwargs,
1783
- )
1784
- return response
1785
- except (InputCheckError, OutputCheckError) as e:
1786
- log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
1787
- raise e
1788
- except ModelProviderError as e:
1789
- log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
1790
- if isinstance(e, StopAgentRun):
1791
- raise e
1792
- last_exception = e
1793
- if attempt < num_attempts - 1: # Don't sleep on the last attempt
1794
- if self.exponential_backoff:
1795
- delay = 2**attempt * self.delay_between_retries
1806
+ try:
1807
+ if stream:
1808
+ response_iterator = self._run_stream(
1809
+ run_response=run_response,
1810
+ run_context=run_context,
1811
+ session=agent_session,
1812
+ user_id=user_id,
1813
+ add_history_to_context=add_history,
1814
+ add_dependencies_to_context=add_dependencies,
1815
+ add_session_state_to_context=add_session_state,
1816
+ response_format=response_format,
1817
+ stream_events=stream_events,
1818
+ yield_run_output=yield_run_output,
1819
+ debug_mode=debug_mode,
1820
+ background_tasks=background_tasks,
1821
+ **kwargs,
1822
+ )
1823
+ return response_iterator
1796
1824
  else:
1797
- delay = self.delay_between_retries
1798
- import time
1799
-
1800
- time.sleep(delay)
1801
- except KeyboardInterrupt:
1802
- run_response.content = "Operation cancelled by user"
1803
- run_response.status = RunStatus.cancelled
1804
-
1805
- if stream:
1806
- return generator_wrapper( # type: ignore
1807
- create_run_cancelled_event(
1808
- from_run_response=run_response,
1809
- reason="Operation cancelled by user",
1825
+ response = self._run(
1826
+ run_response=run_response,
1827
+ run_context=run_context,
1828
+ session=agent_session,
1829
+ user_id=user_id,
1830
+ add_history_to_context=add_history,
1831
+ add_dependencies_to_context=add_dependencies,
1832
+ add_session_state_to_context=add_session_state,
1833
+ response_format=response_format,
1834
+ debug_mode=debug_mode,
1835
+ background_tasks=background_tasks,
1836
+ **kwargs,
1810
1837
  )
1811
- )
1812
- else:
1813
- return run_response
1838
+ return response
1839
+ except (InputCheckError, OutputCheckError) as e:
1840
+ log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
1841
+ raise e
1842
+ except KeyboardInterrupt:
1843
+ run_response.content = "Operation cancelled by user"
1844
+ run_response.status = RunStatus.cancelled
1814
1845
 
1815
- # If we get here, all retries failed
1816
- if last_exception is not None:
1817
- log_error(
1818
- f"Failed after {num_attempts} attempts. Last error using {last_exception.model_name}({last_exception.model_id})"
1819
- )
1820
- if stream:
1821
- return generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
1846
+ if stream:
1847
+ return generator_wrapper( # type: ignore
1848
+ create_run_cancelled_event(
1849
+ from_run_response=run_response,
1850
+ reason="Operation cancelled by user",
1851
+ )
1852
+ )
1853
+ else:
1854
+ return run_response
1855
+ except Exception as e:
1856
+ # Check if this is the last attempt
1857
+ if attempt < num_attempts - 1:
1858
+ # Calculate delay with exponential backoff if enabled
1859
+ if self.exponential_backoff:
1860
+ delay = self.delay_between_retries * (2**attempt)
1861
+ else:
1862
+ delay = self.delay_between_retries
1863
+
1864
+ log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
1865
+ time.sleep(delay)
1866
+ continue
1867
+ else:
1868
+ # Final attempt failed - re-raise the exception
1869
+ log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
1870
+ raise
1871
+ except Exception as e:
1872
+ log_error(f"Unexpected error: {str(e)}")
1873
+ if attempt == num_attempts - 1:
1874
+ if stream:
1875
+ return generator_wrapper(create_run_error_event(run_response, error=str(e))) # type: ignore
1876
+ raise e
1822
1877
 
1823
- raise last_exception
1824
- else:
1825
- if stream:
1826
- return generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
1827
- raise Exception(f"Failed after {num_attempts} attempts.")
1878
+ # If we get here, all retries failed (shouldn't happen with current logic)
1879
+ raise Exception(f"Failed after {num_attempts} attempts.")
1828
1880
 
1829
1881
  async def _arun(
1830
1882
  self,
@@ -1943,7 +1995,12 @@ class Agent:
1943
1995
 
1944
1996
  # 7. Start memory creation as a background task (runs concurrently with the main execution)
1945
1997
  memory_task = None
1946
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
1998
+ if (
1999
+ run_messages.user_message is not None
2000
+ and self.memory_manager is not None
2001
+ and self.enable_user_memories
2002
+ and not self.enable_agentic_memory
2003
+ ):
1947
2004
  log_debug("Starting memory creation in background task.")
1948
2005
  memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
1949
2006
 
@@ -2033,7 +2090,7 @@ class Agent:
2033
2090
  await await_for_open_threads(memory_task=memory_task, cultural_knowledge_task=cultural_knowledge_task)
2034
2091
 
2035
2092
  # 15. Create session summary
2036
- if self.session_summary_manager is not None:
2093
+ if self.session_summary_manager is not None and self.enable_session_summaries:
2037
2094
  # Upsert the RunOutput to Agent Session before creating the session summary
2038
2095
  agent_session.upsert_run(run=run_response)
2039
2096
  try:
@@ -2075,6 +2132,8 @@ class Agent:
2075
2132
  return run_response
2076
2133
 
2077
2134
  finally:
2135
+ # Always disconnect connectable tools
2136
+ self._disconnect_connectable_tools()
2078
2137
  # Always disconnect MCP tools
2079
2138
  await self._disconnect_mcp_tools()
2080
2139
 
@@ -2221,7 +2280,12 @@ class Agent:
2221
2280
 
2222
2281
  # 7. Start memory creation as a background task (runs concurrently with the main execution)
2223
2282
  memory_task = None
2224
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2283
+ if (
2284
+ run_messages.user_message is not None
2285
+ and self.memory_manager is not None
2286
+ and self.enable_user_memories
2287
+ and not self.enable_agentic_memory
2288
+ ):
2225
2289
  log_debug("Starting memory creation in background task.")
2226
2290
  memory_task = create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
2227
2291
 
@@ -2357,7 +2421,7 @@ class Agent:
2357
2421
  yield item
2358
2422
 
2359
2423
  # 12. Create session summary
2360
- if self.session_summary_manager is not None:
2424
+ if self.session_summary_manager is not None and self.enable_session_summaries:
2361
2425
  # Upsert the RunOutput to Agent Session before creating the session summary
2362
2426
  agent_session.upsert_run(run=run_response)
2363
2427
 
@@ -2442,6 +2506,8 @@ class Agent:
2442
2506
  user_id=user_id,
2443
2507
  )
2444
2508
  finally:
2509
+ # Always disconnect connectable tools
2510
+ self._disconnect_connectable_tools()
2445
2511
  # Always disconnect MCP tools
2446
2512
  await self._disconnect_mcp_tools()
2447
2513
 
@@ -2479,7 +2545,6 @@ class Agent:
2479
2545
  files: Optional[Sequence[File]] = None,
2480
2546
  stream_events: Optional[bool] = None,
2481
2547
  stream_intermediate_steps: Optional[bool] = None,
2482
- retries: Optional[int] = None,
2483
2548
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
2484
2549
  add_history_to_context: Optional[bool] = None,
2485
2550
  add_dependencies_to_context: Optional[bool] = None,
@@ -2506,7 +2571,6 @@ class Agent:
2506
2571
  files: Optional[Sequence[File]] = None,
2507
2572
  stream_events: Optional[bool] = None,
2508
2573
  stream_intermediate_steps: Optional[bool] = None,
2509
- retries: Optional[int] = None,
2510
2574
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
2511
2575
  add_history_to_context: Optional[bool] = None,
2512
2576
  add_dependencies_to_context: Optional[bool] = None,
@@ -2535,7 +2599,6 @@ class Agent:
2535
2599
  files: Optional[Sequence[File]] = None,
2536
2600
  stream_events: Optional[bool] = None,
2537
2601
  stream_intermediate_steps: Optional[bool] = None,
2538
- retries: Optional[int] = None,
2539
2602
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
2540
2603
  add_history_to_context: Optional[bool] = None,
2541
2604
  add_dependencies_to_context: Optional[bool] = None,
@@ -2672,9 +2735,6 @@ class Agent:
2672
2735
  # Prepare arguments for the model (must be after run_context is fully initialized)
2673
2736
  response_format = self._get_response_format(run_context=run_context) if self.parser_model is None else None
2674
2737
 
2675
- # If no retries are set, use the agent's default retries
2676
- retries = retries if retries is not None else self.retries
2677
-
2678
2738
  # Create a new run_response for this attempt
2679
2739
  run_response = RunOutput(
2680
2740
  run_id=run_id,
@@ -2694,12 +2754,13 @@ class Agent:
2694
2754
  run_response.metrics = Metrics()
2695
2755
  run_response.metrics.start_timer()
2696
2756
 
2697
- last_exception = None
2698
- num_attempts = retries + 1
2699
-
2700
2757
  yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
2701
2758
 
2759
+ # Set up retry logic
2760
+ num_attempts = self.retries + 1
2761
+
2702
2762
  for attempt in range(num_attempts):
2763
+ log_debug(f"Retrying Agent run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
2703
2764
  try:
2704
2765
  # Pass the new run_response to _arun
2705
2766
  if stream:
@@ -2732,23 +2793,9 @@ class Agent:
2732
2793
  background_tasks=background_tasks,
2733
2794
  **kwargs,
2734
2795
  )
2735
-
2736
2796
  except (InputCheckError, OutputCheckError) as e:
2737
2797
  log_error(f"Validation failed: {str(e)} | Check trigger: {e.check_trigger}")
2738
2798
  raise e
2739
- except ModelProviderError as e:
2740
- log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
2741
- if isinstance(e, StopAgentRun):
2742
- raise e
2743
- last_exception = e
2744
- if attempt < num_attempts - 1: # Don't sleep on the last attempt
2745
- if self.exponential_backoff:
2746
- delay = 2**attempt * self.delay_between_retries
2747
- else:
2748
- delay = self.delay_between_retries
2749
- import time
2750
-
2751
- time.sleep(delay)
2752
2799
  except KeyboardInterrupt:
2753
2800
  run_response.content = "Operation cancelled by user"
2754
2801
  run_response.status = RunStatus.cancelled
@@ -2762,20 +2809,27 @@ class Agent:
2762
2809
  )
2763
2810
  else:
2764
2811
  return run_response
2812
+ except Exception as e:
2813
+ # Check if this is the last attempt
2814
+ if attempt < num_attempts - 1:
2815
+ # Calculate delay with exponential backoff if enabled
2816
+ if self.exponential_backoff:
2817
+ delay = self.delay_between_retries * (2**attempt)
2818
+ else:
2819
+ delay = self.delay_between_retries
2765
2820
 
2766
- # If we get here, all retries failed
2767
- if last_exception is not None:
2768
- log_error(
2769
- f"Failed after {num_attempts} attempts. Last error using {last_exception.model_name}({last_exception.model_id})"
2770
- )
2821
+ log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
2822
+ time.sleep(delay)
2823
+ continue
2824
+ else:
2825
+ # Final attempt failed - re-raise the exception
2826
+ log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
2827
+ if stream:
2828
+ return async_generator_wrapper(create_run_error_event(run_response, error=str(e))) # type: ignore
2829
+ raise e
2771
2830
 
2772
- if stream:
2773
- return async_generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
2774
- raise last_exception
2775
- else:
2776
- if stream:
2777
- return async_generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
2778
- raise Exception(f"Failed after {num_attempts} attempts.")
2831
+ # If we get here, all retries failed
2832
+ raise Exception(f"Failed after {num_attempts} attempts.")
2779
2833
 
2780
2834
  @overload
2781
2835
  def continue_run(
@@ -2784,16 +2838,17 @@ class Agent:
2784
2838
  *,
2785
2839
  run_id: Optional[str] = None,
2786
2840
  updated_tools: Optional[List[ToolExecution]] = None,
2841
+ requirements: Optional[List[RunRequirement]] = None,
2787
2842
  stream: Literal[False] = False,
2788
2843
  stream_events: Optional[bool] = None,
2789
2844
  stream_intermediate_steps: Optional[bool] = None,
2790
2845
  user_id: Optional[str] = None,
2791
2846
  session_id: Optional[str] = None,
2792
- retries: Optional[int] = None,
2793
2847
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
2794
2848
  dependencies: Optional[Dict[str, Any]] = None,
2795
2849
  metadata: Optional[Dict[str, Any]] = None,
2796
2850
  debug_mode: Optional[bool] = None,
2851
+ yield_run_output: bool = False,
2797
2852
  ) -> RunOutput: ...
2798
2853
 
2799
2854
  @overload
@@ -2803,16 +2858,17 @@ class Agent:
2803
2858
  *,
2804
2859
  run_id: Optional[str] = None,
2805
2860
  updated_tools: Optional[List[ToolExecution]] = None,
2861
+ requirements: Optional[List[RunRequirement]] = None,
2806
2862
  stream: Literal[True] = True,
2807
2863
  stream_events: Optional[bool] = False,
2808
2864
  stream_intermediate_steps: Optional[bool] = None,
2809
2865
  user_id: Optional[str] = None,
2810
2866
  session_id: Optional[str] = None,
2811
- retries: Optional[int] = None,
2812
2867
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
2813
2868
  dependencies: Optional[Dict[str, Any]] = None,
2814
2869
  metadata: Optional[Dict[str, Any]] = None,
2815
2870
  debug_mode: Optional[bool] = None,
2871
+ yield_run_output: bool = False,
2816
2872
  ) -> Iterator[RunOutputEvent]: ...
2817
2873
 
2818
2874
  def continue_run(
@@ -2821,36 +2877,37 @@ class Agent:
2821
2877
  *,
2822
2878
  run_id: Optional[str] = None, # type: ignore
2823
2879
  updated_tools: Optional[List[ToolExecution]] = None,
2880
+ requirements: Optional[List[RunRequirement]] = None,
2824
2881
  stream: Optional[bool] = None,
2825
2882
  stream_events: Optional[bool] = False,
2826
2883
  stream_intermediate_steps: Optional[bool] = None,
2827
2884
  user_id: Optional[str] = None,
2828
2885
  session_id: Optional[str] = None,
2829
2886
  run_context: Optional[RunContext] = None,
2830
- retries: Optional[int] = None,
2831
2887
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
2832
2888
  dependencies: Optional[Dict[str, Any]] = None,
2833
2889
  metadata: Optional[Dict[str, Any]] = None,
2834
2890
  debug_mode: Optional[bool] = None,
2891
+ yield_run_output: bool = False,
2835
2892
  **kwargs,
2836
- ) -> Union[RunOutput, Iterator[RunOutputEvent]]:
2893
+ ) -> Union[RunOutput, Iterator[Union[RunOutputEvent, RunOutput]]]:
2837
2894
  """Continue a previous run.
2838
2895
 
2839
2896
  Args:
2840
2897
  run_response: The run response to continue.
2841
2898
  run_id: The run id to continue. Alternative to passing run_response.
2842
- updated_tools: The updated tools to use for the run. Required to be used with `run_id`.
2899
+ requirements: The requirements to continue the run. This or updated_tools is required with `run_id`.
2843
2900
  stream: Whether to stream the response.
2844
2901
  stream_events: Whether to stream all events.
2845
2902
  user_id: The user id to continue the run for.
2846
2903
  session_id: The session id to continue the run for.
2847
2904
  run_context: The run context to use for the run.
2848
- retries: The number of retries to continue the run for.
2849
2905
  knowledge_filters: The knowledge filters to use for the run.
2850
2906
  dependencies: The dependencies to use for the run.
2851
2907
  metadata: The metadata to use for the run.
2852
2908
  debug_mode: Whether to enable debug mode.
2853
2909
  (deprecated) stream_intermediate_steps: Whether to stream all steps.
2910
+ (deprecated) updated_tools: Use 'requirements' instead.
2854
2911
  """
2855
2912
  if run_response is None and run_id is None:
2856
2913
  raise ValueError("Either run_response or run_id must be provided.")
@@ -2899,106 +2956,129 @@ class Agent:
2899
2956
  dependencies=dependencies,
2900
2957
  )
2901
2958
 
2902
- # Resolve dependencies
2903
- if run_context.dependencies is not None:
2904
- self._resolve_run_dependencies(run_context=run_context)
2905
-
2906
- # When filters are passed manually
2907
- if self.knowledge_filters or run_context.knowledge_filters or knowledge_filters:
2908
- run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
2909
-
2910
- # Merge agent metadata with run metadata
2911
- run_context.metadata = metadata
2912
- if self.metadata is not None:
2913
- if run_context.metadata is None:
2914
- run_context.metadata = self.metadata
2915
- else:
2916
- merge_dictionaries(run_context.metadata, self.metadata)
2917
-
2918
- # If no retries are set, use the agent's default retries
2919
- retries = retries if retries is not None else self.retries
2920
-
2921
- # Use stream override value when necessary
2922
- if stream is None:
2923
- stream = False if self.stream is None else self.stream
2924
-
2925
- # Considering both stream_events and stream_intermediate_steps (deprecated)
2926
- if stream_intermediate_steps is not None:
2927
- warnings.warn(
2928
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
2929
- DeprecationWarning,
2930
- stacklevel=2,
2931
- )
2932
- stream_events = stream_events or stream_intermediate_steps
2933
-
2934
- # Can't stream events if streaming is disabled
2935
- if stream is False:
2936
- stream_events = False
2959
+ # Resolve retry parameters
2960
+ num_attempts = self.retries + 1
2937
2961
 
2938
- if stream_events is None:
2939
- stream_events = False if self.stream_events is None else self.stream_events
2962
+ for attempt in range(num_attempts):
2963
+ log_debug(f"Retrying Agent continue_run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
2940
2964
 
2941
- # Can't stream events if streaming is disabled
2942
- if stream is False:
2943
- stream_events = False
2965
+ try:
2966
+ # Resolve dependencies
2967
+ if run_context.dependencies is not None:
2968
+ self._resolve_run_dependencies(run_context=run_context)
2969
+
2970
+ # When filters are passed manually
2971
+ if self.knowledge_filters or run_context.knowledge_filters or knowledge_filters:
2972
+ run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
2973
+
2974
+ # Merge agent metadata with run metadata
2975
+ run_context.metadata = metadata
2976
+ if self.metadata is not None:
2977
+ if run_context.metadata is None:
2978
+ run_context.metadata = self.metadata
2979
+ else:
2980
+ merge_dictionaries(run_context.metadata, self.metadata)
2981
+
2982
+ # Use stream override value when necessary
2983
+ if stream is None:
2984
+ stream = False if self.stream is None else self.stream
2985
+
2986
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
2987
+ if stream_intermediate_steps is not None:
2988
+ warnings.warn(
2989
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
2990
+ DeprecationWarning,
2991
+ stacklevel=2,
2992
+ )
2993
+ stream_events = stream_events or stream_intermediate_steps
2994
+
2995
+ # Can't stream events if streaming is disabled
2996
+ if stream is False:
2997
+ stream_events = False
2998
+
2999
+ if stream_events is None:
3000
+ stream_events = False if self.stream_events is None else self.stream_events
3001
+
3002
+ # Can't stream events if streaming is disabled
3003
+ if stream is False:
3004
+ stream_events = False
3005
+
3006
+ self.stream = self.stream or stream
3007
+ self.stream_events = self.stream_events or stream_events
3008
+
3009
+ # Run can be continued from previous run response or from passed run_response context
3010
+ if run_response is not None:
3011
+ # The run is continued from a provided run_response. This contains the updated tools.
3012
+ input = run_response.messages or []
3013
+ elif run_id is not None:
3014
+ # The run is continued from a run_id, one of requirements or updated_tool (deprecated) is required.
3015
+ if updated_tools is None and requirements is None:
3016
+ raise ValueError(
3017
+ "To continue a run from a given run_id, the requirements parameter must be provided."
3018
+ )
2944
3019
 
2945
- self.stream = self.stream or stream
2946
- self.stream_events = self.stream_events or stream_events
3020
+ runs = agent_session.runs
3021
+ run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
3022
+ if run_response is None:
3023
+ raise RuntimeError(f"No runs found for run ID {run_id}")
2947
3024
 
2948
- # Run can be continued from previous run response or from passed run_response context
2949
- if run_response is not None:
2950
- # The run is continued from a provided run_response. This contains the updated tools.
2951
- input = run_response.messages or []
2952
- elif run_id is not None:
2953
- # The run is continued from a run_id. This requires the updated tools to be passed.
2954
- if updated_tools is None:
2955
- raise ValueError("Updated tools are required to continue a run from a run_id.")
3025
+ input = run_response.messages or []
2956
3026
 
2957
- runs = agent_session.runs
2958
- run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
2959
- if run_response is None:
2960
- raise RuntimeError(f"No runs found for run ID {run_id}")
2961
- run_response.tools = updated_tools
2962
- input = run_response.messages or []
2963
- else:
2964
- raise ValueError("Either run_response or run_id must be provided.")
3027
+ # If we have updated_tools, set them in the run_response
3028
+ if updated_tools is not None:
3029
+ warnings.warn(
3030
+ "The 'updated_tools' parameter is deprecated and will be removed in future versions. Use 'requirements' instead.",
3031
+ DeprecationWarning,
3032
+ stacklevel=2,
3033
+ )
3034
+ run_response.tools = updated_tools
3035
+
3036
+ # If we have requirements, get the updated tools and set them in the run_response
3037
+ elif requirements is not None:
3038
+ run_response.requirements = requirements
3039
+ updated_tools = [req.tool_execution for req in requirements if req.tool_execution is not None]
3040
+ if updated_tools and run_response.tools:
3041
+ updated_tools_map = {tool.tool_call_id: tool for tool in updated_tools}
3042
+ run_response.tools = [
3043
+ updated_tools_map.get(tool.tool_call_id, tool) for tool in run_response.tools
3044
+ ]
3045
+ else:
3046
+ run_response.tools = updated_tools
3047
+ else:
3048
+ raise ValueError("Either run_response or run_id must be provided.")
2965
3049
 
2966
- # Prepare arguments for the model
2967
- self._set_default_model()
2968
- response_format = self._get_response_format(run_context=run_context)
2969
- self.model = cast(Model, self.model)
3050
+ # Prepare arguments for the model
3051
+ self._set_default_model()
3052
+ response_format = self._get_response_format(run_context=run_context)
3053
+ self.model = cast(Model, self.model)
2970
3054
 
2971
- processed_tools = self.get_tools(
2972
- run_response=run_response,
2973
- run_context=run_context,
2974
- session=agent_session,
2975
- user_id=user_id,
2976
- )
3055
+ processed_tools = self.get_tools(
3056
+ run_response=run_response,
3057
+ run_context=run_context,
3058
+ session=agent_session,
3059
+ user_id=user_id,
3060
+ )
2977
3061
 
2978
- _tools = self._determine_tools_for_model(
2979
- model=self.model,
2980
- processed_tools=processed_tools,
2981
- run_response=run_response,
2982
- run_context=run_context,
2983
- session=agent_session,
2984
- )
3062
+ _tools = self._determine_tools_for_model(
3063
+ model=self.model,
3064
+ processed_tools=processed_tools,
3065
+ run_response=run_response,
3066
+ run_context=run_context,
3067
+ session=agent_session,
3068
+ )
2985
3069
 
2986
- last_exception = None
2987
- num_attempts = retries + 1
2988
- for attempt in range(num_attempts):
2989
- run_response = cast(RunOutput, run_response)
3070
+ run_response = cast(RunOutput, run_response)
2990
3071
 
2991
- log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
3072
+ log_debug(f"Agent Run Start: {run_response.run_id}", center=True)
2992
3073
 
2993
- # Prepare run messages
2994
- run_messages = self._get_continue_run_messages(
2995
- input=input,
2996
- )
3074
+ # Prepare run messages
3075
+ run_messages = self._get_continue_run_messages(
3076
+ input=input,
3077
+ )
2997
3078
 
2998
- # Reset the run state
2999
- run_response.status = RunStatus.running
3079
+ # Reset the run state
3080
+ run_response.status = RunStatus.running
3000
3081
 
3001
- try:
3002
3082
  if stream:
3003
3083
  response_iterator = self._continue_run_stream(
3004
3084
  run_response=run_response,
@@ -3009,6 +3089,7 @@ class Agent:
3009
3089
  session=agent_session,
3010
3090
  response_format=response_format,
3011
3091
  stream_events=stream_events,
3092
+ yield_run_output=yield_run_output,
3012
3093
  debug_mode=debug_mode,
3013
3094
  background_tasks=background_tasks,
3014
3095
  **kwargs,
@@ -3028,42 +3109,34 @@ class Agent:
3028
3109
  **kwargs,
3029
3110
  )
3030
3111
  return response
3031
- except ModelProviderError as e:
3032
- log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
3033
- if isinstance(e, StopAgentRun):
3034
- raise e
3035
- last_exception = e
3036
- if attempt < num_attempts - 1: # Don't sleep on the last attempt
3112
+ except KeyboardInterrupt:
3113
+ if stream:
3114
+ return generator_wrapper( # type: ignore
3115
+ create_run_cancelled_event(run_response, "Operation cancelled by user") # type: ignore
3116
+ )
3117
+ else:
3118
+ run_response.content = "Operation cancelled by user" # type: ignore
3119
+ run_response.status = RunStatus.cancelled # type: ignore
3120
+ return run_response # type: ignore
3121
+ except Exception as e:
3122
+ # Check if this is the last attempt
3123
+ if attempt < num_attempts - 1:
3124
+ # Calculate delay with exponential backoff if enabled
3037
3125
  if self.exponential_backoff:
3038
- delay = 2**attempt * self.delay_between_retries
3126
+ delay = self.delay_between_retries * (2**attempt)
3039
3127
  else:
3040
3128
  delay = self.delay_between_retries
3041
- import time
3042
3129
 
3130
+ log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
3043
3131
  time.sleep(delay)
3044
- except KeyboardInterrupt:
3045
- if stream:
3046
- return generator_wrapper( # type: ignore
3047
- create_run_cancelled_event(run_response, "Operation cancelled by user")
3048
- )
3132
+ continue
3049
3133
  else:
3050
- run_response.content = "Operation cancelled by user"
3051
- run_response.status = RunStatus.cancelled
3052
- return run_response
3134
+ # Final attempt failed - re-raise the exception
3135
+ log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
3136
+ raise
3053
3137
 
3054
3138
  # If we get here, all retries failed
3055
- if last_exception is not None:
3056
- log_error(
3057
- f"Failed after {num_attempts} attempts. Last error using {last_exception.model_name}({last_exception.model_id})"
3058
- )
3059
-
3060
- if stream:
3061
- return generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
3062
- raise last_exception
3063
- else:
3064
- if stream:
3065
- return generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
3066
- raise Exception(f"Failed after {num_attempts} attempts.")
3139
+ raise Exception(f"Failed after {num_attempts} attempts.")
3067
3140
 
3068
3141
  def _continue_run(
3069
3142
  self,
@@ -3148,7 +3221,7 @@ class Agent:
3148
3221
  raise_if_cancelled(run_response.run_id) # type: ignore
3149
3222
 
3150
3223
  # 7. Create session summary
3151
- if self.session_summary_manager is not None:
3224
+ if self.session_summary_manager is not None and self.enable_session_summaries:
3152
3225
  # Upsert the RunOutput to Agent Session before creating the session summary
3153
3226
  session.upsert_run(run=run_response)
3154
3227
 
@@ -3182,6 +3255,8 @@ class Agent:
3182
3255
 
3183
3256
  return run_response
3184
3257
  finally:
3258
+ # Always disconnect connectable tools
3259
+ self._disconnect_connectable_tools()
3185
3260
  # Always clean up the run tracking
3186
3261
  cleanup_run(run_response.run_id) # type: ignore
3187
3262
 
@@ -3196,9 +3271,10 @@ class Agent:
3196
3271
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3197
3272
  stream_events: bool = False,
3198
3273
  debug_mode: Optional[bool] = None,
3274
+ yield_run_output: bool = False,
3199
3275
  background_tasks: Optional[Any] = None,
3200
3276
  **kwargs,
3201
- ) -> Iterator[RunOutputEvent]:
3277
+ ) -> Iterator[Union[RunOutputEvent, RunOutput]]:
3202
3278
  """Continue a previous run.
3203
3279
 
3204
3280
  Steps:
@@ -3281,7 +3357,7 @@ class Agent:
3281
3357
  raise_if_cancelled(run_response.run_id) # type: ignore
3282
3358
 
3283
3359
  # 4. Create session summary
3284
- if self.session_summary_manager is not None:
3360
+ if self.session_summary_manager is not None and self.enable_session_summaries:
3285
3361
  # Upsert the RunOutput to Agent Session before creating the session summary
3286
3362
  session.upsert_run(run=run_response)
3287
3363
 
@@ -3331,6 +3407,9 @@ class Agent:
3331
3407
  if stream_events:
3332
3408
  yield completed_event # type: ignore
3333
3409
 
3410
+ if yield_run_output:
3411
+ yield run_response
3412
+
3334
3413
  # Log Agent Telemetry
3335
3414
  self._log_agent_telemetry(session_id=session.session_id, run_id=run_response.run_id)
3336
3415
 
@@ -3355,6 +3434,8 @@ class Agent:
3355
3434
  run_response=run_response, session=session, run_context=run_context, user_id=user_id
3356
3435
  )
3357
3436
  finally:
3437
+ # Always disconnect connectable tools
3438
+ self._disconnect_connectable_tools()
3358
3439
  # Always clean up the run tracking
3359
3440
  cleanup_run(run_response.run_id) # type: ignore
3360
3441
 
@@ -3368,9 +3449,9 @@ class Agent:
3368
3449
  stream_intermediate_steps: Optional[bool] = None,
3369
3450
  run_id: Optional[str] = None,
3370
3451
  updated_tools: Optional[List[ToolExecution]] = None,
3452
+ requirements: Optional[List[RunRequirement]] = None,
3371
3453
  user_id: Optional[str] = None,
3372
3454
  session_id: Optional[str] = None,
3373
- retries: Optional[int] = None,
3374
3455
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
3375
3456
  dependencies: Optional[Dict[str, Any]] = None,
3376
3457
  metadata: Optional[Dict[str, Any]] = None,
@@ -3388,9 +3469,9 @@ class Agent:
3388
3469
  stream_intermediate_steps: Optional[bool] = None,
3389
3470
  run_id: Optional[str] = None,
3390
3471
  updated_tools: Optional[List[ToolExecution]] = None,
3472
+ requirements: Optional[List[RunRequirement]] = None,
3391
3473
  user_id: Optional[str] = None,
3392
3474
  session_id: Optional[str] = None,
3393
- retries: Optional[int] = None,
3394
3475
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
3395
3476
  dependencies: Optional[Dict[str, Any]] = None,
3396
3477
  metadata: Optional[Dict[str, Any]] = None,
@@ -3404,13 +3485,13 @@ class Agent:
3404
3485
  *,
3405
3486
  run_id: Optional[str] = None, # type: ignore
3406
3487
  updated_tools: Optional[List[ToolExecution]] = None,
3488
+ requirements: Optional[List[RunRequirement]] = None,
3407
3489
  stream: Optional[bool] = None,
3408
3490
  stream_events: Optional[bool] = None,
3409
3491
  stream_intermediate_steps: Optional[bool] = None,
3410
3492
  user_id: Optional[str] = None,
3411
3493
  session_id: Optional[str] = None,
3412
3494
  run_context: Optional[RunContext] = None,
3413
- retries: Optional[int] = None,
3414
3495
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
3415
3496
  dependencies: Optional[Dict[str, Any]] = None,
3416
3497
  metadata: Optional[Dict[str, Any]] = None,
@@ -3423,19 +3504,20 @@ class Agent:
3423
3504
  Args:
3424
3505
  run_response: The run response to continue.
3425
3506
  run_id: The run id to continue. Alternative to passing run_response.
3426
- updated_tools: The updated tools to use for the run. Required to be used with `run_id`.
3507
+
3508
+ requirements: The requirements to continue the run. This or updated_tools is required with `run_id`.
3427
3509
  stream: Whether to stream the response.
3428
3510
  stream_events: Whether to stream all events.
3429
3511
  user_id: The user id to continue the run for.
3430
3512
  session_id: The session id to continue the run for.
3431
3513
  run_context: The run context to use for the run.
3432
- retries: The number of retries to continue the run for.
3433
3514
  knowledge_filters: The knowledge filters to use for the run.
3434
3515
  dependencies: The dependencies to use for continuing the run.
3435
3516
  metadata: The metadata to use for continuing the run.
3436
3517
  debug_mode: Whether to enable debug mode.
3437
3518
  yield_run_output: Whether to yield the run response.
3438
3519
  (deprecated) stream_intermediate_steps: Whether to stream all steps.
3520
+ (deprecated) updated_tools: Use 'requirements' instead.
3439
3521
  """
3440
3522
  if run_response is None and run_id is None:
3441
3523
  raise ValueError("Either run_response or run_id must be provided.")
@@ -3443,6 +3525,12 @@ class Agent:
3443
3525
  if run_response is None and (run_id is not None and (session_id is None and self.session_id is None)):
3444
3526
  raise ValueError("Session ID is required to continue a run from a run_id.")
3445
3527
 
3528
+ if updated_tools is not None:
3529
+ warnings.warn(
3530
+ "The 'updated_tools' parameter is deprecated and will be removed in future versions. Use 'requirements' instead.",
3531
+ DeprecationWarning,
3532
+ stacklevel=2,
3533
+ )
3446
3534
  background_tasks = kwargs.pop("background_tasks", None)
3447
3535
  if background_tasks is not None:
3448
3536
  from fastapi import BackgroundTasks
@@ -3460,9 +3548,6 @@ class Agent:
3460
3548
 
3461
3549
  dependencies = dependencies if dependencies is not None else self.dependencies
3462
3550
 
3463
- # If no retries are set, use the agent's default retries
3464
- retries = retries if retries is not None else self.retries
3465
-
3466
3551
  # Use stream override value when necessary
3467
3552
  if stream is None:
3468
3553
  stream = False if self.stream is None else self.stream
@@ -3502,6 +3587,9 @@ class Agent:
3502
3587
  else:
3503
3588
  merge_dictionaries(metadata, self.metadata)
3504
3589
 
3590
+ # Resolve retry parameters
3591
+ num_attempts = self.retries + 1
3592
+
3505
3593
  # Prepare arguments for the model
3506
3594
  response_format = self._get_response_format(run_context=run_context)
3507
3595
  self.model = cast(Model, self.model)
@@ -3517,15 +3605,16 @@ class Agent:
3517
3605
  metadata=metadata,
3518
3606
  )
3519
3607
 
3520
- last_exception = None
3521
- num_attempts = retries + 1
3522
3608
  for attempt in range(num_attempts):
3609
+ log_debug(f"Retrying Agent acontinue_run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
3610
+
3523
3611
  try:
3524
3612
  if stream:
3525
3613
  return self._acontinue_run_stream(
3526
3614
  run_response=run_response,
3527
3615
  run_context=run_context,
3528
3616
  updated_tools=updated_tools,
3617
+ requirements=requirements,
3529
3618
  run_id=run_id,
3530
3619
  user_id=user_id,
3531
3620
  session_id=session_id,
@@ -3542,6 +3631,7 @@ class Agent:
3542
3631
  run_response=run_response,
3543
3632
  run_context=run_context,
3544
3633
  updated_tools=updated_tools,
3634
+ requirements=requirements,
3545
3635
  run_id=run_id,
3546
3636
  user_id=user_id,
3547
3637
  response_format=response_format,
@@ -3549,19 +3639,6 @@ class Agent:
3549
3639
  background_tasks=background_tasks,
3550
3640
  **kwargs,
3551
3641
  )
3552
- except ModelProviderError as e:
3553
- log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
3554
- if isinstance(e, StopAgentRun):
3555
- raise e
3556
- last_exception = e
3557
- if attempt < num_attempts - 1: # Don't sleep on the last attempt
3558
- if self.exponential_backoff:
3559
- delay = 2**attempt * self.delay_between_retries
3560
- else:
3561
- delay = self.delay_between_retries
3562
- import time
3563
-
3564
- time.sleep(delay)
3565
3642
  except KeyboardInterrupt:
3566
3643
  run_response = cast(RunOutput, run_response)
3567
3644
  if stream:
@@ -3572,19 +3649,25 @@ class Agent:
3572
3649
  run_response.content = "Operation cancelled by user"
3573
3650
  run_response.status = RunStatus.cancelled
3574
3651
  return run_response
3652
+ except Exception as e:
3653
+ # Check if this is the last attempt
3654
+ if attempt < num_attempts - 1:
3655
+ # Calculate delay with exponential backoff if enabled
3656
+ if self.exponential_backoff:
3657
+ delay = self.delay_between_retries * (2**attempt)
3658
+ else:
3659
+ delay = self.delay_between_retries
3660
+
3661
+ log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
3662
+ time.sleep(delay)
3663
+ continue
3664
+ else:
3665
+ # Final attempt failed - re-raise the exception
3666
+ log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
3667
+ raise
3575
3668
 
3576
3669
  # If we get here, all retries failed
3577
- if last_exception is not None:
3578
- log_error(
3579
- f"Failed after {num_attempts} attempts. Last error using {last_exception.model_name}({last_exception.model_id})"
3580
- )
3581
- if stream:
3582
- return async_generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
3583
- raise last_exception
3584
- else:
3585
- if stream:
3586
- return async_generator_wrapper(create_run_error_event(run_response, error=str(last_exception))) # type: ignore
3587
- raise Exception(f"Failed after {num_attempts} attempts.")
3670
+ raise Exception(f"Failed after {num_attempts} attempts.")
3588
3671
 
3589
3672
  async def _acontinue_run(
3590
3673
  self,
@@ -3592,6 +3675,7 @@ class Agent:
3592
3675
  run_context: RunContext,
3593
3676
  run_response: Optional[RunOutput] = None,
3594
3677
  updated_tools: Optional[List[ToolExecution]] = None,
3678
+ requirements: Optional[List[RunRequirement]] = None,
3595
3679
  run_id: Optional[str] = None,
3596
3680
  user_id: Optional[str] = None,
3597
3681
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
@@ -3644,15 +3728,29 @@ class Agent:
3644
3728
  input = run_response.messages or []
3645
3729
  elif run_id is not None:
3646
3730
  # The run is continued from a run_id. This requires the updated tools to be passed.
3647
- if updated_tools is None:
3648
- raise ValueError("Updated tools are required to continue a run from a run_id.")
3731
+ if updated_tools is None and requirements is None:
3732
+ raise ValueError("Either updated tools or requirements are required to continue a run from a run_id.")
3649
3733
 
3650
3734
  runs = agent_session.runs
3651
3735
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
3652
3736
  if run_response is None:
3653
3737
  raise RuntimeError(f"No runs found for run ID {run_id}")
3654
- run_response.tools = updated_tools
3738
+
3655
3739
  input = run_response.messages or []
3740
+
3741
+ # If we have updated_tools, set them in the run_response
3742
+ if updated_tools is not None:
3743
+ run_response.tools = updated_tools
3744
+
3745
+ # If we have requirements, get the updated tools and set them in the run_response
3746
+ elif requirements is not None:
3747
+ run_response.requirements = requirements
3748
+ updated_tools = [req.tool_execution for req in requirements if req.tool_execution is not None]
3749
+ if updated_tools and run_response.tools:
3750
+ updated_tools_map = {tool.tool_call_id: tool for tool in updated_tools}
3751
+ run_response.tools = [updated_tools_map.get(tool.tool_call_id, tool) for tool in run_response.tools]
3752
+ else:
3753
+ run_response.tools = updated_tools
3656
3754
  else:
3657
3755
  raise ValueError("Either run_response or run_id must be provided.")
3658
3756
 
@@ -3748,7 +3846,7 @@ class Agent:
3748
3846
  raise_if_cancelled(run_response.run_id) # type: ignore
3749
3847
 
3750
3848
  # 13. Create session summary
3751
- if self.session_summary_manager is not None:
3849
+ if self.session_summary_manager is not None and self.enable_session_summaries:
3752
3850
  # Upsert the RunOutput to Agent Session before creating the session summary
3753
3851
  agent_session.upsert_run(run=run_response)
3754
3852
 
@@ -3791,6 +3889,8 @@ class Agent:
3791
3889
 
3792
3890
  return run_response
3793
3891
  finally:
3892
+ # Always disconnect connectable tools
3893
+ self._disconnect_connectable_tools()
3794
3894
  # Always disconnect MCP tools
3795
3895
  await self._disconnect_mcp_tools()
3796
3896
 
@@ -3803,6 +3903,7 @@ class Agent:
3803
3903
  run_context: RunContext,
3804
3904
  run_response: Optional[RunOutput] = None,
3805
3905
  updated_tools: Optional[List[ToolExecution]] = None,
3906
+ requirements: Optional[List[RunRequirement]] = None,
3806
3907
  run_id: Optional[str] = None,
3807
3908
  user_id: Optional[str] = None,
3808
3909
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
@@ -3852,17 +3953,32 @@ class Agent:
3852
3953
  if run_response is not None:
3853
3954
  # The run is continued from a provided run_response. This contains the updated tools.
3854
3955
  input = run_response.messages or []
3956
+
3855
3957
  elif run_id is not None:
3856
- # The run is continued from a run_id. This requires the updated tools to be passed.
3857
- if updated_tools is None:
3858
- raise ValueError("Updated tools are required to continue a run from a run_id.")
3958
+ # The run is continued from a run_id. This requires the updated tools or requirements to be passed.
3959
+ if updated_tools is None and requirements is None:
3960
+ raise ValueError("Either updated tools or requirements are required to continue a run from a run_id.")
3859
3961
 
3860
3962
  runs = agent_session.runs
3861
3963
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
3862
3964
  if run_response is None:
3863
3965
  raise RuntimeError(f"No runs found for run ID {run_id}")
3864
- run_response.tools = updated_tools
3966
+
3865
3967
  input = run_response.messages or []
3968
+
3969
+ # If we have updated_tools, set them in the run_response
3970
+ if updated_tools is not None:
3971
+ run_response.tools = updated_tools
3972
+
3973
+ # If we have requirements, get the updated tools and set them in the run_response
3974
+ elif requirements is not None:
3975
+ run_response.requirements = requirements
3976
+ updated_tools = [req.tool_execution for req in requirements if req.tool_execution is not None]
3977
+ if updated_tools and run_response.tools:
3978
+ updated_tools_map = {tool.tool_call_id: tool for tool in updated_tools}
3979
+ run_response.tools = [updated_tools_map.get(tool.tool_call_id, tool) for tool in run_response.tools]
3980
+ else:
3981
+ run_response.tools = updated_tools
3866
3982
  else:
3867
3983
  raise ValueError("Either run_response or run_id must be provided.")
3868
3984
 
@@ -4003,7 +4119,7 @@ class Agent:
4003
4119
  raise_if_cancelled(run_response.run_id) # type: ignore
4004
4120
 
4005
4121
  # 9. Create session summary
4006
- if self.session_summary_manager is not None:
4122
+ if self.session_summary_manager is not None and self.enable_session_summaries:
4007
4123
  # Upsert the RunOutput to Agent Session before creating the session summary
4008
4124
  agent_session.upsert_run(run=run_response)
4009
4125
 
@@ -4081,6 +4197,8 @@ class Agent:
4081
4197
  user_id=user_id,
4082
4198
  )
4083
4199
  finally:
4200
+ # Always disconnect connectable tools
4201
+ self._disconnect_connectable_tools()
4084
4202
  # Always disconnect MCP tools
4085
4203
  await self._disconnect_mcp_tools()
4086
4204
 
@@ -4496,6 +4614,7 @@ class Agent:
4496
4614
  create_run_paused_event(
4497
4615
  from_run_response=run_response,
4498
4616
  tools=run_response.tools,
4617
+ requirements=run_response.requirements,
4499
4618
  ),
4500
4619
  run_response,
4501
4620
  events_to_skip=self.events_to_skip, # type: ignore
@@ -4544,6 +4663,7 @@ class Agent:
4544
4663
  create_run_paused_event(
4545
4664
  from_run_response=run_response,
4546
4665
  tools=run_response.tools,
4666
+ requirements=run_response.requirements,
4547
4667
  ),
4548
4668
  run_response,
4549
4669
  events_to_skip=self.events_to_skip, # type: ignore
@@ -5359,7 +5479,7 @@ class Agent:
5359
5479
  run_response.images = []
5360
5480
  run_response.images.append(image)
5361
5481
 
5362
- # Handle tool interruption events
5482
+ # Handle tool interruption events (HITL flow)
5363
5483
  elif model_response_event.event == ModelResponseEvent.tool_call_paused.value:
5364
5484
  # Add tool calls to the run_response
5365
5485
  tool_executions_list = model_response_event.tool_executions
@@ -5369,6 +5489,10 @@ class Agent:
5369
5489
  run_response.tools = tool_executions_list
5370
5490
  else:
5371
5491
  run_response.tools.extend(tool_executions_list)
5492
+ # Add requirement to the run_response
5493
+ if run_response.requirements is None:
5494
+ run_response.requirements = []
5495
+ run_response.requirements.append(RunRequirement(tool_execution=tool_executions_list[-1]))
5372
5496
 
5373
5497
  # If the model response is a tool_call_started, add the tool call to the run_response
5374
5498
  elif (
@@ -5536,7 +5660,12 @@ class Agent:
5536
5660
  user_message_str = (
5537
5661
  run_messages.user_message.get_content_string() if run_messages.user_message is not None else None
5538
5662
  )
5539
- if user_message_str is not None and user_message_str.strip() != "" and self.memory_manager is not None:
5663
+ if (
5664
+ user_message_str is not None
5665
+ and user_message_str.strip() != ""
5666
+ and self.memory_manager is not None
5667
+ and self.enable_user_memories
5668
+ ):
5540
5669
  log_debug("Managing user memories")
5541
5670
  self.memory_manager.create_user_memories( # type: ignore
5542
5671
  message=user_message_str,
@@ -5564,7 +5693,7 @@ class Agent:
5564
5693
  for msg in parsed_messages
5565
5694
  if msg.content and (not isinstance(msg.content, str) or msg.content.strip() != "")
5566
5695
  ]
5567
- if len(non_empty_messages) > 0 and self.memory_manager is not None:
5696
+ if len(non_empty_messages) > 0 and self.memory_manager is not None and self.enable_user_memories:
5568
5697
  self.memory_manager.create_user_memories(messages=non_empty_messages, user_id=user_id, agent_id=self.id) # type: ignore
5569
5698
  else:
5570
5699
  log_warning("Unable to add messages to memory")
@@ -5577,7 +5706,12 @@ class Agent:
5577
5706
  user_message_str = (
5578
5707
  run_messages.user_message.get_content_string() if run_messages.user_message is not None else None
5579
5708
  )
5580
- if user_message_str is not None and user_message_str.strip() != "" and self.memory_manager is not None:
5709
+ if (
5710
+ user_message_str is not None
5711
+ and user_message_str.strip() != ""
5712
+ and self.memory_manager is not None
5713
+ and self.enable_user_memories
5714
+ ):
5581
5715
  log_debug("Managing user memories")
5582
5716
  await self.memory_manager.acreate_user_memories( # type: ignore
5583
5717
  message=user_message_str,
@@ -5605,7 +5739,7 @@ class Agent:
5605
5739
  for msg in parsed_messages
5606
5740
  if msg.content and (not isinstance(msg.content, str) or msg.content.strip() != "")
5607
5741
  ]
5608
- if len(non_empty_messages) > 0 and self.memory_manager is not None:
5742
+ if len(non_empty_messages) > 0 and self.memory_manager is not None and self.enable_user_memories:
5609
5743
  await self.memory_manager.acreate_user_memories( # type: ignore
5610
5744
  messages=non_empty_messages, user_id=user_id, agent_id=self.id
5611
5745
  )
@@ -5649,6 +5783,9 @@ class Agent:
5649
5783
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5650
5784
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5651
5785
 
5786
+ # Connect tools that require connection management
5787
+ self._connect_connectable_tools()
5788
+
5652
5789
  # Add provided tools
5653
5790
  if self.tools is not None:
5654
5791
  # If not running in async mode, raise if any tool is async
@@ -5695,6 +5832,7 @@ class Agent:
5695
5832
  run_response=run_response,
5696
5833
  async_mode=False,
5697
5834
  knowledge_filters=run_context.knowledge_filters,
5835
+ run_context=run_context,
5698
5836
  )
5699
5837
  )
5700
5838
  else:
@@ -5703,6 +5841,7 @@ class Agent:
5703
5841
  run_response=run_response,
5704
5842
  async_mode=False,
5705
5843
  knowledge_filters=run_context.knowledge_filters,
5844
+ run_context=run_context,
5706
5845
  )
5707
5846
  )
5708
5847
 
@@ -5721,6 +5860,9 @@ class Agent:
5721
5860
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5722
5861
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5723
5862
 
5863
+ # Connect tools that require connection management
5864
+ self._connect_connectable_tools()
5865
+
5724
5866
  # Connect MCP tools
5725
5867
  await self._connect_mcp_tools()
5726
5868
 
@@ -5783,6 +5925,7 @@ class Agent:
5783
5925
  run_response=run_response,
5784
5926
  async_mode=True,
5785
5927
  knowledge_filters=run_context.knowledge_filters,
5928
+ run_context=run_context,
5786
5929
  )
5787
5930
  )
5788
5931
  else:
@@ -5791,6 +5934,7 @@ class Agent:
5791
5934
  run_response=run_response,
5792
5935
  async_mode=True,
5793
5936
  knowledge_filters=run_context.knowledge_filters,
5937
+ run_context=run_context,
5794
5938
  )
5795
5939
  )
5796
5940
 
@@ -6927,12 +7071,13 @@ class Agent:
6927
7071
  Optional[List[UserMemory]]: The user memories.
6928
7072
  """
6929
7073
  if self.memory_manager is None:
6930
- return None
7074
+ self._set_memory_manager()
7075
+
6931
7076
  user_id = user_id if user_id is not None else self.user_id
6932
7077
  if user_id is None:
6933
7078
  user_id = "default"
6934
7079
 
6935
- return self.memory_manager.get_user_memories(user_id=user_id)
7080
+ return self.memory_manager.get_user_memories(user_id=user_id) # type: ignore
6936
7081
 
6937
7082
  async def aget_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
6938
7083
  """Get the user memories for the given user ID.
@@ -6943,12 +7088,13 @@ class Agent:
6943
7088
  Optional[List[UserMemory]]: The user memories.
6944
7089
  """
6945
7090
  if self.memory_manager is None:
6946
- return None
7091
+ self._set_memory_manager()
7092
+
6947
7093
  user_id = user_id if user_id is not None else self.user_id
6948
7094
  if user_id is None:
6949
7095
  user_id = "default"
6950
7096
 
6951
- return await self.memory_manager.aget_user_memories(user_id=user_id)
7097
+ return await self.memory_manager.aget_user_memories(user_id=user_id) # type: ignore
6952
7098
 
6953
7099
  def get_culture_knowledge(self) -> Optional[List[CulturalKnowledge]]:
6954
7100
  """Get the cultural knowledge the agent has access to
@@ -7820,7 +7966,7 @@ class Agent:
7820
7966
  retrieval_timer = Timer()
7821
7967
  retrieval_timer.start()
7822
7968
  docs_from_knowledge = self.get_relevant_docs_from_knowledge(
7823
- query=user_msg_content, filters=knowledge_filters, **kwargs
7969
+ query=user_msg_content, filters=knowledge_filters, run_context=run_context, **kwargs
7824
7970
  )
7825
7971
  if docs_from_knowledge is not None:
7826
7972
  references = MessageReferences(
@@ -7994,7 +8140,7 @@ class Agent:
7994
8140
  retrieval_timer = Timer()
7995
8141
  retrieval_timer.start()
7996
8142
  docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
7997
- query=user_msg_content, filters=knowledge_filters, **kwargs
8143
+ query=user_msg_content, filters=knowledge_filters, run_context=run_context, **kwargs
7998
8144
  )
7999
8145
  if docs_from_knowledge is not None:
8000
8146
  references = MessageReferences(
@@ -8575,6 +8721,7 @@ class Agent:
8575
8721
  num_documents: Optional[int] = None,
8576
8722
  filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
8577
8723
  validate_filters: bool = False,
8724
+ run_context: Optional[RunContext] = None,
8578
8725
  **kwargs,
8579
8726
  ) -> Optional[List[Union[Dict[str, Any], str]]]:
8580
8727
  """Get relevant docs from the knowledge base to answer a query.
@@ -8584,6 +8731,7 @@ class Agent:
8584
8731
  num_documents (Optional[int]): Number of documents to return.
8585
8732
  filters (Optional[Dict[str, Any]]): Filters to apply to the search.
8586
8733
  validate_filters (bool): Whether to validate the filters against known valid filter keys.
8734
+ run_context (Optional[RunContext]): Runtime context containing dependencies and other context.
8587
8735
  **kwargs: Additional keyword arguments.
8588
8736
 
8589
8737
  Returns:
@@ -8591,6 +8739,9 @@ class Agent:
8591
8739
  """
8592
8740
  from agno.knowledge.document import Document
8593
8741
 
8742
+ # Extract dependencies from run_context if available
8743
+ dependencies = run_context.dependencies if run_context else None
8744
+
8594
8745
  if num_documents is None and self.knowledge is not None:
8595
8746
  num_documents = self.knowledge.max_results
8596
8747
  # Validate the filters against known valid filter keys
@@ -8622,6 +8773,11 @@ class Agent:
8622
8773
  knowledge_retriever_kwargs = {"agent": self}
8623
8774
  if "filters" in sig.parameters:
8624
8775
  knowledge_retriever_kwargs["filters"] = filters
8776
+ if "run_context" in sig.parameters:
8777
+ knowledge_retriever_kwargs["run_context"] = run_context
8778
+ elif "dependencies" in sig.parameters:
8779
+ # Backward compatibility: support dependencies parameter
8780
+ knowledge_retriever_kwargs["dependencies"] = dependencies
8625
8781
  knowledge_retriever_kwargs.update({"query": query, "num_documents": num_documents, **kwargs})
8626
8782
  return self.knowledge_retriever(**knowledge_retriever_kwargs)
8627
8783
  except Exception as e:
@@ -8660,11 +8816,15 @@ class Agent:
8660
8816
  num_documents: Optional[int] = None,
8661
8817
  filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
8662
8818
  validate_filters: bool = False,
8819
+ run_context: Optional[RunContext] = None,
8663
8820
  **kwargs,
8664
8821
  ) -> Optional[List[Union[Dict[str, Any], str]]]:
8665
8822
  """Get relevant documents from knowledge base asynchronously."""
8666
8823
  from agno.knowledge.document import Document
8667
8824
 
8825
+ # Extract dependencies from run_context if available
8826
+ dependencies = run_context.dependencies if run_context else None
8827
+
8668
8828
  if num_documents is None and self.knowledge is not None:
8669
8829
  num_documents = self.knowledge.max_results
8670
8830
 
@@ -8696,6 +8856,11 @@ class Agent:
8696
8856
  knowledge_retriever_kwargs = {"agent": self}
8697
8857
  if "filters" in sig.parameters:
8698
8858
  knowledge_retriever_kwargs["filters"] = filters
8859
+ if "run_context" in sig.parameters:
8860
+ knowledge_retriever_kwargs["run_context"] = run_context
8861
+ elif "dependencies" in sig.parameters:
8862
+ # Backward compatibility: support dependencies parameter
8863
+ knowledge_retriever_kwargs["dependencies"] = dependencies
8699
8864
  knowledge_retriever_kwargs.update({"query": query, "num_documents": num_documents, **kwargs})
8700
8865
  result = self.knowledge_retriever(**knowledge_retriever_kwargs)
8701
8866
 
@@ -10038,6 +10203,7 @@ class Agent:
10038
10203
  run_response: RunOutput,
10039
10204
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
10040
10205
  async_mode: bool = False,
10206
+ run_context: Optional[RunContext] = None,
10041
10207
  ) -> Function:
10042
10208
  """Factory function to create a search_knowledge_base function with filters."""
10043
10209
 
@@ -10054,7 +10220,9 @@ class Agent:
10054
10220
  # Get the relevant documents from the knowledge base, passing filters
10055
10221
  retrieval_timer = Timer()
10056
10222
  retrieval_timer.start()
10057
- docs_from_knowledge = self.get_relevant_docs_from_knowledge(query=query, filters=knowledge_filters)
10223
+ docs_from_knowledge = self.get_relevant_docs_from_knowledge(
10224
+ query=query, filters=knowledge_filters, run_context=run_context
10225
+ )
10058
10226
  if docs_from_knowledge is not None:
10059
10227
  references = MessageReferences(
10060
10228
  query=query,
@@ -10085,7 +10253,9 @@ class Agent:
10085
10253
  """
10086
10254
  retrieval_timer = Timer()
10087
10255
  retrieval_timer.start()
10088
- docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(query=query, filters=knowledge_filters)
10256
+ docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
10257
+ query=query, filters=knowledge_filters, run_context=run_context
10258
+ )
10089
10259
  if docs_from_knowledge is not None:
10090
10260
  references = MessageReferences(
10091
10261
  query=query,
@@ -10114,6 +10284,7 @@ class Agent:
10114
10284
  run_response: RunOutput,
10115
10285
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
10116
10286
  async_mode: bool = False,
10287
+ run_context: Optional[RunContext] = None,
10117
10288
  ) -> Function:
10118
10289
  """Factory function to create a search_knowledge_base function with filters."""
10119
10290
 
@@ -10134,7 +10305,7 @@ class Agent:
10134
10305
  retrieval_timer = Timer()
10135
10306
  retrieval_timer.start()
10136
10307
  docs_from_knowledge = self.get_relevant_docs_from_knowledge(
10137
- query=query, filters=search_filters, validate_filters=True
10308
+ query=query, filters=search_filters, validate_filters=True, run_context=run_context
10138
10309
  )
10139
10310
  if docs_from_knowledge is not None:
10140
10311
  references = MessageReferences(
@@ -10171,7 +10342,7 @@ class Agent:
10171
10342
  retrieval_timer = Timer()
10172
10343
  retrieval_timer.start()
10173
10344
  docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
10174
- query=query, filters=search_filters, validate_filters=True
10345
+ query=query, filters=search_filters, validate_filters=True, run_context=run_context
10175
10346
  )
10176
10347
  if docs_from_knowledge is not None:
10177
10348
  references = MessageReferences(