agno 2.3.6__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
 
@@ -2075,6 +2113,8 @@ class Agent:
2075
2113
  return run_response
2076
2114
 
2077
2115
  finally:
2116
+ # Always disconnect connectable tools
2117
+ self._disconnect_connectable_tools()
2078
2118
  # Always disconnect MCP tools
2079
2119
  await self._disconnect_mcp_tools()
2080
2120
 
@@ -2442,6 +2482,8 @@ class Agent:
2442
2482
  user_id=user_id,
2443
2483
  )
2444
2484
  finally:
2485
+ # Always disconnect connectable tools
2486
+ self._disconnect_connectable_tools()
2445
2487
  # Always disconnect MCP tools
2446
2488
  await self._disconnect_mcp_tools()
2447
2489
 
@@ -2784,6 +2826,7 @@ class Agent:
2784
2826
  *,
2785
2827
  run_id: Optional[str] = None,
2786
2828
  updated_tools: Optional[List[ToolExecution]] = None,
2829
+ requirements: Optional[List[RunRequirement]] = None,
2787
2830
  stream: Literal[False] = False,
2788
2831
  stream_events: Optional[bool] = None,
2789
2832
  stream_intermediate_steps: Optional[bool] = None,
@@ -2794,6 +2837,7 @@ class Agent:
2794
2837
  dependencies: Optional[Dict[str, Any]] = None,
2795
2838
  metadata: Optional[Dict[str, Any]] = None,
2796
2839
  debug_mode: Optional[bool] = None,
2840
+ yield_run_output: bool = False,
2797
2841
  ) -> RunOutput: ...
2798
2842
 
2799
2843
  @overload
@@ -2803,6 +2847,7 @@ class Agent:
2803
2847
  *,
2804
2848
  run_id: Optional[str] = None,
2805
2849
  updated_tools: Optional[List[ToolExecution]] = None,
2850
+ requirements: Optional[List[RunRequirement]] = None,
2806
2851
  stream: Literal[True] = True,
2807
2852
  stream_events: Optional[bool] = False,
2808
2853
  stream_intermediate_steps: Optional[bool] = None,
@@ -2813,6 +2858,7 @@ class Agent:
2813
2858
  dependencies: Optional[Dict[str, Any]] = None,
2814
2859
  metadata: Optional[Dict[str, Any]] = None,
2815
2860
  debug_mode: Optional[bool] = None,
2861
+ yield_run_output: bool = False,
2816
2862
  ) -> Iterator[RunOutputEvent]: ...
2817
2863
 
2818
2864
  def continue_run(
@@ -2821,6 +2867,7 @@ class Agent:
2821
2867
  *,
2822
2868
  run_id: Optional[str] = None, # type: ignore
2823
2869
  updated_tools: Optional[List[ToolExecution]] = None,
2870
+ requirements: Optional[List[RunRequirement]] = None,
2824
2871
  stream: Optional[bool] = None,
2825
2872
  stream_events: Optional[bool] = False,
2826
2873
  stream_intermediate_steps: Optional[bool] = None,
@@ -2832,14 +2879,15 @@ class Agent:
2832
2879
  dependencies: Optional[Dict[str, Any]] = None,
2833
2880
  metadata: Optional[Dict[str, Any]] = None,
2834
2881
  debug_mode: Optional[bool] = None,
2882
+ yield_run_output: bool = False,
2835
2883
  **kwargs,
2836
- ) -> Union[RunOutput, Iterator[RunOutputEvent]]:
2884
+ ) -> Union[RunOutput, Iterator[Union[RunOutputEvent, RunOutput]]]:
2837
2885
  """Continue a previous run.
2838
2886
 
2839
2887
  Args:
2840
2888
  run_response: The run response to continue.
2841
2889
  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`.
2890
+ requirements: The requirements to continue the run. This or updated_tools is required with `run_id`.
2843
2891
  stream: Whether to stream the response.
2844
2892
  stream_events: Whether to stream all events.
2845
2893
  user_id: The user id to continue the run for.
@@ -2851,6 +2899,7 @@ class Agent:
2851
2899
  metadata: The metadata to use for the run.
2852
2900
  debug_mode: Whether to enable debug mode.
2853
2901
  (deprecated) stream_intermediate_steps: Whether to stream all steps.
2902
+ (deprecated) updated_tools: Use 'requirements' instead.
2854
2903
  """
2855
2904
  if run_response is None and run_id is None:
