agno 2.3.19__py3-none-any.whl → 2.3.20__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 +2466 -2048
- agno/db/dynamo/utils.py +26 -3
- agno/db/firestore/utils.py +25 -10
- agno/db/gcs_json/utils.py +14 -2
- agno/db/in_memory/utils.py +14 -2
- agno/db/json/utils.py +14 -2
- agno/db/mysql/utils.py +13 -3
- agno/db/postgres/utils.py +13 -3
- agno/db/redis/utils.py +26 -10
- agno/db/schemas/memory.py +15 -19
- agno/db/singlestore/utils.py +13 -3
- agno/db/sqlite/utils.py +15 -3
- agno/db/utils.py +22 -0
- agno/models/litellm/chat.py +6 -0
- agno/os/routers/knowledge/schemas.py +1 -1
- agno/os/routers/memory/schemas.py +14 -1
- agno/os/routers/metrics/schemas.py +1 -1
- agno/os/schema.py +11 -9
- agno/run/__init__.py +2 -4
- agno/run/cancel.py +65 -52
- agno/run/cancellation_management/__init__.py +9 -0
- agno/run/cancellation_management/base.py +78 -0
- agno/run/cancellation_management/in_memory_cancellation_manager.py +100 -0
- agno/run/cancellation_management/redis_cancellation_manager.py +236 -0
- agno/team/team.py +1217 -1136
- agno/utils/response.py +1 -13
- agno/vectordb/weaviate/__init__.py +1 -1
- agno/workflow/workflow.py +23 -16
- {agno-2.3.19.dist-info → agno-2.3.20.dist-info}/METADATA +3 -2
- {agno-2.3.19.dist-info → agno-2.3.20.dist-info}/RECORD +33 -29
- {agno-2.3.19.dist-info → agno-2.3.20.dist-info}/WHEEL +0 -0
- {agno-2.3.19.dist-info → agno-2.3.20.dist-info}/licenses/LICENSE +0 -0
- {agno-2.3.19.dist-info → agno-2.3.20.dist-info}/top_level.txt +0 -0
agno/team/team.py
CHANGED
|
@@ -6,6 +6,7 @@ import json
|
|
|
6
6
|
import time
|
|
7
7
|
import warnings
|
|
8
8
|
from collections import ChainMap, deque
|
|
9
|
+
from concurrent.futures import Future
|
|
9
10
|
from copy import copy
|
|
10
11
|
from dataclasses import dataclass
|
|
11
12
|
from os import getenv
|
|
@@ -56,13 +57,16 @@ from agno.reasoning.step import NextAction, ReasoningStep, ReasoningSteps
|
|
|
56
57
|
from agno.run import RunContext, RunStatus
|
|
57
58
|
from agno.run.agent import RunEvent, RunOutput, RunOutputEvent
|
|
58
59
|
from agno.run.cancel import (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
acleanup_run,
|
|
61
|
+
araise_if_cancelled,
|
|
62
|
+
aregister_run,
|
|
62
63
|
cleanup_run,
|
|
63
64
|
raise_if_cancelled,
|
|
64
65
|
register_run,
|
|
65
66
|
)
|
|
67
|
+
from agno.run.cancel import (
|
|
68
|
+
cancel_run as cancel_run_global,
|
|
69
|
+
)
|
|
66
70
|
from agno.run.messages import RunMessages
|
|
67
71
|
from agno.run.team import (
|
|
68
72
|
TeamRunEvent,
|
|
@@ -169,7 +173,6 @@ from agno.utils.reasoning import (
|
|
|
169
173
|
)
|
|
170
174
|
from agno.utils.response import (
|
|
171
175
|
check_if_run_cancelled,
|
|
172
|
-
generator_wrapper,
|
|
173
176
|
)
|
|
174
177
|
from agno.utils.safe_formatter import SafeFormatter
|
|
175
178
|
from agno.utils.string import generate_id_from_name, parse_response_dict_str, parse_response_model_str
|
|
@@ -1040,7 +1043,7 @@ class Team:
|
|
|
1040
1043
|
for tool in self.tools:
|
|
1041
1044
|
if (
|
|
1042
1045
|
hasattr(tool, "requires_connect")
|
|
1043
|
-
and tool.requires_connect
|
|
1046
|
+
and tool.requires_connect # type: ignore
|
|
1044
1047
|
and hasattr(tool, "connect")
|
|
1045
1048
|
and tool not in self._connectable_tools_initialized_on_run
|
|
1046
1049
|
):
|
|
@@ -1456,175 +1459,231 @@ class Team:
|
|
|
1456
1459
|
12. Create session summary
|
|
1457
1460
|
13. Cleanup and store (scrub, stop timer, add to session, calculate metrics, save session)
|
|
1458
1461
|
"""
|
|
1459
|
-
|
|
1460
|
-
run_input = cast(TeamRunInput, run_response.input)
|
|
1461
|
-
self.model = cast(Model, self.model)
|
|
1462
|
-
if self.pre_hooks is not None:
|
|
1463
|
-
# Can modify the run input
|
|
1464
|
-
pre_hook_iterator = self._execute_pre_hooks(
|
|
1465
|
-
hooks=self.pre_hooks, # type: ignore
|
|
1466
|
-
run_response=run_response,
|
|
1467
|
-
run_input=run_input,
|
|
1468
|
-
run_context=run_context,
|
|
1469
|
-
session=session,
|
|
1470
|
-
user_id=user_id,
|
|
1471
|
-
debug_mode=debug_mode,
|
|
1472
|
-
background_tasks=background_tasks,
|
|
1473
|
-
**kwargs,
|
|
1474
|
-
)
|
|
1475
|
-
# Consume the generator without yielding
|
|
1476
|
-
deque(pre_hook_iterator, maxlen=0)
|
|
1462
|
+
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
1477
1463
|
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1464
|
+
memory_future = None
|
|
1465
|
+
try:
|
|
1466
|
+
# Set up retry logic
|
|
1467
|
+
num_attempts = self.retries + 1
|
|
1468
|
+
for attempt in range(num_attempts):
|
|
1469
|
+
try:
|
|
1470
|
+
# 1. Execute pre-hooks
|
|
1471
|
+
run_input = cast(TeamRunInput, run_response.input)
|
|
1472
|
+
self.model = cast(Model, self.model)
|
|
1473
|
+
if self.pre_hooks is not None:
|
|
1474
|
+
# Can modify the run input
|
|
1475
|
+
pre_hook_iterator = self._execute_pre_hooks(
|
|
1476
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1477
|
+
run_response=run_response,
|
|
1478
|
+
run_input=run_input,
|
|
1479
|
+
run_context=run_context,
|
|
1480
|
+
session=session,
|
|
1481
|
+
user_id=user_id,
|
|
1482
|
+
debug_mode=debug_mode,
|
|
1483
|
+
background_tasks=background_tasks,
|
|
1484
|
+
**kwargs,
|
|
1485
|
+
)
|
|
1486
|
+
# Consume the generator without yielding
|
|
1487
|
+
deque(pre_hook_iterator, maxlen=0)
|
|
1481
1488
|
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
run_context=run_context,
|
|
1486
|
-
team_run_context=team_run_context,
|
|
1487
|
-
session=session,
|
|
1488
|
-
user_id=user_id,
|
|
1489
|
-
async_mode=False,
|
|
1490
|
-
input_message=run_input.input_content,
|
|
1491
|
-
images=run_input.images,
|
|
1492
|
-
videos=run_input.videos,
|
|
1493
|
-
audio=run_input.audios,
|
|
1494
|
-
files=run_input.files,
|
|
1495
|
-
debug_mode=debug_mode,
|
|
1496
|
-
add_history_to_context=add_history_to_context,
|
|
1497
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
1498
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
1499
|
-
stream=False,
|
|
1500
|
-
stream_events=False,
|
|
1501
|
-
)
|
|
1489
|
+
# 2. Determine tools for model
|
|
1490
|
+
# Initialize team run context
|
|
1491
|
+
team_run_context: Dict[str, Any] = {}
|
|
1502
1492
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1493
|
+
_tools = self._determine_tools_for_model(
|
|
1494
|
+
model=self.model,
|
|
1495
|
+
run_response=run_response,
|
|
1496
|
+
run_context=run_context,
|
|
1497
|
+
team_run_context=team_run_context,
|
|
1498
|
+
session=session,
|
|
1499
|
+
user_id=user_id,
|
|
1500
|
+
async_mode=False,
|
|
1501
|
+
input_message=run_input.input_content,
|
|
1502
|
+
images=run_input.images,
|
|
1503
|
+
videos=run_input.videos,
|
|
1504
|
+
audio=run_input.audios,
|
|
1505
|
+
files=run_input.files,
|
|
1506
|
+
debug_mode=debug_mode,
|
|
1507
|
+
add_history_to_context=add_history_to_context,
|
|
1508
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1509
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1510
|
+
stream=False,
|
|
1511
|
+
stream_events=False,
|
|
1512
|
+
)
|
|
1522
1513
|
|
|
1523
|
-
|
|
1514
|
+
# 3. Prepare run messages
|
|
1515
|
+
run_messages: RunMessages = self._get_run_messages(
|
|
1516
|
+
run_response=run_response,
|
|
1517
|
+
session=session,
|
|
1518
|
+
run_context=run_context,
|
|
1519
|
+
user_id=user_id,
|
|
1520
|
+
input_message=run_input.input_content,
|
|
1521
|
+
audio=run_input.audios,
|
|
1522
|
+
images=run_input.images,
|
|
1523
|
+
videos=run_input.videos,
|
|
1524
|
+
files=run_input.files,
|
|
1525
|
+
add_history_to_context=add_history_to_context,
|
|
1526
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1527
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1528
|
+
tools=_tools,
|
|
1529
|
+
**kwargs,
|
|
1530
|
+
)
|
|
1531
|
+
if len(run_messages.messages) == 0:
|
|
1532
|
+
log_error("No messages to be sent to the model.")
|
|
1524
1533
|
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
and not self.enable_agentic_memory
|
|
1532
|
-
):
|
|
1533
|
-
log_debug("Starting memory creation in background thread.")
|
|
1534
|
-
memory_future = self.background_executor.submit(
|
|
1535
|
-
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1536
|
-
)
|
|
1534
|
+
# 4. Start memory creation in background thread
|
|
1535
|
+
memory_future = self._start_memory_future(
|
|
1536
|
+
run_messages=run_messages,
|
|
1537
|
+
user_id=user_id,
|
|
1538
|
+
existing_future=None,
|
|
1539
|
+
)
|
|
1537
1540
|
|
|
1538
|
-
|
|
1541
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1539
1542
|
|
|
1540
|
-
|
|
1541
|
-
|
|
1543
|
+
# 5. Reason about the task if reasoning is enabled
|
|
1544
|
+
self._handle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
1542
1545
|
|
|
1543
|
-
|
|
1544
|
-
|
|
1546
|
+
# Check for cancellation before model call
|
|
1547
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1545
1548
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1549
|
+
# 6. Get the model response for the team leader
|
|
1550
|
+
self.model = cast(Model, self.model)
|
|
1551
|
+
model_response: ModelResponse = self.model.response(
|
|
1552
|
+
messages=run_messages.messages,
|
|
1553
|
+
response_format=response_format,
|
|
1554
|
+
tools=_tools,
|
|
1555
|
+
tool_choice=self.tool_choice,
|
|
1556
|
+
tool_call_limit=self.tool_call_limit,
|
|
1557
|
+
send_media_to_model=self.send_media_to_model,
|
|
1558
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
1559
|
+
)
|
|
1557
1560
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1561
|
+
# Check for cancellation after model call
|
|
1562
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1560
1563
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1564
|
+
# If an output model is provided, generate output using the output model
|
|
1565
|
+
self._parse_response_with_output_model(model_response, run_messages)
|
|
1563
1566
|
|
|
1564
|
-
|
|
1565
|
-
|
|
1567
|
+
# If a parser model is provided, structure the response separately
|
|
1568
|
+
self._parse_response_with_parser_model(model_response, run_messages, run_context=run_context)
|
|
1566
1569
|
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1570
|
+
# 7. Update TeamRunOutput with the model response
|
|
1571
|
+
self._update_run_response(
|
|
1572
|
+
model_response=model_response,
|
|
1573
|
+
run_response=run_response,
|
|
1574
|
+
run_messages=run_messages,
|
|
1575
|
+
run_context=run_context,
|
|
1576
|
+
)
|
|
1574
1577
|
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
+
# 8. Store media if enabled
|
|
1579
|
+
if self.store_media:
|
|
1580
|
+
store_media_util(run_response, model_response)
|
|
1578
1581
|
|
|
1579
|
-
|
|
1580
|
-
|
|
1582
|
+
# 9. Convert response to structured format
|
|
1583
|
+
self._convert_response_to_structured_format(run_response=run_response, run_context=run_context)
|
|
1581
1584
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1585
|
+
# 10. Execute post-hooks after output is generated but before response is returned
|
|
1586
|
+
if self.post_hooks is not None:
|
|
1587
|
+
iterator = self._execute_post_hooks(
|
|
1588
|
+
hooks=self.post_hooks, # type: ignore
|
|
1589
|
+
run_output=run_response,
|
|
1590
|
+
run_context=run_context,
|
|
1591
|
+
session=session,
|
|
1592
|
+
user_id=user_id,
|
|
1593
|
+
debug_mode=debug_mode,
|
|
1594
|
+
background_tasks=background_tasks,
|
|
1595
|
+
**kwargs,
|
|
1596
|
+
)
|
|
1597
|
+
deque(iterator, maxlen=0)
|
|
1598
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1596
1599
|
|
|
1597
|
-
|
|
1598
|
-
|
|
1600
|
+
# 11. Wait for background memory creation
|
|
1601
|
+
wait_for_open_threads(memory_future=memory_future) # type: ignore
|
|
1599
1602
|
|
|
1600
|
-
|
|
1603
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1601
1604
|
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1605
|
+
# 12. Create session summary
|
|
1606
|
+
if self.session_summary_manager is not None:
|
|
1607
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
1608
|
+
session.upsert_run(run_response=run_response)
|
|
1609
|
+
try:
|
|
1610
|
+
self.session_summary_manager.create_session_summary(session=session)
|
|
1611
|
+
except Exception as e:
|
|
1612
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
1610
1613
|
|
|
1611
|
-
|
|
1614
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1615
|
+
|
|
1616
|
+
# Set the run status to completed
|
|
1617
|
+
run_response.status = RunStatus.completed
|
|
1618
|
+
|
|
1619
|
+
# 13. Cleanup and store the run response
|
|
1620
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1621
|
+
|
|
1622
|
+
# Log Team Telemetry
|
|
1623
|
+
self._log_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
|
|
1624
|
+
|
|
1625
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
1626
|
+
|
|
1627
|
+
return run_response
|
|
1628
|
+
except RunCancelledException as e:
|
|
1629
|
+
# Handle run cancellation during streaming
|
|
1630
|
+
log_info(f"Team run {run_response.run_id} was cancelled during streaming")
|
|
1631
|
+
run_response.status = RunStatus.cancelled
|
|
1632
|
+
run_response.content = str(e)
|
|
1633
|
+
|
|
1634
|
+
# Cleanup and store the run response and session
|
|
1635
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1636
|
+
|
|
1637
|
+
return run_response
|
|
1638
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
1639
|
+
run_response.status = RunStatus.error
|
|
1640
|
+
|
|
1641
|
+
if run_response.content is None:
|
|
1642
|
+
run_response.content = str(e)
|
|
1643
|
+
|
|
1644
|
+
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
1645
|
+
|
|
1646
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1647
|
+
|
|
1648
|
+
return run_response
|
|
1649
|
+
except KeyboardInterrupt:
|
|
1650
|
+
run_response = cast(TeamRunOutput, run_response)
|
|
1651
|
+
run_response.status = RunStatus.cancelled
|
|
1652
|
+
run_response.content = "Operation cancelled by user"
|
|
1653
|
+
return run_response
|
|
1654
|
+
except Exception as e:
|
|
1655
|
+
if attempt < num_attempts - 1:
|
|
1656
|
+
# Calculate delay with exponential backoff if enabled
|
|
1657
|
+
if self.exponential_backoff:
|
|
1658
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
1659
|
+
else:
|
|
1660
|
+
delay = self.delay_between_retries
|
|
1612
1661
|
|
|
1613
|
-
|
|
1614
|
-
|
|
1662
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
1663
|
+
time.sleep(delay)
|
|
1664
|
+
continue
|
|
1615
1665
|
|
|
1616
|
-
|
|
1617
|
-
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1666
|
+
run_response.status = RunStatus.error
|
|
1618
1667
|
|
|
1619
|
-
|
|
1620
|
-
|
|
1668
|
+
# If the content is None, set it to the error message
|
|
1669
|
+
if run_response.content is None:
|
|
1670
|
+
run_response.content = str(e)
|
|
1621
1671
|
|
|
1622
|
-
|
|
1672
|
+
log_error(f"Error in Agent run: {str(e)}")
|
|
1623
1673
|
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
cleanup_run(run_response.run_id) # type: ignore
|
|
1674
|
+
# Cleanup and store the run response and session
|
|
1675
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1627
1676
|
|
|
1677
|
+
return run_response
|
|
1678
|
+
finally:
|
|
1679
|
+
# Cancel background futures on error (wait_for_open_threads handles waiting on success)
|
|
1680
|
+
if memory_future is not None and not memory_future.done():
|
|
1681
|
+
memory_future.cancel()
|
|
1682
|
+
|
|
1683
|
+
# Always disconnect connectable tools
|
|
1684
|
+
self._disconnect_connectable_tools()
|
|
1685
|
+
# Always clean up the run tracking
|
|
1686
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
1628
1687
|
return run_response
|
|
1629
1688
|
|
|
1630
1689
|
def _run_stream(
|
|
@@ -1656,252 +1715,328 @@ class Team:
|
|
|
1656
1715
|
9. Create session summary
|
|
1657
1716
|
10. Cleanup and store (scrub, add to session, calculate metrics, save session)
|
|
1658
1717
|
"""
|
|
1659
|
-
|
|
1660
|
-
# 1. Execute pre-hooks
|
|
1661
|
-
run_input = cast(TeamRunInput, run_response.input)
|
|
1662
|
-
self.model = cast(Model, self.model)
|
|
1663
|
-
if self.pre_hooks is not None:
|
|
1664
|
-
# Can modify the run input
|
|
1665
|
-
pre_hook_iterator = self._execute_pre_hooks(
|
|
1666
|
-
hooks=self.pre_hooks, # type: ignore
|
|
1667
|
-
run_response=run_response,
|
|
1668
|
-
run_context=run_context,
|
|
1669
|
-
run_input=run_input,
|
|
1670
|
-
session=session,
|
|
1671
|
-
user_id=user_id,
|
|
1672
|
-
debug_mode=debug_mode,
|
|
1673
|
-
stream_events=stream_events,
|
|
1674
|
-
background_tasks=background_tasks,
|
|
1675
|
-
**kwargs,
|
|
1676
|
-
)
|
|
1677
|
-
for pre_hook_event in pre_hook_iterator:
|
|
1678
|
-
yield pre_hook_event
|
|
1679
|
-
|
|
1680
|
-
# 2. Determine tools for model
|
|
1681
|
-
# Initialize team run context
|
|
1682
|
-
team_run_context: Dict[str, Any] = {}
|
|
1683
|
-
|
|
1684
|
-
_tools = self._determine_tools_for_model(
|
|
1685
|
-
model=self.model,
|
|
1686
|
-
run_response=run_response,
|
|
1687
|
-
run_context=run_context,
|
|
1688
|
-
team_run_context=team_run_context,
|
|
1689
|
-
session=session,
|
|
1690
|
-
user_id=user_id,
|
|
1691
|
-
async_mode=False,
|
|
1692
|
-
input_message=run_input.input_content,
|
|
1693
|
-
images=run_input.images,
|
|
1694
|
-
videos=run_input.videos,
|
|
1695
|
-
audio=run_input.audios,
|
|
1696
|
-
files=run_input.files,
|
|
1697
|
-
debug_mode=debug_mode,
|
|
1698
|
-
add_history_to_context=add_history_to_context,
|
|
1699
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
1700
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
1701
|
-
stream=True,
|
|
1702
|
-
stream_events=stream_events,
|
|
1703
|
-
)
|
|
1704
|
-
|
|
1705
|
-
# 3. Prepare run messages
|
|
1706
|
-
run_messages: RunMessages = self._get_run_messages(
|
|
1707
|
-
run_response=run_response,
|
|
1708
|
-
run_context=run_context,
|
|
1709
|
-
session=session,
|
|
1710
|
-
user_id=user_id,
|
|
1711
|
-
input_message=run_input.input_content,
|
|
1712
|
-
audio=run_input.audios,
|
|
1713
|
-
images=run_input.images,
|
|
1714
|
-
videos=run_input.videos,
|
|
1715
|
-
files=run_input.files,
|
|
1716
|
-
add_history_to_context=add_history_to_context,
|
|
1717
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
1718
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
1719
|
-
tools=_tools,
|
|
1720
|
-
**kwargs,
|
|
1721
|
-
)
|
|
1722
|
-
if len(run_messages.messages) == 0:
|
|
1723
|
-
log_error("No messages to be sent to the model.")
|
|
1724
|
-
|
|
1725
1718
|
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
1726
1719
|
|
|
1727
|
-
# 4. Start memory creation in background thread
|
|
1728
1720
|
memory_future = None
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
log_debug("Starting memory creation in background thread.")
|
|
1736
|
-
memory_future = self.background_executor.submit(
|
|
1737
|
-
self._make_memories, run_messages=run_messages, user_id=user_id
|
|
1738
|
-
)
|
|
1721
|
+
try:
|
|
1722
|
+
# Set up retry logic
|
|
1723
|
+
num_attempts = self.retries + 1
|
|
1724
|
+
for attempt in range(num_attempts):
|
|
1725
|
+
if num_attempts > 1:
|
|
1726
|
+
log_debug(f"Retrying Team run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
1739
1727
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1728
|
+
try:
|
|
1729
|
+
# 1. Execute pre-hooks
|
|
1730
|
+
run_input = cast(TeamRunInput, run_response.input)
|
|
1731
|
+
self.model = cast(Model, self.model)
|
|
1732
|
+
if self.pre_hooks is not None:
|
|
1733
|
+
# Can modify the run input
|
|
1734
|
+
pre_hook_iterator = self._execute_pre_hooks(
|
|
1735
|
+
hooks=self.pre_hooks, # type: ignore
|
|
1736
|
+
run_response=run_response,
|
|
1737
|
+
run_context=run_context,
|
|
1738
|
+
run_input=run_input,
|
|
1739
|
+
session=session,
|
|
1740
|
+
user_id=user_id,
|
|
1741
|
+
debug_mode=debug_mode,
|
|
1742
|
+
stream_events=stream_events,
|
|
1743
|
+
background_tasks=background_tasks,
|
|
1744
|
+
**kwargs,
|
|
1745
|
+
)
|
|
1746
|
+
for pre_hook_event in pre_hook_iterator:
|
|
1747
|
+
yield pre_hook_event
|
|
1748
1748
|
|
|
1749
|
-
|
|
1749
|
+
# 2. Determine tools for model
|
|
1750
|
+
# Initialize team run context
|
|
1751
|
+
team_run_context: Dict[str, Any] = {}
|
|
1750
1752
|
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1753
|
+
_tools = self._determine_tools_for_model(
|
|
1754
|
+
model=self.model,
|
|
1755
|
+
run_response=run_response,
|
|
1756
|
+
run_context=run_context,
|
|
1757
|
+
team_run_context=team_run_context,
|
|
1758
|
+
session=session,
|
|
1759
|
+
user_id=user_id,
|
|
1760
|
+
async_mode=False,
|
|
1761
|
+
input_message=run_input.input_content,
|
|
1762
|
+
images=run_input.images,
|
|
1763
|
+
videos=run_input.videos,
|
|
1764
|
+
audio=run_input.audios,
|
|
1765
|
+
files=run_input.files,
|
|
1766
|
+
debug_mode=debug_mode,
|
|
1767
|
+
add_history_to_context=add_history_to_context,
|
|
1768
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1769
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1770
|
+
stream=True,
|
|
1771
|
+
stream_events=stream_events,
|
|
1772
|
+
)
|
|
1757
1773
|
|
|
1758
|
-
|
|
1759
|
-
|
|
1774
|
+
# 3. Prepare run messages
|
|
1775
|
+
run_messages: RunMessages = self._get_run_messages(
|
|
1776
|
+
run_response=run_response,
|
|
1777
|
+
run_context=run_context,
|
|
1778
|
+
session=session,
|
|
1779
|
+
user_id=user_id,
|
|
1780
|
+
input_message=run_input.input_content,
|
|
1781
|
+
audio=run_input.audios,
|
|
1782
|
+
images=run_input.images,
|
|
1783
|
+
videos=run_input.videos,
|
|
1784
|
+
files=run_input.files,
|
|
1785
|
+
add_history_to_context=add_history_to_context,
|
|
1786
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
1787
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
1788
|
+
tools=_tools,
|
|
1789
|
+
**kwargs,
|
|
1790
|
+
)
|
|
1791
|
+
if len(run_messages.messages) == 0:
|
|
1792
|
+
log_error("No messages to be sent to the model.")
|
|
1760
1793
|
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
tools=_tools,
|
|
1768
|
-
response_format=response_format,
|
|
1769
|
-
stream_events=stream_events,
|
|
1770
|
-
session_state=run_context.session_state,
|
|
1771
|
-
run_context=run_context,
|
|
1772
|
-
):
|
|
1773
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1774
|
-
yield event
|
|
1775
|
-
else:
|
|
1776
|
-
for event in self._handle_model_response_stream(
|
|
1777
|
-
session=session,
|
|
1778
|
-
run_response=run_response,
|
|
1779
|
-
run_messages=run_messages,
|
|
1780
|
-
tools=_tools,
|
|
1781
|
-
response_format=response_format,
|
|
1782
|
-
stream_events=stream_events,
|
|
1783
|
-
session_state=run_context.session_state,
|
|
1784
|
-
run_context=run_context,
|
|
1785
|
-
):
|
|
1786
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1787
|
-
from agno.run.team import IntermediateRunContentEvent, RunContentEvent
|
|
1794
|
+
# 4. Start memory creation in background thread
|
|
1795
|
+
memory_future = self._start_memory_future(
|
|
1796
|
+
run_messages=run_messages,
|
|
1797
|
+
user_id=user_id,
|
|
1798
|
+
existing_future=None,
|
|
1799
|
+
)
|
|
1788
1800
|
|
|
1789
|
-
|
|
1801
|
+
# Start the Run by yielding a RunStarted event
|
|
1790
1802
|
if stream_events:
|
|
1791
|
-
yield
|
|
1792
|
-
|
|
1793
|
-
|
|
1803
|
+
yield handle_event( # type: ignore
|
|
1804
|
+
create_team_run_started_event(run_response),
|
|
1805
|
+
run_response,
|
|
1806
|
+
events_to_skip=self.events_to_skip,
|
|
1807
|
+
store_events=self.store_events,
|
|
1794
1808
|
)
|
|
1795
|
-
else:
|
|
1796
|
-
yield event
|
|
1797
1809
|
|
|
1798
|
-
|
|
1799
|
-
session=session,
|
|
1800
|
-
run_response=run_response,
|
|
1801
|
-
run_messages=run_messages,
|
|
1802
|
-
stream_events=stream_events,
|
|
1803
|
-
):
|
|
1804
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1805
|
-
yield event
|
|
1810
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1806
1811
|
|
|
1807
|
-
|
|
1808
|
-
|
|
1812
|
+
# 5. Reason about the task if reasoning is enabled
|
|
1813
|
+
yield from self._handle_reasoning_stream(
|
|
1814
|
+
run_response=run_response,
|
|
1815
|
+
run_messages=run_messages,
|
|
1816
|
+
stream_events=stream_events,
|
|
1817
|
+
)
|
|
1809
1818
|
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
session=session, run_response=run_response, stream_events=stream_events, run_context=run_context
|
|
1813
|
-
)
|
|
1819
|
+
# Check for cancellation before model processing
|
|
1820
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1814
1821
|
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1822
|
+
# 6. Get a response from the model
|
|
1823
|
+
if self.output_model is None:
|
|
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
|
+
yield event
|
|
1836
|
+
else:
|
|
1837
|
+
for event in self._handle_model_response_stream(
|
|
1838
|
+
session=session,
|
|
1839
|
+
run_response=run_response,
|
|
1840
|
+
run_messages=run_messages,
|
|
1841
|
+
tools=_tools,
|
|
1842
|
+
response_format=response_format,
|
|
1843
|
+
stream_events=stream_events,
|
|
1844
|
+
session_state=run_context.session_state,
|
|
1845
|
+
run_context=run_context,
|
|
1846
|
+
):
|
|
1847
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1848
|
+
from agno.run.team import IntermediateRunContentEvent, RunContentEvent
|
|
1849
|
+
|
|
1850
|
+
if isinstance(event, RunContentEvent):
|
|
1851
|
+
if stream_events:
|
|
1852
|
+
yield IntermediateRunContentEvent(
|
|
1853
|
+
content=event.content,
|
|
1854
|
+
content_type=event.content_type,
|
|
1855
|
+
)
|
|
1856
|
+
else:
|
|
1857
|
+
yield event
|
|
1837
1858
|
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1859
|
+
for event in self._generate_response_with_output_model_stream(
|
|
1860
|
+
session=session,
|
|
1861
|
+
run_response=run_response,
|
|
1862
|
+
run_messages=run_messages,
|
|
1863
|
+
stream_events=stream_events,
|
|
1864
|
+
):
|
|
1865
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1866
|
+
yield event
|
|
1846
1867
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
if self.session_summary_manager is not None:
|
|
1850
|
-
# Upsert the RunOutput to Team Session before creating the session summary
|
|
1851
|
-
session.upsert_run(run_response=run_response)
|
|
1868
|
+
# Check for cancellation after model processing
|
|
1869
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1852
1870
|
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
events_to_skip=self.events_to_skip,
|
|
1858
|
-
store_events=self.store_events,
|
|
1859
|
-
)
|
|
1860
|
-
try:
|
|
1861
|
-
self.session_summary_manager.create_session_summary(session=session)
|
|
1862
|
-
except Exception as e:
|
|
1863
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
1864
|
-
if stream_events:
|
|
1865
|
-
yield handle_event( # type: ignore
|
|
1866
|
-
create_team_session_summary_completed_event(
|
|
1867
|
-
from_run_response=run_response, session_summary=session.summary
|
|
1868
|
-
),
|
|
1869
|
-
run_response,
|
|
1870
|
-
events_to_skip=self.events_to_skip,
|
|
1871
|
-
store_events=self.store_events,
|
|
1872
|
-
)
|
|
1871
|
+
# 7. Parse response with parser model if provided
|
|
1872
|
+
yield from self._parse_response_with_parser_model_stream(
|
|
1873
|
+
session=session, run_response=run_response, stream_events=stream_events, run_context=run_context
|
|
1874
|
+
)
|
|
1873
1875
|
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1876
|
+
# Yield RunContentCompletedEvent
|
|
1877
|
+
if stream_events:
|
|
1878
|
+
yield handle_event( # type: ignore
|
|
1879
|
+
create_team_run_content_completed_event(from_run_response=run_response),
|
|
1880
|
+
run_response,
|
|
1881
|
+
events_to_skip=self.events_to_skip,
|
|
1882
|
+
store_events=self.store_events,
|
|
1883
|
+
)
|
|
1884
|
+
# Execute post-hooks after output is generated but before response is returned
|
|
1885
|
+
if self.post_hooks is not None:
|
|
1886
|
+
yield from self._execute_post_hooks(
|
|
1887
|
+
hooks=self.post_hooks, # type: ignore
|
|
1888
|
+
run_output=run_response,
|
|
1889
|
+
run_context=run_context,
|
|
1890
|
+
session=session,
|
|
1891
|
+
user_id=user_id,
|
|
1892
|
+
debug_mode=debug_mode,
|
|
1893
|
+
stream_events=stream_events,
|
|
1894
|
+
background_tasks=background_tasks,
|
|
1895
|
+
**kwargs,
|
|
1896
|
+
)
|
|
1897
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1884
1898
|
|
|
1885
|
-
|
|
1886
|
-
|
|
1899
|
+
# 8. Wait for background memory creation
|
|
1900
|
+
yield from wait_for_thread_tasks_stream(
|
|
1901
|
+
run_response=run_response,
|
|
1902
|
+
memory_future=memory_future, # type: ignore
|
|
1903
|
+
stream_events=stream_events,
|
|
1904
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
1905
|
+
store_events=self.store_events,
|
|
1906
|
+
)
|
|
1887
1907
|
|
|
1888
|
-
|
|
1889
|
-
|
|
1908
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1909
|
+
# 9. Create session summary
|
|
1910
|
+
if self.session_summary_manager is not None:
|
|
1911
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
1912
|
+
session.upsert_run(run_response=run_response)
|
|
1890
1913
|
|
|
1891
|
-
|
|
1892
|
-
|
|
1914
|
+
if stream_events:
|
|
1915
|
+
yield handle_event( # type: ignore
|
|
1916
|
+
create_team_session_summary_started_event(from_run_response=run_response),
|
|
1917
|
+
run_response,
|
|
1918
|
+
events_to_skip=self.events_to_skip,
|
|
1919
|
+
store_events=self.store_events,
|
|
1920
|
+
)
|
|
1921
|
+
try:
|
|
1922
|
+
self.session_summary_manager.create_session_summary(session=session)
|
|
1923
|
+
except Exception as e:
|
|
1924
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
1925
|
+
if stream_events:
|
|
1926
|
+
yield handle_event( # type: ignore
|
|
1927
|
+
create_team_session_summary_completed_event(
|
|
1928
|
+
from_run_response=run_response, session_summary=session.summary
|
|
1929
|
+
),
|
|
1930
|
+
run_response,
|
|
1931
|
+
events_to_skip=self.events_to_skip,
|
|
1932
|
+
store_events=self.store_events,
|
|
1933
|
+
)
|
|
1934
|
+
|
|
1935
|
+
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
1936
|
+
# Create the run completed event
|
|
1937
|
+
completed_event = handle_event(
|
|
1938
|
+
create_team_run_completed_event(
|
|
1939
|
+
from_run_response=run_response,
|
|
1940
|
+
),
|
|
1941
|
+
run_response,
|
|
1942
|
+
events_to_skip=self.events_to_skip,
|
|
1943
|
+
store_events=self.store_events,
|
|
1944
|
+
)
|
|
1945
|
+
|
|
1946
|
+
# Set the run status to completed
|
|
1947
|
+
run_response.status = RunStatus.completed
|
|
1948
|
+
|
|
1949
|
+
# 10. Cleanup and store the run response
|
|
1950
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1951
|
+
|
|
1952
|
+
if stream_events:
|
|
1953
|
+
yield completed_event
|
|
1954
|
+
|
|
1955
|
+
if yield_run_output:
|
|
1956
|
+
yield run_response
|
|
1957
|
+
|
|
1958
|
+
# Log Team Telemetry
|
|
1959
|
+
self._log_team_telemetry(session_id=session.session_id, run_id=run_response.run_id)
|
|
1960
|
+
|
|
1961
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
1962
|
+
|
|
1963
|
+
break
|
|
1964
|
+
except RunCancelledException as e:
|
|
1965
|
+
# Handle run cancellation during streaming
|
|
1966
|
+
log_info(f"Team run {run_response.run_id} was cancelled during streaming")
|
|
1967
|
+
run_response.status = RunStatus.cancelled
|
|
1968
|
+
run_response.content = str(e)
|
|
1969
|
+
|
|
1970
|
+
# Yield the cancellation event
|
|
1971
|
+
yield handle_event(
|
|
1972
|
+
create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
1973
|
+
run_response,
|
|
1974
|
+
events_to_skip=self.events_to_skip,
|
|
1975
|
+
store_events=self.store_events,
|
|
1976
|
+
)
|
|
1977
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1978
|
+
break
|
|
1979
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
1980
|
+
run_response.status = RunStatus.error
|
|
1981
|
+
|
|
1982
|
+
# Add error event to list of events
|
|
1983
|
+
run_error = create_team_run_error_event(
|
|
1984
|
+
run_response,
|
|
1985
|
+
error=str(e),
|
|
1986
|
+
error_id=e.error_id,
|
|
1987
|
+
error_type=e.type,
|
|
1988
|
+
additional_data=e.additional_data,
|
|
1989
|
+
)
|
|
1990
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
1991
|
+
|
|
1992
|
+
if run_response.content is None:
|
|
1993
|
+
run_response.content = str(e)
|
|
1994
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
1995
|
+
yield run_error
|
|
1996
|
+
break
|
|
1997
|
+
|
|
1998
|
+
except KeyboardInterrupt:
|
|
1999
|
+
run_response = cast(TeamRunOutput, run_response)
|
|
2000
|
+
yield handle_event( # type: ignore
|
|
2001
|
+
create_team_run_cancelled_event(
|
|
2002
|
+
from_run_response=run_response, reason="Operation cancelled by user"
|
|
2003
|
+
),
|
|
2004
|
+
run_response,
|
|
2005
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2006
|
+
store_events=self.store_events,
|
|
2007
|
+
)
|
|
2008
|
+
break
|
|
2009
|
+
except Exception as e:
|
|
2010
|
+
if attempt < num_attempts - 1:
|
|
2011
|
+
# Calculate delay with exponential backoff if enabled
|
|
2012
|
+
if self.exponential_backoff:
|
|
2013
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2014
|
+
else:
|
|
2015
|
+
delay = self.delay_between_retries
|
|
1893
2016
|
|
|
1894
|
-
|
|
1895
|
-
|
|
2017
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2018
|
+
time.sleep(delay)
|
|
2019
|
+
continue
|
|
1896
2020
|
|
|
1897
|
-
|
|
1898
|
-
|
|
2021
|
+
run_response.status = RunStatus.error
|
|
2022
|
+
run_error = create_team_run_error_event(run_response, error=str(e))
|
|
2023
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2024
|
+
if run_response.content is None:
|
|
2025
|
+
run_response.content = str(e)
|
|
1899
2026
|
|
|
1900
|
-
|
|
2027
|
+
log_error(f"Error in Team run: {str(e)}")
|
|
1901
2028
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
2029
|
+
self._cleanup_and_store(run_response=run_response, session=session)
|
|
2030
|
+
yield run_error
|
|
2031
|
+
finally:
|
|
2032
|
+
# Cancel background futures on error (wait_for_thread_tasks_stream handles waiting on success)
|
|
2033
|
+
if memory_future is not None and not memory_future.done():
|
|
2034
|
+
memory_future.cancel()
|
|
2035
|
+
|
|
2036
|
+
# Always disconnect connectable tools
|
|
2037
|
+
self._disconnect_connectable_tools()
|
|
2038
|
+
# Always clean up the run tracking
|
|
2039
|
+
cleanup_run(run_response.run_id) # type: ignore
|
|
1905
2040
|
|
|
1906
2041
|
@overload
|
|
1907
2042
|
def run(
|
|
@@ -2011,288 +2146,173 @@ class Team:
|
|
|
2011
2146
|
)
|
|
2012
2147
|
yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
|
|
2013
2148
|
|
|
2014
|
-
#
|
|
2015
|
-
|
|
2016
|
-
for attempt in range(num_attempts):
|
|
2017
|
-
if num_attempts > 1:
|
|
2018
|
-
log_debug(f"Retrying Team run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2019
|
-
|
|
2020
|
-
try:
|
|
2021
|
-
# Register run for cancellation tracking
|
|
2022
|
-
register_run(run_id) # type: ignore
|
|
2023
|
-
|
|
2024
|
-
background_tasks = kwargs.pop("background_tasks", None)
|
|
2025
|
-
if background_tasks is not None:
|
|
2026
|
-
from fastapi import BackgroundTasks
|
|
2027
|
-
|
|
2028
|
-
background_tasks: BackgroundTasks = background_tasks # type: ignore
|
|
2029
|
-
|
|
2030
|
-
# Validate input against input_schema if provided
|
|
2031
|
-
validated_input = validate_input(input, self.input_schema)
|
|
2032
|
-
|
|
2033
|
-
# Normalise hook & guardails
|
|
2034
|
-
if not self._hooks_normalised:
|
|
2035
|
-
if self.pre_hooks:
|
|
2036
|
-
self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
|
|
2037
|
-
if self.post_hooks:
|
|
2038
|
-
self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
|
|
2039
|
-
self._hooks_normalised = True
|
|
2040
|
-
|
|
2041
|
-
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
2042
|
-
|
|
2043
|
-
image_artifacts, video_artifacts, audio_artifacts, file_artifacts = validate_media_object_id(
|
|
2044
|
-
images=images, videos=videos, audios=audio, files=files
|
|
2045
|
-
)
|
|
2046
|
-
|
|
2047
|
-
# Create RunInput to capture the original user input
|
|
2048
|
-
run_input = TeamRunInput(
|
|
2049
|
-
input_content=validated_input,
|
|
2050
|
-
images=image_artifacts,
|
|
2051
|
-
videos=video_artifacts,
|
|
2052
|
-
audios=audio_artifacts,
|
|
2053
|
-
files=file_artifacts,
|
|
2054
|
-
)
|
|
2055
|
-
|
|
2056
|
-
# Read existing session from database
|
|
2057
|
-
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2058
|
-
self._update_metadata(session=team_session)
|
|
2059
|
-
|
|
2060
|
-
# Initialize session state
|
|
2061
|
-
session_state = self._initialize_session_state(
|
|
2062
|
-
session_state=session_state if session_state is not None else {},
|
|
2063
|
-
user_id=user_id,
|
|
2064
|
-
session_id=session_id,
|
|
2065
|
-
run_id=run_id,
|
|
2066
|
-
)
|
|
2067
|
-
# Update session state from DB
|
|
2068
|
-
session_state = self._load_session_state(session=team_session, session_state=session_state)
|
|
2069
|
-
|
|
2070
|
-
# Determine runtime dependencies
|
|
2071
|
-
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
2149
|
+
# Register run for cancellation tracking
|
|
2150
|
+
register_run(run_id) # type: ignore
|
|
2072
2151
|
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2152
|
+
background_tasks = kwargs.pop("background_tasks", None)
|
|
2153
|
+
if background_tasks is not None:
|
|
2154
|
+
from fastapi import BackgroundTasks
|
|
2076
2155
|
|
|
2077
|
-
|
|
2078
|
-
run_context = run_context or RunContext(
|
|
2079
|
-
run_id=run_id,
|
|
2080
|
-
session_id=session_id,
|
|
2081
|
-
user_id=user_id,
|
|
2082
|
-
session_state=session_state,
|
|
2083
|
-
dependencies=dependencies,
|
|
2084
|
-
output_schema=output_schema,
|
|
2085
|
-
)
|
|
2086
|
-
# output_schema parameter takes priority, even if run_context was provided
|
|
2087
|
-
run_context.output_schema = output_schema
|
|
2088
|
-
|
|
2089
|
-
# Resolve callable dependencies if present
|
|
2090
|
-
if run_context.dependencies is not None:
|
|
2091
|
-
self._resolve_run_dependencies(run_context=run_context)
|
|
2092
|
-
|
|
2093
|
-
# Determine runtime context parameters
|
|
2094
|
-
add_dependencies = (
|
|
2095
|
-
add_dependencies_to_context
|
|
2096
|
-
if add_dependencies_to_context is not None
|
|
2097
|
-
else self.add_dependencies_to_context
|
|
2098
|
-
)
|
|
2099
|
-
add_session_state = (
|
|
2100
|
-
add_session_state_to_context
|
|
2101
|
-
if add_session_state_to_context is not None
|
|
2102
|
-
else self.add_session_state_to_context
|
|
2103
|
-
)
|
|
2104
|
-
add_history = (
|
|
2105
|
-
add_history_to_context if add_history_to_context is not None else self.add_history_to_context
|
|
2106
|
-
)
|
|
2156
|
+
background_tasks: BackgroundTasks = background_tasks # type: ignore
|
|
2107
2157
|
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
|
|
2158
|
+
# Validate input against input_schema if provided
|
|
2159
|
+
validated_input = validate_input(input, self.input_schema)
|
|
2111
2160
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2161
|
+
# Normalise hook & guardails
|
|
2162
|
+
if not self._hooks_normalised:
|
|
2163
|
+
if self.pre_hooks:
|
|
2164
|
+
self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
|
|
2165
|
+
if self.post_hooks:
|
|
2166
|
+
self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
|
|
2167
|
+
self._hooks_normalised = True
|
|
2115
2168
|
|
|
2116
|
-
|
|
2117
|
-
stream_events = stream_events or stream_intermediate_steps
|
|
2169
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
2118
2170
|
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2171
|
+
image_artifacts, video_artifacts, audio_artifacts, file_artifacts = validate_media_object_id(
|
|
2172
|
+
images=images, videos=videos, audios=audio, files=files
|
|
2173
|
+
)
|
|
2122
2174
|
|
|
2123
|
-
|
|
2124
|
-
|
|
2175
|
+
# Create RunInput to capture the original user input
|
|
2176
|
+
run_input = TeamRunInput(
|
|
2177
|
+
input_content=validated_input,
|
|
2178
|
+
images=image_artifacts,
|
|
2179
|
+
videos=video_artifacts,
|
|
2180
|
+
audios=audio_artifacts,
|
|
2181
|
+
files=file_artifacts,
|
|
2182
|
+
)
|
|
2125
2183
|
|
|
2126
|
-
|
|
2184
|
+
# Read existing session from database
|
|
2185
|
+
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2186
|
+
self._update_metadata(session=team_session)
|
|
2127
2187
|
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2188
|
+
# Initialize session state
|
|
2189
|
+
session_state = self._initialize_session_state(
|
|
2190
|
+
session_state=session_state if session_state is not None else {},
|
|
2191
|
+
user_id=user_id,
|
|
2192
|
+
session_id=session_id,
|
|
2193
|
+
run_id=run_id,
|
|
2194
|
+
)
|
|
2195
|
+
# Update session state from DB
|
|
2196
|
+
session_state = self._load_session_state(session=team_session, session_state=session_state)
|
|
2133
2197
|
|
|
2134
|
-
|
|
2135
|
-
|
|
2198
|
+
# Determine runtime dependencies
|
|
2199
|
+
dependencies = dependencies if dependencies is not None else self.dependencies
|
|
2136
2200
|
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
)
|
|
2201
|
+
# Resolve output_schema parameter takes precedence, then fall back to self.output_schema
|
|
2202
|
+
if output_schema is None:
|
|
2203
|
+
output_schema = self.output_schema
|
|
2141
2204
|
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2205
|
+
# Initialize run context
|
|
2206
|
+
run_context = run_context or RunContext(
|
|
2207
|
+
run_id=run_id,
|
|
2208
|
+
session_id=session_id,
|
|
2209
|
+
user_id=user_id,
|
|
2210
|
+
session_state=session_state,
|
|
2211
|
+
dependencies=dependencies,
|
|
2212
|
+
output_schema=output_schema,
|
|
2213
|
+
)
|
|
2214
|
+
# output_schema parameter takes priority, even if run_context was provided
|
|
2215
|
+
run_context.output_schema = output_schema
|
|
2153
2216
|
|
|
2154
|
-
|
|
2155
|
-
|
|
2217
|
+
# Resolve callable dependencies if present
|
|
2218
|
+
if run_context.dependencies is not None:
|
|
2219
|
+
self._resolve_run_dependencies(run_context=run_context)
|
|
2156
2220
|
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2221
|
+
# Determine runtime context parameters
|
|
2222
|
+
add_dependencies = (
|
|
2223
|
+
add_dependencies_to_context if add_dependencies_to_context is not None else self.add_dependencies_to_context
|
|
2224
|
+
)
|
|
2225
|
+
add_session_state = (
|
|
2226
|
+
add_session_state_to_context
|
|
2227
|
+
if add_session_state_to_context is not None
|
|
2228
|
+
else self.add_session_state_to_context
|
|
2229
|
+
)
|
|
2230
|
+
add_history = add_history_to_context if add_history_to_context is not None else self.add_history_to_context
|
|
2160
2231
|
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
run_context=run_context,
|
|
2165
|
-
session=team_session,
|
|
2166
|
-
user_id=user_id,
|
|
2167
|
-
add_history_to_context=add_history,
|
|
2168
|
-
add_dependencies_to_context=add_dependencies,
|
|
2169
|
-
add_session_state_to_context=add_session_state,
|
|
2170
|
-
response_format=response_format,
|
|
2171
|
-
stream_events=stream_events,
|
|
2172
|
-
yield_run_output=yield_run_output,
|
|
2173
|
-
debug_mode=debug_mode,
|
|
2174
|
-
background_tasks=background_tasks,
|
|
2175
|
-
**kwargs,
|
|
2176
|
-
) # type: ignore
|
|
2232
|
+
# When filters are passed manually
|
|
2233
|
+
if self.knowledge_filters or knowledge_filters:
|
|
2234
|
+
run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
|
|
2177
2235
|
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
run_context=run_context,
|
|
2182
|
-
session=team_session,
|
|
2183
|
-
user_id=user_id,
|
|
2184
|
-
add_history_to_context=add_history,
|
|
2185
|
-
add_dependencies_to_context=add_dependencies,
|
|
2186
|
-
add_session_state_to_context=add_session_state,
|
|
2187
|
-
response_format=response_format,
|
|
2188
|
-
debug_mode=debug_mode,
|
|
2189
|
-
background_tasks=background_tasks,
|
|
2190
|
-
**kwargs,
|
|
2191
|
-
)
|
|
2192
|
-
except InputCheckError as e:
|
|
2193
|
-
run_response.status = RunStatus.error
|
|
2194
|
-
if stream:
|
|
2195
|
-
run_error = create_team_run_error_event(
|
|
2196
|
-
run_response,
|
|
2197
|
-
error=str(e),
|
|
2198
|
-
error_id=e.error_id,
|
|
2199
|
-
error_type=e.type,
|
|
2200
|
-
additional_data=e.additional_data,
|
|
2201
|
-
)
|
|
2202
|
-
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2203
|
-
if run_response.content is None:
|
|
2204
|
-
run_response.content = str(e)
|
|
2236
|
+
# Use stream override value when necessary
|
|
2237
|
+
if stream is None:
|
|
2238
|
+
stream = False if self.stream is None else self.stream
|
|
2205
2239
|
|
|
2206
|
-
|
|
2240
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
2241
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
2207
2242
|
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
return run_response
|
|
2212
|
-
except RunCancelledException as e:
|
|
2213
|
-
# Handle run cancellation during streaming
|
|
2214
|
-
log_info(f"Team run {run_response.run_id} was cancelled during streaming")
|
|
2215
|
-
run_response.status = RunStatus.cancelled
|
|
2216
|
-
run_response.content = str(e)
|
|
2243
|
+
# Can't stream events if streaming is disabled
|
|
2244
|
+
if stream is False:
|
|
2245
|
+
stream_events = False
|
|
2217
2246
|
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
cancelled_run_error = handle_event(
|
|
2221
|
-
create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2222
|
-
run_response,
|
|
2223
|
-
events_to_skip=self.events_to_skip,
|
|
2224
|
-
store_events=self.store_events,
|
|
2225
|
-
)
|
|
2226
|
-
return generator_wrapper(cancelled_run_error) # type: ignore
|
|
2227
|
-
else:
|
|
2228
|
-
return run_response
|
|
2229
|
-
except KeyboardInterrupt:
|
|
2230
|
-
# Handle KeyboardInterrupt - stop retries immediately
|
|
2231
|
-
run_response.status = RunStatus.cancelled
|
|
2232
|
-
run_response.content = "Operation cancelled by user"
|
|
2233
|
-
if stream:
|
|
2234
|
-
cancelled_run_error = handle_event(
|
|
2235
|
-
create_team_run_cancelled_event(
|
|
2236
|
-
from_run_response=run_response, reason="Operation cancelled by user"
|
|
2237
|
-
),
|
|
2238
|
-
run_response,
|
|
2239
|
-
events_to_skip=self.events_to_skip,
|
|
2240
|
-
store_events=self.store_events,
|
|
2241
|
-
)
|
|
2242
|
-
return generator_wrapper(cancelled_run_error) # type: ignore
|
|
2243
|
-
else:
|
|
2244
|
-
return run_response
|
|
2245
|
-
except (InputCheckError, OutputCheckError) as e:
|
|
2246
|
-
run_response.status = RunStatus.error
|
|
2247
|
+
if stream_events is None:
|
|
2248
|
+
stream_events = False if self.stream_events is None else self.stream_events
|
|
2247
2249
|
|
|
2248
|
-
|
|
2249
|
-
# Add error event to list of events
|
|
2250
|
-
run_error = create_team_run_error_event(
|
|
2251
|
-
run_response,
|
|
2252
|
-
error=str(e),
|
|
2253
|
-
error_id=e.error_id,
|
|
2254
|
-
error_type=e.type,
|
|
2255
|
-
additional_data=e.additional_data,
|
|
2256
|
-
)
|
|
2257
|
-
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2250
|
+
self.model = cast(Model, self.model)
|
|
2258
2251
|
|
|
2259
|
-
|
|
2260
|
-
|
|
2252
|
+
if self.metadata is not None:
|
|
2253
|
+
if metadata is None:
|
|
2254
|
+
metadata = self.metadata
|
|
2255
|
+
else:
|
|
2256
|
+
merge_dictionaries(metadata, self.metadata)
|
|
2261
2257
|
|
|
2262
|
-
|
|
2258
|
+
if metadata:
|
|
2259
|
+
run_context.metadata = metadata
|
|
2263
2260
|
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
except Exception as e:
|
|
2269
|
-
if attempt < num_attempts - 1:
|
|
2270
|
-
# Calculate delay with exponential backoff if enabled
|
|
2271
|
-
if self.exponential_backoff:
|
|
2272
|
-
delay = self.delay_between_retries * (2**attempt)
|
|
2273
|
-
else:
|
|
2274
|
-
delay = self.delay_between_retries
|
|
2261
|
+
# Configure the model for runs
|
|
2262
|
+
response_format: Optional[Union[Dict, Type[BaseModel]]] = (
|
|
2263
|
+
self._get_response_format(run_context=run_context) if self.parser_model is None else None
|
|
2264
|
+
)
|
|
2275
2265
|
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2266
|
+
# Create a new run_response for this attempt
|
|
2267
|
+
run_response = TeamRunOutput(
|
|
2268
|
+
run_id=run_id,
|
|
2269
|
+
session_id=session_id,
|
|
2270
|
+
user_id=user_id,
|
|
2271
|
+
team_id=self.id,
|
|
2272
|
+
team_name=self.name,
|
|
2273
|
+
metadata=run_context.metadata,
|
|
2274
|
+
session_state=run_context.session_state,
|
|
2275
|
+
input=run_input,
|
|
2276
|
+
)
|
|
2279
2277
|
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
run_error = create_team_run_error_event(run_response, error=str(e))
|
|
2283
|
-
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2284
|
-
if run_response.content is None:
|
|
2285
|
-
run_response.content = str(e)
|
|
2278
|
+
run_response.model = self.model.id if self.model is not None else None
|
|
2279
|
+
run_response.model_provider = self.model.provider if self.model is not None else None
|
|
2286
2280
|
|
|
2287
|
-
|
|
2281
|
+
# Start the run metrics timer, to calculate the run duration
|
|
2282
|
+
run_response.metrics = Metrics()
|
|
2283
|
+
run_response.metrics.start_timer()
|
|
2288
2284
|
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2285
|
+
if stream:
|
|
2286
|
+
return self._run_stream(
|
|
2287
|
+
run_response=run_response,
|
|
2288
|
+
run_context=run_context,
|
|
2289
|
+
session=team_session,
|
|
2290
|
+
user_id=user_id,
|
|
2291
|
+
add_history_to_context=add_history,
|
|
2292
|
+
add_dependencies_to_context=add_dependencies,
|
|
2293
|
+
add_session_state_to_context=add_session_state,
|
|
2294
|
+
response_format=response_format,
|
|
2295
|
+
stream_events=stream_events,
|
|
2296
|
+
yield_run_output=yield_run_output,
|
|
2297
|
+
debug_mode=debug_mode,
|
|
2298
|
+
background_tasks=background_tasks,
|
|
2299
|
+
**kwargs,
|
|
2300
|
+
) # type: ignore
|
|
2293
2301
|
|
|
2294
|
-
|
|
2295
|
-
|
|
2302
|
+
else:
|
|
2303
|
+
return self._run(
|
|
2304
|
+
run_response=run_response,
|
|
2305
|
+
run_context=run_context,
|
|
2306
|
+
session=team_session,
|
|
2307
|
+
user_id=user_id,
|
|
2308
|
+
add_history_to_context=add_history,
|
|
2309
|
+
add_dependencies_to_context=add_dependencies,
|
|
2310
|
+
add_session_state_to_context=add_session_state,
|
|
2311
|
+
response_format=response_format,
|
|
2312
|
+
debug_mode=debug_mode,
|
|
2313
|
+
background_tasks=background_tasks,
|
|
2314
|
+
**kwargs,
|
|
2315
|
+
)
|
|
2296
2316
|
|
|
2297
2317
|
async def _arun(
|
|
2298
2318
|
self,
|
|
@@ -2327,275 +2347,272 @@ class Team:
|
|
|
2327
2347
|
14. Create session summary
|
|
2328
2348
|
15. Cleanup and store (scrub, add to session, calculate metrics, save session)
|
|
2329
2349
|
"""
|
|
2350
|
+
await aregister_run(run_context.run_id)
|
|
2330
2351
|
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
2331
2352
|
memory_task = None
|
|
2332
2353
|
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2354
|
+
try:
|
|
2355
|
+
# Set up retry logic
|
|
2356
|
+
num_attempts = self.retries + 1
|
|
2357
|
+
for attempt in range(num_attempts):
|
|
2358
|
+
if num_attempts > 1:
|
|
2359
|
+
log_debug(f"Retrying Team run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2338
2360
|
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2361
|
+
try:
|
|
2362
|
+
if run_context.dependencies is not None:
|
|
2363
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2342
2364
|
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2365
|
+
# 1. Read or create session. Reads from the database if provided.
|
|
2366
|
+
if self._has_async_db():
|
|
2367
|
+
team_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
2368
|
+
else:
|
|
2369
|
+
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2348
2370
|
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
)
|
|
2358
|
-
# Update session state from DB
|
|
2359
|
-
if run_context.session_state is not None:
|
|
2360
|
-
run_context.session_state = self._load_session_state(
|
|
2361
|
-
session=team_session, session_state=run_context.session_state
|
|
2371
|
+
# 2. Update metadata and session state
|
|
2372
|
+
self._update_metadata(session=team_session)
|
|
2373
|
+
# Initialize session state
|
|
2374
|
+
run_context.session_state = self._initialize_session_state(
|
|
2375
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2376
|
+
user_id=user_id,
|
|
2377
|
+
session_id=session_id,
|
|
2378
|
+
run_id=run_response.run_id,
|
|
2362
2379
|
)
|
|
2380
|
+
# Update session state from DB
|
|
2381
|
+
if run_context.session_state is not None:
|
|
2382
|
+
run_context.session_state = self._load_session_state(
|
|
2383
|
+
session=team_session, session_state=run_context.session_state
|
|
2384
|
+
)
|
|
2385
|
+
|
|
2386
|
+
run_input = cast(TeamRunInput, run_response.input)
|
|
2387
|
+
|
|
2388
|
+
# 3. Execute pre-hooks after session is loaded but before processing starts
|
|
2389
|
+
if self.pre_hooks is not None:
|
|
2390
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2391
|
+
hooks=self.pre_hooks, # type: ignore
|
|
2392
|
+
run_response=run_response,
|
|
2393
|
+
run_context=run_context,
|
|
2394
|
+
run_input=run_input,
|
|
2395
|
+
session=team_session,
|
|
2396
|
+
user_id=user_id,
|
|
2397
|
+
debug_mode=debug_mode,
|
|
2398
|
+
background_tasks=background_tasks,
|
|
2399
|
+
**kwargs,
|
|
2400
|
+
)
|
|
2363
2401
|
|
|
2364
|
-
|
|
2402
|
+
# Consume the async iterator without yielding
|
|
2403
|
+
async for _ in pre_hook_iterator:
|
|
2404
|
+
pass
|
|
2365
2405
|
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2406
|
+
# 4. Determine tools for model
|
|
2407
|
+
team_run_context: Dict[str, Any] = {}
|
|
2408
|
+
self.model = cast(Model, self.model)
|
|
2409
|
+
await self._check_and_refresh_mcp_tools()
|
|
2410
|
+
_tools = self._determine_tools_for_model(
|
|
2411
|
+
model=self.model,
|
|
2370
2412
|
run_response=run_response,
|
|
2371
2413
|
run_context=run_context,
|
|
2372
|
-
|
|
2414
|
+
team_run_context=team_run_context,
|
|
2373
2415
|
session=team_session,
|
|
2374
2416
|
user_id=user_id,
|
|
2417
|
+
async_mode=True,
|
|
2418
|
+
input_message=run_input.input_content,
|
|
2419
|
+
images=run_input.images,
|
|
2420
|
+
videos=run_input.videos,
|
|
2421
|
+
audio=run_input.audios,
|
|
2422
|
+
files=run_input.files,
|
|
2375
2423
|
debug_mode=debug_mode,
|
|
2376
|
-
|
|
2377
|
-
|
|
2424
|
+
add_history_to_context=add_history_to_context,
|
|
2425
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2426
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2427
|
+
stream=False,
|
|
2428
|
+
stream_events=False,
|
|
2378
2429
|
)
|
|
2379
2430
|
|
|
2380
|
-
#
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
images=run_input.images,
|
|
2398
|
-
videos=run_input.videos,
|
|
2399
|
-
audio=run_input.audios,
|
|
2400
|
-
files=run_input.files,
|
|
2401
|
-
debug_mode=debug_mode,
|
|
2402
|
-
add_history_to_context=add_history_to_context,
|
|
2403
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2404
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2405
|
-
stream=False,
|
|
2406
|
-
stream_events=False,
|
|
2407
|
-
)
|
|
2408
|
-
|
|
2409
|
-
# 5. Prepare run messages
|
|
2410
|
-
run_messages = await self._aget_run_messages(
|
|
2411
|
-
run_response=run_response,
|
|
2412
|
-
run_context=run_context,
|
|
2413
|
-
session=team_session, # type: ignore
|
|
2414
|
-
user_id=user_id,
|
|
2415
|
-
input_message=run_input.input_content,
|
|
2416
|
-
audio=run_input.audios,
|
|
2417
|
-
images=run_input.images,
|
|
2418
|
-
videos=run_input.videos,
|
|
2419
|
-
files=run_input.files,
|
|
2420
|
-
add_history_to_context=add_history_to_context,
|
|
2421
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2422
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2423
|
-
tools=_tools,
|
|
2424
|
-
**kwargs,
|
|
2425
|
-
)
|
|
2431
|
+
# 5. Prepare run messages
|
|
2432
|
+
run_messages = await self._aget_run_messages(
|
|
2433
|
+
run_response=run_response,
|
|
2434
|
+
run_context=run_context,
|
|
2435
|
+
session=team_session, # type: ignore
|
|
2436
|
+
user_id=user_id,
|
|
2437
|
+
input_message=run_input.input_content,
|
|
2438
|
+
audio=run_input.audios,
|
|
2439
|
+
images=run_input.images,
|
|
2440
|
+
videos=run_input.videos,
|
|
2441
|
+
files=run_input.files,
|
|
2442
|
+
add_history_to_context=add_history_to_context,
|
|
2443
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2444
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2445
|
+
tools=_tools,
|
|
2446
|
+
**kwargs,
|
|
2447
|
+
)
|
|
2426
2448
|
|
|
2427
|
-
|
|
2428
|
-
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
2449
|
+
self.model = cast(Model, self.model)
|
|
2429
2450
|
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
and not self.enable_agentic_memory
|
|
2437
|
-
):
|
|
2438
|
-
log_debug("Starting memory creation in background task.")
|
|
2439
|
-
memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2440
|
-
|
|
2441
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2442
|
-
# 7. Reason about the task if reasoning is enabled
|
|
2443
|
-
await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
2444
|
-
|
|
2445
|
-
# Check for cancellation before model call
|
|
2446
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2447
|
-
|
|
2448
|
-
# 8. Get the model response for the team leader
|
|
2449
|
-
model_response = await self.model.aresponse(
|
|
2450
|
-
messages=run_messages.messages,
|
|
2451
|
-
tools=_tools,
|
|
2452
|
-
tool_choice=self.tool_choice,
|
|
2453
|
-
tool_call_limit=self.tool_call_limit,
|
|
2454
|
-
response_format=response_format,
|
|
2455
|
-
send_media_to_model=self.send_media_to_model,
|
|
2456
|
-
run_response=run_response,
|
|
2457
|
-
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
2458
|
-
) # type: ignore
|
|
2451
|
+
# 6. Start memory creation in background task
|
|
2452
|
+
memory_task = await self._astart_memory_task(
|
|
2453
|
+
run_messages=run_messages,
|
|
2454
|
+
user_id=user_id,
|
|
2455
|
+
existing_task=memory_task,
|
|
2456
|
+
)
|
|
2459
2457
|
|
|
2460
|
-
|
|
2461
|
-
|
|
2458
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2459
|
+
# 7. Reason about the task if reasoning is enabled
|
|
2460
|
+
await self._ahandle_reasoning(run_response=run_response, run_messages=run_messages)
|
|
2462
2461
|
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
model_response=model_response, run_messages=run_messages
|
|
2466
|
-
)
|
|
2462
|
+
# Check for cancellation before model call
|
|
2463
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2467
2464
|
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2465
|
+
# 8. Get the model response for the team leader
|
|
2466
|
+
model_response = await self.model.aresponse(
|
|
2467
|
+
messages=run_messages.messages,
|
|
2468
|
+
tools=_tools,
|
|
2469
|
+
tool_choice=self.tool_choice,
|
|
2470
|
+
tool_call_limit=self.tool_call_limit,
|
|
2471
|
+
response_format=response_format,
|
|
2472
|
+
send_media_to_model=self.send_media_to_model,
|
|
2473
|
+
run_response=run_response,
|
|
2474
|
+
compression_manager=self.compression_manager if self.compress_tool_results else None,
|
|
2475
|
+
) # type: ignore
|
|
2472
2476
|
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
model_response=model_response,
|
|
2476
|
-
run_response=run_response,
|
|
2477
|
-
run_messages=run_messages,
|
|
2478
|
-
run_context=run_context,
|
|
2479
|
-
)
|
|
2477
|
+
# Check for cancellation after model call
|
|
2478
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2480
2479
|
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2480
|
+
# If an output model is provided, generate output using the output model
|
|
2481
|
+
await self._agenerate_response_with_output_model(
|
|
2482
|
+
model_response=model_response, run_messages=run_messages
|
|
2483
|
+
)
|
|
2484
2484
|
|
|
2485
|
-
|
|
2486
|
-
|
|
2485
|
+
# If a parser model is provided, structure the response separately
|
|
2486
|
+
await self._aparse_response_with_parser_model(
|
|
2487
|
+
model_response=model_response, run_messages=run_messages, run_context=run_context
|
|
2488
|
+
)
|
|
2487
2489
|
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2490
|
+
# 9. Update TeamRunOutput with the model response
|
|
2491
|
+
self._update_run_response(
|
|
2492
|
+
model_response=model_response,
|
|
2493
|
+
run_response=run_response,
|
|
2494
|
+
run_messages=run_messages,
|
|
2493
2495
|
run_context=run_context,
|
|
2494
|
-
|
|
2495
|
-
user_id=user_id,
|
|
2496
|
-
debug_mode=debug_mode,
|
|
2497
|
-
background_tasks=background_tasks,
|
|
2498
|
-
**kwargs,
|
|
2499
|
-
):
|
|
2500
|
-
pass
|
|
2496
|
+
)
|
|
2501
2497
|
|
|
2502
|
-
|
|
2498
|
+
# 10. Store media if enabled
|
|
2499
|
+
if self.store_media:
|
|
2500
|
+
store_media_util(run_response, model_response)
|
|
2503
2501
|
|
|
2504
|
-
|
|
2505
|
-
|
|
2502
|
+
# 11. Convert response to structured format
|
|
2503
|
+
self._convert_response_to_structured_format(run_response=run_response, run_context=run_context)
|
|
2506
2504
|
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2505
|
+
# 12. Execute post-hooks after output is generated but before response is returned
|
|
2506
|
+
if self.post_hooks is not None:
|
|
2507
|
+
async for _ in self._aexecute_post_hooks(
|
|
2508
|
+
hooks=self.post_hooks, # type: ignore
|
|
2509
|
+
run_output=run_response,
|
|
2510
|
+
run_context=run_context,
|
|
2511
|
+
session=team_session,
|
|
2512
|
+
user_id=user_id,
|
|
2513
|
+
debug_mode=debug_mode,
|
|
2514
|
+
background_tasks=background_tasks,
|
|
2515
|
+
**kwargs,
|
|
2516
|
+
):
|
|
2517
|
+
pass
|
|
2516
2518
|
|
|
2517
|
-
|
|
2518
|
-
run_response.status = RunStatus.completed
|
|
2519
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2519
2520
|
|
|
2520
|
-
|
|
2521
|
-
|
|
2521
|
+
# 13. Wait for background memory creation
|
|
2522
|
+
await await_for_open_threads(memory_task=memory_task)
|
|
2522
2523
|
|
|
2523
|
-
|
|
2524
|
-
|
|
2524
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2525
|
+
# 14. Create session summary
|
|
2526
|
+
if self.session_summary_manager is not None:
|
|
2527
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
2528
|
+
team_session.upsert_run(run_response=run_response)
|
|
2529
|
+
try:
|
|
2530
|
+
await self.session_summary_manager.acreate_session_summary(session=team_session)
|
|
2531
|
+
except Exception as e:
|
|
2532
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2525
2533
|
|
|
2526
|
-
|
|
2534
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2535
|
+
run_response.status = RunStatus.completed
|
|
2527
2536
|
|
|
2528
|
-
|
|
2537
|
+
# 15. Cleanup and store the run response and session
|
|
2538
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2529
2539
|
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
log_info(f"Run {run_response.run_id} was cancelled")
|
|
2533
|
-
run_response.content = str(e)
|
|
2534
|
-
run_response.status = RunStatus.cancelled
|
|
2540
|
+
# Log Team Telemetry
|
|
2541
|
+
await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
|
|
2535
2542
|
|
|
2536
|
-
|
|
2537
|
-
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2543
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2538
2544
|
|
|
2539
|
-
|
|
2545
|
+
return run_response
|
|
2540
2546
|
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
run_response,
|
|
2545
|
-
error=str(e),
|
|
2546
|
-
error_id=e.error_id,
|
|
2547
|
-
error_type=e.type,
|
|
2548
|
-
additional_data=e.additional_data,
|
|
2549
|
-
)
|
|
2550
|
-
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2551
|
-
if run_response.content is None:
|
|
2547
|
+
except RunCancelledException as e:
|
|
2548
|
+
# Handle run cancellation
|
|
2549
|
+
log_info(f"Run {run_response.run_id} was cancelled")
|
|
2552
2550
|
run_response.content = str(e)
|
|
2551
|
+
run_response.status = RunStatus.cancelled
|
|
2553
2552
|
|
|
2554
|
-
|
|
2553
|
+
# Cleanup and store the run response and session
|
|
2554
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2555
2555
|
|
|
2556
|
-
|
|
2556
|
+
return run_response
|
|
2557
2557
|
|
|
2558
|
-
|
|
2558
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2559
|
+
run_response.status = RunStatus.error
|
|
2560
|
+
run_error = create_team_run_error_event(
|
|
2561
|
+
run_response,
|
|
2562
|
+
error=str(e),
|
|
2563
|
+
error_id=e.error_id,
|
|
2564
|
+
error_type=e.type,
|
|
2565
|
+
additional_data=e.additional_data,
|
|
2566
|
+
)
|
|
2567
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2568
|
+
if run_response.content is None:
|
|
2569
|
+
run_response.content = str(e)
|
|
2559
2570
|
|
|
2560
|
-
|
|
2561
|
-
if attempt < num_attempts - 1:
|
|
2562
|
-
# Calculate delay with exponential backoff if enabled
|
|
2563
|
-
if self.exponential_backoff:
|
|
2564
|
-
delay = self.delay_between_retries * (2**attempt)
|
|
2565
|
-
else:
|
|
2566
|
-
delay = self.delay_between_retries
|
|
2571
|
+
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
2567
2572
|
|
|
2568
|
-
|
|
2569
|
-
time.sleep(delay)
|
|
2570
|
-
continue
|
|
2573
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2571
2574
|
|
|
2572
|
-
|
|
2573
|
-
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2575
|
+
return run_response
|
|
2574
2576
|
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
+
except Exception as e:
|
|
2578
|
+
if attempt < num_attempts - 1:
|
|
2579
|
+
# Calculate delay with exponential backoff if enabled
|
|
2580
|
+
if self.exponential_backoff:
|
|
2581
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2582
|
+
else:
|
|
2583
|
+
delay = self.delay_between_retries
|
|
2584
|
+
|
|
2585
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2586
|
+
time.sleep(delay)
|
|
2587
|
+
continue
|
|
2577
2588
|
|
|
2578
|
-
|
|
2589
|
+
run_error = create_team_run_error_event(run_response, error=str(e))
|
|
2590
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2579
2591
|
|
|
2580
|
-
|
|
2581
|
-
|
|
2592
|
+
if run_response.content is None:
|
|
2593
|
+
run_response.content = str(e)
|
|
2582
2594
|
|
|
2583
|
-
|
|
2595
|
+
log_error(f"Error in Team run: {str(e)}")
|
|
2584
2596
|
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2597
|
+
# Cleanup and store the run response and session
|
|
2598
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2599
|
+
|
|
2600
|
+
return run_response
|
|
2601
|
+
finally:
|
|
2602
|
+
# Always disconnect connectable tools
|
|
2603
|
+
self._disconnect_connectable_tools()
|
|
2604
|
+
await self._disconnect_mcp_tools()
|
|
2605
|
+
|
|
2606
|
+
# Cancel background task on error (await_for_open_threads handles waiting on success)
|
|
2607
|
+
if memory_task is not None and not memory_task.done():
|
|
2608
|
+
memory_task.cancel()
|
|
2609
|
+
try:
|
|
2610
|
+
await memory_task
|
|
2611
|
+
except asyncio.CancelledError:
|
|
2612
|
+
pass
|
|
2596
2613
|
|
|
2597
|
-
|
|
2598
|
-
|
|
2614
|
+
# Always clean up the run tracking
|
|
2615
|
+
await acleanup_run(run_response.run_id) # type: ignore
|
|
2599
2616
|
|
|
2600
2617
|
return run_response
|
|
2601
2618
|
|
|
@@ -2633,365 +2650,362 @@ class Team:
|
|
|
2633
2650
|
12. Create session summary
|
|
2634
2651
|
13. Cleanup and store (scrub, add to session, calculate metrics, save session)
|
|
2635
2652
|
"""
|
|
2653
|
+
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
2636
2654
|
|
|
2637
2655
|
memory_task = None
|
|
2638
2656
|
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2657
|
+
try:
|
|
2658
|
+
# Set up retry logic
|
|
2659
|
+
num_attempts = self.retries + 1
|
|
2660
|
+
for attempt in range(num_attempts):
|
|
2661
|
+
if num_attempts > 1:
|
|
2662
|
+
log_debug(f"Retrying Team run {run_response.run_id}. Attempt {attempt + 1} of {num_attempts}...")
|
|
2644
2663
|
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2664
|
+
try:
|
|
2665
|
+
# 1. Resolve dependencies
|
|
2666
|
+
if run_context.dependencies is not None:
|
|
2667
|
+
await self._aresolve_run_dependencies(run_context=run_context)
|
|
2649
2668
|
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2669
|
+
# 2. Read or create session. Reads from the database if provided.
|
|
2670
|
+
if self._has_async_db():
|
|
2671
|
+
team_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
2672
|
+
else:
|
|
2673
|
+
team_session = self._read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2655
2674
|
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2675
|
+
# 3. Update metadata and session state
|
|
2676
|
+
self._update_metadata(session=team_session)
|
|
2677
|
+
# Initialize session state
|
|
2678
|
+
run_context.session_state = self._initialize_session_state(
|
|
2679
|
+
session_state=run_context.session_state if run_context.session_state is not None else {},
|
|
2680
|
+
user_id=user_id,
|
|
2681
|
+
session_id=session_id,
|
|
2682
|
+
run_id=run_response.run_id,
|
|
2683
|
+
)
|
|
2684
|
+
# Update session state from DB
|
|
2685
|
+
if run_context.session_state is not None:
|
|
2686
|
+
run_context.session_state = self._load_session_state(
|
|
2687
|
+
session=team_session, session_state=run_context.session_state
|
|
2688
|
+
) # type: ignore
|
|
2670
2689
|
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2690
|
+
# 4. Execute pre-hooks
|
|
2691
|
+
run_input = cast(TeamRunInput, run_response.input)
|
|
2692
|
+
self.model = cast(Model, self.model)
|
|
2693
|
+
if self.pre_hooks is not None:
|
|
2694
|
+
pre_hook_iterator = self._aexecute_pre_hooks(
|
|
2695
|
+
hooks=self.pre_hooks, # type: ignore
|
|
2696
|
+
run_response=run_response,
|
|
2697
|
+
run_context=run_context,
|
|
2698
|
+
run_input=run_input,
|
|
2699
|
+
session=team_session,
|
|
2700
|
+
user_id=user_id,
|
|
2701
|
+
debug_mode=debug_mode,
|
|
2702
|
+
stream_events=stream_events,
|
|
2703
|
+
background_tasks=background_tasks,
|
|
2704
|
+
**kwargs,
|
|
2705
|
+
)
|
|
2706
|
+
async for pre_hook_event in pre_hook_iterator:
|
|
2707
|
+
yield pre_hook_event
|
|
2708
|
+
|
|
2709
|
+
# 5. Determine tools for model
|
|
2710
|
+
team_run_context: Dict[str, Any] = {}
|
|
2711
|
+
self.model = cast(Model, self.model)
|
|
2712
|
+
await self._check_and_refresh_mcp_tools()
|
|
2713
|
+
_tools = self._determine_tools_for_model(
|
|
2714
|
+
model=self.model,
|
|
2677
2715
|
run_response=run_response,
|
|
2678
2716
|
run_context=run_context,
|
|
2679
|
-
|
|
2680
|
-
session=team_session,
|
|
2717
|
+
team_run_context=team_run_context,
|
|
2718
|
+
session=team_session, # type: ignore
|
|
2681
2719
|
user_id=user_id,
|
|
2720
|
+
async_mode=True,
|
|
2721
|
+
input_message=run_input.input_content,
|
|
2722
|
+
images=run_input.images,
|
|
2723
|
+
videos=run_input.videos,
|
|
2724
|
+
audio=run_input.audios,
|
|
2725
|
+
files=run_input.files,
|
|
2682
2726
|
debug_mode=debug_mode,
|
|
2727
|
+
add_history_to_context=add_history_to_context,
|
|
2728
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2729
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2730
|
+
stream=True,
|
|
2683
2731
|
stream_events=stream_events,
|
|
2684
|
-
background_tasks=background_tasks,
|
|
2685
|
-
**kwargs,
|
|
2686
2732
|
)
|
|
2687
|
-
async for pre_hook_event in pre_hook_iterator:
|
|
2688
|
-
yield pre_hook_event
|
|
2689
|
-
|
|
2690
|
-
# 5. Determine tools for model
|
|
2691
|
-
team_run_context: Dict[str, Any] = {}
|
|
2692
|
-
self.model = cast(Model, self.model)
|
|
2693
|
-
await self._check_and_refresh_mcp_tools()
|
|
2694
|
-
_tools = self._determine_tools_for_model(
|
|
2695
|
-
model=self.model,
|
|
2696
|
-
run_response=run_response,
|
|
2697
|
-
run_context=run_context,
|
|
2698
|
-
team_run_context=team_run_context,
|
|
2699
|
-
session=team_session, # type: ignore
|
|
2700
|
-
user_id=user_id,
|
|
2701
|
-
async_mode=True,
|
|
2702
|
-
input_message=run_input.input_content,
|
|
2703
|
-
images=run_input.images,
|
|
2704
|
-
videos=run_input.videos,
|
|
2705
|
-
audio=run_input.audios,
|
|
2706
|
-
files=run_input.files,
|
|
2707
|
-
debug_mode=debug_mode,
|
|
2708
|
-
add_history_to_context=add_history_to_context,
|
|
2709
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2710
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2711
|
-
stream=True,
|
|
2712
|
-
stream_events=stream_events,
|
|
2713
|
-
)
|
|
2714
|
-
|
|
2715
|
-
# 6. Prepare run messages
|
|
2716
|
-
run_messages = await self._aget_run_messages(
|
|
2717
|
-
run_response=run_response,
|
|
2718
|
-
run_context=run_context,
|
|
2719
|
-
session=team_session, # type: ignore
|
|
2720
|
-
user_id=user_id,
|
|
2721
|
-
input_message=run_input.input_content,
|
|
2722
|
-
audio=run_input.audios,
|
|
2723
|
-
images=run_input.images,
|
|
2724
|
-
videos=run_input.videos,
|
|
2725
|
-
files=run_input.files,
|
|
2726
|
-
add_history_to_context=add_history_to_context,
|
|
2727
|
-
add_dependencies_to_context=add_dependencies_to_context,
|
|
2728
|
-
add_session_state_to_context=add_session_state_to_context,
|
|
2729
|
-
tools=_tools,
|
|
2730
|
-
**kwargs,
|
|
2731
|
-
)
|
|
2732
|
-
|
|
2733
|
-
log_debug(f"Team Run Start: {run_response.run_id}", center=True)
|
|
2734
|
-
|
|
2735
|
-
# 7. Start memory creation in background task
|
|
2736
|
-
memory_task = None
|
|
2737
|
-
if (
|
|
2738
|
-
run_messages.user_message is not None
|
|
2739
|
-
and self.memory_manager is not None
|
|
2740
|
-
and self.enable_user_memories
|
|
2741
|
-
and not self.enable_agentic_memory
|
|
2742
|
-
):
|
|
2743
|
-
log_debug("Starting memory creation in background task.")
|
|
2744
|
-
memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
2745
2733
|
|
|
2746
|
-
|
|
2747
|
-
|
|
2734
|
+
# 6. Prepare run messages
|
|
2735
|
+
run_messages = await self._aget_run_messages(
|
|
2736
|
+
run_response=run_response,
|
|
2737
|
+
run_context=run_context,
|
|
2738
|
+
session=team_session, # type: ignore
|
|
2739
|
+
user_id=user_id,
|
|
2740
|
+
input_message=run_input.input_content,
|
|
2741
|
+
audio=run_input.audios,
|
|
2742
|
+
images=run_input.images,
|
|
2743
|
+
videos=run_input.videos,
|
|
2744
|
+
files=run_input.files,
|
|
2745
|
+
add_history_to_context=add_history_to_context,
|
|
2746
|
+
add_dependencies_to_context=add_dependencies_to_context,
|
|
2747
|
+
add_session_state_to_context=add_session_state_to_context,
|
|
2748
|
+
tools=_tools,
|
|
2749
|
+
**kwargs,
|
|
2750
|
+
)
|
|
2748
2751
|
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
events_to_skip=self.events_to_skip,
|
|
2755
|
-
store_events=self.store_events,
|
|
2752
|
+
# 7. Start memory creation in background task
|
|
2753
|
+
memory_task = await self._astart_memory_task(
|
|
2754
|
+
run_messages=run_messages,
|
|
2755
|
+
user_id=user_id,
|
|
2756
|
+
existing_task=memory_task,
|
|
2756
2757
|
)
|
|
2757
2758
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
run_response=run_response,
|
|
2761
|
-
run_messages=run_messages,
|
|
2762
|
-
stream_events=stream_events,
|
|
2763
|
-
):
|
|
2764
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2765
|
-
yield item
|
|
2759
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
2760
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
2766
2761
|
|
|
2767
|
-
|
|
2768
|
-
|
|
2762
|
+
# Yield the run started event
|
|
2763
|
+
if stream_events:
|
|
2764
|
+
yield handle_event( # type: ignore
|
|
2765
|
+
create_team_run_started_event(from_run_response=run_response),
|
|
2766
|
+
run_response,
|
|
2767
|
+
events_to_skip=self.events_to_skip,
|
|
2768
|
+
store_events=self.store_events,
|
|
2769
|
+
)
|
|
2769
2770
|
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
async for event in self._ahandle_model_response_stream(
|
|
2773
|
-
session=team_session,
|
|
2774
|
-
run_response=run_response,
|
|
2775
|
-
run_messages=run_messages,
|
|
2776
|
-
tools=_tools,
|
|
2777
|
-
response_format=response_format,
|
|
2778
|
-
stream_events=stream_events,
|
|
2779
|
-
session_state=run_context.session_state,
|
|
2780
|
-
run_context=run_context,
|
|
2781
|
-
):
|
|
2782
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2783
|
-
yield event
|
|
2784
|
-
else:
|
|
2785
|
-
async for event in self._ahandle_model_response_stream(
|
|
2786
|
-
session=team_session,
|
|
2771
|
+
# 8. Reason about the task if reasoning is enabled
|
|
2772
|
+
async for item in self._ahandle_reasoning_stream(
|
|
2787
2773
|
run_response=run_response,
|
|
2788
2774
|
run_messages=run_messages,
|
|
2789
|
-
tools=_tools,
|
|
2790
|
-
response_format=response_format,
|
|
2791
2775
|
stream_events=stream_events,
|
|
2792
|
-
session_state=run_context.session_state,
|
|
2793
|
-
run_context=run_context,
|
|
2794
2776
|
):
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
if isinstance(event, RunContentEvent):
|
|
2799
|
-
if stream_events:
|
|
2800
|
-
yield IntermediateRunContentEvent(
|
|
2801
|
-
content=event.content,
|
|
2802
|
-
content_type=event.content_type,
|
|
2803
|
-
)
|
|
2804
|
-
else:
|
|
2805
|
-
yield event
|
|
2777
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2778
|
+
yield item
|
|
2806
2779
|
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
run_response=run_response,
|
|
2810
|
-
run_messages=run_messages,
|
|
2811
|
-
stream_events=stream_events,
|
|
2812
|
-
):
|
|
2813
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2814
|
-
yield event
|
|
2780
|
+
# Check for cancellation before model processing
|
|
2781
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2815
2782
|
|
|
2816
|
-
|
|
2817
|
-
|
|
2783
|
+
# 9. Get a response from the model
|
|
2784
|
+
if self.output_model is None:
|
|
2785
|
+
async for event in self._ahandle_model_response_stream(
|
|
2786
|
+
session=team_session,
|
|
2787
|
+
run_response=run_response,
|
|
2788
|
+
run_messages=run_messages,
|
|
2789
|
+
tools=_tools,
|
|
2790
|
+
response_format=response_format,
|
|
2791
|
+
stream_events=stream_events,
|
|
2792
|
+
session_state=run_context.session_state,
|
|
2793
|
+
run_context=run_context,
|
|
2794
|
+
):
|
|
2795
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2796
|
+
yield event
|
|
2797
|
+
else:
|
|
2798
|
+
async for event in self._ahandle_model_response_stream(
|
|
2799
|
+
session=team_session,
|
|
2800
|
+
run_response=run_response,
|
|
2801
|
+
run_messages=run_messages,
|
|
2802
|
+
tools=_tools,
|
|
2803
|
+
response_format=response_format,
|
|
2804
|
+
stream_events=stream_events,
|
|
2805
|
+
session_state=run_context.session_state,
|
|
2806
|
+
run_context=run_context,
|
|
2807
|
+
):
|
|
2808
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2809
|
+
from agno.run.team import IntermediateRunContentEvent, RunContentEvent
|
|
2810
|
+
|
|
2811
|
+
if isinstance(event, RunContentEvent):
|
|
2812
|
+
if stream_events:
|
|
2813
|
+
yield IntermediateRunContentEvent(
|
|
2814
|
+
content=event.content,
|
|
2815
|
+
content_type=event.content_type,
|
|
2816
|
+
)
|
|
2817
|
+
else:
|
|
2818
|
+
yield event
|
|
2818
2819
|
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2820
|
+
async for event in self._agenerate_response_with_output_model_stream(
|
|
2821
|
+
session=team_session,
|
|
2822
|
+
run_response=run_response,
|
|
2823
|
+
run_messages=run_messages,
|
|
2824
|
+
stream_events=stream_events,
|
|
2825
|
+
):
|
|
2826
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2827
|
+
yield event
|
|
2827
2828
|
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
yield handle_event( # type: ignore
|
|
2831
|
-
create_team_run_content_completed_event(from_run_response=run_response),
|
|
2832
|
-
run_response,
|
|
2833
|
-
events_to_skip=self.events_to_skip,
|
|
2834
|
-
store_events=self.store_events,
|
|
2835
|
-
)
|
|
2829
|
+
# Check for cancellation after model processing
|
|
2830
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2836
2831
|
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
async for event in self._aexecute_post_hooks(
|
|
2840
|
-
hooks=self.post_hooks, # type: ignore
|
|
2841
|
-
run_output=run_response,
|
|
2842
|
-
run_context=run_context,
|
|
2832
|
+
# 10. Parse response with parser model if provided
|
|
2833
|
+
async for event in self._aparse_response_with_parser_model_stream(
|
|
2843
2834
|
session=team_session,
|
|
2844
|
-
|
|
2845
|
-
debug_mode=debug_mode,
|
|
2835
|
+
run_response=run_response,
|
|
2846
2836
|
stream_events=stream_events,
|
|
2847
|
-
|
|
2848
|
-
**kwargs,
|
|
2837
|
+
run_context=run_context,
|
|
2849
2838
|
):
|
|
2850
2839
|
yield event
|
|
2851
2840
|
|
|
2852
|
-
|
|
2853
|
-
# 11. Wait for background memory creation
|
|
2854
|
-
async for event in await_for_thread_tasks_stream(
|
|
2855
|
-
run_response=run_response,
|
|
2856
|
-
memory_task=memory_task,
|
|
2857
|
-
stream_events=stream_events,
|
|
2858
|
-
events_to_skip=self.events_to_skip, # type: ignore
|
|
2859
|
-
store_events=self.store_events,
|
|
2860
|
-
):
|
|
2861
|
-
yield event
|
|
2862
|
-
|
|
2863
|
-
raise_if_cancelled(run_response.run_id) # type: ignore
|
|
2864
|
-
|
|
2865
|
-
# 12. Create session summary
|
|
2866
|
-
if self.session_summary_manager is not None:
|
|
2867
|
-
# Upsert the RunOutput to Team Session before creating the session summary
|
|
2868
|
-
team_session.upsert_run(run_response=run_response)
|
|
2869
|
-
|
|
2870
|
-
if stream_events:
|
|
2871
|
-
yield handle_event( # type: ignore
|
|
2872
|
-
create_team_session_summary_started_event(from_run_response=run_response),
|
|
2873
|
-
run_response,
|
|
2874
|
-
events_to_skip=self.events_to_skip,
|
|
2875
|
-
store_events=self.store_events,
|
|
2876
|
-
)
|
|
2877
|
-
try:
|
|
2878
|
-
await self.session_summary_manager.acreate_session_summary(session=team_session)
|
|
2879
|
-
except Exception as e:
|
|
2880
|
-
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2841
|
+
# Yield RunContentCompletedEvent
|
|
2881
2842
|
if stream_events:
|
|
2882
2843
|
yield handle_event( # type: ignore
|
|
2883
|
-
|
|
2884
|
-
from_run_response=run_response, session_summary=team_session.summary
|
|
2885
|
-
),
|
|
2844
|
+
create_team_run_content_completed_event(from_run_response=run_response),
|
|
2886
2845
|
run_response,
|
|
2887
2846
|
events_to_skip=self.events_to_skip,
|
|
2888
2847
|
store_events=self.store_events,
|
|
2889
2848
|
)
|
|
2890
2849
|
|
|
2891
|
-
|
|
2850
|
+
# Execute post-hooks after output is generated but before response is returned
|
|
2851
|
+
if self.post_hooks is not None:
|
|
2852
|
+
async for event in self._aexecute_post_hooks(
|
|
2853
|
+
hooks=self.post_hooks, # type: ignore
|
|
2854
|
+
run_output=run_response,
|
|
2855
|
+
run_context=run_context,
|
|
2856
|
+
session=team_session,
|
|
2857
|
+
user_id=user_id,
|
|
2858
|
+
debug_mode=debug_mode,
|
|
2859
|
+
stream_events=stream_events,
|
|
2860
|
+
background_tasks=background_tasks,
|
|
2861
|
+
**kwargs,
|
|
2862
|
+
):
|
|
2863
|
+
yield event
|
|
2892
2864
|
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
2865
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2866
|
+
# 11. Wait for background memory creation
|
|
2867
|
+
async for event in await_for_thread_tasks_stream(
|
|
2868
|
+
run_response=run_response,
|
|
2869
|
+
memory_task=memory_task,
|
|
2870
|
+
stream_events=stream_events,
|
|
2871
|
+
events_to_skip=self.events_to_skip, # type: ignore
|
|
2872
|
+
store_events=self.store_events,
|
|
2873
|
+
):
|
|
2874
|
+
yield event
|
|
2900
2875
|
|
|
2901
|
-
|
|
2902
|
-
run_response.status = RunStatus.completed
|
|
2876
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2903
2877
|
|
|
2904
|
-
|
|
2905
|
-
|
|
2878
|
+
# 12. Create session summary
|
|
2879
|
+
if self.session_summary_manager is not None:
|
|
2880
|
+
# Upsert the RunOutput to Team Session before creating the session summary
|
|
2881
|
+
team_session.upsert_run(run_response=run_response)
|
|
2906
2882
|
|
|
2907
|
-
|
|
2908
|
-
|
|
2883
|
+
if stream_events:
|
|
2884
|
+
yield handle_event( # type: ignore
|
|
2885
|
+
create_team_session_summary_started_event(from_run_response=run_response),
|
|
2886
|
+
run_response,
|
|
2887
|
+
events_to_skip=self.events_to_skip,
|
|
2888
|
+
store_events=self.store_events,
|
|
2889
|
+
)
|
|
2890
|
+
try:
|
|
2891
|
+
await self.session_summary_manager.acreate_session_summary(session=team_session)
|
|
2892
|
+
except Exception as e:
|
|
2893
|
+
log_warning(f"Error in session summary creation: {str(e)}")
|
|
2894
|
+
if stream_events:
|
|
2895
|
+
yield handle_event( # type: ignore
|
|
2896
|
+
create_team_session_summary_completed_event(
|
|
2897
|
+
from_run_response=run_response, session_summary=team_session.summary
|
|
2898
|
+
),
|
|
2899
|
+
run_response,
|
|
2900
|
+
events_to_skip=self.events_to_skip,
|
|
2901
|
+
store_events=self.store_events,
|
|
2902
|
+
)
|
|
2909
2903
|
|
|
2910
|
-
|
|
2911
|
-
|
|
2904
|
+
await araise_if_cancelled(run_response.run_id) # type: ignore
|
|
2905
|
+
|
|
2906
|
+
# Create the run completed event
|
|
2907
|
+
completed_event = handle_event(
|
|
2908
|
+
create_team_run_completed_event(from_run_response=run_response),
|
|
2909
|
+
run_response,
|
|
2910
|
+
events_to_skip=self.events_to_skip,
|
|
2911
|
+
store_events=self.store_events,
|
|
2912
|
+
)
|
|
2912
2913
|
|
|
2913
|
-
|
|
2914
|
-
|
|
2914
|
+
# Set the run status to completed
|
|
2915
|
+
run_response.status = RunStatus.completed
|
|
2915
2916
|
|
|
2916
|
-
|
|
2917
|
+
# 13. Cleanup and store the run response and session
|
|
2918
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2917
2919
|
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
log_info(f"Team run {run_response.run_id} was cancelled during async streaming")
|
|
2921
|
-
run_response.status = RunStatus.cancelled
|
|
2922
|
-
run_response.content = str(e)
|
|
2920
|
+
if stream_events:
|
|
2921
|
+
yield completed_event
|
|
2923
2922
|
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2927
|
-
run_response,
|
|
2928
|
-
events_to_skip=self.events_to_skip,
|
|
2929
|
-
store_events=self.store_events,
|
|
2930
|
-
)
|
|
2923
|
+
if yield_run_output:
|
|
2924
|
+
yield run_response
|
|
2931
2925
|
|
|
2932
|
-
|
|
2933
|
-
|
|
2926
|
+
# Log Team Telemetry
|
|
2927
|
+
await self._alog_team_telemetry(session_id=team_session.session_id, run_id=run_response.run_id)
|
|
2934
2928
|
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
error_type=e.type,
|
|
2942
|
-
additional_data=e.additional_data,
|
|
2943
|
-
)
|
|
2944
|
-
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2945
|
-
if run_response.content is None:
|
|
2929
|
+
log_debug(f"Team Run End: {run_response.run_id}", center=True, symbol="*")
|
|
2930
|
+
break
|
|
2931
|
+
except RunCancelledException as e:
|
|
2932
|
+
# Handle run cancellation during async streaming
|
|
2933
|
+
log_info(f"Team run {run_response.run_id} was cancelled during async streaming")
|
|
2934
|
+
run_response.status = RunStatus.cancelled
|
|
2946
2935
|
run_response.content = str(e)
|
|
2947
2936
|
|
|
2948
|
-
|
|
2937
|
+
# Yield the cancellation event
|
|
2938
|
+
yield handle_event( # type: ignore
|
|
2939
|
+
create_team_run_cancelled_event(from_run_response=run_response, reason=str(e)),
|
|
2940
|
+
run_response,
|
|
2941
|
+
events_to_skip=self.events_to_skip,
|
|
2942
|
+
store_events=self.store_events,
|
|
2943
|
+
)
|
|
2944
|
+
|
|
2945
|
+
# Cleanup and store the run response and session
|
|
2946
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2949
2947
|
|
|
2950
|
-
|
|
2948
|
+
except (InputCheckError, OutputCheckError) as e:
|
|
2949
|
+
run_response.status = RunStatus.error
|
|
2950
|
+
run_error = create_team_run_error_event(
|
|
2951
|
+
run_response,
|
|
2952
|
+
error=str(e),
|
|
2953
|
+
error_id=e.error_id,
|
|
2954
|
+
error_type=e.type,
|
|
2955
|
+
additional_data=e.additional_data,
|
|
2956
|
+
)
|
|
2957
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2958
|
+
if run_response.content is None:
|
|
2959
|
+
run_response.content = str(e)
|
|
2951
2960
|
|
|
2952
|
-
|
|
2961
|
+
log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
|
|
2953
2962
|
|
|
2954
|
-
|
|
2963
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2955
2964
|
|
|
2956
|
-
|
|
2957
|
-
if attempt < num_attempts - 1:
|
|
2958
|
-
# Calculate delay with exponential backoff if enabled
|
|
2959
|
-
if self.exponential_backoff:
|
|
2960
|
-
delay = self.delay_between_retries * (2**attempt)
|
|
2961
|
-
else:
|
|
2962
|
-
delay = self.delay_between_retries
|
|
2965
|
+
yield run_error
|
|
2963
2966
|
|
|
2964
|
-
|
|
2965
|
-
time.sleep(delay)
|
|
2966
|
-
continue
|
|
2967
|
+
break
|
|
2967
2968
|
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2969
|
+
except Exception as e:
|
|
2970
|
+
if attempt < num_attempts - 1:
|
|
2971
|
+
# Calculate delay with exponential backoff if enabled
|
|
2972
|
+
if self.exponential_backoff:
|
|
2973
|
+
delay = self.delay_between_retries * (2**attempt)
|
|
2974
|
+
else:
|
|
2975
|
+
delay = self.delay_between_retries
|
|
2976
|
+
|
|
2977
|
+
log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
|
|
2978
|
+
time.sleep(delay)
|
|
2979
|
+
continue
|
|
2973
2980
|
|
|
2974
|
-
|
|
2981
|
+
run_response.status = RunStatus.error
|
|
2982
|
+
run_error = create_team_run_error_event(run_response, error=str(e))
|
|
2983
|
+
run_response.events = add_team_error_event(error=run_error, events=run_response.events)
|
|
2984
|
+
if run_response.content is None:
|
|
2985
|
+
run_response.content = str(e)
|
|
2975
2986
|
|
|
2976
|
-
|
|
2977
|
-
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2987
|
+
log_error(f"Error in Team run: {str(e)}")
|
|
2978
2988
|
|
|
2979
|
-
|
|
2989
|
+
# Cleanup and store the run response and session
|
|
2990
|
+
await self._acleanup_and_store(run_response=run_response, session=team_session)
|
|
2980
2991
|
|
|
2981
|
-
|
|
2982
|
-
# Always disconnect connectable tools
|
|
2983
|
-
self._disconnect_connectable_tools()
|
|
2984
|
-
await self._disconnect_mcp_tools()
|
|
2985
|
-
# Cancel the memory task if it's still running
|
|
2986
|
-
if memory_task is not None and not memory_task.done():
|
|
2987
|
-
memory_task.cancel()
|
|
2988
|
-
try:
|
|
2989
|
-
await memory_task
|
|
2990
|
-
except asyncio.CancelledError:
|
|
2991
|
-
pass
|
|
2992
|
+
yield run_error
|
|
2992
2993
|
|
|
2993
|
-
|
|
2994
|
-
|
|
2994
|
+
finally:
|
|
2995
|
+
# Always disconnect connectable tools
|
|
2996
|
+
self._disconnect_connectable_tools()
|
|
2997
|
+
await self._disconnect_mcp_tools()
|
|
2998
|
+
|
|
2999
|
+
# Cancel background task on error (await_for_thread_tasks_stream handles waiting on success)
|
|
3000
|
+
if memory_task is not None and not memory_task.done():
|
|
3001
|
+
memory_task.cancel()
|
|
3002
|
+
try:
|
|
3003
|
+
await memory_task
|
|
3004
|
+
except asyncio.CancelledError:
|
|
3005
|
+
pass
|
|
3006
|
+
|
|
3007
|
+
# Always clean up the run tracking
|
|
3008
|
+
await acleanup_run(run_response.run_id) # type: ignore
|
|
2995
3009
|
|
|
2996
3010
|
@overload
|
|
2997
3011
|
async def arun(
|
|
@@ -3083,7 +3097,6 @@ class Team:
|
|
|
3083
3097
|
|
|
3084
3098
|
# Set the id for the run and register it immediately for cancellation tracking
|
|
3085
3099
|
run_id = run_id or str(uuid4())
|
|
3086
|
-
register_run(run_id)
|
|
3087
3100
|
|
|
3088
3101
|
if (add_history_to_context or self.add_history_to_context) and not self.db and not self.parent_team_id:
|
|
3089
3102
|
log_warning(
|
|
@@ -3982,6 +3995,74 @@ class Team:
|
|
|
3982
3995
|
team_id=self.id,
|
|
3983
3996
|
)
|
|
3984
3997
|
|
|
3998
|
+
async def _astart_memory_task(
|
|
3999
|
+
self,
|
|
4000
|
+
run_messages: RunMessages,
|
|
4001
|
+
user_id: Optional[str],
|
|
4002
|
+
existing_task: Optional[asyncio.Task[None]],
|
|
4003
|
+
) -> Optional[asyncio.Task[None]]:
|
|
4004
|
+
"""Cancel any existing memory task and start a new one if conditions are met.
|
|
4005
|
+
|
|
4006
|
+
Args:
|
|
4007
|
+
run_messages: The run messages containing the user message.
|
|
4008
|
+
user_id: The user ID for memory creation.
|
|
4009
|
+
existing_task: An existing memory task to cancel before starting a new one.
|
|
4010
|
+
|
|
4011
|
+
Returns:
|
|
4012
|
+
A new memory task if conditions are met, None otherwise.
|
|
4013
|
+
"""
|
|
4014
|
+
# Cancel any existing task from a previous retry attempt
|
|
4015
|
+
if existing_task is not None and not existing_task.done():
|
|
4016
|
+
existing_task.cancel()
|
|
4017
|
+
try:
|
|
4018
|
+
await existing_task
|
|
4019
|
+
except asyncio.CancelledError:
|
|
4020
|
+
pass
|
|
4021
|
+
|
|
4022
|
+
# Create new task if conditions are met
|
|
4023
|
+
if (
|
|
4024
|
+
run_messages.user_message is not None
|
|
4025
|
+
and self.memory_manager is not None
|
|
4026
|
+
and self.enable_user_memories
|
|
4027
|
+
and not self.enable_agentic_memory
|
|
4028
|
+
):
|
|
4029
|
+
log_debug("Starting memory creation in background task.")
|
|
4030
|
+
return asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
|
|
4031
|
+
|
|
4032
|
+
return None
|
|
4033
|
+
|
|
4034
|
+
def _start_memory_future(
|
|
4035
|
+
self,
|
|
4036
|
+
run_messages: RunMessages,
|
|
4037
|
+
user_id: Optional[str],
|
|
4038
|
+
existing_future: Optional[Future[None]],
|
|
4039
|
+
) -> Optional[Future[None]]:
|
|
4040
|
+
"""Cancel any existing memory future and start a new one if conditions are met.
|
|
4041
|
+
|
|
4042
|
+
Args:
|
|
4043
|
+
run_messages: The run messages containing the user message.
|
|
4044
|
+
user_id: The user ID for memory creation.
|
|
4045
|
+
existing_future: An existing memory future to cancel before starting a new one.
|
|
4046
|
+
|
|
4047
|
+
Returns:
|
|
4048
|
+
A new memory future if conditions are met, None otherwise.
|
|
4049
|
+
"""
|
|
4050
|
+
# Cancel any existing future from a previous retry attempt
|
|
4051
|
+
if existing_future is not None and not existing_future.done():
|
|
4052
|
+
existing_future.cancel()
|
|
4053
|
+
|
|
4054
|
+
# Create new future if conditions are met
|
|
4055
|
+
if (
|
|
4056
|
+
run_messages.user_message is not None
|
|
4057
|
+
and self.memory_manager is not None
|
|
4058
|
+
and self.enable_user_memories
|
|
4059
|
+
and not self.enable_agentic_memory
|
|
4060
|
+
):
|
|
4061
|
+
log_debug("Starting memory creation in background thread.")
|
|
4062
|
+
return self.background_executor.submit(self._make_memories, run_messages=run_messages, user_id=user_id)
|
|
4063
|
+
|
|
4064
|
+
return None
|
|
4065
|
+
|
|
3985
4066
|
def _get_response_format(
|
|
3986
4067
|
self, model: Optional[Model] = None, run_context: Optional[RunContext] = None
|
|
3987
4068
|
) -> Optional[Union[Dict, Type[BaseModel]]]:
|