agno 2.3.13__py3-none-any.whl → 2.3.14__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 +1131 -1402
- agno/eval/__init__.py +21 -8
- agno/knowledge/embedder/azure_openai.py +0 -1
- agno/knowledge/embedder/google.py +1 -1
- agno/models/anthropic/claude.py +4 -1
- agno/models/base.py +8 -4
- agno/models/openai/responses.py +2 -2
- agno/os/app.py +39 -0
- agno/os/interfaces/a2a/router.py +619 -9
- agno/os/interfaces/a2a/utils.py +31 -32
- agno/os/middleware/jwt.py +5 -5
- agno/os/routers/agents/schema.py +14 -1
- agno/os/routers/teams/schema.py +14 -1
- agno/os/utils.py +61 -53
- agno/reasoning/anthropic.py +85 -1
- agno/reasoning/azure_ai_foundry.py +93 -1
- agno/reasoning/deepseek.py +91 -1
- agno/reasoning/gemini.py +81 -1
- agno/reasoning/groq.py +103 -1
- agno/reasoning/manager.py +1244 -0
- agno/reasoning/ollama.py +93 -1
- agno/reasoning/openai.py +113 -1
- agno/reasoning/vertexai.py +85 -1
- agno/run/agent.py +11 -0
- agno/run/base.py +1 -1
- agno/run/team.py +11 -0
- agno/session/team.py +0 -3
- agno/team/team.py +1201 -1445
- agno/utils/events.py +69 -2
- agno/utils/hooks.py +4 -10
- agno/utils/print_response/agent.py +26 -0
- agno/utils/print_response/team.py +11 -0
- agno/utils/prompts.py +8 -6
- agno/utils/string.py +46 -0
- agno/utils/team.py +1 -1
- agno/vectordb/milvus/milvus.py +32 -3
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/METADATA +3 -2
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/RECORD +41 -40
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/WHEEL +0 -0
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.13.dist-info → agno-2.3.14.dist-info}/top_level.txt +0 -0
agno/team/team.py
CHANGED
|
@@ -11,7 +11,6 @@ from dataclasses import dataclass
|
|
|
11
11
|
from os import getenv
|
|
12
12
|
from textwrap import dedent
|
|
13
13
|
from typing import (
|
|
14
|
-
TYPE_CHECKING,
|
|
15
14
|
Any,
|
|
16
15
|
AsyncIterator,
|
|
17
16
|
Callable,
|
|
@@ -33,12 +32,10 @@ from uuid import uuid4
|
|
|
33
32
|
|
|
34
33
|
from pydantic import BaseModel
|
|
35
34
|
|
|
36
|
-
if TYPE_CHECKING:
|
|
37
|
-
from agno.eval.base import BaseEval
|
|
38
|
-
|
|
39
35
|
from agno.agent import Agent
|
|
40
36
|
from agno.compression.manager import CompressionManager
|
|
41
37
|
from agno.db.base import AsyncBaseDb, BaseDb, SessionType, UserMemory
|
|
38
|
+
from agno.eval.base import BaseEval
|
|
42
39
|
from agno.exceptions import (
|
|
43
40
|
InputCheckError,
|
|
44
41
|
OutputCheckError,
|
|
@@ -67,7 +64,12 @@ from agno.run.cancel import (
|
|
|
67
64
|
register_run,
|
|
68
65
|
)
|
|
69
66
|
from agno.run.messages import RunMessages
|
|
70
|
-
from agno.run.team import
|
|
67
|
+
from agno.run.team import (
|
|
68
|
+
TeamRunEvent,
|
|
69
|
+
TeamRunInput,
|
|
70
|
+
TeamRunOutput,
|
|
71
|
+
TeamRunOutputEvent,
|
|
72
|
+
)
|
|
71
73
|
from agno.session import SessionSummaryManager, TeamSession, WorkflowSession
|
|
72
74
|
from agno.session.summary import SessionSummary
|
|
73
75
|
from agno.tools import Toolkit
|
|
@@ -107,6 +109,7 @@ from agno.utils.agent import (
|
|
|
107
109
|
)
|
|
108
110
|
from agno.utils.common import is_typed_dict, validate_typed_dict
|
|
109
111
|
from agno.utils.events import (
|
|
112
|
+
add_team_error_event,
|
|
110
113
|
create_team_parser_model_response_completed_event,
|
|
111
114
|
create_team_parser_model_response_started_event,
|
|
112
115
|
create_team_post_hook_completed_event,
|
|
@@ -114,11 +117,13 @@ from agno.utils.events import (
|
|
|
114
117
|
create_team_pre_hook_completed_event,
|
|
115
118
|
create_team_pre_hook_started_event,
|
|
116
119
|
create_team_reasoning_completed_event,
|
|
120
|
+
create_team_reasoning_content_delta_event,
|
|
117
121
|
create_team_reasoning_started_event,
|
|
118
122
|
create_team_reasoning_step_event,
|
|
119
123
|
create_team_run_cancelled_event,
|
|
120
124
|
create_team_run_completed_event,
|
|
121
125
|
create_team_run_content_completed_event,
|
|
126
|
+
create_team_run_error_event,
|
|
122
127
|
create_team_run_output_content_event,
|
|
123
128
|
create_team_run_started_event,
|
|
124
129
|
create_team_session_summary_completed_event,
|
|
@@ -161,12 +166,11 @@ from agno.utils.reasoning import (
|
|
|
161
166
|
update_run_output_with_reasoning,
|
|
162
167
|
)
|
|
163
168
|
from agno.utils.response import (
|
|
164
|
-
async_generator_wrapper,
|
|
165
169
|
check_if_run_cancelled,
|
|
166
170
|
generator_wrapper,
|
|
167
171
|
)
|
|
168
172
|
from agno.utils.safe_formatter import SafeFormatter
|
|
169
|
-
from agno.utils.string import generate_id_from_name, parse_response_model_str
|
|
173
|
+
from agno.utils.string import generate_id_from_name, parse_response_dict_str, parse_response_model_str
|
|
170
174
|
from agno.utils.team import (
|
|
171
175
|
add_interaction_to_team_run_context,
|
|
172
176
|
format_member_agent_task,
|
|
@@ -354,17 +358,18 @@ class Team:
|
|
|
354
358
|
|
|
355
359
|
# --- Team Hooks ---
|
|
356
360
|
# Functions called right after team session is loaded, before processing starts
|
|
357
|
-
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
361
|
+
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None
|
|
358
362
|
# Functions called after output is generated but before the response is returned
|
|
359
|
-
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
363
|
+
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None
|
|
360
364
|
# If True, run hooks as FastAPI background tasks (non-blocking). Set by AgentOS.
|
|
361
365
|
_run_hooks_in_background: Optional[bool] = None
|
|
362
366
|
|
|
363
367
|
# --- Structured output ---
|
|
364
368
|
# Input schema for validating input
|
|
365
369
|
input_schema: Optional[Type[BaseModel]] = None
|
|
366
|
-
#
|
|
367
|
-
|
|
370
|
+
# Provide a response model to get the response in the implied format.
|
|
371
|
+
# You can use a Pydantic model or a JSON fitting the provider's expected schema.
|
|
372
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None
|
|
368
373
|
# Provide a secondary model to parse the response from the primary model
|
|
369
374
|
parser_model: Optional[Model] = None
|
|
370
375
|
# Provide a prompt for the parser model
|
|
@@ -525,10 +530,10 @@ class Team:
|
|
|
525
530
|
tool_call_limit: Optional[int] = None,
|
|
526
531
|
tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
|
|
527
532
|
tool_hooks: Optional[List[Callable]] = None,
|
|
528
|
-
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
529
|
-
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail,
|
|
533
|
+
pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None,
|
|
534
|
+
post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, BaseEval]]] = None,
|
|
530
535
|
input_schema: Optional[Type[BaseModel]] = None,
|
|
531
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
536
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
532
537
|
parser_model: Optional[Union[Model, str]] = None,
|
|
533
538
|
parser_model_prompt: Optional[str] = None,
|
|
534
539
|
output_model: Optional[Union[Model, str]] = None,
|
|
@@ -728,7 +733,7 @@ class Team:
|
|
|
728
733
|
self._tool_instructions: Optional[List[str]] = None
|
|
729
734
|
|
|
730
735
|
# True if we should parse a member response model
|
|
731
|
-
self._member_response_model: Optional[Type[BaseModel]] = None
|
|
736
|
+
self._member_response_model: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None
|
|
732
737
|
|
|
733
738
|
self._formatter: Optional[SafeFormatter] = None
|
|
734
739
|
|
|
@@ -1484,7 +1489,6 @@ class Team:
|
|
|
1484
1489
|
**kwargs: Any,
|
|
1485
1490
|
) -> TeamRunOutput:
|
|
1486
1491
|
"""Run the Team and return the response.
|
|
1487
|
-
|
|
1488
1492
|
Steps:
|
|
1489
1493
|
1. Execute pre-hooks
|
|
1490
1494
|
2. Determine tools for model
|
|
@@ -1500,7 +1504,6 @@ class Team:
|
|
|
1500
1504
|
12. Create session summary
|
|
1501
1505
|
13. Cleanup and store (scrub, stop timer, add to session, calculate metrics, save session)
|
|
1502
1506
|
"""
|
|
1503
|
-
|
|
1504
1507
|
# 1. Execute pre-hooks
|
|
1505
1508
|
run_input = cast(TeamRunInput, run_response.input)
|
|
1506
1509
|
self.model = cast(Model, self.model)
|
|
@@ -1580,108 +1583,97 @@ class Team:
|
|
|
1580
1583
|
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1581
1584
|
)
|
|
1582
1585
|
|
|
1583
|
-
|
|
1584
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1585
|
-
|
|
1586
|
-
# 5. Reason about the task if reasoning is enabled
|
|
1587
|
-
self._handle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
1586
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1588
1587
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1588
|
+
# 5. Reason about the task if reasoning is enabled
|
|
1589
|
+
self._handle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
1591
1590
|
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
model_response: ModelResponse = self.model.response(
|
|
1595
|
-
messages=run_messages.messages,
|
|
1596
|
-
response_format=response_format,
|
|
1597
|
-
tools=_tools,
|
|
1598
|
-
tool_choice=self.tool_choice,
|
|
1599
|
-
tool_call_limit=self.tool_call_limit,
|
|
1600
|
-
send_media_to_model=self.send_media_to_model,
|
|
1601
|
-
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
1602
|
-
)
|
|
1591
|
+
# Check for cancellation before model call
|
|
1592
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1603
1593
|
|
|
1604
|
-
|
|
1605
|
-
|
|
1594
|
+
# 6. Get the model response for the team leader
|
|
1595
|
+
self.model = cast(Model, self.model)
|
|
1596
|
+
model_response: ModelResponse = self.model.response(
|
|
1597
|
+
messages=run_messages.messages,
|
|
1598
|
+
response_format=response_format,
|
|
1599
|
+
tools=_tools,
|
|
1600
|
+
tool_choice=self.tool_choice,
|
|
1601
|
+
tool_call_limit=self.tool_call_limit,
|
|
1602
|
+
send_media_to_model=self.send_media_to_model,
|
|
1603
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
1604
|
+
)
|
|
1606
1605
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1606
|
+
# Check for cancellation after model call
|
|
1607
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1609
1608
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1609
|
+
# If an output model is provided, generate output using the output model
|
|
1610
|
+
self._parse_response_with_output_model(model_response, run_messages)
|
|
1612
1611
|
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
model_response=model_response,
|
|
1616
|
-
run_response=run_response,
|
|
1617
|
-
run_messages=run_messages,
|
|
1618
|
-
run_context=run_context,
|
|
1619
|
-
)
|
|
1612
|
+
# If a parser model is provided, structure the response separately
|
|
1613
|
+
self._parse_response_with_parser_model(model_response, run_messages, run_context=run_context)
|
|
1620
1614
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1615
|
+
# 7. Update TeamRunOutput with the model response
|
|
1616
|
+
self._update_run_response(
|
|
1617
|
+
model_response=model_response,
|
|
1618
|
+
run_response=run_response,
|
|
1619
|
+
run_messages=run_messages,
|
|
1620
|
+
run_context=run_context,
|
|
1621
|
+
)
|
|
1624
1622
|
|
|
1625
|
-
|
|
1626
|
-
|
|
1623
|
+
# 8. Store media if enabled
|
|
1624
|
+
if self.store_media:
|
|
1625
|
+
store_media_util(run_response, model_response)
|
|
1627
1626
|
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
iterator = self._execute_post_hooks(
|
|
1631
|
-
hooks=self.post_hooks, # type: ignore
|
|
1632
|
-
run_output=run_response,
|
|
1633
|
-
run_context=run_context,
|
|
1634
|
-
session=session,
|
|
1635
|
-
user_id=user_id,
|
|
1636
|
-
debug_mode=debug_mode,
|
|
1637
|
-
background_tasks=background_tasks,
|
|
1638
|
-
**kwargs,
|
|
1639
|
-
)
|
|
1640
|
-
deque(iterator, maxlen=0)
|
|
1641
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1627
|
+
# 9. Convert response to structured format
|
|
1628
|
+
self._convert_response_to_structured_format(run_response=run_response, run_context=run_context)
|
|
1642
1629
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1630
|
+
# 10. Execute post-hooks after output is generated but before response is returned
|
|
1631
|
+
if self.post_hooks is not None:
|
|
1632
|
+
iterator = self._execute_post_hooks(
|
|
1633
|
+
hooks=self.post_hooks, # type: ignore
|
|
1634
|
+
run_output=run_response,
|
|
1635
|
+
run_context=run_context,
|
|
1636
|
+
session=session,
|
|
1637
|
+
user_id=user_id,
|
|
1638
|
+
debug_mode=debug_mode,
|
|
1639
|
+
background_tasks=background_tasks,
|
|
1640
|
+
**kwargs,
|
|
1641
|
+
)
|
|
1642
|
+
deque(iterator, maxlen=0)
|
|
1643
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1645
1644
|
|
|
1646
|
-
|
|
1645
|
+
# 11. Wait for background memory creation
|
|
1646
|
+
wait_for_open_threads(memory_future=memory_future)
|
|
1647
1647
|
|
|
1648
|
-
|
|
1649
|
-
if self.session_summary_manager is not None:
|
|
1650
|
-
# Upsert the RunOutput to Team Session before creating the session summary
|
|
1651
|
-
session.upsert_run(run_response=run_response)
|
|
1652
|
-
try:
|
|
1653
|
-
self.session_summary_manager.create_session_summary(session=session)
|
|
1654
|
-
except Exception as e:
|
|
1655
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
1648
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1656
1649
|
|
|
1657
|
-
|
|
1650
|
+
# 12. Create session summary
|
|
1651
|
+
if self.session_summary_manager is not None:
|
|
1652
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
1653
|
+
session.upsert_run(run_response=run_response)
|
|
1654
|
+
try:
|
|
1655
|
+
self.session_summary_manager.create_session_summary(session=session)
|
|
1656
|
+
except Exception as e:
|
|
1657
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
1658
1658
|
|
|
1659
|
-
|
|
1660
|
-
run_response.status = RunStatus.completed
|
|
1659
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1661
1660
|
|
|
1662
|
-
|
|
1663
|
-
|
|
1661
|
+
# Set the run status to completed
|
|
1662
|
+
run_response.status = RunStatus.completed
|
|
1664
1663
|
|
|
1665
|
-
|
|
1666
|
-
|
|
1664
|
+
# 13. Cleanup and store the run response
|
|
1665
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1667
1666
|
|
|
1668
|
-
|
|
1667
|
+
# Log Team Telemetry
|
|
1668
|
+
self._log_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
|
|
1669
1669
|
|
|
1670
|
-
|
|
1670
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
1671
1671
|
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
run_response.status = RunStatus.cancelled
|
|
1676
|
-
run_response.content = str(e)
|
|
1672
|
+
# Disconnect tools and clean up run tracking
|
|
1673
|
+
self._disconnect_connectable_tools()
|
|
1674
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
1677
1675
|
|
|
1678
|
-
|
|
1679
|
-
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1680
|
-
return run_response
|
|
1681
|
-
finally:
|
|
1682
|
-
# Always disconnect connectable tools
|
|
1683
|
-
self._disconnect_connectable_tools()
|
|
1684
|
-
cleanup_run(run_response.run_id) # type: ignore
|
|
1676
|
+
return run_response
|
|
1685
1677
|
|
|
1686
1678
|
def _run_stream(
|
|
1687
1679
|
self,
|
|
@@ -1700,7 +1692,6 @@ class Team:
|
|
|
1700
1692
|
**kwargs: Any,
|
|
1701
1693
|
) -> Iterator[Union[TeamRunOutputEvent, RunOutputEvent, TeamRunOutput]]:
|
|
1702
1694
|
"""Run the Team and return the response iterator.
|
|
1703
|
-
|
|
1704
1695
|
Steps:
|
|
1705
1696
|
1. Execute pre-hooks
|
|
1706
1697
|
2. Determine tools for model
|
|
@@ -1794,190 +1785,171 @@ class Team:
|
|
|
1794
1785
|
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1795
1786
|
)
|
|
1796
1787
|
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1788
|
+
# Start the Run by yielding a RunStarted event
|
|
1789
|
+
if stream_events:
|
|
1790
|
+
yield handle_event( # type: ignore
|
|
1791
|
+
create_team_run_started_event(run_response),
|
|
1792
|
+
run_response,
|
|
1793
|
+
events_to_skip=self.events_to_skip,
|
|
1794
|
+
store_events=self.store_events,
|
|
1795
|
+
)
|
|
1796
|
+
|
|
1797
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1798
|
+
|
|
1799
|
+
# 5. Reason about the task if reasoning is enabled
|
|
1800
|
+
yield from self._handle_reasoning_stream(
|
|
1801
|
+
run_response=run_response,
|
|
1802
|
+
run_messages=run_messages,
|
|
1803
|
+
stream_events=stream_events,
|
|
1804
|
+
)
|
|
1806
1805
|
|
|
1807
|
-
|
|
1806
|
+
# Check for cancellation before model processing
|
|
1807
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1808
1808
|
|
|
1809
|
-
|
|
1810
|
-
|
|
1809
|
+
# 6. Get a response from the model
|
|
1810
|
+
if self.output_model is None:
|
|
1811
|
+
for event in self._handle_model_response_stream(
|
|
1812
|
+
session=session,
|
|
1811
1813
|
run_response=run_response,
|
|
1812
1814
|
run_messages=run_messages,
|
|
1815
|
+
tools=_tools,
|
|
1816
|
+
response_format=response_format,
|
|
1813
1817
|
stream_events=stream_events,
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
+
session_state=run_context.session_state,
|
|
1819
|
+
run_context=run_context,
|
|
1820
|
+
):
|
|
1821
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1822
|
+
yield event
|
|
1823
|
+
else:
|
|
1824
|
+
for event in self._handle_model_response_stream(
|
|
1825
|
+
session=session,
|
|
1826
|
+
run_response=run_response,
|
|
1827
|
+
run_messages=run_messages,
|
|
1828
|
+
tools=_tools,
|
|
1829
|
+
response_format=response_format,
|
|
1830
|
+
stream_events=stream_events,
|
|
1831
|
+
session_state=run_context.session_state,
|
|
1832
|
+
run_context=run_context,
|
|
1833
|
+
):
|
|
1834
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1835
|
+
from agno.run.team import IntermediateRunContentEvent, RunContentEvent
|
|
1818
1836
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
response_format=response_format,
|
|
1827
|
-
stream_events=stream_events,
|
|
1828
|
-
session_state=run_context.session_state,
|
|
1829
|
-
run_context=run_context,
|
|
1830
|
-
):
|
|
1831
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1837
|
+
if isinstance(event, RunContentEvent):
|
|
1838
|
+
if stream_events:
|
|
1839
|
+
yield IntermediateRunContentEvent(
|
|
1840
|
+
content=event.content,
|
|
1841
|
+
content_type=event.content_type,
|
|
1842
|
+
)
|
|
1843
|
+
else:
|
|
1832
1844
|
yield event
|
|
1833
|
-
else:
|
|
1834
|
-
for event in self._handle_model_response_stream(
|
|
1835
|
-
session=session,
|
|
1836
|
-
run_response=run_response,
|
|
1837
|
-
run_messages=run_messages,
|
|
1838
|
-
tools=_tools,
|
|
1839
|
-
response_format=response_format,
|
|
1840
|
-
stream_events=stream_events,
|
|
1841
|
-
session_state=run_context.session_state,
|
|
1842
|
-
run_context=run_context,
|
|
1843
|
-
):
|
|
1844
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1845
|
-
from agno.run.team import IntermediateRunContentEvent, RunContentEvent
|
|
1846
1845
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1846
|
+
for event in self._generate_response_with_output_model_stream(
|
|
1847
|
+
session=session,
|
|
1848
|
+
run_response=run_response,
|
|
1849
|
+
run_messages=run_messages,
|
|
1850
|
+
stream_events=stream_events,
|
|
1851
|
+
):
|
|
1852
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1853
|
+
yield event
|
|
1855
1854
|
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
run_response=run_response,
|
|
1859
|
-
run_messages=run_messages,
|
|
1860
|
-
stream_events=stream_events,
|
|
1861
|
-
):
|
|
1862
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1863
|
-
yield event
|
|
1855
|
+
# Check for cancellation after model processing
|
|
1856
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1864
1857
|
|
|
1865
|
-
|
|
1866
|
-
|
|
1858
|
+
# 7. Parse response with parser model if provided
|
|
1859
|
+
yield from self._parse_response_with_parser_model_stream(
|
|
1860
|
+
session=session, run_response=run_response, stream_events=stream_events, run_context=run_context
|
|
1861
|
+
)
|
|
1867
1862
|
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1863
|
+
# Yield RunContentCompletedEvent
|
|
1864
|
+
if stream_events:
|
|
1865
|
+
yield handle_event( # type: ignore
|
|
1866
|
+
create_team_run_content_completed_event(from_run_response=run_response),
|
|
1867
|
+
run_response,
|
|
1868
|
+
events_to_skip=self.events_to_skip,
|
|
1869
|
+
store_events=self.store_events,
|
|
1870
|
+
)
|
|
1871
|
+
# Execute post-hooks after output is generated but before response is returned
|
|
1872
|
+
if self.post_hooks is not None:
|
|
1873
|
+
yield from self._execute_post_hooks(
|
|
1874
|
+
hooks=self.post_hooks, # type: ignore
|
|
1875
|
+
run_output=run_response,
|
|
1876
|
+
run_context=run_context,
|
|
1877
|
+
session=session,
|
|
1878
|
+
user_id=user_id,
|
|
1879
|
+
debug_mode=debug_mode,
|
|
1880
|
+
stream_events=stream_events,
|
|
1881
|
+
background_tasks=background_tasks,
|
|
1882
|
+
**kwargs,
|
|
1871
1883
|
)
|
|
1884
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1885
|
+
|
|
1886
|
+
# 8. Wait for background memory creation
|
|
1887
|
+
yield from wait_for_thread_tasks_stream(
|
|
1888
|
+
run_response=run_response,
|
|
1889
|
+
memory_future=memory_future,
|
|
1890
|
+
stream_events=stream_events,
|
|
1891
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
1892
|
+
store_events=self.store_events,
|
|
1893
|
+
)
|
|
1894
|
+
|
|
1895
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1896
|
+
# 9. Create session summary
|
|
1897
|
+
if self.session_summary_manager is not None:
|
|
1898
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
1899
|
+
session.upsert_run(run_response=run_response)
|
|
1872
1900
|
|
|
1873
|
-
# Yield RunContentCompletedEvent
|
|
1874
1901
|
if stream_events:
|
|
1875
1902
|
yield handle_event( # type: ignore
|
|
1876
|
-
|
|
1903
|
+
create_team_session_summary_started_event(from_run_response=run_response),
|
|
1877
1904
|
run_response,
|
|
1878
1905
|
events_to_skip=self.events_to_skip,
|
|
1879
1906
|
store_events=self.store_events,
|
|
1880
1907
|
)
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1908
|
+
try:
|
|
1909
|
+
self.session_summary_manager.create_session_summary(session=session)
|
|
1910
|
+
except Exception as e:
|
|
1911
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
1912
|
+
if stream_events:
|
|
1913
|
+
yield handle_event( # type: ignore
|
|
1914
|
+
create_team_session_summary_completed_event(
|
|
1915
|
+
from_run_response=run_response, session_summary=session.summary
|
|
1916
|
+
),
|
|
1917
|
+
run_response,
|
|
1918
|
+
events_to_skip=self.events_to_skip,
|
|
1919
|
+
store_events=self.store_events,
|
|
1893
1920
|
)
|
|
1894
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1895
|
-
|
|
1896
|
-
# 8. Wait for background memory creation
|
|
1897
|
-
yield from wait_for_thread_tasks_stream(
|
|
1898
|
-
run_response=run_response,
|
|
1899
|
-
memory_future=memory_future,
|
|
1900
|
-
stream_events=stream_events,
|
|
1901
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
1902
|
-
store_events=self.store_events,
|
|
1903
|
-
)
|
|
1904
|
-
|
|
1905
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1906
|
-
# 9. Create session summary
|
|
1907
|
-
if self.session_summary_manager is not None:
|
|
1908
|
-
# Upsert the RunOutput to Team Session before creating the session summary
|
|
1909
|
-
session.upsert_run(run_response=run_response)
|
|
1910
1921
|
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
1922
|
-
if stream_events:
|
|
1923
|
-
yield handle_event( # type: ignore
|
|
1924
|
-
create_team_session_summary_completed_event(
|
|
1925
|
-
from_run_response=run_response, session_summary=session.summary
|
|
1926
|
-
),
|
|
1927
|
-
run_response,
|
|
1928
|
-
events_to_skip=self.events_to_skip,
|
|
1929
|
-
store_events=self.store_events,
|
|
1930
|
-
)
|
|
1931
|
-
|
|
1932
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1933
|
-
# Create the run completed event
|
|
1934
|
-
completed_event = handle_event(
|
|
1935
|
-
create_team_run_completed_event(
|
|
1936
|
-
from_run_response=run_response,
|
|
1937
|
-
),
|
|
1938
|
-
run_response,
|
|
1939
|
-
events_to_skip=self.events_to_skip,
|
|
1940
|
-
store_events=self.store_events,
|
|
1941
|
-
)
|
|
1942
|
-
|
|
1943
|
-
# Set the run status to completed
|
|
1944
|
-
run_response.status = RunStatus.completed
|
|
1945
|
-
|
|
1946
|
-
# 10. Cleanup and store the run response
|
|
1947
|
-
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1922
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1923
|
+
# Create the run completed event
|
|
1924
|
+
completed_event = handle_event(
|
|
1925
|
+
create_team_run_completed_event(
|
|
1926
|
+
from_run_response=run_response,
|
|
1927
|
+
),
|
|
1928
|
+
run_response,
|
|
1929
|
+
events_to_skip=self.events_to_skip,
|
|
1930
|
+
store_events=self.store_events,
|
|
1931
|
+
)
|
|
1948
1932
|
|
|
1949
|
-
|
|
1950
|
-
|
|
1933
|
+
# Set the run status to completed
|
|
1934
|
+
run_response.status = RunStatus.completed
|
|
1951
1935
|
|
|
1952
|
-
|
|
1953
|
-
|
|
1936
|
+
# 10. Cleanup and store the run response
|
|
1937
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1954
1938
|
|
|
1955
|
-
|
|
1956
|
-
|
|
1939
|
+
if stream_events:
|
|
1940
|
+
yield completed_event
|
|
1957
1941
|
|
|
1958
|
-
|
|
1942
|
+
if yield_run_output:
|
|
1943
|
+
yield run_response
|
|
1959
1944
|
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
log_info(f"Team run {run_response.run_id} was cancelled during streaming")
|
|
1963
|
-
run_response.status = RunStatus.cancelled
|
|
1964
|
-
run_response.content = str(e)
|
|
1945
|
+
# Log Team Telemetry
|
|
1946
|
+
self._log_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
|
|
1965
1947
|
|
|
1966
|
-
|
|
1967
|
-
yield handle_event( # type: ignore
|
|
1968
|
-
create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
1969
|
-
run_response,
|
|
1970
|
-
events_to_skip=self.events_to_skip,
|
|
1971
|
-
store_events=self.store_events,
|
|
1972
|
-
)
|
|
1948
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
1973
1949
|
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
# Always disconnect connectable tools
|
|
1978
|
-
self._disconnect_connectable_tools()
|
|
1979
|
-
# Always clean up the run tracking
|
|
1980
|
-
cleanup_run(run_response.run_id) # type: ignore
|
|
1950
|
+
# Disconnect tools and clean up run tracking
|
|
1951
|
+
self._disconnect_connectable_tools()
|
|
1952
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
1981
1953
|
|
|
1982
1954
|
@overload
|
|
1983
1955
|
def run(
|
|
@@ -2002,7 +1974,7 @@ class Team:
|
|
|
2002
1974
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2003
1975
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2004
1976
|
debug_mode: Optional[bool] = None,
|
|
2005
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
1977
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2006
1978
|
**kwargs: Any,
|
|
2007
1979
|
) -> TeamRunOutput: ...
|
|
2008
1980
|
|
|
@@ -2032,7 +2004,7 @@ class Team:
|
|
|
2032
2004
|
debug_mode: Optional[bool] = None,
|
|
2033
2005
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2034
2006
|
yield_run_output: bool = False,
|
|
2035
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2007
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2036
2008
|
**kwargs: Any,
|
|
2037
2009
|
) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent]]: ...
|
|
2038
2010
|
|
|
@@ -2061,16 +2033,15 @@ class Team:
|
|
|
2061
2033
|
debug_mode: Optional[bool] = None,
|
|
2062
2034
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2063
2035
|
yield_run_output: bool = False,
|
|
2064
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
2036
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2065
2037
|
**kwargs: Any,
|
|
2066
2038
|
) -> Union[TeamRunOutput, Iterator[Union[RunOutputEvent, TeamRunOutputEvent]]]:
|
|
2067
2039
|
"""Run the Team and return the response."""
|
|
2068
2040
|
if self._has_async_db():
|
|
2069
2041
|
raise Exception("run() is not supported with an async DB. Please use arun() instead.")
|
|
2070
2042
|
|
|
2071
|
-
# Set the id for the run
|
|
2043
|
+
# Set the id for the run
|
|
2072
2044
|
run_id = run_id or str(uuid4())
|
|
2073
|
-
register_run(run_id)
|
|
2074
2045
|
|
|
2075
2046
|
# Initialize Team
|
|
2076
2047
|
self.initialize_team(debug_mode=debug_mode)
|
|
@@ -2088,148 +2059,155 @@ class Team:
|
|
|
2088
2059
|
)
|
|
2089
2060
|
yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
|
|
2090
2061
|
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
# Validate input against input_schema if provided
|
|
2098
|
-
validated_input = self._validate_input(input)
|
|
2062
|
+
# Set up retry logic
|
|
2063
|
+
num_attempts = self.retries + 1
|
|
2064
|
+
for attempt in range(num_attempts):
|
|
2065
|
+
if num_attempts > 1:
|
|
2066
|
+
log_debug(f"Retrying Team run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2099
2067
|
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
|
|
2104
|
-
if self.post_hooks:
|
|
2105
|
-
self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
|
|
2106
|
-
self._hooks_normalised = True
|
|
2068
|
+
try:
|
|
2069
|
+
# Register run for cancellation tracking
|
|
2070
|
+
register_run(run_id) # type: ignore
|
|
2107
2071
|
|
|
2108
|
-
|
|
2072
|
+
background_tasks = kwargs.pop("background_tasks", None)
|
|
2073
|
+
if background_tasks is not None:
|
|
2074
|
+
from fastapi import BackgroundTasks
|
|
2109
2075
|
|
|
2110
|
-
|
|
2111
|
-
images=images, videos=videos, audios=audio, files=files
|
|
2112
|
-
)
|
|
2076
|
+
background_tasks: BackgroundTasks = background_tasks # type: ignore
|
|
2113
2077
|
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
input_content=validated_input,
|
|
2117
|
-
images=image_artifacts,
|
|
2118
|
-
videos=video_artifacts,
|
|
2119
|
-
audios=audio_artifacts,
|
|
2120
|
-
files=file_artifacts,
|
|
2121
|
-
)
|
|
2078
|
+
# Validate input against input_schema if provided
|
|
2079
|
+
validated_input = self._validate_input(input)
|
|
2122
2080
|
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2081
|
+
# Normalise hook & guardails
|
|
2082
|
+
if not self._hooks_normalised:
|
|
2083
|
+
if self.pre_hooks:
|
|
2084
|
+
self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
|
|
2085
|
+
if self.post_hooks:
|
|
2086
|
+
self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
|
|
2087
|
+
self._hooks_normalised = True
|
|
2126
2088
|
|
|
2127
|
-
|
|
2128
|
-
session_state = self._initialize_session_state(
|
|
2129
|
-
session_state=session_state if session_state is not None else {},
|
|
2130
|
-
user_id=user_id,
|
|
2131
|
-
session_id=session_id,
|
|
2132
|
-
run_id=run_id,
|
|
2133
|
-
)
|
|
2134
|
-
# Update session state from DB
|
|
2135
|
-
session_state = self._load_session_state(session=team_session, session_state=session_state)
|
|
2089
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
2136
2090
|
|
|
2137
|
-
|
|
2138
|
-
|
|
2091
|
+
image_artifacts, video_artifacts, audio_artifacts, file_artifacts = validate_media_object_id(
|
|
2092
|
+
images=images, videos=videos, audios=audio, files=files
|
|
2093
|
+
)
|
|
2139
2094
|
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2095
|
+
# Create RunInput to capture the original user input
|
|
2096
|
+
run_input = TeamRunInput(
|
|
2097
|
+
input_content=validated_input,
|
|
2098
|
+
images=image_artifacts,
|
|
2099
|
+
videos=video_artifacts,
|
|
2100
|
+
audios=audio_artifacts,
|
|
2101
|
+
files=file_artifacts,
|
|
2102
|
+
)
|
|
2143
2103
|
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
session_id=session_id,
|
|
2148
|
-
user_id=user_id,
|
|
2149
|
-
session_state=session_state,
|
|
2150
|
-
dependencies=dependencies,
|
|
2151
|
-
output_schema=output_schema,
|
|
2152
|
-
)
|
|
2153
|
-
# output_schema parameter takes priority, even if run_context was provided
|
|
2154
|
-
run_context.output_schema = output_schema
|
|
2104
|
+
# Read existing session from database
|
|
2105
|
+
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2106
|
+
self._update_metadata(session=team_session)
|
|
2155
2107
|
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2108
|
+
# Initialize session state
|
|
2109
|
+
session_state = self._initialize_session_state(
|
|
2110
|
+
session_state=session_state if session_state is not None else {},
|
|
2111
|
+
user_id=user_id,
|
|
2112
|
+
session_id=session_id,
|
|
2113
|
+
run_id=run_id,
|
|
2114
|
+
)
|
|
2115
|
+
# Update session state from DB
|
|
2116
|
+
session_state = self._load_session_state(session=team_session, session_state=session_state)
|
|
2159
2117
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
add_dependencies_to_context if add_dependencies_to_context is not None else self.add_dependencies_to_context
|
|
2163
|
-
)
|
|
2164
|
-
add_session_state = (
|
|
2165
|
-
add_session_state_to_context
|
|
2166
|
-
if add_session_state_to_context is not None
|
|
2167
|
-
else self.add_session_state_to_context
|
|
2168
|
-
)
|
|
2169
|
-
add_history = add_history_to_context if add_history_to_context is not None else self.add_history_to_context
|
|
2118
|
+
# Determine runtime dependencies
|
|
2119
|
+
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
2170
2120
|
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2121
|
+
# Resolve output_schema parameter takes precedence, then fall back to self.output_schema
|
|
2122
|
+
if output_schema is None:
|
|
2123
|
+
output_schema = self.output_schema
|
|
2174
2124
|
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2125
|
+
# Initialize run context
|
|
2126
|
+
run_context = run_context or RunContext(
|
|
2127
|
+
run_id=run_id,
|
|
2128
|
+
session_id=session_id,
|
|
2129
|
+
user_id=user_id,
|
|
2130
|
+
session_state=session_state,
|
|
2131
|
+
dependencies=dependencies,
|
|
2132
|
+
output_schema=output_schema,
|
|
2133
|
+
)
|
|
2134
|
+
# output_schema parameter takes priority, even if run_context was provided
|
|
2135
|
+
run_context.output_schema = output_schema
|
|
2136
|
+
|
|
2137
|
+
# Resolve callable dependencies if present
|
|
2138
|
+
if run_context.dependencies is not None:
|
|
2139
|
+
self._resolve_run_dependencies(run_context=run_context)
|
|
2140
|
+
|
|
2141
|
+
# Determine runtime context parameters
|
|
2142
|
+
add_dependencies = (
|
|
2143
|
+
add_dependencies_to_context
|
|
2144
|
+
if add_dependencies_to_context is not None
|
|
2145
|
+
else self.add_dependencies_to_context
|
|
2146
|
+
)
|
|
2147
|
+
add_session_state = (
|
|
2148
|
+
add_session_state_to_context
|
|
2149
|
+
if add_session_state_to_context is not None
|
|
2150
|
+
else self.add_session_state_to_context
|
|
2151
|
+
)
|
|
2152
|
+
add_history = (
|
|
2153
|
+
add_history_to_context if add_history_to_context is not None else self.add_history_to_context
|
|
2154
|
+
)
|
|
2178
2155
|
|
|
2179
|
-
|
|
2180
|
-
|
|
2156
|
+
# When filters are passed manually
|
|
2157
|
+
if self.knowledge_filters or knowledge_filters:
|
|
2158
|
+
run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
|
|
2181
2159
|
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2160
|
+
# Use stream override value when necessary
|
|
2161
|
+
if stream is None:
|
|
2162
|
+
stream = False if self.stream is None else self.stream
|
|
2185
2163
|
|
|
2186
|
-
|
|
2187
|
-
|
|
2164
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
2165
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
2188
2166
|
|
|
2189
|
-
|
|
2167
|
+
# Can't stream events if streaming is disabled
|
|
2168
|
+
if stream is False:
|
|
2169
|
+
stream_events = False
|
|
2190
2170
|
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
metadata = self.metadata
|
|
2194
|
-
else:
|
|
2195
|
-
merge_dictionaries(metadata, self.metadata)
|
|
2171
|
+
if stream_events is None:
|
|
2172
|
+
stream_events = False if self.stream_events is None else self.stream_events
|
|
2196
2173
|
|
|
2197
|
-
|
|
2198
|
-
run_context.metadata = metadata
|
|
2174
|
+
self.model = cast(Model, self.model)
|
|
2199
2175
|
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2176
|
+
if self.metadata is not None:
|
|
2177
|
+
if metadata is None:
|
|
2178
|
+
metadata = self.metadata
|
|
2179
|
+
else:
|
|
2180
|
+
merge_dictionaries(metadata, self.metadata)
|
|
2204
2181
|
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
run_id=run_id,
|
|
2208
|
-
session_id=session_id,
|
|
2209
|
-
user_id=user_id,
|
|
2210
|
-
team_id=self.id,
|
|
2211
|
-
team_name=self.name,
|
|
2212
|
-
metadata=run_context.metadata,
|
|
2213
|
-
session_state=run_context.session_state,
|
|
2214
|
-
input=run_input,
|
|
2215
|
-
)
|
|
2182
|
+
if metadata:
|
|
2183
|
+
run_context.metadata = metadata
|
|
2216
2184
|
|
|
2217
|
-
|
|
2218
|
-
|
|
2185
|
+
# Configure the model for runs
|
|
2186
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = (
|
|
2187
|
+
self._get_response_format(run_context=run_context) if self.parser_model is None else None
|
|
2188
|
+
)
|
|
2219
2189
|
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2190
|
+
# Create a new run_response for this attempt
|
|
2191
|
+
run_response = TeamRunOutput(
|
|
2192
|
+
run_id=run_id,
|
|
2193
|
+
session_id=session_id,
|
|
2194
|
+
user_id=user_id,
|
|
2195
|
+
team_id=self.id,
|
|
2196
|
+
team_name=self.name,
|
|
2197
|
+
metadata=run_context.metadata,
|
|
2198
|
+
session_state=run_context.session_state,
|
|
2199
|
+
input=run_input,
|
|
2200
|
+
)
|
|
2223
2201
|
|
|
2224
|
-
|
|
2225
|
-
|
|
2202
|
+
run_response.model = self.model.id if self.model is not None else None
|
|
2203
|
+
run_response.model_provider = self.model.provider if self.model is not None else None
|
|
2204
|
+
|
|
2205
|
+
# Start the run metrics timer, to calculate the run duration
|
|
2206
|
+
run_response.metrics = Metrics()
|
|
2207
|
+
run_response.metrics.start_timer()
|
|
2226
2208
|
|
|
2227
|
-
for attempt in range(num_attempts):
|
|
2228
|
-
log_debug(f"Retrying Team run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2229
|
-
# Run the team
|
|
2230
|
-
try:
|
|
2231
2209
|
if stream:
|
|
2232
|
-
|
|
2210
|
+
return self._run_stream(
|
|
2233
2211
|
run_response=run_response,
|
|
2234
2212
|
run_context=run_context,
|
|
2235
2213
|
session=team_session,
|
|
@@ -2243,9 +2221,8 @@ class Team:
|
|
|
2243
2221
|
debug_mode=debug_mode,
|
|
2244
2222
|
background_tasks=background_tasks,
|
|
2245
2223
|
**kwargs,
|
|
2246
|
-
)
|
|
2224
|
+
) # type: ignore
|
|
2247
2225
|
|
|
2248
|
-
return response_iterator # type: ignore
|
|
2249
2226
|
else:
|
|
2250
2227
|
return self._run(
|
|
2251
2228
|
run_response=run_response,
|
|
@@ -2260,24 +2237,67 @@ class Team:
|
|
|
2260
2237
|
background_tasks=background_tasks,
|
|
2261
2238
|
**kwargs,
|
|
2262
2239
|
)
|
|
2240
|
+
except InputCheckError as e:
|
|
2241
|
+
run_response.status = RunStatus.error
|
|
2242
|
+
if stream:
|
|
2243
|
+
run_error = create_team_run_error_event(
|
|
2244
|
+
run_response,
|
|
2245
|
+
error=str(e),
|
|
2246
|
+
error_id=e.error_id,
|
|
2247
|
+
error_type=e.type,
|
|
2248
|
+
additional_data=e.additional_data,
|
|
2249
|
+
)
|
|
2250
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2251
|
+
if run_response.content is None:
|
|
2252
|
+
run_response.content = str(e)
|
|
2263
2253
|
|
|
2264
|
-
except (InputCheckError, OutputCheckError) as e:
|
|
2265
2254
|
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2255
|
+
|
|
2256
|
+
if stream:
|
|
2257
|
+
return generator_wrapper(run_error) # type: ignore
|
|
2258
|
+
else:
|
|
2259
|
+
return run_response
|
|
2260
|
+
except RunCancelledException as e:
|
|
2261
|
+
# Handle run cancellation during streaming
|
|
2262
|
+
log_info(f"Team run {run_response.run_id} was cancelled during streaming")
|
|
2269
2263
|
run_response.status = RunStatus.cancelled
|
|
2264
|
+
run_response.content = str(e)
|
|
2270
2265
|
|
|
2266
|
+
# Yield the cancellation event
|
|
2271
2267
|
if stream:
|
|
2272
|
-
|
|
2273
|
-
create_team_run_cancelled_event(
|
|
2274
|
-
|
|
2275
|
-
|
|
2268
|
+
cancelled_run_error = handle_event(
|
|
2269
|
+
create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2270
|
+
run_response,
|
|
2271
|
+
events_to_skip=self.events_to_skip,
|
|
2272
|
+
store_events=self.store_events,
|
|
2276
2273
|
)
|
|
2274
|
+
return generator_wrapper(cancelled_run_error) # type: ignore
|
|
2275
|
+
else:
|
|
2276
|
+
return run_response
|
|
2277
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2278
|
+
run_response.status = RunStatus.error
|
|
2279
|
+
|
|
2280
|
+
if stream:
|
|
2281
|
+
# Add error event to list of events
|
|
2282
|
+
run_error = create_team_run_error_event(
|
|
2283
|
+
run_response,
|
|
2284
|
+
error=str(e),
|
|
2285
|
+
error_id=e.error_id,
|
|
2286
|
+
error_type=e.type,
|
|
2287
|
+
additional_data=e.additional_data,
|
|
2288
|
+
)
|
|
2289
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2290
|
+
|
|
2291
|
+
if run_response.content is None:
|
|
2292
|
+
run_response.content = str(e)
|
|
2293
|
+
|
|
2294
|
+
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
2295
|
+
|
|
2296
|
+
if stream:
|
|
2297
|
+
return generator_wrapper(run_error) # type: ignore
|
|
2277
2298
|
else:
|
|
2278
2299
|
return run_response
|
|
2279
2300
|
except Exception as e:
|
|
2280
|
-
# Check if this is the last attempt
|
|
2281
2301
|
if attempt < num_attempts - 1:
|
|
2282
2302
|
# Calculate delay with exponential backoff if enabled
|
|
2283
2303
|
if self.exponential_backoff:
|
|
@@ -2287,12 +2307,23 @@ class Team:
|
|
|
2287
2307
|
|
|
2288
2308
|
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2289
2309
|
time.sleep(delay)
|
|
2310
|
+
continue
|
|
2311
|
+
|
|
2312
|
+
run_response.status = RunStatus.error
|
|
2313
|
+
if stream:
|
|
2314
|
+
run_error = create_team_run_error_event(run_response, error=str(e))
|
|
2315
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2316
|
+
if run_response.content is None:
|
|
2317
|
+
run_response.content = str(e)
|
|
2318
|
+
|
|
2319
|
+
log_error(f"Error in Team run: {str(e)}")
|
|
2320
|
+
|
|
2321
|
+
if stream:
|
|
2322
|
+
return generator_wrapper(run_error) # type: ignore
|
|
2290
2323
|
else:
|
|
2291
|
-
|
|
2292
|
-
log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
|
|
2293
|
-
raise e
|
|
2324
|
+
return run_response
|
|
2294
2325
|
|
|
2295
|
-
# If we get here, all retries failed
|
|
2326
|
+
# If we get here, all retries failed (shouldn't happen with current logic)
|
|
2296
2327
|
raise Exception(f"Failed after {num_attempts} attempts.")
|
|
2297
2328
|
|
|
2298
2329
|
async def _arun(
|
|
@@ -2329,219 +2360,276 @@ class Team:
|
|
|
2329
2360
|
15. Cleanup and store (scrub, add to session, calculate metrics, save session)
|
|
2330
2361
|
"""
|
|
2331
2362
|
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
2363
|
+
memory_task = None
|
|
2332
2364
|
|
|
2333
|
-
|
|
2334
|
-
|
|
2365
|
+
# Set up retry logic
|
|
2366
|
+
num_attempts = self.retries + 1
|
|
2367
|
+
for attempt in range(num_attempts):
|
|
2368
|
+
if num_attempts > 1:
|
|
2369
|
+
log_debug(f"Retrying Team run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2335
2370
|
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
else:
|
|
2340
|
-
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2371
|
+
try:
|
|
2372
|
+
if run_context.dependencies is not None:
|
|
2373
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2341
2374
|
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
user_id=user_id,
|
|
2348
|
-
session_id=session_id,
|
|
2349
|
-
run_id=run_response.run_id,
|
|
2350
|
-
)
|
|
2351
|
-
# Update session state from DB
|
|
2352
|
-
if run_context.session_state is not None:
|
|
2353
|
-
run_context.session_state = self._load_session_state(
|
|
2354
|
-
session=team_session, session_state=run_context.session_state
|
|
2355
|
-
)
|
|
2375
|
+
# 1. Read or create session. Reads from the database if provided.
|
|
2376
|
+
if self._has_async_db():
|
|
2377
|
+
team_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
2378
|
+
else:
|
|
2379
|
+
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2356
2380
|
|
|
2357
|
-
|
|
2381
|
+
# 2. Update metadata and session state
|
|
2382
|
+
self._update_metadata(session=team_session)
|
|
2383
|
+
# Initialize session state
|
|
2384
|
+
run_context.session_state = self._initialize_session_state(
|
|
2385
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2386
|
+
user_id=user_id,
|
|
2387
|
+
session_id=session_id,
|
|
2388
|
+
run_id=run_response.run_id,
|
|
2389
|
+
)
|
|
2390
|
+
# Update session state from DB
|
|
2391
|
+
if run_context.session_state is not None:
|
|
2392
|
+
run_context.session_state = self._load_session_state(
|
|
2393
|
+
session=team_session, session_state=run_context.session_state
|
|
2394
|
+
)
|
|
2358
2395
|
|
|
2359
|
-
|
|
2360
|
-
if self.pre_hooks is not None:
|
|
2361
|
-
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2362
|
-
hooks=self.pre_hooks, # type: ignore
|
|
2363
|
-
run_response=run_response,
|
|
2364
|
-
run_context=run_context,
|
|
2365
|
-
run_input=run_input,
|
|
2366
|
-
session=team_session,
|
|
2367
|
-
user_id=user_id,
|
|
2368
|
-
debug_mode=debug_mode,
|
|
2369
|
-
background_tasks=background_tasks,
|
|
2370
|
-
**kwargs,
|
|
2371
|
-
)
|
|
2396
|
+
run_input = cast(TeamRunInput, run_response.input)
|
|
2372
2397
|
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2398
|
+
# 3. Execute pre-hooks after session is loaded but before processing starts
|
|
2399
|
+
if self.pre_hooks is not None:
|
|
2400
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2401
|
+
hooks=self.pre_hooks, # type: ignore
|
|
2402
|
+
run_response=run_response,
|
|
2403
|
+
run_context=run_context,
|
|
2404
|
+
run_input=run_input,
|
|
2405
|
+
session=team_session,
|
|
2406
|
+
user_id=user_id,
|
|
2407
|
+
debug_mode=debug_mode,
|
|
2408
|
+
background_tasks=background_tasks,
|
|
2409
|
+
**kwargs,
|
|
2410
|
+
)
|
|
2376
2411
|
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
await self._check_and_refresh_mcp_tools()
|
|
2381
|
-
_tools = self._determine_tools_for_model(
|
|
2382
|
-
model=self.model,
|
|
2383
|
-
run_response=run_response,
|
|
2384
|
-
run_context=run_context,
|
|
2385
|
-
team_run_context=team_run_context,
|
|
2386
|
-
session=team_session,
|
|
2387
|
-
user_id=user_id,
|
|
2388
|
-
async_mode=True,
|
|
2389
|
-
input_message=run_input.input_content,
|
|
2390
|
-
images=run_input.images,
|
|
2391
|
-
videos=run_input.videos,
|
|
2392
|
-
audio=run_input.audios,
|
|
2393
|
-
files=run_input.files,
|
|
2394
|
-
debug_mode=debug_mode,
|
|
2395
|
-
add_history_to_context=add_history_to_context,
|
|
2396
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2397
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2398
|
-
stream=False,
|
|
2399
|
-
stream_events=False,
|
|
2400
|
-
)
|
|
2412
|
+
# Consume the async iterator without yielding
|
|
2413
|
+
async for _ in pre_hook_iterator:
|
|
2414
|
+
pass
|
|
2401
2415
|
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2416
|
+
# 4. Determine tools for model
|
|
2417
|
+
team_run_context: Dict[str, Any] = {}
|
|
2418
|
+
self.model = cast(Model, self.model)
|
|
2419
|
+
await self._check_and_refresh_mcp_tools()
|
|
2420
|
+
_tools = self._determine_tools_for_model(
|
|
2421
|
+
model=self.model,
|
|
2422
|
+
run_response=run_response,
|
|
2423
|
+
run_context=run_context,
|
|
2424
|
+
team_run_context=team_run_context,
|
|
2425
|
+
session=team_session,
|
|
2426
|
+
user_id=user_id,
|
|
2427
|
+
async_mode=True,
|
|
2428
|
+
input_message=run_input.input_content,
|
|
2429
|
+
images=run_input.images,
|
|
2430
|
+
videos=run_input.videos,
|
|
2431
|
+
audio=run_input.audios,
|
|
2432
|
+
files=run_input.files,
|
|
2433
|
+
debug_mode=debug_mode,
|
|
2434
|
+
add_history_to_context=add_history_to_context,
|
|
2435
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2436
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2437
|
+
stream=False,
|
|
2438
|
+
stream_events=False,
|
|
2439
|
+
)
|
|
2419
2440
|
|
|
2420
|
-
|
|
2421
|
-
|
|
2441
|
+
# 5. Prepare run messages
|
|
2442
|
+
run_messages = await self._aget_run_messages(
|
|
2443
|
+
run_response=run_response,
|
|
2444
|
+
run_context=run_context,
|
|
2445
|
+
session=team_session, # type: ignore
|
|
2446
|
+
user_id=user_id,
|
|
2447
|
+
input_message=run_input.input_content,
|
|
2448
|
+
audio=run_input.audios,
|
|
2449
|
+
images=run_input.images,
|
|
2450
|
+
videos=run_input.videos,
|
|
2451
|
+
files=run_input.files,
|
|
2452
|
+
add_history_to_context=add_history_to_context,
|
|
2453
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2454
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2455
|
+
tools=_tools,
|
|
2456
|
+
**kwargs,
|
|
2457
|
+
)
|
|
2422
2458
|
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
if (
|
|
2426
|
-
run_messages.user_message is not None
|
|
2427
|
-
and self.memory_manager is not None
|
|
2428
|
-
and self.enable_user_memories
|
|
2429
|
-
and not self.enable_agentic_memory
|
|
2430
|
-
):
|
|
2431
|
-
log_debug("Starting memory creation in background task.")
|
|
2432
|
-
memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2459
|
+
self.model = cast(Model, self.model)
|
|
2460
|
+
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
2433
2461
|
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2462
|
+
# 6. Start memory creation in background task
|
|
2463
|
+
memory_task = None
|
|
2464
|
+
if (
|
|
2465
|
+
run_messages.user_message is not None
|
|
2466
|
+
and self.memory_manager is not None
|
|
2467
|
+
and self.enable_user_memories
|
|
2468
|
+
and not self.enable_agentic_memory
|
|
2469
|
+
):
|
|
2470
|
+
log_debug("Starting memory creation in background task.")
|
|
2471
|
+
memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2438
2472
|
|
|
2439
|
-
|
|
2440
|
-
|
|
2473
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2474
|
+
# 7. Reason about the task if reasoning is enabled
|
|
2475
|
+
await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
2441
2476
|
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
messages=run_messages.messages,
|
|
2445
|
-
tools=_tools,
|
|
2446
|
-
tool_choice=self.tool_choice,
|
|
2447
|
-
tool_call_limit=self.tool_call_limit,
|
|
2448
|
-
response_format=response_format,
|
|
2449
|
-
send_media_to_model=self.send_media_to_model,
|
|
2450
|
-
run_response=run_response,
|
|
2451
|
-
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
2452
|
-
) # type: ignore
|
|
2477
|
+
# Check for cancellation before model call
|
|
2478
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2453
2479
|
|
|
2454
|
-
|
|
2455
|
-
|
|
2480
|
+
# 8. Get the model response for the team leader
|
|
2481
|
+
model_response = await self.model.aresponse(
|
|
2482
|
+
messages=run_messages.messages,
|
|
2483
|
+
tools=_tools,
|
|
2484
|
+
tool_choice=self.tool_choice,
|
|
2485
|
+
tool_call_limit=self.tool_call_limit,
|
|
2486
|
+
response_format=response_format,
|
|
2487
|
+
send_media_to_model=self.send_media_to_model,
|
|
2488
|
+
run_response=run_response,
|
|
2489
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
2490
|
+
) # type: ignore
|
|
2456
2491
|
|
|
2457
|
-
|
|
2458
|
-
|
|
2492
|
+
# Check for cancellation after model call
|
|
2493
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2459
2494
|
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2495
|
+
# If an output model is provided, generate output using the output model
|
|
2496
|
+
await self._agenerate_response_with_output_model(
|
|
2497
|
+
model_response=model_response, run_messages=run_messages
|
|
2498
|
+
)
|
|
2464
2499
|
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
run_messages=run_messages,
|
|
2470
|
-
run_context=run_context,
|
|
2471
|
-
)
|
|
2500
|
+
# If a parser model is provided, structure the response separately
|
|
2501
|
+
await self._aparse_response_with_parser_model(
|
|
2502
|
+
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
2503
|
+
)
|
|
2472
2504
|
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2505
|
+
# 9. Update TeamRunOutput with the model response
|
|
2506
|
+
self._update_run_response(
|
|
2507
|
+
model_response=model_response,
|
|
2508
|
+
run_response=run_response,
|
|
2509
|
+
run_messages=run_messages,
|
|
2510
|
+
run_context=run_context,
|
|
2511
|
+
)
|
|
2476
2512
|
|
|
2477
|
-
|
|
2478
|
-
|
|
2513
|
+
# 10. Store media if enabled
|
|
2514
|
+
if self.store_media:
|
|
2515
|
+
store_media_util(run_response, model_response)
|
|
2479
2516
|
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
async for _ in self._aexecute_post_hooks(
|
|
2483
|
-
hooks=self.post_hooks, # type: ignore
|
|
2484
|
-
run_output=run_response,
|
|
2485
|
-
run_context=run_context,
|
|
2486
|
-
session=team_session,
|
|
2487
|
-
user_id=user_id,
|
|
2488
|
-
debug_mode=debug_mode,
|
|
2489
|
-
background_tasks=background_tasks,
|
|
2490
|
-
**kwargs,
|
|
2491
|
-
):
|
|
2492
|
-
pass
|
|
2517
|
+
# 11. Convert response to structured format
|
|
2518
|
+
self._convert_response_to_structured_format(run_response=run_response, run_context=run_context)
|
|
2493
2519
|
|
|
2494
|
-
|
|
2520
|
+
# 12. Execute post-hooks after output is generated but before response is returned
|
|
2521
|
+
if self.post_hooks is not None:
|
|
2522
|
+
async for _ in self._aexecute_post_hooks(
|
|
2523
|
+
hooks=self.post_hooks, # type: ignore
|
|
2524
|
+
run_output=run_response,
|
|
2525
|
+
run_context=run_context,
|
|
2526
|
+
session=team_session,
|
|
2527
|
+
user_id=user_id,
|
|
2528
|
+
debug_mode=debug_mode,
|
|
2529
|
+
background_tasks=background_tasks,
|
|
2530
|
+
**kwargs,
|
|
2531
|
+
):
|
|
2532
|
+
pass
|
|
2495
2533
|
|
|
2496
|
-
|
|
2497
|
-
await await_for_open_threads(memory_task=memory_task)
|
|
2534
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2498
2535
|
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
if self.session_summary_manager is not None:
|
|
2502
|
-
# Upsert the RunOutput to Team Session before creating the session summary
|
|
2503
|
-
team_session.upsert_run(run_response=run_response)
|
|
2504
|
-
try:
|
|
2505
|
-
await self.session_summary_manager.acreate_session_summary(session=team_session)
|
|
2506
|
-
except Exception as e:
|
|
2507
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2536
|
+
# 13. Wait for background memory creation
|
|
2537
|
+
await await_for_open_threads(memory_task=memory_task)
|
|
2508
2538
|
|
|
2509
|
-
|
|
2510
|
-
|
|
2539
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2540
|
+
# 14. Create session summary
|
|
2541
|
+
if self.session_summary_manager is not None:
|
|
2542
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
2543
|
+
team_session.upsert_run(run_response=run_response)
|
|
2544
|
+
try:
|
|
2545
|
+
await self.session_summary_manager.acreate_session_summary(session=team_session)
|
|
2546
|
+
except Exception as e:
|
|
2547
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2511
2548
|
|
|
2512
|
-
|
|
2513
|
-
|
|
2549
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2550
|
+
run_response.status = RunStatus.completed
|
|
2514
2551
|
|
|
2515
|
-
|
|
2516
|
-
|
|
2552
|
+
# 15. Cleanup and store the run response and session
|
|
2553
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2517
2554
|
|
|
2518
|
-
|
|
2555
|
+
# Log Team Telemetry
|
|
2556
|
+
await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
|
|
2519
2557
|
|
|
2520
|
-
|
|
2521
|
-
except RunCancelledException as e:
|
|
2522
|
-
# Handle run cancellation
|
|
2523
|
-
log_info(f"Run {run_response.run_id} was cancelled")
|
|
2524
|
-
run_response.content = str(e)
|
|
2525
|
-
run_response.status = RunStatus.cancelled
|
|
2558
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2526
2559
|
|
|
2527
|
-
|
|
2528
|
-
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2560
|
+
return run_response
|
|
2529
2561
|
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2562
|
+
except RunCancelledException as e:
|
|
2563
|
+
# Handle run cancellation
|
|
2564
|
+
log_info(f"Run {run_response.run_id} was cancelled")
|
|
2565
|
+
run_response.content = str(e)
|
|
2566
|
+
run_response.status = RunStatus.cancelled
|
|
2567
|
+
|
|
2568
|
+
# Cleanup and store the run response and session
|
|
2569
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2570
|
+
|
|
2571
|
+
return run_response
|
|
2572
|
+
|
|
2573
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2574
|
+
run_response.status = RunStatus.error
|
|
2575
|
+
run_error = create_team_run_error_event(
|
|
2576
|
+
run_response,
|
|
2577
|
+
error=str(e),
|
|
2578
|
+
error_id=e.error_id,
|
|
2579
|
+
error_type=e.type,
|
|
2580
|
+
additional_data=e.additional_data,
|
|
2581
|
+
)
|
|
2582
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2583
|
+
if run_response.content is None:
|
|
2584
|
+
run_response.content = str(e)
|
|
2542
2585
|
|
|
2543
|
-
|
|
2544
|
-
|
|
2586
|
+
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
2587
|
+
|
|
2588
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2589
|
+
|
|
2590
|
+
return run_response
|
|
2591
|
+
|
|
2592
|
+
except Exception as e:
|
|
2593
|
+
if attempt < num_attempts - 1:
|
|
2594
|
+
# Calculate delay with exponential backoff if enabled
|
|
2595
|
+
if self.exponential_backoff:
|
|
2596
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2597
|
+
else:
|
|
2598
|
+
delay = self.delay_between_retries
|
|
2599
|
+
|
|
2600
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2601
|
+
time.sleep(delay)
|
|
2602
|
+
continue
|
|
2603
|
+
|
|
2604
|
+
run_error = create_team_run_error_event(run_response, error=str(e))
|
|
2605
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2606
|
+
|
|
2607
|
+
if run_response.content is None:
|
|
2608
|
+
run_response.content = str(e)
|
|
2609
|
+
|
|
2610
|
+
log_error(f"Error in Team run: {str(e)}")
|
|
2611
|
+
|
|
2612
|
+
# Cleanup and store the run response and session
|
|
2613
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2614
|
+
|
|
2615
|
+
return run_response
|
|
2616
|
+
|
|
2617
|
+
finally:
|
|
2618
|
+
# Always disconnect connectable tools
|
|
2619
|
+
self._disconnect_connectable_tools()
|
|
2620
|
+
await self._disconnect_mcp_tools()
|
|
2621
|
+
# Cancel the memory task if it's still running
|
|
2622
|
+
if memory_task is not None and not memory_task.done():
|
|
2623
|
+
memory_task.cancel()
|
|
2624
|
+
try:
|
|
2625
|
+
await memory_task
|
|
2626
|
+
except asyncio.CancelledError:
|
|
2627
|
+
pass
|
|
2628
|
+
|
|
2629
|
+
# Always clean up the run tracking
|
|
2630
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
2631
|
+
|
|
2632
|
+
return run_response
|
|
2545
2633
|
|
|
2546
2634
|
async def _arun_stream(
|
|
2547
2635
|
self,
|
|
@@ -2578,307 +2666,364 @@ class Team:
|
|
|
2578
2666
|
13. Cleanup and store (scrub, add to session, calculate metrics, save session)
|
|
2579
2667
|
"""
|
|
2580
2668
|
|
|
2581
|
-
|
|
2582
|
-
if run_context.dependencies is not None:
|
|
2583
|
-
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2584
|
-
|
|
2585
|
-
# 2. Read or create session. Reads from the database if provided.
|
|
2586
|
-
if self._has_async_db():
|
|
2587
|
-
team_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
2588
|
-
else:
|
|
2589
|
-
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2590
|
-
|
|
2591
|
-
# 3. Update metadata and session state
|
|
2592
|
-
self._update_metadata(session=team_session)
|
|
2593
|
-
# Initialize session state
|
|
2594
|
-
run_context.session_state = self._initialize_session_state(
|
|
2595
|
-
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2596
|
-
user_id=user_id,
|
|
2597
|
-
session_id=session_id,
|
|
2598
|
-
run_id=run_response.run_id,
|
|
2599
|
-
)
|
|
2600
|
-
# Update session state from DB
|
|
2601
|
-
if run_context.session_state is not None:
|
|
2602
|
-
run_context.session_state = self._load_session_state(
|
|
2603
|
-
session=team_session, session_state=run_context.session_state
|
|
2604
|
-
) # type: ignore
|
|
2669
|
+
memory_task = None
|
|
2605
2670
|
|
|
2606
|
-
#
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
hooks=self.pre_hooks, # type: ignore
|
|
2612
|
-
run_response=run_response,
|
|
2613
|
-
run_context=run_context,
|
|
2614
|
-
run_input=run_input,
|
|
2615
|
-
session=team_session,
|
|
2616
|
-
user_id=user_id,
|
|
2617
|
-
debug_mode=debug_mode,
|
|
2618
|
-
stream_events=stream_events,
|
|
2619
|
-
background_tasks=background_tasks,
|
|
2620
|
-
**kwargs,
|
|
2621
|
-
)
|
|
2622
|
-
async for pre_hook_event in pre_hook_iterator:
|
|
2623
|
-
yield pre_hook_event
|
|
2671
|
+
# Set up retry logic
|
|
2672
|
+
num_attempts = self.retries + 1
|
|
2673
|
+
for attempt in range(num_attempts):
|
|
2674
|
+
if num_attempts > 1:
|
|
2675
|
+
log_debug(f"Retrying Team run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2624
2676
|
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
_tools = self._determine_tools_for_model(
|
|
2630
|
-
model=self.model,
|
|
2631
|
-
run_response=run_response,
|
|
2632
|
-
run_context=run_context,
|
|
2633
|
-
team_run_context=team_run_context,
|
|
2634
|
-
session=team_session, # type: ignore
|
|
2635
|
-
user_id=user_id,
|
|
2636
|
-
async_mode=True,
|
|
2637
|
-
input_message=run_input.input_content,
|
|
2638
|
-
images=run_input.images,
|
|
2639
|
-
videos=run_input.videos,
|
|
2640
|
-
audio=run_input.audios,
|
|
2641
|
-
files=run_input.files,
|
|
2642
|
-
debug_mode=debug_mode,
|
|
2643
|
-
add_history_to_context=add_history_to_context,
|
|
2644
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2645
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2646
|
-
stream=True,
|
|
2647
|
-
stream_events=stream_events,
|
|
2648
|
-
)
|
|
2677
|
+
try:
|
|
2678
|
+
# 1. Resolve dependencies
|
|
2679
|
+
if run_context.dependencies is not None:
|
|
2680
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2649
2681
|
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
user_id=user_id,
|
|
2656
|
-
input_message=run_input.input_content,
|
|
2657
|
-
audio=run_input.audios,
|
|
2658
|
-
images=run_input.images,
|
|
2659
|
-
videos=run_input.videos,
|
|
2660
|
-
files=run_input.files,
|
|
2661
|
-
add_history_to_context=add_history_to_context,
|
|
2662
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2663
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2664
|
-
tools=_tools,
|
|
2665
|
-
**kwargs,
|
|
2666
|
-
)
|
|
2682
|
+
# 2. Read or create session. Reads from the database if provided.
|
|
2683
|
+
if self._has_async_db():
|
|
2684
|
+
team_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
2685
|
+
else:
|
|
2686
|
+
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2667
2687
|
|
|
2668
|
-
|
|
2688
|
+
# 3. Update metadata and session state
|
|
2689
|
+
self._update_metadata(session=team_session)
|
|
2690
|
+
# Initialize session state
|
|
2691
|
+
run_context.session_state = self._initialize_session_state(
|
|
2692
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2693
|
+
user_id=user_id,
|
|
2694
|
+
session_id=session_id,
|
|
2695
|
+
run_id=run_response.run_id,
|
|
2696
|
+
)
|
|
2697
|
+
# Update session state from DB
|
|
2698
|
+
if run_context.session_state is not None:
|
|
2699
|
+
run_context.session_state = self._load_session_state(
|
|
2700
|
+
session=team_session, session_state=run_context.session_state
|
|
2701
|
+
) # type: ignore
|
|
2702
|
+
|
|
2703
|
+
# 4. Execute pre-hooks
|
|
2704
|
+
run_input = cast(TeamRunInput, run_response.input)
|
|
2705
|
+
self.model = cast(Model, self.model)
|
|
2706
|
+
if self.pre_hooks is not None:
|
|
2707
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2708
|
+
hooks=self.pre_hooks, # type: ignore
|
|
2709
|
+
run_response=run_response,
|
|
2710
|
+
run_context=run_context,
|
|
2711
|
+
run_input=run_input,
|
|
2712
|
+
session=team_session,
|
|
2713
|
+
user_id=user_id,
|
|
2714
|
+
debug_mode=debug_mode,
|
|
2715
|
+
stream_events=stream_events,
|
|
2716
|
+
background_tasks=background_tasks,
|
|
2717
|
+
**kwargs,
|
|
2718
|
+
)
|
|
2719
|
+
async for pre_hook_event in pre_hook_iterator:
|
|
2720
|
+
yield pre_hook_event
|
|
2721
|
+
|
|
2722
|
+
# 5. Determine tools for model
|
|
2723
|
+
team_run_context: Dict[str, Any] = {}
|
|
2724
|
+
self.model = cast(Model, self.model)
|
|
2725
|
+
await self._check_and_refresh_mcp_tools()
|
|
2726
|
+
_tools = self._determine_tools_for_model(
|
|
2727
|
+
model=self.model,
|
|
2728
|
+
run_response=run_response,
|
|
2729
|
+
run_context=run_context,
|
|
2730
|
+
team_run_context=team_run_context,
|
|
2731
|
+
session=team_session, # type: ignore
|
|
2732
|
+
user_id=user_id,
|
|
2733
|
+
async_mode=True,
|
|
2734
|
+
input_message=run_input.input_content,
|
|
2735
|
+
images=run_input.images,
|
|
2736
|
+
videos=run_input.videos,
|
|
2737
|
+
audio=run_input.audios,
|
|
2738
|
+
files=run_input.files,
|
|
2739
|
+
debug_mode=debug_mode,
|
|
2740
|
+
add_history_to_context=add_history_to_context,
|
|
2741
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2742
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2743
|
+
stream=True,
|
|
2744
|
+
stream_events=stream_events,
|
|
2745
|
+
)
|
|
2669
2746
|
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2747
|
+
# 6. Prepare run messages
|
|
2748
|
+
run_messages = await self._aget_run_messages(
|
|
2749
|
+
run_response=run_response,
|
|
2750
|
+
run_context=run_context,
|
|
2751
|
+
session=team_session, # type: ignore
|
|
2752
|
+
user_id=user_id,
|
|
2753
|
+
input_message=run_input.input_content,
|
|
2754
|
+
audio=run_input.audios,
|
|
2755
|
+
images=run_input.images,
|
|
2756
|
+
videos=run_input.videos,
|
|
2757
|
+
files=run_input.files,
|
|
2758
|
+
add_history_to_context=add_history_to_context,
|
|
2759
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2760
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2761
|
+
tools=_tools,
|
|
2762
|
+
**kwargs,
|
|
2763
|
+
)
|
|
2680
2764
|
|
|
2681
|
-
|
|
2682
|
-
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
2683
|
-
stream_events = stream_events or stream_intermediate_steps
|
|
2765
|
+
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
2684
2766
|
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
)
|
|
2767
|
+
# 7. Start memory creation in background task
|
|
2768
|
+
memory_task = None
|
|
2769
|
+
if (
|
|
2770
|
+
run_messages.user_message is not None
|
|
2771
|
+
and self.memory_manager is not None
|
|
2772
|
+
and self.enable_user_memories
|
|
2773
|
+
and not self.enable_agentic_memory
|
|
2774
|
+
):
|
|
2775
|
+
log_debug("Starting memory creation in background task.")
|
|
2776
|
+
memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2693
2777
|
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
run_response=run_response,
|
|
2697
|
-
run_messages=run_messages,
|
|
2698
|
-
stream_events=stream_events,
|
|
2699
|
-
):
|
|
2700
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2701
|
-
yield item
|
|
2778
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
2779
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
2702
2780
|
|
|
2703
|
-
|
|
2704
|
-
|
|
2781
|
+
# Yield the run started event
|
|
2782
|
+
if stream_events:
|
|
2783
|
+
yield handle_event( # type: ignore
|
|
2784
|
+
create_team_run_started_event(from_run_response=run_response),
|
|
2785
|
+
run_response,
|
|
2786
|
+
events_to_skip=self.events_to_skip,
|
|
2787
|
+
store_events=self.store_events,
|
|
2788
|
+
)
|
|
2705
2789
|
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
async for event in self._ahandle_model_response_stream(
|
|
2709
|
-
session=team_session,
|
|
2790
|
+
# 8. Reason about the task if reasoning is enabled
|
|
2791
|
+
async for item in self._ahandle_reasoning_stream(
|
|
2710
2792
|
run_response=run_response,
|
|
2711
2793
|
run_messages=run_messages,
|
|
2712
|
-
tools=_tools,
|
|
2713
|
-
response_format=response_format,
|
|
2714
2794
|
stream_events=stream_events,
|
|
2715
|
-
session_state=run_context.session_state,
|
|
2716
|
-
run_context=run_context,
|
|
2717
2795
|
):
|
|
2718
2796
|
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2719
|
-
yield
|
|
2720
|
-
|
|
2721
|
-
|
|
2797
|
+
yield item
|
|
2798
|
+
|
|
2799
|
+
# Check for cancellation before model processing
|
|
2800
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2801
|
+
|
|
2802
|
+
# 9. Get a response from the model
|
|
2803
|
+
if self.output_model is None:
|
|
2804
|
+
async for event in self._ahandle_model_response_stream(
|
|
2805
|
+
session=team_session,
|
|
2806
|
+
run_response=run_response,
|
|
2807
|
+
run_messages=run_messages,
|
|
2808
|
+
tools=_tools,
|
|
2809
|
+
response_format=response_format,
|
|
2810
|
+
stream_events=stream_events,
|
|
2811
|
+
session_state=run_context.session_state,
|
|
2812
|
+
run_context=run_context,
|
|
2813
|
+
):
|
|
2814
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2815
|
+
yield event
|
|
2816
|
+
else:
|
|
2817
|
+
async for event in self._ahandle_model_response_stream(
|
|
2818
|
+
session=team_session,
|
|
2819
|
+
run_response=run_response,
|
|
2820
|
+
run_messages=run_messages,
|
|
2821
|
+
tools=_tools,
|
|
2822
|
+
response_format=response_format,
|
|
2823
|
+
stream_events=stream_events,
|
|
2824
|
+
session_state=run_context.session_state,
|
|
2825
|
+
run_context=run_context,
|
|
2826
|
+
):
|
|
2827
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2828
|
+
from agno.run.team import IntermediateRunContentEvent, RunContentEvent
|
|
2829
|
+
|
|
2830
|
+
if isinstance(event, RunContentEvent):
|
|
2831
|
+
if stream_events:
|
|
2832
|
+
yield IntermediateRunContentEvent(
|
|
2833
|
+
content=event.content,
|
|
2834
|
+
content_type=event.content_type,
|
|
2835
|
+
)
|
|
2836
|
+
else:
|
|
2837
|
+
yield event
|
|
2838
|
+
|
|
2839
|
+
async for event in self._agenerate_response_with_output_model_stream(
|
|
2840
|
+
session=team_session,
|
|
2841
|
+
run_response=run_response,
|
|
2842
|
+
run_messages=run_messages,
|
|
2843
|
+
stream_events=stream_events,
|
|
2844
|
+
):
|
|
2845
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2846
|
+
yield event
|
|
2847
|
+
|
|
2848
|
+
# Check for cancellation after model processing
|
|
2849
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2850
|
+
|
|
2851
|
+
# 10. Parse response with parser model if provided
|
|
2852
|
+
async for event in self._aparse_response_with_parser_model_stream(
|
|
2722
2853
|
session=team_session,
|
|
2723
2854
|
run_response=run_response,
|
|
2724
|
-
run_messages=run_messages,
|
|
2725
|
-
tools=_tools,
|
|
2726
|
-
response_format=response_format,
|
|
2727
2855
|
stream_events=stream_events,
|
|
2728
|
-
session_state=run_context.session_state,
|
|
2729
2856
|
run_context=run_context,
|
|
2730
2857
|
):
|
|
2731
|
-
|
|
2732
|
-
from agno.run.team import IntermediateRunContentEvent, RunContentEvent
|
|
2858
|
+
yield event
|
|
2733
2859
|
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2860
|
+
# Yield RunContentCompletedEvent
|
|
2861
|
+
if stream_events:
|
|
2862
|
+
yield handle_event( # type: ignore
|
|
2863
|
+
create_team_run_content_completed_event(from_run_response=run_response),
|
|
2864
|
+
run_response,
|
|
2865
|
+
events_to_skip=self.events_to_skip,
|
|
2866
|
+
store_events=self.store_events,
|
|
2867
|
+
)
|
|
2868
|
+
|
|
2869
|
+
# Execute post-hooks after output is generated but before response is returned
|
|
2870
|
+
if self.post_hooks is not None:
|
|
2871
|
+
async for event in self._aexecute_post_hooks(
|
|
2872
|
+
hooks=self.post_hooks, # type: ignore
|
|
2873
|
+
run_output=run_response,
|
|
2874
|
+
run_context=run_context,
|
|
2875
|
+
session=team_session,
|
|
2876
|
+
user_id=user_id,
|
|
2877
|
+
debug_mode=debug_mode,
|
|
2878
|
+
stream_events=stream_events,
|
|
2879
|
+
background_tasks=background_tasks,
|
|
2880
|
+
**kwargs,
|
|
2881
|
+
):
|
|
2741
2882
|
yield event
|
|
2742
2883
|
|
|
2743
|
-
|
|
2744
|
-
|
|
2884
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2885
|
+
# 11. Wait for background memory creation
|
|
2886
|
+
async for event in await_for_thread_tasks_stream(
|
|
2745
2887
|
run_response=run_response,
|
|
2746
|
-
|
|
2888
|
+
memory_task=memory_task,
|
|
2747
2889
|
stream_events=stream_events,
|
|
2890
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2891
|
+
store_events=self.store_events,
|
|
2748
2892
|
):
|
|
2749
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2750
2893
|
yield event
|
|
2751
2894
|
|
|
2752
|
-
|
|
2753
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2895
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2754
2896
|
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
yield event
|
|
2897
|
+
# 12. Create session summary
|
|
2898
|
+
if self.session_summary_manager is not None:
|
|
2899
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
2900
|
+
team_session.upsert_run(run_response=run_response)
|
|
2760
2901
|
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2902
|
+
if stream_events:
|
|
2903
|
+
yield handle_event( # type: ignore
|
|
2904
|
+
create_team_session_summary_started_event(from_run_response=run_response),
|
|
2905
|
+
run_response,
|
|
2906
|
+
events_to_skip=self.events_to_skip,
|
|
2907
|
+
store_events=self.store_events,
|
|
2908
|
+
)
|
|
2909
|
+
try:
|
|
2910
|
+
await self.session_summary_manager.acreate_session_summary(session=team_session)
|
|
2911
|
+
except Exception as e:
|
|
2912
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2913
|
+
if stream_events:
|
|
2914
|
+
yield handle_event( # type: ignore
|
|
2915
|
+
create_team_session_summary_completed_event(
|
|
2916
|
+
from_run_response=run_response, session_summary=team_session.summary
|
|
2917
|
+
),
|
|
2918
|
+
run_response,
|
|
2919
|
+
events_to_skip=self.events_to_skip,
|
|
2920
|
+
store_events=self.store_events,
|
|
2921
|
+
)
|
|
2922
|
+
|
|
2923
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2924
|
+
|
|
2925
|
+
# Create the run completed event
|
|
2926
|
+
completed_event = handle_event(
|
|
2927
|
+
create_team_run_completed_event(from_run_response=run_response),
|
|
2765
2928
|
run_response,
|
|
2766
2929
|
events_to_skip=self.events_to_skip,
|
|
2767
2930
|
store_events=self.store_events,
|
|
2768
2931
|
)
|
|
2769
2932
|
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
async for event in self._aexecute_post_hooks(
|
|
2773
|
-
hooks=self.post_hooks, # type: ignore
|
|
2774
|
-
run_output=run_response,
|
|
2775
|
-
run_context=run_context,
|
|
2776
|
-
session=team_session,
|
|
2777
|
-
user_id=user_id,
|
|
2778
|
-
debug_mode=debug_mode,
|
|
2779
|
-
stream_events=stream_events,
|
|
2780
|
-
background_tasks=background_tasks,
|
|
2781
|
-
**kwargs,
|
|
2782
|
-
):
|
|
2783
|
-
yield event
|
|
2933
|
+
# Set the run status to completed
|
|
2934
|
+
run_response.status = RunStatus.completed
|
|
2784
2935
|
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
async for event in await_for_thread_tasks_stream(
|
|
2788
|
-
run_response=run_response,
|
|
2789
|
-
memory_task=memory_task,
|
|
2790
|
-
stream_events=stream_events,
|
|
2791
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
2792
|
-
store_events=self.store_events,
|
|
2793
|
-
):
|
|
2794
|
-
yield event
|
|
2936
|
+
# 13. Cleanup and store the run response and session
|
|
2937
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2795
2938
|
|
|
2796
|
-
|
|
2939
|
+
if stream_events:
|
|
2940
|
+
yield completed_event
|
|
2797
2941
|
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
# Upsert the RunOutput to Team Session before creating the session summary
|
|
2801
|
-
team_session.upsert_run(run_response=run_response)
|
|
2942
|
+
if yield_run_output:
|
|
2943
|
+
yield run_response
|
|
2802
2944
|
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
create_team_session_summary_started_event(from_run_response=run_response),
|
|
2806
|
-
run_response,
|
|
2807
|
-
events_to_skip=self.events_to_skip,
|
|
2808
|
-
store_events=self.store_events,
|
|
2809
|
-
)
|
|
2810
|
-
try:
|
|
2811
|
-
await self.session_summary_manager.acreate_session_summary(session=team_session)
|
|
2812
|
-
except Exception as e:
|
|
2813
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2814
|
-
if stream_events:
|
|
2815
|
-
yield handle_event( # type: ignore
|
|
2816
|
-
create_team_session_summary_completed_event(
|
|
2817
|
-
from_run_response=run_response, session_summary=team_session.summary
|
|
2818
|
-
),
|
|
2819
|
-
run_response,
|
|
2820
|
-
events_to_skip=self.events_to_skip,
|
|
2821
|
-
store_events=self.store_events,
|
|
2822
|
-
)
|
|
2945
|
+
# Log Team Telemetry
|
|
2946
|
+
await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
|
|
2823
2947
|
|
|
2824
|
-
|
|
2948
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2825
2949
|
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
run_response
|
|
2830
|
-
|
|
2831
|
-
store_events=self.store_events,
|
|
2832
|
-
)
|
|
2950
|
+
except RunCancelledException as e:
|
|
2951
|
+
# Handle run cancellation during async streaming
|
|
2952
|
+
log_info(f"Team run {run_response.run_id} was cancelled during async streaming")
|
|
2953
|
+
run_response.status = RunStatus.cancelled
|
|
2954
|
+
run_response.content = str(e)
|
|
2833
2955
|
|
|
2834
|
-
|
|
2835
|
-
|
|
2956
|
+
# Yield the cancellation event
|
|
2957
|
+
yield handle_event( # type: ignore
|
|
2958
|
+
create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2959
|
+
run_response,
|
|
2960
|
+
events_to_skip=self.events_to_skip,
|
|
2961
|
+
store_events=self.store_events,
|
|
2962
|
+
)
|
|
2836
2963
|
|
|
2837
|
-
|
|
2838
|
-
|
|
2964
|
+
# Cleanup and store the run response and session
|
|
2965
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2839
2966
|
|
|
2840
|
-
|
|
2841
|
-
|
|
2967
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2968
|
+
run_response.status = RunStatus.error
|
|
2969
|
+
run_error = create_team_run_error_event(
|
|
2970
|
+
run_response,
|
|
2971
|
+
error=str(e),
|
|
2972
|
+
error_id=e.error_id,
|
|
2973
|
+
error_type=e.type,
|
|
2974
|
+
additional_data=e.additional_data,
|
|
2975
|
+
)
|
|
2976
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2977
|
+
if run_response.content is None:
|
|
2978
|
+
run_response.content = str(e)
|
|
2842
2979
|
|
|
2843
|
-
|
|
2844
|
-
yield run_response
|
|
2980
|
+
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
2845
2981
|
|
|
2846
|
-
|
|
2847
|
-
await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
|
|
2982
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2848
2983
|
|
|
2849
|
-
|
|
2984
|
+
yield run_error
|
|
2850
2985
|
|
|
2851
|
-
|
|
2852
|
-
# Handle run cancellation during async streaming
|
|
2853
|
-
log_info(f"Team run {run_response.run_id} was cancelled during async streaming")
|
|
2854
|
-
run_response.status = RunStatus.cancelled
|
|
2855
|
-
run_response.content = str(e)
|
|
2986
|
+
break
|
|
2856
2987
|
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2988
|
+
except Exception as e:
|
|
2989
|
+
if attempt < num_attempts - 1:
|
|
2990
|
+
# Calculate delay with exponential backoff if enabled
|
|
2991
|
+
if self.exponential_backoff:
|
|
2992
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2993
|
+
else:
|
|
2994
|
+
delay = self.delay_between_retries
|
|
2995
|
+
|
|
2996
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2997
|
+
time.sleep(delay)
|
|
2998
|
+
continue
|
|
2864
2999
|
|
|
2865
|
-
|
|
2866
|
-
|
|
3000
|
+
run_response.status = RunStatus.error
|
|
3001
|
+
run_error = create_team_run_error_event(run_response, error=str(e))
|
|
3002
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
3003
|
+
if run_response.content is None:
|
|
3004
|
+
run_response.content = str(e)
|
|
2867
3005
|
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
3006
|
+
log_error(f"Error in Team run: {str(e)}")
|
|
3007
|
+
|
|
3008
|
+
# Cleanup and store the run response and session
|
|
3009
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
3010
|
+
|
|
3011
|
+
yield run_error
|
|
3012
|
+
|
|
3013
|
+
finally:
|
|
3014
|
+
# Always disconnect connectable tools
|
|
3015
|
+
self._disconnect_connectable_tools()
|
|
3016
|
+
await self._disconnect_mcp_tools()
|
|
3017
|
+
# Cancel the memory task if it's still running
|
|
3018
|
+
if memory_task is not None and not memory_task.done():
|
|
3019
|
+
memory_task.cancel()
|
|
3020
|
+
try:
|
|
3021
|
+
await memory_task
|
|
3022
|
+
except asyncio.CancelledError:
|
|
3023
|
+
pass
|
|
2879
3024
|
|
|
2880
|
-
|
|
2881
|
-
|
|
3025
|
+
# Always clean up the run tracking
|
|
3026
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
2882
3027
|
|
|
2883
3028
|
@overload
|
|
2884
3029
|
async def arun(
|
|
@@ -2904,7 +3049,7 @@ class Team:
|
|
|
2904
3049
|
dependencies: Optional[Dict[str, Any]] = None,
|
|
2905
3050
|
metadata: Optional[Dict[str, Any]] = None,
|
|
2906
3051
|
debug_mode: Optional[bool] = None,
|
|
2907
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
3052
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2908
3053
|
**kwargs: Any,
|
|
2909
3054
|
) -> TeamRunOutput: ...
|
|
2910
3055
|
|
|
@@ -2934,7 +3079,7 @@ class Team:
|
|
|
2934
3079
|
debug_mode: Optional[bool] = None,
|
|
2935
3080
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2936
3081
|
yield_run_output: bool = False,
|
|
2937
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
3082
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2938
3083
|
**kwargs: Any,
|
|
2939
3084
|
) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent]]: ...
|
|
2940
3085
|
|
|
@@ -2963,7 +3108,7 @@ class Team:
|
|
|
2963
3108
|
debug_mode: Optional[bool] = None,
|
|
2964
3109
|
yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
|
|
2965
3110
|
yield_run_output: bool = False,
|
|
2966
|
-
output_schema: Optional[Type[BaseModel]] = None,
|
|
3111
|
+
output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None,
|
|
2967
3112
|
**kwargs: Any,
|
|
2968
3113
|
) -> Union[TeamRunOutput, AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent]]]:
|
|
2969
3114
|
"""Run the Team asynchronously and return the response."""
|
|
@@ -3110,79 +3255,38 @@ class Team:
|
|
|
3110
3255
|
|
|
3111
3256
|
yield_run_output = bool(yield_run_output or yield_run_response) # For backwards compatibility
|
|
3112
3257
|
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
add_session_state_to_context=add_session_state,
|
|
3146
|
-
response_format=response_format,
|
|
3147
|
-
debug_mode=debug_mode,
|
|
3148
|
-
background_tasks=background_tasks,
|
|
3149
|
-
**kwargs,
|
|
3150
|
-
)
|
|
3151
|
-
|
|
3152
|
-
except (InputCheckError, OutputCheckError) as e:
|
|
3153
|
-
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
3154
|
-
raise e
|
|
3155
|
-
except KeyboardInterrupt:
|
|
3156
|
-
run_response.content = "Operation cancelled by user"
|
|
3157
|
-
run_response.status = RunStatus.cancelled
|
|
3158
|
-
|
|
3159
|
-
if stream:
|
|
3160
|
-
return async_generator_wrapper(
|
|
3161
|
-
create_team_run_cancelled_event(
|
|
3162
|
-
from_run_response=run_response, reason="Operation cancelled by user"
|
|
3163
|
-
)
|
|
3164
|
-
)
|
|
3165
|
-
else:
|
|
3166
|
-
return run_response
|
|
3167
|
-
except Exception as e:
|
|
3168
|
-
# Check if this is the last attempt
|
|
3169
|
-
if attempt < num_attempts - 1:
|
|
3170
|
-
# Calculate delay with exponential backoff if enabled
|
|
3171
|
-
if self.exponential_backoff:
|
|
3172
|
-
delay = self.delay_between_retries * (2**attempt)
|
|
3173
|
-
else:
|
|
3174
|
-
delay = self.delay_between_retries
|
|
3175
|
-
|
|
3176
|
-
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
3177
|
-
time.sleep(delay)
|
|
3178
|
-
continue
|
|
3179
|
-
else:
|
|
3180
|
-
# Final attempt failed - re-raise the exception
|
|
3181
|
-
log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
|
|
3182
|
-
raise e
|
|
3183
|
-
|
|
3184
|
-
# If we get here, all retries failed
|
|
3185
|
-
raise Exception(f"Failed after {num_attempts} attempts.")
|
|
3258
|
+
if stream:
|
|
3259
|
+
return self._arun_stream( # type: ignore
|
|
3260
|
+
input=validated_input,
|
|
3261
|
+
run_response=run_response,
|
|
3262
|
+
run_context=run_context,
|
|
3263
|
+
session_id=session_id,
|
|
3264
|
+
user_id=user_id,
|
|
3265
|
+
add_history_to_context=add_history,
|
|
3266
|
+
add_dependencies_to_context=add_dependencies,
|
|
3267
|
+
add_session_state_to_context=add_session_state,
|
|
3268
|
+
response_format=response_format,
|
|
3269
|
+
stream_events=stream_events,
|
|
3270
|
+
yield_run_output=yield_run_output,
|
|
3271
|
+
debug_mode=debug_mode,
|
|
3272
|
+
background_tasks=background_tasks,
|
|
3273
|
+
**kwargs,
|
|
3274
|
+
)
|
|
3275
|
+
else:
|
|
3276
|
+
return self._arun( # type: ignore
|
|
3277
|
+
input=validated_input,
|
|
3278
|
+
run_response=run_response,
|
|
3279
|
+
run_context=run_context,
|
|
3280
|
+
session_id=session_id,
|
|
3281
|
+
user_id=user_id,
|
|
3282
|
+
add_history_to_context=add_history,
|
|
3283
|
+
add_dependencies_to_context=add_dependencies,
|
|
3284
|
+
add_session_state_to_context=add_session_state,
|
|
3285
|
+
response_format=response_format,
|
|
3286
|
+
debug_mode=debug_mode,
|
|
3287
|
+
background_tasks=background_tasks,
|
|
3288
|
+
**kwargs,
|
|
3289
|
+
)
|
|
3186
3290
|
|
|
3187
3291
|
def _update_run_response(
|
|
3188
3292
|
self,
|
|
@@ -3199,7 +3303,7 @@ class Team:
|
|
|
3199
3303
|
# Update the run_response content with the structured output
|
|
3200
3304
|
run_response.content = model_response.parsed
|
|
3201
3305
|
# Update the run_response content_type with the structured output class name
|
|
3202
|
-
run_response.content_type = output_schema.__name__
|
|
3306
|
+
run_response.content_type = "dict" if isinstance(output_schema, dict) else output_schema.__name__
|
|
3203
3307
|
else:
|
|
3204
3308
|
# Update the run_response content with the model response content
|
|
3205
3309
|
if not run_response.content:
|
|
@@ -3498,12 +3602,16 @@ class Team:
|
|
|
3498
3602
|
self._convert_response_to_structured_format(full_model_response, run_context=run_context)
|
|
3499
3603
|
# Get output_schema from run_context
|
|
3500
3604
|
output_schema = run_context.output_schema if run_context else None
|
|
3501
|
-
content_type = output_schema.__name__ # type: ignore
|
|
3605
|
+
content_type = "dict" if isinstance(output_schema, dict) else output_schema.__name__ # type: ignore
|
|
3502
3606
|
run_response.content_type = content_type
|
|
3503
3607
|
elif self._member_response_model is not None:
|
|
3504
3608
|
full_model_response.content = model_response_event.content
|
|
3505
3609
|
self._convert_response_to_structured_format(full_model_response, run_context=run_context)
|
|
3506
|
-
content_type =
|
|
3610
|
+
content_type = (
|
|
3611
|
+
"dict"
|
|
3612
|
+
if isinstance(self._member_response_model, dict)
|
|
3613
|
+
else self._member_response_model.__name__
|
|
3614
|
+
) # type: ignore
|
|
3507
3615
|
run_response.content_type = content_type
|
|
3508
3616
|
elif isinstance(model_response_event.content, str):
|
|
3509
3617
|
full_model_response.content = (full_model_response.content or "") + model_response_event.content
|
|
@@ -3755,41 +3863,71 @@ class Team:
|
|
|
3755
3863
|
output_schema = run_context.output_schema if run_context else None
|
|
3756
3864
|
|
|
3757
3865
|
# Convert the response to the structured format if needed
|
|
3758
|
-
if output_schema is not None
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3866
|
+
if output_schema is not None:
|
|
3867
|
+
# If the output schema is a dict, do not convert it into a BaseModel
|
|
3868
|
+
if isinstance(output_schema, dict):
|
|
3869
|
+
if isinstance(run_response.content, dict):
|
|
3870
|
+
# Content is already a dict - just set content_type
|
|
3871
|
+
if hasattr(run_response, "content_type"):
|
|
3872
|
+
run_response.content_type = "dict"
|
|
3873
|
+
elif isinstance(run_response.content, str):
|
|
3874
|
+
parsed_dict = parse_response_dict_str(run_response.content)
|
|
3875
|
+
if parsed_dict is not None:
|
|
3876
|
+
run_response.content = parsed_dict
|
|
3766
3877
|
if hasattr(run_response, "content_type"):
|
|
3767
|
-
run_response.content_type =
|
|
3878
|
+
run_response.content_type = "dict"
|
|
3768
3879
|
else:
|
|
3769
|
-
log_warning("Failed to
|
|
3770
|
-
|
|
3771
|
-
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3880
|
+
log_warning("Failed to parse JSON response")
|
|
3881
|
+
# If the output schema is a Pydantic model and parse_response is True, parse it into a BaseModel
|
|
3882
|
+
elif not isinstance(run_response.content, output_schema):
|
|
3883
|
+
if isinstance(run_response.content, str) and self.parse_response:
|
|
3884
|
+
try:
|
|
3885
|
+
parsed_response_content = parse_response_model_str(run_response.content, output_schema)
|
|
3886
|
+
|
|
3887
|
+
# Update TeamRunOutput
|
|
3888
|
+
if parsed_response_content is not None:
|
|
3889
|
+
run_response.content = parsed_response_content
|
|
3890
|
+
if hasattr(run_response, "content_type"):
|
|
3891
|
+
run_response.content_type = output_schema.__name__
|
|
3892
|
+
else:
|
|
3893
|
+
log_warning("Failed to convert response to output_schema")
|
|
3894
|
+
except Exception as e:
|
|
3895
|
+
log_warning(f"Failed to convert response to output model: {e}")
|
|
3896
|
+
else:
|
|
3897
|
+
log_warning("Something went wrong. Team run response content is not a string")
|
|
3898
|
+
elif self._member_response_model is not None:
|
|
3899
|
+
# Handle dict schema from member
|
|
3900
|
+
if isinstance(self._member_response_model, dict):
|
|
3901
|
+
if isinstance(run_response.content, dict):
|
|
3902
|
+
# Content is already a dict - just set content_type
|
|
3903
|
+
if hasattr(run_response, "content_type"):
|
|
3904
|
+
run_response.content_type = "dict"
|
|
3905
|
+
elif isinstance(run_response.content, str):
|
|
3906
|
+
parsed_dict = parse_response_dict_str(run_response.content)
|
|
3907
|
+
if parsed_dict is not None:
|
|
3908
|
+
run_response.content = parsed_dict
|
|
3785
3909
|
if hasattr(run_response, "content_type"):
|
|
3786
|
-
run_response.content_type =
|
|
3910
|
+
run_response.content_type = "dict"
|
|
3787
3911
|
else:
|
|
3788
|
-
log_warning("Failed to
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3912
|
+
log_warning("Failed to parse JSON response")
|
|
3913
|
+
# Handle Pydantic schema from member
|
|
3914
|
+
elif not isinstance(run_response.content, self._member_response_model):
|
|
3915
|
+
if isinstance(run_response.content, str):
|
|
3916
|
+
try:
|
|
3917
|
+
parsed_response_content = parse_response_model_str(
|
|
3918
|
+
run_response.content, self._member_response_model
|
|
3919
|
+
)
|
|
3920
|
+
# Update TeamRunOutput
|
|
3921
|
+
if parsed_response_content is not None:
|
|
3922
|
+
run_response.content = parsed_response_content
|
|
3923
|
+
if hasattr(run_response, "content_type"):
|
|
3924
|
+
run_response.content_type = self._member_response_model.__name__
|
|
3925
|
+
else:
|
|
3926
|
+
log_warning("Failed to convert response to output_schema")
|
|
3927
|
+
except Exception as e:
|
|
3928
|
+
log_warning(f"Failed to convert response to output model: {e}")
|
|
3929
|
+
else:
|
|
3930
|
+
log_warning("Something went wrong. Member run response content is not a string")
|
|
3793
3931
|
|
|
3794
3932
|
def _cleanup_and_store(self, run_response: TeamRunOutput, session: TeamSession) -> None:
|
|
3795
3933
|
# Scrub the stored run based on storage flags
|
|
@@ -3892,6 +4030,10 @@ class Team:
|
|
|
3892
4030
|
elif model.supports_json_schema_outputs:
|
|
3893
4031
|
if self.use_json_mode:
|
|
3894
4032
|
log_debug("Setting Model.response_format to JSON response mode")
|
|
4033
|
+
# Handle JSON schema - pass through directly (user provides full provider format)
|
|
4034
|
+
if isinstance(output_schema, dict):
|
|
4035
|
+
return output_schema
|
|
4036
|
+
# Handle Pydantic schema
|
|
3895
4037
|
return {
|
|
3896
4038
|
"type": "json_schema",
|
|
3897
4039
|
"json_schema": {
|
|
@@ -4750,281 +4892,77 @@ class Team:
|
|
|
4750
4892
|
|
|
4751
4893
|
return updated_reasoning_content
|
|
4752
4894
|
|
|
4753
|
-
def
|
|
4895
|
+
def _handle_reasoning_event(
|
|
4754
4896
|
self,
|
|
4897
|
+
event: "ReasoningEvent", # type: ignore # noqa: F821
|
|
4755
4898
|
run_response: TeamRunOutput,
|
|
4756
|
-
run_messages: RunMessages,
|
|
4757
4899
|
stream_events: bool,
|
|
4758
4900
|
) -> Iterator[TeamRunOutputEvent]:
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
create_team_reasoning_started_event(from_run_response=run_response),
|
|
4762
|
-
run_response,
|
|
4763
|
-
events_to_skip=self.events_to_skip,
|
|
4764
|
-
store_events=self.store_events,
|
|
4765
|
-
)
|
|
4766
|
-
|
|
4767
|
-
use_default_reasoning = False
|
|
4768
|
-
|
|
4769
|
-
# Get the reasoning model
|
|
4770
|
-
reasoning_model: Optional[Model] = self.reasoning_model
|
|
4771
|
-
reasoning_model_provided = reasoning_model is not None
|
|
4772
|
-
if reasoning_model is None and self.model is not None:
|
|
4773
|
-
from copy import deepcopy
|
|
4774
|
-
|
|
4775
|
-
reasoning_model = deepcopy(self.model)
|
|
4776
|
-
if reasoning_model is None:
|
|
4777
|
-
log_warning("Reasoning error. Reasoning model is None, continuing regular session...")
|
|
4778
|
-
return
|
|
4779
|
-
|
|
4780
|
-
# If a reasoning model is provided, use it to generate reasoning
|
|
4781
|
-
if reasoning_model_provided:
|
|
4782
|
-
from agno.reasoning.anthropic import is_anthropic_reasoning_model
|
|
4783
|
-
from agno.reasoning.azure_ai_foundry import is_ai_foundry_reasoning_model
|
|
4784
|
-
from agno.reasoning.deepseek import is_deepseek_reasoning_model
|
|
4785
|
-
from agno.reasoning.gemini import is_gemini_reasoning_model
|
|
4786
|
-
from agno.reasoning.groq import is_groq_reasoning_model
|
|
4787
|
-
from agno.reasoning.helpers import get_reasoning_agent
|
|
4788
|
-
from agno.reasoning.ollama import is_ollama_reasoning_model
|
|
4789
|
-
from agno.reasoning.openai import is_openai_reasoning_model
|
|
4790
|
-
from agno.reasoning.vertexai import is_vertexai_reasoning_model
|
|
4791
|
-
|
|
4792
|
-
reasoning_agent = self.reasoning_agent or get_reasoning_agent(
|
|
4793
|
-
reasoning_model=reasoning_model,
|
|
4794
|
-
session_state=self.session_state,
|
|
4795
|
-
dependencies=self.dependencies,
|
|
4796
|
-
metadata=self.metadata,
|
|
4797
|
-
)
|
|
4798
|
-
is_deepseek = is_deepseek_reasoning_model(reasoning_model)
|
|
4799
|
-
is_groq = is_groq_reasoning_model(reasoning_model)
|
|
4800
|
-
is_openai = is_openai_reasoning_model(reasoning_model)
|
|
4801
|
-
is_ollama = is_ollama_reasoning_model(reasoning_model)
|
|
4802
|
-
is_ai_foundry = is_ai_foundry_reasoning_model(reasoning_model)
|
|
4803
|
-
is_gemini = is_gemini_reasoning_model(reasoning_model)
|
|
4804
|
-
is_anthropic = is_anthropic_reasoning_model(reasoning_model)
|
|
4805
|
-
is_vertexai = is_vertexai_reasoning_model(reasoning_model)
|
|
4806
|
-
|
|
4807
|
-
if (
|
|
4808
|
-
is_deepseek
|
|
4809
|
-
or is_groq
|
|
4810
|
-
or is_openai
|
|
4811
|
-
or is_ollama
|
|
4812
|
-
or is_ai_foundry
|
|
4813
|
-
or is_gemini
|
|
4814
|
-
or is_anthropic
|
|
4815
|
-
or is_vertexai
|
|
4816
|
-
):
|
|
4817
|
-
reasoning_message: Optional[Message] = None
|
|
4818
|
-
if is_deepseek:
|
|
4819
|
-
from agno.reasoning.deepseek import get_deepseek_reasoning
|
|
4820
|
-
|
|
4821
|
-
log_debug("Starting DeepSeek Reasoning", center=True, symbol="=")
|
|
4822
|
-
reasoning_message = get_deepseek_reasoning(
|
|
4823
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
4824
|
-
)
|
|
4825
|
-
elif is_groq:
|
|
4826
|
-
from agno.reasoning.groq import get_groq_reasoning
|
|
4827
|
-
|
|
4828
|
-
log_debug("Starting Groq Reasoning", center=True, symbol="=")
|
|
4829
|
-
reasoning_message = get_groq_reasoning(
|
|
4830
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
4831
|
-
)
|
|
4832
|
-
elif is_openai:
|
|
4833
|
-
from agno.reasoning.openai import get_openai_reasoning
|
|
4834
|
-
|
|
4835
|
-
log_debug("Starting OpenAI Reasoning", center=True, symbol="=")
|
|
4836
|
-
reasoning_message = get_openai_reasoning(
|
|
4837
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
4838
|
-
)
|
|
4839
|
-
elif is_ollama:
|
|
4840
|
-
from agno.reasoning.ollama import get_ollama_reasoning
|
|
4841
|
-
|
|
4842
|
-
log_debug("Starting Ollama Reasoning", center=True, symbol="=")
|
|
4843
|
-
reasoning_message = get_ollama_reasoning(
|
|
4844
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
4845
|
-
)
|
|
4846
|
-
elif is_ai_foundry:
|
|
4847
|
-
from agno.reasoning.azure_ai_foundry import get_ai_foundry_reasoning
|
|
4848
|
-
|
|
4849
|
-
log_debug("Starting Azure AI Foundry Reasoning", center=True, symbol="=")
|
|
4850
|
-
reasoning_message = get_ai_foundry_reasoning(
|
|
4851
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
4852
|
-
)
|
|
4853
|
-
elif is_gemini:
|
|
4854
|
-
from agno.reasoning.gemini import get_gemini_reasoning
|
|
4855
|
-
|
|
4856
|
-
log_debug("Starting Gemini Reasoning", center=True, symbol="=")
|
|
4857
|
-
reasoning_message = get_gemini_reasoning(
|
|
4858
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
4859
|
-
)
|
|
4860
|
-
elif is_anthropic:
|
|
4861
|
-
from agno.reasoning.anthropic import get_anthropic_reasoning
|
|
4901
|
+
"""
|
|
4902
|
+
Convert a ReasoningEvent from the ReasoningManager to Team-specific TeamRunOutputEvents.
|
|
4862
4903
|
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
elif is_vertexai:
|
|
4868
|
-
from agno.reasoning.vertexai import get_vertexai_reasoning
|
|
4904
|
+
This method handles the conversion of generic reasoning events to Team events,
|
|
4905
|
+
keeping the Team._reason() method clean and simple.
|
|
4906
|
+
"""
|
|
4907
|
+
from agno.reasoning.manager import ReasoningEventType
|
|
4869
4908
|
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
)
|
|
4909
|
+
if event.event_type == ReasoningEventType.started:
|
|
4910
|
+
if stream_events:
|
|
4911
|
+
yield handle_event( # type: ignore
|
|
4912
|
+
create_team_reasoning_started_event(from_run_response=run_response),
|
|
4913
|
+
run_response,
|
|
4914
|
+
events_to_skip=self.events_to_skip,
|
|
4915
|
+
store_events=self.store_events,
|
|
4916
|
+
)
|
|
4874
4917
|
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4918
|
+
elif event.event_type == ReasoningEventType.content_delta:
|
|
4919
|
+
if stream_events and event.reasoning_content:
|
|
4920
|
+
yield handle_event( # type: ignore
|
|
4921
|
+
create_team_reasoning_content_delta_event(
|
|
4922
|
+
from_run_response=run_response,
|
|
4923
|
+
reasoning_content=event.reasoning_content,
|
|
4924
|
+
),
|
|
4925
|
+
run_response,
|
|
4926
|
+
events_to_skip=self.events_to_skip,
|
|
4927
|
+
store_events=self.store_events,
|
|
4928
|
+
)
|
|
4878
4929
|
|
|
4879
|
-
|
|
4880
|
-
|
|
4930
|
+
elif event.event_type == ReasoningEventType.step:
|
|
4931
|
+
if event.reasoning_step:
|
|
4932
|
+
# Update run_response with this step
|
|
4881
4933
|
update_run_output_with_reasoning(
|
|
4882
4934
|
run_response=run_response,
|
|
4883
|
-
reasoning_steps=[
|
|
4884
|
-
reasoning_agent_messages=[
|
|
4935
|
+
reasoning_steps=[event.reasoning_step],
|
|
4936
|
+
reasoning_agent_messages=[],
|
|
4885
4937
|
)
|
|
4886
4938
|
if stream_events:
|
|
4939
|
+
updated_reasoning_content = self._format_reasoning_step_content(
|
|
4940
|
+
run_response=run_response,
|
|
4941
|
+
reasoning_step=event.reasoning_step,
|
|
4942
|
+
)
|
|
4887
4943
|
yield handle_event( # type: ignore
|
|
4888
|
-
|
|
4944
|
+
create_team_reasoning_step_event(
|
|
4889
4945
|
from_run_response=run_response,
|
|
4890
|
-
|
|
4891
|
-
|
|
4946
|
+
reasoning_step=event.reasoning_step,
|
|
4947
|
+
reasoning_content=updated_reasoning_content,
|
|
4892
4948
|
),
|
|
4893
4949
|
run_response,
|
|
4894
4950
|
events_to_skip=self.events_to_skip,
|
|
4895
4951
|
store_events=self.store_events,
|
|
4896
4952
|
)
|
|
4897
|
-
else:
|
|
4898
|
-
log_info(
|
|
4899
|
-
f"Reasoning model: {reasoning_model.__class__.__name__} is not a native reasoning model, defaulting to manual Chain-of-Thought reasoning"
|
|
4900
|
-
)
|
|
4901
|
-
use_default_reasoning = True
|
|
4902
|
-
# If no reasoning model is provided, use default reasoning
|
|
4903
|
-
else:
|
|
4904
|
-
use_default_reasoning = True
|
|
4905
|
-
|
|
4906
|
-
if use_default_reasoning:
|
|
4907
|
-
from agno.reasoning.default import get_default_reasoning_agent
|
|
4908
|
-
from agno.reasoning.helpers import get_next_action, update_messages_with_reasoning
|
|
4909
|
-
|
|
4910
|
-
# Get default reasoning agent
|
|
4911
|
-
use_json_mode: bool = self.use_json_mode
|
|
4912
4953
|
|
|
4913
|
-
|
|
4914
|
-
if
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
|
|
4919
|
-
tool_call_limit=self.tool_call_limit,
|
|
4920
|
-
telemetry=self.telemetry,
|
|
4921
|
-
debug_mode=self.debug_mode,
|
|
4922
|
-
debug_level=self.debug_level,
|
|
4923
|
-
use_json_mode=use_json_mode,
|
|
4924
|
-
session_state=self.session_state,
|
|
4925
|
-
dependencies=self.dependencies,
|
|
4926
|
-
metadata=self.metadata,
|
|
4954
|
+
elif event.event_type == ReasoningEventType.completed:
|
|
4955
|
+
if event.message and event.reasoning_steps:
|
|
4956
|
+
update_run_output_with_reasoning(
|
|
4957
|
+
run_response=run_response,
|
|
4958
|
+
reasoning_steps=event.reasoning_steps,
|
|
4959
|
+
reasoning_agent_messages=event.reasoning_messages,
|
|
4927
4960
|
)
|
|
4928
|
-
|
|
4929
|
-
# Validate reasoning agent
|
|
4930
|
-
if reasoning_agent is None:
|
|
4931
|
-
log_warning("Reasoning error. Reasoning agent is None, continuing regular session...")
|
|
4932
|
-
return
|
|
4933
|
-
# Ensure the reasoning agent response model is ReasoningSteps
|
|
4934
|
-
if (
|
|
4935
|
-
reasoning_agent.output_schema is not None
|
|
4936
|
-
and not isinstance(reasoning_agent.output_schema, type)
|
|
4937
|
-
and not issubclass(reasoning_agent.output_schema, ReasoningSteps)
|
|
4938
|
-
):
|
|
4939
|
-
log_warning("Reasoning agent response model should be `ReasoningSteps`, continuing regular session...")
|
|
4940
|
-
return
|
|
4941
|
-
# Ensure the reasoning model and agent do not show tool calls
|
|
4942
|
-
|
|
4943
|
-
step_count = 1
|
|
4944
|
-
next_action = NextAction.CONTINUE
|
|
4945
|
-
reasoning_messages: List[Message] = []
|
|
4946
|
-
all_reasoning_steps: List[ReasoningStep] = []
|
|
4947
|
-
log_debug("Starting Reasoning", center=True, symbol="=")
|
|
4948
|
-
while next_action == NextAction.CONTINUE and step_count < self.reasoning_max_steps:
|
|
4949
|
-
log_debug(f"Step {step_count}", center=True, symbol="-")
|
|
4950
|
-
step_count += 1
|
|
4951
|
-
try:
|
|
4952
|
-
# Run the reasoning agent
|
|
4953
|
-
reasoning_agent_response: RunOutput = reasoning_agent.run( # type: ignore
|
|
4954
|
-
input=run_messages.get_input_messages()
|
|
4955
|
-
)
|
|
4956
|
-
if reasoning_agent_response.content is None or reasoning_agent_response.messages is None:
|
|
4957
|
-
log_warning("Reasoning error. Reasoning response is empty, continuing regular session...")
|
|
4958
|
-
break
|
|
4959
|
-
|
|
4960
|
-
if isinstance(reasoning_agent_response.content, str):
|
|
4961
|
-
log_warning(
|
|
4962
|
-
"Reasoning error. Content is a string, not structured output. Continuing regular session..."
|
|
4963
|
-
)
|
|
4964
|
-
break
|
|
4965
|
-
|
|
4966
|
-
if reasoning_agent_response.content.reasoning_steps is None:
|
|
4967
|
-
log_warning("Reasoning error. Reasoning steps are empty, continuing regular session...")
|
|
4968
|
-
break
|
|
4969
|
-
|
|
4970
|
-
reasoning_steps: List[ReasoningStep] = reasoning_agent_response.content.reasoning_steps
|
|
4971
|
-
all_reasoning_steps.extend(reasoning_steps)
|
|
4972
|
-
# Yield reasoning steps
|
|
4973
|
-
if stream_events:
|
|
4974
|
-
for reasoning_step in reasoning_steps:
|
|
4975
|
-
updated_reasoning_content = self._format_reasoning_step_content(
|
|
4976
|
-
run_response, reasoning_step
|
|
4977
|
-
)
|
|
4978
|
-
|
|
4979
|
-
yield handle_event( # type: ignore
|
|
4980
|
-
create_team_reasoning_step_event(
|
|
4981
|
-
from_run_response=run_response,
|
|
4982
|
-
reasoning_step=reasoning_step,
|
|
4983
|
-
reasoning_content=updated_reasoning_content,
|
|
4984
|
-
),
|
|
4985
|
-
run_response,
|
|
4986
|
-
events_to_skip=self.events_to_skip,
|
|
4987
|
-
store_events=self.store_events,
|
|
4988
|
-
)
|
|
4989
|
-
|
|
4990
|
-
# Find the index of the first assistant message
|
|
4991
|
-
first_assistant_index = next(
|
|
4992
|
-
(i for i, m in enumerate(reasoning_agent_response.messages) if m.role == "assistant"),
|
|
4993
|
-
len(reasoning_agent_response.messages),
|
|
4994
|
-
)
|
|
4995
|
-
# Extract reasoning messages starting from the message after the first assistant message
|
|
4996
|
-
reasoning_messages = reasoning_agent_response.messages[first_assistant_index:]
|
|
4997
|
-
|
|
4998
|
-
# Add reasoning step to the Agent's run_response
|
|
4999
|
-
update_run_output_with_reasoning(
|
|
5000
|
-
run_response=run_response,
|
|
5001
|
-
reasoning_steps=reasoning_steps,
|
|
5002
|
-
reasoning_agent_messages=reasoning_agent_response.messages,
|
|
5003
|
-
)
|
|
5004
|
-
|
|
5005
|
-
# Get the next action
|
|
5006
|
-
next_action = get_next_action(reasoning_steps[-1])
|
|
5007
|
-
if next_action == NextAction.FINAL_ANSWER:
|
|
5008
|
-
break
|
|
5009
|
-
except Exception as e:
|
|
5010
|
-
log_error(f"Reasoning error: {e}")
|
|
5011
|
-
break
|
|
5012
|
-
|
|
5013
|
-
log_debug(f"Total Reasoning steps: {len(all_reasoning_steps)}")
|
|
5014
|
-
log_debug("Reasoning finished", center=True, symbol="=")
|
|
5015
|
-
|
|
5016
|
-
# Update the messages_for_model to include reasoning messages
|
|
5017
|
-
update_messages_with_reasoning(
|
|
5018
|
-
run_messages=run_messages,
|
|
5019
|
-
reasoning_messages=reasoning_messages,
|
|
5020
|
-
)
|
|
5021
|
-
|
|
5022
|
-
# Yield the final reasoning completed event
|
|
5023
4961
|
if stream_events:
|
|
5024
4962
|
yield handle_event( # type: ignore
|
|
5025
4963
|
create_team_reasoning_completed_event(
|
|
5026
4964
|
from_run_response=run_response,
|
|
5027
|
-
content=ReasoningSteps(reasoning_steps=
|
|
4965
|
+
content=ReasoningSteps(reasoning_steps=event.reasoning_steps),
|
|
5028
4966
|
content_type=ReasoningSteps.__name__,
|
|
5029
4967
|
),
|
|
5030
4968
|
run_response,
|
|
@@ -5032,285 +4970,97 @@ class Team:
|
|
|
5032
4970
|
store_events=self.store_events,
|
|
5033
4971
|
)
|
|
5034
4972
|
|
|
5035
|
-
|
|
4973
|
+
elif event.event_type == ReasoningEventType.error:
|
|
4974
|
+
log_warning(f"Reasoning error. {event.error}, continuing regular session...")
|
|
4975
|
+
|
|
4976
|
+
def _reason(
|
|
5036
4977
|
self,
|
|
5037
4978
|
run_response: TeamRunOutput,
|
|
5038
4979
|
run_messages: RunMessages,
|
|
5039
4980
|
stream_events: bool,
|
|
5040
|
-
) ->
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
create_team_reasoning_started_event(from_run_response=run_response),
|
|
5044
|
-
run_response,
|
|
5045
|
-
events_to_skip=self.events_to_skip,
|
|
5046
|
-
store_events=self.store_events,
|
|
5047
|
-
)
|
|
4981
|
+
) -> Iterator[TeamRunOutputEvent]:
|
|
4982
|
+
"""
|
|
4983
|
+
Run reasoning using the ReasoningManager.
|
|
5048
4984
|
|
|
5049
|
-
|
|
4985
|
+
Handles both native reasoning models (DeepSeek, Anthropic, etc.) and
|
|
4986
|
+
default Chain-of-Thought reasoning with a clean, unified interface.
|
|
4987
|
+
"""
|
|
4988
|
+
from agno.reasoning.manager import ReasoningConfig, ReasoningManager
|
|
5050
4989
|
|
|
5051
|
-
# Get the reasoning model
|
|
4990
|
+
# Get the reasoning model (use copy of main model if not provided)
|
|
5052
4991
|
reasoning_model: Optional[Model] = self.reasoning_model
|
|
5053
|
-
reasoning_model_provided = reasoning_model is not None
|
|
5054
4992
|
if reasoning_model is None and self.model is not None:
|
|
5055
4993
|
from copy import deepcopy
|
|
5056
4994
|
|
|
5057
4995
|
reasoning_model = deepcopy(self.model)
|
|
5058
|
-
if reasoning_model is None:
|
|
5059
|
-
log_warning("Reasoning error. Reasoning model is None, continuing regular session...")
|
|
5060
|
-
return
|
|
5061
4996
|
|
|
5062
|
-
#
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
from agno.reasoning.azure_ai_foundry import is_ai_foundry_reasoning_model
|
|
5066
|
-
from agno.reasoning.deepseek import is_deepseek_reasoning_model
|
|
5067
|
-
from agno.reasoning.gemini import is_gemini_reasoning_model
|
|
5068
|
-
from agno.reasoning.groq import is_groq_reasoning_model
|
|
5069
|
-
from agno.reasoning.helpers import get_reasoning_agent
|
|
5070
|
-
from agno.reasoning.ollama import is_ollama_reasoning_model
|
|
5071
|
-
from agno.reasoning.openai import is_openai_reasoning_model
|
|
5072
|
-
from agno.reasoning.vertexai import is_vertexai_reasoning_model
|
|
5073
|
-
|
|
5074
|
-
reasoning_agent = self.reasoning_agent or get_reasoning_agent(
|
|
4997
|
+
# Create reasoning manager with config
|
|
4998
|
+
manager = ReasoningManager(
|
|
4999
|
+
ReasoningConfig(
|
|
5075
5000
|
reasoning_model=reasoning_model,
|
|
5001
|
+
reasoning_agent=self.reasoning_agent,
|
|
5002
|
+
min_steps=self.reasoning_min_steps,
|
|
5003
|
+
max_steps=self.reasoning_max_steps,
|
|
5004
|
+
tools=self.tools,
|
|
5005
|
+
tool_call_limit=self.tool_call_limit,
|
|
5006
|
+
use_json_mode=self.use_json_mode,
|
|
5007
|
+
telemetry=self.telemetry,
|
|
5008
|
+
debug_mode=self.debug_mode,
|
|
5009
|
+
debug_level=self.debug_level,
|
|
5076
5010
|
session_state=self.session_state,
|
|
5077
5011
|
dependencies=self.dependencies,
|
|
5078
5012
|
metadata=self.metadata,
|
|
5079
5013
|
)
|
|
5080
|
-
|
|
5081
|
-
is_groq = is_groq_reasoning_model(reasoning_model)
|
|
5082
|
-
is_openai = is_openai_reasoning_model(reasoning_model)
|
|
5083
|
-
is_ollama = is_ollama_reasoning_model(reasoning_model)
|
|
5084
|
-
is_ai_foundry = is_ai_foundry_reasoning_model(reasoning_model)
|
|
5085
|
-
is_gemini = is_gemini_reasoning_model(reasoning_model)
|
|
5086
|
-
is_anthropic = is_anthropic_reasoning_model(reasoning_model)
|
|
5087
|
-
is_vertexai = is_vertexai_reasoning_model(reasoning_model)
|
|
5088
|
-
|
|
5089
|
-
if (
|
|
5090
|
-
is_deepseek
|
|
5091
|
-
or is_groq
|
|
5092
|
-
or is_openai
|
|
5093
|
-
or is_ollama
|
|
5094
|
-
or is_ai_foundry
|
|
5095
|
-
or is_gemini
|
|
5096
|
-
or is_anthropic
|
|
5097
|
-
or is_vertexai
|
|
5098
|
-
):
|
|
5099
|
-
reasoning_message: Optional[Message] = None
|
|
5100
|
-
if is_deepseek:
|
|
5101
|
-
from agno.reasoning.deepseek import aget_deepseek_reasoning
|
|
5102
|
-
|
|
5103
|
-
log_debug("Starting DeepSeek Reasoning", center=True, symbol="=")
|
|
5104
|
-
reasoning_message = await aget_deepseek_reasoning(
|
|
5105
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5106
|
-
)
|
|
5107
|
-
elif is_groq:
|
|
5108
|
-
from agno.reasoning.groq import aget_groq_reasoning
|
|
5109
|
-
|
|
5110
|
-
log_debug("Starting Groq Reasoning", center=True, symbol="=")
|
|
5111
|
-
reasoning_message = await aget_groq_reasoning(
|
|
5112
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5113
|
-
)
|
|
5114
|
-
elif is_openai:
|
|
5115
|
-
from agno.reasoning.openai import aget_openai_reasoning
|
|
5116
|
-
|
|
5117
|
-
log_debug("Starting OpenAI Reasoning", center=True, symbol="=")
|
|
5118
|
-
reasoning_message = await aget_openai_reasoning(
|
|
5119
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5120
|
-
)
|
|
5121
|
-
elif is_ollama:
|
|
5122
|
-
from agno.reasoning.ollama import get_ollama_reasoning
|
|
5123
|
-
|
|
5124
|
-
log_debug("Starting Ollama Reasoning", center=True, symbol="=")
|
|
5125
|
-
reasoning_message = get_ollama_reasoning(
|
|
5126
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5127
|
-
)
|
|
5128
|
-
elif is_ai_foundry:
|
|
5129
|
-
from agno.reasoning.azure_ai_foundry import get_ai_foundry_reasoning
|
|
5130
|
-
|
|
5131
|
-
log_debug("Starting Azure AI Foundry Reasoning", center=True, symbol="=")
|
|
5132
|
-
reasoning_message = get_ai_foundry_reasoning(
|
|
5133
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5134
|
-
)
|
|
5135
|
-
elif is_gemini:
|
|
5136
|
-
from agno.reasoning.gemini import aget_gemini_reasoning
|
|
5137
|
-
|
|
5138
|
-
log_debug("Starting Gemini Reasoning", center=True, symbol="=")
|
|
5139
|
-
reasoning_message = await aget_gemini_reasoning(
|
|
5140
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5141
|
-
)
|
|
5142
|
-
elif is_anthropic:
|
|
5143
|
-
from agno.reasoning.anthropic import aget_anthropic_reasoning
|
|
5144
|
-
|
|
5145
|
-
log_debug("Starting Anthropic Claude Reasoning", center=True, symbol="=")
|
|
5146
|
-
reasoning_message = await aget_anthropic_reasoning(
|
|
5147
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5148
|
-
)
|
|
5149
|
-
elif is_vertexai:
|
|
5150
|
-
from agno.reasoning.vertexai import aget_vertexai_reasoning
|
|
5151
|
-
|
|
5152
|
-
log_debug("Starting VertexAI Reasoning", center=True, symbol="=")
|
|
5153
|
-
reasoning_message = await aget_vertexai_reasoning(
|
|
5154
|
-
reasoning_agent=reasoning_agent, messages=run_messages.get_input_messages()
|
|
5155
|
-
)
|
|
5156
|
-
|
|
5157
|
-
if reasoning_message is None:
|
|
5158
|
-
log_warning("Reasoning error. Reasoning response is None, continuing regular session...")
|
|
5159
|
-
return
|
|
5160
|
-
run_messages.messages.append(reasoning_message)
|
|
5161
|
-
# Add reasoning step to the Agent's run_response
|
|
5162
|
-
update_run_output_with_reasoning(
|
|
5163
|
-
run_response=run_response,
|
|
5164
|
-
reasoning_steps=[ReasoningStep(result=reasoning_message.content)],
|
|
5165
|
-
reasoning_agent_messages=[reasoning_message],
|
|
5166
|
-
)
|
|
5167
|
-
if stream_events:
|
|
5168
|
-
yield handle_event( # type: ignore
|
|
5169
|
-
create_team_reasoning_completed_event(
|
|
5170
|
-
from_run_response=run_response,
|
|
5171
|
-
content=ReasoningSteps(reasoning_steps=[ReasoningStep(result=reasoning_message.content)]),
|
|
5172
|
-
content_type=ReasoningSteps.__name__,
|
|
5173
|
-
),
|
|
5174
|
-
run_response,
|
|
5175
|
-
events_to_skip=self.events_to_skip,
|
|
5176
|
-
store_events=self.store_events,
|
|
5177
|
-
)
|
|
5178
|
-
else:
|
|
5179
|
-
log_info(
|
|
5180
|
-
f"Reasoning model: {reasoning_model.__class__.__name__} is not a native reasoning model, defaulting to manual Chain-of-Thought reasoning"
|
|
5181
|
-
)
|
|
5182
|
-
use_default_reasoning = True
|
|
5183
|
-
# If no reasoning model is provided, use default reasoning
|
|
5184
|
-
else:
|
|
5185
|
-
use_default_reasoning = True
|
|
5186
|
-
|
|
5187
|
-
if use_default_reasoning:
|
|
5188
|
-
from agno.reasoning.default import get_default_reasoning_agent
|
|
5189
|
-
from agno.reasoning.helpers import get_next_action, update_messages_with_reasoning
|
|
5190
|
-
|
|
5191
|
-
# Get default reasoning agent
|
|
5192
|
-
use_json_mode: bool = self.use_json_mode
|
|
5193
|
-
reasoning_agent: Optional[Agent] = self.reasoning_agent # type: ignore
|
|
5194
|
-
if reasoning_agent is None:
|
|
5195
|
-
reasoning_agent = get_default_reasoning_agent( # type: ignore
|
|
5196
|
-
reasoning_model=reasoning_model,
|
|
5197
|
-
min_steps=self.reasoning_min_steps,
|
|
5198
|
-
max_steps=self.reasoning_max_steps,
|
|
5199
|
-
telemetry=self.telemetry,
|
|
5200
|
-
debug_mode=self.debug_mode,
|
|
5201
|
-
debug_level=self.debug_level,
|
|
5202
|
-
use_json_mode=use_json_mode,
|
|
5203
|
-
session_state=self.session_state,
|
|
5204
|
-
dependencies=self.dependencies,
|
|
5205
|
-
metadata=self.metadata,
|
|
5206
|
-
)
|
|
5207
|
-
|
|
5208
|
-
# Validate reasoning agent
|
|
5209
|
-
if reasoning_agent is None:
|
|
5210
|
-
log_warning("Reasoning error. Reasoning agent is None, continuing regular session...")
|
|
5211
|
-
return
|
|
5212
|
-
# Ensure the reasoning agent response model is ReasoningSteps
|
|
5213
|
-
if (
|
|
5214
|
-
reasoning_agent.output_schema is not None
|
|
5215
|
-
and not isinstance(reasoning_agent.output_schema, type)
|
|
5216
|
-
and not issubclass(reasoning_agent.output_schema, ReasoningSteps)
|
|
5217
|
-
):
|
|
5218
|
-
log_warning("Reasoning agent response model should be `ReasoningSteps`, continuing regular session...")
|
|
5219
|
-
return
|
|
5220
|
-
|
|
5221
|
-
# Ensure the reasoning model and agent do not show tool calls
|
|
5222
|
-
|
|
5223
|
-
step_count = 1
|
|
5224
|
-
next_action = NextAction.CONTINUE
|
|
5225
|
-
reasoning_messages: List[Message] = []
|
|
5226
|
-
all_reasoning_steps: List[ReasoningStep] = []
|
|
5227
|
-
log_debug("Starting Reasoning", center=True, symbol="=")
|
|
5228
|
-
while next_action == NextAction.CONTINUE and step_count < self.reasoning_max_steps:
|
|
5229
|
-
log_debug(f"Step {step_count}", center=True, symbol="-")
|
|
5230
|
-
step_count += 1
|
|
5231
|
-
try:
|
|
5232
|
-
# Run the reasoning agent
|
|
5233
|
-
reasoning_agent_response: RunOutput = await reasoning_agent.arun( # type: ignore
|
|
5234
|
-
input=run_messages.get_input_messages()
|
|
5235
|
-
)
|
|
5236
|
-
if reasoning_agent_response.content is None or reasoning_agent_response.messages is None:
|
|
5237
|
-
log_warning("Reasoning error. Reasoning response is empty, continuing regular session...")
|
|
5238
|
-
break
|
|
5239
|
-
|
|
5240
|
-
if isinstance(reasoning_agent_response.content, str):
|
|
5241
|
-
log_warning(
|
|
5242
|
-
"Reasoning error. Content is a string, not structured output. Continuing regular session..."
|
|
5243
|
-
)
|
|
5244
|
-
break
|
|
5245
|
-
|
|
5246
|
-
if reasoning_agent_response.content.reasoning_steps is None:
|
|
5247
|
-
log_warning("Reasoning error. Reasoning steps are empty, continuing regular session...")
|
|
5248
|
-
break
|
|
5249
|
-
|
|
5250
|
-
reasoning_steps: List[ReasoningStep] = reasoning_agent_response.content.reasoning_steps
|
|
5251
|
-
all_reasoning_steps.extend(reasoning_steps)
|
|
5252
|
-
# Yield reasoning steps
|
|
5253
|
-
if stream_events:
|
|
5254
|
-
for reasoning_step in reasoning_steps:
|
|
5255
|
-
updated_reasoning_content = self._format_reasoning_step_content(
|
|
5256
|
-
run_response, reasoning_step
|
|
5257
|
-
)
|
|
5014
|
+
)
|
|
5258
5015
|
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
reasoning_step=reasoning_step,
|
|
5263
|
-
reasoning_content=updated_reasoning_content,
|
|
5264
|
-
),
|
|
5265
|
-
run_response,
|
|
5266
|
-
events_to_skip=self.events_to_skip,
|
|
5267
|
-
store_events=self.store_events,
|
|
5268
|
-
)
|
|
5016
|
+
# Use the unified reason() method and convert events
|
|
5017
|
+
for event in manager.reason(run_messages, stream=stream_events):
|
|
5018
|
+
yield from self._handle_reasoning_event(event, run_response, stream_events)
|
|
5269
5019
|
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5020
|
+
async def _areason(
|
|
5021
|
+
self,
|
|
5022
|
+
run_response: TeamRunOutput,
|
|
5023
|
+
run_messages: RunMessages,
|
|
5024
|
+
stream_events: bool,
|
|
5025
|
+
) -> AsyncIterator[TeamRunOutputEvent]:
|
|
5026
|
+
"""
|
|
5027
|
+
Run reasoning asynchronously using the ReasoningManager.
|
|
5277
5028
|
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
reasoning_agent_messages=reasoning_agent_response.messages,
|
|
5283
|
-
)
|
|
5029
|
+
Handles both native reasoning models (DeepSeek, Anthropic, etc.) and
|
|
5030
|
+
default Chain-of-Thought reasoning with a clean, unified interface.
|
|
5031
|
+
"""
|
|
5032
|
+
from agno.reasoning.manager import ReasoningConfig, ReasoningManager
|
|
5284
5033
|
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
except Exception as e:
|
|
5290
|
-
log_error(f"Reasoning error: {e}")
|
|
5291
|
-
break
|
|
5034
|
+
# Get the reasoning model (use copy of main model if not provided)
|
|
5035
|
+
reasoning_model: Optional[Model] = self.reasoning_model
|
|
5036
|
+
if reasoning_model is None and self.model is not None:
|
|
5037
|
+
from copy import deepcopy
|
|
5292
5038
|
|
|
5293
|
-
|
|
5294
|
-
log_debug("Reasoning finished", center=True, symbol="=")
|
|
5039
|
+
reasoning_model = deepcopy(self.model)
|
|
5295
5040
|
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5041
|
+
# Create reasoning manager with config
|
|
5042
|
+
manager = ReasoningManager(
|
|
5043
|
+
ReasoningConfig(
|
|
5044
|
+
reasoning_model=reasoning_model,
|
|
5045
|
+
reasoning_agent=self.reasoning_agent,
|
|
5046
|
+
min_steps=self.reasoning_min_steps,
|
|
5047
|
+
max_steps=self.reasoning_max_steps,
|
|
5048
|
+
tools=self.tools,
|
|
5049
|
+
tool_call_limit=self.tool_call_limit,
|
|
5050
|
+
use_json_mode=self.use_json_mode,
|
|
5051
|
+
telemetry=self.telemetry,
|
|
5052
|
+
debug_mode=self.debug_mode,
|
|
5053
|
+
debug_level=self.debug_level,
|
|
5054
|
+
session_state=self.session_state,
|
|
5055
|
+
dependencies=self.dependencies,
|
|
5056
|
+
metadata=self.metadata,
|
|
5300
5057
|
)
|
|
5058
|
+
)
|
|
5301
5059
|
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
from_run_response=run_response,
|
|
5307
|
-
content=ReasoningSteps(reasoning_steps=all_reasoning_steps),
|
|
5308
|
-
content_type=ReasoningSteps.__name__,
|
|
5309
|
-
),
|
|
5310
|
-
run_response,
|
|
5311
|
-
events_to_skip=self.events_to_skip,
|
|
5312
|
-
store_events=self.store_events,
|
|
5313
|
-
)
|
|
5060
|
+
# Use the unified areason() method and convert events
|
|
5061
|
+
async for event in manager.areason(run_messages, stream=stream_events):
|
|
5062
|
+
for output_event in self._handle_reasoning_event(event, run_response, stream_events):
|
|
5063
|
+
yield output_event
|
|
5314
5064
|
|
|
5315
5065
|
def _resolve_run_dependencies(self, run_context: RunContext) -> None:
|
|
5316
5066
|
from inspect import signature
|
|
@@ -7021,7 +6771,7 @@ class Team:
|
|
|
7021
6771
|
log_error(f"Failed to convert sanitized context to JSON: {e}")
|
|
7022
6772
|
return str(context)
|
|
7023
6773
|
|
|
7024
|
-
def _get_json_output_prompt(self, output_schema: Optional[Type[BaseModel]] = None) -> str:
|
|
6774
|
+
def _get_json_output_prompt(self, output_schema: Optional[Union[Type[BaseModel], Dict[str, Any]]] = None) -> str:
|
|
7025
6775
|
"""Return the JSON output prompt for the Agent.
|
|
7026
6776
|
|
|
7027
6777
|
This is added to the system prompt when the output_schema is set and structured_outputs is False.
|
|
@@ -7038,7 +6788,11 @@ class Team:
|
|
|
7038
6788
|
json_output_prompt += "\n<json_fields>"
|
|
7039
6789
|
json_output_prompt += f"\n{json.dumps(output_schema)}"
|
|
7040
6790
|
json_output_prompt += "\n</json_fields>"
|
|
7041
|
-
elif
|
|
6791
|
+
elif isinstance(output_schema, dict):
|
|
6792
|
+
json_output_prompt += "\n<json_fields>"
|
|
6793
|
+
json_output_prompt += f"\n{json.dumps(output_schema)}"
|
|
6794
|
+
json_output_prompt += "\n</json_fields>"
|
|
6795
|
+
elif isinstance(output_schema, type) and issubclass(output_schema, BaseModel):
|
|
7042
6796
|
json_schema = output_schema.model_json_schema()
|
|
7043
6797
|
if json_schema is not None:
|
|
7044
6798
|
response_model_properties = {}
|
|
@@ -7552,7 +7306,7 @@ class Team:
|
|
|
7552
7306
|
member_agent_run_response.parent_run_id = run_response.run_id # type: ignore
|
|
7553
7307
|
|
|
7554
7308
|
# Update the top-level team run_response tool call to have the run_id of the member run
|
|
7555
|
-
if run_response.tools is not None:
|
|
7309
|
+
if run_response.tools is not None and member_agent_run_response is not None:
|
|
7556
7310
|
for tool in run_response.tools:
|
|
7557
7311
|
if tool.tool_name and tool.tool_name.lower() == "delegate_task_to_member":
|
|
7558
7312
|
tool.child_run_id = member_agent_run_response.run_id # type: ignore
|
|
@@ -7782,9 +7536,9 @@ class Team:
|
|
|
7782
7536
|
check_if_run_cancelled(member_agent_run_response_event)
|
|
7783
7537
|
|
|
7784
7538
|
# Yield the member event directly
|
|
7785
|
-
member_agent_run_response_event.parent_run_id = (
|
|
7786
|
-
|
|
7787
|
-
)
|
|
7539
|
+
member_agent_run_response_event.parent_run_id = getattr(
|
|
7540
|
+
member_agent_run_response_event, "parent_run_id", None
|
|
7541
|
+
) or (run_response.run_id if run_response is not None else None)
|
|
7788
7542
|
yield member_agent_run_response_event # type: ignore
|
|
7789
7543
|
else:
|
|
7790
7544
|
member_agent_run_response = await member_agent.arun( # type: ignore
|
|
@@ -7895,7 +7649,8 @@ class Team:
|
|
|
7895
7649
|
|
|
7896
7650
|
# Yield the member event directly
|
|
7897
7651
|
member_agent_run_response_chunk.parent_run_id = (
|
|
7898
|
-
member_agent_run_response_chunk.parent_run_id
|
|
7652
|
+
member_agent_run_response_chunk.parent_run_id
|
|
7653
|
+
or (run_response.run_id if run_response is not None else None)
|
|
7899
7654
|
)
|
|
7900
7655
|
yield member_agent_run_response_chunk # type: ignore
|
|
7901
7656
|
|
|
@@ -8005,7 +7760,8 @@ class Team:
|
|
|
8005
7760
|
|
|
8006
7761
|
check_if_run_cancelled(member_agent_run_output_event)
|
|
8007
7762
|
member_agent_run_output_event.parent_run_id = (
|
|
8008
|
-
member_agent_run_output_event.parent_run_id
|
|
7763
|
+
member_agent_run_output_event.parent_run_id
|
|
7764
|
+
or (run_response.run_id if run_response is not None else None)
|
|
8009
7765
|
)
|
|
8010
7766
|
await queue.put(member_agent_run_output_event)
|
|
8011
7767
|
finally:
|