2856
2905
  raise ValueError("Either run_response or run_id must be provided.")
@@ -2950,16 +2999,35 @@ class Agent:
2950
2999
  # The run is continued from a provided run_response. This contains the updated tools.
2951
3000
  input = run_response.messages or []
2952
3001
  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.")
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.")
2956
3005
 
2957
3006
  runs = agent_session.runs
2958
3007
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
2959
3008
  if run_response is None:
2960
3009
  raise RuntimeError(f"No runs found for run ID {run_id}")
2961
- run_response.tools = updated_tools
3010
+
2962
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
2963
3031
  else:
2964
3032
  raise ValueError("Either run_response or run_id must be provided.")
2965
3033
 
@@ -3009,6 +3077,7 @@ class Agent:
3009
3077
  session=agent_session,
3010
3078
  response_format=response_format,
3011
3079
  stream_events=stream_events,
3080
+ yield_run_output=yield_run_output,
3012
3081
  debug_mode=debug_mode,
3013
3082
  background_tasks=background_tasks,
3014
3083
  **kwargs,
@@ -3182,6 +3251,8 @@ class Agent:
3182
3251
 
3183
3252
  return run_response
3184
3253
  finally:
3254
+ # Always disconnect connectable tools
3255
+ self._disconnect_connectable_tools()
3185
3256
  # Always clean up the run tracking
3186
3257
  cleanup_run(run_response.run_id) # type: ignore
3187
3258
 
@@ -3196,9 +3267,10 @@ class Agent:
3196
3267
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3197
3268
  stream_events: bool = False,
3198
3269
  debug_mode: Optional[bool] = None,
3270
+ yield_run_output: bool = False,
3199
3271
  background_tasks: Optional[Any] = None,
3200
3272
  **kwargs,
