agno 2.3.5__py3-none-any.whl → 2.3.7__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
@@ -68,6 +68,7 @@ from agno.run.cancel import (
68
68
  register_run,
69
69
  )
70
70
  from agno.run.messages import RunMessages
71
+ from agno.run.requirement import RunRequirement
71
72
  from agno.run.team import TeamRunOutputEvent
72
73
  from agno.session import AgentSession, SessionSummaryManager, TeamSession, WorkflowSession
73
74
  from agno.session.summary import SessionSummary
@@ -683,6 +684,7 @@ class Agent:
683
684
  self._hooks_normalised = False
684
685
 
685
686
  self._mcp_tools_initialized_on_run: List[Any] = []
687
+ self._connectable_tools_initialized_on_run: List[Any] = []
686
688
 
687
689
  # Lazy-initialized shared thread pool executor for background tasks (memory, cultural knowledge, etc.)
688
690
  self._background_executor: Optional[Any] = None
@@ -912,16 +914,48 @@ class Agent:
912
914
  and any(c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__)
913
915
  and not tool.initialized # type: ignore
914
916
  ):
915
- # Connect the MCP server
916
- await tool.connect() # type: ignore
917
- self._mcp_tools_initialized_on_run.append(tool) # type: ignore
917
+ try:
918
+ # Connect the MCP server
919
+ await tool.connect() # type: ignore
920
+ self._mcp_tools_initialized_on_run.append(tool) # type: ignore
921
+ except Exception as e:
922
+ log_warning(f"Error connecting tool: {str(e)}")
918
923
 
919
924
  async def _disconnect_mcp_tools(self) -> None:
920
925
  """Disconnect the MCP tools from the agent."""
921
926
  for tool in self._mcp_tools_initialized_on_run:
922
- await tool.close()
927
+ try:
928
+ await tool.close()
929
+ except Exception as e:
930
+ log_warning(f"Error disconnecting tool: {str(e)}")
923
931
  self._mcp_tools_initialized_on_run = []
924
932
 
933
+ def _connect_connectable_tools(self) -> None:
934
+ """Connect tools that require connection management (e.g., database connections)."""
935
+ if self.tools:
936
+ for tool in self.tools:
937
+ if (
938
+ hasattr(tool, "requires_connect")
939
+ and tool.requires_connect
940
+ and hasattr(tool, "connect")
941
+ and tool not in self._connectable_tools_initialized_on_run
942
+ ):
943
+ try:
944
+ tool.connect() # type: ignore
945
+ self._connectable_tools_initialized_on_run.append(tool)
946
+ except Exception as e:
947
+ log_warning(f"Error connecting tool: {str(e)}")
948
+
949
+ def _disconnect_connectable_tools(self) -> None:
950
+ """Disconnect tools that require connection management."""
951
+ for tool in self._connectable_tools_initialized_on_run:
952
+ if hasattr(tool, "close"):
953
+ try:
954
+ tool.close() # type: ignore
955
+ except Exception as e:
956
+ log_warning(f"Error disconnecting tool: {str(e)}")
957
+ self._connectable_tools_initialized_on_run = []
958
+
925
959
  def _initialize_session(
926
960
  self,
927
961
  session_id: Optional[str] = None,
@@ -1175,6 +1209,8 @@ class Agent:
1175
1209
 
1176
1210
  return run_response
1177
1211
  finally:
1212
+ # Always disconnect connectable tools
1213
+ self._disconnect_connectable_tools()
1178
1214
  # Always clean up the run tracking
1179
1215
  cleanup_run(run_response.run_id) # type: ignore
1180
1216
 
@@ -1493,6 +1529,8 @@ class Agent:
1493
1529
  run_response=run_response, session=session, run_context=run_context, user_id=user_id
1494
1530
  )
1495
1531
  finally:
1532
+ # Always disconnect connectable tools
1533
+ self._disconnect_connectable_tools()
1496
1534
  # Always clean up the run tracking
1497
1535
  cleanup_run(run_response.run_id) # type: ignore
1498
1536
 
@@ -1645,7 +1683,10 @@ class Agent:
1645
1683
 
1646
1684
  # Initialize session state
1647
1685
  session_state = self._initialize_session_state(
1648
- session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_id
1686
+ session_state=session_state if session_state is not None else {},
1687
+ user_id=user_id,
1688
+ session_id=session_id,
1689
+ run_id=run_id,
1649
1690
  )
