agno 1.7.7__py3-none-any.whl → 1.7.8__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 +219 -14
- agno/document/reader/youtube_reader.py +8 -4
- agno/models/anthropic/claude.py +1 -1
- agno/models/base.py +4 -0
- agno/models/message.py +6 -2
- agno/models/openai/chat.py +3 -0
- agno/models/openai/responses.py +6 -5
- agno/run/response.py +31 -0
- agno/run/team.py +17 -0
- agno/storage/gcs_json.py +1 -1
- agno/storage/json.py +2 -1
- agno/storage/redis.py +1 -1
- agno/storage/yaml.py +1 -1
- agno/team/team.py +432 -225
- agno/tools/function.py +21 -11
- agno/tools/googlecalendar.py +567 -121
- agno/tools/googlesheets.py +6 -1
- agno/tools/mcp.py +19 -1
- agno/utils/events.py +50 -0
- agno/utils/response.py +3 -1
- {agno-1.7.7.dist-info → agno-1.7.8.dist-info}/METADATA +1 -1
- {agno-1.7.7.dist-info → agno-1.7.8.dist-info}/RECORD +26 -26
- {agno-1.7.7.dist-info → agno-1.7.8.dist-info}/WHEEL +0 -0
- {agno-1.7.7.dist-info → agno-1.7.8.dist-info}/entry_points.txt +0 -0
- {agno-1.7.7.dist-info → agno-1.7.8.dist-info}/licenses/LICENSE +0 -0
- {agno-1.7.7.dist-info → agno-1.7.8.dist-info}/top_level.txt +0 -0
agno/team/team.py
CHANGED
|
@@ -230,6 +230,10 @@ class Team:
|
|
|
230
230
|
parser_model: Optional[Model] = None
|
|
231
231
|
# Provide a prompt for the parser model
|
|
232
232
|
parser_model_prompt: Optional[str] = None
|
|
233
|
+
# Provide an output model to parse the response from the team
|
|
234
|
+
output_model: Optional[Model] = None
|
|
235
|
+
# Provide a prompt for the output model
|
|
236
|
+
output_model_prompt: Optional[str] = None
|
|
233
237
|
# If `response_model` is set, sets the response mode of the model, i.e. if the model should explicitly respond with a JSON object instead of a Pydantic model
|
|
234
238
|
use_json_mode: bool = False
|
|
235
239
|
# If True, parse the response
|
|
@@ -346,6 +350,8 @@ class Team:
|
|
|
346
350
|
response_model: Optional[Type[BaseModel]] = None,
|
|
347
351
|
parser_model: Optional[Model] = None,
|
|
348
352
|
parser_model_prompt: Optional[str] = None,
|
|
353
|
+
output_model: Optional[Model] = None,
|
|
354
|
+
output_model_prompt: Optional[str] = None,
|
|
349
355
|
use_json_mode: bool = False,
|
|
350
356
|
parse_response: bool = True,
|
|
351
357
|
memory: Optional[Union[TeamMemory, Memory]] = None,
|
|
@@ -432,6 +438,8 @@ class Team:
|
|
|
432
438
|
self.response_model = response_model
|
|
433
439
|
self.parser_model = parser_model
|
|
434
440
|
self.parser_model_prompt = parser_model_prompt
|
|
441
|
+
self.output_model = output_model
|
|
442
|
+
self.output_model_prompt = output_model_prompt
|
|
435
443
|
self.use_json_mode = use_json_mode
|
|
436
444
|
self.parse_response = parse_response
|
|
437
445
|
|
|
@@ -1016,6 +1024,9 @@ class Team:
|
|
|
1016
1024
|
tool_call_limit=self.tool_call_limit,
|
|
1017
1025
|
)
|
|
1018
1026
|
|
|
1027
|
+
# If an output model is provided, generate output using the output model
|
|
1028
|
+
self._parse_response_with_output_model(model_response, run_messages)
|
|
1029
|
+
|
|
1019
1030
|
# If a parser model is provided, structure the response separately
|
|
1020
1031
|
self._parse_response_with_parser_model(model_response, run_messages)
|
|
1021
1032
|
|
|
@@ -1085,12 +1096,31 @@ class Team:
|
|
|
1085
1096
|
yield self._handle_event(create_team_run_response_started_event(run_response), run_response)
|
|
1086
1097
|
|
|
1087
1098
|
# 2. Get a response from the model
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1099
|
+
if self.output_model is None:
|
|
1100
|
+
yield from self._handle_model_response_stream(
|
|
1101
|
+
run_response=run_response,
|
|
1102
|
+
run_messages=run_messages,
|
|
1103
|
+
response_format=response_format,
|
|
1104
|
+
stream_intermediate_steps=stream_intermediate_steps,
|
|
1105
|
+
)
|
|
1106
|
+
else:
|
|
1107
|
+
for event in self._handle_model_response_stream(
|
|
1108
|
+
run_response=run_response,
|
|
1109
|
+
run_messages=run_messages,
|
|
1110
|
+
response_format=response_format,
|
|
1111
|
+
stream_intermediate_steps=stream_intermediate_steps,
|
|
1112
|
+
):
|
|
1113
|
+
from agno.run.team import RunResponseContentEvent
|
|
1114
|
+
|
|
1115
|
+
if isinstance(event, RunResponseContentEvent):
|
|
1116
|
+
if stream_intermediate_steps:
|
|
1117
|
+
yield event
|
|
1118
|
+
else:
|
|
1119
|
+
yield event
|
|
1120
|
+
|
|
1121
|
+
yield from self._generate_response_with_output_model_stream(
|
|
1122
|
+
run_response=run_response, run_messages=run_messages
|
|
1123
|
+
)
|
|
1094
1124
|
|
|
1095
1125
|
# If a parser model is provided, structure the response separately
|
|
1096
1126
|
yield from self._parse_response_with_parser_model_stream(
|
|
@@ -1409,6 +1439,9 @@ class Team:
|
|
|
1409
1439
|
tool_call_limit=self.tool_call_limit,
|
|
1410
1440
|
) # type: ignore
|
|
1411
1441
|
|
|
1442
|
+
# If an output model is provided, generate output using the output model
|
|
1443
|
+
await self._agenerate_response_with_output_model(model_response=model_response, run_messages=run_messages)
|
|
1444
|
+
|
|
1412
1445
|
# If a parser model is provided, structure the response separately
|
|
1413
1446
|
await self._aparse_response_with_parser_model(model_response=model_response, run_messages=run_messages)
|
|
1414
1447
|
|
|
@@ -1477,13 +1510,29 @@ class Team:
|
|
|
1477
1510
|
)
|
|
1478
1511
|
|
|
1479
1512
|
# 2. Get a response from the model
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1513
|
+
if self.output_model is None:
|
|
1514
|
+
async for event in self._ahandle_model_response_stream(
|
|
1515
|
+
run_response=run_response,
|
|
1516
|
+
run_messages=run_messages,
|
|
1517
|
+
response_format=response_format,
|
|
1518
|
+
stream_intermediate_steps=stream_intermediate_steps,
|
|
1519
|
+
):
|
|
1520
|
+
yield event
|
|
1521
|
+
else:
|
|
1522
|
+
from agno.run.team import RunResponseContentEvent
|
|
1523
|
+
|
|
1524
|
+
async for event in self._agenerate_response_with_output_model_stream(
|
|
1525
|
+
run_response=run_response,
|
|
1526
|
+
run_messages=run_messages,
|
|
1527
|
+
stream_intermediate_steps=stream_intermediate_steps,
|
|
1528
|
+
):
|
|
1529
|
+
if isinstance(event, RunResponseContentEvent):
|
|
1530
|
+
if stream_intermediate_steps:
|
|
1531
|
+
yield event
|
|
1532
|
+
else:
|
|
1533
|
+
yield event
|
|
1534
|
+
|
|
1535
|
+
yield event
|
|
1487
1536
|
|
|
1488
1537
|
# If a parser model is provided, structure the response separately
|
|
1489
1538
|
async for event in self._aparse_response_with_parser_model_stream(
|
|
@@ -2387,6 +2436,108 @@ class Team:
|
|
|
2387
2436
|
else:
|
|
2388
2437
|
log_warning("A response model is required to parse the response with a parser model")
|
|
2389
2438
|
|
|
2439
|
+
def _parse_response_with_output_model(self, model_response: ModelResponse, run_messages: RunMessages) -> None:
|
|
2440
|
+
"""Parse the model response using the output model."""
|
|
2441
|
+
if self.output_model is None:
|
|
2442
|
+
return
|
|
2443
|
+
|
|
2444
|
+
messages_for_output_model = self.get_messages_for_output_model(run_messages.messages)
|
|
2445
|
+
output_model_response: ModelResponse = self.output_model.response(messages=messages_for_output_model)
|
|
2446
|
+
model_response.content = output_model_response.content
|
|
2447
|
+
|
|
2448
|
+
def _generate_response_with_output_model_stream(
|
|
2449
|
+
self, run_response: TeamRunResponse, run_messages: RunMessages, stream_intermediate_steps: bool = True
|
|
2450
|
+
):
|
|
2451
|
+
"""Parse the model response using the output model stream."""
|
|
2452
|
+
|
|
2453
|
+
from agno.utils.events import (
|
|
2454
|
+
create_team_output_model_response_completed_event,
|
|
2455
|
+
create_team_output_model_response_started_event,
|
|
2456
|
+
)
|
|
2457
|
+
|
|
2458
|
+
if self.output_model is None:
|
|
2459
|
+
return
|
|
2460
|
+
|
|
2461
|
+
if stream_intermediate_steps:
|
|
2462
|
+
yield self._handle_event(create_team_output_model_response_started_event(run_response), run_response)
|
|
2463
|
+
|
|
2464
|
+
messages_for_output_model = self.get_messages_for_output_model(run_messages.messages)
|
|
2465
|
+
model_response = ModelResponse(content="")
|
|
2466
|
+
|
|
2467
|
+
for model_response_event in self.output_model.response_stream(messages=messages_for_output_model):
|
|
2468
|
+
yield from self._handle_model_response_chunk(
|
|
2469
|
+
run_response=run_response,
|
|
2470
|
+
full_model_response=model_response,
|
|
2471
|
+
model_response_event=model_response_event,
|
|
2472
|
+
)
|
|
2473
|
+
|
|
2474
|
+
# Update the TeamRunResponse content
|
|
2475
|
+
run_response.content = model_response.content
|
|
2476
|
+
run_response.created_at = model_response.created_at
|
|
2477
|
+
|
|
2478
|
+
if stream_intermediate_steps:
|
|
2479
|
+
yield self._handle_event(create_team_output_model_response_completed_event(run_response), run_response)
|
|
2480
|
+
|
|
2481
|
+
# Build a list of messages that should be added to the RunResponse
|
|
2482
|
+
messages_for_run_response = [m for m in run_messages.messages if m.add_to_agent_memory]
|
|
2483
|
+
# Update the RunResponse messages
|
|
2484
|
+
run_response.messages = messages_for_run_response
|
|
2485
|
+
# Update the RunResponse metrics
|
|
2486
|
+
run_response.metrics = self._aggregate_metrics_from_messages(messages_for_run_response)
|
|
2487
|
+
|
|
2488
|
+
async def _agenerate_response_with_output_model(
|
|
2489
|
+
self, model_response: ModelResponse, run_messages: RunMessages
|
|
2490
|
+
) -> None:
|
|
2491
|
+
"""Parse the model response using the output model stream."""
|
|
2492
|
+
if self.output_model is None:
|
|
2493
|
+
return
|
|
2494
|
+
|
|
2495
|
+
messages_for_output_model = self.get_messages_for_output_model(run_messages.messages)
|
|
2496
|
+
output_model_response: ModelResponse = await self.output_model.aresponse(messages=messages_for_output_model)
|
|
2497
|
+
model_response.content = output_model_response.content
|
|
2498
|
+
|
|
2499
|
+
async def _agenerate_response_with_output_model_stream(
|
|
2500
|
+
self, run_response: TeamRunResponse, run_messages: RunMessages, stream_intermediate_steps: bool = True
|
|
2501
|
+
):
|
|
2502
|
+
"""Parse the model response using the output model stream."""
|
|
2503
|
+
from agno.utils.events import (
|
|
2504
|
+
create_team_output_model_response_completed_event,
|
|
2505
|
+
create_team_output_model_response_started_event,
|
|
2506
|
+
)
|
|
2507
|
+
|
|
2508
|
+
if self.output_model is None:
|
|
2509
|
+
return
|
|
2510
|
+
|
|
2511
|
+
if stream_intermediate_steps:
|
|
2512
|
+
yield self._handle_event(create_team_output_model_response_started_event(run_response), run_response)
|
|
2513
|
+
|
|
2514
|
+
messages_for_output_model = self.get_messages_for_output_model(run_messages.messages)
|
|
2515
|
+
model_response = ModelResponse(content="")
|
|
2516
|
+
|
|
2517
|
+
model_response_stream = self.output_model.aresponse_stream(messages=messages_for_output_model)
|
|
2518
|
+
|
|
2519
|
+
async for model_response_event in model_response_stream:
|
|
2520
|
+
for event in self._handle_model_response_chunk(
|
|
2521
|
+
run_response=run_response,
|
|
2522
|
+
full_model_response=model_response,
|
|
2523
|
+
model_response_event=model_response_event,
|
|
2524
|
+
):
|
|
2525
|
+
yield event
|
|
2526
|
+
|
|
2527
|
+
# Update the TeamRunResponse content
|
|
2528
|
+
run_response.content = model_response.content
|
|
2529
|
+
run_response.created_at = model_response.created_at
|
|
2530
|
+
|
|
2531
|
+
if stream_intermediate_steps:
|
|
2532
|
+
yield self._handle_event(create_team_output_model_response_completed_event(run_response), run_response)
|
|
2533
|
+
|
|
2534
|
+
# Build a list of messages that should be added to the RunResponse
|
|
2535
|
+
messages_for_run_response = [m for m in run_messages.messages if m.add_to_agent_memory]
|
|
2536
|
+
# Update the RunResponse messages
|
|
2537
|
+
run_response.messages = messages_for_run_response
|
|
2538
|
+
# Update the RunResponse metrics
|
|
2539
|
+
run_response.metrics = self._aggregate_metrics_from_messages(messages_for_run_response)
|
|
2540
|
+
|
|
2390
2541
|
def _handle_event(self, event: Union[RunResponseEvent, TeamRunResponseEvent], run_response: TeamRunResponse):
|
|
2391
2542
|
# We only store events that are not run_response_content events
|
|
2392
2543
|
events_to_skip = [event.value for event in self.events_to_skip] if self.events_to_skip else []
|
|
@@ -2788,10 +2939,9 @@ class Team:
|
|
|
2788
2939
|
if not tags_to_include_in_markdown:
|
|
2789
2940
|
tags_to_include_in_markdown = {"think", "thinking"}
|
|
2790
2941
|
|
|
2791
|
-
stream_intermediate_steps = True # With streaming print response, we need to stream intermediate steps
|
|
2792
|
-
|
|
2793
2942
|
_response_content: str = ""
|
|
2794
2943
|
_response_thinking: str = ""
|
|
2944
|
+
_response_reasoning_content: str = ""
|
|
2795
2945
|
reasoning_steps: List[ReasoningStep] = []
|
|
2796
2946
|
|
|
2797
2947
|
# Track tool calls by member and team
|
|
@@ -2856,7 +3006,7 @@ class Team:
|
|
|
2856
3006
|
if self.response_model is not None:
|
|
2857
3007
|
team_markdown = False
|
|
2858
3008
|
|
|
2859
|
-
if isinstance(resp, tuple(get_args(TeamRunResponseEvent))):
|
|
3009
|
+
if isinstance(resp, tuple(get_args(TeamRunResponseEvent))) and resp.team_id == self.team_id:
|
|
2860
3010
|
if resp.event == TeamRunEvent.run_response_content:
|
|
2861
3011
|
if isinstance(resp.content, str):
|
|
2862
3012
|
_response_content += resp.content
|
|
@@ -2867,6 +3017,8 @@ class Team:
|
|
|
2867
3017
|
log_warning(f"Failed to convert response to JSON: {e}")
|
|
2868
3018
|
if resp.thinking is not None:
|
|
2869
3019
|
_response_thinking += resp.thinking
|
|
3020
|
+
if hasattr(resp, "reasoning_content") and resp.reasoning_content is not None:
|
|
3021
|
+
_response_reasoning_content += resp.reasoning_content
|
|
2870
3022
|
if (
|
|
2871
3023
|
hasattr(resp, "extra_data")
|
|
2872
3024
|
and resp.extra_data is not None
|
|
@@ -2887,7 +3039,12 @@ class Team:
|
|
|
2887
3039
|
team_tool_calls.append(tool)
|
|
2888
3040
|
|
|
2889
3041
|
# Collect member tool calls, avoiding duplicates
|
|
2890
|
-
if
|
|
3042
|
+
if (
|
|
3043
|
+
self.show_tool_calls
|
|
3044
|
+
and hasattr(resp, "member_responses")
|
|
3045
|
+
and resp.member_responses
|
|
3046
|
+
and self.show_members_responses
|
|
3047
|
+
):
|
|
2891
3048
|
for member_response in resp.member_responses:
|
|
2892
3049
|
member_id = None
|
|
2893
3050
|
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
@@ -2918,6 +3075,7 @@ class Team:
|
|
|
2918
3075
|
# Create new panels for each chunk
|
|
2919
3076
|
panels = []
|
|
2920
3077
|
|
|
3078
|
+
# Print user message
|
|
2921
3079
|
if message and show_message:
|
|
2922
3080
|
render = True
|
|
2923
3081
|
# Convert message to a panel
|
|
@@ -2950,60 +3108,61 @@ class Team:
|
|
|
2950
3108
|
panels.append(status)
|
|
2951
3109
|
|
|
2952
3110
|
# Process member responses and their tool calls
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
3111
|
+
if self.show_members_responses and hasattr(resp, "member_responses") and resp.member_responses:
|
|
3112
|
+
for member_response in resp.member_responses:
|
|
3113
|
+
member_id = None
|
|
3114
|
+
member_name = "Team Member"
|
|
3115
|
+
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
3116
|
+
member_id = member_response.agent_id
|
|
3117
|
+
member_name = self._get_member_name(member_id)
|
|
3118
|
+
elif isinstance(member_response, TeamRunResponse) and member_response.team_id is not None:
|
|
3119
|
+
member_id = member_response.team_id
|
|
3120
|
+
member_name = self._get_member_name(member_id)
|
|
2962
3121
|
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3122
|
+
# If we have tool calls for this member, display them
|
|
3123
|
+
if self.show_tool_calls and member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
3124
|
+
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
3125
|
+
if formatted_calls:
|
|
3126
|
+
console_width = console.width if console else 80
|
|
3127
|
+
panel_width = console_width + 30
|
|
2969
3128
|
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
tool_calls_text = "\n\n".join(lines)
|
|
3129
|
+
lines = []
|
|
3130
|
+
for call in formatted_calls:
|
|
3131
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
3132
|
+
lines.append(wrapped_call)
|
|
2976
3133
|
|
|
2977
|
-
|
|
2978
|
-
content=tool_calls_text,
|
|
2979
|
-
title=f"{member_name} Tool Calls",
|
|
2980
|
-
border_style="yellow",
|
|
2981
|
-
)
|
|
2982
|
-
panels.append(member_tool_calls_panel)
|
|
3134
|
+
tool_calls_text = "\n\n".join(lines)
|
|
2983
3135
|
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3136
|
+
member_tool_calls_panel = create_panel(
|
|
3137
|
+
content=tool_calls_text,
|
|
3138
|
+
title=f"{member_name} Tool Calls",
|
|
3139
|
+
border_style="yellow",
|
|
3140
|
+
)
|
|
3141
|
+
panels.append(member_tool_calls_panel)
|
|
2989
3142
|
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
3143
|
+
# Process member response content
|
|
3144
|
+
if member_id is not None:
|
|
3145
|
+
show_markdown = False
|
|
3146
|
+
if markdown:
|
|
3147
|
+
show_markdown = True
|
|
3148
|
+
|
|
3149
|
+
member_response_content = self._parse_response_content(
|
|
3150
|
+
member_response,
|
|
3151
|
+
tags_to_include_in_markdown,
|
|
3152
|
+
show_markdown=show_markdown,
|
|
3153
|
+
)
|
|
2995
3154
|
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3155
|
+
member_response_panel = create_panel(
|
|
3156
|
+
content=member_response_content,
|
|
3157
|
+
title=f"{member_name} Response",
|
|
3158
|
+
border_style="magenta",
|
|
3159
|
+
)
|
|
3001
3160
|
|
|
3002
|
-
|
|
3161
|
+
panels.append(member_response_panel)
|
|
3003
3162
|
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3163
|
+
# Store for reference
|
|
3164
|
+
if member_id is not None:
|
|
3165
|
+
member_response_panels[member_id] = member_response_panel
|
|
3007
3166
|
|
|
3008
3167
|
# Add team tool calls panel if available (before the team response)
|
|
3009
3168
|
if self.show_tool_calls and team_tool_calls:
|
|
@@ -3128,90 +3287,91 @@ class Team:
|
|
|
3128
3287
|
final_panels.append(thinking_panel)
|
|
3129
3288
|
|
|
3130
3289
|
# Add member tool calls and responses in correct order
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
if member_id:
|
|
3139
|
-
# First add tool calls if any
|
|
3140
|
-
if self.show_tool_calls and member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
3141
|
-
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
3142
|
-
if formatted_calls:
|
|
3143
|
-
console_width = console.width if console else 80
|
|
3144
|
-
panel_width = console_width + 30
|
|
3145
|
-
|
|
3146
|
-
lines = []
|
|
3147
|
-
for call in formatted_calls:
|
|
3148
|
-
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
3149
|
-
lines.append(wrapped_call)
|
|
3290
|
+
if self.show_members_responses:
|
|
3291
|
+
for i, member_response in enumerate(self.run_response.member_responses if self.run_response else []):
|
|
3292
|
+
member_id = None
|
|
3293
|
+
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
3294
|
+
member_id = member_response.agent_id
|
|
3295
|
+
elif isinstance(member_response, TeamRunResponse) and member_response.team_id is not None:
|
|
3296
|
+
member_id = member_response.team_id
|
|
3150
3297
|
|
|
3151
|
-
|
|
3298
|
+
if member_id:
|
|
3299
|
+
# First add tool calls if any
|
|
3300
|
+
if self.show_tool_calls and member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
3301
|
+
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
3302
|
+
if formatted_calls:
|
|
3303
|
+
console_width = console.width if console else 80
|
|
3304
|
+
panel_width = console_width + 30
|
|
3152
3305
|
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
border_style="yellow",
|
|
3158
|
-
)
|
|
3159
|
-
final_panels.append(member_tool_calls_panel)
|
|
3306
|
+
lines = []
|
|
3307
|
+
for call in formatted_calls:
|
|
3308
|
+
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
3309
|
+
lines.append(wrapped_call)
|
|
3160
3310
|
|
|
3161
|
-
|
|
3162
|
-
reasoning_steps = []
|
|
3163
|
-
if (
|
|
3164
|
-
member_response.extra_data is not None
|
|
3165
|
-
and member_response.extra_data.reasoning_steps is not None
|
|
3166
|
-
):
|
|
3167
|
-
reasoning_steps = member_response.extra_data.reasoning_steps
|
|
3168
|
-
if reasoning_steps and show_reasoning:
|
|
3169
|
-
for j, step in enumerate(reasoning_steps, 1):
|
|
3170
|
-
member_reasoning_panel = self._build_reasoning_step_panel(
|
|
3171
|
-
j, step, show_full_reasoning, color="magenta"
|
|
3172
|
-
)
|
|
3173
|
-
final_panels.append(member_reasoning_panel)
|
|
3311
|
+
tool_calls_text = "\n\n".join(lines)
|
|
3174
3312
|
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3313
|
+
member_name = self._get_member_name(member_id)
|
|
3314
|
+
member_tool_calls_panel = create_panel(
|
|
3315
|
+
content=tool_calls_text,
|
|
3316
|
+
title=f"{member_name} Tool Calls",
|
|
3317
|
+
border_style="yellow",
|
|
3318
|
+
)
|
|
3319
|
+
final_panels.append(member_tool_calls_panel)
|
|
3181
3320
|
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3321
|
+
# Add reasoning steps if any
|
|
3322
|
+
reasoning_steps = []
|
|
3323
|
+
if (
|
|
3324
|
+
member_response.extra_data is not None
|
|
3325
|
+
and member_response.extra_data.reasoning_steps is not None
|
|
3326
|
+
):
|
|
3327
|
+
reasoning_steps = member_response.extra_data.reasoning_steps
|
|
3328
|
+
if reasoning_steps and show_reasoning:
|
|
3329
|
+
for j, step in enumerate(reasoning_steps, 1):
|
|
3330
|
+
member_reasoning_panel = self._build_reasoning_step_panel(
|
|
3331
|
+
j, step, show_full_reasoning, color="magenta"
|
|
3332
|
+
)
|
|
3333
|
+
final_panels.append(member_reasoning_panel)
|
|
3187
3334
|
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3335
|
+
# Then add response
|
|
3336
|
+
show_markdown = False
|
|
3337
|
+
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
3338
|
+
show_markdown = member_markdown.get(member_response.agent_id, False)
|
|
3339
|
+
elif isinstance(member_response, TeamRunResponse) and member_response.team_id is not None:
|
|
3340
|
+
show_markdown = member_markdown.get(member_response.team_id, False)
|
|
3193
3341
|
|
|
3194
|
-
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
)
|
|
3199
|
-
final_panels.append(member_response_panel)
|
|
3200
|
-
|
|
3201
|
-
# Add citations if any
|
|
3202
|
-
if member_response.citations is not None and member_response.citations.urls is not None:
|
|
3203
|
-
md_content = "\n".join(
|
|
3204
|
-
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
3205
|
-
for i, citation in enumerate(member_response.citations.urls)
|
|
3206
|
-
if citation.url # Only include citations with valid URLs
|
|
3342
|
+
member_response_content = self._parse_response_content(
|
|
3343
|
+
member_response,
|
|
3344
|
+
tags_to_include_in_markdown,
|
|
3345
|
+
show_markdown=show_markdown,
|
|
3207
3346
|
)
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3347
|
+
|
|
3348
|
+
member_name = "Team Member"
|
|
3349
|
+
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
3350
|
+
member_name = self._get_member_name(member_response.agent_id)
|
|
3351
|
+
elif isinstance(member_response, TeamRunResponse) and member_response.team_id is not None:
|
|
3352
|
+
member_name = self._get_member_name(member_response.team_id)
|
|
3353
|
+
|
|
3354
|
+
member_response_panel = create_panel(
|
|
3355
|
+
content=member_response_content,
|
|
3356
|
+
title=f"{member_name} Response",
|
|
3357
|
+
border_style="magenta",
|
|
3358
|
+
)
|
|
3359
|
+
final_panels.append(member_response_panel)
|
|
3360
|
+
|
|
3361
|
+
# Add citations if any
|
|
3362
|
+
if member_response.citations is not None and member_response.citations.urls is not None:
|
|
3363
|
+
md_content = "\n".join(
|
|
3364
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
3365
|
+
for i, citation in enumerate(member_response.citations.urls)
|
|
3366
|
+
if citation.url # Only include citations with valid URLs
|
|
3213
3367
|
)
|
|
3214
|
-
|
|
3368
|
+
if md_content: # Only create panel if there are citations
|
|
3369
|
+
citations_panel = create_panel(
|
|
3370
|
+
content=Markdown(md_content),
|
|
3371
|
+
title="Citations",
|
|
3372
|
+
border_style="magenta",
|
|
3373
|
+
)
|
|
3374
|
+
final_panels.append(citations_panel)
|
|
3215
3375
|
|
|
3216
3376
|
# Add team tool calls before team response
|
|
3217
3377
|
if self.show_tool_calls and team_tool_calls:
|
|
@@ -3654,12 +3814,11 @@ class Team:
|
|
|
3654
3814
|
if not tags_to_include_in_markdown:
|
|
3655
3815
|
tags_to_include_in_markdown = {"think", "thinking"}
|
|
3656
3816
|
|
|
3657
|
-
stream_intermediate_steps = True # With streaming print response, we need to stream intermediate steps
|
|
3658
|
-
|
|
3659
3817
|
self.run_response = cast(TeamRunResponse, self.run_response)
|
|
3660
3818
|
|
|
3661
3819
|
_response_content: str = ""
|
|
3662
3820
|
_response_thinking: str = ""
|
|
3821
|
+
_response_reasoning_content: str = ""
|
|
3663
3822
|
reasoning_steps: List[ReasoningStep] = []
|
|
3664
3823
|
|
|
3665
3824
|
# Track tool calls by member and team
|
|
@@ -3722,7 +3881,7 @@ class Team:
|
|
|
3722
3881
|
if self.response_model is not None:
|
|
3723
3882
|
team_markdown = False
|
|
3724
3883
|
|
|
3725
|
-
if isinstance(resp, tuple(get_args(TeamRunResponseEvent))):
|
|
3884
|
+
if isinstance(resp, tuple(get_args(TeamRunResponseEvent))) and resp.team_id == self.team_id:
|
|
3726
3885
|
if resp.event == TeamRunEvent.run_response_content:
|
|
3727
3886
|
if isinstance(resp.content, str):
|
|
3728
3887
|
_response_content += resp.content
|
|
@@ -3733,6 +3892,8 @@ class Team:
|
|
|
3733
3892
|
log_warning(f"Failed to convert response to JSON: {e}")
|
|
3734
3893
|
if resp.thinking is not None:
|
|
3735
3894
|
_response_thinking += resp.thinking
|
|
3895
|
+
if hasattr(resp, "reasoning_content") and resp.reasoning_content is not None:
|
|
3896
|
+
_response_reasoning_content += resp.reasoning_content
|
|
3736
3897
|
if (
|
|
3737
3898
|
hasattr(resp, "extra_data")
|
|
3738
3899
|
and resp.extra_data is not None
|
|
@@ -3753,7 +3914,12 @@ class Team:
|
|
|
3753
3914
|
team_tool_calls.append(tool)
|
|
3754
3915
|
|
|
3755
3916
|
# Collect member tool calls, avoiding duplicates
|
|
3756
|
-
if
|
|
3917
|
+
if (
|
|
3918
|
+
self.show_tool_calls
|
|
3919
|
+
and hasattr(resp, "member_responses")
|
|
3920
|
+
and resp.member_responses
|
|
3921
|
+
and self.show_members_responses
|
|
3922
|
+
):
|
|
3757
3923
|
for member_response in resp.member_responses:
|
|
3758
3924
|
member_id = None
|
|
3759
3925
|
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
@@ -3817,6 +3983,18 @@ class Team:
|
|
|
3817
3983
|
if render:
|
|
3818
3984
|
live_console.update(Group(*panels))
|
|
3819
3985
|
|
|
3986
|
+
if len(_response_reasoning_content) > 0:
|
|
3987
|
+
render = True
|
|
3988
|
+
# Create panel for reasoning content
|
|
3989
|
+
reasoning_panel = create_panel(
|
|
3990
|
+
content=Text(_response_reasoning_content),
|
|
3991
|
+
title=f"Reasoning ({response_timer.elapsed:.1f}s)",
|
|
3992
|
+
border_style="green",
|
|
3993
|
+
)
|
|
3994
|
+
panels.append(reasoning_panel)
|
|
3995
|
+
if render:
|
|
3996
|
+
live_console.update(Group(*panels))
|
|
3997
|
+
|
|
3820
3998
|
# Add tool calls panel if available
|
|
3821
3999
|
if self.show_tool_calls and resp is not None and self.run_response.formatted_tool_calls:
|
|
3822
4000
|
render = True
|
|
@@ -3929,95 +4107,98 @@ class Team:
|
|
|
3929
4107
|
final_panels.append(thinking_panel)
|
|
3930
4108
|
|
|
3931
4109
|
# Add member tool calls and responses in correct order
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
if member_id:
|
|
3940
|
-
# First add tool calls if any
|
|
3941
|
-
if self.show_tool_calls and member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
3942
|
-
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
3943
|
-
if formatted_calls:
|
|
3944
|
-
console_width = console.width if console else 80
|
|
3945
|
-
panel_width = console_width + 30
|
|
3946
|
-
|
|
3947
|
-
lines = []
|
|
3948
|
-
# Create a set to track already added calls by their string representation
|
|
3949
|
-
added_calls = set()
|
|
3950
|
-
for call in formatted_calls:
|
|
3951
|
-
if call not in added_calls:
|
|
3952
|
-
added_calls.add(call)
|
|
3953
|
-
# Wrap the call text to fit within the panel
|
|
3954
|
-
wrapped_call = textwrap.fill(f"• {call}", width=panel_width, subsequent_indent=" ")
|
|
3955
|
-
lines.append(wrapped_call)
|
|
4110
|
+
if self.show_members_responses:
|
|
4111
|
+
for i, member_response in enumerate(self.run_response.member_responses if self.run_response else []):
|
|
4112
|
+
member_id = None
|
|
4113
|
+
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
4114
|
+
member_id = member_response.agent_id
|
|
4115
|
+
elif isinstance(member_response, TeamRunResponse) and member_response.team_id is not None:
|
|
4116
|
+
member_id = member_response.team_id
|
|
3956
4117
|
|
|
3957
|
-
|
|
4118
|
+
if member_id:
|
|
4119
|
+
# First add tool calls if any
|
|
4120
|
+
if self.show_tool_calls and member_id in member_tool_calls and member_tool_calls[member_id]:
|
|
4121
|
+
formatted_calls = format_tool_calls(member_tool_calls[member_id])
|
|
4122
|
+
if formatted_calls:
|
|
4123
|
+
console_width = console.width if console else 80
|
|
4124
|
+
panel_width = console_width + 30
|
|
4125
|
+
|
|
4126
|
+
lines = []
|
|
4127
|
+
# Create a set to track already added calls by their string representation
|
|
4128
|
+
added_calls = set()
|
|
4129
|
+
for call in formatted_calls:
|
|
4130
|
+
if call not in added_calls:
|
|
4131
|
+
added_calls.add(call)
|
|
4132
|
+
# Wrap the call text to fit within the panel
|
|
4133
|
+
wrapped_call = textwrap.fill(
|
|
4134
|
+
f"• {call}", width=panel_width, subsequent_indent=" "
|
|
4135
|
+
)
|
|
4136
|
+
lines.append(wrapped_call)
|
|
3958
4137
|
|
|
3959
|
-
|
|
3960
|
-
member_tool_calls_panel = create_panel(
|
|
3961
|
-
content=tool_calls_text,
|
|
3962
|
-
title=f"{member_name} Tool Calls",
|
|
3963
|
-
border_style="yellow",
|
|
3964
|
-
)
|
|
3965
|
-
final_panels.append(member_tool_calls_panel)
|
|
4138
|
+
tool_calls_text = "\n\n".join(lines)
|
|
3966
4139
|
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
if reasoning_steps and show_reasoning:
|
|
3975
|
-
for j, step in enumerate(reasoning_steps, 1):
|
|
3976
|
-
member_reasoning_panel = self._build_reasoning_step_panel(
|
|
3977
|
-
j, step, show_full_reasoning, color="magenta"
|
|
3978
|
-
)
|
|
3979
|
-
final_panels.append(member_reasoning_panel)
|
|
4140
|
+
member_name = self._get_member_name(member_id)
|
|
4141
|
+
member_tool_calls_panel = create_panel(
|
|
4142
|
+
content=tool_calls_text,
|
|
4143
|
+
title=f"{member_name} Tool Calls",
|
|
4144
|
+
border_style="yellow",
|
|
4145
|
+
)
|
|
4146
|
+
final_panels.append(member_tool_calls_panel)
|
|
3980
4147
|
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
4148
|
+
# Add reasoning steps if any
|
|
4149
|
+
reasoning_steps = []
|
|
4150
|
+
if (
|
|
4151
|
+
member_response.extra_data is not None
|
|
4152
|
+
and member_response.extra_data.reasoning_steps is not None
|
|
4153
|
+
):
|
|
4154
|
+
reasoning_steps = member_response.extra_data.reasoning_steps
|
|
4155
|
+
if reasoning_steps and show_reasoning:
|
|
4156
|
+
for j, step in enumerate(reasoning_steps, 1):
|
|
4157
|
+
member_reasoning_panel = self._build_reasoning_step_panel(
|
|
4158
|
+
j, step, show_full_reasoning, color="magenta"
|
|
4159
|
+
)
|
|
4160
|
+
final_panels.append(member_reasoning_panel)
|
|
3987
4161
|
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
4162
|
+
# Then add response
|
|
4163
|
+
show_markdown = False
|
|
4164
|
+
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
4165
|
+
show_markdown = member_markdown.get(member_response.agent_id, False)
|
|
4166
|
+
elif isinstance(member_response, TeamRunResponse) and member_response.team_id is not None:
|
|
4167
|
+
show_markdown = member_markdown.get(member_response.team_id, False)
|
|
3993
4168
|
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
4169
|
+
member_response_content = self._parse_response_content(
|
|
4170
|
+
member_response,
|
|
4171
|
+
tags_to_include_in_markdown,
|
|
4172
|
+
show_markdown=show_markdown,
|
|
4173
|
+
)
|
|
3999
4174
|
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
4011
|
-
for i, citation in enumerate(member_response.citations.urls)
|
|
4012
|
-
if citation.url # Only include citations with valid URLs
|
|
4175
|
+
member_name = "Team Member"
|
|
4176
|
+
if isinstance(member_response, RunResponse) and member_response.agent_id is not None:
|
|
4177
|
+
member_name = self._get_member_name(member_response.agent_id)
|
|
4178
|
+
elif isinstance(member_response, TeamRunResponse) and member_response.team_id is not None:
|
|
4179
|
+
member_name = self._get_member_name(member_response.team_id)
|
|
4180
|
+
|
|
4181
|
+
member_response_panel = create_panel(
|
|
4182
|
+
content=member_response_content,
|
|
4183
|
+
title=f"{member_name} Response",
|
|
4184
|
+
border_style="magenta",
|
|
4013
4185
|
)
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4186
|
+
final_panels.append(member_response_panel)
|
|
4187
|
+
|
|
4188
|
+
# Add citations if any
|
|
4189
|
+
if member_response.citations is not None and member_response.citations.urls is not None:
|
|
4190
|
+
md_content = "\n".join(
|
|
4191
|
+
f"{i + 1}. [{citation.title or citation.url}]({citation.url})"
|
|
4192
|
+
for i, citation in enumerate(member_response.citations.urls)
|
|
4193
|
+
if citation.url # Only include citations with valid URLs
|
|
4019
4194
|
)
|
|
4020
|
-
|
|
4195
|
+
if md_content: # Only create panel if there are citations
|
|
4196
|
+
citations_panel = create_panel(
|
|
4197
|
+
content=Markdown(md_content),
|
|
4198
|
+
title="Citations",
|
|
4199
|
+
border_style="magenta",
|
|
4200
|
+
)
|
|
4201
|
+
final_panels.append(citations_panel)
|
|
4021
4202
|
|
|
4022
4203
|
# Add team tool calls before team response
|
|
4023
4204
|
if self.show_tool_calls and team_tool_calls:
|
|
@@ -5508,6 +5689,13 @@ class Team:
|
|
|
5508
5689
|
return Message.model_validate(message)
|
|
5509
5690
|
except Exception as e:
|
|
5510
5691
|
log_warning(f"Failed to validate message: {e}")
|
|
5692
|
+
elif isinstance(message, BaseModel):
|
|
5693
|
+
try:
|
|
5694
|
+
# Create a user message with the BaseModel content
|
|
5695
|
+
content = message.model_dump_json(indent=2, exclude_none=True)
|
|
5696
|
+
return Message(role="user", content=content)
|
|
5697
|
+
except Exception as e:
|
|
5698
|
+
log_warning(f"Failed to convert BaseModel to message: {e}")
|
|
5511
5699
|
|
|
5512
5700
|
def get_messages_for_parser_model(
|
|
5513
5701
|
self, model_response: ModelResponse, response_format: Optional[Union[Dict, Type[BaseModel]]]
|
|
@@ -5549,6 +5737,24 @@ class Team:
|
|
|
5549
5737
|
Message(role="user", content=run_response.content),
|
|
5550
5738
|
]
|
|
5551
5739
|
|
|
5740
|
+
def get_messages_for_output_model(self, messages: List[Message]) -> List[Message]:
|
|
5741
|
+
"""Get the messages for the output model."""
|
|
5742
|
+
|
|
5743
|
+
if self.output_model_prompt is not None:
|
|
5744
|
+
system_message_exists = False
|
|
5745
|
+
for message in messages:
|
|
5746
|
+
if message.role == "system":
|
|
5747
|
+
system_message_exists = True
|
|
5748
|
+
message.content = self.output_model_prompt
|
|
5749
|
+
break
|
|
5750
|
+
if not system_message_exists:
|
|
5751
|
+
messages.insert(0, Message(role="system", content=self.output_model_prompt))
|
|
5752
|
+
|
|
5753
|
+
# Remove the last assistant message from the messages list
|
|
5754
|
+
messages.pop(-1)
|
|
5755
|
+
|
|
5756
|
+
return messages
|
|
5757
|
+
|
|
5552
5758
|
def _format_message_with_state_variables(self, message: Any, user_id: Optional[str] = None) -> Any:
|
|
5553
5759
|
"""Format a message with the session state variables."""
|
|
5554
5760
|
import re
|
|
@@ -6920,9 +7126,10 @@ class Team:
|
|
|
6920
7126
|
# If the session_state is already set, merge the session_state from the database with the current session_state
|
|
6921
7127
|
if self.session_state is not None and len(self.session_state) > 0:
|
|
6922
7128
|
# This updates session_state_from_db
|
|
6923
|
-
merge_dictionaries(
|
|
6924
|
-
|
|
6925
|
-
|
|
7129
|
+
merge_dictionaries(self.session_state, session_state_from_db)
|
|
7130
|
+
else:
|
|
7131
|
+
# Update the current session_state
|
|
7132
|
+
self.session_state = session_state_from_db
|
|
6926
7133
|
|
|
6927
7134
|
if "team_session_state" in session.session_data:
|
|
6928
7135
|
team_session_state_from_db = session.session_data.get("team_session_state")
|