3201
- ) -> Iterator[RunOutputEvent]:
3273
+ ) -> Iterator[Union[RunOutputEvent, RunOutput]]:
3202
3274
  """Continue a previous run.
3203
3275
 
3204
3276
  Steps:
@@ -3331,6 +3403,9 @@ class Agent:
3331
3403
  if stream_events:
3332
3404
  yield completed_event # type: ignore
3333
3405
 
3406
+ if yield_run_output:
3407
+ yield run_response
3408
+
3334
3409
  # Log Agent Telemetry
3335
3410
  self._log_agent_telemetry(session_id=session.session_id, run_id=run_response.run_id)
3336
3411
 
@@ -3355,6 +3430,8 @@ class Agent:
3355
3430
  run_response=run_response, session=session, run_context=run_context, user_id=user_id
3356
3431
  )
3357
3432
  finally:
3433
+ # Always disconnect connectable tools
3434
+ self._disconnect_connectable_tools()
3358
3435
  # Always clean up the run tracking
3359
3436
  cleanup_run(run_response.run_id) # type: ignore
3360
3437
 
@@ -3368,6 +3445,7 @@ class Agent:
3368
3445
  stream_intermediate_steps: Optional[bool] = None,
3369
3446
  run_id: Optional[str] = None,
3370
3447
  updated_tools: Optional[List[ToolExecution]] = None,
3448
+ requirements: Optional[List[RunRequirement]] = None,
3371
3449
  user_id: Optional[str] = None,
3372
3450
  session_id: Optional[str] = None,
3373
3451
  retries: Optional[int] = None,
@@ -3388,6 +3466,7 @@ class Agent:
3388
3466
  stream_intermediate_steps: Optional[bool] = None,
3389
3467
  run_id: Optional[str] = None,
3390
3468
  updated_tools: Optional[List[ToolExecution]] = None,
3469
+ requirements: Optional[List[RunRequirement]] = None,
3391
3470
  user_id: Optional[str] = None,
3392
3471
  session_id: Optional[str] = None,
3393
3472
  retries: Optional[int] = None,
@@ -3404,6 +3483,7 @@ class Agent:
3404
3483
  *,
3405
3484
  run_id: Optional[str] = None, # type: ignore
3406
3485
  updated_tools: Optional[List[ToolExecution]] = None,
3486
+ requirements: Optional[List[RunRequirement]] = None,
3407
3487
  stream: Optional[bool] = None,
3408
3488
  stream_events: Optional[bool] = None,
3409
3489
  stream_intermediate_steps: Optional[bool] = None,
@@ -3423,7 +3503,8 @@ class Agent:
3423
3503
  Args:
3424
3504
  run_response: The run response to continue.
3425
3505
  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`.
3506
+
3507
+ requirements: The requirements to continue the run. This or updated_tools is required with `run_id`.
3427
3508
  stream: Whether to stream the response.
3428
3509
  stream_events: Whether to stream all events.
3429
3510
  user_id: The user id to continue the run for.
@@ -3436,6 +3517,7 @@ class Agent:
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
@@ -3526,6 +3614,7 @@ class Agent:
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,
@@ -3592,6 +3682,7 @@ class Agent:
3592
3682
  run_context: RunContext,
3593
3683
  run_response: Optional[RunOutput] = None,
3594
3684
  updated_tools: Optional[List[ToolExecution]] = None,
3685
+ requirements: Optional[List[RunRequirement]] = None,
3595
3686
  run_id: Optional[str] = None,
3596
3687
  user_id: Optional[str] = None,
3597
3688
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
@@ -3644,15 +3735,29 @@ class Agent:
3644
3735
  input = run_response.messages or []
3645
3736
  elif run_id is not None:
3646
3737
  # 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.")
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.")
3649
3740
 
3650
3741
  runs = agent_session.runs
3651
3742
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
3652
3743
  if run_response is None:
3653
3744
  raise RuntimeError(f"No runs found for run ID {run_id}")
3654
- run_response.tools = updated_tools
3745
+
3655
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
3656
3761
  else:
3657
3762
  raise ValueError("Either run_response or run_id must be provided.")
3658
3763
 
@@ -3791,6 +3896,8 @@ class Agent:
3791
3896
 
3792
3897
  return run_response
3793
3898
  finally:
3899
+ # Always disconnect connectable tools
3900
+ self._disconnect_connectable_tools()
3794
3901
  # Always disconnect MCP tools
3795
3902
  await self._disconnect_mcp_tools()
3796
3903
 
@@ -3803,6 +3910,7 @@ class Agent:
3803
3910
  run_context: RunContext,
3804
3911
  run_response: Optional[RunOutput] = None,
3805
3912
  updated_tools: Optional[List[ToolExecution]] = None,
3913
+ requirements: Optional[List[RunRequirement]] = None,
3806
3914
  run_id: Optional[str] = None,
3807
3915
  user_id: Optional[str] = None,
3808
3916
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
@@ -3852,17 +3960,32 @@ class Agent:
3852
3960
  if run_response is not None:
3853
3961
  # The run is continued from a provided run_response. This contains the updated tools.
3854
3962
  input = run_response.messages or []
3963
+
3855
3964
  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.")
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.")
3859
3968
 
3860
3969
  runs = agent_session.runs
3861
3970
  run_response = next((r for r in runs if r.run_id == run_id), None) # type: ignore
3862
3971
  if run_response is None:
3863
3972
  raise RuntimeError(f"No runs found for run ID {run_id}")
3864
- run_response.tools = updated_tools
3973
+
3865
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
3866
3989
  else:
3867
3990
  raise ValueError("Either run_response or run_id must be provided.")
3868
3991
 
@@ -4081,6 +4204,8 @@ class Agent:
4081
4204
  user_id=user_id,
4082
4205
  )
4083
4206
  finally:
4207
+ # Always disconnect connectable tools
4208
+ self._disconnect_connectable_tools()
4084
4209
  # Always disconnect MCP tools
4085
4210
  await self._disconnect_mcp_tools()
4086
4211
 
@@ -4496,6 +4621,7 @@ class Agent:
4496
4621
  create_run_paused_event(
4497
4622
  from_run_response=run_response,
4498
4623
  tools=run_response.tools,
4624
+ requirements=run_response.requirements,
4499
4625
  ),
4500
4626
  run_response,
4501
4627
  events_to_skip=self.events_to_skip, # type: ignore
@@ -4544,6 +4670,7 @@ class Agent:
4544
4670
  create_run_paused_event(
4545
4671
  from_run_response=run_response,
4546
4672
  tools=run_response.tools,
4673
+ requirements=run_response.requirements,
4547
4674
  ),
4548
4675
  run_response,
4549
4676
  events_to_skip=self.events_to_skip, # type: ignore
@@ -5359,7 +5486,7 @@ class Agent:
5359
5486
  run_response.images = []
5360
5487
  run_response.images.append(image)
5361
5488
 
5362
- # Handle tool interruption events
5489
+ # Handle tool interruption events (HITL flow)
5363
5490
  elif model_response_event.event == ModelResponseEvent.tool_call_paused.value:
5364
5491
  # Add tool calls to the run_response
5365
5492
  tool_executions_list = model_response_event.tool_executions
@@ -5369,6 +5496,10 @@ class Agent:
5369
5496
  run_response.tools = tool_executions_list
5370
5497
  else:
5371
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]))
5372
5503
 
5373
5504
  # If the model response is a tool_call_started, add the tool call to the run_response
5374
5505
  elif (
@@ -5649,6 +5780,9 @@ class Agent:
5649
5780
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5650
5781
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5651
5782
 
5783
+ # Connect tools that require connection management
5784
+ self._connect_connectable_tools()
5785
+
5652
5786
  # Add provided tools
5653
5787
  if self.tools is not None:
5654
5788
  # If not running in async mode, raise if any tool is async
@@ -5695,6 +5829,7 @@ class Agent:
5695
5829
  run_response=run_response,
5696
5830
  async_mode=False,
5697
5831
  knowledge_filters=run_context.knowledge_filters,
5832
+ run_context=run_context,
5698
5833
  )
5699
5834
  )
5700
5835
  else:
@@ -5703,6 +5838,7 @@ class Agent:
5703
5838
  run_response=run_response,
5704
5839
  async_mode=False,
5705
5840
  knowledge_filters=run_context.knowledge_filters,
5841
+ run_context=run_context,
5706
5842
  )
5707
5843
  )
5708
5844
 
@@ -5721,6 +5857,9 @@ class Agent:
5721
5857
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5722
5858
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5723
5859
 
5860
+ # Connect tools that require connection management
5861
+ self._connect_connectable_tools()
5862
+
5724
5863
  # Connect MCP tools
5725
5864
  await self._connect_mcp_tools()
5726
5865
 
@@ -5783,6 +5922,7 @@ class Agent:
5783
5922
  run_response=run_response,
5784
5923
  async_mode=True,
5785
5924
  knowledge_filters=run_context.knowledge_filters,
5925
+ run_context=run_context,
5786
5926
  )
5787
5927
  )
5788
5928
  else:
@@ -5791,6 +5931,7 @@ class Agent:
5791
5931
  run_response=run_response,
5792
5932
  async_mode=True,
5793
5933
  knowledge_filters=run_context.knowledge_filters,
5934
+ run_context=run_context,
5794
5935
  )
5795
5936
  )
5796
5937
 
@@ -7820,7 +7961,7 @@ class Agent:
7820
7961
  retrieval_timer = Timer()
7821
7962
  retrieval_timer.start()
7822
7963
  docs_from_knowledge = self.get_relevant_docs_from_knowledge(
7823
- query=user_msg_content, filters=knowledge_filters, **kwargs
7964
+ query=user_msg_content, filters=knowledge_filters, run_context=run_context, **kwargs
7824
7965
  )
7825
7966
  if docs_from_knowledge is not None:
7826
7967
  references = MessageReferences(
@@ -7994,7 +8135,7 @@ class Agent:
7994
8135
  retrieval_timer = Timer()
7995
8136
  retrieval_timer.start()
7996
8137
  docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
7997
- query=user_msg_content, filters=knowledge_filters, **kwargs
8138
+ query=user_msg_content, filters=knowledge_filters, run_context=run_context, **kwargs
7998
8139
  )
7999
8140
  if docs_from_knowledge is not None:
8000
8141
  references = MessageReferences(
@@ -8575,6 +8716,7 @@ class Agent:
8575
8716
  num_documents: Optional[int] = None,
8576
8717
  filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
8577
8718
  validate_filters: bool = False,
8719
+ run_context: Optional[RunContext] = None,
8578
8720
  **kwargs,
8579
8721
  ) -> Optional[List[Union[Dict[str, Any], str]]]:
8580
8722
  """Get relevant docs from the knowledge base to answer a query.