1650
1691
  # Update session state from DB
1651
1692
  session_state = self._load_session_state(session=agent_session, session_state=session_state)
@@ -1866,7 +1907,7 @@ class Agent:
1866
1907
  self._update_metadata(session=agent_session)
1867
1908
  # Initialize session state
1868
1909
  run_context.session_state = self._initialize_session_state(
1869
- session_state=run_context.session_state or {},
1910
+ session_state=run_context.session_state if run_context.session_state is not None else {},
1870
1911
  user_id=user_id,
1871
1912
  session_id=session_id,
1872
1913
  run_id=run_response.run_id,
@@ -2072,6 +2113,8 @@ class Agent:
2072
2113
  return run_response
2073
2114
 
2074
2115
  finally:
2116
+ # Always disconnect connectable tools
2117
+ self._disconnect_connectable_tools()
2075
2118
  # Always disconnect MCP tools
2076
2119
  await self._disconnect_mcp_tools()
2077
2120
 
@@ -2144,7 +2187,7 @@ class Agent:
2144
2187
  self._update_metadata(session=agent_session)
2145
2188
  # Initialize session state
2146
2189
  run_context.session_state = self._initialize_session_state(
2147
- session_state=run_context.session_state or {},
2190
+ session_state=run_context.session_state if run_context.session_state is not None else {},
2148
2191
  user_id=user_id,
2149
2192
  session_id=session_id,
2150
2193
  run_id=run_response.run_id,
@@ -2439,6 +2482,8 @@ class Agent:
2439
2482
  user_id=user_id,
2440
2483
  )
2441
2484
  finally:
2485
+ # Always disconnect connectable tools
2486
+ self._disconnect_connectable_tools()
2442
2487
  # Always disconnect MCP tools
2443
2488
  await self._disconnect_mcp_tools()
2444
2489
 
@@ -2781,6 +2826,7 @@ class Agent:
2781
2826
  *,
2782
2827
  run_id: Optional[str] = None,
2783
2828
  updated_tools: Optional[List[ToolExecution]] = None,
2829
+ requirements: Optional[List[RunRequirement]] = None,
2784
2830
  stream: Literal[False] = False,
2785
2831
  stream_events: Optional[bool] = None,
2786
2832
  stream_intermediate_steps: Optional[bool] = None,
@@ -2791,6 +2837,7 @@ class Agent:
2791
2837
  dependencies: Optional[Dict[str, Any]] = None,
2792
2838
  metadata: Optional[Dict[str, Any]] = None,
2793
2839
  debug_mode: Optional[bool] = None,
2840
+ yield_run_output: bool = False,
2794
2841
  ) -> RunOutput: ...
2795
2842
 
2796
2843
  @overload
@@ -2800,6 +2847,7 @@ class Agent:
2800
2847
  *,
2801
2848
  run_id: Optional[str] = None,
2802
2849
  updated_tools: Optional[List[ToolExecution]] = None,
2850
+ requirements: Optional[List[RunRequirement]] = None,
2803
2851
  stream: Literal[True] = True,
2804
2852
  stream_events: Optional[bool] = False,
2805
2853
  stream_intermediate_steps: Optional[bool] = None,
@@ -2810,6 +2858,7 @@ class Agent:
2810
2858
  dependencies: Optional[Dict[str, Any]] = None,
2811
2859
  metadata: Optional[Dict[str, Any]] = None,
2812
2860
  debug_mode: Optional[bool] = None,
2861
+ yield_run_output: bool = False,
2813
2862
  ) -> Iterator[RunOutputEvent]: ...
2814
2863
 
