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