agno 2.0.0rc1__py3-none-any.whl → 2.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. agno/agent/agent.py +101 -140
  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/reranker/__init__.py +9 -0
  22. agno/knowledge/utils.py +10 -10
  23. agno/media.py +269 -268
  24. agno/models/aws/bedrock.py +3 -7
  25. agno/models/base.py +50 -54
  26. agno/models/google/gemini.py +11 -10
  27. agno/models/message.py +4 -4
  28. agno/models/ollama/chat.py +1 -1
  29. agno/models/openai/chat.py +33 -14
  30. agno/models/response.py +5 -5
  31. agno/os/app.py +40 -29
  32. agno/os/mcp.py +39 -59
  33. agno/os/router.py +547 -16
  34. agno/os/routers/evals/evals.py +197 -12
  35. agno/os/routers/knowledge/knowledge.py +428 -14
  36. agno/os/routers/memory/memory.py +250 -28
  37. agno/os/routers/metrics/metrics.py +125 -7
  38. agno/os/routers/session/session.py +393 -25
  39. agno/os/schema.py +55 -2
  40. agno/run/agent.py +37 -28
  41. agno/run/base.py +9 -19
  42. agno/run/team.py +110 -19
  43. agno/run/workflow.py +41 -28
  44. agno/team/team.py +808 -1080
  45. agno/tools/brightdata.py +3 -3
  46. agno/tools/cartesia.py +3 -5
  47. agno/tools/dalle.py +7 -4
  48. agno/tools/desi_vocal.py +2 -2
  49. agno/tools/e2b.py +6 -6
  50. agno/tools/eleven_labs.py +3 -3
  51. agno/tools/fal.py +4 -4
  52. agno/tools/function.py +7 -7
  53. agno/tools/giphy.py +2 -2
  54. agno/tools/lumalab.py +3 -3
  55. agno/tools/mcp.py +1 -2
  56. agno/tools/models/azure_openai.py +2 -2
  57. agno/tools/models/gemini.py +3 -3
  58. agno/tools/models/groq.py +3 -5
  59. agno/tools/models/nebius.py +2 -2
  60. agno/tools/models_labs.py +5 -5
  61. agno/tools/openai.py +4 -9
  62. agno/tools/opencv.py +3 -3
  63. agno/tools/replicate.py +7 -7
  64. agno/utils/events.py +5 -5
  65. agno/utils/gemini.py +1 -1
  66. agno/utils/log.py +52 -2
  67. agno/utils/mcp.py +57 -5
  68. agno/utils/models/aws_claude.py +1 -1
  69. agno/utils/models/claude.py +0 -8
  70. agno/utils/models/cohere.py +1 -1
  71. agno/utils/models/watsonx.py +1 -1
  72. agno/utils/openai.py +1 -1
  73. agno/utils/print_response/team.py +177 -73
  74. agno/utils/streamlit.py +27 -0
  75. agno/vectordb/lancedb/lance_db.py +82 -25
  76. agno/workflow/step.py +7 -7
  77. agno/workflow/types.py +13 -13
  78. agno/workflow/workflow.py +37 -28
  79. {agno-2.0.0rc1.dist-info → agno-2.0.1.dist-info}/METADATA +140 -1
  80. {agno-2.0.0rc1.dist-info → agno-2.0.1.dist-info}/RECORD +83 -84
  81. agno-2.0.1.dist-info/licenses/LICENSE +201 -0
  82. agno/knowledge/reader/gcs_reader.py +0 -67
  83. agno-2.0.0rc1.dist-info/licenses/LICENSE +0 -375
  84. {agno-2.0.0rc1.dist-info → agno-2.0.1.dist-info}/WHEEL +0 -0
  85. {agno-2.0.0rc1.dist-info → agno-2.0.1.dist-info}/top_level.txt +0 -0
agno/team/team.py CHANGED
@@ -33,7 +33,7 @@ from agno.agent import Agent
33
33
  from agno.db.base import BaseDb, SessionType, UserMemory
34
34
  from agno.exceptions import ModelProviderError, RunCancelledException
35
35
  from agno.knowledge.knowledge import Knowledge
36
- from agno.media import Audio, AudioArtifact, AudioResponse, File, Image, ImageArtifact, Video, VideoArtifact
36
+ from agno.media import Audio, File, Image, Video
37
37
  from agno.memory import MemoryManager
38
38
  from agno.models.base import Model
39
39
  from agno.models.message import Message, MessageReferences
@@ -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
@@ -531,11 +546,11 @@ class Team:
531
546
 
532
547
  # TODO: Remove these
533
548
  # Images generated during this session
534
- self.images: Optional[List[ImageArtifact]] = None
549
+ self.images: Optional[List[Image]] = None
535
550
  # Audio generated during this session
536
- self.audio: Optional[List[AudioArtifact]] = None
551
+ self.audio: Optional[List[Audio]] = None
537
552
  # Videos generated during this session
538
- self.videos: Optional[List[VideoArtifact]] = None
553
+ self.videos: Optional[List[Video]] = None
539
554
 
540
555
  # Team session
541
556
  self._team_session: Optional[TeamSession] = None