2815
2864
  def continue_run(
@@ -2818,6 +2867,7 @@ class Agent:
2818
2867
  *,
2819
2868
  run_id: Optional[str] = None, # type: ignore
2820
2869
  updated_tools: Optional[List[ToolExecution]] = None,
2870
+ requirements: Optional[List[RunRequirement]] = None,
2821
2871
  stream: Optional[bool] = None,
2822
2872
  stream_events: Optional[bool] = False,
2823
2873
  stream_intermediate_steps: Optional[bool] = None,
@@ -2829,14 +2879,15 @@ class Agent:
2829
2879
  dependencies: Optional[Dict[str, Any]] = None,
2830
2880
  metadata: Optional[Dict[str, Any]] = None,
2831
2881
  debug_mode: Optional[bool] = None,
2882
+ yield_run_output: bool = False,
2832
2883
  **kwargs,
2833
- ) -> Union[RunOutput, Iterator[RunOutputEvent]]:
2884
+ ) -> Union[RunOutput, Iterator[Union[RunOutputEvent, RunOutput]]]:
2834
2885
  """Continue a previous run.
2835
2886
 
2836
2887
  Args:
2837
2888
  run_response: The run response to continue.
2838
2889
  run_id: The run id to continue. Alternative to passing run_response.
2839
- updated_tools: The updated tools to use for the run. Required to be used with `run_id`.
2890
+ requirements: The requirements to continue the run. This or updated_tools is required with `run_id`.
2840
2891
  stream: Whether to stream the response.
2841
2892
  stream_events: Whether to stream all events.
2842
2893
  user_id: The user id to continue the run for.
@@ -2848,6 +2899,7 @@ class Agent:
2848
2899
  metadata: The metadata to use for the run.
2849
2900
  debug_mode: Whether to enable debug mode.
2850
2901
  (deprecated) stream_intermediate_steps: Whether to stream all steps.
2902
+ (deprecated) updated_tools: Use 'requirements' instead.
2851
2903
  """
2852
2904
  if run_response is None and run_id is None:
2853
2905
  raise ValueError("Either run_response or run_id must be provided.")
@@ -2947,16 +2999,35 @@ class Agent:
2947
2999
  # The run is continued from a provided run_response. This contains the updated tools.
2948
3000
  input = run_response.messages or []
2949
3001
  elif run_id is not None:
2950
- # The run is continued from a run_id. This requires the updated tools to be passed.
2951
- if updated_tools is None:
2952
- raise ValueError("Updated tools are required to continue a run from a run_id.")
3002
+ # The run is continued from a run_id, one of requirements or updated_tool (deprecated) is required.
3003
+ if updated_tools is None and requirements is None:
3004
+ raise ValueError("To continue a run from a given run_id, the requirements parameter must be provided.")
2953
3005
 
2954
3006
  runs = agent_session.runs
2955
3007
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
2956
3008
  if run_response is None:
2957
3009
  raise RuntimeError(f"No runs found for run ID {run_id}")
2958
- run_response.tools = updated_tools
3010
+
2959
3011
  input = run_response.messages or []
3012
+
3013
+ # If we have updated_tools, set them in the run_response
3014
+ if updated_tools is not None:
3015
+ warnings.warn(
3016
+ "The 'updated_tools' parameter is deprecated and will be removed in future versions. Use 'requirements' instead.",
3017
+ DeprecationWarning,
3018
+ stacklevel=2,
3019
+ )
3020
+ run_response.tools = updated_tools
3021
+
3022
+ # If we have requirements, get the updated tools and set them in the run_response
3023
+ elif requirements is not None:
3024
+ run_response.requirements = requirements
3025
+ updated_tools = [req.tool_execution for req in requirements if req.tool_execution is not None]
3026
+ if updated_tools and run_response.tools:
3027
+ updated_tools_map = {tool.tool_call_id: tool for tool in updated_tools}
3028
+ run_response.tools = [updated_tools_map.get(tool.tool_call_id, tool) for tool in run_response.tools]
3029
+ else:
3030
+ run_response.tools = updated_tools
2960
3031
  else:
2961
3032
  raise ValueError("Either run_response or run_id must be provided.")
2962
3033
 
@@ -3006,6 +3077,7 @@ class Agent:
3006
3077
  session=agent_session,
3007
3078
  response_format=response_format,
3008
3079
  stream_events=stream_events,
3080
+ yield_run_output=yield_run_output,
3009
3081
  debug_mode=debug_mode,
3010
3082
  background_tasks=background_tasks,
3011
3083
  **kwargs,
@@ -3179,6 +3251,8 @@ class Agent:
3179
3251
 
3180
3252
  return run_response
3181
3253
  finally:
3254
+ # Always disconnect connectable tools
3255
+ self._disconnect_connectable_tools()
3182
3256
  # Always clean up the run tracking
3183
3257
  cleanup_run(run_response.run_id) # type: ignore
3184
3258
 
@@ -3193,9 +3267,10 @@ class Agent:
3193
3267
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3194
3268
  stream_events: bool = False,
3195
3269
  debug_mode: Optional[bool] = None,