@@ -8584,6 +8726,7 @@ class Agent:
8584
8726
  num_documents (Optional[int]): Number of documents to return.
8585
8727
  filters (Optional[Dict[str, Any]]): Filters to apply to the search.
8586
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.
8587
8730
  **kwargs: Additional keyword arguments.
8588
8731
 
8589
8732
  Returns:
@@ -8591,6 +8734,9 @@ class Agent:
8591
8734
  """
8592
8735
  from agno.knowledge.document import Document
8593
8736
 
8737
+ # Extract dependencies from run_context if available
8738
+ dependencies = run_context.dependencies if run_context else None
8739
+
8594
8740
  if num_documents is None and self.knowledge is not None:
8595
8741
  num_documents = self.knowledge.max_results
8596
8742
  # Validate the filters against known valid filter keys
@@ -8622,6 +8768,11 @@ class Agent:
8622
8768
  knowledge_retriever_kwargs = {"agent": self}
8623
8769
  if "filters" in sig.parameters:
8624
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
8625
8776
  knowledge_retriever_kwargs.update({"query": query, "num_documents": num_documents, **kwargs})
8626
8777
  return self.knowledge_retriever(**knowledge_retriever_kwargs)
8627
8778
  except Exception as e:
@@ -8660,11 +8811,15 @@ class Agent:
8660
8811
  num_documents: Optional[int] = None,
8661
8812
  filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
8662
8813
  validate_filters: bool = False,
8814
+ run_context: Optional[RunContext] = None,
8663
8815
  **kwargs,
8664
8816
  ) -> Optional[List[Union[Dict[str, Any], str]]]:
8665
8817
  """Get relevant documents from knowledge base asynchronously."""
8666
8818
  from agno.knowledge.document import Document
8667
8819
 
8820
+ # Extract dependencies from run_context if available
8821
+ dependencies = run_context.dependencies if run_context else None
8822
+
8668
8823
  if num_documents is None and self.knowledge is not None:
8669
8824
  num_documents = self.knowledge.max_results
8670
8825
 
@@ -8696,6 +8851,11 @@ class Agent:
8696
8851
  knowledge_retriever_kwargs = {"agent": self}
8697
8852
  if "filters" in sig.parameters:
8698
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
8699
8859
  knowledge_retriever_kwargs.update({"query": query, "num_documents": num_documents, **kwargs})
8700
8860
  result = self.knowledge_retriever(**knowledge_retriever_kwargs)
8701
8861
 
@@ -10038,6 +10198,7 @@ class Agent:
10038
10198
  run_response: RunOutput,
10039
10199
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
10040
10200
  async_mode: bool = False,
10201
+ run_context: Optional[RunContext] = None,
10041
10202
  ) -> Function:
10042
10203
  """Factory function to create a search_knowledge_base function with filters."""
10043
10204
 
@@ -10054,7 +10215,9 @@ class Agent:
10054
10215
  # Get the relevant documents from the knowledge base, passing filters
10055
10216
  retrieval_timer = Timer()
10056
10217
  retrieval_timer.start()
10057
- 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
+ )
10058
10221
  if docs_from_knowledge is not None:
10059
10222
  references = MessageReferences(
10060
10223
  query=query,
@@ -10085,7 +10248,10 @@ class Agent:
10085
10248
  """