@@ -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._validate_media_object_id(
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._validate_media_object_id(
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:
@@ -2113,28 +2155,38 @@ class Team:
2113
2155
  should_yield = True
2114
2156
 
2115
2157
  # Process thinking
2116
- if model_response_event.reasoning_content is not None:
2117
- if not full_model_response.reasoning_content:
2118
- full_model_response.reasoning_content = model_response_event.reasoning_content
2119
- else:
2120
- full_model_response.reasoning_content += model_response_event.reasoning_content
2121
- should_yield = True
2122
-
2123
- if model_response_event.citations is not None:
2124
- # We get citations in one chunk
2125
- full_model_response.citations = model_response_event.citations
2126
- should_yield = True
2127
-
2128
- # Process audio
2129
2158
  if model_response_event.audio is not None:
2130
2159
  if full_model_response.audio is None:
2131
- full_model_response.audio = AudioResponse(id=str(uuid4()), content="", transcript="")
2160
+ full_model_response.audio = Audio(id=str(uuid4()), content=b"", transcript="")
2132
2161
 
2133
2162
  if model_response_event.audio.id is not None:
2134
2163
  full_model_response.audio.id = model_response_event.audio.id # type: ignore
2164
+
2135
2165
  if model_response_event.audio.content is not None:
2136
- full_model_response.audio.content += model_response_event.audio.content # type: ignore
2166
+ # Handle both base64 string and bytes content
2167
+ if isinstance(model_response_event.audio.content, str):
2168
+ # Decode base64 string to bytes
2169
+ try:
2170
+ import base64
2171
+
2172
+ decoded_content = base64.b64decode(model_response_event.audio.content)
2173
+ if full_model_response.audio.content is None:
2174
+ full_model_response.audio.content = b""
2175
+ full_model_response.audio.content += decoded_content
2176
+ except Exception:
2177
+ # If decode fails, encode string as bytes
2178
+ if full_model_response.audio.content is None:
2179
+ full_model_response.audio.content = b""
2180
+ full_model_response.audio.content += model_response_event.audio.content.encode("utf-8")
2181
+ elif isinstance(model_response_event.audio.content, bytes):
2182
+ # Content is already bytes
2183
+ if full_model_response.audio.content is None:
2184
+ full_model_response.audio.content = b""
2185
+ full_model_response.audio.content += model_response_event.audio.content
2186
+
2137
2187
  if model_response_event.audio.transcript is not None:
2188
+ if full_model_response.audio.transcript is None:
2189
+ full_model_response.audio.transcript = ""
2138
2190
  full_model_response.audio.transcript += model_response_event.audio.transcript # type: ignore
2139
2191
  if model_response_event.audio.expires_at is not None:
2140
2192
  full_model_response.audio.expires_at = model_response_event.audio.expires_at # type: ignore
@@ -2937,31 +2989,85 @@ class Team:
2937
2989
  return member.name or entity_id
2938
2990
  return entity_id
2939
2991
 
2940
- def _parse_response_content(
2992
+ def _scrub_media_from_run_output(self, run_response: TeamRunOutput) -> None:
2993
+ """
2994
+ Completely remove all media from RunOutput when store_media=False.
2995
+ This includes media in input, output artifacts, and all messages.
2996
+ """
2997
+ # 1. Scrub RunInput media
2998
+ if run_response.input is not None:
2999
+ run_response.input.images = []
3000
+ run_response.input.videos = []
3001
+ run_response.input.audios = []
3002
+ run_response.input.files = []
3003
+
3004
+ # 2. RunOutput artifact media are skipped since we don't store them when store_media=False
3005
+
3006
+ # 3. Scrub media from all messages
3007
+ if run_response.messages:
3008
+ for message in run_response.messages:
3009
+ self._scrub_media_from_message(message)
3010
+
3011
+ # 4. Scrub media from additional_input messages if any
3012
+ if run_response.additional_input:
3013
+ for message in run_response.additional_input:
3014
+ self._scrub_media_from_message(message)
3015
+
3016
+ # 5. Scrub media from reasoning_messages if any
3017
+ if run_response.reasoning_messages:
3018
+ for message in run_response.reasoning_messages:
3019
+ self._scrub_media_from_message(message)
3020
+
3021
+ def _scrub_media_from_message(self, message: Message) -> None:
3022
+ """Remove all media from a Message object."""
3023
+ # Input media
3024
+ message.images = None
3025
+ message.videos = None
3026
+ message.audio = None
3027
+ message.files = None
3028
+
3029
+ # Output media
3030
+ message.audio_output = None
3031
+ message.image_output = None
3032
+ message.video_output = None
3033
+
3034
+ def _validate_media_object_id(
2941
3035
  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
2948
-
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}")
3036
+ images: Optional[Sequence[Image]] = None,
3037
+ videos: Optional[Sequence[Video]] = None,
3038
+ audios: Optional[Sequence[Audio]] = None,
3039
+ ) -> tuple:
3040
+ image_list = None
3041
+ if images:
3042
+ image_list = []
3043
+ for img in images:
3044
+ if not img.id:
3045
+ from uuid import uuid4
3046
+
3047
+ img.id = str(uuid4())
3048
+ image_list.append(img)
3049
+
3050
+ video_list = None
3051
+ if videos:
3052
+ video_list = []
3053
+ for vid in videos:
3054
+ if not vid.id:
3055
+ from uuid import uuid4
3056
+
3057
+ vid.id = str(uuid4())
3058
+ video_list.append(vid)
3059
+
3060
+ audio_list = None
3061
+ if audios:
3062
+ audio_list = []
3063
+ for aud in audios:
3064
+ if not aud.id:
3065
+ from uuid import uuid4
3066
+
3067
+ aud.id = str(uuid4())
3068
+ audio_list.append(aud)
3069
+
3070
+ return image_list, video_list, audio_list
2965
3071
 
2966
3072
  def cli_app(
2967
3073
  self,
@@ -3632,6 +3738,137 @@ class Team:
3632
3738
  except Exception as e:
3633
3739
  log_warning(f"Failed to resolve context for '{key}': {e}")
3634
3740
 
3741
+ def _collect_joint_images(
3742
+ self,
3743
+ run_input: Optional[TeamRunInput] = None,
3744
+ session: Optional[TeamSession] = None,
3745
+ ) -> Optional[Sequence[Image]]:
3746
+ """Collect images from input, session history, and current run response."""
3747
+ joint_images: List[Image] = []
3748
+
3749
+ # 1. Add images from current input
3750
+ if run_input and run_input.images:
3751
+ joint_images.extend(run_input.images)
3752
+ log_debug(f"Added {len(run_input.images)} input images to joint list")
3753
+
3754
+ # 2. Add images from session history (from both input and generated sources)
3755
+ try:
3756
+ if session and session.runs:
3757
+ for historical_run in session.runs:
3758
+ # Add generated images from previous runs
3759
+ if historical_run.images:
3760
+ joint_images.extend(historical_run.images)
3761
+ log_debug(
3762
+ f"Added {len(historical_run.images)} generated images from historical run {historical_run.run_id}"
3763
+ )
3764
+
3765
+ # Add input images from previous runs
3766
+ if historical_run.input and historical_run.input.images:
3767
+ joint_images.extend(historical_run.input.images)
3768
+ log_debug(
3769
+ f"Added {len(historical_run.input.images)} input images from historical run {historical_run.run_id}"
3770
+ )
3771
+ except Exception as e:
3772
+ log_debug(f"Could not access session history for images: {e}")
3773
+
3774
+ if joint_images:
3775
+ log_debug(f"Images Available to Model: {len(joint_images)} images")
3776
+ return joint_images if joint_images else None
3777
+
3778
+ def _collect_joint_videos(
3779
+ self,
3780
+ run_input: Optional[TeamRunInput] = None,
3781
+ session: Optional[TeamSession] = None,
3782
+ ) -> Optional[Sequence[Video]]:
3783
+ """Collect videos from input, session history, and current run response."""
3784
+ joint_videos: List[Video] = []
3785
+
3786
+ # 1. Add videos from current input
3787
+ if run_input and run_input.videos:
3788
+ joint_videos.extend(run_input.videos)
3789
+ log_debug(f"Added {len(run_input.videos)} input videos to joint list")
3790
+
3791
+ # 2. Add videos from session history (from both input and generated sources)
3792
+ try:
3793
+ if session and session.runs:
3794
+ for historical_run in session.runs:
3795
+ # Add generated videos from previous runs
3796
+ if historical_run.videos:
3797
+ joint_videos.extend(historical_run.videos)
3798
+ log_debug(
3799
+ f"Added {len(historical_run.videos)} generated videos from historical run {historical_run.run_id}"
3800
+ )
3801
+
3802
+ # Add input videos from previous runs
3803
+ if historical_run.input and historical_run.input.videos:
3804
+ joint_videos.extend(historical_run.input.videos)
3805
+ log_debug(
3806
+ f"Added {len(historical_run.input.videos)} input videos from historical run {historical_run.run_id}"
3807
+ )
3808
+ except Exception as e:
3809
+ log_debug(f"Could not access session history for videos: {e}")
3810
+
3811
+ if joint_videos:
3812
+ log_debug(f"Videos Available to Model: {len(joint_videos)} videos")
3813
+ return joint_videos if joint_videos else None
3814
+
3815
+ def _collect_joint_audios(
3816
+ self,
3817
+ run_input: Optional[TeamRunInput] = None,
3818
+ session: Optional[TeamSession] = None,
3819
+ ) -> Optional[Sequence[Audio]]:
3820
+ """Collect audios from input, session history, and current run response."""
3821
+ joint_audios: List[Audio] = []
3822
+
3823
+ # 1. Add audios from current input
3824
+ if run_input and run_input.audios:
3825
+ joint_audios.extend(run_input.audios)
3826
+ log_debug(f"Added {len(run_input.audios)} input audios to joint list")
3827
+
3828
+ # 2. Add audios from session history (from both input and generated sources)
3829
+ try:
3830
+ if session and session.runs:
3831
+ for historical_run in session.runs:
3832
+ # Add generated audios from previous runs
3833
+ if historical_run.audio:
3834
+ joint_audios.extend(historical_run.audio)
3835
+ log_debug(
3836
+ f"Added {len(historical_run.audio)} generated audios from historical run {historical_run.run_id}"
3837
+ )
3838
+
3839
+ # Add input audios from previous runs
3840
+ if historical_run.input and historical_run.input.audios:
3841
+ joint_audios.extend(historical_run.input.audios)
3842
+ log_debug(
3843
+ f"Added {len(historical_run.input.audios)} input audios from historical run {historical_run.run_id}"
3844
+ )
3845
+ except Exception as e:
3846
+ log_debug(f"Could not access session history for audios: {e}")
3847
+
3848
+ if joint_audios:
3849
+ log_debug(f"Audios Available to Model: {len(joint_audios)} audios")
3850
+ return joint_audios if joint_audios else None
3851
+
3852
+ def _collect_joint_files(
3853
+ self,
3854
+ run_input: Optional[TeamRunInput] = None,
3855
+ ) -> Optional[Sequence[File]]:
3856
+ """Collect files from input and session history."""
3857
+ from agno.utils.log import log_debug
3858
+
3859
+ joint_files: List[File] = []
3860
+
3861
+ # 1. Add files from current input
3862
+ if run_input and run_input.files:
3863
+ joint_files.extend(run_input.files)
3864
+
3865
+ # TODO: Files aren't stored in session history yet and dont have a FileArtifact
3866
+
3867
+ if joint_files:
3868
+ log_debug(f"Files Available to Model: {len(joint_files)} files")
3869
+
3870
+ return joint_files if joint_files else None
3871
+
3635
3872
  def determine_tools_for_model(
3636
3873
  self,
3637
3874
  model: Model,
@@ -3648,7 +3885,6 @@ class Team:
3648
3885
  audio: Optional[Sequence[Audio]] = None,
3649
3886
  files: Optional[Sequence[File]] = None,
3650
3887
  workflow_context: Optional[Dict] = None,
3651
- store_member_responses: bool = False,
3652
3888
  debug_mode: Optional[bool] = None,
3653
3889
  add_history_to_context: Optional[bool] = None,
3654
3890
  dependencies: Optional[Dict[str, Any]] = None,
@@ -3700,94 +3936,46 @@ class Team:
3700
3936
  if self.knowledge is not None and self.update_knowledge:
3701
3937
  _tools.append(self.add_to_knowledge)
3702
3938
 
3703
- if self.mode == "route":
3939
+ # Get the user message if we are using the input directly
3940
+ user_message = None
3941
+ if self.determine_input_for_members is False:
3704
3942
  user_message = self._get_user_message(
3705
3943
  run_response=run_response,
3706
3944
  session_state=session_state,
3707
3945
  input_message=input_message,
3946
+ user_id=user_id,
3708
3947
  audio=audio,
3709
3948
  images=images,
3710
3949
  videos=videos,
3711
3950
  files=files,
3712
- user_id=user_id,
3713
3951
  dependencies=dependencies,
3714
3952
  add_dependencies_to_context=add_dependencies_to_context,
3715
3953
  metadata=metadata,
3716
3954
  )
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
3955
 
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)
3956
+ delegate_task_func = self._get_delegate_task_function(
3957
+ run_response=run_response,
3958
+ session=session,
3959
+ session_state=session_state,
3960
+ team_run_context=team_run_context,
3961
+ input=user_message,
3962
+ user_id=user_id,
3963
+ stream=self.stream or False,
3964
+ stream_intermediate_steps=self.stream_intermediate_steps,
3965
+ async_mode=async_mode,
3966
+ images=images, # type: ignore
3967
+ videos=videos, # type: ignore
3968
+ audio=audio, # type: ignore
3969
+ files=files, # type: ignore
3970
+ knowledge_filters=knowledge_filters,
3971
+ workflow_context=workflow_context,
3972
+ debug_mode=debug_mode,
3973
+ add_history_to_context=add_history_to_context,
3974
+ )
3788
3975
 
3789
- if self.get_member_information_tool:
3790
- _tools.append(self.get_member_information)
3976
+ _tools.append(delegate_task_func)
3977
+ if self.get_member_information_tool:
3978
+ _tools.append(self.get_member_information)
3791
3979
 
3792
3980
  self._functions_for_model = {}
3793
3981
  self._tools_for_model = []
@@ -3866,6 +4054,29 @@ class Team:
3866
4054
  except Exception as e:
3867
4055
  log_warning(f"Could not add tool {tool}: {e}")
3868
4056
 
4057
+ if self._functions_for_model:
4058
+ from inspect import signature
4059
+
4060
+ # Check if any functions need media before collecting
4061
+ needs_media = any(
4062
+ any(param in signature(func.entrypoint).parameters for param in ["images", "videos", "audios", "files"])
4063
+ for func in self._functions_for_model.values()
4064
+ if func.entrypoint is not None
4065
+ )
4066
+
4067
+ if needs_media:
4068
+ # Only collect media if functions actually need them
4069
+ joint_images = self._collect_joint_images(run_response.input, session)
4070
+ joint_files = self._collect_joint_files(run_response.input)
4071
+ joint_audios = self._collect_joint_audios(run_response.input, session)
4072
+ joint_videos = self._collect_joint_videos(run_response.input, session)
4073
+
4074
+ for func in self._functions_for_model.values():
4075
+ func._images = joint_images
4076
+ func._files = joint_files
4077
+ func._audios = joint_audios
4078
+ func._videos = joint_videos
4079
+
3869
4080
  def get_members_system_message_content(self, indent: int = 0) -> str:
3870
4081
  system_message_content = ""
3871
4082
  for idx, member in enumerate(self.members):
@@ -4039,9 +4250,17 @@ class Team:
4039
4250
  system_message_content += "</team_members>\n"
4040
4251
 
4041
4252
  system_message_content += "\n<how_to_respond>\n"
4042
- if self.mode == "coordinate":
4253
+
4254
+ if self.delegate_task_to_all_members:
4255
+ system_message_content += (
4256
+ "- 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"
4257
+ "- 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"
4258
+ "- Analyze the responses from all members and evaluate whether the task has been completed.\n"
4259
+ "- If you feel the task has been completed, you can stop and respond to the user.\n"
4260
+ )
4261
+ else:
4043
4262
  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"
4263
+ "- Your role is to delegate tasks to members in your team with the highest likelihood of completing the user's request.\n"
4045
4264
  "- Carefully analyze the tools available to the members and their roles before delegating tasks.\n"
4046
4265
  "- You cannot use a member tool directly. You can only delegate tasks to members.\n"
4047
4266
  "- When you delegate a task to another member, make sure to include:\n"
@@ -4055,24 +4274,6 @@ class Team:
4055
4274
  "- For simple greetings, thanks, or questions about the team itself, you should respond directly.\n"
4056
4275
  "- For all work requests, tasks, or questions requiring expertise, route to appropriate team members.\n"
4057
4276
  )
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
4277
  system_message_content += "</how_to_respond>\n\n"
4077
4278
 
4078
4279
  # Attached media
@@ -4357,10 +4558,10 @@ class Team:
4357
4558
  return Message(
4358
4559
  role="user",
4359
4560
  content="",
4360
- images=images,
4361
- audio=audio,
4362
- videos=videos,
4363
- files=files,
4561
+ images=None if not self.send_media_to_model else images,
4562
+ audio=None if not self.send_media_to_model else audio,
4563
+ videos=None if not self.send_media_to_model else videos,
4564
+ files=None if not self.send_media_to_model else files,
4364
4565
  **kwargs,
4365
4566
  )
4366
4567
  else:
@@ -4381,10 +4582,10 @@ class Team:
4381
4582
  return Message(
4382
4583
  role="user",
4383
4584
  content=input_content,
4384
- images=images,
4385
- audio=audio,
4386
- videos=videos,
4387
- files=files,
4585
+ images=None if not self.send_media_to_model else images,
4586
+ audio=None if not self.send_media_to_model else audio,
4587
+ videos=None if not self.send_media_to_model else videos,
4588
+ files=None if not self.send_media_to_model else files,
4388
4589
  **kwargs,
4389
4590
  )
4390
4591
 
@@ -4473,10 +4674,10 @@ class Team:
4473
4674
  return Message(
4474
4675
  role="user",
4475
4676
  content=user_msg_content,
4476
- audio=audio,
4477
- images=images,
4478
- videos=videos,
4479
- files=files,
4677
+ images=None if not self.send_media_to_model else images,
4678
+ audio=None if not self.send_media_to_model else audio,
4679
+ videos=None if not self.send_media_to_model else videos,
4680
+ files=None if not self.send_media_to_model else files,
4480
4681
  **kwargs,
4481
4682
  )
4482
4683
 
@@ -4831,11 +5032,11 @@ class Team:
4831
5032
  if self.share_member_interactions:
4832
5033
  team_member_interactions_str = self._get_team_member_interactions_str(team_run_context=team_run_context) # type: ignore
4833
5034
  if context_images := self._get_team_run_context_images(team_run_context=team_run_context): # type: ignore
4834
- images.extend([Image.from_artifact(img) for img in context_images])
5035
+ images.extend(context_images)
4835
5036
  if context_videos := self._get_team_run_context_videos(team_run_context=team_run_context): # type: ignore
4836
- videos.extend([Video.from_artifact(vid) for vid in context_videos])
5037
+ videos.extend(context_videos)
4837
5038
  if context_audio := self._get_team_run_context_audio(team_run_context=team_run_context): # type: ignore
4838
- audio.extend([Audio.from_artifact(aud) for aud in context_audio])
5039
+ audio.extend(context_audio)
4839
5040
  return team_member_interactions_str
4840
5041
 
4841
5042
  def _find_member_by_id(self, member_id: str) -> Optional[Tuple[int, Union[Agent, "Team"]]]:
@@ -4865,7 +5066,7 @@ class Team:
4865
5066
 
4866
5067
  return None
4867
5068
 
4868
- def _get_run_member_agents_function(
5069
+ def _get_delegate_task_function(
4869
5070
  self,
4870
5071
  run_response: TeamRunOutput,
4871
5072
  session: TeamSession,
@@ -4875,12 +5076,13 @@ class Team:
4875
5076
  stream: bool = False,
4876
5077
  stream_intermediate_steps: bool = False,
4877
5078
  async_mode: bool = False,
5079
+ input: Optional[Message] = None, # Used for determine_input_for_memberss=False
4878
5080
  images: Optional[List[Image]] = None,
4879
5081
  videos: Optional[List[Video]] = None,
4880
5082
  audio: Optional[List[Audio]] = None,
4881
5083
  files: Optional[List[File]] = None,
5084
+ knowledge_filters: Optional[Dict[str, Any]] = None,
4882
5085
  workflow_context: Optional[Dict] = None,
4883
- store_member_responses: bool = False,
4884
5086
  debug_mode: Optional[bool] = None,
4885
5087
  add_history_to_context: Optional[bool] = None,
4886
5088
  ) -> Function:
@@ -4893,555 +5095,84 @@ class Team:
4893
5095
  if not files:
4894
5096
  files = []
4895
5097
 
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()
5098
+ def _setup_delegate_task_to_member(
5099
+ member_agent: Union[Agent, "Team"], task_description: str, expected_output: Optional[str] = None
5100
+ ):
5101
+ # 1. Initialize the member agent
5102
+ self._initialize_member(member_agent)
4911
5103
 
4912
- # 1. Determine team context to send
5104
+ # 2. Determine team context to send
4913
5105
  team_member_interactions_str = self._determine_team_member_interactions(
4914
5106
  team_run_context, images, videos, audio
4915
5107
  )
4916
5108
 
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
- )
5109
+ member_agent_task: Union[str, Message]
4921
5110
 
4922
- for member_agent_index, member_agent in enumerate(self.members):
4923
- self._initialize_member(member_agent)
5111
+ # 3. Create the member agent task or use the input directly
5112
+ if self.determine_input_for_members is False:
5113
+ member_agent_task = input # type: ignore
5114
+ else:
5115
+ # Don't override the expected output of a member agent
5116
+ if member_agent.expected_output is not None:
5117
+ expected_output = None
5118
+
5119
+ member_agent_task = format_member_agent_task( # type: ignore
5120
+ task_description, expected_output, team_member_interactions_str
5121
+ )
4924
5122
 
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:
5123
+ # 4. Add history for the member if enabled
5124
+ history = None
5125
+ if member_agent.add_history_to_context:
5126
+ history = self._get_history_for_member_agent(session, member_agent)
5127
+ if history:
5128
+ if isinstance(member_agent_task, str):
4930
5129
  history.append(Message(role="user", content=member_agent_task))
5130
+ else:
5131
+ history.append(member_agent_task)
4931
5132
 
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
5133
+ # 5. Handle respond_directly
5134
+ if self.respond_directly:
5135
+ # Since we return the response directly from the member agent, we need to set the output schema from the team down.
5136
+ if not member_agent.output_schema and self.output_schema:
5137
+ member_agent.output_schema = self.output_schema
4961
5138
 
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(",")
5417
-
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
5139
+ # If the member will produce structured output, we need to parse the response
5140
+ if member_agent.output_schema is not None:
5141
+ self._member_response_model = member_agent.output_schema
5422
5142
 
5423
- yield json.dumps(member_agent_run_response.content, indent=2) # type: ignore
5424
- except Exception as e:
5425
- yield str(e)
5143
+ # 6. Handle enable_agentic_knowledge_filters on the member agent
5144
+ if self.enable_agentic_knowledge_filters and not member_agent.enable_agentic_knowledge_filters:
5145
+ member_agent.enable_agentic_knowledge_filters = self.enable_agentic_knowledge_filters
5426
5146
 
5427
- # Afterward, switch back to the team logger
5428
- use_team_logger()
5147
+ return member_agent_task, history
5429
5148
 
5149
+ def _process_delegate_task_to_member(
5150
+ member_agent_run_response: Optional[Union[TeamRunOutput, RunOutput]],
5151
+ member_agent: Union[Agent, "Team"],
5152
+ member_agent_task: Union[str, Message],
5153
+ member_session_state_copy: Dict[str, Any],
5154
+ ):
5430
5155
  # Add team run id to the member run
5431
5156
  if member_agent_run_response is not None:
5432
5157
  member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
5433
5158
 
5434
- # Update the memory
5435
- member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5159
+ # Update the team run context
5160
+ member_name = member_agent.name if member_agent.name else member_agent.id if member_agent.id else "Unknown"
5161
+ if isinstance(member_agent_task, str):
5162
+ normalized_task = member_agent_task
5163
+ elif member_agent_task.content:
5164
+ normalized_task = str(member_agent_task.content)
5165
+ else:
5166
+ normalized_task = ""
5436
5167
  self._add_interaction_to_team_run_context(
5437
5168
  team_run_context=team_run_context,
5438
5169
  member_name=member_name,
5439
- task=task_description,
5170
+ task=normalized_task,
5440
5171
  run_response=member_agent_run_response, # type: ignore
5441
5172
  )
5442
5173
 
5443
- # Add the member run to the team run response
5444
- if store_member_responses and run_response and member_agent_run_response:
5174
+ # Add the member run to the team run response if enabled
5175
+ if run_response and member_agent_run_response:
5445
5176
  run_response.add_member_run(member_agent_run_response)
5446
5177
 
5447
5178
  # Add the member run to the team session
@@ -5455,9 +5186,9 @@ class Team:
5455
5186
  if member_agent_run_response is not None:
5456
5187
  self._update_team_media(member_agent_run_response) # type: ignore
5457
5188
 
5458
- async def adelegate_task_to_member(
5189
+ def delegate_task_to_member(
5459
5190
  member_id: str, task_description: str, expected_output: Optional[str] = None
5460
- ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5191
+ ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5461
5192
  """Use this function to delegate a task to the selected team member.
5462
5193
  You must provide a clear and concise description of the task the member should achieve AND the expected output.
5463
5194
 
@@ -5476,41 +5207,16 @@ class Team:
5476
5207
  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
5208
  return
5478
5209
 
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))
5210
+ _, member_agent = result
5211
+ member_agent_task, history = _setup_delegate_task_to_member(member_agent, task_description, expected_output)
5500
5212
 
5501
5213
  # Make sure for the member agent, we are using the agent logger
5502
5214
  use_agent_logger()
5503
5215
 
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
5216
  member_session_state_copy = copy(session_state)
5511
5217
  if stream:
5512
- member_agent_run_response_stream = member_agent.arun( # type: ignore
5513
- input=member_input,
5218
+ member_agent_run_response_stream = member_agent.run(
5219
+ input=member_agent_task if not history else history,
5514
5220
  user_id=user_id,
5515
5221
  # All members have the same session_id
5516
5222
  session_id=session.session_id,
@@ -5530,217 +5236,22 @@ class Team:
5530
5236
  yield_run_response=True,
5531
5237
  )
5532
5238
  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
5239
+ for member_agent_run_output_event in member_agent_run_response_stream:
5240
+ # If we get the final response, we can break out of the loop
5241
+ if isinstance(member_agent_run_output_event, TeamRunOutput) or isinstance(
5242
+ member_agent_run_output_event, RunOutput
5536
5243
  ):
5537
- member_agent_run_response = member_agent_run_response_event # type: ignore
5244
+ member_agent_run_response = member_agent_run_output_event # type: ignore
5538
5245
  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
5246
 
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
5247
+ # Check if the run is cancelled
5248
+ check_if_run_cancelled(member_agent_run_output_event)
5708
5249
 
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
5250
+ # Yield the member event directly
5251
+ yield member_agent_run_output_event
5741
5252
  else:
5742
5253
  member_agent_run_response = member_agent.run( # type: ignore
5743
- input=member_agent_task if history is None else history,
5254
+ input=member_agent_task if not history else history,
5744
5255
  user_id=user_id,
5745
5256
  # All members have the same session_id
5746
5257
  session_id=session.session_id,
@@ -5751,11 +5262,13 @@ class Team:
5751
5262
  files=files,
5752
5263
  stream=False,
5753
5264
  debug_mode=debug_mode,
5265
+ workflow_context=workflow_context,
5754
5266
  add_history_to_context=add_history_to_context,
5755
5267
  knowledge_filters=knowledge_filters
5756
5268
  if not member_agent.knowledge_filters and member_agent.knowledge
5757
5269
  else None,
5758
5270
  )
5271
+
5759
5272
  check_if_run_cancelled(member_agent_run_response) # type: ignore
5760
5273
 
5761
5274
  try:
@@ -5764,13 +5277,12 @@ class Team:
5764
5277
  ):
5765
5278
  yield "No response from the member agent."
5766
5279
  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
5280
+ content = member_agent_run_response.content.strip() # type: ignore
5281
+ if len(content) > 0:
5282
+ yield content
5769
5283
 
5770
5284
  # 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
- ):
5285
+ elif member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0: # type: ignore
5774
5286
  tool_str = ""
5775
5287
  for tool in member_agent_run_response.tools: # type: ignore
5776
5288
  if tool.result:
@@ -5789,46 +5301,23 @@ class Team:
5789
5301
  # Afterward, switch back to the team logger
5790
5302
  use_team_logger()
5791
5303
 
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
5304
+ _process_delegate_task_to_member(
5305
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5803
5306
  )
5804
5307
 
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
5308
+ async def adelegate_task_to_member(
5309
+ member_id: str, task_description: str, expected_output: Optional[str] = None
5822
5310
  ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5823
- """Use this function to forward a message to the selected team member.
5311
+ """Use this function to delegate a task to the selected team member.
5312
+ You must provide a clear and concise description of the task the member should achieve AND the expected output.
5824
5313
 
5825
5314
  Args:
5826
5315
  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.
5316
+ task_description (str): A clear and concise description of the task the member should achieve.
5827
5317
  expected_output (str, optional): The expected output from the member (optional).
5828
5318
  Returns:
5829
5319
  str: The result of the delegated task.
5830
5320
  """
5831
- self._member_response_model = None
5832
5321
 
5833
5322
  # Find the member agent using the helper function
5834
5323
  result = self._find_member_by_id(member_id)
@@ -5837,38 +5326,16 @@ class Team:
5837
5326
  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
5327
  return
5839
5328
 
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
5329
+ _, member_agent = result
5330
+ member_agent_task, history = _setup_delegate_task_to_member(member_agent, task_description, expected_output)
5846
5331
 
5847
5332
  # Make sure for the member agent, we are using the agent logger
5848
5333
  use_agent_logger()
5849
5334
 
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
5335
  member_session_state_copy = copy(session_state)
5869
5336
  if stream:
5870
5337
  member_agent_run_response_stream = member_agent.arun( # type: ignore
5871
- input=member_input,
5338
+ input=member_agent_task if not history else history,
5872
5339
  user_id=user_id,
5873
5340
  # All members have the same session_id
5874
5341
  session_id=session.session_id,
@@ -5879,9 +5346,9 @@ class Team:
5879
5346
  files=files,
5880
5347
  stream=True,
5881
5348
  stream_intermediate_steps=stream_intermediate_steps,
5882
- workflow_context=workflow_context,
5883
5349
  debug_mode=debug_mode,
5884
5350
  add_history_to_context=add_history_to_context,
5351
+ workflow_context=workflow_context,
5885
5352
  knowledge_filters=knowledge_filters
5886
5353
  if not member_agent.knowledge_filters and member_agent.knowledge
5887
5354
  else None,
@@ -5889,16 +5356,21 @@ class Team:
5889
5356
  )
5890
5357
  member_agent_run_response = None
5891
5358
  async for member_agent_run_response_event in member_agent_run_response_stream:
5359
+ # If we get the final response, we can break out of the loop
5892
5360
  if isinstance(member_agent_run_response_event, TeamRunOutput) or isinstance(
5893
5361
  member_agent_run_response_event, RunOutput
5894
5362
  ):
5895
5363
  member_agent_run_response = member_agent_run_response_event # type: ignore
5896
5364
  break
5365
+
5366
+ # Check if the run is cancelled
5897
5367
  check_if_run_cancelled(member_agent_run_response_event)
5368
+
5369
+ # Yield the member event directly
5898
5370
  yield member_agent_run_response_event
5899
5371
  else:
5900
5372
  member_agent_run_response = await member_agent.arun( # type: ignore
5901
- input=member_input,
5373
+ input=member_agent_task if not history else history,
5902
5374
  user_id=user_id,
5903
5375
  # All members have the same session_id
5904
5376
  session_id=session.session_id,
@@ -5909,9 +5381,10 @@ class Team:
5909
5381
  files=files,
5910
5382
  stream=False,
5911
5383
  debug_mode=debug_mode,
5384
+ workflow_context=workflow_context,
5912
5385
  add_history_to_context=add_history_to_context,
5913
5386
  knowledge_filters=knowledge_filters
5914
- if (member_agent.knowledge_filters and member_agent.knowledge)
5387
+ if not member_agent.knowledge_filters and member_agent.knowledge
5915
5388
  else None,
5916
5389
  )
5917
5390
  check_if_run_cancelled(member_agent_run_response) # type: ignore
@@ -5927,7 +5400,8 @@ class Team:
5927
5400
 
5928
5401
  # If the content is empty but we have tool calls
5929
5402
  elif (
5930
- member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0 # type: ignore
5403
+ member_agent_run_response.tools is not None # type: ignore
5404
+ and len(member_agent_run_response.tools) > 0 # type: ignore
5931
5405
  ):
5932
5406
  yield ",".join([tool.result for tool in member_agent_run_response.tools if tool.result]) # type: ignore
5933
5407
  elif issubclass(type(member_agent_run_response.content), BaseModel): # type: ignore
@@ -5942,45 +5416,295 @@ class Team:
5942
5416
  # Afterward, switch back to the team logger
5943
5417
  use_team_logger()
5944
5418
 
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
5419
+ _process_delegate_task_to_member(
5420
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5956
5421
  )
5957
5422
 
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)
5423
+ # When the task should be delegated to all members
5424
+ def delegate_task_to_members(
5425
+ task_description: str, expected_output: Optional[str] = None
5426
+ ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5427
+ """
5428
+ Use this function to delegate a task to all the member agents and return a response.
5429
+ You must provide a clear and concise description of the task the member should achieve AND the expected output.
5961
5430
 
5962
- # Add the member run to the team session
5963
- if member_agent_run_response:
5964
- session.upsert_run(member_agent_run_response)
5431
+ Args:
5432
+ task_description (str): A clear and concise description of the task to send to member agents.
5433
+ expected_output (str, optional): The expected output from the member agents (optional).
5434
+ Returns:
5435
+ str: The result of the delegated task.
5436
+ """
5965
5437
 
5966
- # Update team session state
5967
- merge_dictionaries(session_state, member_session_state_copy) # type: ignore
5438
+ # Run all the members sequentially
5439
+ for _, member_agent in enumerate(self.members):
5440
+ member_agent_task, history = _setup_delegate_task_to_member(
5441
+ member_agent, task_description, expected_output
5442
+ )
5968
5443
 
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
5444
+ member_session_state_copy = copy(session_state)
5445
+ if stream:
5446
+ member_agent_run_response_stream = member_agent.run(
5447
+ input=member_agent_task if not history else history,
5448
+ user_id=user_id,
5449
+ # All members have the same session_id
5450
+ session_id=session.session_id,
5451
+ session_state=member_session_state_copy, # Send a copy to the agent
5452
+ images=images,
5453
+ videos=videos,
5454
+ audio=audio,
5455
+ files=files,
5456
+ stream=True,
5457
+ stream_intermediate_steps=stream_intermediate_steps,
5458
+ workflow_context=workflow_context,
5459
+ knowledge_filters=knowledge_filters
5460
+ if not member_agent.knowledge_filters and member_agent.knowledge
5461
+ else None,
5462
+ debug_mode=debug_mode,
5463
+ add_history_to_context=add_history_to_context,
5464
+ yield_run_response=True,
5465
+ )
5466
+ member_agent_run_response = None
5467
+ for member_agent_run_response_chunk in member_agent_run_response_stream:
5468
+ # If we get the final response, we can break out of the loop
5469
+ if isinstance(member_agent_run_response_chunk, TeamRunOutput) or isinstance(
5470
+ member_agent_run_response_chunk, RunOutput
5471
+ ):
5472
+ member_agent_run_response = member_agent_run_response_chunk # type: ignore
5473
+ break
5972
5474
 
5973
- if async_mode:
5974
- forward_function = aforward_task_to_member # type: ignore
5475
+ # Check if the run is cancelled
5476
+ check_if_run_cancelled(member_agent_run_response_chunk)
5477
+
5478
+ # Yield the member event directly
5479
+ yield member_agent_run_response_chunk
5480
+
5481
+ else:
5482
+ member_agent_run_response = member_agent.run( # type: ignore
5483
+ input=member_agent_task if not history else history,
5484
+ user_id=user_id,
5485
+ # All members have the same session_id
5486
+ session_id=session.session_id,
5487
+ session_state=member_session_state_copy, # Send a copy to the agent
5488
+ images=images,
5489
+ videos=videos,
5490
+ audio=audio,
5491
+ files=files,
5492
+ stream=False,
5493
+ workflow_context=workflow_context,
5494
+ knowledge_filters=knowledge_filters
5495
+ if not member_agent.knowledge_filters and member_agent.knowledge
5496
+ else None,
5497
+ debug_mode=debug_mode,
5498
+ add_history_to_context=add_history_to_context,
5499
+ )
5500
+
5501
+ check_if_run_cancelled(member_agent_run_response) # type: ignore
5502
+
5503
+ try:
5504
+ if member_agent_run_response.content is None and ( # type: ignore
5505
+ member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0 # type: ignore
5506
+ ):
5507
+ yield f"Agent {member_agent.name}: No response from the member agent."
5508
+ elif isinstance(member_agent_run_response.content, str): # type: ignore
5509
+ if len(member_agent_run_response.content.strip()) > 0: # type: ignore
5510
+ yield f"Agent {member_agent.name}: {member_agent_run_response.content}" # type: ignore
5511
+ elif (
5512
+ member_agent_run_response.tools is not None and len(member_agent_run_response.tools) > 0 # type: ignore
5513
+ ):
5514
+ yield f"Agent {member_agent.name}: {','.join([tool.result for tool in member_agent_run_response.tools])}" # type: ignore
5515
+ elif issubclass(type(member_agent_run_response.content), BaseModel): # type: ignore
5516
+ yield f"Agent {member_agent.name}: {member_agent_run_response.content.model_dump_json(indent=2)}" # type: ignore
5517
+ else:
5518
+ import json
5519
+
5520
+ yield f"Agent {member_agent.name}: {json.dumps(member_agent_run_response.content, indent=2)}" # type: ignore
5521
+ except Exception as e:
5522
+ yield f"Agent {member_agent.name}: Error - {str(e)}"
5523
+
5524
+ _process_delegate_task_to_member(
5525
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5526
+ )
5527
+
5528
+ # After all the member runs, switch back to the team logger
5529
+ use_team_logger()
5530
+
5531
+ # When the task should be delegated to all members
5532
+ async def adelegate_task_to_members(
5533
+ task_description: str, expected_output: Optional[str] = None
5534
+ ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent, str]]:
5535
+ """Use this function to delegate a task to all the member agents and return a response.
5536
+ You must provide a clear and concise description of the task to send to member agents AND the expected output.
5537
+
5538
+ Args:
5539
+ task_description (str): A clear and concise description of the task to send to member agents.
5540
+ expected_output (str, optional): The expected output from the member agents (optional).
5541
+ Returns:
5542
+ str: The result of the delegated task.
5543
+ """
5544
+
5545
+ if stream:
5546
+ # Concurrent streaming: launch each member as a streaming worker and merge events
5547
+ done_marker = object()
5548
+ queue: "asyncio.Queue[Union[RunOutputEvent, TeamRunOutputEvent, str, object]]" = asyncio.Queue()
5549
+
5550
+ async def stream_member(agent: Union[Agent, "Team"], idx: int) -> None:
5551
+ member_agent_task, history = _setup_delegate_task_to_member(
5552
+ agent, task_description, expected_output
5553
+ )
5554
+ member_session_state_copy = copy(session_state)
5555
+
5556
+ member_stream = agent.arun( # type: ignore
5557
+ input=member_agent_task if not history else history,
5558
+ user_id=user_id,
5559
+ session_id=session.session_id,
5560
+ session_state=member_session_state_copy, # Send a copy to the agent
5561
+ images=images,
5562
+ videos=videos,
5563
+ audio=audio,
5564
+ files=files,
5565
+ stream=True,
5566
+ stream_intermediate_steps=stream_intermediate_steps,
5567
+ workflow_context=workflow_context,
5568
+ debug_mode=debug_mode,
5569
+ knowledge_filters=knowledge_filters
5570
+ if not member_agent.knowledge_filters and member_agent.knowledge
5571
+ else None,
5572
+ add_history_to_context=add_history_to_context,
5573
+ yield_run_response=True,
5574
+ )
5575
+ member_agent_run_response = None
5576
+ try:
5577
+ async for member_agent_run_output_event in member_stream:
5578
+ if isinstance(member_agent_run_output_event, TeamRunOutput) or isinstance(
5579
+ member_agent_run_output_event, RunOutput
5580
+ ):
5581
+ member_agent_run_response = member_agent_run_output_event # type: ignore
5582
+ break
5583
+ check_if_run_cancelled(member_agent_run_output_event)
5584
+ await queue.put(member_agent_run_output_event)
5585
+ finally:
5586
+ _process_delegate_task_to_member(
5587
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5588
+ )
5589
+
5590
+ # Initialize and launch all members
5591
+ tasks: List[asyncio.Task[None]] = []
5592
+ for member_agent_index, member_agent in enumerate(self.members):
5593
+ current_agent = member_agent
5594
+ current_index = member_agent_index
5595
+ self._initialize_member(current_agent)
5596
+ tasks.append(asyncio.create_task(stream_member(current_agent, current_index)))
5597
+
5598
+ # Drain queue until all members reported done
5599
+ completed = 0
5600
+ try:
5601
+ while completed < len(tasks):
5602
+ item = await queue.get()
5603
+ if item is done_marker:
5604
+ completed += 1
5605
+ else:
5606
+ yield item # type: ignore
5607
+ finally:
5608
+ # Ensure tasks do not leak on cancellation
5609
+ for t in tasks:
5610
+ if not t.done():
5611
+ t.cancel()
5612
+ # Await cancellation to suppress warnings
5613
+ for t in tasks:
5614
+ with contextlib.suppress(Exception):
5615
+ await t
5616
+
5617
+ else:
5618
+ # Non-streaming concurrent run of members; collect results when done
5619
+ tasks = []
5620
+ for member_agent_index, member_agent in enumerate(self.members):
5621
+ current_agent = member_agent
5622
+ current_index = member_agent_index
5623
+ member_agent_task, history = _setup_delegate_task_to_member(
5624
+ current_agent, task_description, expected_output
5625
+ )
5626
+
5627
+ async def run_member_agent(agent=current_agent) -> str:
5628
+ member_session_state_copy = copy(session_state)
5629
+ member_agent_run_response = await agent.arun(
5630
+ input=member_agent_task if not history else history,
5631
+ user_id=user_id,
5632
+ # All members have the same session_id
5633
+ session_id=session.session_id,
5634
+ session_state=member_session_state_copy, # Send a copy to the agent
5635
+ images=images,
5636
+ videos=videos,
5637
+ audio=audio,
5638
+ files=files,
5639
+ stream=False,
5640
+ stream_intermediate_steps=stream_intermediate_steps,
5641
+ debug_mode=debug_mode,
5642
+ workflow_context=workflow_context,
5643
+ knowledge_filters=knowledge_filters
5644
+ if not member_agent.knowledge_filters and member_agent.knowledge
5645
+ else None,
5646
+ add_history_to_context=add_history_to_context,
5647
+ )
5648
+ check_if_run_cancelled(member_agent_run_response)
5649
+
5650
+ _process_delegate_task_to_member(
5651
+ member_agent_run_response, member_agent, member_agent_task, member_session_state_copy
5652
+ )
5653
+
5654
+ member_name = member_agent.name if member_agent.name else f"agent_{member_agent_index}"
5655
+ try:
5656
+ if member_agent_run_response.content is None and (
5657
+ member_agent_run_response.tools is None or len(member_agent_run_response.tools) == 0
5658
+ ):
5659
+ return f"Agent {member_name}: No response from the member agent."
5660
+ elif isinstance(member_agent_run_response.content, str):
5661
+ if len(member_agent_run_response.content.strip()) > 0:
5662
+ return f"Agent {member_name}: {member_agent_run_response.content}"
5663
+ elif (
5664
+ member_agent_run_response.tools is not None
5665
+ and len(member_agent_run_response.tools) > 0
5666
+ ):
5667
+ return f"Agent {member_name}: {','.join([tool.result for tool in member_agent_run_response.tools])}"
5668
+ elif issubclass(type(member_agent_run_response.content), BaseModel):
5669
+ return f"Agent {member_name}: {member_agent_run_response.content.model_dump_json(indent=2)}" # type: ignore
5670
+ else:
5671
+ import json
5672
+
5673
+ return f"Agent {member_name}: {json.dumps(member_agent_run_response.content, indent=2)}"
5674
+ except Exception as e:
5675
+ return f"Agent {member_name}: Error - {str(e)}"
5676
+
5677
+ return f"Agent {member_name}: No Response"
5678
+
5679
+ tasks.append(run_member_agent) # type: ignore
5680
+
5681
+ results = await asyncio.gather(*[task() for task in tasks]) # type: ignore
5682
+ for result in results:
5683
+ yield result
5684
+
5685
+ # After all the member runs, switch back to the team logger
5686
+ use_team_logger()
5687
+
5688
+ if self.delegate_task_to_all_members:
5689
+ if async_mode:
5690
+ delegate_function = adelegate_task_to_members # type: ignore
5691
+ else:
5692
+ delegate_function = delegate_task_to_members # type: ignore
5693
+
5694
+ delegate_func = Function.from_callable(delegate_function, name="delegate_task_to_members")
5975
5695
  else:
5976
- forward_function = forward_task_to_member # type: ignore
5696
+ if async_mode:
5697
+ delegate_function = adelegate_task_to_member # type: ignore
5698
+ else:
5699
+ delegate_function = delegate_task_to_member # type: ignore
5977
5700
 
5978
- forward_func = Function.from_callable(forward_function, name="forward_task_to_member", strict=True)
5701
+ delegate_func = Function.from_callable(delegate_function, name="delegate_task_to_member")
5979
5702
 
5980
- forward_func.stop_after_tool_call = True
5981
- forward_func.show_result = True
5703
+ if self.respond_directly:
5704
+ delegate_func.stop_after_tool_call = True
5705
+ delegate_func.show_result = True
5982
5706
 
5983
- return forward_func
5707
+ return delegate_func
5984
5708
 
5985
5709
  ###########################################################################
5986
5710
  # Session Management
@@ -6128,7 +5852,7 @@ class Team:
6128
5852
 
6129
5853
  return team_session
6130
5854
 
6131
- log_warning(f"TeamSession {session_id_to_load} not found in db")
5855
+ log_debug(f"TeamSession {session_id_to_load} not found in db")
6132
5856
  return None
6133
5857
 
6134
5858
  def save_session(self, session: TeamSession) -> None:
@@ -6138,6 +5862,12 @@ class Team:
6138
5862
  session.session_data["session_state"].pop("current_session_id", None) # type: ignore
6139
5863
  session.session_data["session_state"].pop("current_user_id", None) # type: ignore
6140
5864
  session.session_data["session_state"].pop("current_run_id", None) # type: ignore
5865
+
5866
+ # scrub the member responses if not storing them
5867
+ if not self.store_member_responses and session.runs is not None:
5868
+ for run in session.runs:
5869
+ if hasattr(run, "member_responses"):
5870
+ run.member_responses = []
6141
5871
  self._upsert_session(session=session)
6142
5872
  log_debug(f"Created or updated TeamSession record: {session.session_id}")
6143
5873
 
@@ -6376,7 +6106,7 @@ class Team:
6376
6106
  team_member_interactions_str += "</member interactions>\n"
6377
6107
  return team_member_interactions_str
6378
6108
 
6379
- def _get_team_run_context_images(self, team_run_context: Dict[str, Any]) -> List[ImageArtifact]:
6109
+ def _get_team_run_context_images(self, team_run_context: Dict[str, Any]) -> List[Image]:
6380
6110
  if not team_run_context:
6381
6111
  return []
6382
6112
  images = []
@@ -6386,7 +6116,7 @@ class Team:
6386
6116
  images.extend(interaction["run_response"].images)
6387
6117
  return images
6388
6118
 
6389
- def _get_team_run_context_videos(self, team_run_context: Dict[str, Any]) -> List[VideoArtifact]:
6119
+ def _get_team_run_context_videos(self, team_run_context: Dict[str, Any]) -> List[Video]:
6390
6120
  if not team_run_context:
6391
6121
  return []
6392
6122
  videos = []
@@ -6396,7 +6126,7 @@ class Team:
6396
6126
  videos.extend(interaction["run_response"].videos)
6397
6127
  return videos
6398
6128
 
6399
- def _get_team_run_context_audio(self, team_run_context: Dict[str, Any]) -> List[AudioArtifact]:
6129
+ def _get_team_run_context_audio(self, team_run_context: Dict[str, Any]) -> List[Audio]:
6400
6130
  if not team_run_context:
6401
6131
  return []
6402
6132
  audio = []
@@ -6410,21 +6140,21 @@ class Team:
6410
6140
  # Handle images, videos and audio
6411
6141
  ###########################################################################
6412
6142
 
6413
- def _add_image(self, image: ImageArtifact, run_response: TeamRunOutput) -> None:
6143
+ def _add_image(self, image: Image, run_response: TeamRunOutput) -> None:
6414
6144
  """Add an image to both the agent's stateful storage and the current run response"""
6415
6145
  # Add to run response
6416
6146
  if run_response.images is None:
6417
6147
  run_response.images = []
6418
6148
  run_response.images.append(image)
6419
6149
 
6420
- def _add_video(self, video: VideoArtifact, run_response: TeamRunOutput) -> None:
6150
+ def _add_video(self, video: Video, run_response: TeamRunOutput) -> None:
6421
6151
  """Add a video to both the agent's stateful storage and the current run response"""
6422
6152
  # Add to run response
6423
6153
  if run_response.videos is None:
6424
6154
  run_response.videos = []
6425
6155
  run_response.videos.append(video)
6426
6156
 
6427
- def _add_audio(self, audio: AudioArtifact, run_response: TeamRunOutput) -> None:
6157
+ def _add_audio(self, audio: Audio, run_response: TeamRunOutput) -> None:
6428
6158
  """Add audio to both the agent's stateful storage and the current run response"""
6429
6159
  # Add to run response
6430
6160
  if run_response.audio is None:
@@ -6867,8 +6597,6 @@ class Team:
6867
6597
  team_data["team_id"] = self.id
6868
6598
  if self.model is not None:
6869
6599
  team_data["model"] = self.model.to_dict()
6870
- if self.mode is not None:
6871
- team_data["mode"] = self.mode
6872
6600
  return team_data
6873
6601
 
6874
6602
  ###########################################################################