3270
+ yield_run_output: bool = False,
3196
3271
  background_tasks: Optional[Any] = None,
3197
3272
  **kwargs,
3198
- ) -> Iterator[RunOutputEvent]:
3273
+ ) -> Iterator[Union[RunOutputEvent, RunOutput]]:
3199
3274
  """Continue a previous run.
3200
3275
 
3201
3276
  Steps:
@@ -3328,6 +3403,9 @@ class Agent:
3328
3403
  if stream_events:
3329
3404
  yield completed_event # type: ignore
3330
3405
 
3406
+ if yield_run_output:
3407
+ yield run_response
3408
+
3331
3409
  # Log Agent Telemetry
3332
3410
  self._log_agent_telemetry(session_id=session.session_id, run_id=run_response.run_id)
3333
3411
 
@@ -3352,6 +3430,8 @@ class Agent:
3352
3430
  run_response=run_response, session=session, run_context=run_context, user_id=user_id
3353
3431
  )
3354
3432
  finally:
3433
+ # Always disconnect connectable tools
3434
+ self._disconnect_connectable_tools()
3355
3435
  # Always clean up the run tracking
3356
3436
  cleanup_run(run_response.run_id) # type: ignore
3357
3437
 
@@ -3365,6 +3445,7 @@ class Agent:
3365
3445
  stream_intermediate_steps: Optional[bool] = None,
3366
3446
  run_id: Optional[str] = None,
3367
3447
  updated_tools: Optional[List[ToolExecution]] = None,
3448
+ requirements: Optional[List[RunRequirement]] = None,
3368
3449
  user_id: Optional[str] = None,
3369
3450
  session_id: Optional[str] = None,
3370
3451
  retries: Optional[int] = None,
@@ -3385,6 +3466,7 @@ class Agent:
3385
3466
  stream_intermediate_steps: Optional[bool] = None,
3386
3467
  run_id: Optional[str] = None,
3387
3468
  updated_tools: Optional[List[ToolExecution]] = None,
3469
+ requirements: Optional[List[RunRequirement]] = None,
3388
3470
  user_id: Optional[str] = None,
3389
3471
  session_id: Optional[str] = None,
3390
3472
  retries: Optional[int] = None,
@@ -3401,6 +3483,7 @@ class Agent:
3401
3483
  *,
3402
3484
  run_id: Optional[str] = None, # type: ignore
3403
3485
  updated_tools: Optional[List[ToolExecution]] = None,
3486
+ requirements: Optional[List[RunRequirement]] = None,
3404
3487
  stream: Optional[bool] = None,
3405
3488
  stream_events: Optional[bool] = None,
3406
3489
  stream_intermediate_steps: Optional[bool] = None,
@@ -3420,7 +3503,8 @@ class Agent:
3420
3503
  Args:
3421
3504
  run_response: The run response to continue.
3422
3505
  run_id: The run id to continue. Alternative to passing run_response.
3423
- updated_tools: The updated tools to use for the run. Required to be used with `run_id`.
3506
+
3507
+ requirements: The requirements to continue the run. This or updated_tools is required with `run_id`.
3424
3508
  stream: Whether to stream the response.
3425
3509
  stream_events: Whether to stream all events.
3426
3510
  user_id: The user id to continue the run for.
@@ -3433,6 +3517,7 @@ class Agent:
3433
3517
  debug_mode: Whether to enable debug mode.
3434
3518
  yield_run_output: Whether to yield the run response.
3435
3519
  (deprecated) stream_intermediate_steps: Whether to stream all steps.
3520
+ (deprecated) updated_tools: Use 'requirements' instead.
3436
3521
  """
3437
3522
  if run_response is None and run_id is None:
3438
3523
  raise ValueError("Either run_response or run_id must be provided.")
@@ -3440,6 +3525,12 @@ class Agent:
3440
3525
  if run_response is None and (run_id is not None and (session_id is None and self.session_id is None)):
3441
3526
  raise ValueError("Session ID is required to continue a run from a run_id.")
3442
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
+ )
3443
3534
  background_tasks = kwargs.pop("background_tasks", None)
3444
3535
  if background_tasks is not None:
3445
3536
  from fastapi import BackgroundTasks
@@ -3523,6 +3614,7 @@ class Agent:
3523
3614
  run_response=run_response,
3524
3615
  run_context=run_context,
3525
3616
  updated_tools=updated_tools,
