agno 2.1.10__py3-none-any.whl → 2.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. agno/agent/agent.py +1594 -1248
  2. agno/knowledge/knowledge.py +11 -0
  3. agno/knowledge/reader/pptx_reader.py +101 -0
  4. agno/knowledge/reader/reader_factory.py +14 -0
  5. agno/knowledge/types.py +1 -0
  6. agno/models/anthropic/claude.py +2 -2
  7. agno/models/base.py +4 -4
  8. agno/models/ollama/chat.py +7 -2
  9. agno/os/app.py +1 -1
  10. agno/os/interfaces/a2a/router.py +2 -2
  11. agno/os/interfaces/agui/router.py +2 -2
  12. agno/os/router.py +7 -7
  13. agno/os/routers/evals/schemas.py +31 -31
  14. agno/os/routers/health.py +6 -2
  15. agno/os/routers/knowledge/schemas.py +49 -47
  16. agno/os/routers/memory/schemas.py +16 -16
  17. agno/os/routers/metrics/schemas.py +16 -16
  18. agno/os/routers/session/session.py +382 -7
  19. agno/os/schema.py +254 -231
  20. agno/os/utils.py +1 -1
  21. agno/run/agent.py +54 -1
  22. agno/run/team.py +48 -0
  23. agno/run/workflow.py +15 -5
  24. agno/session/summary.py +45 -13
  25. agno/session/team.py +90 -5
  26. agno/team/team.py +1130 -849
  27. agno/utils/agent.py +372 -0
  28. agno/utils/events.py +144 -2
  29. agno/utils/message.py +60 -0
  30. agno/utils/print_response/agent.py +10 -6
  31. agno/utils/print_response/team.py +6 -4
  32. agno/utils/print_response/workflow.py +7 -5
  33. agno/utils/team.py +9 -8
  34. agno/workflow/condition.py +17 -9
  35. agno/workflow/loop.py +18 -10
  36. agno/workflow/parallel.py +14 -6
  37. agno/workflow/router.py +16 -8
  38. agno/workflow/step.py +14 -6
  39. agno/workflow/steps.py +14 -6
  40. agno/workflow/workflow.py +331 -123
  41. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/METADATA +63 -23
  42. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/RECORD +45 -43
  43. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/WHEEL +0 -0
  44. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/licenses/LICENSE +0 -0
  45. {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/top_level.txt +0 -0
agno/team/team.py CHANGED
@@ -10,7 +10,6 @@ from typing import (
10
10
  Any,
11
11
  AsyncIterator,
12
12
  Callable,
13
- Coroutine,
14
13
  Dict,
15
14
  Iterator,
16
15
  List,
@@ -59,15 +58,28 @@ from agno.run.cancel import (
59
58
  )
60
59
  from agno.run.messages import RunMessages
61
60
  from agno.run.team import TeamRunEvent, TeamRunInput, TeamRunOutput, TeamRunOutputEvent
62
- from agno.session import SessionSummaryManager, TeamSession
61
+ from agno.session import SessionSummaryManager, TeamSession, WorkflowSession
63
62
  from agno.tools import Toolkit
64
63
  from agno.tools.function import Function
64
+ from agno.utils.agent import (
65
+ await_for_background_tasks,
66
+ await_for_background_tasks_stream,
67
+ collect_joint_audios,
68
+ collect_joint_files,
69
+ collect_joint_images,
70
+ collect_joint_videos,
71
+ scrub_history_messages_from_run_output,
72
+ scrub_media_from_run_output,
73
+ scrub_tool_results_from_run_output,
74
+ wait_for_background_tasks,
75
+ wait_for_background_tasks_stream,
76
+ )
65
77
  from agno.utils.common import is_typed_dict, validate_typed_dict
66
78
  from agno.utils.events import (
67
- create_team_memory_update_completed_event,
68
- create_team_memory_update_started_event,
69
79
  create_team_parser_model_response_completed_event,
70
80
  create_team_parser_model_response_started_event,
81
+ create_team_post_hook_completed_event,
82
+ create_team_post_hook_started_event,
71
83
  create_team_pre_hook_completed_event,
72
84
  create_team_pre_hook_started_event,
73
85
  create_team_reasoning_completed_event,
@@ -75,11 +87,15 @@ from agno.utils.events import (
75
87
  create_team_reasoning_step_event,
76
88
  create_team_run_cancelled_event,
77
89
  create_team_run_completed_event,
90
+ create_team_run_content_completed_event,
78
91
  create_team_run_error_event,
79
92
  create_team_run_output_content_event,
80
93
  create_team_run_started_event,
94
+ create_team_session_summary_completed_event,
95
+ create_team_session_summary_started_event,
81
96
  create_team_tool_call_completed_event,
82
97
  create_team_tool_call_started_event,
98
+ handle_event,
83
99
  )
84
100
  from agno.utils.hooks import filter_hook_args, normalize_hooks
85
101
  from agno.utils.knowledge import get_agentic_or_user_search_filters
@@ -95,7 +111,7 @@ from agno.utils.log import (
95
111
  use_team_logger,
96
112
  )
97
113
  from agno.utils.merge_dict import merge_dictionaries
98
- from agno.utils.message import get_text_from_message
114
+ from agno.utils.message import filter_tool_calls, get_text_from_message
99
115
  from agno.utils.print_response.team import (
100
116
  aprint_response,
101
117
  aprint_response_stream,
@@ -131,15 +147,22 @@ class Team:
131
147
  model: Optional[Model] = None
132
148
 
133
149
  # --- Team settings ---
134
- # Name of the team
135
- name: Optional[str] = None
136
150
  # Team UUID (autogenerated if not set)
137
151
  id: Optional[str] = None
138
- # If this team is part of a team itself, this is the role of the team
139
- parent_team_id: Optional[str] = None
152
+ # Name of the team
153
+ name: Optional[str] = None
140
154
  # If this team is part of a team itself, this is the role of the team
141
155
  role: Optional[str] = None
142
156
 
157
+ # --- If this Team is part of a team itself ---
158
+ # If this team is part of a team itself, this is the ID of the parent team. This is set automatically.
159
+ parent_team_id: Optional[str] = None
160
+
161
+ # --- If this Team is part of a workflow ---
162
+ # Optional workflow ID. Indicates this team is part of a workflow. This is set automatically.
163
+ workflow_id: Optional[str] = None
164
+
165
+ # --- Team execution settings ---
143
166
  # If True, the team leader won't process responses from the members and instead will return them directly
144
167
  # Should not be used in combination with delegate_task_to_all_members
145
168
  respond_directly: bool = False
@@ -148,10 +171,6 @@ class Team:
148
171
  # Set to false if you want to send the run input directly to the member agents
149
172
  determine_input_for_members: bool = True
150
173
 
151
- # --- If this Team is part of a workflow ---
152
- # Optional workflow ID. Indicates this team is part of a workflow.
153
- workflow_id: Optional[str] = None
154
-
155
174
  # --- User settings ---
156
175
  # Default user ID for this team
157
176
  user_id: Optional[str] = None
@@ -170,13 +189,22 @@ class Team:
170
189
  # If True, cache the current Team session in memory for faster access
171
190
  cache_session: bool = False
172
191
 
173
- # If True, allow searching through previous sessions
192
+ # Add this flag to control if the workflow should send the team history to the members. This means sending the team-level history to the members, not the agent-level history.
193
+ add_team_history_to_members: bool = False
194
+ # Number of historical runs to include in the messages sent to the members
195
+ num_team_history_runs: int = 3
196
+ # If True, send all member interactions (request/response) during the current run to members that have been delegated a task to
197
+ share_member_interactions: bool = False
198
+
199
+ # If True, adds a tool to allow searching through previous sessions
174
200
  search_session_history: Optional[bool] = False
175
201
  # Number of past sessions to include in the search
176
202
  num_history_sessions: Optional[int] = None
177
203
 
178
- # If True, resolve the session_state, dependencies, and metadata in the user and system messages
179
- resolve_in_context: bool = True
204
+ # If True, adds a tool to allow the team to read the team history (this is deprecated and will be removed in a future version)
205
+ read_team_history: bool = False
206
+ # If True, adds a tool to allow the team to read the chat history
207
+ read_chat_history: bool = False
180
208
 
181
209
  # --- System message settings ---
182
210
  # A description of the Team that is added to the start of the system message.
@@ -206,6 +234,9 @@ class Team:
206
234
  # Role for the system message
207
235
  system_message_role: str = "system"
208
236
 
237
+ # If True, resolve the session_state, dependencies, and metadata in the user and system messages
238
+ resolve_in_context: bool = True
239
+
209
240
  # --- Extra Messages ---
210
241
  # A list of extra messages added after the system message and before the user message.
211
242
  # Use these for few-shot learning or to provide additional context to the Model.
@@ -244,17 +275,12 @@ class Team:
244
275
  references_format: Literal["json", "yaml"] = "json"
245
276
 
246
277
  # --- Tools ---
247
- # If True, send all previous member interactions to members
248
- share_member_interactions: bool = False
249
278
  # If True, add a tool to get information about the team members
250
279
  get_member_information_tool: bool = False
251
280
  # Add a tool to search the knowledge base (aka Agentic RAG)
252
281
  # Only added if knowledge is provided.
253
282
  search_knowledge: bool = True
254
283
 
255
- # If True, read the team history
256
- read_team_history: bool = False
257
-
258
284
  # If False, media (images, videos, audio, files) is only available to tools and not sent to the LLM
259
285
  send_media_to_model: bool = True
260
286
  # If True, store media in run output
@@ -327,6 +353,8 @@ class Team:
327
353
  add_history_to_context: bool = False
328
354
  # Number of historical runs to include in the messages
329
355
  num_history_runs: int = 3
356
+ # Maximum number of tool calls to include from history (None = no limit)
357
+ max_tool_calls_from_history: Optional[int] = None
330
358
 
331
359
  # --- Team Storage ---
332
360
  # Metadata stored with this team
@@ -342,8 +370,10 @@ class Team:
342
370
  # --- Team Streaming ---
343
371
  # Stream the response from the Team
344
372
  stream: Optional[bool] = None
345
- # Stream the intermediate steps from the Team
346
- stream_intermediate_steps: bool = False
373
+ # Stream the intermediate steps from the Agent
374
+ stream_events: Optional[bool] = None
375
+ # [Deprecated] Stream the intermediate steps from the Agent
376
+ stream_intermediate_steps: Optional[bool] = None
347
377
  # Stream the member events from the Team
348
378
  stream_member_events: bool = True
349
379
 
@@ -393,6 +423,8 @@ class Team:
393
423
  overwrite_db_session_state: bool = False,
394
424
  resolve_in_context: bool = True,
395
425
  cache_session: bool = False,
426
+ add_team_history_to_members: bool = False,
427
+ num_team_history_runs: int = 3,
396
428
  search_session_history: Optional[bool] = False,
397
429
  num_history_sessions: Optional[int] = None,
398
430
  description: Optional[str] = None,
@@ -421,10 +453,13 @@ class Team:
421
453
  get_member_information_tool: bool = False,
422
454
  search_knowledge: bool = True,
423
455
  read_team_history: bool = False,
456
+ read_chat_history: bool = False,
424
457
  store_media: bool = True,
425
458
  store_tool_messages: bool = True,
426
459
  store_history_messages: bool = True,
427
460
  send_media_to_model: bool = True,
461
+ add_history_to_context: bool = False,
462
+ num_history_runs: int = 3,
428
463
  tools: Optional[List[Union[Toolkit, Callable, Function, Dict]]] = None,
429
464
  tool_call_limit: Optional[int] = None,
430
465
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
@@ -447,8 +482,7 @@ class Team:
447
482
  enable_session_summaries: bool = False,
448
483
  session_summary_manager: Optional[SessionSummaryManager] = None,
449
484
  add_session_summary_to_context: Optional[bool] = None,
450
- add_history_to_context: bool = False,
451
- num_history_runs: int = 3,
485
+ max_tool_calls_from_history: Optional[int] = None,
452
486
  metadata: Optional[Dict[str, Any]] = None,
453
487
  reasoning: bool = False,
454
488
  reasoning_model: Optional[Model] = None,
@@ -456,7 +490,8 @@ class Team:
456
490
  reasoning_min_steps: int = 1,
457
491
  reasoning_max_steps: int = 10,
458
492
  stream: Optional[bool] = None,
459
- stream_intermediate_steps: bool = False,
493
+ stream_events: Optional[bool] = None,
494
+ stream_intermediate_steps: Optional[bool] = None,
460
495
  store_events: bool = False,
461
496
  events_to_skip: Optional[List[Union[RunEvent, TeamRunEvent]]] = None,
462
497
  store_member_responses: bool = False,
@@ -490,6 +525,10 @@ class Team:
490
525
  self.resolve_in_context = resolve_in_context
491
526
  self.cache_session = cache_session
492
527
 
528
+ self.add_history_to_context = add_history_to_context
529
+ self.num_history_runs = num_history_runs
530
+ self.add_team_history_to_members = add_team_history_to_members
531
+ self.num_team_history_runs = num_team_history_runs
493
532
  self.search_session_history = search_session_history
494
533
  self.num_history_sessions = num_history_sessions
495
534
 
@@ -521,7 +560,7 @@ class Team:
521
560
  self.share_member_interactions = share_member_interactions
522
561
  self.get_member_information_tool = get_member_information_tool
523
562
  self.search_knowledge = search_knowledge
524
- self.read_team_history = read_team_history
563
+ self.read_chat_history = read_chat_history or read_team_history
525
564
 
526
565
  self.store_media = store_media
527
566
  self.store_tool_messages = store_tool_messages
@@ -557,13 +596,9 @@ class Team:
557
596
  self.add_session_summary_to_context = add_session_summary_to_context
558
597
  self.add_history_to_context = add_history_to_context
559
598
  self.num_history_runs = num_history_runs
599
+ self.max_tool_calls_from_history = max_tool_calls_from_history
560
600
  self.metadata = metadata
561
601
 
562
- if add_history_to_context and not db:
563
- log_warning(
564
- "add_history_to_context is True, but no database has been assigned to the agent. History will not be added to the context."
565
- )
566
-
567
602
  self.reasoning = reasoning
568
603
  self.reasoning_model = reasoning_model
569
604
  self.reasoning_agent = reasoning_agent
@@ -571,7 +606,7 @@ class Team:
571
606
  self.reasoning_max_steps = reasoning_max_steps
572
607
 
573
608
  self.stream = stream
574
- self.stream_intermediate_steps = stream_intermediate_steps
609
+ self.stream_events = stream_events or stream_intermediate_steps
575
610
  self.store_events = store_events
576
611
  self.store_member_responses = store_member_responses
577
612
 
@@ -620,6 +655,22 @@ class Team:
620
655
 
621
656
  self._hooks_normalised = False
622
657
 
658
+ # Lazy-initialized shared thread pool executor for background tasks (memory, cultural knowledge, etc.)
659
+ self._background_executor: Optional[Any] = None
660
+
661
+ @property
662
+ def background_executor(self) -> Any:
663
+ """Lazy initialization of shared thread pool executor for background tasks.
664
+
665
+ Handles both memory creation and cultural knowledge updates concurrently.
666
+ Initialized only on first use (runtime, not instantiation) and reused across runs.
667
+ """
668
+ if self._background_executor is None:
669
+ from concurrent.futures import ThreadPoolExecutor
670
+
671
+ self._background_executor = ThreadPoolExecutor(max_workers=3, thread_name_prefix="agno-bg")
672
+ return self._background_executor
673
+
623
674
  @property
624
675
  def should_parse_structured_output(self) -> bool:
625
676
  return self.output_schema is not None and self.parse_response and self.parser_model is None
@@ -759,11 +810,11 @@ class Team:
759
810
 
760
811
  def _initialize_session(
761
812
  self,
762
- run_id: str,
763
- user_id: Optional[str] = None,
764
813
  session_id: Optional[str] = None,
765
- session_state: Optional[Dict[str, Any]] = None,
766
- ) -> Tuple[str, Optional[str], Dict[str, Any]]:
814
+ user_id: Optional[str] = None,
815
+ ) -> Tuple[str, Optional[str]]:
816
+ """Initialize the session for the team."""
817
+
767
818
  if session_id is None:
768
819
  if self.session_id:
769
820
  session_id = self.session_id
@@ -775,30 +826,26 @@ class Team:
775
826
  log_debug(f"Session ID: {session_id}", center=True)
776
827
 
777
828
  # Use the default user_id when necessary
778
- if user_id is None:
829
+ if user_id is None or user_id == "":
779
830
  user_id = self.user_id
780
831
 
781
- # Determine the session_state with proper precedence
782
- if session_state is None:
783
- session_state = self.session_state or {}
784
- else:
785
- # If run session_state is provided, merge agent defaults under it
786
- # This ensures run state takes precedence over agent defaults
787
- if self.session_state:
788
- from agno.utils.merge_dict import merge_dictionaries
789
-
790
- base_state = self.session_state.copy()
791
- merge_dictionaries(base_state, session_state)
792
- session_state.clear()
793
- session_state.update(base_state)
832
+ return session_id, user_id
794
833
 
795
- if user_id is not None:
834
+ def _initialize_session_state(
835
+ self,
836
+ session_state: Dict[str, Any],
837
+ user_id: Optional[str] = None,
838
+ session_id: Optional[str] = None,
839
+ run_id: Optional[str] = None,
840
+ ) -> Dict[str, Any]:
841
+ """Initialize the session state for the team."""
842
+ if user_id:
796
843
  session_state["current_user_id"] = user_id
797
844
  if session_id is not None:
798
845
  session_state["current_session_id"] = session_id
799
- session_state["current_run_id"] = run_id
800
-
801
- return session_id, user_id, session_state # type: ignore
846
+ if run_id is not None:
847
+ session_state["current_run_id"] = run_id
848
+ return session_state
802
849
 
803
850
  def _has_async_db(self) -> bool:
804
851
  """Return True if the db the team is equipped with is an Async implementation"""
@@ -810,7 +857,7 @@ class Team:
810
857
 
811
858
  if self.delegate_task_to_all_members and self.respond_directly:
812
859
  log_warning(
813
- "delegate_task_to_all_members and respond_directly are both enabled. The task will be delegated to all members."
860
+ "`delegate_task_to_all_members` and `respond_directly` are both enabled. The task will be delegated to all members, but `respond_directly` will be disabled."
814
861
  )
815
862
  self.respond_directly = False
816
863
 
@@ -887,11 +934,13 @@ class Team:
887
934
  all_args.update(kwargs)
888
935
 
889
936
  for i, hook in enumerate(hooks):
890
- yield self._handle_event(
937
+ yield handle_event( # type: ignore
891
938
  run_response=run_response,
892
939
  event=create_team_pre_hook_started_event(
893
940
  from_run_response=run_response, run_input=run_input, pre_hook_name=hook.__name__
894
941
  ),
942
+ events_to_skip=self.events_to_skip,
943
+ store_events=self.store_events,
895
944
  )
896
945
  try:
897
946
  # Filter arguments to only include those that the hook accepts
@@ -899,11 +948,13 @@ class Team:
899
948
 
900
949
  hook(**filtered_args)
901
950
 
902
- yield self._handle_event(
951
+ yield handle_event( # type: ignore
903
952
  run_response=run_response,
904
953
  event=create_team_pre_hook_completed_event(
905
954
  from_run_response=run_response, run_input=run_input, pre_hook_name=hook.__name__
906
955
  ),
956
+ events_to_skip=self.events_to_skip,
957
+ store_events=self.store_events,
907
958
  )
908
959
 
909
960
  except (InputCheckError, OutputCheckError) as e:
@@ -949,11 +1000,13 @@ class Team:
949
1000
  all_args.update(kwargs)
950
1001
 
951
1002
  for i, hook in enumerate(hooks):
952
- yield self._handle_event(
1003
+ yield handle_event( # type: ignore
953
1004
  run_response=run_response,
954
1005
  event=create_team_pre_hook_started_event(
955
1006
  from_run_response=run_response, run_input=run_input, pre_hook_name=hook.__name__
956
1007
  ),
1008
+ events_to_skip=self.events_to_skip,
1009
+ store_events=self.store_events,
957
1010
  )
958
1011
  try:
959
1012
  # Filter arguments to only include those that the hook accepts
@@ -965,11 +1018,13 @@ class Team:
965
1018
  # Synchronous function
966
1019
  hook(**filtered_args)
967
1020
 
968
- yield self._handle_event(
1021
+ yield handle_event( # type: ignore
969
1022
  run_response=run_response,
970
1023
  event=create_team_pre_hook_completed_event(
971
1024
  from_run_response=run_response, run_input=run_input, pre_hook_name=hook.__name__
972
1025
  ),
1026
+ events_to_skip=self.events_to_skip,
1027
+ store_events=self.store_events,
973
1028
  )
974
1029
 
975
1030
  except (InputCheckError, OutputCheckError) as e:
@@ -995,7 +1050,7 @@ class Team:
995
1050
  user_id: Optional[str] = None,
996
1051
  debug_mode: Optional[bool] = None,
997
1052
  **kwargs: Any,
998
- ) -> None:
1053
+ ) -> Iterator[TeamRunOutputEvent]:
999
1054
  """Execute multiple post-hook functions in succession."""
1000
1055
  if hooks is None:
1001
1056
  return
@@ -1014,12 +1069,31 @@ class Team:
1014
1069
  all_args.update(kwargs)
1015
1070
 
1016
1071
  for i, hook in enumerate(hooks):
1072
+ yield handle_event( # type: ignore
1073
+ run_response=run_output,
1074
+ event=create_team_post_hook_started_event( # type: ignore
1075
+ from_run_response=run_output,
1076
+ post_hook_name=hook.__name__,
1077
+ ),
1078
+ events_to_skip=self.events_to_skip,
1079
+ store_events=self.store_events,
1080
+ )
1017
1081
  try:
1018
1082
  # Filter arguments to only include those that the hook accepts
1019
1083
  filtered_args = filter_hook_args(hook, all_args)
1020
1084
 
1021
1085
  hook(**filtered_args)
1022
1086
 
1087
+ yield handle_event( # type: ignore
1088
+ run_response=run_output,
1089
+ event=create_team_post_hook_completed_event( # type: ignore
1090
+ from_run_response=run_output,
1091
+ post_hook_name=hook.__name__,
1092
+ ),
1093
+ events_to_skip=self.events_to_skip,
1094
+ store_events=self.store_events,
1095
+ )
1096
+
1023
1097
  except (InputCheckError, OutputCheckError) as e:
1024
1098
  raise e
1025
1099
  except Exception as e:
@@ -1037,7 +1111,7 @@ class Team:
1037
1111
  metadata: Optional[Dict[str, Any]] = None,
1038
1112
  debug_mode: Optional[bool] = None,
1039
1113
  **kwargs: Any,
1040
- ) -> None:
1114
+ ) -> AsyncIterator[TeamRunOutputEvent]:
1041
1115
  """Execute multiple post-hook functions in succession (async version)."""
1042
1116
  if hooks is None:
1043
1117
  return
@@ -1056,6 +1130,15 @@ class Team:
1056
1130
  all_args.update(kwargs)
1057
1131
 
1058
1132
  for i, hook in enumerate(hooks):
1133
+ yield handle_event( # type: ignore
1134
+ run_response=run_output,
1135
+ event=create_team_post_hook_started_event( # type: ignore
1136
+ from_run_response=run_output,
1137
+ post_hook_name=hook.__name__,
1138
+ ),
1139
+ events_to_skip=self.events_to_skip,
1140
+ store_events=self.store_events,
1141
+ )
1059
1142
  try:
1060
1143
  # Filter arguments to only include those that the hook accepts
1061
1144
  filtered_args = filter_hook_args(hook, all_args)
@@ -1065,6 +1148,15 @@ class Team:
1065
1148
  else:
1066
1149
  hook(**filtered_args)
1067
1150
 
1151
+ yield handle_event( # type: ignore
1152
+ run_response=run_output,
1153
+ event=create_team_post_hook_completed_event( # type: ignore
1154
+ from_run_response=run_output,
1155
+ post_hook_name=hook.__name__,
1156
+ ),
1157
+ events_to_skip=self.events_to_skip,
1158
+ store_events=self.store_events,
1159
+ )
1068
1160
  except (InputCheckError, OutputCheckError) as e:
1069
1161
  raise e
1070
1162
  except Exception as e:
@@ -1091,15 +1183,18 @@ class Team:
1091
1183
 
1092
1184
  Steps:
1093
1185
  1. Execute pre-hooks
1094
- 2. Get run messages
1095
- 3. Reason about the task(s) if reasoning is enabled
1096
- 4. Get a response from the model
1097
- 5. Update TeamRunOutput
1098
- 6. Execute post-hooks
1099
- 7. Add RunOutput to Team Session
1100
- 8. Calculate session metrics
1101
- 9. Update Team Memory
1102
- 10. Save session to storage
1186
+ 2. Determine tools for model
1187
+ 3. Prepare run messages
1188
+ 4. Start memory creation in background thread
1189
+ 5. Reason about the task if reasoning is enabled
1190
+ 6. Get a response from the model
1191
+ 7. Update TeamRunOutput with the model response
1192
+ 8. Store media if enabled
1193
+ 9. Convert response to structured format
1194
+ 10. Execute post-hooks
1195
+ 11. Wait for background memory creation
1196
+ 12. Create session summary
1197
+ 13. Cleanup and store (scrub, stop timer, add to session, calculate metrics, save session)
1103
1198
  """
1104
1199
 
1105
1200
  # Register run for cancellation tracking
@@ -1125,6 +1220,7 @@ class Team:
1125
1220
  # Consume the generator without yielding
1126
1221
  deque(pre_hook_iterator, maxlen=0)
1127
1222
 
1223
+ # 2. Determine tools for model
1128
1224
  # Initialize team run context
1129
1225
  team_run_context: Dict[str, Any] = {}
1130
1226
 
@@ -1150,7 +1246,7 @@ class Team:
1150
1246
  metadata=metadata,
1151
1247
  )
1152
1248
 
1153
- # 2. Prepare run messages
1249
+ # 3. Prepare run messages
1154
1250
  run_messages: RunMessages = self._get_run_messages(
1155
1251
  run_response=run_response,
1156
1252
  session=session,
@@ -1174,95 +1270,112 @@ class Team:
1174
1270
 
1175
1271
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
1176
1272
 
1177
- # 3. Reason about the task(s) if reasoning is enabled
1178
- self._handle_reasoning(run_response=run_response, run_messages=run_messages)
1179
-
1180
- # Check for cancellation before model call
1181
- raise_if_cancelled(run_response.run_id) # type: ignore
1273
+ # 4. Start memory creation in background thread
1274
+ memory_future = None
1275
+ if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
1276
+ log_debug("Starting memory creation in background thread.")
1277
+ memory_future = self.background_executor.submit(
1278
+ self._make_memories, run_messages=run_messages, user_id=user_id
1279
+ )
1182
1280
 
1183
- # 4. Get the model response for the team leader
1184
- self.model = cast(Model, self.model)
1185
- model_response: ModelResponse = self.model.response(
1186
- messages=run_messages.messages,
1187
- response_format=response_format,
1188
- tools=self._tools_for_model,
1189
- functions=self._functions_for_model,
1190
- tool_choice=self.tool_choice,
1191
- tool_call_limit=self.tool_call_limit,
1192
- send_media_to_model=self.send_media_to_model,
1193
- )
1281
+ try:
1282
+ raise_if_cancelled(run_response.run_id) # type: ignore
1194
1283
 
1195
- # Check for cancellation after model call
1196
- raise_if_cancelled(run_response.run_id) # type: ignore
1284
+ # 5. Reason about the task if reasoning is enabled
1285
+ self._handle_reasoning(run_response=run_response, run_messages=run_messages)
1197
1286
 
1198
- # If an output model is provided, generate output using the output model
1199
- self._parse_response_with_output_model(model_response, run_messages)
1287
+ # Check for cancellation before model call
1288
+ raise_if_cancelled(run_response.run_id) # type: ignore
1200
1289
 
1201
- # If a parser model is provided, structure the response separately
1202
- self._parse_response_with_parser_model(model_response, run_messages)
1290
+ # 6. Get the model response for the team leader
1291
+ self.model = cast(Model, self.model)
1292
+ model_response: ModelResponse = self.model.response(
1293
+ messages=run_messages.messages,
1294
+ response_format=response_format,
1295
+ tools=self._tools_for_model,
1296
+ functions=self._functions_for_model,
1297
+ tool_choice=self.tool_choice,
1298
+ tool_call_limit=self.tool_call_limit,
1299
+ send_media_to_model=self.send_media_to_model,
1300
+ )
1203
1301
 
1204
- # 5. Update TeamRunOutput
1205
- self._update_run_response(model_response=model_response, run_response=run_response, run_messages=run_messages)
1302
+ # Check for cancellation after model call
1303
+ raise_if_cancelled(run_response.run_id) # type: ignore
1206
1304
 
1207
- if self.store_media:
1208
- self._store_media(run_response, model_response)
1209
- else:
1210
- self._scrub_media_from_run_output(run_response)
1305
+ # If an output model is provided, generate output using the output model
1306
+ self._parse_response_with_output_model(model_response, run_messages)
1211
1307
 
1212
- # Parse team response model
1213
- self._convert_response_to_structured_format(run_response=run_response)
1308
+ # If a parser model is provided, structure the response separately
1309
+ self._parse_response_with_parser_model(model_response, run_messages)
1214
1310
 
1215
- # 6. Execute post-hooks after output is generated but before response is returned
1216
- if self.post_hooks is not None:
1217
- self._execute_post_hooks(
1218
- hooks=self.post_hooks, # type: ignore
1219
- run_output=run_response,
1220
- session=session,
1221
- session_state=session_state,
1222
- dependencies=dependencies,
1223
- metadata=metadata,
1224
- user_id=user_id,
1225
- debug_mode=debug_mode,
1226
- **kwargs,
1311
+ # 7. Update TeamRunOutput with the model response
1312
+ self._update_run_response(
1313
+ model_response=model_response, run_response=run_response, run_messages=run_messages
1227
1314
  )
1228
1315
 
1229
- run_response.status = RunStatus.completed
1316
+ # 8. Store media if enabled
1317
+ if self.store_media:
1318
+ self._store_media(run_response, model_response)
1230
1319
 
1231
- # Set the run duration
1232
- if run_response.metrics:
1233
- run_response.metrics.stop_timer()
1320
+ # 9. Convert response to structured format
1321
+ self._convert_response_to_structured_format(run_response=run_response)
1234
1322
 
1235
- # 7. Add the RunOutput to Team Session
1236
- session.upsert_run(run_response=run_response)
1323
+ # 10. Execute post-hooks after output is generated but before response is returned
1324
+ if self.post_hooks is not None:
1325
+ iterator = self._execute_post_hooks(
1326
+ hooks=self.post_hooks, # type: ignore
1327
+ run_output=run_response,
1328
+ session=session,
1329
+ session_state=session_state,
1330
+ dependencies=dependencies,
1331
+ metadata=metadata,
1332
+ user_id=user_id,
1333
+ debug_mode=debug_mode,
1334
+ **kwargs,
1335
+ )
1336
+ deque(iterator, maxlen=0)
1337
+ raise_if_cancelled(run_response.run_id) # type: ignore
1237
1338
 
1238
- # 8. Calculate session metrics
1239
- self._update_session_metrics(session=session)
1339
+ # 11. Wait for background memory creation
1340
+ wait_for_background_tasks(memory_future=memory_future)
1240
1341
 
1241
- # 9. Update Team Memory
1242
- response_iterator = self._make_memories_and_summaries(
1243
- run_response=run_response,
1244
- run_messages=run_messages,
1245
- session=session,
1246
- user_id=user_id,
1247
- )
1248
- deque(response_iterator, maxlen=0)
1342
+ raise_if_cancelled(run_response.run_id) # type: ignore
1343
+
1344
+ # 12. Create session summary
1345
+ if self.session_summary_manager is not None:
1346
+ # Upsert the RunOutput to Team Session before creating the session summary
1347
+ session.upsert_run(run_response=run_response)
1348
+ try:
1349
+ self.session_summary_manager.create_session_summary(session=session)
1350
+ except Exception as e:
1351
+ log_warning(f"Error in session summary creation: {str(e)}")
1352
+
1353
+ raise_if_cancelled(run_response.run_id) # type: ignore
1249
1354
 
1250
- # 10. Scrub the stored run based on storage flags
1251
- if self._scrub_run_output_for_storage(run_response):
1252
- session.upsert_run(run_response=run_response)
1355
+ # Set the run status to completed
1356
+ run_response.status = RunStatus.completed
1253
1357
 
1254
- # 11. Save session to storage
1255
- self.save_session(session=session)
1358
+ # 13. Cleanup and store the run response
1359
+ self._cleanup_and_store(run_response=run_response, session=session)
1256
1360
 
1257
- # Log Team Telemetry
1258
- self._log_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
1361
+ # Log Team Telemetry
1362
+ self._log_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
1363
+
1364
+ log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
1259
1365
 
1260
- log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
1366
+ return run_response
1261
1367
 
1262
- # Always clean up the run tracking
1263
- cleanup_run(run_response.run_id) # type: ignore
1368
+ except RunCancelledException as e:
1369
+ # Handle run cancellation during streaming
1370
+ log_info(f"Team run {run_response.run_id} was cancelled")
1371
+ run_response.status = RunStatus.cancelled
1372
+ run_response.content = str(e)
1264
1373
 
1265
- return run_response
1374
+ # Add the RunOutput to Team Session even when cancelled
1375
+ self._cleanup_and_store(run_response=run_response, session=session)
1376
+ return run_response
1377
+ finally:
1378
+ cleanup_run(run_response.run_id) # type: ignore
1266
1379
 
1267
1380
  def _run_stream(
1268
1381
  self,
@@ -1277,7 +1390,7 @@ class Team:
1277
1390
  metadata: Optional[Dict[str, Any]] = None,
1278
1391
  dependencies: Optional[Dict[str, Any]] = None,
1279
1392
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1280
- stream_intermediate_steps: bool = False,
1393
+ stream_events: bool = False,
1281
1394
  yield_run_response: bool = False,
1282
1395
  debug_mode: Optional[bool] = None,
1283
1396
  **kwargs: Any,
@@ -1286,14 +1399,15 @@ class Team:
1286
1399
 
1287
1400
  Steps:
1288
1401
  1. Execute pre-hooks
1289
- 2. Prepare run messages
1290
- 3. Reason about the task(s) if reasoning is enabled
1291
- 4. Get a response from the model
1292
- 5. Add the run to Team Session
1293
- 6. Update Team Memory
1294
- 7. Create the run completed event
1295
- 8. Calculate session metrics
1296
- 9. Save session to storage
1402
+ 2. Determine tools for model
1403
+ 3. Prepare run messages
1404
+ 4. Start memory creation in background thread
1405
+ 5. Reason about the task if reasoning is enabled
1406
+ 6. Get a response from the model
1407
+ 7. Parse response with parser model if provided
1408
+ 8. Wait for background memory creation
1409
+ 9. Create session summary
1410
+ 10. Cleanup and store (scrub, add to session, calculate metrics, save session)
1297
1411
  """
1298
1412
  # Register run for cancellation tracking
1299
1413
  register_run(run_response.run_id) # type: ignore
@@ -1318,6 +1432,7 @@ class Team:
1318
1432
  for pre_hook_event in pre_hook_iterator:
1319
1433
  yield pre_hook_event
1320
1434
 
1435
+ # 2. Determine tools for model
1321
1436
  # Initialize team run context
1322
1437
  team_run_context: Dict[str, Any] = {}
1323
1438
 
@@ -1343,7 +1458,7 @@ class Team:
1343
1458
  metadata=metadata,
1344
1459
  )
1345
1460
 
1346
- # 2. Prepare run messages
1461
+ # 3. Prepare run messages
1347
1462
  run_messages: RunMessages = self._get_run_messages(
1348
1463
  run_response=run_response,
1349
1464
  session=session,
@@ -1367,28 +1482,44 @@ class Team:
1367
1482
 
1368
1483
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
1369
1484
 
1485
+ # 4. Start memory creation in background thread
1486
+ memory_future = None
1487
+ if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
1488
+ log_debug("Starting memory creation in background thread.")
1489
+ memory_future = self.background_executor.submit(
1490
+ self._make_memories, run_messages=run_messages, user_id=user_id
1491
+ )
1492
+
1370
1493
  try:
1371
1494
  # Start the Run by yielding a RunStarted event
1372
- if stream_intermediate_steps:
1373
- yield self._handle_event(create_team_run_started_event(run_response), run_response)
1495
+ if stream_events:
1496
+ yield handle_event( # type: ignore
1497
+ create_team_run_started_event(run_response),
1498
+ run_response,
1499
+ events_to_skip=self.events_to_skip,
1500
+ store_events=self.store_events,
1501
+ )
1502
+
1503
+ raise_if_cancelled(run_response.run_id) # type: ignore
1374
1504
 
1375
- # 3. Reason about the task(s) if reasoning is enabled
1505
+ # 5. Reason about the task if reasoning is enabled
1376
1506
  yield from self._handle_reasoning_stream(
1377
1507
  run_response=run_response,
1378
1508
  run_messages=run_messages,
1509
+ stream_events=stream_events,
1379
1510
  )
1380
1511
 
1381
1512
  # Check for cancellation before model processing
1382
1513
  raise_if_cancelled(run_response.run_id) # type: ignore
1383
1514
 
1384
- # 4. Get a response from the model
1515
+ # 6. Get a response from the model
1385
1516
  if self.output_model is None:
1386
1517
  for event in self._handle_model_response_stream(
1387
1518
  session=session,
1388
1519
  run_response=run_response,
1389
1520
  run_messages=run_messages,
1390
1521
  response_format=response_format,
1391
- stream_intermediate_steps=stream_intermediate_steps,
1522
+ stream_events=stream_events,
1392
1523
  ):
1393
1524
  raise_if_cancelled(run_response.run_id) # type: ignore
1394
1525
  yield event
@@ -1398,13 +1529,13 @@ class Team:
1398
1529
  run_response=run_response,
1399
1530
  run_messages=run_messages,
1400
1531
  response_format=response_format,
1401
- stream_intermediate_steps=stream_intermediate_steps,
1532
+ stream_events=stream_events,
1402
1533
  ):
1403
1534
  raise_if_cancelled(run_response.run_id) # type: ignore
1404
1535
  from agno.run.team import IntermediateRunContentEvent, RunContentEvent
1405
1536
 
1406
1537
  if isinstance(event, RunContentEvent):
1407
- if stream_intermediate_steps:
1538
+ if stream_events:
1408
1539
  yield IntermediateRunContentEvent(
1409
1540
  content=event.content,
1410
1541
  content_type=event.content_type,
@@ -1416,7 +1547,7 @@ class Team:
1416
1547
  session=session,
1417
1548
  run_response=run_response,
1418
1549
  run_messages=run_messages,
1419
- stream_intermediate_steps=stream_intermediate_steps,
1550
+ stream_events=stream_events,
1420
1551
  ):
1421
1552
  raise_if_cancelled(run_response.run_id) # type: ignore
1422
1553
  yield event
@@ -1424,14 +1555,22 @@ class Team:
1424
1555
  # Check for cancellation after model processing
1425
1556
  raise_if_cancelled(run_response.run_id) # type: ignore
1426
1557
 
1427
- # If a parser model is provided, structure the response separately
1558
+ # 7. Parse response with parser model if provided
1428
1559
  yield from self._parse_response_with_parser_model_stream(
1429
- session=session, run_response=run_response, stream_intermediate_steps=stream_intermediate_steps
1560
+ session=session, run_response=run_response, stream_events=stream_events
1430
1561
  )
1431
1562
 
1563
+ # Yield RunContentCompletedEvent
1564
+ if stream_events:
1565
+ yield handle_event( # type: ignore
1566
+ create_team_run_content_completed_event(from_run_response=run_response),
1567
+ run_response,
1568
+ events_to_skip=self.events_to_skip,
1569
+ store_events=self.store_events,
1570
+ )
1432
1571
  # Execute post-hooks after output is generated but before response is returned
1433
1572
  if self.post_hooks is not None:
1434
- self._execute_post_hooks(
1573
+ yield from self._execute_post_hooks(
1435
1574
  hooks=self.post_hooks, # type: ignore
1436
1575
  run_output=run_response,
1437
1576
  session_state=session_state,
@@ -1442,42 +1581,62 @@ class Team:
1442
1581
  debug_mode=debug_mode,
1443
1582
  **kwargs,
1444
1583
  )
1584
+ raise_if_cancelled(run_response.run_id) # type: ignore
1445
1585
 
1446
- run_response.status = RunStatus.completed
1447
- # Set the run duration
1448
- if run_response.metrics:
1449
- run_response.metrics.stop_timer()
1450
-
1451
- # 5. Add the run to Team Session
1452
- session.upsert_run(run_response=run_response)
1453
-
1454
- # 6. Update Team Memory
1455
- yield from self._make_memories_and_summaries(
1586
+ # 8. Wait for background memory creation
1587
+ yield from wait_for_background_tasks_stream(
1456
1588
  run_response=run_response,
1457
- run_messages=run_messages,
1458
- session=session,
1459
- user_id=user_id,
1589
+ memory_future=memory_future,
1590
+ stream_events=stream_events,
1591
+ events_to_skip=self.events_to_skip, # type: ignore
1592
+ store_events=self.store_events,
1460
1593
  )
1461
1594
 
1462
- # 7. Create the run completed event
1463
- completed_event = self._handle_event(
1595
+ raise_if_cancelled(run_response.run_id) # type: ignore
1596
+ # 9. Create session summary
1597
+ if self.session_summary_manager is not None:
1598
+ # Upsert the RunOutput to Team Session before creating the session summary
1599
+ session.upsert_run(run_response=run_response)
1600
+
1601
+ if stream_events:
1602
+ yield handle_event( # type: ignore
1603
+ create_team_session_summary_started_event(from_run_response=run_response),
1604
+ run_response,
1605
+ events_to_skip=self.events_to_skip,
1606
+ store_events=self.store_events,
1607
+ )
1608
+ try:
1609
+ self.session_summary_manager.create_session_summary(session=session)
1610
+ except Exception as e:
1611
+ log_warning(f"Error in session summary creation: {str(e)}")
1612
+ if stream_events:
1613
+ yield handle_event( # type: ignore
1614
+ create_team_session_summary_completed_event(
1615
+ from_run_response=run_response, session_summary=session.summary
1616
+ ),
1617
+ run_response,
1618
+ events_to_skip=self.events_to_skip,
1619
+ store_events=self.store_events,
1620
+ )
1621
+
1622
+ raise_if_cancelled(run_response.run_id) # type: ignore
1623
+ # Create the run completed event
1624
+ completed_event = handle_event(
1464
1625
  create_team_run_completed_event(
1465
1626
  from_run_response=run_response,
1466
1627
  ),
1467
1628
  run_response,
1629
+ events_to_skip=self.events_to_skip,
1630
+ store_events=self.store_events,
1468
1631
  )
1469
1632
 
1470
- # 8. Calculate session metrics
1471
- self._update_session_metrics(session=session)
1472
-
1473
- # 9. Scrub the stored run based on storage flags
1474
- if self._scrub_run_output_for_storage(run_response):
1475
- session.upsert_run(run_response=run_response)
1633
+ # Set the run status to completed
1634
+ run_response.status = RunStatus.completed
1476
1635
 
1477
- # 10. Save session to storage
1478
- self.save_session(session=session)
1636
+ # 10. Cleanup and store the run response
1637
+ self._cleanup_and_store(run_response=run_response, session=session)
1479
1638
 
1480
- if stream_intermediate_steps:
1639
+ if stream_events:
1481
1640
  yield completed_event
1482
1641
 
1483
1642
  if yield_run_response:
@@ -1495,14 +1654,15 @@ class Team:
1495
1654
  run_response.content = str(e)
1496
1655
 
1497
1656
  # Yield the cancellation event
1498
- yield self._handle_event(
1657
+ yield handle_event( # type: ignore
1499
1658
  create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
1500
1659
  run_response,
1660
+ events_to_skip=self.events_to_skip,
1661
+ store_events=self.store_events,
1501
1662
  )
1502
1663
 
1503
1664
  # Add the RunOutput to Team Session even when cancelled
1504
- session.upsert_run(run_response=run_response)
1505
- self.save_session(session=session)
1665
+ self._cleanup_and_store(run_response=run_response, session=session)
1506
1666
  finally:
1507
1667
  # Always clean up the run tracking
1508
1668
  cleanup_run(run_response.run_id) # type: ignore
@@ -1513,6 +1673,7 @@ class Team:
1513
1673
  input: Union[str, List, Dict, Message, BaseModel, List[Message]],
1514
1674
  *,
1515
1675
  stream: Literal[False] = False,
1676
+ stream_events: Optional[bool] = None,
1516
1677
  stream_intermediate_steps: Optional[bool] = None,
1517
1678
  session_id: Optional[str] = None,
1518
1679
  session_state: Optional[Dict[str, Any]] = None,
@@ -1538,6 +1699,7 @@ class Team:
1538
1699
  input: Union[str, List, Dict, Message, BaseModel, List[Message]],
1539
1700
  *,
1540
1701
  stream: Literal[True] = True,
1702
+ stream_events: Optional[bool] = None,
1541
1703
  stream_intermediate_steps: Optional[bool] = None,
1542
1704
  session_id: Optional[str] = None,
1543
1705
  session_state: Optional[Dict[str, Any]] = None,
@@ -1563,6 +1725,7 @@ class Team:
1563
1725
  input: Union[str, List, Dict, Message, BaseModel, List[Message]],
1564
1726
  *,
1565
1727
  stream: Optional[bool] = None,
1728
+ stream_events: Optional[bool] = None,
1566
1729
  stream_intermediate_steps: Optional[bool] = None,
1567
1730
  session_id: Optional[str] = None,
1568
1731
  session_state: Optional[Dict[str, Any]] = None,
@@ -1586,6 +1749,14 @@ class Team:
1586
1749
  if self._has_async_db():
1587
1750
  raise Exception("run() is not supported with an async DB. Please use arun() instead.")
1588
1751
 
1752
+ # Initialize Team
1753
+ self.initialize_team(debug_mode=debug_mode)
1754
+
1755
+ if (add_history_to_context or self.add_history_to_context) and not self.db and not self.parent_team_id:
1756
+ log_warning(
1757
+ "add_history_to_context is True, but no database has been assigned to the team. History will not be added to the context."
1758
+ )
1759
+
1589
1760
  # Create a run_id for this specific run
1590
1761
  run_id = str(uuid4())
1591
1762
 
@@ -1600,12 +1771,7 @@ class Team:
1600
1771
  self.post_hooks = normalize_hooks(self.post_hooks)
1601
1772
  self._hooks_normalised = True
1602
1773
 
1603
- session_id, user_id, session_state = self._initialize_session(
1604
- run_id=run_id, session_id=session_id, user_id=user_id, session_state=session_state
1605
- )
1606
-
1607
- # Initialize Team
1608
- self.initialize_team(debug_mode=debug_mode)
1774
+ session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
1609
1775
 
1610
1776
  image_artifacts, video_artifacts, audio_artifacts, file_artifacts = self._validate_media_object_id(
1611
1777
  images=images, videos=videos, audios=audio, files=files
@@ -1624,6 +1790,10 @@ class Team:
1624
1790
  team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
1625
1791
  self._update_metadata(session=team_session)
1626
1792
 
1793
+ # Initialize session state
1794
+ session_state = self._initialize_session_state(
1795
+ session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_id
1796
+ )
1627
1797
  # Update session state from DB
1628
1798
  session_state = self._load_session_state(session=team_session, session_state=session_state)
1629
1799
 
@@ -1656,17 +1826,18 @@ class Team:
1656
1826
  if stream is None:
1657
1827
  stream = False if self.stream is None else self.stream
1658
1828
 
1659
- if stream_intermediate_steps is None:
1660
- stream_intermediate_steps = (
1661
- False if self.stream_intermediate_steps is None else self.stream_intermediate_steps
1662
- )
1829
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
1830
+ stream_events = stream_events or stream_intermediate_steps
1663
1831
 
1664
- # Can't have stream_intermediate_steps if stream is False
1832
+ # Can't stream events if streaming is disabled
1665
1833
  if stream is False:
1666
- stream_intermediate_steps = False
1834
+ stream_events = False
1835
+
1836
+ if stream_events is None:
1837
+ stream_events = False if self.stream_events is None else self.stream_events
1667
1838
 
1668
1839
  self.stream = self.stream or stream
1669
- self.stream_intermediate_steps = self.stream_intermediate_steps or (stream_intermediate_steps and self.stream)
1840
+ self.stream_events = self.stream_events or stream_events
1670
1841
 
1671
1842
  # Configure the model for runs
1672
1843
  response_format: Optional[Union[Dict, Type[BaseModel]]] = (
@@ -1722,7 +1893,7 @@ class Team:
1722
1893
  metadata=metadata,
1723
1894
  dependencies=run_dependencies,
1724
1895
  response_format=response_format,
1725
- stream_intermediate_steps=stream_intermediate_steps,
1896
+ stream_events=stream_events,
1726
1897
  yield_run_response=yield_run_response,
1727
1898
  debug_mode=debug_mode,
1728
1899
  **kwargs,
@@ -1761,17 +1932,6 @@ class Team:
1761
1932
  else:
1762
1933
  delay = self.delay_between_retries
1763
1934
  time.sleep(delay)
1764
- except RunCancelledException as e:
1765
- # Handle run cancellation
1766
- log_info(f"Team run {run_response.run_id} was cancelled")
1767
- run_response.content = str(e)
1768
- run_response.status = RunStatus.cancelled
1769
-
1770
- # Add the RunOutput to Team Session even when cancelled
1771
- team_session.upsert_run(run_response=run_response)
1772
- self.save_session(session=team_session)
1773
-
1774
- return run_response
1775
1935
  except KeyboardInterrupt:
1776
1936
  run_response.content = "Operation cancelled by user"
1777
1937
  run_response.status = RunStatus.cancelled
@@ -1812,10 +1972,6 @@ class Team:
1812
1972
  add_history_to_context: Optional[bool] = None,
1813
1973
  knowledge_filters: Optional[Dict[str, Any]] = None,
1814
1974
  metadata: Optional[Dict[str, Any]] = None,
1815
- audio: Optional[Sequence[Audio]] = None,
1816
- images: Optional[Sequence[Image]] = None,
1817
- videos: Optional[Sequence[Video]] = None,
1818
- files: Optional[Sequence[File]] = None,
1819
1975
  debug_mode: Optional[bool] = None,
1820
1976
  dependencies: Optional[Dict[str, Any]] = None,
1821
1977
  **kwargs: Any,
@@ -1828,16 +1984,16 @@ class Team:
1828
1984
  3. Execute pre-hooks
1829
1985
  4. Determine tools for model
1830
1986
  5. Prepare run messages
1831
- 6. Reason about the task if reasoning is enabled
1832
- 7. Get a response from the Model (includes running function calls)
1833
- 8. Update TeamRunOutput
1834
- 9. Add the run to memory
1835
- 10. Calculate session metrics
1836
- 11. Parse team response model
1837
- 12. Update Team Memory
1838
- 13. Scrub the stored run if needed
1839
- 14. Save session to storage
1840
- 15. Execute post-hooks
1987
+ 6. Start memory creation in background task
1988
+ 7. Reason about the task if reasoning is enabled
1989
+ 8. Get a response from the Model
1990
+ 9. Update TeamRunOutput with the model response
1991
+ 10. Store media if enabled
1992
+ 11. Convert response to structured format
1993
+ 12. Execute post-hooks
1994
+ 13. Wait for background memory creation
1995
+ 14. Create session summary
1996
+ 15. Cleanup and store (scrub, add to session, calculate metrics, save session)
1841
1997
  """
1842
1998
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
1843
1999
 
@@ -1854,6 +2010,11 @@ class Team:
1854
2010
 
1855
2011
  # 2. Update metadata and session state
1856
2012
  self._update_metadata(session=team_session)
2013
+ # Initialize session state
2014
+ session_state = self._initialize_session_state(
2015
+ session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_response.run_id
2016
+ )
2017
+ # Update session state from DB
1857
2018
  session_state = self._load_session_state(session=team_session, session_state=session_state) # type: ignore
1858
2019
 
1859
2020
  run_input = cast(TeamRunInput, run_response.input)
@@ -1909,10 +2070,10 @@ class Team:
1909
2070
  session_state=session_state,
1910
2071
  user_id=user_id,
1911
2072
  input_message=run_input.input_content,
1912
- audio=audio,
1913
- images=images,
1914
- videos=videos,
1915
- files=files,
2073
+ audio=run_input.audios,
2074
+ images=run_input.images,
2075
+ videos=run_input.videos,
2076
+ files=run_input.files,
1916
2077
  knowledge_filters=knowledge_filters,
1917
2078
  add_history_to_context=add_history_to_context,
1918
2079
  dependencies=dependencies,
@@ -1921,98 +2082,122 @@ class Team:
1921
2082
  **kwargs,
1922
2083
  )
1923
2084
 
2085
+ self.model = cast(Model, self.model)
2086
+ log_debug(f"Team Run Start: {run_response.run_id}", center=True)
2087
+
2088
+ # 6. Start memory creation in background task
2089
+ import asyncio
2090
+
2091
+ memory_task = None
2092
+ if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2093
+ log_debug("Starting memory creation in background task.")
2094
+ memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
2095
+
1924
2096
  # Register run for cancellation tracking
1925
2097
  register_run(run_response.run_id) # type: ignore
1926
2098
 
1927
- # 6. Reason about the task(s) if reasoning is enabled
1928
- await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
2099
+ try:
2100
+ raise_if_cancelled(run_response.run_id) # type: ignore
2101
+ # 7. Reason about the task if reasoning is enabled
2102
+ await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
1929
2103
 
1930
- # Check for cancellation before model call
1931
- raise_if_cancelled(run_response.run_id) # type: ignore
2104
+ # Check for cancellation before model call
2105
+ raise_if_cancelled(run_response.run_id) # type: ignore
1932
2106
 
1933
- # 7. Get the model response for the team leader
1934
- self.model = cast(Model, self.model)
1935
- model_response = await self.model.aresponse(
1936
- messages=run_messages.messages,
1937
- tools=self._tools_for_model,
1938
- functions=self._functions_for_model,
1939
- tool_choice=self.tool_choice,
1940
- tool_call_limit=self.tool_call_limit,
1941
- response_format=response_format,
1942
- send_media_to_model=self.send_media_to_model,
1943
- ) # type: ignore
1944
- raise_if_cancelled(run_response.run_id) # type: ignore
2107
+ # 8. Get the model response for the team leader
2108
+ model_response = await self.model.aresponse(
2109
+ messages=run_messages.messages,
2110
+ tools=self._tools_for_model,
2111
+ functions=self._functions_for_model,
2112
+ tool_choice=self.tool_choice,
2113
+ tool_call_limit=self.tool_call_limit,
2114
+ response_format=response_format,
2115
+ send_media_to_model=self.send_media_to_model,
2116
+ ) # type: ignore
2117
+
2118
+ # Check for cancellation after model call
2119
+ raise_if_cancelled(run_response.run_id) # type: ignore
1945
2120
 
1946
- # If an output model is provided, generate output using the output model
1947
- await self._agenerate_response_with_output_model(model_response=model_response, run_messages=run_messages)
1948
- # If a parser model is provided, structure the response separately
1949
- await self._aparse_response_with_parser_model(model_response=model_response, run_messages=run_messages)
2121
+ # If an output model is provided, generate output using the output model
2122
+ await self._agenerate_response_with_output_model(model_response=model_response, run_messages=run_messages)
1950
2123
 
1951
- # 8. Update TeamRunOutput
1952
- self._update_run_response(model_response=model_response, run_response=run_response, run_messages=run_messages)
2124
+ # If a parser model is provided, structure the response separately
2125
+ await self._aparse_response_with_parser_model(model_response=model_response, run_messages=run_messages)
1953
2126
 
1954
- # Optional: Store media
1955
- if self.store_media:
1956
- self._store_media(run_response, model_response)
1957
- else:
1958
- self._scrub_media_from_run_output(run_response)
2127
+ # 9. Update TeamRunOutput with the model response
2128
+ self._update_run_response(
2129
+ model_response=model_response, run_response=run_response, run_messages=run_messages
2130
+ )
1959
2131
 
1960
- # 11. Parse team response model
1961
- self._convert_response_to_structured_format(run_response=run_response)
2132
+ # 10. Store media if enabled
2133
+ if self.store_media:
2134
+ self._store_media(run_response, model_response)
1962
2135
 
1963
- # Execute post-hooks after output is generated but before response is returned
1964
- if self.post_hooks is not None:
1965
- await self._aexecute_post_hooks(
1966
- hooks=self.post_hooks, # type: ignore
1967
- run_output=run_response,
1968
- session=team_session,
1969
- user_id=user_id,
1970
- debug_mode=debug_mode,
1971
- session_state=session_state,
1972
- dependencies=dependencies,
1973
- metadata=metadata,
1974
- **kwargs,
1975
- )
2136
+ # 11. Convert response to structured format
2137
+ self._convert_response_to_structured_format(run_response=run_response)
1976
2138
 
1977
- run_response.status = RunStatus.completed
2139
+ # 12. Execute post-hooks after output is generated but before response is returned
2140
+ if self.post_hooks is not None:
2141
+ async for _ in self._aexecute_post_hooks(
2142
+ hooks=self.post_hooks, # type: ignore
2143
+ run_output=run_response,
2144
+ session=team_session,
2145
+ session_state=session_state,
2146
+ dependencies=dependencies,
2147
+ metadata=metadata,
2148
+ user_id=user_id,
2149
+ debug_mode=debug_mode,
2150
+ **kwargs,
2151
+ ):
2152
+ pass
1978
2153
 
1979
- # Set the run duration
1980
- if run_response.metrics:
1981
- run_response.metrics.stop_timer()
2154
+ raise_if_cancelled(run_response.run_id) # type: ignore
1982
2155
 
1983
- # 9. Add the run to memory
1984
- team_session.upsert_run(run_response=run_response)
2156
+ # 13. Wait for background memory creation
2157
+ await await_for_background_tasks(memory_task=memory_task)
1985
2158
 
1986
- # 10. Calculate session metrics
1987
- self._update_session_metrics(session=team_session)
2159
+ raise_if_cancelled(run_response.run_id) # type: ignore
2160
+ # 14. Create session summary
2161
+ if self.session_summary_manager is not None:
2162
+ # Upsert the RunOutput to Team Session before creating the session summary
2163
+ team_session.upsert_run(run_response=run_response)
2164
+ try:
2165
+ await self.session_summary_manager.acreate_session_summary(session=team_session)
2166
+ except Exception as e:
2167
+ log_warning(f"Error in session summary creation: {str(e)}")
1988
2168
 
1989
- # 12. Update Team Memory
1990
- async for _ in self._amake_memories_and_summaries(
1991
- run_response=run_response,
1992
- session=team_session,
1993
- run_messages=run_messages,
1994
- user_id=user_id,
1995
- ):
1996
- pass
2169
+ raise_if_cancelled(run_response.run_id) # type: ignore
2170
+ run_response.status = RunStatus.completed
1997
2171
 
1998
- # 13. Scrub the stored run based on storage flags
1999
- if self._scrub_run_output_for_storage(run_response):
2000
- team_session.upsert_run(run_response=run_response)
2172
+ # 15. Cleanup and store the run response and session
2173
+ await self._acleanup_and_store(run_response=run_response, session=team_session)
2001
2174
 
2002
- # 14. Save session to storage
2003
- if self._has_async_db():
2004
- await self.asave_session(session=team_session)
2005
- else:
2006
- self.save_session(session=team_session)
2175
+ # Log Team Telemetry
2176
+ await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
2007
2177
 
2008
- # Log Team Telemetry
2009
- await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
2178
+ log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
2010
2179
 
2011
- log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
2180
+ return run_response
2181
+ except RunCancelledException as e:
2182
+ # Handle run cancellation
2183
+ log_info(f"Run {run_response.run_id} was cancelled")
2184
+ run_response.content = str(e)
2185
+ run_response.status = RunStatus.cancelled
2012
2186
 
2013
- cleanup_run(run_response.run_id) # type: ignore
2187
+ # Cleanup and store the run response and session
2188
+ await self._acleanup_and_store(run_response=run_response, session=team_session)
2014
2189
 
2015
- return run_response
2190
+ return run_response
2191
+ finally:
2192
+ # Cancel the memory task if it's still running
2193
+ if memory_task is not None and not memory_task.done():
2194
+ memory_task.cancel()
2195
+ try:
2196
+ await memory_task
2197
+ except asyncio.CancelledError:
2198
+ pass
2199
+ # Always clean up the run tracking
2200
+ cleanup_run(run_response.run_id) # type: ignore
2016
2201
 
2017
2202
  async def _arun_stream(
2018
2203
  self,
@@ -2021,6 +2206,7 @@ class Team:
2021
2206
  session_state: Optional[Dict[str, Any]] = None,
2022
2207
  user_id: Optional[str] = None,
2023
2208
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2209
+ stream_events: bool = False,
2024
2210
  stream_intermediate_steps: bool = False,
2025
2211
  yield_run_response: bool = False,
2026
2212
  add_dependencies_to_context: Optional[bool] = None,
@@ -2028,10 +2214,6 @@ class Team:
2028
2214
  add_history_to_context: Optional[bool] = None,
2029
2215
  knowledge_filters: Optional[Dict[str, Any]] = None,
2030
2216
  metadata: Optional[Dict[str, Any]] = None,
2031
- audio: Optional[Sequence[Audio]] = None,
2032
- images: Optional[Sequence[Image]] = None,
2033
- videos: Optional[Sequence[Video]] = None,
2034
- files: Optional[Sequence[File]] = None,
2035
2217
  debug_mode: Optional[bool] = None,
2036
2218
  dependencies: Optional[Dict[str, Any]] = None,
2037
2219
  **kwargs: Any,
@@ -2045,15 +2227,13 @@ class Team:
2045
2227
  4. Execute pre-hooks
2046
2228
  5. Determine tools for model
2047
2229
  6. Prepare run messages
2048
- 7. Yield the run started event
2049
- 8. Reason about the task(s) if reasoning is enabled
2230
+ 7. Start memory creation in background task
2231
+ 8. Reason about the task if reasoning is enabled
2050
2232
  9. Get a response from the model
2051
- 10. Add the run to memory
2052
- 11. Update Team Memory
2053
- 12. Calculate session metrics
2054
- 13. Create the run completed event
2055
- 14. Scrub the stored run if needed
2056
- 15. Save session to storage
2233
+ 10. Parse response with parser model if provided
2234
+ 11. Wait for background memory creation
2235
+ 12. Create session summary
2236
+ 13. Cleanup and store (scrub, add to session, calculate metrics, save session)
2057
2237
  """
2058
2238
 
2059
2239
  # 1. Resolve dependencies
@@ -2068,6 +2248,11 @@ class Team:
2068
2248
 
2069
2249
  # 3. Update metadata and session state
2070
2250
  self._update_metadata(session=team_session)
2251
+ # Initialize session state
2252
+ session_state = self._initialize_session_state(
2253
+ session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_response.run_id
2254
+ )
2255
+ # Update session state from DB
2071
2256
  session_state = self._load_session_state(session=team_session, session_state=session_state) # type: ignore
2072
2257
 
2073
2258
  # 4. Execute pre-hooks
@@ -2102,10 +2287,10 @@ class Team:
2102
2287
  async_mode=True,
2103
2288
  knowledge_filters=knowledge_filters,
2104
2289
  input_message=run_input.input_content,
2105
- images=images,
2106
- videos=videos,
2107
- audio=audio,
2108
- files=files,
2290
+ images=run_input.images,
2291
+ videos=run_input.videos,
2292
+ audio=run_input.audios,
2293
+ files=run_input.files,
2109
2294
  debug_mode=debug_mode,
2110
2295
  add_history_to_context=add_history_to_context,
2111
2296
  dependencies=dependencies,
@@ -2119,10 +2304,10 @@ class Team:
2119
2304
  session_state=session_state,
2120
2305
  user_id=user_id,
2121
2306
  input_message=run_input.input_content,
2122
- audio=audio,
2123
- images=images,
2124
- videos=videos,
2125
- files=files,
2307
+ audio=run_input.audios,
2308
+ images=run_input.images,
2309
+ videos=run_input.videos,
2310
+ files=run_input.files,
2126
2311
  knowledge_filters=knowledge_filters,
2127
2312
  add_history_to_context=add_history_to_context,
2128
2313
  dependencies=dependencies,
@@ -2134,16 +2319,36 @@ class Team:
2134
2319
 
2135
2320
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
2136
2321
 
2322
+ # 7. Start memory creation in background task
2323
+ import asyncio
2324
+
2325
+ memory_task = None
2326
+ if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2327
+ log_debug("Starting memory creation in background task.")
2328
+ memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
2329
+
2137
2330
  # Register run for cancellation tracking
2138
2331
  register_run(run_response.run_id) # type: ignore
2139
2332
 
2140
2333
  try:
2141
- # 7. Yield the run started event
2142
- if stream_intermediate_steps:
2143
- yield self._handle_event(create_team_run_started_event(from_run_response=run_response), run_response)
2334
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
2335
+ stream_events = stream_events or stream_intermediate_steps
2144
2336
 
2145
- # 8. Reason about the task(s) if reasoning is enabled
2146
- async for item in self._ahandle_reasoning_stream(run_response=run_response, run_messages=run_messages):
2337
+ # Yield the run started event
2338
+ if stream_events:
2339
+ yield handle_event( # type: ignore
2340
+ create_team_run_started_event(from_run_response=run_response),
2341
+ run_response,
2342
+ events_to_skip=self.events_to_skip,
2343
+ store_events=self.store_events,
2344
+ )
2345
+
2346
+ # 8. Reason about the task if reasoning is enabled
2347
+ async for item in self._ahandle_reasoning_stream(
2348
+ run_response=run_response,
2349
+ run_messages=run_messages,
2350
+ stream_events=stream_events,
2351
+ ):
2147
2352
  raise_if_cancelled(run_response.run_id) # type: ignore
2148
2353
  yield item
2149
2354
 
@@ -2157,7 +2362,7 @@ class Team:
2157
2362
  run_response=run_response,
2158
2363
  run_messages=run_messages,
2159
2364
  response_format=response_format,
2160
- stream_intermediate_steps=stream_intermediate_steps,
2365
+ stream_events=stream_events,
2161
2366
  ):
2162
2367
  raise_if_cancelled(run_response.run_id) # type: ignore
2163
2368
  yield event
@@ -2167,13 +2372,13 @@ class Team:
2167
2372
  run_response=run_response,
2168
2373
  run_messages=run_messages,
2169
2374
  response_format=response_format,
2170
- stream_intermediate_steps=stream_intermediate_steps,
2375
+ stream_events=stream_events,
2171
2376
  ):
2172
2377
  raise_if_cancelled(run_response.run_id) # type: ignore
2173
2378
  from agno.run.team import IntermediateRunContentEvent, RunContentEvent
2174
2379
 
2175
2380
  if isinstance(event, RunContentEvent):
2176
- if stream_intermediate_steps:
2381
+ if stream_events:
2177
2382
  yield IntermediateRunContentEvent(
2178
2383
  content=event.content,
2179
2384
  content_type=event.content_type,
@@ -2185,7 +2390,7 @@ class Team:
2185
2390
  session=team_session,
2186
2391
  run_response=run_response,
2187
2392
  run_messages=run_messages,
2188
- stream_intermediate_steps=stream_intermediate_steps,
2393
+ stream_events=stream_events,
2189
2394
  ):
2190
2395
  raise_if_cancelled(run_response.run_id) # type: ignore
2191
2396
  yield event
@@ -2193,15 +2398,24 @@ class Team:
2193
2398
  # Check for cancellation after model processing
2194
2399
  raise_if_cancelled(run_response.run_id) # type: ignore
2195
2400
 
2196
- # If a parser model is provided, structure the response separately
2401
+ # 10. Parse response with parser model if provided
2197
2402
  async for event in self._aparse_response_with_parser_model_stream(
2198
- session=team_session, run_response=run_response, stream_intermediate_steps=stream_intermediate_steps
2403
+ session=team_session, run_response=run_response, stream_events=stream_events
2199
2404
  ):
2200
2405
  yield event
2201
2406
 
2407
+ # Yield RunContentCompletedEvent
2408
+ if stream_events:
2409
+ yield handle_event( # type: ignore
2410
+ create_team_run_content_completed_event(from_run_response=run_response),
2411
+ run_response,
2412
+ events_to_skip=self.events_to_skip,
2413
+ store_events=self.store_events,
2414
+ )
2415
+
2202
2416
  # Execute post-hooks after output is generated but before response is returned
2203
2417
  if self.post_hooks is not None:
2204
- await self._aexecute_post_hooks(
2418
+ async for event in self._aexecute_post_hooks(
2205
2419
  hooks=self.post_hooks, # type: ignore
2206
2420
  run_output=run_response,
2207
2421
  session_state=session_state,
@@ -2211,45 +2425,65 @@ class Team:
2211
2425
  user_id=user_id,
2212
2426
  debug_mode=debug_mode,
2213
2427
  **kwargs,
2214
- )
2215
-
2216
- # Set the run duration
2217
- if run_response.metrics:
2218
- run_response.metrics.stop_timer()
2219
-
2220
- run_response.status = RunStatus.completed
2221
-
2222
- # 10. Add the run to memory
2223
- team_session.upsert_run(run_response=run_response)
2428
+ ):
2429
+ yield event
2224
2430
 
2225
- # 11. Update Team Memory
2226
- async for event in self._amake_memories_and_summaries(
2431
+ raise_if_cancelled(run_response.run_id) # type: ignore
2432
+ # 11. Wait for background memory creation
2433
+ async for event in await_for_background_tasks_stream(
2227
2434
  run_response=run_response,
2228
- session=team_session,
2229
- run_messages=run_messages,
2230
- user_id=user_id,
2435
+ memory_task=memory_task,
2436
+ stream_events=stream_events,
2437
+ events_to_skip=self.events_to_skip, # type: ignore
2438
+ store_events=self.store_events,
2231
2439
  ):
2232
2440
  yield event
2233
2441
 
2234
- # 12. Calculate session metrics
2235
- self._update_session_metrics(session=team_session)
2442
+ raise_if_cancelled(run_response.run_id) # type: ignore
2443
+
2444
+ # 12. Create session summary
2445
+ if self.session_summary_manager is not None:
2446
+ # Upsert the RunOutput to Team Session before creating the session summary
2447
+ team_session.upsert_run(run_response=run_response)
2236
2448
 
2237
- # 13. Create the run completed event
2238
- completed_event = self._handle_event(
2239
- create_team_run_completed_event(from_run_response=run_response), run_response
2449
+ if stream_events:
2450
+ yield handle_event( # type: ignore
2451
+ create_team_session_summary_started_event(from_run_response=run_response),
2452
+ run_response,
2453
+ events_to_skip=self.events_to_skip,
2454
+ store_events=self.store_events,
2455
+ )
2456
+ try:
2457
+ await self.session_summary_manager.acreate_session_summary(session=team_session)
2458
+ except Exception as e:
2459
+ log_warning(f"Error in session summary creation: {str(e)}")
2460
+ if stream_events:
2461
+ yield handle_event( # type: ignore
2462
+ create_team_session_summary_completed_event(
2463
+ from_run_response=run_response, session_summary=team_session.summary
2464
+ ),
2465
+ run_response,
2466
+ events_to_skip=self.events_to_skip,
2467
+ store_events=self.store_events,
2468
+ )
2469
+
2470
+ raise_if_cancelled(run_response.run_id) # type: ignore
2471
+
2472
+ # Create the run completed event
2473
+ completed_event = handle_event(
2474
+ create_team_run_completed_event(from_run_response=run_response),
2475
+ run_response,
2476
+ events_to_skip=self.events_to_skip,
2477
+ store_events=self.store_events,
2240
2478
  )
2241
2479
 
2242
- # 14. Scrub the stored run based on storage flags
2243
- if self._scrub_run_output_for_storage(run_response):
2244
- team_session.upsert_run(run_response=run_response)
2480
+ # Set the run status to completed
2481
+ run_response.status = RunStatus.completed
2245
2482
 
2246
- # 15. Save the session to storage
2247
- if self._has_async_db():
2248
- await self.asave_session(session=team_session)
2249
- else:
2250
- self.save_session(session=team_session)
2483
+ # 13. Cleanup and store the run response and session
2484
+ await self._acleanup_and_store(run_response=run_response, session=team_session)
2251
2485
 
2252
- if stream_intermediate_steps:
2486
+ if stream_events:
2253
2487
  yield completed_event
2254
2488
 
2255
2489
  if yield_run_response:
@@ -2267,18 +2501,24 @@ class Team:
2267
2501
  run_response.content = str(e)
2268
2502
 
2269
2503
  # Yield the cancellation event
2270
- yield self._handle_event(
2504
+ yield handle_event( # type: ignore
2271
2505
  create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
2272
2506
  run_response,
2507
+ events_to_skip=self.events_to_skip,
2508
+ store_events=self.store_events,
2273
2509
  )
2274
2510
 
2275
- # Add the RunOutput to Team Session even when cancelled
2276
- team_session.upsert_run(run_response=run_response)
2277
- if self._has_async_db():
2278
- await self.asave_session(session=team_session)
2279
- else:
2280
- self.save_session(session=team_session)
2511
+ # Cleanup and store the run response and session
2512
+ await self._acleanup_and_store(run_response=run_response, session=team_session)
2513
+
2281
2514
  finally:
2515
+ # Cancel the memory task if it's still running
2516
+ if memory_task is not None and not memory_task.done():
2517
+ memory_task.cancel()
2518
+ try:
2519
+ await memory_task
2520
+ except asyncio.CancelledError:
2521
+ pass
2282
2522
  # Always clean up the run tracking
2283
2523
  cleanup_run(run_response.run_id) # type: ignore
2284
2524
 
@@ -2288,6 +2528,7 @@ class Team:
2288
2528
  input: Union[str, List, Dict, Message, BaseModel],
2289
2529
  *,
2290
2530
  stream: Literal[False] = False,
2531
+ stream_events: Optional[bool] = None,
2291
2532
  stream_intermediate_steps: Optional[bool] = None,
2292
2533
  session_id: Optional[str] = None,
2293
2534
  session_state: Optional[Dict[str, Any]] = None,
@@ -2313,6 +2554,7 @@ class Team:
2313
2554
  input: Union[str, List, Dict, Message, BaseModel],
2314
2555
  *,
2315
2556
  stream: Literal[True] = True,
2557
+ stream_events: Optional[bool] = None,
2316
2558
  stream_intermediate_steps: Optional[bool] = None,
2317
2559
  session_id: Optional[str] = None,
2318
2560
  session_state: Optional[Dict[str, Any]] = None,
@@ -2338,6 +2580,7 @@ class Team:
2338
2580
  input: Union[str, List, Dict, Message, BaseModel],
2339
2581
  *,
2340
2582
  stream: Optional[bool] = None,
2583
+ stream_events: Optional[bool] = None,
2341
2584
  stream_intermediate_steps: Optional[bool] = None,
2342
2585
  session_id: Optional[str] = None,
2343
2586
  session_state: Optional[Dict[str, Any]] = None,
@@ -2359,6 +2602,11 @@ class Team:
2359
2602
  ) -> Union[TeamRunOutput, AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent]]]:
2360
2603
  """Run the Team asynchronously and return the response."""
2361
2604
 
2605
+ if (add_history_to_context or self.add_history_to_context) and not self.db and not self.parent_team_id:
2606
+ log_warning(
2607
+ "add_history_to_context is True, but no database has been assigned to the team. History will not be added to the context."
2608
+ )
2609
+
2362
2610
  # Create a run_id for this specific run
2363
2611
  run_id = str(uuid4())
2364
2612
 
@@ -2373,9 +2621,7 @@ class Team:
2373
2621
  self.post_hooks = normalize_hooks(self.post_hooks, async_mode=True)
2374
2622
  self._hooks_normalised = True
2375
2623
 
2376
- session_id, user_id, session_state = self._initialize_session(
2377
- run_id=run_id, session_id=session_id, user_id=user_id, session_state=session_state
2378
- )
2624
+ session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
2379
2625
 
2380
2626
  # Initialize Team
2381
2627
  self.initialize_team(debug_mode=debug_mode)
@@ -2409,17 +2655,18 @@ class Team:
2409
2655
  if stream is None:
2410
2656
  stream = False if self.stream is None else self.stream
2411
2657
 
2412
- if stream_intermediate_steps is None:
2413
- stream_intermediate_steps = (
2414
- False if self.stream_intermediate_steps is None else self.stream_intermediate_steps
2415
- )
2658
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
2659
+ stream_events = stream_events or stream_intermediate_steps
2416
2660
 
2417
- # Can't have stream_intermediate_steps if stream is False
2661
+ # Can't stream events if streaming is disabled
2418
2662
  if stream is False:
2419
- stream_intermediate_steps = False
2663
+ stream_events = False
2664
+
2665
+ if stream_events is None:
2666
+ stream_events = False if self.stream_events is None else self.stream_events
2420
2667
 
2421
2668
  self.stream = self.stream or stream
2422
- self.stream_intermediate_steps = self.stream_intermediate_steps or (stream_intermediate_steps and self.stream)
2669
+ self.stream_events = self.stream_events or stream_events
2423
2670
 
2424
2671
  # Configure the model for runs
2425
2672
  response_format: Optional[Union[Dict, Type[BaseModel]]] = (
@@ -2480,7 +2727,7 @@ class Team:
2480
2727
  metadata=metadata,
2481
2728
  response_format=response_format,
2482
2729
  dependencies=run_dependencies,
2483
- stream_intermediate_steps=stream_intermediate_steps,
2730
+ stream_events=stream_events,
2484
2731
  yield_run_response=yield_run_response,
2485
2732
  debug_mode=debug_mode,
2486
2733
  **kwargs,
@@ -2493,10 +2740,6 @@ class Team:
2493
2740
  session_id=session_id,
2494
2741
  session_state=session_state,
2495
2742
  user_id=user_id,
2496
- audio=audio,
2497
- images=images,
2498
- videos=videos,
2499
- files=files,
2500
2743
  knowledge_filters=effective_filters,
2501
2744
  add_history_to_context=add_history,
2502
2745
  add_dependencies_to_context=add_dependencies,
@@ -2631,7 +2874,7 @@ class Team:
2631
2874
  run_response: TeamRunOutput,
2632
2875
  run_messages: RunMessages,
2633
2876
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2634
- stream_intermediate_steps: bool = False,
2877
+ stream_events: bool = False,
2635
2878
  ) -> Iterator[Union[TeamRunOutputEvent, RunOutputEvent]]:
2636
2879
  self.model = cast(Model, self.model)
2637
2880
 
@@ -2662,7 +2905,7 @@ class Team:
2662
2905
  full_model_response=full_model_response,
2663
2906
  model_response_event=model_response_event,
2664
2907
  reasoning_state=reasoning_state,
2665
- stream_intermediate_steps=stream_intermediate_steps,
2908
+ stream_events=stream_events,
2666
2909
  parse_structured_output=self.should_parse_structured_output,
2667
2910
  )
2668
2911
 
@@ -2678,20 +2921,22 @@ class Team:
2678
2921
  if full_model_response.provider_data is not None:
2679
2922
  run_response.model_provider_data = full_model_response.provider_data
2680
2923
 
2681
- if stream_intermediate_steps and reasoning_state["reasoning_started"]:
2924
+ if stream_events and reasoning_state["reasoning_started"]:
2682
2925
  all_reasoning_steps: List[ReasoningStep] = []
2683
2926
  if run_response.reasoning_steps:
2684
2927
  all_reasoning_steps = cast(List[ReasoningStep], run_response.reasoning_steps)
2685
2928
 
2686
2929
  if all_reasoning_steps:
2687
2930
  add_reasoning_metrics_to_metadata(run_response, reasoning_state["reasoning_time_taken"])
2688
- yield self._handle_event(
2931
+ yield handle_event( # type: ignore
2689
2932
  create_team_reasoning_completed_event(
2690
2933
  from_run_response=run_response,
2691
2934
  content=ReasoningSteps(reasoning_steps=all_reasoning_steps),
2692
2935
  content_type=ReasoningSteps.__name__,
2693
2936
  ),
2694
2937
  run_response,
2938
+ events_to_skip=self.events_to_skip,
2939
+ store_events=self.store_events,
2695
2940
  )
2696
2941
 
2697
2942
  # Build a list of messages that should be added to the RunOutput
@@ -2711,7 +2956,7 @@ class Team:
2711
2956
  run_response: TeamRunOutput,
2712
2957
  run_messages: RunMessages,
2713
2958
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2714
- stream_intermediate_steps: bool = False,
2959
+ stream_events: bool = False,
2715
2960
  ) -> AsyncIterator[Union[TeamRunOutputEvent, RunOutputEvent]]:
2716
2961
  self.model = cast(Model, self.model)
2717
2962
 
@@ -2743,7 +2988,7 @@ class Team:
2743
2988
  full_model_response=full_model_response,
2744
2989
  model_response_event=model_response_event,
2745
2990
  reasoning_state=reasoning_state,
2746
- stream_intermediate_steps=stream_intermediate_steps,
2991
+ stream_events=stream_events,
2747
2992
  parse_structured_output=self.should_parse_structured_output,
2748
2993
  ):
2749
2994
  yield event
@@ -2772,20 +3017,22 @@ class Team:
2772
3017
  # Update the TeamRunOutput metrics
2773
3018
  run_response.metrics = self._calculate_metrics(messages_for_run_response)
2774
3019
 
2775
- if stream_intermediate_steps and reasoning_state["reasoning_started"]:
3020
+ if stream_events and reasoning_state["reasoning_started"]:
2776
3021
  all_reasoning_steps: List[ReasoningStep] = []
2777
3022
  if run_response.reasoning_steps:
2778
3023
  all_reasoning_steps = cast(List[ReasoningStep], run_response.reasoning_steps)
2779
3024
 
2780
3025
  if all_reasoning_steps:
2781
3026
  add_reasoning_metrics_to_metadata(run_response, reasoning_state["reasoning_time_taken"])
2782
- yield self._handle_event(
3027
+ yield handle_event( # type: ignore
2783
3028
  create_team_reasoning_completed_event(
2784
3029
  from_run_response=run_response,
2785
3030
  content=ReasoningSteps(reasoning_steps=all_reasoning_steps),
2786
3031
  content_type=ReasoningSteps.__name__,
2787
3032
  ),
2788
3033
  run_response,
3034
+ events_to_skip=self.events_to_skip,
3035
+ store_events=self.store_events,
2789
3036
  )
2790
3037
 
2791
3038
  def _handle_model_response_chunk(
@@ -2795,7 +3042,7 @@ class Team:
2795
3042
  full_model_response: ModelResponse,
2796
3043
  model_response_event: Union[ModelResponse, TeamRunOutputEvent, RunOutputEvent],
2797
3044
  reasoning_state: Optional[Dict[str, Any]] = None,
2798
- stream_intermediate_steps: bool = False,
3045
+ stream_events: bool = False,
2799
3046
  parse_structured_output: bool = False,
2800
3047
  ) -> Iterator[Union[TeamRunOutputEvent, RunOutputEvent]]:
2801
3048
  if isinstance(model_response_event, tuple(get_args(RunOutputEvent))) or isinstance(
@@ -2812,7 +3059,12 @@ class Team:
2812
3059
  if not model_response_event.run_id: # type: ignore
2813
3060
  model_response_event.run_id = run_response.run_id # type: ignore
2814
3061
  # We just bubble the event up
2815
- yield self._handle_event(model_response_event, run_response) # type: ignore
3062
+ yield handle_event( # type: ignore
3063
+ model_response_event, # type: ignore
3064
+ run_response,
3065
+ events_to_skip=self.events_to_skip,
3066
+ store_events=self.store_events,
3067
+ ) # type: ignore
2816
3068
  else:
2817
3069
  # Don't yield anything
2818
3070
  return
@@ -2918,7 +3170,7 @@ class Team:
2918
3170
  # Only yield the chunk
2919
3171
  if should_yield:
2920
3172
  if content_type == "str":
2921
- yield self._handle_event(
3173
+ yield handle_event( # type: ignore
2922
3174
  create_team_run_output_content_event(
2923
3175
  from_run_response=run_response,
2924
3176
  content=model_response_event.content,
@@ -2930,15 +3182,19 @@ class Team:
2930
3182
  image=model_response_event.images[-1] if model_response_event.images else None,
2931
3183
  ),
2932
3184
  run_response,
3185
+ events_to_skip=self.events_to_skip,
3186
+ store_events=self.store_events,
2933
3187
  )
2934
3188
  else:
2935
- yield self._handle_event(
3189
+ yield handle_event( # type: ignore
2936
3190
  create_team_run_output_content_event(
2937
3191
  from_run_response=run_response,
2938
3192
  content=full_model_response.content,
2939
3193
  content_type=content_type,
2940
3194
  ),
2941
3195
  run_response,
3196
+ events_to_skip=self.events_to_skip,
3197
+ store_events=self.store_events,
2942
3198
  )
2943
3199
 
2944
3200
  # If the model response is a tool_call_started, add the tool call to the run_response
@@ -2953,12 +3209,14 @@ class Team:
2953
3209
  run_response.tools.extend(tool_executions_list)
2954
3210
 
2955
3211
  for tool in tool_executions_list:
2956
- yield self._handle_event(
3212
+ yield handle_event( # type: ignore
2957
3213
  create_team_tool_call_started_event(
2958
3214
  from_run_response=run_response,
2959
3215
  tool=tool,
2960
3216
  ),
2961
3217
  run_response,
3218
+ events_to_skip=self.events_to_skip,
3219
+ store_events=self.store_events,
2962
3220
  )
2963
3221
 
2964
3222
  # If the model response is a tool_call_completed, update the existing tool call in the run_response
@@ -3016,33 +3274,39 @@ class Team:
3016
3274
  "reasoning_time_taken"
3017
3275
  ] + float(metrics.duration)
3018
3276
 
3019
- yield self._handle_event(
3277
+ yield handle_event( # type: ignore
3020
3278
  create_team_tool_call_completed_event(
3021
3279
  from_run_response=run_response,
3022
3280
  tool=tool_call,
3023
3281
  content=model_response_event.content,
3024
3282
  ),
3025
3283
  run_response,
3284
+ events_to_skip=self.events_to_skip,
3285
+ store_events=self.store_events,
3026
3286
  )
3027
3287
 
3028
- if stream_intermediate_steps:
3288
+ if stream_events:
3029
3289
  if reasoning_step is not None:
3030
3290
  if reasoning_state is not None and not reasoning_state["reasoning_started"]:
3031
- yield self._handle_event(
3291
+ yield handle_event( # type: ignore
3032
3292
  create_team_reasoning_started_event(
3033
3293
  from_run_response=run_response,
3034
3294
  ),
3035
3295
  run_response,
3296
+ events_to_skip=self.events_to_skip,
3297
+ store_events=self.store_events,
3036
3298
  )
3037
3299
  reasoning_state["reasoning_started"] = True
3038
3300
 
3039
- yield self._handle_event(
3301
+ yield handle_event( # type: ignore
3040
3302
  create_team_reasoning_step_event(
3041
3303
  from_run_response=run_response,
3042
3304
  reasoning_step=reasoning_step,
3043
3305
  reasoning_content=run_response.reasoning_content or "",
3044
3306
  ),
3045
3307
  run_response,
3308
+ events_to_skip=self.events_to_skip,
3309
+ store_events=self.store_events,
3046
3310
  )
3047
3311
 
3048
3312
  def _convert_response_to_structured_format(self, run_response: Union[TeamRunOutput, RunOutput, ModelResponse]):
@@ -3083,97 +3347,71 @@ class Team:
3083
3347
  else:
3084
3348
  log_warning("Something went wrong. Member run response content is not a string")
3085
3349
 
3086
- def _make_memories_and_summaries(
3087
- self,
3088
- run_response: TeamRunOutput,
3089
- run_messages: RunMessages,
3090
- session: TeamSession,
3091
- user_id: Optional[str] = None,
3092
- ) -> Iterator[TeamRunOutputEvent]:
3093
- from concurrent.futures import ThreadPoolExecutor, as_completed
3350
+ def _cleanup_and_store(self, run_response: TeamRunOutput, session: TeamSession) -> None:
3351
+ # Scrub the stored run based on storage flags
3352
+ self._scrub_run_output_for_storage(run_response)
3094
3353
 
3095
- # Create a thread pool with a reasonable number of workers
3096
- with ThreadPoolExecutor(max_workers=3) as executor:
3097
- futures = []
3098
- user_message_str = (
3099
- run_messages.user_message.get_content_string() if run_messages.user_message is not None else None
3100
- )
3101
- # Create user memories
3102
- if user_message_str is not None and self.memory_manager is not None and not self.enable_agentic_memory:
3103
- futures.append(
3104
- executor.submit(
3105
- self.memory_manager.create_user_memories,
3106
- message=user_message_str,
3107
- user_id=user_id,
3108
- team_id=self.id,
3109
- )
3110
- )
3354
+ # Stop the timer for the Run duration
3355
+ if run_response.metrics:
3356
+ run_response.metrics.stop_timer()
3111
3357
 
3112
- # Create session summary
3113
- if self.session_summary_manager is not None:
3114
- log_debug("Creating session summary.")
3115
- futures.append(
3116
- executor.submit(
3117
- self.session_summary_manager.create_session_summary, # type: ignore
3118
- session=session,
3119
- )
3120
- )
3358
+ # Add RunOutput to Agent Session
3359
+ session.upsert_run(run_response=run_response)
3121
3360
 
3122
- if futures:
3123
- if self.stream_intermediate_steps:
3124
- yield self._handle_event(
3125
- create_team_memory_update_started_event(from_run_response=run_response), run_response
3126
- )
3361
+ # Calculate session metrics
3362
+ self._update_session_metrics(session=session)
3127
3363
 
3128
- # Wait for all operations to complete and handle any errors
3129
- for future in as_completed(futures):
3130
- try:
3131
- future.result()
3132
- except Exception as e:
3133
- log_warning(f"Error in memory/summary operation: {str(e)}")
3364
+ # Save session to memory
3365
+ self.save_session(session=session)
3366
+
3367
+ async def _acleanup_and_store(self, run_response: TeamRunOutput, session: TeamSession) -> None:
3368
+ # Scrub the stored run based on storage flags
3369
+ self._scrub_run_output_for_storage(run_response)
3370
+
3371
+ # Stop the timer for the Run duration
3372
+ if run_response.metrics:
3373
+ run_response.metrics.stop_timer()
3374
+
3375
+ # Add RunOutput to Agent Session
3376
+ session.upsert_run(run_response=run_response)
3377
+
3378
+ # Calculate session metrics
3379
+ self._update_session_metrics(session=session)
3134
3380
 
3135
- if self.stream_intermediate_steps:
3136
- yield self._handle_event(
3137
- create_team_memory_update_completed_event(from_run_response=run_response),
3138
- run_response,
3139
- )
3381
+ # Save session to memory
3382
+ self.save_session(session=session)
3140
3383
 
3141
- async def _amake_memories_and_summaries(
3384
+ def _make_memories(
3142
3385
  self,
3143
- run_response: TeamRunOutput,
3144
3386
  run_messages: RunMessages,
3145
- session: TeamSession,
3146
3387
  user_id: Optional[str] = None,
3147
- ) -> AsyncIterator[TeamRunOutputEvent]:
3148
- tasks: List[Coroutine] = []
3149
-
3388
+ ):
3150
3389
  user_message_str = (
3151
3390
  run_messages.user_message.get_content_string() if run_messages.user_message is not None else None
3152
3391
  )
3153
- if user_message_str is not None and self.memory_manager is not None and not self.enable_agentic_memory:
3154
- tasks.append(
3155
- self.memory_manager.acreate_user_memories(message=user_message_str, user_id=user_id, team_id=self.id)
3392
+ if user_message_str is not None and user_message_str.strip() != "" and self.memory_manager is not None:
3393
+ log_debug("Creating user memories.")
3394
+ self.memory_manager.create_user_memories(
3395
+ message=user_message_str,
3396
+ user_id=user_id,
3397
+ team_id=self.id,
3156
3398
  )
3157
3399
 
3158
- if self.session_summary_manager is not None:
3159
- tasks.append(self.session_summary_manager.acreate_session_summary(session=session))
3160
-
3161
- if tasks:
3162
- if self.stream_intermediate_steps:
3163
- yield self._handle_event(
3164
- create_team_memory_update_started_event(from_run_response=run_response), run_response
3165
- )
3166
-
3167
- # Execute all tasks concurrently and handle any errors
3168
- try:
3169
- await asyncio.gather(*tasks)
3170
- except Exception as e:
3171
- log_warning(f"Error in memory/summary operation: {str(e)}")
3172
-
3173
- if self.stream_intermediate_steps:
3174
- yield self._handle_event(
3175
- create_team_memory_update_completed_event(from_run_response=run_response), run_response
3176
- )
3400
+ async def _amake_memories(
3401
+ self,
3402
+ run_messages: RunMessages,
3403
+ user_id: Optional[str] = None,
3404
+ ):
3405
+ user_message_str = (
3406
+ run_messages.user_message.get_content_string() if run_messages.user_message is not None else None
3407
+ )
3408
+ if user_message_str is not None and user_message_str.strip() != "" and self.memory_manager is not None:
3409
+ log_debug("Creating user memories.")
3410
+ await self.memory_manager.acreate_user_memories(
3411
+ message=user_message_str,
3412
+ user_id=user_id,
3413
+ team_id=self.id,
3414
+ )
3177
3415
 
3178
3416
  def _get_response_format(self, model: Optional[Model] = None) -> Optional[Union[Dict, Type[BaseModel]]]:
3179
3417
  model = cast(Model, model or self.model)
@@ -3269,14 +3507,20 @@ class Team:
3269
3507
  log_warning("A response model is required to parse the response with a parser model")
3270
3508
 
3271
3509
  def _parse_response_with_parser_model_stream(
3272
- self, session: TeamSession, run_response: TeamRunOutput, stream_intermediate_steps: bool = True
3510
+ self,
3511
+ session: TeamSession,
3512
+ run_response: TeamRunOutput,
3513
+ stream_events: bool = False,
3273
3514
  ):
3274
3515
  """Parse the model response using the parser model"""
3275
3516
  if self.parser_model is not None:
3276
3517
  if self.output_schema is not None:
3277
- if stream_intermediate_steps:
3278
- yield self._handle_event(
3279
- create_team_parser_model_response_started_event(run_response), run_response
3518
+ if stream_events:
3519
+ yield handle_event( # type: ignore
3520
+ create_team_parser_model_response_started_event(run_response),
3521
+ run_response,
3522
+ events_to_skip=self.events_to_skip,
3523
+ store_events=self.store_events,
3280
3524
  )
3281
3525
 
3282
3526
  parser_model_response = ModelResponse(content="")
@@ -3295,7 +3539,7 @@ class Team:
3295
3539
  full_model_response=parser_model_response,
3296
3540
  model_response_event=model_response_event,
3297
3541
  parse_structured_output=True,
3298
- stream_intermediate_steps=stream_intermediate_steps,
3542
+ stream_events=stream_events,
3299
3543
  )
3300
3544
 
3301
3545
  run_response.content = parser_model_response.content
@@ -3311,23 +3555,29 @@ class Team:
3311
3555
  else:
3312
3556
  log_warning("Unable to parse response with parser model")
3313
3557
 
3314
- if stream_intermediate_steps:
3315
- yield self._handle_event(
3316
- create_team_parser_model_response_completed_event(run_response), run_response
3558
+ if stream_events:
3559
+ yield handle_event( # type: ignore
3560
+ create_team_parser_model_response_completed_event(run_response),
3561
+ run_response,
3562
+ events_to_skip=self.events_to_skip,
3563
+ store_events=self.store_events,
3317
3564
  )
3318
3565
 
3319
3566
  else:
3320
3567
  log_warning("A response model is required to parse the response with a parser model")
3321
3568
 
3322
3569
  async def _aparse_response_with_parser_model_stream(
3323
- self, session: TeamSession, run_response: TeamRunOutput, stream_intermediate_steps: bool = True
3570
+ self, session: TeamSession, run_response: TeamRunOutput, stream_events: bool = False
3324
3571
  ):
3325
3572
  """Parse the model response using the parser model stream."""
3326
3573
  if self.parser_model is not None:
3327
3574
  if self.output_schema is not None:
3328
- if stream_intermediate_steps:
3329
- yield self._handle_event(
3330
- create_team_parser_model_response_started_event(run_response), run_response
3575
+ if stream_events:
3576
+ yield handle_event( # type: ignore
3577
+ create_team_parser_model_response_started_event(run_response),
3578
+ run_response,
3579
+ events_to_skip=self.events_to_skip,
3580
+ store_events=self.store_events,
3331
3581
  )
3332
3582
 
3333
3583
  parser_model_response = ModelResponse(content="")
@@ -3347,7 +3597,7 @@ class Team:
3347
3597
  full_model_response=parser_model_response,
3348
3598
  model_response_event=model_response_event,
3349
3599
  parse_structured_output=True,
3350
- stream_intermediate_steps=stream_intermediate_steps,
3600
+ stream_events=stream_events,
3351
3601
  ):
3352
3602
  yield event
3353
3603
 
@@ -3364,9 +3614,12 @@ class Team:
3364
3614
  else:
3365
3615
  log_warning("Unable to parse response with parser model")
3366
3616
 
3367
- if stream_intermediate_steps:
3368
- yield self._handle_event(
3369
- create_team_parser_model_response_completed_event(run_response), run_response
3617
+ if stream_events:
3618
+ yield handle_event( # type: ignore
3619
+ create_team_parser_model_response_completed_event(run_response),
3620
+ run_response,
3621
+ events_to_skip=self.events_to_skip,
3622
+ store_events=self.store_events,
3370
3623
  )
3371
3624
  else:
3372
3625
  log_warning("A response model is required to parse the response with a parser model")
@@ -3385,7 +3638,7 @@ class Team:
3385
3638
  session: TeamSession,
3386
3639
  run_response: TeamRunOutput,
3387
3640
  run_messages: RunMessages,
3388
- stream_intermediate_steps: bool = False,
3641
+ stream_events: bool = False,
3389
3642
  ):
3390
3643
  """Parse the model response using the output model stream."""
3391
3644
  from agno.utils.events import (
@@ -3396,8 +3649,13 @@ class Team:
3396
3649
  if self.output_model is None:
3397
3650
  return
3398
3651
 
3399
- if stream_intermediate_steps:
3400
- yield self._handle_event(create_team_output_model_response_started_event(run_response), run_response)
3652
+ if stream_events:
3653
+ yield handle_event( # type: ignore
3654
+ create_team_output_model_response_started_event(run_response),
3655
+ run_response,
3656
+ events_to_skip=self.events_to_skip,
3657
+ store_events=self.store_events,
3658
+ )
3401
3659
 
3402
3660
  messages_for_output_model = self._get_messages_for_output_model(run_messages.messages)
3403
3661
  model_response = ModelResponse(content="")
@@ -3413,8 +3671,13 @@ class Team:
3413
3671
  # Update the TeamRunResponse content
3414
3672
  run_response.content = model_response.content
3415
3673
 
3416
- if stream_intermediate_steps:
3417
- yield self._handle_event(create_team_output_model_response_completed_event(run_response), run_response)
3674
+ if stream_events:
3675
+ yield handle_event( # type: ignore
3676
+ create_team_output_model_response_completed_event(run_response),
3677
+ run_response,
3678
+ events_to_skip=self.events_to_skip,
3679
+ store_events=self.store_events,
3680
+ )
3418
3681
 
3419
3682
  # Build a list of messages that should be added to the RunResponse
3420
3683
  messages_for_run_response = [m for m in run_messages.messages if m.add_to_agent_memory]
@@ -3439,7 +3702,7 @@ class Team:
3439
3702
  session: TeamSession,
3440
3703
  run_response: TeamRunOutput,
3441
3704
  run_messages: RunMessages,
3442
- stream_intermediate_steps: bool = False,
3705
+ stream_events: bool = False,
3443
3706
  ):
3444
3707
  """Parse the model response using the output model stream."""
3445
3708
  from agno.utils.events import (
@@ -3450,8 +3713,13 @@ class Team:
3450
3713
  if self.output_model is None:
3451
3714
  return
3452
3715
 
3453
- if stream_intermediate_steps:
3454
- yield self._handle_event(create_team_output_model_response_started_event(run_response), run_response)
3716
+ if stream_events:
3717
+ yield handle_event( # type: ignore
3718
+ create_team_output_model_response_started_event(run_response),
3719
+ run_response,
3720
+ events_to_skip=self.events_to_skip,
3721
+ store_events=self.store_events,
3722
+ )
3455
3723
 
3456
3724
  messages_for_output_model = self._get_messages_for_output_model(run_messages.messages)
3457
3725
  model_response = ModelResponse(content="")
@@ -3468,8 +3736,13 @@ class Team:
3468
3736
  # Update the TeamRunResponse content
3469
3737
  run_response.content = model_response.content
3470
3738
 
3471
- if stream_intermediate_steps:
3472
- yield self._handle_event(create_team_output_model_response_completed_event(run_response), run_response)
3739
+ if stream_events:
3740
+ yield handle_event( # type: ignore
3741
+ create_team_output_model_response_completed_event(run_response),
3742
+ run_response,
3743
+ events_to_skip=self.events_to_skip,
3744
+ store_events=self.store_events,
3745
+ )
3473
3746
 
3474
3747
  # Build a list of messages that should be added to the RunResponse
3475
3748
  messages_for_run_response = [m for m in run_messages.messages if m.add_to_agent_memory]
@@ -3500,6 +3773,7 @@ class Team:
3500
3773
  input: Union[List, Dict, str, Message, BaseModel, List[Message]],
3501
3774
  *,
3502
3775
  stream: Optional[bool] = None,
3776
+ stream_events: Optional[bool] = None,
3503
3777
  stream_intermediate_steps: Optional[bool] = None,
3504
3778
  session_id: Optional[str] = None,
3505
3779
  session_state: Optional[Dict[str, Any]] = None,
@@ -3540,8 +3814,15 @@ class Team:
3540
3814
  if stream is None:
3541
3815
  stream = self.stream or False
3542
3816
 
3543
- if stream_intermediate_steps is None:
3544
- stream_intermediate_steps = self.stream_intermediate_steps or False
3817
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
3818
+ stream_events = stream_events or stream_intermediate_steps
3819
+
3820
+ # Can't stream events if streaming is disabled
3821
+ if stream is False:
3822
+ stream_events = False
3823
+
3824
+ if stream_events is None:
3825
+ stream_events = False if self.stream_events is None else self.stream_events
3545
3826
 
3546
3827
  if stream:
3547
3828
  print_response_stream(
@@ -3560,7 +3841,7 @@ class Team:
3560
3841
  videos=videos,
3561
3842
  files=files,
3562
3843
  markdown=markdown,
3563
- stream_intermediate_steps=stream_intermediate_steps,
3844
+ stream_events=stream_events,
3564
3845
  knowledge_filters=knowledge_filters,
3565
3846
  add_history_to_context=add_history_to_context,
3566
3847
  dependencies=dependencies,
@@ -3602,6 +3883,7 @@ class Team:
3602
3883
  input: Union[List, Dict, str, Message, BaseModel, List[Message]],
3603
3884
  *,
3604
3885
  stream: Optional[bool] = None,
3886
+ stream_events: Optional[bool] = None,
3605
3887
  stream_intermediate_steps: Optional[bool] = None,
3606
3888
  session_id: Optional[str] = None,
3607
3889
  session_state: Optional[Dict[str, Any]] = None,
@@ -3637,8 +3919,15 @@ class Team:
3637
3919
  if stream is None:
3638
3920
  stream = self.stream or False
3639
3921
 
3640
- if stream_intermediate_steps is None:
3641
- stream_intermediate_steps = self.stream_intermediate_steps or False
3922
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
3923
+ stream_events = stream_events or stream_intermediate_steps
3924
+
3925
+ # Can't stream events if streaming is disabled
3926
+ if stream is False:
3927
+ stream_events = False
3928
+
3929
+ if stream_events is None:
3930
+ stream_events = False if self.stream_events is None else self.stream_events
3642
3931
 
3643
3932
  if stream:
3644
3933
  await aprint_response_stream(
@@ -3657,7 +3946,7 @@ class Team:
3657
3946
  videos=videos,
3658
3947
  files=files,
3659
3948
  markdown=markdown,
3660
- stream_intermediate_steps=stream_intermediate_steps,
3949
+ stream_events=stream_events,
3661
3950
  knowledge_filters=knowledge_filters,
3662
3951
  add_history_to_context=add_history_to_context,
3663
3952
  dependencies=dependencies,
@@ -3704,88 +3993,6 @@ class Team:
3704
3993
  return member.name or entity_id
3705
3994
  return entity_id
3706
3995
 
3707
- def _scrub_media_from_run_output(self, run_response: TeamRunOutput) -> None:
3708
- """
3709
- Completely remove all media from RunOutput when store_media=False.
3710
- This includes media in input, output artifacts, and all messages.
3711
- """
3712
- # 1. Scrub RunInput media
3713
- if run_response.input is not None:
3714
- run_response.input.images = []
3715
- run_response.input.videos = []
3716
- run_response.input.audios = []
3717
- run_response.input.files = []
3718
-
3719
- # 3. Scrub media from all messages
3720
- if run_response.messages:
3721
- for message in run_response.messages:
3722
- self._scrub_media_from_message(message)
3723
-
3724
- # 4. Scrub media from additional_input messages if any
3725
- if run_response.additional_input:
3726
- for message in run_response.additional_input:
3727
- self._scrub_media_from_message(message)
3728
-
3729
- # 5. Scrub media from reasoning_messages if any
3730
- if run_response.reasoning_messages:
3731
- for message in run_response.reasoning_messages:
3732
- self._scrub_media_from_message(message)
3733
-
3734
- def _scrub_media_from_message(self, message: Message) -> None:
3735
- """Remove all media from a Message object."""
3736
- # Input media
3737
- message.images = None
3738
- message.videos = None
3739
- message.audio = None
3740
- message.files = None
3741
-
3742
- # Output media
3743
- message.audio_output = None
3744
- message.image_output = None
3745
- message.video_output = None
3746
-
3747
- def _scrub_tool_results_from_run_output(self, run_response: TeamRunOutput) -> None:
3748
- """
3749
- Remove all tool-related data from RunOutput when store_tool_messages=False.
3750
- This removes both the tool call and its corresponding result to maintain API consistency.
3751
- """
3752
- if not run_response.messages:
3753
- return
3754
-
3755
- # Step 1: Collect all tool_call_ids from tool result messages
3756
- tool_call_ids_to_remove = set()
3757
- for message in run_response.messages:
3758
- if message.role == "tool" and message.tool_call_id:
3759
- tool_call_ids_to_remove.add(message.tool_call_id)
3760
-
3761
- # Step 2: Remove tool result messages (role="tool")
3762
- run_response.messages = [msg for msg in run_response.messages if msg.role != "tool"]
3763
-
3764
- # Step 3: Remove assistant messages that made those tool calls
3765
- filtered_messages = []
3766
- for message in run_response.messages:
3767
- # Check if this assistant message made any of the tool calls we're removing
3768
- should_remove = False
3769
- if message.role == "assistant" and message.tool_calls:
3770
- for tool_call in message.tool_calls:
3771
- if tool_call.get("id") in tool_call_ids_to_remove:
3772
- should_remove = True
3773
- break
3774
-
3775
- if not should_remove:
3776
- filtered_messages.append(message)
3777
-
3778
- run_response.messages = filtered_messages
3779
-
3780
- def _scrub_history_messages_from_run_output(self, run_response: TeamRunOutput) -> None:
3781
- """
3782
- Remove all history messages from TeamRunOutput when store_history_messages=False.
3783
- This removes messages that were loaded from the team's memory.
3784
- """
3785
- # Remove messages with from_history=True
3786
- if run_response.messages:
3787
- run_response.messages = [msg for msg in run_response.messages if not msg.from_history]
3788
-
3789
3996
  def _scrub_run_output_for_storage(self, run_response: TeamRunOutput) -> bool:
3790
3997
  """
3791
3998
  Scrub run output based on storage flags before persisting to database.
@@ -3794,15 +4001,15 @@ class Team:
3794
4001
  scrubbed = False
3795
4002
 
3796
4003
  if not self.store_media:
3797
- self._scrub_media_from_run_output(run_response)
4004
+ scrub_media_from_run_output(run_response)
3798
4005
  scrubbed = True
3799
4006
 
3800
4007
  if not self.store_tool_messages:
3801
- self._scrub_tool_results_from_run_output(run_response)
4008
+ scrub_tool_results_from_run_output(run_response)
3802
4009
  scrubbed = True
3803
4010
 
3804
4011
  if not self.store_history_messages:
3805
- self._scrub_history_messages_from_run_output(run_response)
4012
+ scrub_history_messages_from_run_output(run_response)
3806
4013
  scrubbed = True
3807
4014
 
3808
4015
  return scrubbed
@@ -3959,32 +4166,42 @@ class Team:
3959
4166
  # Helpers
3960
4167
  ###########################################################################
3961
4168
 
3962
- def _handle_reasoning(self, run_response: TeamRunOutput, run_messages: RunMessages) -> None:
4169
+ def _handle_reasoning(self, run_response: TeamRunOutput, run_messages: RunMessages):
3963
4170
  if self.reasoning or self.reasoning_model is not None:
3964
- reasoning_generator = self._reason(run_response=run_response, run_messages=run_messages)
4171
+ reasoning_generator = self._reason(
4172
+ run_response=run_response, run_messages=run_messages, stream_events=False
4173
+ )
3965
4174
 
3966
4175
  # Consume the generator without yielding
3967
4176
  deque(reasoning_generator, maxlen=0)
3968
4177
 
3969
4178
  def _handle_reasoning_stream(
3970
- self, run_response: TeamRunOutput, run_messages: RunMessages
4179
+ self, run_response: TeamRunOutput, run_messages: RunMessages, stream_events: bool
3971
4180
  ) -> Iterator[TeamRunOutputEvent]:
3972
4181
  if self.reasoning or self.reasoning_model is not None:
3973
- reasoning_generator = self._reason(run_response=run_response, run_messages=run_messages)
4182
+ reasoning_generator = self._reason(
4183
+ run_response=run_response,
4184
+ run_messages=run_messages,
4185
+ stream_events=stream_events,
4186
+ )
3974
4187
  yield from reasoning_generator
3975
4188
 
3976
4189
  async def _ahandle_reasoning(self, run_response: TeamRunOutput, run_messages: RunMessages) -> None:
3977
4190
  if self.reasoning or self.reasoning_model is not None:
3978
- reason_generator = self._areason(run_response=run_response, run_messages=run_messages)
4191
+ reason_generator = self._areason(run_response=run_response, run_messages=run_messages, stream_events=False)
3979
4192
  # Consume the generator without yielding
3980
4193
  async for _ in reason_generator:
3981
4194
  pass
3982
4195
 
3983
4196
  async def _ahandle_reasoning_stream(
3984
- self, run_response: TeamRunOutput, run_messages: RunMessages
4197
+ self, run_response: TeamRunOutput, run_messages: RunMessages, stream_events: bool
3985
4198
  ) -> AsyncIterator[TeamRunOutputEvent]:
3986
4199
  if self.reasoning or self.reasoning_model is not None:
3987
- reason_generator = self._areason(run_response=run_response, run_messages=run_messages)
4200
+ reason_generator = self._areason(
4201
+ run_response=run_response,
4202
+ run_messages=run_messages,
4203
+ stream_events=stream_events,
4204
+ )
3988
4205
  async for item in reason_generator:
3989
4206
  yield item
3990
4207
 
@@ -4062,9 +4279,15 @@ class Team:
4062
4279
  self,
4063
4280
  run_response: TeamRunOutput,
4064
4281
  run_messages: RunMessages,
4282
+ stream_events: bool,
4065
4283
  ) -> Iterator[TeamRunOutputEvent]:
4066
- if self.stream_intermediate_steps:
4067
- yield self._handle_event(create_team_reasoning_started_event(from_run_response=run_response), run_response)
4284
+ if stream_events:
4285
+ yield handle_event( # type: ignore
4286
+ create_team_reasoning_started_event(from_run_response=run_response),
4287
+ run_response,
4288
+ events_to_skip=self.events_to_skip,
4289
+ store_events=self.store_events,
4290
+ )
4068
4291
 
4069
4292
  use_default_reasoning = False
4070
4293
 
@@ -4185,14 +4408,16 @@ class Team:
4185
4408
  reasoning_steps=[ReasoningStep(result=reasoning_message.content)],
4186
4409
  reasoning_agent_messages=[reasoning_message],
4187
4410
  )
4188
- if self.stream_intermediate_steps:
4189
- yield self._handle_event(
4411
+ if stream_events:
4412
+ yield handle_event( # type: ignore
4190
4413
  create_team_reasoning_completed_event(
4191
4414
  from_run_response=run_response,
4192
4415
  content=ReasoningSteps(reasoning_steps=[ReasoningStep(result=reasoning_message.content)]),
4193
4416
  content_type=ReasoningSteps.__name__,
4194
4417
  ),
4195
4418
  run_response,
4419
+ events_to_skip=self.events_to_skip,
4420
+ store_events=self.store_events,
4196
4421
  )
4197
4422
  else:
4198
4423
  log_warning(
@@ -4270,19 +4495,21 @@ class Team:
4270
4495
  reasoning_steps: List[ReasoningStep] = reasoning_agent_response.content.reasoning_steps
4271
4496
  all_reasoning_steps.extend(reasoning_steps)
4272
4497
  # Yield reasoning steps
4273
- if self.stream_intermediate_steps:
4498
+ if stream_events:
4274
4499
  for reasoning_step in reasoning_steps:
4275
4500
  updated_reasoning_content = self._format_reasoning_step_content(
4276
4501
  run_response, reasoning_step
4277
4502
  )
4278
4503
 
4279
- yield self._handle_event(
4504
+ yield handle_event( # type: ignore
4280
4505
  create_team_reasoning_step_event(
4281
4506
  from_run_response=run_response,
4282
4507
  reasoning_step=reasoning_step,
4283
4508
  reasoning_content=updated_reasoning_content,
4284
4509
  ),
4285
4510
  run_response,
4511
+ events_to_skip=self.events_to_skip,
4512
+ store_events=self.store_events,
4286
4513
  )
4287
4514
 
4288
4515
  # Find the index of the first assistant message
@@ -4318,23 +4545,31 @@ class Team:
4318
4545
  )
4319
4546
 
4320
4547
  # Yield the final reasoning completed event
4321
- if self.stream_intermediate_steps:
4322
- yield self._handle_event(
4548
+ if stream_events:
4549
+ yield handle_event( # type: ignore
4323
4550
  create_team_reasoning_completed_event(
4324
4551
  from_run_response=run_response,
4325
4552
  content=ReasoningSteps(reasoning_steps=all_reasoning_steps),
4326
4553
  content_type=ReasoningSteps.__name__,
4327
4554
  ),
4328
4555
  run_response,
4556
+ events_to_skip=self.events_to_skip,
4557
+ store_events=self.store_events,
4329
4558
  )
4330
4559
 
4331
4560
  async def _areason(
4332
4561
  self,
4333
4562
  run_response: TeamRunOutput,
4334
4563
  run_messages: RunMessages,
4564
+ stream_events: bool,
4335
4565
  ) -> AsyncIterator[TeamRunOutputEvent]:
4336
- if self.stream_intermediate_steps:
4337
- yield self._handle_event(create_team_reasoning_started_event(from_run_response=run_response), run_response)
4566
+ if stream_events:
4567
+ yield handle_event( # type: ignore
4568
+ create_team_reasoning_started_event(from_run_response=run_response),
4569
+ run_response,
4570
+ events_to_skip=self.events_to_skip,
4571
+ store_events=self.store_events,
4572
+ )
4338
4573
 
4339
4574
  use_default_reasoning = False
4340
4575
 
@@ -4454,14 +4689,16 @@ class Team:
4454
4689
  reasoning_steps=[ReasoningStep(result=reasoning_message.content)],
4455
4690
  reasoning_agent_messages=[reasoning_message],
4456
4691
  )
4457
- if self.stream_intermediate_steps:
4458
- yield self._handle_event(
4692
+ if stream_events:
4693
+ yield handle_event( # type: ignore
4459
4694
  create_team_reasoning_completed_event(
4460
4695
  from_run_response=run_response,
4461
4696
  content=ReasoningSteps(reasoning_steps=[ReasoningStep(result=reasoning_message.content)]),
4462
4697
  content_type=ReasoningSteps.__name__,
4463
4698
  ),
4464
4699
  run_response,
4700
+ events_to_skip=self.events_to_skip,
4701
+ store_events=self.store_events,
4465
4702
  )
4466
4703
  else:
4467
4704
  log_warning(
@@ -4538,19 +4775,21 @@ class Team:
4538
4775
  reasoning_steps: List[ReasoningStep] = reasoning_agent_response.content.reasoning_steps
4539
4776
  all_reasoning_steps.extend(reasoning_steps)
4540
4777
  # Yield reasoning steps
4541
- if self.stream_intermediate_steps:
4778
+ if stream_events:
4542
4779
  for reasoning_step in reasoning_steps:
4543
4780
  updated_reasoning_content = self._format_reasoning_step_content(
4544
4781
  run_response, reasoning_step
4545
4782
  )
4546
4783
 
4547
- yield self._handle_event(
4784
+ yield handle_event( # type: ignore
4548
4785
  create_team_reasoning_step_event(
4549
4786
  from_run_response=run_response,
4550
4787
  reasoning_step=reasoning_step,
4551
4788
  reasoning_content=updated_reasoning_content,
4552
4789
  ),
4553
4790
  run_response,
4791
+ events_to_skip=self.events_to_skip,
4792
+ store_events=self.store_events,
4554
4793
  )
4555
4794
 
4556
4795
  # Find the index of the first assistant message
@@ -4586,14 +4825,16 @@ class Team:
4586
4825
  )
4587
4826
 
4588
4827
  # Yield the final reasoning completed event
4589
- if self.stream_intermediate_steps:
4590
- yield self._handle_event(
4828
+ if stream_events:
4829
+ yield handle_event( # type: ignore # type: ignore
4591
4830
  create_team_reasoning_completed_event(
4592
4831
  from_run_response=run_response,
4593
4832
  content=ReasoningSteps(reasoning_steps=all_reasoning_steps),
4594
4833
  content_type=ReasoningSteps.__name__,
4595
4834
  ),
4596
4835
  run_response,
4836
+ events_to_skip=self.events_to_skip,
4837
+ store_events=self.store_events,
4597
4838
  )
4598
4839
 
4599
4840
  def _resolve_run_dependencies(self, dependencies: Optional[Dict[str, Any]] = None) -> None:
@@ -4641,137 +4882,6 @@ class Team:
4641
4882
  except Exception as e:
4642
4883
  log_warning(f"Failed to resolve context for '{key}': {e}")
4643
4884
 
4644
- def _collect_joint_images(
4645
- self,
4646
- run_input: Optional[TeamRunInput] = None,
4647
- session: Optional[TeamSession] = None,
4648
- ) -> Optional[Sequence[Image]]:
4649
- """Collect images from input, session history, and current run response."""
4650
- joint_images: List[Image] = []
4651
-
4652
- # 1. Add images from current input
4653
- if run_input and run_input.images:
4654
- joint_images.extend(run_input.images)
4655
- log_debug(f"Added {len(run_input.images)} input images to joint list")
4656
-
4657
- # 2. Add images from session history (from both input and generated sources)
4658
- try:
4659
- if session and session.runs:
4660
- for historical_run in session.runs:
4661
- # Add generated images from previous runs
4662
- if historical_run.images:
4663
- joint_images.extend(historical_run.images)
4664
- log_debug(
4665
- f"Added {len(historical_run.images)} generated images from historical run {historical_run.run_id}"
4666
- )
4667
-
4668
- # Add input images from previous runs
4669
- if historical_run.input and historical_run.input.images:
4670
- joint_images.extend(historical_run.input.images)
4671
- log_debug(
4672
- f"Added {len(historical_run.input.images)} input images from historical run {historical_run.run_id}"
4673
- )
4674
- except Exception as e:
4675
- log_debug(f"Could not access session history for images: {e}")
4676
-
4677
- if joint_images:
4678
- log_debug(f"Images Available to Model: {len(joint_images)} images")
4679
- return joint_images if joint_images else None
4680
-
4681
- def _collect_joint_videos(
4682
- self,
4683
- run_input: Optional[TeamRunInput] = None,
4684
- session: Optional[TeamSession] = None,
4685
- ) -> Optional[Sequence[Video]]:
4686
- """Collect videos from input, session history, and current run response."""
4687
- joint_videos: List[Video] = []
4688
-
4689
- # 1. Add videos from current input
4690
- if run_input and run_input.videos:
4691
- joint_videos.extend(run_input.videos)
4692
- log_debug(f"Added {len(run_input.videos)} input videos to joint list")
4693
-
4694
- # 2. Add videos from session history (from both input and generated sources)
4695
- try:
4696
- if session and session.runs:
4697
- for historical_run in session.runs:
4698
- # Add generated videos from previous runs
4699
- if historical_run.videos:
4700
- joint_videos.extend(historical_run.videos)
4701
- log_debug(
4702
- f"Added {len(historical_run.videos)} generated videos from historical run {historical_run.run_id}"
4703
- )
4704
-
4705
- # Add input videos from previous runs
4706
- if historical_run.input and historical_run.input.videos:
4707
- joint_videos.extend(historical_run.input.videos)
4708
- log_debug(
4709
- f"Added {len(historical_run.input.videos)} input videos from historical run {historical_run.run_id}"
4710
- )
4711
- except Exception as e:
4712
- log_debug(f"Could not access session history for videos: {e}")
4713
-
4714
- if joint_videos:
4715
- log_debug(f"Videos Available to Model: {len(joint_videos)} videos")
4716
- return joint_videos if joint_videos else None
4717
-
4718
- def _collect_joint_audios(
4719
- self,
4720
- run_input: Optional[TeamRunInput] = None,
4721
- session: Optional[TeamSession] = None,
4722
- ) -> Optional[Sequence[Audio]]:
4723
- """Collect audios from input, session history, and current run response."""
4724
- joint_audios: List[Audio] = []
4725
-
4726
- # 1. Add audios from current input
4727
- if run_input and run_input.audios:
4728
- joint_audios.extend(run_input.audios)
4729
- log_debug(f"Added {len(run_input.audios)} input audios to joint list")
4730
-
4731
- # 2. Add audios from session history (from both input and generated sources)
4732
- try:
4733
- if session and session.runs:
4734
- for historical_run in session.runs:
4735
- # Add generated audios from previous runs
4736
- if historical_run.audio:
4737
- joint_audios.extend(historical_run.audio)
4738
- log_debug(
4739
- f"Added {len(historical_run.audio)} generated audios from historical run {historical_run.run_id}"
4740
- )
4741
-
4742
- # Add input audios from previous runs
4743
- if historical_run.input and historical_run.input.audios:
4744
- joint_audios.extend(historical_run.input.audios)
4745
- log_debug(
4746
- f"Added {len(historical_run.input.audios)} input audios from historical run {historical_run.run_id}"
4747
- )
4748
- except Exception as e:
4749
- log_debug(f"Could not access session history for audios: {e}")
4750
-
4751
- if joint_audios:
4752
- log_debug(f"Audios Available to Model: {len(joint_audios)} audios")
4753
- return joint_audios if joint_audios else None
4754
-
4755
- def _collect_joint_files(
4756
- self,
4757
- run_input: Optional[TeamRunInput] = None,
4758
- ) -> Optional[Sequence[File]]:
4759
- """Collect files from input and session history."""
4760
- from agno.utils.log import log_debug
4761
-
4762
- joint_files: List[File] = []
4763
-
4764
- # 1. Add files from current input
4765
- if run_input and run_input.files:
4766
- joint_files.extend(run_input.files)
4767
-
4768
- # TODO: Files aren't stored in session history yet and dont have a FileArtifact
4769
-
4770
- if joint_files:
4771
- log_debug(f"Files Available to Model: {len(joint_files)} files")
4772
-
4773
- return joint_files if joint_files else None
4774
-
4775
4885
  def determine_tools_for_model(
4776
4886
  self,
4777
4887
  model: Model,
@@ -4802,14 +4912,14 @@ class Team:
4802
4912
  for tool in self.tools:
4803
4913
  _tools.append(tool)
4804
4914
 
4805
- if self.read_team_history:
4806
- _tools.append(self._get_team_history_function(session=session))
4915
+ if self.read_chat_history:
4916
+ _tools.append(self._get_chat_history_function(session=session, async_mode=async_mode))
4807
4917
 
4808
4918
  if self.memory_manager is not None and self.enable_agentic_memory:
4809
4919
  _tools.append(self._get_update_user_memory_function(user_id=user_id, async_mode=async_mode))
4810
4920
 
4811
4921
  if self.enable_agentic_state:
4812
- _tools.append(self.update_session_state)
4922
+ _tools.append(Function(name="update_session_state", entrypoint=self._update_session_state_tool))
4813
4923
 
4814
4924
  if self.search_session_history:
4815
4925
  _tools.append(
@@ -4848,7 +4958,7 @@ class Team:
4848
4958
 
4849
4959
  if self.members:
4850
4960
  # Get the user message if we are using the input directly
4851
- user_message = None
4961
+ user_message_content = None
4852
4962
  if self.determine_input_for_members is False:
4853
4963
  user_message = self._get_user_message(
4854
4964
  run_response=run_response,
@@ -4863,16 +4973,17 @@ class Team:
4863
4973
  add_dependencies_to_context=add_dependencies_to_context,
4864
4974
  metadata=metadata,
4865
4975
  )
4976
+ user_message_content = user_message.content if user_message is not None else None
4866
4977
 
4867
4978
  delegate_task_func = self._get_delegate_task_function(
4868
4979
  run_response=run_response,
4869
4980
  session=session,
4870
4981
  session_state=session_state,
4871
4982
  team_run_context=team_run_context,
4872
- input=user_message,
4983
+ input=user_message_content,
4873
4984
  user_id=user_id,
4874
4985
  stream=self.stream or False,
4875
- stream_intermediate_steps=self.stream_intermediate_steps,
4986
+ stream_events=self.stream_events or False,
4876
4987
  async_mode=async_mode,
4877
4988
  images=images, # type: ignore
4878
4989
  videos=videos, # type: ignore
@@ -4983,10 +5094,10 @@ class Team:
4983
5094
 
4984
5095
  if needs_media:
4985
5096
  # Only collect media if functions actually need them
4986
- joint_images = self._collect_joint_images(run_response.input, session)
4987
- joint_files = self._collect_joint_files(run_response.input)
4988
- joint_audios = self._collect_joint_audios(run_response.input, session)
4989
- joint_videos = self._collect_joint_videos(run_response.input, session)
5097
+ joint_images = collect_joint_images(run_response.input, session) # type: ignore
5098
+ joint_files = collect_joint_files(run_response.input) # type: ignore
5099
+ joint_audios = collect_joint_audios(run_response.input, session) # type: ignore
5100
+ joint_videos = collect_joint_videos(run_response.input, session) # type: ignore
4990
5101
 
4991
5102
  for func in self._functions_for_model.values():
4992
5103
  func._images = joint_images
@@ -5183,12 +5294,11 @@ class Team:
5183
5294
  "- You cannot use a member tool directly. You can only delegate tasks to members.\n"
5184
5295
  "- When you delegate a task to another member, make sure to include:\n"
5185
5296
  " - member_id (str): The ID of the member to delegate the task to. Use only the ID of the member, not the ID of the team followed by the ID of the member.\n"
5186
- " - task_description (str): A clear description of the task.\n"
5187
- " - expected_output (str): The expected output.\n"
5297
+ " - task (str): A clear description of the task. Determine the best way to describe the task to the member.\n"
5188
5298
  "- You can delegate tasks to multiple members at once.\n"
5189
5299
  "- You must always analyze the responses from members before responding to the user.\n"
5190
5300
  "- After analyzing the responses from the members, if you feel the task has been completed, you can stop and respond to the user.\n"
5191
- "- If you are not satisfied with the responses from the members, you should re-assign the task.\n"
5301
+ "- If you are NOT satisfied with the responses from the members, you should re-assign the task to a different member.\n"
5192
5302
  "- For simple greetings, thanks, or questions about the team itself, you should respond directly.\n"
5193
5303
  "- For all work requests, tasks, or questions requiring expertise, route to appropriate team members.\n"
5194
5304
  )
@@ -5477,8 +5587,7 @@ class Team:
5477
5587
  "- You cannot use a member tool directly. You can only delegate tasks to members.\n"
5478
5588
  "- When you delegate a task to another member, make sure to include:\n"
5479
5589
  " - member_id (str): The ID of the member to delegate the task to. Use only the ID of the member, not the ID of the team followed by the ID of the member.\n"
5480
- " - task_description (str): A clear description of the task.\n"
5481
- " - expected_output (str): The expected output.\n"
5590
+ " - task (str): A clear description of the task.\n"
5482
5591
  "- You can delegate tasks to multiple members at once.\n"
5483
5592
  "- You must always analyze the responses from members before responding to the user.\n"
5484
5593
  "- After analyzing the responses from the members, if you feel the task has been completed, you can stop and respond to the user.\n"
@@ -5727,13 +5836,16 @@ class Team:
5727
5836
  for _msg in history_copy:
5728
5837
  _msg.from_history = True
5729
5838
 
5839
+ # Filter tool calls from history messages
5840
+ if self.max_tool_calls_from_history is not None:
5841
+ filter_tool_calls(history_copy, self.max_tool_calls_from_history)
5842
+
5730
5843
  log_debug(f"Adding {len(history_copy)} messages from history")
5731
5844
 
5732
5845
  # Extend the messages with the history
5733
5846
  run_messages.messages += history_copy
5734
5847
 
5735
5848
  # 5. Add user message to run_messages (message second as per Dirk's requirement)
5736
- user_message: Optional[Message] = None
5737
5849
  # 5.1 Build user message if message is None, str or list
5738
5850
  user_message = self._get_user_message(
5739
5851
  run_response=run_response,
@@ -5855,13 +5967,16 @@ class Team:
5855
5967
  for _msg in history_copy:
5856
5968
  _msg.from_history = True
5857
5969
 
5970
+ # Filter tool calls from history messages
5971
+ if self.max_tool_calls_from_history is not None:
5972
+ filter_tool_calls(history_copy, self.max_tool_calls_from_history)
5973
+
5858
5974
  log_debug(f"Adding {len(history_copy)} messages from history")
5859
5975
 
5860
5976
  # Extend the messages with the history
5861
5977
  run_messages.messages += history_copy
5862
5978
 
5863
5979
  # 5. Add user message to run_messages (message second as per Dirk's requirement)
5864
- user_message: Optional[Message] = None
5865
5980
  # 5.1 Build user message if message is None, str or list
5866
5981
  user_message = self._get_user_message(
5867
5982
  run_response=run_response,
@@ -6305,10 +6420,15 @@ class Team:
6305
6420
  """Get information about the members of the team, including their IDs, names, and roles."""
6306
6421
  return self.get_members_system_message_content(indent=0)
6307
6422
 
6308
- def _get_team_history_function(self, session: TeamSession) -> Callable:
6309
- def get_team_history(num_chats: Optional[int] = None) -> str:
6423
+ def _get_chat_history_function(self, session: TeamSession, async_mode: bool = False):
6424
+ def get_chat_history(num_chats: Optional[int] = None) -> str:
6310
6425
  """
6311
- Use this function to get the team chat history.
6426
+ Use this function to get the team chat history in reverse chronological order.
6427
+ Leave the num_chats parameter blank to get the entire chat history.
6428
+ Example:
6429
+ - To get the last chat, use num_chats=1
6430
+ - To get the last 5 chats, use num_chats=5
6431
+ - To get all chats, leave num_chats blank
6312
6432
 
6313
6433
  Args:
6314
6434
  num_chats: The number of chats to return.
@@ -6317,12 +6437,42 @@ class Team:
6317
6437
 
6318
6438
  Returns:
6319
6439
  str: A JSON string containing a list of dictionaries representing the team chat history.
6440
+ """
6441
+ import json
6442
+
6443
+ history: List[Dict[str, Any]] = []
6444
+
6445
+ all_chats = session.get_messages_from_last_n_runs(
6446
+ team_id=self.id,
6447
+ )
6448
+
6449
+ if len(all_chats) == 0:
6450
+ return ""
6451
+
6452
+ for chat in all_chats[::-1]: # type: ignore
6453
+ history.insert(0, chat.to_dict()) # type: ignore
6454
+
6455
+ if num_chats is not None:
6456
+ history = history[:num_chats]
6457
+
6458
+ return json.dumps(history)
6320
6459
 
6460
+ async def aget_chat_history(num_chats: Optional[int] = None) -> str:
6461
+ """
6462
+ Use this function to get the team chat history in reverse chronological order.
6463
+ Leave the num_chats parameter blank to get the entire chat history.
6321
6464
  Example:
6322
6465
  - To get the last chat, use num_chats=1
6323
6466
  - To get the last 5 chats, use num_chats=5
6324
- - To get all chats, use num_chats=None
6325
- - To get the first chat, use num_chats=None and take the first message
6467
+ - To get all chats, leave num_chats blank
6468
+
6469
+ Args:
6470
+ num_chats: The number of chats to return.
6471
+ Each chat contains 2 messages. One from the team and one from the user.
6472
+ Default: None
6473
+
6474
+ Returns:
6475
+ str: A JSON string containing a list of dictionaries representing the team chat history.
6326
6476
  """
6327
6477
  import json
6328
6478
 
@@ -6343,9 +6493,13 @@ class Team:
6343
6493
 
6344
6494
  return json.dumps(history)
6345
6495
 
6346
- return get_team_history
6496
+ if async_mode:
6497
+ get_chat_history_func = aget_chat_history
6498
+ else:
6499
+ get_chat_history_func = get_chat_history # type: ignore
6500
+ return Function.from_callable(get_chat_history_func, name="get_chat_history")
6347
6501
 
6348
- def update_session_state(self, session_state, session_state_updates: dict) -> str:
6502
+ def _update_session_state_tool(self, session_state, session_state_updates: dict) -> str:
6349
6503
  """
6350
6504
  Update the shared session state. Provide any updates as a dictionary of key-value pairs.
6351
6505
  Example:
@@ -6361,7 +6515,7 @@ class Team:
6361
6515
 
6362
6516
  def _get_previous_sessions_messages_function(
6363
6517
  self, num_history_sessions: Optional[int] = 2, user_id: Optional[str] = None
6364
- ) -> Callable:
6518
+ ):
6365
6519
  """Factory function to create a get_previous_session_messages function.
6366
6520
 
6367
6521
  Args:
@@ -6477,9 +6631,9 @@ class Team:
6477
6631
  return json.dumps([msg.to_dict() for msg in all_messages]) if all_messages else "No history found"
6478
6632
 
6479
6633
  if self._has_async_db():
6480
- return aget_previous_session_messages
6634
+ return Function.from_callable(aget_previous_session_messages, name="get_previous_session_messages")
6481
6635
  else:
6482
- return get_previous_session_messages
6636
+ return Function.from_callable(get_previous_session_messages, name="get_previous_session_messages")
6483
6637
 
6484
6638
  def _get_history_for_member_agent(self, session: TeamSession, member_agent: Union[Agent, "Team"]) -> List[Message]:
6485
6639
  from copy import deepcopy
@@ -6514,7 +6668,12 @@ class Team:
6514
6668
  return []
6515
6669
 
6516
6670
  def _determine_team_member_interactions(
6517
- self, team_run_context: Dict[str, Any], images: List[Image], videos: List[Video], audio: List[Audio]
6671
+ self,
6672
+ team_run_context: Dict[str, Any],
6673
+ images: List[Image],
6674
+ videos: List[Video],
6675
+ audio: List[Audio],
6676
+ files: List[File],
6518
6677
  ) -> Optional[str]:
6519
6678
  team_member_interactions_str = None
6520
6679
  if self.share_member_interactions:
@@ -6525,6 +6684,8 @@ class Team:
6525
6684
  videos.extend(context_videos)
6526
6685
  if context_audio := self._get_team_run_context_audio(team_run_context=team_run_context): # type: ignore
6527
6686
  audio.extend(context_audio)
6687
+ if context_files := self._get_team_run_context_files(team_run_context=team_run_context): # type: ignore
6688
+ files.extend(context_files)
6528
6689
  return team_member_interactions_str
6529
6690
 
6530
6691
  def _find_member_by_id(self, member_id: str) -> Optional[Tuple[int, Union[Agent, "Team"]]]:
@@ -6562,9 +6723,9 @@ class Team:
6562
6723
  team_run_context: Dict[str, Any],
6563
6724
  user_id: Optional[str] = None,
6564
6725
  stream: bool = False,
6565
- stream_intermediate_steps: bool = False,
6726
+ stream_events: bool = False,
6566
6727
  async_mode: bool = False,
6567
- input: Optional[Message] = None, # Used for determine_input_for_memberss=False
6728
+ input: Optional[str] = None, # Used for determine_input_for_members=False
6568
6729
  images: Optional[List[Image]] = None,
6569
6730
  videos: Optional[List[Video]] = None,
6570
6731
  audio: Optional[List[Audio]] = None,
@@ -6586,54 +6747,54 @@ class Team:
6586
6747
  if not files:
6587
6748
  files = []
6588
6749
 
6589
- def _setup_delegate_task_to_member(
6590
- member_agent: Union[Agent, "Team"], task_description: str, expected_output: Optional[str] = None
6591
- ):
6750
+ def _setup_delegate_task_to_member(member_agent: Union[Agent, "Team"], task_description: str):
6592
6751
  # 1. Initialize the member agent
6593
6752
  self._initialize_member(member_agent)
6594
6753
 
6595
- # 2. Determine team context to send
6754
+ # 2. Handle respond_directly nuances
6755
+ if self.respond_directly:
6756
+ # Since we return the response directly from the member agent, we need to set the output schema from the team down.
6757
+ if not member_agent.output_schema and self.output_schema:
6758
+ member_agent.output_schema = self.output_schema
6759
+
6760
+ # If the member will produce structured output, we need to parse the response
6761
+ if member_agent.output_schema is not None:
6762
+ self._member_response_model = member_agent.output_schema
6763
+
6764
+ # 3. Handle enable_agentic_knowledge_filters on the member agent
6765
+ if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
6766
+ member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
6767
+
6768
+ # 4. Determine team context to send
6596
6769
  team_member_interactions_str = self._determine_team_member_interactions(
6597
- team_run_context, images, videos, audio
6770
+ team_run_context, images=images, videos=videos, audio=audio, files=files
6598
6771
  )
6599
6772
 
6600
- member_agent_task: Union[str, Message]
6773
+ # 5. Get the team history
6774
+ team_history_str = None
6775
+ if self.add_team_history_to_members and session:
6776
+ team_history_str = session.get_team_history_context(num_runs=self.num_team_history_runs)
6601
6777
 
6602
- # 3. Create the member agent task or use the input directly
6778
+ # 6. Create the member agent task or use the input directly
6603
6779
  if self.determine_input_for_members is False:
6604
6780
  member_agent_task = input # type: ignore
6605
6781
  else:
6606
- # Don't override the expected output of a member agent
6607
- if member_agent.expected_output is not None:
6608
- expected_output = None
6782
+ member_agent_task = task_description
6609
6783
 
6784
+ if team_history_str or team_member_interactions_str:
6610
6785
  member_agent_task = format_member_agent_task( # type: ignore
6611
- task_description, expected_output, team_member_interactions_str
6786
+ task_description=member_agent_task or "",
6787
+ team_member_interactions_str=team_member_interactions_str,
6788
+ team_history_str=team_history_str,
6612
6789
  )
6613
6790
 
6614
- # 4. Add history for the member if enabled (because we won't load the session for the member, so history won't be loaded automatically)
6791
+ # 7. Add member-level history for the member if enabled (because we won't load the session for the member, so history won't be loaded automatically)
6615
6792
  history = None
6616
- if member_agent.add_history_to_context or add_history_to_context:
6793
+ if member_agent.add_history_to_context:
6617
6794
  history = self._get_history_for_member_agent(session, member_agent)
6618
6795
  if history:
6619
6796
  if isinstance(member_agent_task, str):
6620
6797
  history.append(Message(role="user", content=member_agent_task))
6621
- else:
6622
- history.append(member_agent_task)
6623
-
6624
- # 5. Handle respond_directly
6625
- if self.respond_directly:
6626
- # Since we return the response directly from the member agent, we need to set the output schema from the team down.
6627
- if not member_agent.output_schema and self.output_schema:
6628
- member_agent.output_schema = self.output_schema
6629
-
6630
- # If the member will produce structured output, we need to parse the response
6631
- if member_agent.output_schema is not None:
6632
- self._member_response_model = member_agent.output_schema
6633
-
6634
- # 6. Handle enable_agentic_knowledge_filters on the member agent
6635
- if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
6636
- member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
6637
6798
 
6638
6799
  return member_agent_task, history
6639
6800
 
@@ -6692,28 +6853,28 @@ class Team:
6692
6853
  self._update_team_media(member_agent_run_response) # type: ignore
6693
6854
 
6694
6855
  def delegate_task_to_member(
6695
- member_id: str, task_description: str, expected_output: Optional[str] = None
6856
+ member_id: str, task: str
6696
6857
  ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
6697
6858
  """Use this function to delegate a task to the selected team member.
6698
6859
  You must provide a clear and concise description of the task the member should achieve AND the expected output.
6699
6860
 
6700
6861
  Args:
6701
6862
  member_id (str): The ID of the member to delegate the task to. Use only the ID of the member, not the ID of the team followed by the ID of the member.
6702
- task_description (str): A clear and concise description of the task the member should achieve.
6703
- expected_output (str, optional): The expected output from the member (optional).
6863
+ task (str): A clear and concise description of the task the member should achieve.
6704
6864
  Returns:
6705
6865
  str: The result of the delegated task.
6706
6866
  """
6707
6867
 
6708
6868
  # Find the member agent using the helper function
6709
6869
  result = self._find_member_by_id(member_id)
6710
- history = None
6711
6870
  if result is None:
6712
6871
  yield f"Member with ID {member_id} not found in the team or any subteams. Please choose the correct member from the list of members:\n\n{self.get_members_system_message_content(indent=0)}"
6713
6872
  return
6714
6873
 
6715
6874
  _, member_agent = result
6716
- member_agent_task, history = _setup_delegate_task_to_member(member_agent, task_description, expected_output)
6875
+ member_agent_task, history = _setup_delegate_task_to_member(
6876
+ member_agent=member_agent, task_description=task
6877
+ )
6717
6878
 
6718
6879
  # Make sure for the member agent, we are using the agent logger
6719
6880
  use_agent_logger()
@@ -6731,7 +6892,7 @@ class Team:
6731
6892
  audio=audio,
6732
6893
  files=files,
6733
6894
  stream=True,
6734
- stream_intermediate_steps=stream_intermediate_steps,
6895
+ stream_events=stream_events,
6735
6896
  debug_mode=debug_mode,
6736
6897
  dependencies=dependencies,
6737
6898
  add_dependencies_to_context=add_dependencies_to_context,
@@ -6818,28 +6979,28 @@ class Team:
6818
6979
  )
6819
6980
 
6820
6981
  async def adelegate_task_to_member(
6821
- member_id: str, task_description: str, expected_output: Optional[str] = None
6982
+ member_id: str, task: str
6822
6983
  ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
6823
6984
  """Use this function to delegate a task to the selected team member.
6824
6985
  You must provide a clear and concise description of the task the member should achieve AND the expected output.
6825
6986
 
6826
6987
  Args:
6827
6988
  member_id (str): The ID of the member to delegate the task to. Use only the ID of the member, not the ID of the team followed by the ID of the member.
6828
- task_description (str): A clear and concise description of the task the member should achieve.
6829
- expected_output (str, optional): The expected output from the member (optional).
6989
+ task (str): A clear and concise description of the task the member should achieve.
6830
6990
  Returns:
6831
6991
  str: The result of the delegated task.
6832
6992
  """
6833
6993
 
6834
6994
  # Find the member agent using the helper function
6835
6995
  result = self._find_member_by_id(member_id)
6836
- history = None
6837
6996
  if result is None:
6838
6997
  yield f"Member with ID {member_id} not found in the team or any subteams. Please choose the correct member from the list of members:\n\n{self.get_members_system_message_content(indent=0)}"
6839
6998
  return
6840
6999
 
6841
7000
  _, member_agent = result
6842
- member_agent_task, history = _setup_delegate_task_to_member(member_agent, task_description, expected_output)
7001
+ member_agent_task, history = _setup_delegate_task_to_member(
7002
+ member_agent=member_agent, task_description=task
7003
+ )
6843
7004
 
6844
7005
  # Make sure for the member agent, we are using the agent logger
6845
7006
  use_agent_logger()
@@ -6857,7 +7018,7 @@ class Team:
6857
7018
  audio=audio,
6858
7019
  files=files,
6859
7020
  stream=True,
6860
- stream_intermediate_steps=stream_intermediate_steps,
7021
+ stream_events=stream_events,
6861
7022
  debug_mode=debug_mode,
6862
7023
  dependencies=dependencies,
6863
7024
  add_dependencies_to_context=add_dependencies_to_context,
@@ -6940,16 +7101,13 @@ class Team:
6940
7101
  )
6941
7102
 
6942
7103
  # When the task should be delegated to all members
6943
- def delegate_task_to_members(
6944
- task_description: str, expected_output: Optional[str] = None
6945
- ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
7104
+ def delegate_task_to_members(task: str) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
6946
7105
  """
6947
7106
  Use this function to delegate a task to all the member agents and return a response.
6948
7107
  You must provide a clear and concise description of the task the member should achieve AND the expected output.
6949
7108
 
6950
7109
  Args:
6951
- task_description (str): A clear and concise description of the task to send to member agents.
6952
- expected_output (str, optional): The expected output from the member agents (optional).
7110
+ task (str): A clear and concise description of the task to send to member agents.
6953
7111
  Returns:
6954
7112
  str: The result of the delegated task.
6955
7113
  """
@@ -6957,7 +7115,7 @@ class Team:
6957
7115
  # Run all the members sequentially
6958
7116
  for _, member_agent in enumerate(self.members):
6959
7117
  member_agent_task, history = _setup_delegate_task_to_member(
6960
- member_agent, task_description, expected_output
7118
+ member_agent=member_agent, task_description=task
6961
7119
  )
6962
7120
 
6963
7121
  member_session_state_copy = copy(session_state)
@@ -6973,7 +7131,7 @@ class Team:
6973
7131
  audio=audio,
6974
7132
  files=files,
6975
7133
  stream=True,
6976
- stream_intermediate_steps=stream_intermediate_steps,
7134
+ stream_events=stream_events,
6977
7135
  knowledge_filters=knowledge_filters
6978
7136
  if not member_agent.knowledge_filters and member_agent.knowledge
6979
7137
  else None,
@@ -7055,15 +7213,12 @@ class Team:
7055
7213
  use_team_logger()
7056
7214
 
7057
7215
  # When the task should be delegated to all members
7058
- async def adelegate_task_to_members(
7059
- task_description: str, expected_output: Optional[str] = None
7060
- ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
7216
+ async def adelegate_task_to_members(task: str) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
7061
7217
  """Use this function to delegate a task to all the member agents and return a response.
7062
- You must provide a clear and concise description of the task to send to member agents AND the expected output.
7218
+ You must provide a clear and concise description of the task to send to member agents.
7063
7219
 
7064
7220
  Args:
7065
- task_description (str): A clear and concise description of the task to send to member agents.
7066
- expected_output (str, optional): The expected output from the member agents (optional).
7221
+ task (str): A clear and concise description of the task to send to member agents.
7067
7222
  Returns:
7068
7223
  str: The result of the delegated task.
7069
7224
  """
@@ -7075,8 +7230,8 @@ class Team:
7075
7230
 
7076
7231
  async def stream_member(agent: Union[Agent, "Team"]) -> None:
7077
7232
  member_agent_task, history = _setup_delegate_task_to_member(
7078
- agent, task_description, expected_output
7079
- )
7233
+ member_agent=agent, task_description=task
7234
+ ) # type: ignore
7080
7235
  member_session_state_copy = copy(session_state)
7081
7236
 
7082
7237
  member_stream = agent.arun( # type: ignore
@@ -7089,7 +7244,7 @@ class Team:
7089
7244
  audio=audio,
7090
7245
  files=files,
7091
7246
  stream=True,
7092
- stream_intermediate_steps=stream_intermediate_steps,
7247
+ stream_events=stream_events,
7093
7248
  debug_mode=debug_mode,
7094
7249
  knowledge_filters=knowledge_filters
7095
7250
  if not member_agent.knowledge_filters and member_agent.knowledge
@@ -7150,7 +7305,7 @@ class Team:
7150
7305
  for member_agent_index, member_agent in enumerate(self.members):
7151
7306
  current_agent = member_agent
7152
7307
  member_agent_task, history = _setup_delegate_task_to_member(
7153
- current_agent, task_description, expected_output
7308
+ member_agent=current_agent, task_description=task
7154
7309
  )
7155
7310
 
7156
7311
  async def run_member_agent(agent=current_agent) -> str:
@@ -7166,7 +7321,7 @@ class Team:
7166
7321
  audio=audio,
7167
7322
  files=files,
7168
7323
  stream=False,
7169
- stream_intermediate_steps=stream_intermediate_steps,
7324
+ stream_events=stream_events,
7170
7325
  debug_mode=debug_mode,
7171
7326
  knowledge_filters=knowledge_filters
7172
7327
  if not member_agent.knowledge_filters and member_agent.knowledge
@@ -7240,24 +7395,28 @@ class Team:
7240
7395
  ###########################################################################
7241
7396
  # Session Management
7242
7397
  ###########################################################################
7243
- def _read_session(self, session_id: str) -> Optional[TeamSession]:
7398
+ def _read_session(
7399
+ self, session_id: str, session_type: SessionType = SessionType.TEAM
7400
+ ) -> Optional[Union[TeamSession, WorkflowSession]]:
7244
7401
  """Get a Session from the database."""
7245
7402
  try:
7246
7403
  if not self.db:
7247
7404
  raise ValueError("Db not initialized")
7248
- session = self.db.get_session(session_id=session_id, session_type=SessionType.TEAM)
7405
+ session = self.db.get_session(session_id=session_id, session_type=session_type)
7249
7406
  return session # type: ignore
7250
7407
  except Exception as e:
7251
7408
  log_warning(f"Error getting session from db: {e}")
7252
7409
  return None
7253
7410
 
7254
- async def _aread_session(self, session_id: str) -> Optional[TeamSession]:
7411
+ async def _aread_session(
7412
+ self, session_id: str, session_type: SessionType = SessionType.TEAM
7413
+ ) -> Optional[Union[TeamSession, WorkflowSession]]:
7255
7414
  """Get a Session from the database."""
7256
7415
  try:
7257
7416
  if not self.db:
7258
7417
  raise ValueError("Db not initialized")
7259
7418
  self.db = cast(AsyncBaseDb, self.db)
7260
- session = await self.db.get_session(session_id=session_id, session_type=SessionType.TEAM)
7419
+ session = await self.db.get_session(session_id=session_id, session_type=session_type)
7261
7420
  return session # type: ignore
7262
7421
  except Exception as e:
7263
7422
  log_warning(f"Error getting session from db: {e}")
@@ -7358,12 +7517,17 @@ class Team:
7358
7517
  # Create new session if none found
7359
7518
  if team_session is None:
7360
7519
  log_debug(f"Creating new TeamSession: {session_id}")
7520
+ session_data = {}
7521
+ if self.session_state is not None:
7522
+ from copy import deepcopy
7523
+
7524
+ session_data["session_state"] = deepcopy(self.session_state)
7361
7525
  team_session = TeamSession(
7362
7526
  session_id=session_id,
7363
7527
  team_id=self.id,
7364
7528
  user_id=user_id,
7365
7529
  team_data=self._get_team_data(),
7366
- session_data={},
7530
+ session_data=session_data,
7367
7531
  metadata=self.metadata,
7368
7532
  created_at=int(time()),
7369
7533
  )
@@ -7439,7 +7603,48 @@ class Team:
7439
7603
 
7440
7604
  # Load and return the session from the database
7441
7605
  if self.db is not None:
7442
- team_session = cast(TeamSession, self._read_session(session_id=session_id_to_load)) # type: ignore
7606
+ loaded_session = None
7607
+ # We have a standalone team, so we are loading a TeamSession
7608
+ if self.workflow_id is None:
7609
+ loaded_session = cast(TeamSession, self._read_session(session_id=session_id_to_load)) # type: ignore
7610
+ # We have a workflow team, so we are loading a WorkflowSession
7611
+ else:
7612
+ loaded_session = cast(WorkflowSession, self._read_session(session_id=session_id_to_load)) # type: ignore
7613
+
7614
+ # Cache the session if relevant
7615
+ if loaded_session is not None and self.cache_session:
7616
+ self._agent_session = loaded_session
7617
+
7618
+ return loaded_session
7619
+
7620
+ log_debug(f"TeamSession {session_id_to_load} not found in db")
7621
+ return None
7622
+
7623
+ async def aget_session(
7624
+ self,
7625
+ session_id: Optional[str] = None,
7626
+ ) -> Optional[TeamSession]:
7627
+ """Load an TeamSession from database.
7628
+
7629
+ Args:
7630
+ session_id: The session_id to load from storage.
7631
+
7632
+ Returns:
7633
+ TeamSession: The TeamSession loaded from the database or created if it does not exist.
7634
+ """
7635
+ if not session_id and not self.session_id:
7636
+ return None
7637
+
7638
+ session_id_to_load = session_id or self.session_id
7639
+
7640
+ # If there is a cached session, return it
7641
+ if self.cache_session and hasattr(self, "_team_session") and self._team_session is not None:
7642
+ if self._team_session.session_id == session_id_to_load:
7643
+ return self._team_session
7644
+
7645
+ # Load and return the session from the database
7646
+ if self.db is not None:
7647
+ team_session = cast(TeamSession, await self._aread_session(session_id=session_id_to_load)) # type: ignore
7443
7648
 
7444
7649
  # Cache the session if relevant
7445
7650
  if team_session is not None and self.cache_session:
@@ -7478,7 +7683,17 @@ class Team:
7478
7683
  session.session_data["session_state"].pop("current_session_id", None) # type: ignore
7479
7684
  session.session_data["session_state"].pop("current_user_id", None) # type: ignore
7480
7685
  session.session_data["session_state"].pop("current_run_id", None) # type: ignore
7481
- await self._aupsert_session(session=session)
7686
+
7687
+ # scrub the member responses if not storing them
7688
+ if not self.store_member_responses and session.runs is not None:
7689
+ for run in session.runs:
7690
+ if hasattr(run, "member_responses"):
7691
+ run.member_responses = []
7692
+
7693
+ if self._has_async_db():
7694
+ await self._aupsert_session(session=session)
7695
+ else:
7696
+ self._upsert_session(session=session)
7482
7697
  log_debug(f"Created or updated TeamSession record: {session.session_id}")
7483
7698
 
7484
7699
  def _load_session_state(self, session: TeamSession, session_state: Dict[str, Any]) -> Dict[str, Any]:
@@ -7608,6 +7823,60 @@ class Team:
7608
7823
  raise Exception("Session not found")
7609
7824
  return session.session_data.get("session_state", {}) if session.session_data is not None else {}
7610
7825
 
7826
+ def update_session_state(self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None) -> str:
7827
+ """
7828
+ Update the session state for the given session ID and user ID.
7829
+ Args:
7830
+ session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
7831
+ session_id: The session ID to update. If not provided, the current cached session ID is used.
7832
+ Returns:
7833
+ dict: The updated session state.
7834
+ """
7835
+ session_id = session_id or self.session_id
7836
+ if session_id is None:
7837
+ raise Exception("Session ID is not set")
7838
+ session = self.get_session(session_id=session_id) # type: ignore
7839
+ if session is None:
7840
+ raise Exception("Session not found")
7841
+
7842
+ if session.session_data is not None and "session_state" not in session.session_data:
7843
+ session.session_data["session_state"] = {}
7844
+
7845
+ for key, value in session_state_updates.items():
7846
+ session.session_data["session_state"][key] = value # type: ignore
7847
+
7848
+ self.save_session(session=session)
7849
+
7850
+ return session.session_data["session_state"] # type: ignore
7851
+
7852
+ async def aupdate_session_state(
7853
+ self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
7854
+ ) -> str:
7855
+ """
7856
+ Update the session state for the given session ID and user ID.
7857
+ Args:
7858
+ session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
7859
+ session_id: The session ID to update. If not provided, the current cached session ID is used.
7860
+ Returns:
7861
+ dict: The updated session state.
7862
+ """
7863
+ session_id = session_id or self.session_id
7864
+ if session_id is None:
7865
+ raise Exception("Session ID is not set")
7866
+ session = await self.aget_session(session_id=session_id) # type: ignore
7867
+ if session is None:
7868
+ raise Exception("Session not found")
7869
+
7870
+ if session.session_data is not None and "session_state" not in session.session_data:
7871
+ session.session_data["session_state"] = {}
7872
+
7873
+ for key, value in session_state_updates.items():
7874
+ session.session_data["session_state"][key] = value # type: ignore
7875
+
7876
+ await self.asave_session(session=session)
7877
+
7878
+ return session.session_data["session_state"] # type: ignore
7879
+
7611
7880
  def get_session_metrics(self, session_id: Optional[str] = None) -> Optional[Metrics]:
7612
7881
  """Get the session metrics for the given session ID and user ID."""
7613
7882
  session_id = session_id or self.session_id
@@ -7708,7 +7977,9 @@ class Team:
7708
7977
  return ""
7709
7978
  team_member_interactions_str = ""
7710
7979
  if "member_responses" in team_run_context:
7711
- team_member_interactions_str += "<member interactions>\n"
7980
+ team_member_interactions_str += (
7981
+ "<member_interaction_context>\nSee below interactions wit other team members.\n"
7982
+ )
7712
7983
 
7713
7984
  for interaction in team_run_context["member_responses"]:
7714
7985
  response_dict = interaction["run_response"].to_dict()
@@ -7721,7 +7992,7 @@ class Team:
7721
7992
  team_member_interactions_str += f"Task: {interaction['task']}\n"
7722
7993
  team_member_interactions_str += f"Response: {response_content}\n"
7723
7994
  team_member_interactions_str += "\n"
7724
- team_member_interactions_str += "</member interactions>\n"
7995
+ team_member_interactions_str += "</member_interaction_context>\n"
7725
7996
  return team_member_interactions_str
7726
7997
 
7727
7998
  def _get_team_run_context_images(self, team_run_context: Dict[str, Any]) -> List[Image]:
@@ -7754,6 +8025,16 @@ class Team:
7754
8025
  audio.extend(interaction["run_response"].audio)
7755
8026
  return audio
7756
8027
 
8028
+ def _get_team_run_context_files(self, team_run_context: Dict[str, Any]) -> List[File]:
8029
+ if not team_run_context:
8030
+ return []
8031
+ files = []
8032
+ if "member_responses" in team_run_context:
8033
+ for interaction in team_run_context["member_responses"]:
8034
+ if interaction["run_response"].files:
8035
+ files.extend(interaction["run_response"].files)
8036
+ return files
8037
+
7757
8038
  ###########################################################################
7758
8039
  # Handle images, videos and audio
7759
8040
  ###########################################################################