agno 2.0.0rc1__py3-none-any.whl → 2.0.0rc2__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 (49) hide show
  1. agno/agent/agent.py +32 -14
  2. agno/db/mongo/mongo.py +8 -3
  3. agno/eval/accuracy.py +12 -5
  4. agno/knowledge/chunking/strategy.py +14 -14
  5. agno/knowledge/knowledge.py +156 -120
  6. agno/knowledge/reader/arxiv_reader.py +5 -5
  7. agno/knowledge/reader/csv_reader.py +6 -77
  8. agno/knowledge/reader/docx_reader.py +5 -5
  9. agno/knowledge/reader/firecrawl_reader.py +5 -5
  10. agno/knowledge/reader/json_reader.py +5 -5
  11. agno/knowledge/reader/markdown_reader.py +31 -9
  12. agno/knowledge/reader/pdf_reader.py +10 -123
  13. agno/knowledge/reader/reader_factory.py +65 -72
  14. agno/knowledge/reader/s3_reader.py +44 -114
  15. agno/knowledge/reader/text_reader.py +5 -5
  16. agno/knowledge/reader/url_reader.py +75 -31
  17. agno/knowledge/reader/web_search_reader.py +6 -29
  18. agno/knowledge/reader/website_reader.py +5 -5
  19. agno/knowledge/reader/wikipedia_reader.py +5 -5
  20. agno/knowledge/reader/youtube_reader.py +6 -6
  21. agno/knowledge/utils.py +10 -10
  22. agno/models/aws/bedrock.py +3 -7
  23. agno/models/base.py +37 -6
  24. agno/os/app.py +32 -24
  25. agno/os/mcp.py +39 -59
  26. agno/os/router.py +547 -16
  27. agno/os/routers/evals/evals.py +197 -12
  28. agno/os/routers/knowledge/knowledge.py +428 -14
  29. agno/os/routers/memory/memory.py +250 -28
  30. agno/os/routers/metrics/metrics.py +125 -7
  31. agno/os/routers/session/session.py +393 -25
  32. agno/os/schema.py +55 -2
  33. agno/run/agent.py +9 -0
  34. agno/run/team.py +93 -2
  35. agno/run/workflow.py +25 -12
  36. agno/team/team.py +861 -1051
  37. agno/tools/mcp.py +1 -2
  38. agno/utils/log.py +52 -2
  39. agno/utils/mcp.py +55 -3
  40. agno/utils/models/claude.py +0 -8
  41. agno/utils/print_response/team.py +177 -73
  42. agno/utils/streamlit.py +27 -0
  43. agno/workflow/workflow.py +9 -0
  44. {agno-2.0.0rc1.dist-info → agno-2.0.0rc2.dist-info}/METADATA +1 -1
  45. {agno-2.0.0rc1.dist-info → agno-2.0.0rc2.dist-info}/RECORD +48 -49
  46. agno/knowledge/reader/gcs_reader.py +0 -67
  47. {agno-2.0.0rc1.dist-info → agno-2.0.0rc2.dist-info}/WHEEL +0 -0
  48. {agno-2.0.0rc1.dist-info → agno-2.0.0rc2.dist-info}/licenses/LICENSE +0 -0
  49. {agno-2.0.0rc1.dist-info → agno-2.0.0rc2.dist-info}/top_level.txt +0 -0
agno/team/team.py CHANGED
@@ -51,7 +51,7 @@ from agno.run.cancel import (
51
51
  register_run,
52
52
  )
53
53
  from agno.run.messages import RunMessages
54
- from agno.run.team import TeamRunEvent, TeamRunOutput, TeamRunOutputEvent
54
+ from agno.run.team import TeamRunEvent, TeamRunInput, TeamRunOutput, TeamRunOutputEvent
55
55
  from agno.session import SessionSummaryManager, TeamSession
56
56
  from agno.tools import Toolkit
57
57
  from agno.tools.function import Function