3617
+ requirements=requirements,
3526
3618
  run_id=run_id,
3527
3619
  user_id=user_id,
3528
3620
  session_id=session_id,
@@ -3539,6 +3631,7 @@ class Agent:
3539
3631
  run_response=run_response,
3540
3632
  run_context=run_context,
3541
3633
  updated_tools=updated_tools,
3634
+ requirements=requirements,
3542
3635
  run_id=run_id,
3543
3636
  user_id=user_id,
3544
3637
  response_format=response_format,
@@ -3589,6 +3682,7 @@ class Agent:
3589
3682
  run_context: RunContext,
3590
3683
  run_response: Optional[RunOutput] = None,
3591
3684
  updated_tools: Optional[List[ToolExecution]] = None,
3685
+ requirements: Optional[List[RunRequirement]] = None,
3592
3686
  run_id: Optional[str] = None,
3593
3687
  user_id: Optional[str] = None,
3594
3688
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
@@ -3641,15 +3735,29 @@ class Agent:
3641
3735
  input = run_response.messages or []
3642
3736
  elif run_id is not None:
3643
3737
  # The run is continued from a run_id. This requires the updated tools to be passed.
3644
- if updated_tools is None:
3645
- raise ValueError("Updated tools are required to continue a run from a run_id.")
3738
+ if updated_tools is None and requirements is None:
3739
+ raise ValueError("Either updated tools or requirements are required to continue a run from a run_id.")
3646
3740
 
3647
3741
  runs = agent_session.runs
3648
3742
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
3649
3743
  if run_response is None:
3650
3744
  raise RuntimeError(f"No runs found for run ID {run_id}")
3651
- run_response.tools = updated_tools
3745
+
3652
3746
  input = run_response.messages or []
3747
+
3748
+ # If we have updated_tools, set them in the run_response
3749
+ if updated_tools is not None:
3750
+ run_response.tools = updated_tools
3751
+
3752
+ # If we have requirements, get the updated tools and set them in the run_response
3753
+ elif requirements is not None:
3754
+ run_response.requirements = requirements
3755
+ updated_tools = [req.tool_execution for req in requirements if req.tool_execution is not None]
3756
+ if updated_tools and run_response.tools:
3757
+ updated_tools_map = {tool.tool_call_id: tool for tool in updated_tools}
3758
+ run_response.tools = [updated_tools_map.get(tool.tool_call_id, tool) for tool in run_response.tools]
3759
+ else:
3760
+ run_response.tools = updated_tools
3653
3761
  else:
3654
3762
  raise ValueError("Either run_response or run_id must be provided.")
3655
3763
 
@@ -3788,6 +3896,8 @@ class Agent:
3788
3896
 
3789
3897
  return run_response
3790
3898
  finally:
3899
+ # Always disconnect connectable tools
3900
+ self._disconnect_connectable_tools()
3791
3901
  # Always disconnect MCP tools
3792
3902
  await self._disconnect_mcp_tools()
3793
3903
 
@@ -3800,6 +3910,7 @@ class Agent:
3800
3910
  run_context: RunContext,
3801
3911
  run_response: Optional[RunOutput] = None,
3802
3912
  updated_tools: Optional[List[ToolExecution]] = None,
3913
+ requirements: Optional[List[RunRequirement]] = None,
3803
3914
  run_id: Optional[str] = None,
3804
3915
  user_id: Optional[str] = None,
3805
3916
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
@@ -3849,17 +3960,32 @@ class Agent:
3849
3960
  if run_response is not None:
3850
3961
  # The run is continued from a provided run_response. This contains the updated tools.
3851
3962
  input = run_response.messages or []
3963
+
3852
3964
  elif run_id is not None:
3853
- # The run is continued from a run_id. This requires the updated tools to be passed.
3854
- if updated_tools is None:
3855
- raise ValueError("Updated tools are required to continue a run from a run_id.")
3965
+ # The run is continued from a run_id. This requires the updated tools or requirements to be passed.
3966
+ if updated_tools is None and requirements is None:
3967
+ raise ValueError("Either updated tools or requirements are required to continue a run from a run_id.")
3856
3968
 
3857
3969
  runs = agent_session.runs
3858
3970
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
3859
3971
  if run_response is None:
3860
3972
  raise RuntimeError(f"No runs found for run ID {run_id}")
3861
- run_response.tools = updated_tools
3973
+
3862
3974
  input = run_response.messages or []