10086
10249
  retrieval_timer = Timer()
10087
10250
  retrieval_timer.start()
10088
- 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
+ )
10089
10255
  if docs_from_knowledge is not None:
10090
10256
  references = MessageReferences(
10091
10257
  query=query,
@@ -10114,6 +10280,7 @@ class Agent:
10114
10280
  run_response: RunOutput,
10115
10281
  knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
10116
10282
  async_mode: bool = False,
10283
+ run_context: Optional[RunContext] = None,
10117
10284
  ) -> Function:
10118
10285
  """Factory function to create a search_knowledge_base function with filters."""
10119
10286
 
@@ -10134,7 +10301,7 @@ class Agent:
10134
10301
  retrieval_timer = Timer()
10135
10302
  retrieval_timer.start()
10136
10303
  docs_from_knowledge = self.get_relevant_docs_from_knowledge(
10137
- query=query, filters=search_filters, validate_filters=True
10304
+ query=query, filters=search_filters, validate_filters=True, run_context=run_context
10138
10305
  )
10139
10306
  if docs_from_knowledge is not None:
10140
10307
  references = MessageReferences(
@@ -10171,7 +10338,7 @@ class Agent:
10171
10338
  retrieval_timer = Timer()
10172
10339
  retrieval_timer.start()
10173
10340
  docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
10174
- query=query, filters=search_filters, validate_filters=True
10341
+ query=query, filters=search_filters, validate_filters=True, run_context=run_context
10175
10342
  )
10176
10343
  if docs_from_knowledge is not None:
10177
10344
  references = MessageReferences(