@@ -100,7 +100,6 @@ from agno.utils.reasoning import (
100
100
  from agno.utils.response import (
101
101
  async_generator_wrapper,
102
102
  check_if_run_cancelled,
103
- escape_markdown_tags,
104
103
  generator_wrapper,
105
104
  )
106
105
  from agno.utils.safe_formatter import SafeFormatter
@@ -117,8 +116,6 @@ class Team:
117
116
 
118
117
  members: List[Union[Agent, "Team"]]
119
118
 
120
- mode: Literal["route", "coordinate", "collaborate"] = "coordinate"
121
-
122
119
  # Model for this Team
123
120
  model: Optional[Model] = None
124
121
 
@@ -132,6 +129,14 @@ class Team:
132
129
  # If this team is part of a team itself, this is the role of the team
133
130
  role: Optional[str] = None
134
131
 
132
+ # If True, the team leader won't process responses from the members and instead will return them directly
133
+ # Should not be used in combination with delegate_task_to_all_members
134
+ respond_directly: bool = False
135
+ # If True, the team leader will delegate the task to all members, instead of deciding for a subset
136
+ delegate_task_to_all_members: bool = False
137
+ # Set to false if you want to send the run input directly to the member agents
138
+ determine_input_for_members: bool = True
139
+
135
140
  # --- If this Team is part of a workflow ---
136
141
  # Optional workflow ID. Indicates this team is part of a workflow.
137
142
  workflow_id: Optional[str] = None
@@ -145,9 +150,9 @@ class Team:
145
150
  session_id: Optional[str] = None
146
151
  # Session state (stored in the database to persist across runs)
147
152
  session_state: Optional[Dict[str, Any]] = None
148
- # If True, the team can update the session state
153
+ # Set to True to add the session_state to the context
149
154
  add_session_state_to_context: bool = False
150
- # If True, the team can update the session state
155
+ # Set to True to give the team tools to update the session_state dynamically
151
156
  enable_agentic_state: bool = False
152
157
  # If True, cache the current Team session in memory for faster access
153
158
  cache_session: bool = False
@@ -221,8 +226,6 @@ class Team:
221
226
  references_format: Literal["json", "yaml"] = "json"
222
227
 
223
228
  # --- Tools ---
224
- # If True, enable the team agent to update the team context and automatically send the team context to the members
225
- enable_agentic_context: bool = False
226
229
  # If True, send all previous member interactions to members
227
230
  share_member_interactions: bool = False
228
231
  # If True, add a tool to get information about the team members
@@ -234,6 +237,11 @@ class Team:
234
237
  # If True, read the team history
235
238
  read_team_history: bool = False
236
239
 
240
+ # If False, media (images, videos, audio, files) is only available to tools and not sent to the LLM
241
+ send_media_to_model: bool = True
242
+ # If True, store media in run output
243
+ store_media: bool = True
244
+
237
245
  # --- Team Tools ---
238
246
  # A list of tools provided to the Model.
239
247
  # Tools are functions the model may generate JSON inputs for.
@@ -342,11 +350,13 @@ class Team:
342
350
  def __init__(
343
351
  self,
344
352
  members: List[Union[Agent, "Team"]],
345
- mode: Literal["route", "coordinate", "collaborate"] = "coordinate",
353
+ id: Optional[str] = None,
346
354
  model: Optional[Model] = None,
347
355
  name: Optional[str] = None,
348
356
  role: Optional[str] = None,
349
- id: Optional[str] = None,
357
+ respond_directly: bool = False,
358
+ determine_input_for_members: bool = True,
359
+ delegate_task_to_all_members: bool = False,
350
360
  user_id: Optional[str] = None,
351
361
  session_id: Optional[str] = None,
352
362
  session_state: Optional[Dict[str, Any]] = None,
@@ -376,11 +386,12 @@ class Team:
376
386
  update_knowledge: bool = False,
377
387
  knowledge_retriever: Optional[Callable[..., Optional[List[Union[Dict, str]]]]] = None,
378
388
  references_format: Literal["json", "yaml"] = "json",
379
- enable_agentic_context: bool = False,
380
389
  share_member_interactions: bool = False,
381
390
  get_member_information_tool: bool = False,
382
391
  search_knowledge: bool = True,
383
392
  read_team_history: bool = False,
393
+ store_media: bool = True,
394
+ send_media_to_model: bool = True,
384
395
  tools: Optional[List[Union[Toolkit, Callable, Function, Dict]]] = None,
385
396
  tool_call_limit: Optional[int] = None,
386
397
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
@@ -424,14 +435,16 @@ class Team:
424
435
  ):
425
436
  self.members = members
426
437
 
427
- self.mode = mode
428
-
429
438
  self.model = model
430
439
 
431
440
  self.name = name
432
441
  self.id = id
433
442
  self.role = role
434
443
 
444
+ self.respond_directly = respond_directly
445
+ self.determine_input_for_members = determine_input_for_members
446
+ self.delegate_task_to_all_members = delegate_task_to_all_members
447
+
435
448
  self.user_id = user_id
436
449
  self.session_id = session_id
437
450
  self.session_state = session_state
@@ -465,12 +478,14 @@ class Team:
465
478
  self.knowledge_retriever = knowledge_retriever
466
479
  self.references_format = references_format
467
480
 
468
- self.enable_agentic_context = enable_agentic_context
469
481
  self.share_member_interactions = share_member_interactions
470
482
  self.get_member_information_tool = get_member_information_tool
471
483
  self.search_knowledge = search_knowledge
472
484
  self.read_team_history = read_team_history
473
485
 
486
+ self.store_media = store_media
487
+ self.send_media_to_model = send_media_to_model
488
+
474
489
  self.tools = tools
475
490
  self.tool_choice = tool_choice
476
491
  self.tool_call_limit = tool_call_limit
@@ -591,6 +606,15 @@ class Team:
591
606
  if isinstance(input, Message):
592
607
  input = input.content # type: ignore
593
608
 
609
+ # If input is a string, convert it to a dict
610
+ if isinstance(input, str):
611
+ import json
612
+
613
+ try:
614
+ input = json.loads(input)
615
+ except Exception as e:
616
+ raise ValueError(f"Failed to parse input. Is it a valid JSON string?: {e}")
617
+
594
618
  # Case 1: Message is already a BaseModel instance
595
619
  if isinstance(input, BaseModel):
596
620
  if isinstance(input, self.input_schema):
@@ -715,6 +739,15 @@ class Team:
715
739
  return session_id, user_id, session_state # type: ignore
716
740
 
717
741
  def initialize_team(self, debug_mode: Optional[bool] = None) -> None:
742
+ # Make sure for the team, we are using the team logger
743
+ use_team_logger()
744
+
745
+ if self.delegate_task_to_all_members and self.respond_directly:
746
+ log_warning(
747
+ "delegate_task_to_all_members and respond_directly are both enabled. The task will be delegated to all members."
748
+ )
749
+ self.respond_directly = False
750
+
718
751
  self._set_default_model()
719
752
 
720
753
  # Set debug mode
@@ -738,9 +771,6 @@ class Team:
738
771
  for member in self.members:
739
772
  self._initialize_member(member, debug_mode=self.debug_mode)
740
773
 
741
- # Make sure for the team, we are using the team logger
742
- use_team_logger()
743
-
744
774
  def add_tool(self, tool: Union[Toolkit, Callable, Function, Dict]):
745
775
  if not self.tools:
746
776
  self.tools = []
@@ -812,6 +842,11 @@ class Team:
812
842
  # Update TeamRunOutput
813
843
  self._update_run_response(model_response=model_response, run_response=run_response, run_messages=run_messages)
814
844
 
845
+ if self.store_media:
846
+ self._store_media(run_response, model_response)
847
+ else:
848
+ self._scrub_media_from_run_output(run_response)
849
+
815
850
  # 4. Add the RunOutput to Team Session
816
851
  session.upsert_run(run_response=run_response)
817
852
 
@@ -1010,7 +1045,6 @@ class Team:
1010
1045
  videos: Optional[Sequence[Video]] = None,
1011
1046
  files: Optional[Sequence[File]] = None,
1012
1047
  knowledge_filters: Optional[Dict[str, Any]] = None,
1013
- store_member_responses: Optional[bool] = None,
1014
1048
  add_history_to_context: Optional[bool] = None,
1015
1049
  add_dependencies_to_context: Optional[bool] = None,
1016
1050
  add_session_state_to_context: Optional[bool] = None,
@@ -1036,7 +1070,6 @@ class Team:
1036
1070
  videos: Optional[Sequence[Video]] = None,
1037
1071
  files: Optional[Sequence[File]] = None,
1038
1072
  knowledge_filters: Optional[Dict[str, Any]] = None,
1039
- store_member_responses: Optional[bool] = None,
1040
1073
  add_history_to_context: Optional[bool] = None,
1041
1074
  add_dependencies_to_context: Optional[bool] = None,
1042
1075
  add_session_state_to_context: Optional[bool] = None,
@@ -1061,7 +1094,6 @@ class Team:
1061
1094
  images: Optional[Sequence[Image]] = None,
1062
1095
  videos: Optional[Sequence[Video]] = None,
1063
1096
  files: Optional[Sequence[File]] = None,
1064
- store_member_responses: Optional[bool] = None,
1065
1097
  knowledge_filters: Optional[Dict[str, Any]] = None,
1066
1098
  add_history_to_context: Optional[bool] = None,
1067
1099
  add_dependencies_to_context: Optional[bool] = None,
@@ -1077,9 +1109,6 @@ class Team:
1077
1109
  # Validate input against input_schema if provided
1078
1110
  validated_input = self._validate_input(input)
1079
1111
 
1080
- if store_member_responses is not None:
1081
- self.store_member_responses = store_member_responses
1082
-
1083
1112
  # Create a run_id for this specific run
1084
1113
  run_id = str(uuid4())
1085
1114
 
@@ -1090,6 +1119,15 @@ class Team:
1090
1119
  # Initialize Team
1091
1120
  self.initialize_team(debug_mode=debug_mode)
1092
1121
 
1122
+ image_artifacts, video_artifacts, audio_artifacts = self._convert_media_to_artifacts(
1123
+ images=images, videos=videos, audios=audio
1124
+ )
1125
+
1126
+ # Create RunInput to capture the original user input
1127
+ run_input = TeamRunInput(
1128
+ input_content=input, images=image_artifacts, videos=video_artifacts, audios=audio_artifacts, files=files
1129
+ )
1130
+
1093
1131
  # Read existing session from database
1094
1132
  team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
1095
1133
  self._update_metadata(session=team_session)
@@ -1129,9 +1167,6 @@ class Team:
1129
1167
  if stream is None:
1130
1168
  stream = False if self.stream is None else self.stream
1131
1169
 
1132
- if store_member_responses is None:
1133
- store_member_responses = False if self.store_member_responses is None else self.store_member_responses
1134
-
1135
1170
  if stream_intermediate_steps is None:
1136
1171
  stream_intermediate_steps = (
1137
1172
  False if self.stream_intermediate_steps is None else self.stream_intermediate_steps
@@ -1163,6 +1198,7 @@ class Team:
1163
1198
  team_id=self.id,
1164
1199
  team_name=self.name,
1165
1200
  metadata=metadata,
1201
+ input=run_input,
1166
1202
  )
1167
1203
 
1168
1204
  run_response.model = self.model.id if self.model is not None else None
@@ -1186,7 +1222,6 @@ class Team:
1186
1222
  audio=audio,
1187
1223
  files=files,
1188
1224
  workflow_context=workflow_context,
1189
- store_member_responses=store_member_responses,
1190
1225
  debug_mode=debug_mode,
1191
1226
  add_history_to_context=add_history,
1192
1227
  dependencies=run_dependencies,
@@ -1203,8 +1238,6 @@ class Team:
1203
1238
  for attempt in range(num_attempts):
1204
1239
  # Initialize the current run
1205
1240
 
1206
- log_debug(f"Team Mode: '{self.mode}'", center=True)
1207
-
1208
1241
  # Run the team
1209
1242
  try:
1210
1243
  run_messages = self._get_run_messages(
@@ -1227,10 +1260,6 @@ class Team:
1227
1260
  if len(run_messages.messages) == 0:
1228
1261
  log_error("No messages to be sent to the model.")
1229
1262
 
1230
- self.run_messages = run_messages
1231
- if len(run_messages.messages) == 0:
1232
- log_error("No messages to be sent to the model.")
1233
-
1234
1263
  if stream:
1235
1264
  response_iterator = self._run_stream(
1236
1265
  run_response=run_response,
@@ -1311,7 +1340,6 @@ class Team:
1311
1340
  session: TeamSession,
1312
1341
  user_id: Optional[str] = None,
1313
1342
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1314
- workflow_context: Optional[Dict] = None,
1315
1343
  ) -> TeamRunOutput:
1316
1344
  """Run the Team and return the response.
1317
1345
 
@@ -1358,6 +1386,11 @@ class Team:
1358
1386
  # Update TeamRunOutput
1359
1387
  self._update_run_response(model_response=model_response, run_response=run_response, run_messages=run_messages)
1360
1388
 
1389
+ if self.store_media:
1390
+ self._store_media(run_response, model_response)
1391
+ else:
1392
+ self._scrub_media_from_run_output(run_response)
1393
+
1361
1394
  # 3. Add the run to memory
1362
1395
  session.upsert_run(run_response=run_response)
1363
1396
 
@@ -1554,7 +1587,6 @@ class Team:
1554
1587
  videos: Optional[Sequence[Video]] = None,
1555
1588
  files: Optional[Sequence[File]] = None,
1556
1589
  knowledge_filters: Optional[Dict[str, Any]] = None,
1557
- store_member_responses: Optional[bool] = None,
1558
1590
  add_history_to_context: Optional[bool] = None,
1559
1591
  add_dependencies_to_context: Optional[bool] = None,
1560
1592
  add_session_state_to_context: Optional[bool] = None,
@@ -1579,7 +1611,6 @@ class Team:
1579
1611
  images: Optional[Sequence[Image]] = None,
1580
1612
  videos: Optional[Sequence[Video]] = None,
1581
1613
  files: Optional[Sequence[File]] = None,
1582
- store_member_responses: Optional[bool] = None,
1583
1614
  knowledge_filters: Optional[Dict[str, Any]] = None,
1584
1615
  add_history_to_context: Optional[bool] = None,
1585
1616
  add_dependencies_to_context: Optional[bool] = None,
@@ -1606,7 +1637,6 @@ class Team:
1606
1637
  videos: Optional[Sequence[Video]] = None,
1607
1638
  files: Optional[Sequence[File]] = None,
1608
1639
  knowledge_filters: Optional[Dict[str, Any]] = None,
1609
- store_member_responses: Optional[bool] = None,
1610
1640
  add_history_to_context: Optional[bool] = None,
1611
1641
  add_dependencies_to_context: Optional[bool] = None,
1612
1642
  add_session_state_to_context: Optional[bool] = None,
@@ -1621,9 +1651,6 @@ class Team:
1621
1651
  # Validate input against input_schema if provided
1622
1652
  validated_input = self._validate_input(input)
1623
1653
 
1624
- if store_member_responses is not None:
1625
- self.store_member_responses = store_member_responses
1626
-
1627
1654
  # Create a run_id for this specific run
1628
1655
  run_id = str(uuid4())
1629
1656
 
@@ -1634,6 +1661,15 @@ class Team:
1634
1661
  # Initialize Team
1635
1662
  self.initialize_team(debug_mode=debug_mode)
1636
1663
 
1664
+ image_artifacts, video_artifacts, audio_artifacts = self._convert_media_to_artifacts(
1665
+ images=images, videos=videos, audios=audio
1666
+ )
1667
+
1668
+ # Create RunInput to capture the original user input
1669
+ run_input = TeamRunInput(
1670
+ input_content=input, images=image_artifacts, videos=video_artifacts, audios=audio_artifacts, files=files
1671
+ )
1672
+
1637
1673
  team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
1638
1674
  self._update_metadata(session=team_session)
1639
1675
 
@@ -1671,9 +1707,6 @@ class Team:
1671
1707
  if stream is None:
1672
1708
  stream = False if self.stream is None else self.stream
1673
1709
 
1674
- if store_member_responses is None:
1675
- store_member_responses = False if self.store_member_responses is None else self.store_member_responses
1676
-
1677
1710
  if stream_intermediate_steps is None:
1678
1711
  stream_intermediate_steps = (
1679
1712
  False if self.stream_intermediate_steps is None else self.stream_intermediate_steps
@@ -1706,6 +1739,7 @@ class Team:
1706
1739
  team_id=self.id,
1707
1740
  team_name=self.name,
1708
1741
  metadata=metadata,
1742
+ input=run_input,
1709
1743
  )
1710
1744
 
1711
1745
  run_response.model = self.model.id if self.model is not None else None
@@ -1729,7 +1763,6 @@ class Team:
1729
1763
  audio=audio,
1730
1764
  files=files,
1731
1765
  workflow_context=workflow_context,
1732
- store_member_responses=store_member_responses,
1733
1766
  debug_mode=debug_mode,
1734
1767
  add_history_to_context=add_history_to_context,
1735
1768
  dependencies=dependencies,
@@ -1744,8 +1777,6 @@ class Team:
1744
1777
  num_attempts = retries + 1
1745
1778
 
1746
1779
  for attempt in range(num_attempts):
1747
- log_debug(f"Mode: '{self.mode}'", center=True)
1748
-
1749
1780
  # Run the team
1750
1781
  try:
1751
1782
  run_messages = self._get_run_messages(
@@ -1837,6 +1868,21 @@ class Team:
1837
1868
 
1838
1869
  raise Exception(f"Failed after {num_attempts} attempts.")
1839
1870
 
1871
+ def _store_media(self, run_response: TeamRunOutput, model_response: ModelResponse):
1872
+ """Store media from model response in run_response for persistence"""
1873
+ # Handle generated media fields from ModelResponse (generated media)
1874
+ if model_response.images is not None:
1875
+ for image in model_response.images:
1876
+ self._add_image(image, run_response) # Generated images go to run_response.images
1877
+
1878
+ if model_response.videos is not None:
1879
+ for video in model_response.videos:
1880
+ self._add_video(video, run_response) # Generated videos go to run_response.videos
1881
+
1882
+ if model_response.audios is not None:
1883
+ for audio in model_response.audios:
1884
+ self._add_audio(audio, run_response) # Generated audio go to run_response.audio
1885
+
1840
1886
  def _update_run_response(
1841
1887
  self, model_response: ModelResponse, run_response: TeamRunOutput, run_messages: RunMessages
1842
1888
  ):
@@ -1871,19 +1917,6 @@ class Team:
1871
1917
  else:
1872
1918
  run_response.tools.extend(model_response.tool_executions)
1873
1919
 
1874
- # Handle unified media fields from ModelResponse
1875
- if model_response.images is not None:
1876
- for image in model_response.images:
1877
- self._add_image(image, run_response)
1878
-
1879
- if model_response.videos is not None:
1880
- for video in model_response.videos:
1881
- self._add_video(video, run_response)
1882
-
1883
- if model_response.audios is not None:
1884
- for audio in model_response.audios:
1885
- self._add_audio(audio, run_response)
1886
-
1887
1920
  # Update the run_response audio with the model response audio
1888
1921
  if model_response.audio is not None:
1889
1922
  run_response.response_audio = model_response.audio
@@ -2018,7 +2051,7 @@ class Team:
2018
2051
  stream_model_response=stream_model_response,
2019
2052
  ) # type: ignore
2020
2053
  async for model_response_event in model_stream:
2021
- for chunk in self._handle_model_response_chunk(
2054
+ for event in self._handle_model_response_chunk(
2022
2055
  session=session,
2023
2056
  run_response=run_response,
2024
2057
  full_model_response=full_model_response,
@@ -2028,7 +2061,7 @@ class Team:
2028
2061
  parse_structured_output=self.should_parse_structured_output,
2029
2062
  workflow_context=workflow_context,
2030
2063
  ):
2031
- yield chunk
2064
+ yield event
2032
2065
 
2033
2066
  # Handle structured outputs
2034
2067
  if (self.output_schema is not None) and not self.use_json_mode and (full_model_response.parsed is not None):
@@ -2084,6 +2117,15 @@ class Team:
2084
2117
  model_response_event, tuple(get_args(TeamRunOutputEvent))
2085
2118
  ):
2086
2119
  if self.stream_member_events:
2120
+ if model_response_event.event == TeamRunEvent.custom_event: # type: ignore
2121
+ if hasattr(model_response_event, "team_id"):
2122
+ model_response_event.team_id = self.id
2123
+ if hasattr(model_response_event, "team_name"):
2124
+ model_response_event.team_name = self.name
2125
+ if not model_response_event.session_id: # type: ignore
2126
+ model_response_event.session_id = session.session_id # type: ignore
2127
+ if not model_response_event.run_id: # type: ignore
2128
+ model_response_event.run_id = run_response.run_id # type: ignore
2087
2129
  # We just bubble the event up
2088
2130
  yield self._handle_event(model_response_event, run_response) # type: ignore
2089
2131
  else:
@@ -2937,31 +2979,105 @@ class Team:
2937
2979
  return member.name or entity_id
2938
2980
  return entity_id
2939
2981
 
2940
- def _parse_response_content(
2982
+ def _scrub_media_from_run_output(self, run_response: TeamRunOutput) -> None:
2983
+ """
2984
+ Completely remove all media from RunOutput when store_media=False.
2985
+ This includes media in input, output artifacts, and all messages.
2986
+ """
2987
+ # 1. Scrub RunInput media
2988
+ if run_response.input is not None:
2989
+ run_response.input.images = []
2990
+ run_response.input.videos = []
2991
+ run_response.input.audios = []
2992
+ run_response.input.files = []
2993
+
2994
+ # 2. RunOutput artifact media are skipped since we don't store them when store_media=False
2995
+
2996
+ # 3. Scrub media from all messages
2997
+ if run_response.messages:
2998
+ for message in run_response.messages:
2999
+ self._scrub_media_from_message(message)
3000
+
3001
+ # 4. Scrub media from additional_input messages if any
3002
+ if run_response.additional_input:
3003
+ for message in run_response.additional_input:
3004
+ self._scrub_media_from_message(message)
3005
+
3006
+ # 5. Scrub media from reasoning_messages if any
3007
+ if run_response.reasoning_messages:
3008
+ for message in run_response.reasoning_messages:
3009
+ self._scrub_media_from_message(message)
3010
+
3011
+ def _scrub_media_from_message(self, message: Message) -> None:
3012
+ """Remove all media from a Message object."""
3013
+ # Input media
3014
+ message.images = None
3015
+ message.videos = None
3016
+ message.audio = None
3017
+ message.files = None
3018
+
3019
+ # Output media
3020
+ message.audio_output = None
3021
+ message.image_output = None
3022
+ message.video_output = None
3023
+
3024
+ def _convert_media_to_artifacts(
2941
3025
  self,
2942
- run_response: Union[TeamRunOutput, RunOutput],
2943
- tags_to_include_in_markdown: Set[str],
2944
- show_markdown: bool = True,
2945
- ) -> Any:
2946
- from rich.json import JSON
2947
- from rich.markdown import Markdown
3026
+ images: Optional[Sequence[Image]] = None,
3027
+ videos: Optional[Sequence[Video]] = None,
3028
+ audios: Optional[Sequence[Audio]] = None,
3029
+ ) -> tuple:
3030
+ """Convert raw Image/Video/Audio objects to ImageArtifact/VideoArtifact/AudioArtifact objects."""
3031
+ from uuid import uuid4
2948
3032
 
2949
- if isinstance(run_response.content, str):
2950
- if show_markdown:
2951
- escaped_content = escape_markdown_tags(run_response.content, tags_to_include_in_markdown)
2952
- return Markdown(escaped_content)
2953
- else:
2954
- return run_response.get_content_as_string(indent=4)
2955
- elif isinstance(run_response.content, BaseModel):
2956
- try:
2957
- return JSON(run_response.content.model_dump_json(exclude_none=True), indent=2)
2958
- except Exception as e:
2959
- log_warning(f"Failed to convert response to JSON: {e}")
2960
- else:
2961
- try:
2962
- return JSON(json.dumps(run_response.content), indent=4)
2963
- except Exception as e:
2964
- log_warning(f"Failed to convert response to JSON: {e}")
3033
+ from agno.media import AudioArtifact, ImageArtifact, VideoArtifact
3034
+
3035
+ image_artifacts = None
3036
+ if images:
3037
+ image_artifacts = []
3038
+ for img in images:
3039
+ try:
3040
+ artifact_id = img.id if hasattr(img, "id") and img.id else str(uuid4())
3041
+
3042
+ if img.url:
3043
+ image_artifacts.append(ImageArtifact(id=artifact_id, url=img.url))
3044
+ elif img.content:
3045
+ image_artifacts.append(ImageArtifact(id=artifact_id, content=img.content))
3046
+ except Exception as e:
3047
+ log_warning(f"Error creating ImageArtifact: {e}")
3048
+ continue
3049
+
3050
+ video_artifacts = None
3051
+ if videos:
3052
+ video_artifacts = []
3053
+ for vid in videos:
3054
+ try:
3055
+ artifact_id = vid.id if hasattr(vid, "id") and vid.id else str(uuid4())
3056
+
3057
+ if vid.url:
3058
+ video_artifacts.append(VideoArtifact(id=artifact_id, url=vid.url))
3059
+ elif vid.content:
3060
+ video_artifacts.append(VideoArtifact(id=artifact_id, content=vid.content))
3061
+ except Exception as e:
3062
+ log_warning(f"Error creating VideoArtifact: {e}")
3063
+ continue
3064
+
3065
+ audio_artifacts = None
3066
+ if audios:
3067
+ audio_artifacts = []
3068
+ for aud in audios:
3069
+ try:
3070
+ artifact_id = aud.id if hasattr(aud, "id") and aud.id else str(uuid4())
3071
+
3072
+ if aud.url:
3073
+ audio_artifacts.append(AudioArtifact(id=artifact_id, url=aud.url))
3074
+ elif aud.content:
3075
+ audio_artifacts.append(AudioArtifact(id=artifact_id, base64_audio=aud.content))
3076
+ except Exception as e:
3077
+ log_warning(f"Error creating AudioArtifact: {e}")
3078
+ continue
3079
+
3080
+ return image_artifacts, video_artifacts, audio_artifacts
2965
3081
 
2966
3082
  def cli_app(
2967
3083
  self,
@@ -3632,6 +3748,209 @@ class Team:
3632
3748
  except Exception as e:
3633
3749
  log_warning(f"Failed to resolve context for '{key}': {e}")
3634
3750
 
3751
+ def _collect_joint_images(
3752
+ self,
3753
+ run_input: Optional[TeamRunInput] = None,
3754
+ session: Optional[TeamSession] = None,
3755
+ ) -> Optional[Sequence[Image]]:
3756
+ """Collect images from input, session history, and current run response."""
3757
+ joint_images = []
3758
+
3759
+ # 1. Add images from current input
3760
+ if run_input and run_input.images:
3761
+ for artifact in run_input.images:
3762
+ try:
3763
+ if artifact.url:
3764
+ joint_images.append(Image(url=artifact.url))
3765
+ elif artifact.content:
3766
+ joint_images.append(Image(content=artifact.content))
3767
+ except Exception as e:
3768
+ log_warning(f"Error converting ImageArtifact to Image: {e}")
3769
+ continue
3770
+ log_debug(f"Added {len(run_input.images)} input images to joint list")
3771
+
3772
+ # 2. Add images from session history (from both input and generated sources)
3773
+ try:
3774
+ if session and session.runs:
3775
+ for historical_run in session.runs:
3776
+ # Add generated images from previous runs
3777
+ if historical_run.images:
3778
+ for artifact in historical_run.images:
3779
+ try:
3780
+ if artifact.url:
3781
+ joint_images.append(Image(url=artifact.url))
3782
+ elif artifact.content:
3783
+ joint_images.append(Image(content=artifact.content))
3784
+ except Exception as e:
3785
+ log_warning(f"Error converting historical ImageArtifact to Image: {e}")
3786
+ continue
3787
+ log_debug(
3788
+ f"Added {len(historical_run.images)} generated images from historical run {historical_run.run_id}"
3789
+ )
3790
+
3791
+ # Add input images from previous runs
3792
+ if historical_run.input and historical_run.input.images:
3793
+ for artifact in historical_run.input.images:
3794
+ try:
3795
+ if artifact.url:
3796
+ joint_images.append(Image(url=artifact.url))
3797
+ elif artifact.content:
3798
+ joint_images.append(Image(content=artifact.content))
3799
+ except Exception as e:
3800
+ log_warning(f"Error converting input ImageArtifact to Image: {e}")
3801
+ continue
3802
+ log_debug(
3803
+ f"Added {len(historical_run.input.images)} input images from historical run {historical_run.run_id}"
3804
+ )
3805
+ except Exception as e:
3806
+ log_debug(f"Could not access session history for images: {e}")
3807
+
3808
+ if joint_images:
3809
+ log_debug(f"Images Available to Model: {len(joint_images)} images")
3810
+ return joint_images if joint_images else None
3811
+
3812
+ def _collect_joint_videos(
3813
+ self,
3814
+ run_input: Optional[TeamRunInput] = None,
3815
+ session: Optional[TeamSession] = None,
3816
+ ) -> Optional[Sequence[Video]]:
3817
+ """Collect videos from input, session history, and current run response."""
3818
+ joint_videos = []
3819
+
3820
+ # 1. Add videos from current input
3821
+ if run_input and run_input.videos:
3822
+ for artifact in run_input.videos:
3823
+ try:
3824
+ if artifact.url:
3825
+ joint_videos.append(Video(url=artifact.url))
3826
+ elif artifact.content:
3827
+ joint_videos.append(Video(content=artifact.content))
3828
+ except Exception as e:
3829
+ log_warning(f"Error converting VideoArtifact to Video: {e}")
3830
+ continue
3831
+ log_debug(f"Added {len(run_input.videos)} input videos to joint list")
3832
+
3833
+ # 2. Add videos from session history (from both input and generated sources)
3834
+ try:
3835
+ if session and session.runs:
3836
+ for historical_run in session.runs:
3837
+ # Add generated videos from previous runs
3838
+ if historical_run.videos:
3839
+ for artifact in historical_run.videos:
3840
+ try:
3841
+ if artifact.url:
3842
+ joint_videos.append(Video(url=artifact.url))
3843
+ elif artifact.content:
3844
+ joint_videos.append(Video(content=artifact.content))
3845
+ except Exception as e:
3846
+ log_warning(f"Error converting historical VideoArtifact to Video: {e}")
3847
+ continue
3848
+ log_debug(
3849
+ f"Added {len(historical_run.videos)} generated videos from historical run {historical_run.run_id}"
3850
+ )
3851
+
3852
+ # Add input videos from previous runs
3853
+ if historical_run.input and historical_run.input.videos:
3854
+ for artifact in historical_run.input.videos:
3855
+ try:
3856
+ if artifact.url:
3857
+ joint_videos.append(Video(url=artifact.url))
3858
+ elif artifact.content:
3859
+ joint_videos.append(Video(content=artifact.content))
3860
+ except Exception as e:
3861
+ log_warning(f"Error converting input VideoArtifact to Video: {e}")
3862
+ continue
3863
+ log_debug(
3864
+ f"Added {len(historical_run.input.videos)} input videos from historical run {historical_run.run_id}"
3865
+ )
3866
+ except Exception as e:
3867
+ log_debug(f"Could not access session history for videos: {e}")
3868
+
3869
+ if joint_videos:
3870
+ log_debug(f"Videos Available to Model: {len(joint_videos)} videos")
3871
+ return joint_videos if joint_videos else None
3872
+
3873
+ def _collect_joint_audios(
3874
+ self,
3875
+ run_input: Optional[TeamRunInput] = None,
3876
+ session: Optional[TeamSession] = None,
3877
+ ) -> Optional[Sequence[Audio]]:
3878
+ """Collect audios from input, session history, and current run response."""
3879
+ joint_audios = []
3880
+
3881
+ # 1. Add audios from current input
3882
+ if run_input and run_input.audios:
3883
+ for artifact in run_input.audios:
3884
+ try:
3885
+ if artifact.url:
3886
+ joint_audios.append(Audio(url=artifact.url))
3887
+ elif artifact.base64_audio:
3888
+ joint_audios.append(Audio(content=artifact.base64_audio))
3889
+ except Exception as e:
3890
+ log_warning(f"Error converting AudioArtifact to Audio: {e}")
3891
+ continue
3892
+ log_debug(f"Added {len(run_input.audios)} input audios to joint list")
3893
+
3894
+ # 2. Add audios from session history (from both input and generated sources)
3895
+ try:
3896
+ if session and session.runs:
3897
+ for historical_run in session.runs:
3898
+ # Add generated audios from previous runs
3899
+ if historical_run.audio:
3900
+ for artifact in historical_run.audio:
3901
+ try:
3902
+ if artifact.url:
3903
+ joint_audios.append(Audio(url=artifact.url))
3904
+ elif artifact.base64_audio:
3905
+ joint_audios.append(Audio(content=artifact.base64_audio))
3906
+ except Exception as e:
3907
+ log_warning(f"Error converting historical AudioArtifact to Audio: {e}")
3908
+ continue
3909
+ log_debug(
3910
+ f"Added {len(historical_run.audio)} generated audios from historical run {historical_run.run_id}"
3911
+ )
3912
+
3913
+ # Add input audios from previous runs
3914
+ if historical_run.input and historical_run.input.audios:
3915
+ for artifact in historical_run.input.audios:
3916
+ try:
3917
+ if artifact.url:
3918
+ joint_audios.append(Audio(url=artifact.url))
3919
+ elif artifact.base64_audio:
3920
+ joint_audios.append(Audio(content=artifact.base64_audio))
3921
+ except Exception as e:
3922
+ log_warning(f"Error converting input AudioArtifact to Audio: {e}")
3923
+ continue
3924
+ log_debug(
3925
+ f"Added {len(historical_run.input.audios)} input audios from historical run {historical_run.run_id}"
3926
+ )
3927
+ except Exception as e:
3928
+ log_debug(f"Could not access session history for audios: {e}")
3929
+
3930
+ if joint_audios:
3931
+ log_debug(f"Audios Available to Model: {len(joint_audios)} audios")
3932
+ return joint_audios if joint_audios else None
3933
+
3934
+ def _collect_joint_files(
3935
+ self,
3936
+ run_input: Optional[TeamRunInput] = None,
3937
+ ) -> Optional[Sequence[File]]:
3938
+ """Collect files from input and session history."""
3939
+ from agno.utils.log import log_debug
3940
+
3941
+ joint_files: List[File] = []
3942
+
3943
+ # 1. Add files from current input
3944
+ if run_input and run_input.files:
3945
+ joint_files.extend(run_input.files)
3946
+
3947
+ # TODO: Files aren't stored in session history yet and dont have a FileArtifact
3948
+
3949
+ if joint_files:
3950
+ log_debug(f"Files Available to Model: {len(joint_files)} files")
3951
+
3952
+ return joint_files if joint_files else None
3953
+
3635
3954
  def determine_tools_for_model(
3636
3955
  self,
3637
3956
  model: Model,
@@ -3648,7 +3967,6 @@ class Team:
3648
3967
  audio: Optional[Sequence[Audio]] = None,
3649
3968
  files: Optional[Sequence[File]] = None,
3650
3969
  workflow_context: Optional[Dict] = None,
3651
- store_member_responses: bool = False,
3652
3970
  debug_mode: Optional[bool] = None,
3653
3971
  add_history_to_context: Optional[bool] = None,
3654
3972
  dependencies: Optional[Dict[str, Any]] = None,
@@ -3700,94 +4018,46 @@ class Team:
3700
4018
  if self.knowledge is not None and self.update_knowledge:
3701
4019
  _tools.append(self.add_to_knowledge)
3702
4020
 
3703
- if self.mode == "route":
4021
+ # Get the user message if we are using the input directly
4022
+ user_message = None
4023
+ if self.determine_input_for_members is False:
3704
4024
  user_message = self._get_user_message(
3705
4025
  run_response=run_response,
3706
4026
  session_state=session_state,
3707
4027
  input_message=input_message,
4028
+ user_id=user_id,
3708
4029
  audio=audio,
3709
4030
  images=images,
3710
4031
  videos=videos,
3711
4032
  files=files,
3712
- user_id=user_id,
3713
4033
  dependencies=dependencies,
3714
4034
  add_dependencies_to_context=add_dependencies_to_context,
3715
4035
  metadata=metadata,
3716
4036
  )
3717
- forward_task_func: Function = self._get_forward_task_function(
3718
- input=user_message,
3719
- run_response=run_response,
3720
- session_state=session_state,
3721
- team_run_context=team_run_context,
3722
- user_id=user_id,
3723
- session=session,
3724
- stream=self.stream or False,
3725
- stream_intermediate_steps=self.stream_intermediate_steps,
3726
- async_mode=async_mode,
3727
- images=images, # type: ignore
3728
- videos=videos, # type: ignore
3729
- audio=audio, # type: ignore
3730
- files=files, # type: ignore
3731
- knowledge_filters=knowledge_filters,
3732
- workflow_context=workflow_context,
3733
- store_member_responses=store_member_responses,
3734
- debug_mode=debug_mode,
3735
- add_history_to_context=add_history_to_context,
3736
- dependencies=dependencies,
3737
- )
3738
- _tools.append(forward_task_func)
3739
- if self.get_member_information_tool:
3740
- _tools.append(self.get_member_information)
3741
-
3742
- elif self.mode == "coordinate":
3743
- _tools.append(
3744
- self._get_delegate_task_function(
3745
- run_response=run_response,
3746
- session=session,
3747
- session_state=session_state,
3748
- team_run_context=team_run_context,
3749
- user_id=user_id,
3750
- stream=self.stream or False,
3751
- stream_intermediate_steps=self.stream_intermediate_steps,
3752
- async_mode=async_mode,
3753
- images=images, # type: ignore
3754
- videos=videos, # type: ignore
3755
- audio=audio, # type: ignore
3756
- files=files, # type: ignore
3757
- knowledge_filters=knowledge_filters,
3758
- workflow_context=workflow_context,
3759
- store_member_responses=store_member_responses,
3760
- debug_mode=debug_mode,
3761
- add_history_to_context=add_history_to_context,
3762
- dependencies=dependencies,
3763
- )
3764
- )
3765
- if self.get_member_information_tool:
3766
- _tools.append(self.get_member_information)
3767
4037
 
3768
- elif self.mode == "collaborate":
3769
- run_member_agents_func = self._get_run_member_agents_function(
3770
- run_response=run_response,
3771
- session=session,
3772
- session_state=session_state,
3773
- team_run_context=team_run_context,
3774
- user_id=user_id,
3775
- stream=self.stream or False,
3776
- stream_intermediate_steps=self.stream_intermediate_steps,
3777
- async_mode=async_mode,
3778
- images=images, # type: ignore
3779
- videos=videos, # type: ignore
3780
- audio=audio, # type: ignore
3781
- files=files, # type: ignore
3782
- workflow_context=workflow_context,
3783
- store_member_responses=store_member_responses,
3784
- debug_mode=debug_mode,
3785
- add_history_to_context=add_history_to_context,
3786
- )
3787
- _tools.append(run_member_agents_func)
4038
+ delegate_task_func = self._get_delegate_task_function(
4039
+ run_response=run_response,
4040
+ session=session,
4041
+ session_state=session_state,
4042
+ team_run_context=team_run_context,
4043
+ input=user_message,
4044
+ user_id=user_id,
4045
+ stream=self.stream or False,
4046
+ stream_intermediate_steps=self.stream_intermediate_steps,
4047
+ async_mode=async_mode,
4048
+ images=images, # type: ignore
4049
+ videos=videos, # type: ignore
4050
+ audio=audio, # type: ignore
4051
+ files=files, # type: ignore
4052
+ knowledge_filters=knowledge_filters,
4053
+ workflow_context=workflow_context,
4054
+ debug_mode=debug_mode,
4055
+ add_history_to_context=add_history_to_context,
4056
+ )
3788
4057
 
3789
- if self.get_member_information_tool:
3790
- _tools.append(self.get_member_information)
4058
+ _tools.append(delegate_task_func)
4059
+ if self.get_member_information_tool:
4060
+ _tools.append(self.get_member_information)
3791
4061
 
3792
4062
  self._functions_for_model = {}
3793
4063
  self._tools_for_model = []
@@ -3866,6 +4136,29 @@ class Team:
3866
4136
  except Exception as e:
3867
4137
  log_warning(f"Could not add tool {tool}: {e}")
3868
4138
 
4139
+ if self._functions_for_model:
4140
+ from inspect import signature
4141
+
4142
+ # Check if any functions need media before collecting
4143
+ needs_media = any(
4144
+ any(param in signature(func.entrypoint).parameters for param in ["images", "videos", "audios", "files"])
4145
+ for func in self._functions_for_model.values()
4146
+ if func.entrypoint is not None
4147
+ )
4148
+
4149
+ if needs_media:
4150
+ # Only collect media if functions actually need them
4151
+ joint_images = self._collect_joint_images(run_response.input, session)
4152
+ joint_files = self._collect_joint_files(run_response.input)
4153
+ joint_audios = self._collect_joint_audios(run_response.input, session)
4154
+ joint_videos = self._collect_joint_videos(run_response.input, session)
4155
+
4156
+ for func in self._functions_for_model.values():
4157
+ func._images = joint_images
4158
+ func._files = joint_files
4159
+ func._audios = joint_audios
4160
+ func._videos = joint_videos
4161
+
3869
4162
  def get_members_system_message_content(self, indent: int = 0) -> str:
3870
4163
  system_message_content = ""
3871
4164
  for idx, member in enumerate(self.members):
@@ -4039,9 +4332,17 @@ class Team:
4039
4332
  system_message_content += "</team_members>\n"
4040
4333
 
4041
4334
  system_message_content += "\n<how_to_respond>\n"
4042
- if self.mode == "coordinate":
4335
+
4336
+ if self.delegate_task_to_all_members:
4337
+ system_message_content += (
4338
+ "- You can either respond directly or use the `delegate_task_to_members` tool to delegate a task to all members in your team to get a collaborative response.\n"
4339
+ "- To delegate a task to all members in your team, call `delegate_task_to_members` ONLY once. This will delegate a task to all members in your team.\n"
4340
+ "- Analyze the responses from all members and evaluate whether the task has been completed.\n"
4341
+ "- If you feel the task has been completed, you can stop and respond to the user.\n"
4342
+ )
4343
+ else:
4043
4344
  system_message_content += (
4044
- "- Your role is to forward tasks to members in your team with the highest likelihood of completing the user's request.\n"
4345
+ "- Your role is to delegate tasks to members in your team with the highest likelihood of completing the user's request.\n"
4045
4346
  "- Carefully analyze the tools available to the members and their roles before delegating tasks.\n"
4046
4347
  "- You cannot use a member tool directly. You can only delegate tasks to members.\n"
4047
4348
  "- When you delegate a task to another member, make sure to include:\n"
@@ -4055,24 +4356,6 @@ class Team:
4055
4356
  "- For simple greetings, thanks, or questions about the team itself, you should respond directly.\n"
4056
4357
  "- For all work requests, tasks, or questions requiring expertise, route to appropriate team members.\n"
4057
4358
  )
4058
- elif self.mode == "route":
4059
- system_message_content += (
4060
- "- Your role is to forward tasks to members in your team with the highest likelihood of completing the user's request.\n"
4061
- "- Carefully analyze the tools available to the members and their roles before forwarding tasks.\n"
4062
- "- When you forward a task to another Agent, make sure to include:\n"
4063
- " - member_id (str): The ID of the member to forward the task to. Use only the ID of the member, not the ID of the team followed by the ID of the member.\n"
4064
- " - expected_output (str): The expected output.\n"
4065
- "- You can forward tasks to multiple members at once.\n"
4066
- "- For simple greetings, thanks, or questions about the team itself, you should respond directly.\n"
4067
- "- For all work requests, tasks, or questions requiring expertise, route to appropriate team members.\n"
4068
- )
4069
- elif self.mode == "collaborate":
4070
- system_message_content += (
4071
- "- You can either respond directly or use the `run_member_agents` tool to run all members in your team to get a collaborative response.\n"
4072
- "- To run the members in your team, call `run_member_agents` ONLY once. This will run all members in your team.\n"
4073
- "- Analyze the responses from all members and evaluate whether the task has been completed.\n"
4074
- "- If you feel the task has been completed, you can stop and respond to the user.\n"
4075
- )
4076
4359
  system_message_content += "</how_to_respond>\n\n"
4077
4360
 
4078
4361
  # Attached media
@@ -4357,10 +4640,10 @@ class Team:
4357
4640
  return Message(
4358
4641
  role="user",
4359
4642
  content="",
4360
- images=images,
4361
- audio=audio,
4362
- videos=videos,
4363
- files=files,
4643
+ images=None if not self.send_media_to_model else images,
4644
+ audio=None if not self.send_media_to_model else audio,
4645
+ videos=None if not self.send_media_to_model else videos,
4646
+ files=None if not self.send_media_to_model else files,
4364
4647
  **kwargs,
4365
4648
  )
4366
4649
  else:
@@ -4381,10 +4664,10 @@ class Team:
4381
4664
  return Message(
4382
4665
  role="user",
4383
4666
  content=input_content,
4384
- images=images,
4385
- audio=audio,
4386
- videos=videos,
4387
- files=files,
4667
+ images=None if not self.send_media_to_model else images,
4668
+ audio=None if not self.send_media_to_model else audio,
4669
+ videos=None if not self.send_media_to_model else videos,
4670
+ files=None if not self.send_media_to_model else files,
4388
4671
  **kwargs,
4389
4672
  )
4390
4673
 
@@ -4473,10 +4756,10 @@ class Team:
4473
4756
  return Message(
4474
4757
  role="user",
4475
4758
  content=user_msg_content,
4476
- audio=audio,
4477
- images=images,
4478
- videos=videos,
4479
- files=files,
4759
+ images=None if not self.send_media_to_model else images,
4760
+ audio=None if not self.send_media_to_model else audio,
4761
+ videos=None if not self.send_media_to_model else videos,
4762
+ files=None if not self.send_media_to_model else files,
4480
4763
  **kwargs,
4481
4764
  )
4482
4765
 
@@ -4865,7 +5148,7 @@ class Team:
4865
5148
 
4866
5149
  return None
4867
5150
 
4868
- def _get_run_member_agents_function(
5151
+ def _get_delegate_task_function(
4869
5152
  self,
4870
5153
  run_response: TeamRunOutput,
4871
5154
  session: TeamSession,
@@ -4875,12 +5158,13 @@ class Team:
4875
5158
  stream: bool = False,
4876
5159
  stream_intermediate_steps: bool = False,
4877
5160
  async_mode: bool = False,
5161
+ input: Optional[Message] = None, # Used for determine_input_for_memberss=False
4878
5162
  images: Optional[List[Image]] = None,
4879
5163
  videos: Optional[List[Video]] = None,
4880
5164
  audio: Optional[List[Audio]] = None,
4881
5165
  files: Optional[List[File]] = None,
5166
+ knowledge_filters: Optional[Dict[str, Any]] = None,
4882
5167
  workflow_context: Optional[Dict] = None,
4883
- store_member_responses: bool = False,
4884
5168
  debug_mode: Optional[bool] = None,
4885
5169
  add_history_to_context: Optional[bool] = None,
4886
5170
  ) -> Function:
@@ -4893,555 +5177,84 @@ class Team:
4893
5177
  if not files:
4894
5178
  files = []
4895
5179
 
4896
- def run_member_agents(
4897
- task_description: str, expected_output: Optional[str] = None
4898
- ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
4899
- """
4900
- Send the same task to all the member agents and return the responses.
4901
-
4902
- Args:
4903
- task_description (str): The task description to send to the member agents.
4904
- expected_output (str, optional): The expected output from the member agents.
4905
-
4906
- Returns:
4907
- str: The responses from the member agents.
4908
- """
4909
- # Make sure for the member agent, we are using the agent logger
4910
- use_agent_logger()
5180
+ def _setup_delegate_task_to_member(
5181
+ member_agent: Union[Agent, "Team"], task_description: str, expected_output: Optional[str] = None
5182
+ ):
5183
+ # 1. Initialize the member agent
5184
+ self._initialize_member(member_agent)
4911
5185
 
4912
- # 1. Determine team context to send
5186
+ # 2. Determine team context to send
4913
5187
  team_member_interactions_str = self._determine_team_member_interactions(
4914
5188
  team_run_context, images, videos, audio
4915
5189
  )
4916
5190
 
4917
- # 2. Create the member agent task
4918
- member_agent_task = format_member_agent_task(
4919
- task_description, expected_output, team_member_interactions_str
4920
- )
5191
+ member_agent_task: Union[str, Message]
5192
+
5193
+ # 3. Create the member agent task or use the input directly
5194
+ if self.determine_input_for_members is False:
5195
+ member_agent_task = input # type: ignore
5196
+ else:
5197
+ # Don't override the expected output of a member agent
5198
+ if member_agent.expected_output is not None:
5199
+ expected_output = None
4921
5200
 
4922
- for member_agent_index, member_agent in enumerate(self.members):
4923
- self._initialize_member(member_agent)
5201
+ member_agent_task = format_member_agent_task( # type: ignore
5202
+ task_description, expected_output, team_member_interactions_str
5203
+ )
4924
5204
 
4925
- # Add history for the member if enabled
4926
- history = None
4927
- if member_agent.add_history_to_context:
4928
- history = self._get_history_for_member_agent(session, member_agent)
4929
- if history:
5205
+ # 4. Add history for the member if enabled
5206
+ history = None
5207
+ if member_agent.add_history_to_context:
5208
+ history = self._get_history_for_member_agent(session, member_agent)
5209
+ if history:
5210
+ if isinstance(member_agent_task, str):
4930
5211
  history.append(Message(role="user", content=member_agent_task))
5212
+ else:
5213
+ history.append(member_agent_task)
4931
5214
 
4932
- member_session_state_copy = copy(session_state)
4933
- if stream:
4934
- member_agent_run_response_stream = member_agent.run(
4935
- input=member_agent_task if history is None else history,
4936
- user_id=user_id,
4937
- # All members have the same session_id
4938
- session_id=session.session_id,
4939
- session_state=member_session_state_copy, # Send a copy to the agent
4940
- images=images,
4941
- videos=videos,
4942
- audio=audio,
4943
- files=files,
4944
- stream=True,
4945
- stream_intermediate_steps=stream_intermediate_steps,
4946
- workflow_context=workflow_context,
4947
- debug_mode=debug_mode,
4948
- add_history_to_context=add_history_to_context,
4949
- yield_run_response=True,
4950
- )
4951
- member_agent_run_response = None
4952
- for member_agent_run_response_chunk in member_agent_run_response_stream:
4953
- # If we get the full run response, we can break out of the loop
4954
- if isinstance(member_agent_run_response_chunk, TeamRunOutput) or isinstance(
4955
- member_agent_run_response_chunk, RunOutput
4956
- ):
4957
- member_agent_run_response = member_agent_run_response_chunk # type: ignore
4958
- break
4959
- check_if_run_cancelled(member_agent_run_response_chunk)
4960
- yield member_agent_run_response_chunk
4961
-
4962
- else:
4963
- member_agent_run_response = member_agent.run( # type: ignore
4964
- input=member_agent_task if history is None else history,
4965
- user_id=user_id,
4966
- # All members have the same session_id
4967
- session_id=session.session_id,
4968
- session_state=member_session_state_copy, # Send a copy to the agent
4969
- images=images,
4970
- videos=videos,
4971
- audio=audio,
4972
- files=files,
4973
- stream=False,
4974
- workflow_context=workflow_context,
4975
- debug_mode=debug_mode,
4976
- add_history_to_context=add_history_to_context,
4977
- )
4978
-
4979
- check_if_run_cancelled(member_agent_run_response) # type: ignore
4980
-
4981
- try:
4982
- if member_agent_run_response.content is None and ( # type: ignore
4983
- member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0 # type: ignore
4984
- ):
4985
- yield f"Agent {member_agent.name}: No response from the member agent."
4986
- elif isinstance(member_agent_run_response.content, str): # type: ignore
4987
- if len(member_agent_run_response.content.strip()) > 0: # type: ignore
4988
- yield f"Agent {member_agent.name}: {member_agent_run_response.content}" # type: ignore
4989
- elif (
4990
- member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0 # type: ignore
4991
- ):
4992
- yield f"Agent {member_agent.name}: {','.join([tool.result for tool in member_agent_run_response.tools])}" # type: ignore
4993
- elif issubclass(type(member_agent_run_response.content), BaseModel): # type: ignore
4994
- yield f"Agent {member_agent.name}: {member_agent_run_response.content.model_dump_json(indent=2)}" # type: ignore
4995
- else:
4996
- import json
4997
-
4998
- yield f"Agent {member_agent.name}: {json.dumps(member_agent_run_response.content, indent=2)}" # type: ignore
4999
- except Exception as e:
5000
- yield f"Agent {member_agent.name}: Error - {str(e)}"
5001
-
5002
- # Add team run id to the member run
5003
- if member_agent_run_response is not None:
5004
- member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5005
-
5006
- # Update the memory
5007
- member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5008
- self._add_interaction_to_team_run_context(
5009
- team_run_context=team_run_context,
5010
- member_name=member_name,
5011
- task=task_description,
5012
- run_response=member_agent_run_response, # type: ignore
5013
- )
5014
-
5015
- # Add the member run to the team run response
5016
- if self.store_member_responses and run_response and member_agent_run_response:
5017
- run_response.add_member_run(member_agent_run_response)
5018
-
5019
- # Add the member run to the team session
5020
- if member_agent_run_response:
5021
- session.upsert_run(member_agent_run_response)
5022
-
5023
- # Update team session state
5024
- merge_dictionaries(session_state, member_session_state_copy) # type: ignore
5025
-
5026
- # Update the team media
5027
- if member_agent_run_response is not None:
5028
- self._update_team_media(member_agent_run_response) # type: ignore
5029
-
5030
- # Afterward, switch back to the team logger
5031
- use_team_logger()
5032
-
5033
- async def arun_member_agents(
5034
- task_description: str, expected_output: Optional[str] = None
5035
- ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5036
- """
5037
- Send the same task to all the member agents and return the responses.
5038
-
5039
- Args:
5040
- task_description (str): The task description to send to the member agents.
5041
- expected_output (str): The expected output from the member agents.
5042
-
5043
- Returns:
5044
- str: The responses from the member agents.
5045
- """
5046
- # Make sure for the member agent, we are using the agent logger
5047
- use_agent_logger()
5048
-
5049
- # 1. Determine team context to send
5050
- team_member_interactions_str = self._determine_team_member_interactions(
5051
- team_run_context, images, videos, audio
5052
- )
5053
-
5054
- if stream:
5055
- # Concurrent streaming: launch each member as a streaming worker and merge events
5056
- done_marker = object()
5057
- queue: "asyncio.Queue[Union[RunOutputEvent, TeamRunOutputEvent, str, object]]" = asyncio.Queue()
5058
-
5059
- async def stream_member(agent: Union[Agent, "Team"], idx: int) -> None:
5060
- # Compute expected output per agent (do not mutate shared var)
5061
- local_expected_output = None if agent.expected_output is not None else expected_output
5062
-
5063
- member_agent_task = format_member_agent_task(
5064
- task_description, local_expected_output, team_member_interactions_str
5065
- )
5066
-
5067
- # Add history for the member if enabled
5068
- history = None
5069
- if agent.add_history_to_context:
5070
- history = self._get_history_for_member_agent(session, agent)
5071
- if history:
5072
- history.append(Message(role="user", content=member_agent_task))
5073
-
5074
- member_session_state_copy = copy(session_state)
5075
-
5076
- # Stream events from the member
5077
- member_stream = agent.arun( # type: ignore
5078
- input=member_agent_task if history is None else history,
5079
- user_id=user_id,
5080
- session_id=session.session_id,
5081
- session_state=member_session_state_copy, # Send a copy to the agent
5082
- images=images,
5083
- videos=videos,
5084
- audio=audio,
5085
- files=files,
5086
- stream=True,
5087
- stream_intermediate_steps=stream_intermediate_steps,
5088
- debug_mode=debug_mode,
5089
- yield_run_response=True,
5090
- )
5091
- member_agent_run_response = None
5092
- try:
5093
- async for member_agent_run_output_event in member_stream:
5094
- if isinstance(member_agent_run_output_event, TeamRunOutput) or isinstance(
5095
- member_agent_run_output_event, RunOutput
5096
- ):
5097
- member_agent_run_response = member_agent_run_output_event # type: ignore
5098
- break
5099
- check_if_run_cancelled(member_agent_run_output_event)
5100
- await queue.put(member_agent_run_output_event)
5101
- finally:
5102
- # Add team run id to the member run
5103
- if member_agent_run_response is not None:
5104
- member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5105
-
5106
- member_name = agent.name if agent.name else f"agent_{idx}"
5107
- self._add_interaction_to_team_run_context(
5108
- team_run_context=team_run_context,
5109
- member_name=member_name,
5110
- task=task_description,
5111
- run_response=member_agent_run_response, # type: ignore
5112
- )
5113
-
5114
- # Add the member run to the team run response
5115
- if store_member_responses and run_response:
5116
- run_response.add_member_run(member_agent_run_response) # type: ignore
5117
-
5118
- # Add the member run to the team session
5119
- session.upsert_run(member_agent_run_response) # type: ignore
5120
-
5121
- # Update team session state
5122
- merge_dictionaries(session_state, member_session_state_copy) # type: ignore
5123
-
5124
- # Update the team media
5125
- if member_agent_run_response is not None:
5126
- self._update_team_media(member_agent_run_response)
5127
-
5128
- # Signal completion for this member
5129
- await queue.put(done_marker)
5130
-
5131
- # Initialize and launch all members
5132
- tasks: List[asyncio.Task[None]] = []
5133
- for member_agent_index, member_agent in enumerate(self.members):
5134
- current_agent = member_agent
5135
- current_index = member_agent_index
5136
- self._initialize_member(current_agent)
5137
- tasks.append(asyncio.create_task(stream_member(current_agent, current_index)))
5138
-
5139
- # Drain queue until all members reported done
5140
- completed = 0
5141
- try:
5142
- while completed < len(tasks):
5143
- item = await queue.get()
5144
- if item is done_marker:
5145
- completed += 1
5146
- else:
5147
- yield item # type: ignore
5148
- finally:
5149
- # Ensure tasks do not leak on cancellation
5150
- for t in tasks:
5151
- if not t.done():
5152
- t.cancel()
5153
- # Await cancellation to suppress warnings
5154
- for t in tasks:
5155
- with contextlib.suppress(Exception):
5156
- await t
5157
- else:
5158
- # Non-streaming concurrent run of members; collect results when done
5159
- tasks = []
5160
- for member_agent_index, member_agent in enumerate(self.members):
5161
- current_agent = member_agent
5162
- current_index = member_agent_index
5163
- self._initialize_member(current_agent)
5164
-
5165
- # Compute expected output per agent (do not mutate shared var)
5166
- local_expected_output = None if current_agent.expected_output is not None else expected_output
5167
-
5168
- member_agent_task = format_member_agent_task(
5169
- task_description, local_expected_output, team_member_interactions_str
5170
- )
5171
-
5172
- # Add history for the member if enabled
5173
- history = None
5174
- if current_agent.add_history_to_context:
5175
- history = self._get_history_for_member_agent(session, current_agent)
5176
- if history:
5177
- history.append(Message(role="user", content=member_agent_task))
5178
-
5179
- async def run_member_agent(agent=current_agent) -> str:
5180
- member_session_state_copy = copy(session_state)
5181
- member_agent_run_response = await agent.arun(
5182
- input=member_agent_task if history is None else history,
5183
- user_id=user_id,
5184
- # All members have the same session_id
5185
- session_id=session.session_id,
5186
- images=images,
5187
- videos=videos,
5188
- audio=audio,
5189
- files=files,
5190
- stream=False,
5191
- debug_mode=debug_mode,
5192
- )
5193
- check_if_run_cancelled(member_agent_run_response)
5194
-
5195
- # Add team run id to the member run
5196
- if member_agent_run_response is not None:
5197
- member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5198
-
5199
- # Update the memory
5200
- member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5201
- self._add_interaction_to_team_run_context(
5202
- team_run_context=team_run_context,
5203
- member_name=member_name,
5204
- task=task_description,
5205
- run_response=member_agent_run_response, # type: ignore
5206
- )
5207
-
5208
- # Add the member run to the team run response
5209
- if store_member_responses and run_response and member_agent_run_response:
5210
- run_response.add_member_run(member_agent_run_response)
5211
-
5212
- # Add the member run to the team session
5213
- if member_agent_run_response:
5214
- session.upsert_run(member_agent_run_response)
5215
-
5216
- # Update team session state
5217
- merge_dictionaries(session_state, member_session_state_copy) # type: ignore
5218
-
5219
- # Update the team media
5220
- if member_agent_run_response is not None:
5221
- self._update_team_media(member_agent_run_response) # type: ignore
5222
-
5223
- try:
5224
- if member_agent_run_response.content is None and (
5225
- member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0
5226
- ):
5227
- return f"Agent {member_name}: No response from the member agent."
5228
- elif isinstance(member_agent_run_response.content, str):
5229
- if len(member_agent_run_response.content.strip()) > 0:
5230
- return f"Agent {member_name}: {member_agent_run_response.content}"
5231
- elif (
5232
- member_agent_run_response.tools is not None
5233
- and len(member_agent_run_response.tools) > 0
5234
- ):
5235
- return f"Agent {member_name}: {','.join([tool.result for tool in member_agent_run_response.tools])}"
5236
- elif issubclass(type(member_agent_run_response.content), BaseModel):
5237
- return f"Agent {member_name}: {member_agent_run_response.content.model_dump_json(indent=2)}" # type: ignore
5238
- else:
5239
- import json
5240
-
5241
- return f"Agent {member_name}: {json.dumps(member_agent_run_response.content, indent=2)}"
5242
- except Exception as e:
5243
- return f"Agent {member_name}: Error - {str(e)}"
5244
-
5245
- return f"Agent {member_name}: No Response"
5246
-
5247
- tasks.append(run_member_agent) # type: ignore
5248
-
5249
- results = await asyncio.gather(*[task() for task in tasks]) # type: ignore
5250
- for result in results:
5251
- yield result
5252
-
5253
- # Afterward, switch back to the team logger
5254
- use_team_logger()
5255
-
5256
- if async_mode:
5257
- run_member_agents_function = arun_member_agents # type: ignore
5258
- else:
5259
- run_member_agents_function = run_member_agents # type: ignore
5260
-
5261
- run_member_agents_func = Function.from_callable(
5262
- run_member_agents_function, name="run_member_agents", strict=True
5263
- )
5264
-
5265
- return run_member_agents_func
5266
-
5267
- def _get_delegate_task_function(
5268
- self,
5269
- run_response: TeamRunOutput,
5270
- session: TeamSession,
5271
- session_state: Dict[str, Any],
5272
- team_run_context: Dict[str, Any],
5273
- user_id: Optional[str] = None,
5274
- stream: bool = False,
5275
- stream_intermediate_steps: bool = False,
5276
- async_mode: bool = False,
5277
- images: Optional[List[Image]] = None,
5278
- videos: Optional[List[Video]] = None,
5279
- audio: Optional[List[Audio]] = None,
5280
- files: Optional[List[File]] = None,
5281
- knowledge_filters: Optional[Dict[str, Any]] = None,
5282
- workflow_context: Optional[Dict] = None,
5283
- store_member_responses: bool = False,
5284
- debug_mode: Optional[bool] = None,
5285
- add_history_to_context: Optional[bool] = None,
5286
- dependencies: Optional[Dict[str, Any]] = None,
5287
- ) -> Function:
5288
- if not images:
5289
- images = []
5290
- if not videos:
5291
- videos = []
5292
- if not audio:
5293
- audio = []
5294
- if not files:
5295
- files = []
5296
-
5297
- def delegate_task_to_member(
5298
- member_id: str, task_description: str, expected_output: Optional[str] = None
5299
- ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5300
- """Use this function to delegate a task to the selected team member.
5301
- You must provide a clear and concise description of the task the member should achieve AND the expected output.
5302
-
5303
- Args:
5304
- 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.
5305
- task_description (str): A clear and concise description of the task the member should achieve.
5306
- expected_output (str, optional): The expected output from the member (optional).
5307
- Returns:
5308
- str: The result of the delegated task.
5309
- """
5310
- # 1. Find the member agent using the helper function
5311
- result = self._find_member_by_id(member_id)
5312
- history = None
5313
- if result is None:
5314
- 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)}"
5315
- return
5316
-
5317
- member_agent_index, member_agent = result
5318
- self._initialize_member(member_agent)
5319
-
5320
- # 2. Determine team context to send
5321
- team_member_interactions_str = self._determine_team_member_interactions(
5322
- team_run_context, images, videos, audio
5323
- )
5324
-
5325
- # 3. Create the member agent task
5326
- # Don't override the expected output of a member agent
5327
- if member_agent.expected_output is not None:
5328
- expected_output = None
5329
- member_agent_task = format_member_agent_task(
5330
- task_description, expected_output, team_member_interactions_str
5331
- )
5332
-
5333
- # 4. Add history for the member if enabled
5334
- if member_agent.add_history_to_context:
5335
- history = self._get_history_for_member_agent(session, member_agent)
5336
- if history:
5337
- history.append(Message(role="user", content=member_agent_task))
5338
-
5339
- # Make sure for the member agent, we are using the agent logger
5340
- use_agent_logger()
5341
-
5342
- # Handle enable_agentic_knowledge_filters on the member agent
5343
- if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
5344
- member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
5345
-
5346
- member_session_state_copy = copy(session_state)
5347
- if stream:
5348
- member_agent_run_response_stream = member_agent.run(
5349
- input=member_agent_task if history is None else history,
5350
- user_id=user_id,
5351
- # All members have the same session_id
5352
- session_id=session.session_id,
5353
- session_state=member_session_state_copy, # Send a copy to the agent
5354
- images=images,
5355
- videos=videos,
5356
- audio=audio,
5357
- files=files,
5358
- stream=True,
5359
- stream_intermediate_steps=stream_intermediate_steps,
5360
- debug_mode=debug_mode,
5361
- add_history_to_context=add_history_to_context,
5362
- workflow_context=workflow_context,
5363
- knowledge_filters=knowledge_filters
5364
- if not member_agent.knowledge_filters and member_agent.knowledge
5365
- else None,
5366
- yield_run_response=True,
5367
- )
5368
- member_agent_run_response = None
5369
- for member_agent_run_output_event in member_agent_run_response_stream:
5370
- if isinstance(member_agent_run_output_event, TeamRunOutput) or isinstance(
5371
- member_agent_run_output_event, RunOutput
5372
- ):
5373
- member_agent_run_response = member_agent_run_output_event # type: ignore
5374
- break
5375
- check_if_run_cancelled(member_agent_run_output_event)
5376
-
5377
- # Yield the member event directly
5378
- yield member_agent_run_output_event
5379
- else:
5380
- member_agent_run_response = member_agent.run( # type: ignore
5381
- input=member_agent_task if history is None else history,
5382
- user_id=user_id,
5383
- # All members have the same session_id
5384
- session_id=session.session_id,
5385
- session_state=member_session_state_copy, # Send a copy to the agent
5386
- images=images,
5387
- videos=videos,
5388
- audio=audio,
5389
- files=files,
5390
- stream=False,
5391
- debug_mode=debug_mode,
5392
- add_history_to_context=add_history_to_context,
5393
- knowledge_filters=knowledge_filters
5394
- if not member_agent.knowledge_filters and member_agent.knowledge
5395
- else None,
5396
- )
5397
-
5398
- check_if_run_cancelled(member_agent_run_response) # type: ignore
5399
-
5400
- try:
5401
- if member_agent_run_response.content is None and ( # type: ignore
5402
- member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0 # type: ignore
5403
- ):
5404
- yield "No response from the member agent."
5405
- elif isinstance(member_agent_run_response.content, str): # type: ignore
5406
- content = member_agent_run_response.content.strip() # type: ignore
5407
- if len(content) > 0:
5408
- yield content
5409
-
5410
- # If the content is empty but we have tool calls
5411
- elif member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0: # type: ignore
5412
- tool_str = ""
5413
- for tool in member_agent_run_response.tools: # type: ignore
5414
- if tool.result:
5415
- tool_str += f"{tool.result},"
5416
- yield tool_str.rstrip(",")
5215
+ # 5. Handle respond_directly
5216
+ if self.respond_directly:
5217
+ # Since we return the response directly from the member agent, we need to set the output schema from the team down.
5218
+ if not member_agent.output_schema and self.output_schema:
5219
+ member_agent.output_schema = self.output_schema
5417
5220
 
5418
- elif issubclass(type(member_agent_run_response.content), BaseModel): # type: ignore
5419
- yield member_agent_run_response.content.model_dump_json(indent=2) # type: ignore
5420
- else:
5421
- import json
5221
+ # If the member will produce structured output, we need to parse the response
5222
+ if member_agent.output_schema is not None:
5223
+ self._member_response_model = member_agent.output_schema
5422
5224
 
5423
- yield json.dumps(member_agent_run_response.content, indent=2) # type: ignore
5424
- except Exception as e:
5425
- yield str(e)
5225
+ # 6. Handle enable_agentic_knowledge_filters on the member agent
5226
+ if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
5227
+ member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
5426
5228
 
5427
- # Afterward, switch back to the team logger
5428
- use_team_logger()
5229
+ return member_agent_task, history
5429
5230
 
5231
+ def _process_delegate_task_to_member(
5232
+ member_agent_run_response: Optional[Union[TeamRunOutput, RunOutput]],
5233
+ member_agent: Union[Agent, "Team"],
5234
+ member_agent_task: Union[str, Message],
5235
+ member_session_state_copy: Dict[str, Any],
5236
+ ):
5430
5237
  # Add team run id to the member run
5431
5238
  if member_agent_run_response is not None:
5432
5239
  member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5433
5240
 
5434
- # Update the memory
5435
- member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5241
+ # Update the team run context
5242
+ member_name = member_agent.name if member_agent.name else member_agent.id if member_agent.id else "Unknown"
5243
+ if isinstance(member_agent_task, str):
5244
+ normalized_task = member_agent_task
5245
+ elif member_agent_task.content:
5246
+ normalized_task = str(member_agent_task.content)
5247
+ else:
5248
+ normalized_task = ""
5436
5249
  self._add_interaction_to_team_run_context(
5437
5250
  team_run_context=team_run_context,
5438
5251
  member_name=member_name,
5439
- task=task_description,
5252
+ task=normalized_task,
5440
5253
  run_response=member_agent_run_response, # type: ignore
5441
5254
  )
5442
5255
 
5443
- # Add the member run to the team run response
5444
- if store_member_responses and run_response and member_agent_run_response:
5256
+ # Add the member run to the team run response if enabled
5257
+ if run_response and member_agent_run_response:
5445
5258
  run_response.add_member_run(member_agent_run_response)
5446
5259
 
5447
5260
  # Add the member run to the team session
@@ -5455,9 +5268,9 @@ class Team:
5455
5268
  if member_agent_run_response is not None:
5456
5269
  self._update_team_media(member_agent_run_response) # type: ignore
5457
5270
 
5458
- async def adelegate_task_to_member(
5271
+ def delegate_task_to_member(
5459
5272
  member_id: str, task_description: str, expected_output: Optional[str] = None
5460
- ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5273
+ ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5461
5274
  """Use this function to delegate a task to the selected team member.
5462
5275
  You must provide a clear and concise description of the task the member should achieve AND the expected output.
5463
5276
 
@@ -5476,41 +5289,16 @@ class Team:
5476
5289
  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)}"