3975
+
3976
+ # If we have updated_tools, set them in the run_response
3977
+ if updated_tools is not None:
3978
+ run_response.tools = updated_tools
3979
+
3980
+ # If we have requirements, get the updated tools and set them in the run_response
3981
+ elif requirements is not None:
3982
+ run_response.requirements = requirements
3983
+ updated_tools = [req.tool_execution for req in requirements if req.tool_execution is not None]
3984
+ if updated_tools and run_response.tools:
3985
+ updated_tools_map = {tool.tool_call_id: tool for tool in updated_tools}
3986
+ run_response.tools = [updated_tools_map.get(tool.tool_call_id, tool) for tool in run_response.tools]
3987
+ else:
3988
+ run_response.tools = updated_tools
3863
3989
  else:
3864
3990
  raise ValueError("Either run_response or run_id must be provided.")
3865
3991
 
@@ -4078,6 +4204,8 @@ class Agent:
4078
4204
  user_id=user_id,
4079
4205
  )
4080
4206
  finally:
4207
+ # Always disconnect connectable tools
4208
+ self._disconnect_connectable_tools()
4081
4209
  # Always disconnect MCP tools
4082
4210
  await self._disconnect_mcp_tools()
4083
4211
 
@@ -4493,6 +4621,7 @@ class Agent:
4493
4621
  create_run_paused_event(
4494
4622
  from_run_response=run_response,
4495
4623
  tools=run_response.tools,
4624
+ requirements=run_response.requirements,
4496
4625
  ),
4497
4626
  run_response,
4498
4627
  events_to_skip=self.events_to_skip, # type: ignore
@@ -4541,6 +4670,7 @@ class Agent:
4541
4670
  create_run_paused_event(
4542
4671
  from_run_response=run_response,
4543
4672
  tools=run_response.tools,
4673
+ requirements=run_response.requirements,
4544
4674
  ),
4545
4675
  run_response,
4546
4676
  events_to_skip=self.events_to_skip, # type: ignore
@@ -5356,7 +5486,7 @@ class Agent:
5356
5486
  run_response.images = []
5357
5487
  run_response.images.append(image)
5358
5488
 
5359
- # Handle tool interruption events
5489
+ # Handle tool interruption events (HITL flow)
5360
5490
  elif model_response_event.event == ModelResponseEvent.tool_call_paused.value:
5361
5491
  # Add tool calls to the run_response
5362
5492
  tool_executions_list = model_response_event.tool_executions
@@ -5366,6 +5496,10 @@ class Agent:
5366
5496
  run_response.tools = tool_executions_list
5367
5497
  else:
5368
5498
  run_response.tools.extend(tool_executions_list)
5499
+ # Add requirement to the run_response
5500
+ if run_response.requirements is None:
5501
+ run_response.requirements = []
5502
+ run_response.requirements.append(RunRequirement(tool_execution=tool_executions_list[-1]))
5369
5503
 
5370
5504
  # If the model response is a tool_call_started, add the tool call to the run_response
5371
5505
  elif (
@@ -5646,6 +5780,9 @@ class Agent:
5646
5780
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5647
5781
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5648
5782
 
5783
+ # Connect tools that require connection management
5784
+ self._connect_connectable_tools()
5785
+
5649
5786
  # Add provided tools
5650
5787
  if self.tools is not None:
5651
5788
  # If not running in async mode, raise if any tool is async
@@ -5692,6 +5829,7 @@ class Agent:
5692
5829
  run_response=run_response,
5693
5830
  async_mode=False,
5694
5831
  knowledge_filters=run_context.knowledge_filters,
5832
+ run_context=run_context,
5695
5833
  )
5696
5834
  )
5697
5835
  else:
@@ -5700,6 +5838,7 @@ class Agent:
5700
5838
  run_response=run_response,
5701
5839
  async_mode=False,
5702
5840
  knowledge_filters=run_context.knowledge_filters,
5841
+ run_context=run_context,
5703
5842
  )
5704
5843
  )
5705
5844
 
@@ -5718,6 +5857,9 @@ class Agent:
5718
5857
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5719
5858
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5720
5859
 
5860
+ # Connect tools that require connection management
5861
+ self._connect_connectable_tools()
5862
+
5721
5863
  # Connect MCP tools
5722
5864
  await self._connect_mcp_tools()
5723
5865
 