5477
5290
  return
5478
5291
 
5479
- member_agent_index, member_agent = result
5480
- self._initialize_member(member_agent)
5481
-
5482
- # 2. Determine team context to send
5483
- team_member_interactions_str = self._determine_team_member_interactions(
5484
- team_run_context=team_run_context, images=images, videos=videos, audio=audio
5485
- )
5486
-
5487
- # 3. Create the member agent task
5488
- # Don't override the expected output of a member agent
5489
- if member_agent.expected_output is not None:
5490
- expected_output = None
5491
- member_agent_task = format_member_agent_task(
5492
- task_description, expected_output, team_member_interactions_str
5493
- )
5494
-
5495
- # 4. Add history for the member if enabled
5496
- if member_agent.add_history_to_context:
5497
- history = self._get_history_for_member_agent(session, member_agent)
5498
- if history:
5499
- history.append(Message(role="user", content=member_agent_task))
5292
+ _, member_agent = result
5293
+ member_agent_task, history = _setup_delegate_task_to_member(member_agent, task_description, expected_output)
5500
5294
 
5501
5295
  # Make sure for the member agent, we are using the agent logger
5502
5296
  use_agent_logger()
5503
5297
 
5504
- # Handle enable_agentic_knowledge_filters
5505
- if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
5506
- member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
5507
-
5508
- member_input = member_agent_task if history is None else history
5509
-
5510
5298
  member_session_state_copy = copy(session_state)