@@ -5780,6 +5922,7 @@ class Agent:
5780
5922
  run_response=run_response,
5781
5923
  async_mode=True,
5782
5924
  knowledge_filters=run_context.knowledge_filters,
5925
+ run_context=run_context,
5783
5926
  )
5784
5927
  )
5785
5928
  else:
@@ -5788,6 +5931,7 @@ class Agent:
5788
5931
  run_response=run_response,
5789
5932
  async_mode=True,
5790
5933
  knowledge_filters=run_context.knowledge_filters,
5934
+ run_context=run_context,
5791
5935
  )
5792
5936
  )
5793
5937
 
@@ -6988,7 +7132,7 @@ class Agent:
6988
7132
 
6989
7133
  # Should already be resolved and passed from run() method
6990
7134
  format_variables = ChainMap(
6991
- session_state or {},
7135
+ session_state if session_state is not None else {},
6992
7136
  dependencies or {},
6993
7137
  metadata or {},
6994
7138
  {"user_id": user_id} if user_id is not None else {},
@@ -7817,7 +7961,7 @@ class Agent:
7817
7961
  retrieval_timer = Timer()
7818
7962
  retrieval_timer.start()
7819
7963
  docs_from_knowledge = self.get_relevant_docs_from_knowledge(
7820
- query=user_msg_content, filters=knowledge_filters, **kwargs
7964
+ query=user_msg_content, filters=knowledge_filters, run_context=run_context, **kwargs
7821
7965
  )
7822
7966
  if docs_from_knowledge is not None:
7823
7967
  references = MessageReferences(
@@ -7991,7 +8135,7 @@ class Agent:
7991
8135
  retrieval_timer = Timer()
7992
8136
  retrieval_timer.start()
7993
8137
  docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
7994
- query=user_msg_content, filters=knowledge_filters, **kwargs
8138
+ query=user_msg_content, filters=knowledge_filters, run_context=run_context, **kwargs
7995
8139
  )
7996
8140
  if docs_from_knowledge is not None:
7997
8141
  references = MessageReferences(
@@ -8572,6 +8716,7 @@ class Agent:
8572
8716
  num_documents: Optional[int] = None,
8573
8717
  filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
8574
8718
  validate_filters: bool = False,
8719
+ run_context: Optional[RunContext] = None,
8575
8720
  **kwargs,
8576
8721
  ) -> Optional[List[Union[Dict[str, Any], str]]]:
8577
8722
  """Get relevant docs from the knowledge base to answer a query.
@@ -8581,6 +8726,7 @@ class Agent:
8581
8726
  num_documents (Optional[int]): Number of documents to return.
8582
8727
  filters (Optional[Dict[str, Any]]): Filters to apply to the search.
8583
8728
  validate_filters (bool): Whether to validate the filters against known valid filter keys.
8729
+ run_context (Optional[RunContext]): Runtime context containing dependencies and other context.
8584
8730
  **kwargs: Additional keyword arguments.
8585
8731
 
8586
8732
  Returns:
@@ -8588,6 +8734,9 @@ class Agent:
8588
8734
  """
8589
8735
  from agno.knowledge.document import Document
8590
8736
 
8737
+ # Extract dependencies from run_context if available
8738
+ dependencies = run_context.dependencies if run_context else None
8739
+
8591
8740
  if num_documents is None and self.knowledge is not None:
8592
8741
  num_documents = self.knowledge.max_results
8593
8742
  # Validate the filters against known valid filter keys
@@ -8619,6 +8768,11 @@ class Agent:
8619
8768
  knowledge_retriever_kwargs = {"agent": self}
8620
8769
  if "filters" in sig.parameters:
8621
8770
  knowledge_retriever_kwargs["filters"] = filters
8771
+ if "run_context" in sig.parameters:
8772
+ knowledge_retriever_kwargs["run_context"] = run_context
8773
+ elif "dependencies" in sig.parameters:
8774
+ # Backward compatibility: support dependencies parameter
8775
+ knowledge_retriever_kwargs["dependencies"] = dependencies
8622
8776
  knowledge_retriever_kwargs.update({"query": query, "num_documents": num_documents, **kwargs})
8623
8777
  return self.knowledge_retriever(**knowledge_retriever_kwargs)
8624
8778
  except Exception as e:
@@ -8657,11 +8811,15 @@ class Agent:
8657
8811
  num_documents: Optional[int] = None,
8658
8812
  filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
8659
8813
  validate_filters: bool = False,
8814
+ run_context: Optional[RunContext] = None,
8660
8815
  **kwargs,
8661
8816
  ) -> Optional[List[Union[Dict[str, Any], str]]]:
8662
8817
  """Get relevant documents from knowledge base asynchronously."""
8663
8818
  from agno.knowledge.document import Document
8664
8819
 
8820
+ # Extract dependencies from run_context if available
8821
+ dependencies = run_context.dependencies if run_context else None
8822
+
8665
8823
  if num_documents is None and self.knowledge is not None:
8666
8824
  num_documents = self.knowledge.max_results
8667
8825
 
@@ -8693,6 +8851,11 @@ class Agent:
8693
8851
  knowledge_retriever_kwargs = {"agent": self}
8694
8852
  if "filters" in sig.parameters:
8695
8853
  knowledge_retriever_kwargs["filters"] = filters
8854
+ if "run_context" in sig.parameters:
8855
+ knowledge_retriever_kwargs["run_context"] = run_context
8856
+ elif "dependencies" in sig.parameters:
8857
+ # Backward compatibility: support dependencies parameter
8858
+ knowledge_retriever_kwargs["dependencies"] = dependencies
8696
8859
  knowledge_retriever_kwargs.update({"query": query, "num_documents": num_documents, **kwargs})
8697
8860
  result = self.knowledge_retriever(**knowledge_retriever_kwargs)
8698
8861
 
@@ -10035,6 +10198,7 @@ class Agent:
10035
10198
  run_response: RunOutput,
10036
10199
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
10037
10200
  async_mode: bool = False,
10201
+ run_context: Optional[RunContext] = None,
10038
10202
  ) -> Function:
10039
10203
  """Factory function to create a search_knowledge_base function with filters."""
10040
10204
 
@@ -10051,7 +10215,9 @@ class Agent:
10051
10215
  # Get the relevant documents from the knowledge base, passing filters
10052
10216
  retrieval_timer = Timer()
10053
10217
  retrieval_timer.start()
10054
- docs_from_knowledge = self.get_relevant_docs_from_knowledge(query=query, filters=knowledge_filters)
10218
+ docs_from_knowledge = self.get_relevant_docs_from_knowledge(
10219
+ query=query, filters=knowledge_filters, run_context=run_context
10220
+ )
10055
10221
  if docs_from_knowledge is not None:
10056
10222
  references = MessageReferences(
10057
10223
  query=query,
@@ -10082,7 +10248,10 @@ class Agent:
10082
10248
  """
10083
10249
  retrieval_timer = Timer()
10084
10250
  retrieval_timer.start()
10085
- docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(query=query, filters=knowledge_filters)
10251
+ dependencies = run_context.dependencies if run_context else None
10252
+ docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
10253
+ query=query, filters=knowledge_filters, dependencies=dependencies
10254
+ )
10086
10255
  if docs_from_knowledge is not None:
10087
10256
  references = MessageReferences(
10088
10257
  query=query,
@@ -10111,6 +10280,7 @@ class Agent:
10111
10280
  run_response: RunOutput,
10112
10281
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
10113
10282
  async_mode: bool = False,
10283
+ run_context: Optional[RunContext] = None,
10114
10284
  ) -> Function:
10115
10285
  """Factory function to create a search_knowledge_base function with filters."""
10116
10286
 
@@ -10131,7 +10301,7 @@ class Agent:
10131
10301
  retrieval_timer = Timer()
10132
10302
  retrieval_timer.start()
10133
10303
  docs_from_knowledge = self.get_relevant_docs_from_knowledge(
10134
- query=query, filters=search_filters, validate_filters=True
10304
+ query=query, filters=search_filters, validate_filters=True, run_context=run_context
10135
10305
  )
10136
10306
  if docs_from_knowledge is not None:
10137
10307
  references = MessageReferences(
@@ -10168,7 +10338,7 @@ class Agent:
10168
10338
  retrieval_timer = Timer()
10169
10339
  retrieval_timer.start()
10170
10340
  docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
10171
- query=query, filters=search_filters, validate_filters=True
10341
+ query=query, filters=search_filters, validate_filters=True, run_context=run_context
10172
10342
  )
10173
10343
  if docs_from_knowledge is not None:
10174
10344
  references = MessageReferences(