5511
5299
  if stream:
5512
- member_agent_run_response_stream = member_agent.arun( # type: ignore
5513
- input=member_input,
5300
+ member_agent_run_response_stream = member_agent.run(
5301
+ input=member_agent_task if not history else history,
5514
5302
  user_id=user_id,
5515
5303
  # All members have the same session_id
5516
5304
  session_id=session.session_id,
@@ -5530,217 +5318,22 @@ class Team:
5530
5318
  yield_run_response=True,
5531
5319
  )
5532
5320
  member_agent_run_response = None
5533
- async for member_agent_run_response_event in member_agent_run_response_stream:
5534
- if isinstance(member_agent_run_response_event, TeamRunOutput) or isinstance(
5535
- member_agent_run_response_event, RunOutput
5321
+ for member_agent_run_output_event in member_agent_run_response_stream:
5322
+ # If we get the final response, we can break out of the loop
5323
+ if isinstance(member_agent_run_output_event, TeamRunOutput) or isinstance(
5324
+ member_agent_run_output_event, RunOutput
5536
5325
  ):
5537
- member_agent_run_response = member_agent_run_response_event # type: ignore
5326
+ member_agent_run_response = member_agent_run_output_event # type: ignore
5538
5327
  break
5539
- check_if_run_cancelled(member_agent_run_response_event)
5540
- yield member_agent_run_response_event
5541
- else:
5542
- member_agent_run_response = await member_agent.arun( # type: ignore
5543
- input=member_input,
5544
- user_id=user_id,
5545
- # All members have the same session_id
5546
- session_id=session.session_id,
5547
- session_state=member_session_state_copy, # Send a copy to the agent
5548
- images=images,
5549
- videos=videos,
5550
- audio=audio,
5551
- files=files,
5552
- stream=False,
5553
- debug_mode=debug_mode,
5554
- add_history_to_context=add_history_to_context,
5555
- knowledge_filters=knowledge_filters
5556
- if not member_agent.knowledge_filters and member_agent.knowledge
5557
- else None,
5558
- )
5559
- check_if_run_cancelled(member_agent_run_response) # type: ignore
5560
-
5561
- try:
5562
- if member_agent_run_response.content is None and ( # type: ignore
5563
- member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0 # type: ignore
5564
- ):
5565
- yield "No response from the member agent."
5566
- elif isinstance(member_agent_run_response.content, str): # type: ignore
5567
- if len(member_agent_run_response.content.strip()) > 0: # type: ignore
5568
- yield member_agent_run_response.content # type: ignore
5569
-
5570
- # If the content is empty but we have tool calls
5571
- elif (
5572
- member_agent_run_response.tools is not None # type: ignore
5573
- and len(member_agent_run_response.tools) > 0 # type: ignore
5574
- ):
5575
- yield ",".join([tool.result for tool in member_agent_run_response.tools if tool.result]) # type: ignore
5576
- elif issubclass(type(member_agent_run_response.content), BaseModel): # type: ignore
5577
- yield member_agent_run_response.content.model_dump_json(indent=2) # type: ignore
5578
- else:
5579
- import json
5580
-
5581
- yield json.dumps(member_agent_run_response.content, indent=2) # type: ignore
5582
- except Exception as e:
5583
- yield str(e)
5584
-
5585
- # Afterward, switch back to the team logger
5586
- use_team_logger()
5587
-
5588
- # Add team run id to the member run
5589
- if member_agent_run_response is not None:
5590
- member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5591
-
5592
- # Update the memory
5593
- member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5594
- self._add_interaction_to_team_run_context(
5595
- team_run_context=team_run_context,
5596
- member_name=member_name,
5597
- task=task_description,
5598
- run_response=member_agent_run_response, # type: ignore
5599
- )
5600
-
5601
- # Add the member run to the team run response
5602
- if store_member_responses and run_response and member_agent_run_response:
5603
- run_response.add_member_run(member_agent_run_response)
5604
-
5605
- # Add the member run to the team session
5606
- if member_agent_run_response:
5607
- session.upsert_run(member_agent_run_response)
5608
-
5609
- # Update team session state
5610
- merge_dictionaries(session_state, member_session_state_copy) # type: ignore
5611
-
5612
- # Update the team media
5613
- if member_agent_run_response is not None:
5614
- self._update_team_media(member_agent_run_response) # type: ignore
5615
-
5616
- if async_mode:
5617
- delegate_function = adelegate_task_to_member # type: ignore
5618
- else:
5619
- delegate_function = delegate_task_to_member # type: ignore
5620
-
5621
- delegate_func = Function.from_callable(delegate_function, name="delegate_task_to_member", strict=True)
5622
-
5623
- return delegate_func
5624
-
5625
- def _get_forward_task_function(
5626
- self,
5627
- input: Message,
5628
- run_response: TeamRunOutput,
5629
- team_run_context: Dict[str, Any],
5630
- session: TeamSession,
5631
- session_state: Dict[str, Any],
5632
- user_id: Optional[str] = None,
5633
- stream: bool = False,
5634
- stream_intermediate_steps: bool = False,
5635
- async_mode: bool = False,
5636
- images: Optional[Sequence[Image]] = None,
5637
- videos: Optional[Sequence[Video]] = None,
5638
- audio: Optional[Sequence[Audio]] = None,
5639
- files: Optional[Sequence[File]] = None,
5640
- knowledge_filters: Optional[Dict[str, Any]] = None,
5641
- workflow_context: Optional[Dict] = None,
5642
- store_member_responses: bool = False,
5643
- debug_mode: Optional[bool] = None,
5644
- add_history_to_context: Optional[bool] = None,
5645
- dependencies: Optional[Dict[str, Any]] = None,
5646
- ) -> Function:
5647
- if not images:
5648
- images = []
5649
- if not videos:
5650
- videos = []
5651
- if not audio:
5652
- audio = []
5653
- if not files:
5654
- files = []
5655
-
5656
- def forward_task_to_member(
5657
- member_id: str, expected_output: Optional[str] = None
5658
- ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5659
- """Use this function to forward the request to the selected team member.
5660
- Args:
5661
- 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.
5662
- expected_output (str, optional): The expected output from the member (optional).
5663
- Returns:
5664
- str: The result of the delegated task.
5665
- """
5666
- self._member_response_model = None
5667
-
5668
- # Find the member agent using the helper function
5669
- result = self._find_member_by_id(member_id)
5670
- history = None
5671
- if result is None:
5672
- 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)}"
5673
- return
5674
-
5675
- member_agent_index, member_agent = result
5676
- self._initialize_member(member_agent)
5677
-
5678
- # Since we return the response directly from the member agent, we need to set the response model from the team down.
5679
- if not member_agent.output_schema and self.output_schema:
5680
- member_agent.output_schema = self.output_schema
5681
-
5682
- # If the member will produce structured output, we need to parse the response
5683
- if member_agent.output_schema is not None:
5684
- self._member_response_model = member_agent.output_schema
5685
-
5686
- # Make sure for the member agent, we are using the agent logger
5687
- use_agent_logger()
5688
-
5689
- # If found in subteam, include the path in the task description
5690
- member_agent_task = input.get_content_string() if input is not None else ""
5691
-
5692
- # Add history for the member if enabled
5693
- should_add_member_history = (
5694
- add_history_to_context if add_history_to_context is not None else member_agent.add_history_to_context
5695
- )
5696
- if should_add_member_history:
5697
- history = self._get_history_for_member_agent(session, member_agent)
5698
- if history:
5699
- history.append(Message(role="user", content=member_agent_task))
5700
-
5701
- # Don't override the expected output of a member agent
5702
- if member_agent.expected_output is None and expected_output:
5703
- member_agent_task += f"\n\n<expected_output>\n{expected_output}\n</expected_output>"
5704
5328
 
5705
- # Handle enable_agentic_knowledge_filters
5706
- if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
5707
- member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
5329
+ # Check if the run is cancelled
5330
+ check_if_run_cancelled(member_agent_run_output_event)
5708
5331
 
5709
- # 2. Get the response from the member agent
5710
- member_session_state_copy = copy(session_state)
5711
- if stream:
5712
- member_agent_run_response_stream = member_agent.run(
5713
- input=member_agent_task if history is None else history,
5714
- user_id=user_id,
5715
- # All members have the same session_id
5716
- session_id=session.session_id,
5717
- session_state=member_session_state_copy, # Send a copy to the agent
5718
- images=images,
5719
- videos=videos,
5720
- audio=audio,
5721
- files=files,
5722
- stream=True,
5723
- stream_intermediate_steps=stream_intermediate_steps,
5724
- debug_mode=debug_mode,
5725
- add_history_to_context=add_history_to_context,
5726
- workflow_context=workflow_context,
5727
- knowledge_filters=knowledge_filters
5728
- if not member_agent.knowledge_filters and member_agent.knowledge
5729
- else None,
5730
- yield_run_response=True,
5731
- )
5732
- member_agent_run_response = None
5733
- for member_agent_run_response_chunk in member_agent_run_response_stream:
5734
- if isinstance(member_agent_run_response_chunk, TeamRunOutput) or isinstance(
5735
- member_agent_run_response_chunk, RunOutput
5736
- ):
5737
- member_agent_run_response = member_agent_run_response_chunk # type: ignore
5738
- break
5739
- check_if_run_cancelled(member_agent_run_response_chunk)
5740
- yield member_agent_run_response_chunk
5332
+ # Yield the member event directly
5333
+ yield member_agent_run_output_event
5741
5334
  else:
5742
5335
  member_agent_run_response = member_agent.run( # type: ignore
5743
- input=member_agent_task if history is None else history,
5336
+ input=member_agent_task if not history else history,
5744
5337
  user_id=user_id,
5745
5338
  # All members have the same session_id
5746
5339
  session_id=session.session_id,
@@ -5751,11 +5344,13 @@ class Team:
5751
5344
  files=files,
5752
5345
  stream=False,
5753
5346
  debug_mode=debug_mode,
5347
+ workflow_context=workflow_context,
5754
5348
  add_history_to_context=add_history_to_context,
5755
5349
  knowledge_filters=knowledge_filters
5756
5350
  if not member_agent.knowledge_filters and member_agent.knowledge
5757
5351
  else None,
5758
5352
  )
5353
+
5759
5354
  check_if_run_cancelled(member_agent_run_response) # type: ignore
5760
5355
 
5761
5356
  try:
@@ -5764,13 +5359,12 @@ class Team:
5764
5359
  ):
5765
5360
  yield "No response from the member agent."
5766
5361
  elif isinstance(member_agent_run_response.content, str): # type: ignore
5767
- if len(member_agent_run_response.content.strip()) > 0: # type: ignore
5768
- yield member_agent_run_response.content # type: ignore
5362
+ content = member_agent_run_response.content.strip() # type: ignore
5363
+ if len(content) > 0:
5364
+ yield content
5769
5365
 
5770
5366
  # If the content is empty but we have tool calls
5771
- elif (
5772
- member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0 # type: ignore
5773
- ):
5367
+ elif member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0: # type: ignore
5774
5368
  tool_str = ""
5775
5369
  for tool in member_agent_run_response.tools: # type: ignore
5776
5370
  if tool.result:
@@ -5789,46 +5383,23 @@ class Team:
5789
5383
  # Afterward, switch back to the team logger
5790
5384
  use_team_logger()
5791
5385
 
5792
- # Add team run id to the member run
5793
- if member_agent_run_response is not None:
5794
- member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5795
-
5796
- # Update the memory
5797
- member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5798
- self._add_interaction_to_team_run_context(
5799
- team_run_context=team_run_context,
5800
- member_name=member_name,
5801
- task=member_agent_task,
5802
- run_response=member_agent_run_response, # type: ignore
5386
+ _process_delegate_task_to_member(
5387
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5803
5388
  )
5804
5389
 
5805
- # Add the member run to the team run response
5806
- if store_member_responses and run_response and member_agent_run_response:
5807
- run_response.add_member_run(member_agent_run_response)
5808
-
5809
- # Add the member run to the team session
5810
- if member_agent_run_response:
5811
- session.upsert_run(member_agent_run_response)
5812
-
5813
- # Update team session state
5814
- merge_dictionaries(session_state, member_session_state_copy) # type: ignore
5815
-
5816
- # Update the team media
5817
- if member_agent_run_response is not None:
5818
- self._update_team_media(member_agent_run_response) # type: ignore
5819
-
5820
- async def aforward_task_to_member(
5821
- member_id: str, expected_output: Optional[str] = None
5390
+ async def adelegate_task_to_member(
5391
+ member_id: str, task_description: str, expected_output: Optional[str] = None
5822
5392
  ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5823
- """Use this function to forward a message to the selected team member.
5393
+ """Use this function to delegate a task to the selected team member.
5394
+ You must provide a clear and concise description of the task the member should achieve AND the expected output.
5824
5395
 
5825
5396
  Args:
5826
5397
  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.
5398
+ task_description (str): A clear and concise description of the task the member should achieve.
5827
5399
  expected_output (str, optional): The expected output from the member (optional).
5828
5400
  Returns:
5829
5401
  str: The result of the delegated task.
5830
5402
  """
5831
- self._member_response_model = None
5832
5403
 
5833
5404
  # Find the member agent using the helper function
5834
5405
  result = self._find_member_by_id(member_id)
@@ -5837,38 +5408,16 @@ class Team:
5837
5408
  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)}"
5838
5409
  return
5839
5410
 
5840
- member_agent_index, member_agent = result
5841
- self._initialize_member(member_agent)
5842
-
5843
- # If the member will produce structured output, we need to parse the response
5844
- if member_agent.output_schema is not None:
5845
- self._member_response_model = member_agent.output_schema
5411
+ _, member_agent = result
5412
+ member_agent_task, history = _setup_delegate_task_to_member(member_agent, task_description, expected_output)
5846
5413
 
5847
5414
  # Make sure for the member agent, we are using the agent logger
5848
5415
  use_agent_logger()
5849
5416
 
5850
- # If found in subteam, include the path in the task description
5851
- member_agent_task = input.get_content_string() if input is not None else ""
5852
-
5853
- if member_agent.add_history_to_context:
5854
- history = self._get_history_for_member_agent(session, member_agent)
5855
- if history:
5856
- history.append(Message(role="user", content=member_agent_task))
5857
-
5858
- # Don't override the expected output of a member agent
5859
- if member_agent.expected_output is None and expected_output:
5860
- member_agent_task += f"\n\n<expected_output>\n{expected_output}\n</expected_output>"
5861
-
5862
- # Handle enable_agentic_knowledge_filters
5863
- if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
5864
- member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
5865
-
5866
- member_input = member_agent_task if history is None else history
5867
- # 2. Get the response from the member agent
5868
5417
  member_session_state_copy = copy(session_state)
5869
5418
  if stream:
5870
5419
  member_agent_run_response_stream = member_agent.arun( # type: ignore
5871
- input=member_input,
5420
+ input=member_agent_task if not history else history,
5872
5421
  user_id=user_id,
5873
5422
  # All members have the same session_id
5874
5423
  session_id=session.session_id,
@@ -5879,9 +5428,9 @@ class Team:
5879
5428
  files=files,
5880
5429
  stream=True,
5881
5430
  stream_intermediate_steps=stream_intermediate_steps,
5882
- workflow_context=workflow_context,
5883
5431
  debug_mode=debug_mode,
5884
5432
  add_history_to_context=add_history_to_context,
5433
+ workflow_context=workflow_context,
5885
5434
  knowledge_filters=knowledge_filters
5886
5435
  if not member_agent.knowledge_filters and member_agent.knowledge
5887
5436
  else None,
@@ -5889,16 +5438,21 @@ class Team:
5889
5438
  )
5890
5439
  member_agent_run_response = None
5891
5440
  async for member_agent_run_response_event in member_agent_run_response_stream:
5441
+ # If we get the final response, we can break out of the loop
5892
5442
  if isinstance(member_agent_run_response_event, TeamRunOutput) or isinstance(
5893
5443
  member_agent_run_response_event, RunOutput
5894
5444
  ):
5895
5445
  member_agent_run_response = member_agent_run_response_event # type: ignore
5896
5446
  break
5447
+
5448
+ # Check if the run is cancelled
5897
5449
  check_if_run_cancelled(member_agent_run_response_event)
5450
+
5451
+ # Yield the member event directly
5898
5452
  yield member_agent_run_response_event
5899
5453
  else:
5900
5454
  member_agent_run_response = await member_agent.arun( # type: ignore
5901
- input=member_input,
5455
+ input=member_agent_task if not history else history,
5902
5456
  user_id=user_id,
5903
5457
  # All members have the same session_id
5904
5458
  session_id=session.session_id,
@@ -5909,9 +5463,10 @@ class Team:
5909
5463
  files=files,
5910
5464
  stream=False,
5911
5465
  debug_mode=debug_mode,
5466
+ workflow_context=workflow_context,
5912
5467
  add_history_to_context=add_history_to_context,
5913
5468
  knowledge_filters=knowledge_filters
5914
- if (member_agent.knowledge_filters and member_agent.knowledge)
5469
+ if not member_agent.knowledge_filters and member_agent.knowledge
5915
5470
  else None,
5916
5471
  )
5917
5472
  check_if_run_cancelled(member_agent_run_response) # type: ignore
@@ -5927,7 +5482,8 @@ class Team:
5927
5482
 
5928
5483
  # If the content is empty but we have tool calls
5929
5484
  elif (
5930
- member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0 # type: ignore
5485
+ member_agent_run_response.tools is not None # type: ignore
5486
+ and len(member_agent_run_response.tools) > 0 # type: ignore
5931
5487
  ):
5932
5488
  yield ",".join([tool.result for tool in member_agent_run_response.tools if tool.result]) # type: ignore
5933
5489
  elif issubclass(type(member_agent_run_response.content), BaseModel): # type: ignore
@@ -5942,45 +5498,295 @@ class Team:
5942
5498
  # Afterward, switch back to the team logger
5943
5499
  use_team_logger()
5944
5500
 
5945
- # Add team run id to the member run
5946
- if member_agent_run_response is not None:
5947
- member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5948
-
5949
- # Update the memory
5950
- member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5951
- self._add_interaction_to_team_run_context(
5952
- team_run_context=team_run_context,
5953
- member_name=member_name,
5954
- task=member_agent_task,
5955
- run_response=member_agent_run_response, # type: ignore
5501
+ _process_delegate_task_to_member(
5502
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5956
5503
  )
5957
5504
 
5958
- # Add the member run to the team run response
5959
- if store_member_responses and run_response and member_agent_run_response:
5960
- run_response.add_member_run(member_agent_run_response)
5505
+ # When the task should be delegated to all members
5506
+ def delegate_task_to_members(
5507
+ task_description: str, expected_output: Optional[str] = None
5508
+ ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5509
+ """
5510
+ Use this function to delegate a task to all the member agents and return a response.
5511
+ You must provide a clear and concise description of the task the member should achieve AND the expected output.
5961
5512
 
5962
- # Add the member run to the team session
5963
- if member_agent_run_response:
5964
- session.upsert_run(member_agent_run_response)
5513
+ Args:
5514
+ task_description (str): A clear and concise description of the task to send to member agents.
5515
+ expected_output (str, optional): The expected output from the member agents (optional).
5516
+ Returns:
5517
+ str: The result of the delegated task.
5518
+ """
5965
5519
 
5966
- # Update team session state
5967
- merge_dictionaries(session_state, member_session_state_copy) # type: ignore
5520
+ # Run all the members sequentially
5521
+ for _, member_agent in enumerate(self.members):
5522
+ member_agent_task, history = _setup_delegate_task_to_member(
5523
+ member_agent, task_description, expected_output
5524
+ )
5968
5525
 
5969
- # Update the team media
5970
- if member_agent_run_response is not None:
5971
- self._update_team_media(member_agent_run_response) # type: ignore
5526
+ member_session_state_copy = copy(session_state)
5527
+ if stream:
5528
+ member_agent_run_response_stream = member_agent.run(
5529
+ input=member_agent_task if not history else history,
5530
+ user_id=user_id,
5531
+ # All members have the same session_id
5532
+ session_id=session.session_id,
5533
+ session_state=member_session_state_copy, # Send a copy to the agent
5534
+ images=images,
5535
+ videos=videos,
5536
+ audio=audio,
5537
+ files=files,
5538
+ stream=True,
5539
+ stream_intermediate_steps=stream_intermediate_steps,
5540
+ workflow_context=workflow_context,
5541
+ knowledge_filters=knowledge_filters
5542
+ if not member_agent.knowledge_filters and member_agent.knowledge
5543
+ else None,
5544
+ debug_mode=debug_mode,
5545
+ add_history_to_context=add_history_to_context,
5546
+ yield_run_response=True,
5547
+ )
5548
+ member_agent_run_response = None
5549
+ for member_agent_run_response_chunk in member_agent_run_response_stream:
5550
+ # If we get the final response, we can break out of the loop
5551
+ if isinstance(member_agent_run_response_chunk, TeamRunOutput) or isinstance(
5552
+ member_agent_run_response_chunk, RunOutput
5553
+ ):
5554
+ member_agent_run_response = member_agent_run_response_chunk # type: ignore
5555
+ break
5972
5556
 
5973
- if async_mode:
5974
- forward_function = aforward_task_to_member # type: ignore
5557
+ # Check if the run is cancelled
5558
+ check_if_run_cancelled(member_agent_run_response_chunk)
5559
+
5560
+ # Yield the member event directly
5561
+ yield member_agent_run_response_chunk
5562
+
5563
+ else:
5564
+ member_agent_run_response = member_agent.run( # type: ignore
5565
+ input=member_agent_task if not history else history,
5566
+ user_id=user_id,
5567
+ # All members have the same session_id
5568
+ session_id=session.session_id,
5569
+ session_state=member_session_state_copy, # Send a copy to the agent
5570
+ images=images,
5571
+ videos=videos,
5572
+ audio=audio,
5573
+ files=files,
5574
+ stream=False,
5575
+ workflow_context=workflow_context,
5576
+ knowledge_filters=knowledge_filters
5577
+ if not member_agent.knowledge_filters and member_agent.knowledge
5578
+ else None,
5579
+ debug_mode=debug_mode,
5580
+ add_history_to_context=add_history_to_context,
5581
+ )
5582
+
5583
+ check_if_run_cancelled(member_agent_run_response) # type: ignore
5584
+
5585
+ try:
5586
+ if member_agent_run_response.content is None and ( # type: ignore
5587
+ member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0 # type: ignore
5588
+ ):
5589
+ yield f"Agent {member_agent.name}: No response from the member agent."
5590
+ elif isinstance(member_agent_run_response.content, str): # type: ignore
5591
+ if len(member_agent_run_response.content.strip()) > 0: # type: ignore
5592
+ yield f"Agent {member_agent.name}: {member_agent_run_response.content}" # type: ignore
5593
+ elif (
5594
+ member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0 # type: ignore
5595
+ ):
5596
+ yield f"Agent {member_agent.name}: {','.join([tool.result for tool in member_agent_run_response.tools])}" # type: ignore
5597
+ elif issubclass(type(member_agent_run_response.content), BaseModel): # type: ignore
5598
+ yield f"Agent {member_agent.name}: {member_agent_run_response.content.model_dump_json(indent=2)}" # type: ignore
5599
+ else:
5600
+ import json
5601
+
5602
+ yield f"Agent {member_agent.name}: {json.dumps(member_agent_run_response.content, indent=2)}" # type: ignore
5603
+ except Exception as e:
5604
+ yield f"Agent {member_agent.name}: Error - {str(e)}"
5605
+
5606
+ _process_delegate_task_to_member(
5607
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5608
+ )
5609
+
5610
+ # After all the member runs, switch back to the team logger
5611
+ use_team_logger()
5612
+
5613
+ # When the task should be delegated to all members
5614
+ async def adelegate_task_to_members(
5615
+ task_description: str, expected_output: Optional[str] = None
5616
+ ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5617
+ """Use this function to delegate a task to all the member agents and return a response.
5618
+ You must provide a clear and concise description of the task to send to member agents AND the expected output.
5619
+
5620
+ Args:
5621
+ task_description (str): A clear and concise description of the task to send to member agents.
5622
+ expected_output (str, optional): The expected output from the member agents (optional).
5623
+ Returns:
5624
+ str: The result of the delegated task.
5625
+ """
5626
+
5627
+ if stream:
5628
+ # Concurrent streaming: launch each member as a streaming worker and merge events
5629
+ done_marker = object()
5630
+ queue: "asyncio.Queue[Union[RunOutputEvent, TeamRunOutputEvent, str, object]]" = asyncio.Queue()
5631
+
5632
+ async def stream_member(agent: Union[Agent, "Team"], idx: int) -> None:
5633
+ member_agent_task, history = _setup_delegate_task_to_member(
5634
+ agent, task_description, expected_output
5635
+ )
5636
+ member_session_state_copy = copy(session_state)
5637
+
5638
+ member_stream = agent.arun( # type: ignore
5639
+ input=member_agent_task if not history else history,
5640
+ user_id=user_id,
5641
+ session_id=session.session_id,
5642
+ session_state=member_session_state_copy, # Send a copy to the agent
5643
+ images=images,
5644
+ videos=videos,
5645
+ audio=audio,
5646
+ files=files,
5647
+ stream=True,
5648
+ stream_intermediate_steps=stream_intermediate_steps,
5649
+ workflow_context=workflow_context,
5650
+ debug_mode=debug_mode,
5651
+ knowledge_filters=knowledge_filters
5652
+ if not member_agent.knowledge_filters and member_agent.knowledge
5653
+ else None,
5654
+ add_history_to_context=add_history_to_context,
5655
+ yield_run_response=True,
5656
+ )
5657
+ member_agent_run_response = None
5658
+ try:
5659
+ async for member_agent_run_output_event in member_stream:
5660
+ if isinstance(member_agent_run_output_event, TeamRunOutput) or isinstance(
5661
+ member_agent_run_output_event, RunOutput
5662
+ ):
5663
+ member_agent_run_response = member_agent_run_output_event # type: ignore
5664
+ break
5665
+ check_if_run_cancelled(member_agent_run_output_event)
5666
+ await queue.put(member_agent_run_output_event)
5667
+ finally:
5668
+ _process_delegate_task_to_member(
5669
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5670
+ )
5671
+
5672
+ # Initialize and launch all members
5673
+ tasks: List[asyncio.Task[None]] = []
5674
+ for member_agent_index, member_agent in enumerate(self.members):
5675
+ current_agent = member_agent
5676
+ current_index = member_agent_index
5677
+ self._initialize_member(current_agent)
5678
+ tasks.append(asyncio.create_task(stream_member(current_agent, current_index)))
5679
+
5680
+ # Drain queue until all members reported done
5681
+ completed = 0
5682
+ try:
5683
+ while completed < len(tasks):
5684
+ item = await queue.get()
5685
+ if item is done_marker:
5686
+ completed += 1
5687
+ else:
5688
+ yield item # type: ignore
5689
+ finally:
5690
+ # Ensure tasks do not leak on cancellation
5691
+ for t in tasks:
5692
+ if not t.done():
5693
+ t.cancel()
5694
+ # Await cancellation to suppress warnings
5695
+ for t in tasks:
5696
+ with contextlib.suppress(Exception):
5697
+ await t
5698
+
5699
+ else:
5700
+ # Non-streaming concurrent run of members; collect results when done
5701
+ tasks = []
5702
+ for member_agent_index, member_agent in enumerate(self.members):
5703
+ current_agent = member_agent
5704
+ current_index = member_agent_index
5705
+ member_agent_task, history = _setup_delegate_task_to_member(
5706
+ current_agent, task_description, expected_output
5707
+ )
5708
+
5709
+ async def run_member_agent(agent=current_agent) -> str:
5710
+ member_session_state_copy = copy(session_state)
5711
+ member_agent_run_response = await agent.arun(
5712
+ input=member_agent_task if not history else history,
5713
+ user_id=user_id,
5714
+ # All members have the same session_id
5715
+ session_id=session.session_id,
5716
+ session_state=member_session_state_copy, # Send a copy to the agent
5717
+ images=images,
5718
+ videos=videos,
5719
+ audio=audio,
5720
+ files=files,
5721
+ stream=False,
5722
+ stream_intermediate_steps=stream_intermediate_steps,
5723
+ debug_mode=debug_mode,
5724
+ workflow_context=workflow_context,
5725
+ knowledge_filters=knowledge_filters
5726
+ if not member_agent.knowledge_filters and member_agent.knowledge
5727
+ else None,
5728
+ add_history_to_context=add_history_to_context,
5729
+ )
5730
+ check_if_run_cancelled(member_agent_run_response)
5731
+
5732
+ _process_delegate_task_to_member(
5733
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5734
+ )
5735
+
5736
+ member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5737
+ try:
5738
+ if member_agent_run_response.content is None and (
5739
+ member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0
5740
+ ):
5741
+ return f"Agent {member_name}: No response from the member agent."
5742
+ elif isinstance(member_agent_run_response.content, str):
5743
+ if len(member_agent_run_response.content.strip()) > 0:
5744
+ return f"Agent {member_name}: {member_agent_run_response.content}"
5745
+ elif (
5746
+ member_agent_run_response.tools is not None
5747
+ and len(member_agent_run_response.tools) > 0
5748
+ ):
5749
+ return f"Agent {member_name}: {','.join([tool.result for tool in member_agent_run_response.tools])}"
5750
+ elif issubclass(type(member_agent_run_response.content), BaseModel):
5751
+ return f"Agent {member_name}: {member_agent_run_response.content.model_dump_json(indent=2)}" # type: ignore
5752
+ else:
5753
+ import json
5754
+
5755
+ return f"Agent {member_name}: {json.dumps(member_agent_run_response.content, indent=2)}"
5756
+ except Exception as e:
5757
+ return f"Agent {member_name}: Error - {str(e)}"
5758
+
5759
+ return f"Agent {member_name}: No Response"
5760
+
5761
+ tasks.append(run_member_agent) # type: ignore
5762
+
5763
+ results = await asyncio.gather(*[task() for task in tasks]) # type: ignore
5764
+ for result in results:
5765
+ yield result
5766
+
5767
+ # After all the member runs, switch back to the team logger
5768
+ use_team_logger()
5769
+
5770
+ if self.delegate_task_to_all_members:
5771
+ if async_mode:
5772
+ delegate_function = adelegate_task_to_members # type: ignore
5773
+ else:
5774
+ delegate_function = delegate_task_to_members # type: ignore
5775
+
5776
+ delegate_func = Function.from_callable(delegate_function, name="delegate_task_to_members")
5975
5777
  else:
5976
- forward_function = forward_task_to_member # type: ignore
5778
+ if async_mode:
5779
+ delegate_function = adelegate_task_to_member # type: ignore
5780
+ else:
5781
+ delegate_function = delegate_task_to_member # type: ignore
5977
5782
 
5978
- forward_func = Function.from_callable(forward_function, name="forward_task_to_member", strict=True)
5783
+ delegate_func = Function.from_callable(delegate_function, name="delegate_task_to_member")
5979
5784
 
5980
- forward_func.stop_after_tool_call = True
5981
- forward_func.show_result = True
5785
+ if self.respond_directly:
5786
+ delegate_func.stop_after_tool_call = True
5787
+ delegate_func.show_result = True
5982
5788
 
5983
- return forward_func
5789
+ return delegate_func
5984
5790
 
5985
5791
  ###########################################################################
5986
5792
  # Session Management
@@ -6128,7 +5934,7 @@ class Team:
6128
5934
 
6129
5935
  return team_session
6130
5936
 
6131
- log_warning(f"TeamSession {session_id_to_load} not found in db")
5937
+ log_debug(f"TeamSession {session_id_to_load} not found in db")
6132
5938
  return None
6133
5939
 
6134
5940
  def save_session(self, session: TeamSession) -> None:
@@ -6138,6 +5944,12 @@ class Team:
6138
5944
  session.session_data["session_state"].pop("current_session_id", None) # type: ignore
6139
5945
  session.session_data["session_state"].pop("current_user_id", None) # type: ignore
6140
5946
  session.session_data["session_state"].pop("current_run_id", None) # type: ignore
5947
+
5948
+ # scrub the member responses if not storing them
5949
+ if not self.store_member_responses and session.runs is not None:
5950
+ for run in session.runs:
5951
+ if hasattr(run, "member_responses"):
5952
+ run.member_responses = []
6141
5953
  self._upsert_session(session=session)
6142
5954
  log_debug(f"Created or updated TeamSession record: {session.session_id}")
6143
5955
 
@@ -6867,8 +6679,6 @@ class Team:
6867
6679
  team_data["team_id"] = self.id
6868
6680
  if self.model is not None:
6869
6681
  team_data["model"] = self.model.to_dict()
6870
- if self.mode is not None:
6871
- team_data["mode"] = self.mode
6872
6682
  return team_data
6873
6683
 
6874
6684
  ###########################################################################