agno 2.1.10__py3-none-any.whl → 2.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agno/agent/agent.py +1594 -1248
- agno/knowledge/knowledge.py +11 -0
- agno/knowledge/reader/pptx_reader.py +101 -0
- agno/knowledge/reader/reader_factory.py +14 -0
- agno/knowledge/types.py +1 -0
- agno/models/anthropic/claude.py +2 -2
- agno/models/base.py +4 -4
- agno/models/ollama/chat.py +7 -2
- agno/os/app.py +1 -1
- agno/os/interfaces/a2a/router.py +2 -2
- agno/os/interfaces/agui/router.py +2 -2
- agno/os/router.py +7 -7
- agno/os/routers/evals/schemas.py +31 -31
- agno/os/routers/health.py +6 -2
- agno/os/routers/knowledge/schemas.py +49 -47
- agno/os/routers/memory/schemas.py +16 -16
- agno/os/routers/metrics/schemas.py +16 -16
- agno/os/routers/session/session.py +382 -7
- agno/os/schema.py +254 -231
- agno/os/utils.py +1 -1
- agno/run/agent.py +54 -1
- agno/run/team.py +48 -0
- agno/run/workflow.py +15 -5
- agno/session/summary.py +45 -13
- agno/session/team.py +90 -5
- agno/team/team.py +1130 -849
- agno/utils/agent.py +372 -0
- agno/utils/events.py +144 -2
- agno/utils/message.py +60 -0
- agno/utils/print_response/agent.py +10 -6
- agno/utils/print_response/team.py +6 -4
- agno/utils/print_response/workflow.py +7 -5
- agno/utils/team.py +9 -8
- agno/workflow/condition.py +17 -9
- agno/workflow/loop.py +18 -10
- agno/workflow/parallel.py +14 -6
- agno/workflow/router.py +16 -8
- agno/workflow/step.py +14 -6
- agno/workflow/steps.py +14 -6
- agno/workflow/workflow.py +331 -123
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/METADATA +63 -23
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/RECORD +45 -43
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/WHEEL +0 -0
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.10.dist-info → agno-2.2.1.dist-info}/top_level.txt +0 -0
agno/workflow/workflow.py
CHANGED
|
@@ -29,7 +29,7 @@ from agno.exceptions import InputCheckError, OutputCheckError, RunCancelledExcep
|
|
|
29
29
|
from agno.media import Audio, File, Image, Video
|
|
30
30
|
from agno.models.message import Message
|
|
31
31
|
from agno.models.metrics import Metrics
|
|
32
|
-
from agno.run.agent import RunEvent
|
|
32
|
+
from agno.run.agent import RunContentEvent, RunEvent
|
|
33
33
|
from agno.run.base import RunStatus
|
|
34
34
|
from agno.run.cancel import (
|
|
35
35
|
cancel_run as cancel_run_global,
|
|
@@ -39,6 +39,7 @@ from agno.run.cancel import (
|
|
|
39
39
|
raise_if_cancelled,
|
|
40
40
|
register_run,
|
|
41
41
|
)
|
|
42
|
+
from agno.run.team import RunContentEvent as TeamRunContentEvent
|
|
42
43
|
from agno.run.team import TeamRunEvent
|
|
43
44
|
from agno.run.workflow import (
|
|
44
45
|
StepOutputEvent,
|
|
@@ -147,6 +148,8 @@ class Workflow:
|
|
|
147
148
|
# Stream the response from the Workflow
|
|
148
149
|
stream: Optional[bool] = None
|
|
149
150
|
# Stream the intermediate steps from the Workflow
|
|
151
|
+
stream_events: bool = False
|
|
152
|
+
# [Deprecated] Stream the intermediate steps from the Workflow
|
|
150
153
|
stream_intermediate_steps: bool = False
|
|
151
154
|
# Stream events from executors (agents/teams/functions) within steps
|
|
152
155
|
stream_executor_events: bool = True
|
|
@@ -190,6 +193,7 @@ class Workflow:
|
|
|
190
193
|
user_id: Optional[str] = None,
|
|
191
194
|
debug_mode: Optional[bool] = False,
|
|
192
195
|
stream: Optional[bool] = None,
|
|
196
|
+
stream_events: bool = False,
|
|
193
197
|
stream_intermediate_steps: bool = False,
|
|
194
198
|
stream_executor_events: bool = True,
|
|
195
199
|
store_events: bool = False,
|
|
@@ -214,6 +218,7 @@ class Workflow:
|
|
|
214
218
|
self.store_events = store_events
|
|
215
219
|
self.events_to_skip = events_to_skip or []
|
|
216
220
|
self.stream = stream
|
|
221
|
+
self.stream_events = stream_events
|
|
217
222
|
self.stream_intermediate_steps = stream_intermediate_steps
|
|
218
223
|
self.stream_executor_events = stream_executor_events
|
|
219
224
|
self.store_executor_outputs = store_executor_outputs
|
|
@@ -344,10 +349,8 @@ class Workflow:
|
|
|
344
349
|
self,
|
|
345
350
|
session_id: Optional[str] = None,
|
|
346
351
|
user_id: Optional[str] = None,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
) -> Tuple[str, Optional[str], Dict[str, Any]]:
|
|
350
|
-
"""Initialize the session for the agent."""
|
|
352
|
+
) -> Tuple[str, Optional[str]]:
|
|
353
|
+
"""Initialize the session for the workflow."""
|
|
351
354
|
|
|
352
355
|
if session_id is None:
|
|
353
356
|
if self.session_id:
|
|
@@ -360,27 +363,25 @@ class Workflow:
|
|
|
360
363
|
log_debug(f"Session ID: {session_id}", center=True)
|
|
361
364
|
|
|
362
365
|
# Use the default user_id when necessary
|
|
363
|
-
if user_id is None:
|
|
366
|
+
if user_id is None or user_id == "":
|
|
364
367
|
user_id = self.user_id
|
|
365
368
|
|
|
366
|
-
|
|
367
|
-
if session_state is None:
|
|
368
|
-
session_state = self.session_state or {}
|
|
369
|
-
else:
|
|
370
|
-
# If run session_state is provided, merge agent defaults under it
|
|
371
|
-
# This ensures run state takes precedence over agent defaults
|
|
372
|
-
if self.session_state:
|
|
373
|
-
from agno.utils.merge_dict import merge_dictionaries
|
|
374
|
-
|
|
375
|
-
base_state = self.session_state.copy()
|
|
376
|
-
merge_dictionaries(base_state, session_state)
|
|
377
|
-
session_state.clear()
|
|
378
|
-
session_state.update(base_state)
|
|
369
|
+
return session_id, user_id
|
|
379
370
|
|
|
380
|
-
|
|
371
|
+
def _initialize_session_state(
|
|
372
|
+
self,
|
|
373
|
+
session_state: Dict[str, Any],
|
|
374
|
+
user_id: Optional[str] = None,
|
|
375
|
+
session_id: Optional[str] = None,
|
|
376
|
+
run_id: Optional[str] = None,
|
|
377
|
+
) -> Dict[str, Any]:
|
|
378
|
+
"""Initialize the session state for the workflow."""
|
|
379
|
+
if user_id:
|
|
381
380
|
session_state["current_user_id"] = user_id
|
|
382
381
|
if session_id is not None:
|
|
383
382
|
session_state["current_session_id"] = session_id
|
|
383
|
+
if run_id is not None:
|
|
384
|
+
session_state["current_run_id"] = run_id
|
|
384
385
|
|
|
385
386
|
session_state.update(
|
|
386
387
|
{
|
|
@@ -392,7 +393,7 @@ class Workflow:
|
|
|
392
393
|
if self.name:
|
|
393
394
|
session_state["workflow_name"] = self.name
|
|
394
395
|
|
|
395
|
-
return
|
|
396
|
+
return session_state
|
|
396
397
|
|
|
397
398
|
def _generate_workflow_session_name(self) -> str:
|
|
398
399
|
"""Generate a name for the workflow session"""
|
|
@@ -502,6 +503,62 @@ class Workflow:
|
|
|
502
503
|
raise Exception("Session not found")
|
|
503
504
|
return session.session_data.get("session_state", {}) if session.session_data else {}
|
|
504
505
|
|
|
506
|
+
def update_session_state(
|
|
507
|
+
self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
|
|
508
|
+
) -> Dict[str, Any]:
|
|
509
|
+
"""
|
|
510
|
+
Update the session state for the given session ID.
|
|
511
|
+
Args:
|
|
512
|
+
session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
|
|
513
|
+
session_id: The session ID to update. If not provided, the current cached session ID is used.
|
|
514
|
+
Returns:
|
|
515
|
+
dict: The updated session state.
|
|
516
|
+
"""
|
|
517
|
+
session_id = session_id or self.session_id
|
|
518
|
+
if session_id is None:
|
|
519
|
+
raise Exception("Session ID is not set")
|
|
520
|
+
session = self.get_session(session_id=session_id) # type: ignore
|
|
521
|
+
if session is None:
|
|
522
|
+
raise Exception("Session not found")
|
|
523
|
+
|
|
524
|
+
if session.session_data is not None and "session_state" not in session.session_data:
|
|
525
|
+
session.session_data["session_state"] = {}
|
|
526
|
+
|
|
527
|
+
for key, value in session_state_updates.items():
|
|
528
|
+
session.session_data["session_state"][key] = value # type: ignore
|
|
529
|
+
|
|
530
|
+
self.save_session(session=session)
|
|
531
|
+
|
|
532
|
+
return session.session_data["session_state"] # type: ignore
|
|
533
|
+
|
|
534
|
+
async def aupdate_session_state(
|
|
535
|
+
self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
|
|
536
|
+
) -> Dict[str, Any]:
|
|
537
|
+
"""
|
|
538
|
+
Update the session state for the given session ID (async).
|
|
539
|
+
Args:
|
|
540
|
+
session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
|
|
541
|
+
session_id: The session ID to update. If not provided, the current cached session ID is used.
|
|
542
|
+
Returns:
|
|
543
|
+
dict: The updated session state.
|
|
544
|
+
"""
|
|
545
|
+
session_id = session_id or self.session_id
|
|
546
|
+
if session_id is None:
|
|
547
|
+
raise Exception("Session ID is not set")
|
|
548
|
+
session = await self.aget_session(session_id=session_id) # type: ignore
|
|
549
|
+
if session is None:
|
|
550
|
+
raise Exception("Session not found")
|
|
551
|
+
|
|
552
|
+
if session.session_data is not None and "session_state" not in session.session_data:
|
|
553
|
+
session.session_data["session_state"] = {} # type: ignore
|
|
554
|
+
|
|
555
|
+
for key, value in session_state_updates.items():
|
|
556
|
+
session.session_data["session_state"][key] = value # type: ignore
|
|
557
|
+
|
|
558
|
+
await self.asave_session(session=session)
|
|
559
|
+
|
|
560
|
+
return session.session_data["session_state"] # type: ignore
|
|
561
|
+
|
|
505
562
|
async def adelete_session(self, session_id: str):
|
|
506
563
|
"""Delete the current session and save to storage"""
|
|
507
564
|
if self.db is None:
|
|
@@ -615,12 +672,17 @@ class Workflow:
|
|
|
615
672
|
if workflow_session is None:
|
|
616
673
|
# Creating new session if none found
|
|
617
674
|
log_debug(f"Creating new WorkflowSession: {session_id}")
|
|
675
|
+
session_data = {}
|
|
676
|
+
if self.session_state is not None:
|
|
677
|
+
from copy import deepcopy
|
|
678
|
+
|
|
679
|
+
session_data["session_state"] = deepcopy(self.session_state)
|
|
618
680
|
workflow_session = WorkflowSession(
|
|
619
681
|
session_id=session_id,
|
|
620
682
|
workflow_id=self.id,
|
|
621
683
|
user_id=user_id,
|
|
622
684
|
workflow_data=self._get_workflow_data(),
|
|
623
|
-
session_data=
|
|
685
|
+
session_data=session_data,
|
|
624
686
|
metadata=self.metadata,
|
|
625
687
|
created_at=int(time()),
|
|
626
688
|
)
|
|
@@ -859,7 +921,7 @@ class Workflow:
|
|
|
859
921
|
else:
|
|
860
922
|
step_type = STEP_TYPE_MAPPING[type(step)]
|
|
861
923
|
step_dict = {
|
|
862
|
-
"name": step.name if hasattr(step, "name") else step.__name__,
|
|
924
|
+
"name": step.name if hasattr(step, "name") else step.__name__, # type: ignore
|
|
863
925
|
"description": step.description if hasattr(step, "description") else "User-defined callable step",
|
|
864
926
|
"type": step_type.value,
|
|
865
927
|
}
|
|
@@ -1116,6 +1178,15 @@ class Workflow:
|
|
|
1116
1178
|
logger.error(f"Function signature inspection failed: {e}. Falling back to original calling convention.")
|
|
1117
1179
|
return func(**kwargs)
|
|
1118
1180
|
|
|
1181
|
+
def _accumulate_partial_step_data(
|
|
1182
|
+
self, event: Union[RunContentEvent, TeamRunContentEvent], partial_step_content: str
|
|
1183
|
+
) -> str:
|
|
1184
|
+
"""Accumulate partial step data from streaming events"""
|
|
1185
|
+
if isinstance(event, (RunContentEvent, TeamRunContentEvent)) and event.content:
|
|
1186
|
+
if isinstance(event.content, str):
|
|
1187
|
+
partial_step_content += event.content
|
|
1188
|
+
return partial_step_content
|
|
1189
|
+
|
|
1119
1190
|
def _execute(
|
|
1120
1191
|
self,
|
|
1121
1192
|
session: WorkflowSession,
|
|
@@ -1283,7 +1354,7 @@ class Workflow:
|
|
|
1283
1354
|
execution_input: WorkflowExecutionInput,
|
|
1284
1355
|
workflow_run_response: WorkflowRunOutput,
|
|
1285
1356
|
session_state: Optional[Dict[str, Any]] = None,
|
|
1286
|
-
|
|
1357
|
+
stream_events: bool = False,
|
|
1287
1358
|
**kwargs: Any,
|
|
1288
1359
|
) -> Iterator[WorkflowRunOutputEvent]:
|
|
1289
1360
|
"""Execute a specific pipeline by name with event streaming"""
|
|
@@ -1339,11 +1410,22 @@ class Workflow:
|
|
|
1339
1410
|
|
|
1340
1411
|
early_termination = False
|
|
1341
1412
|
|
|
1413
|
+
# Track partial step data in case of cancellation
|
|
1414
|
+
current_step_name = ""
|
|
1415
|
+
current_step = None
|
|
1416
|
+
partial_step_content = ""
|
|
1417
|
+
|
|
1342
1418
|
for i, step in enumerate(self.steps): # type: ignore[arg-type]
|
|
1343
1419
|
raise_if_cancelled(workflow_run_response.run_id) # type: ignore
|
|
1344
1420
|
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
1345
1421
|
log_debug(f"Streaming step {i + 1}/{self._get_step_count()}: {step_name}")
|
|
1346
1422
|
|
|
1423
|
+
# Track current step for cancellation handler
|
|
1424
|
+
current_step_name = step_name
|
|
1425
|
+
current_step = step
|
|
1426
|
+
# Reset partial data for this step
|
|
1427
|
+
partial_step_content = ""
|
|
1428
|
+
|
|
1347
1429
|
# Create enhanced StepInput
|
|
1348
1430
|
step_input = self._create_step_input(
|
|
1349
1431
|
execution_input=execution_input,
|
|
@@ -1359,7 +1441,7 @@ class Workflow:
|
|
|
1359
1441
|
step_input,
|
|
1360
1442
|
session_id=session.session_id,
|
|
1361
1443
|
user_id=self.user_id,
|
|
1362
|
-
|
|
1444
|
+
stream_events=stream_events,
|
|
1363
1445
|
stream_executor_events=self.stream_executor_events,
|
|
1364
1446
|
workflow_run_response=workflow_run_response,
|
|
1365
1447
|
session_state=session_state,
|
|
@@ -1372,6 +1454,10 @@ class Workflow:
|
|
|
1372
1454
|
num_history_runs=self.num_history_runs,
|
|
1373
1455
|
):
|
|
1374
1456
|
raise_if_cancelled(workflow_run_response.run_id) # type: ignore
|
|
1457
|
+
|
|
1458
|
+
# Accumulate partial data from streaming events
|
|
1459
|
+
partial_step_content = self._accumulate_partial_step_data(event, partial_step_content) # type: ignore
|
|
1460
|
+
|
|
1375
1461
|
# Handle events
|
|
1376
1462
|
if isinstance(event, StepOutput):
|
|
1377
1463
|
step_output = event
|
|
@@ -1486,6 +1572,29 @@ class Workflow:
|
|
|
1486
1572
|
logger.info(f"Workflow run {workflow_run_response.run_id} was cancelled during streaming")
|
|
1487
1573
|
workflow_run_response.status = RunStatus.cancelled
|
|
1488
1574
|
workflow_run_response.content = str(e)
|
|
1575
|
+
|
|
1576
|
+
# Capture partial progress from the step that was cancelled mid-stream
|
|
1577
|
+
if partial_step_content:
|
|
1578
|
+
logger.info(
|
|
1579
|
+
f"Step with name '{current_step_name}' was cancelled. Setting its partial progress as step output."
|
|
1580
|
+
)
|
|
1581
|
+
partial_step_output = StepOutput(
|
|
1582
|
+
step_name=current_step_name,
|
|
1583
|
+
step_id=getattr(current_step, "step_id", None) if current_step else None,
|
|
1584
|
+
step_type=StepType.STEP,
|
|
1585
|
+
executor_type=getattr(current_step, "executor_type", None) if current_step else None,
|
|
1586
|
+
executor_name=getattr(current_step, "executor_name", None) if current_step else None,
|
|
1587
|
+
content=partial_step_content,
|
|
1588
|
+
success=False,
|
|
1589
|
+
error="Cancelled during execution",
|
|
1590
|
+
)
|
|
1591
|
+
collected_step_outputs.append(partial_step_output)
|
|
1592
|
+
|
|
1593
|
+
# Preserve all progress (completed steps + partial step) before cancellation
|
|
1594
|
+
if collected_step_outputs:
|
|
1595
|
+
workflow_run_response.step_results = collected_step_outputs
|
|
1596
|
+
workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
|
|
1597
|
+
|
|
1489
1598
|
cancelled_event = WorkflowCancelledEvent(
|
|
1490
1599
|
run_id=workflow_run_response.run_id or "",
|
|
1491
1600
|
workflow_id=self.id,
|
|
@@ -1588,9 +1697,31 @@ class Workflow:
|
|
|
1588
1697
|
# For regular async functions, use the same signature inspection logic in fallback
|
|
1589
1698
|
return await func(**call_kwargs) # type: ignore
|
|
1590
1699
|
|
|
1700
|
+
async def _aload_or_create_session(
|
|
1701
|
+
self, session_id: str, user_id: Optional[str], session_state: Optional[Dict[str, Any]]
|
|
1702
|
+
) -> Tuple[WorkflowSession, Dict[str, Any]]:
|
|
1703
|
+
"""Load or create session from database, update metadata, and prepare session state.
|
|
1704
|
+
|
|
1705
|
+
Returns:
|
|
1706
|
+
Tuple of (workflow_session, prepared_session_state)
|
|
1707
|
+
"""
|
|
1708
|
+
# Read existing session from database
|
|
1709
|
+
if self._has_async_db():
|
|
1710
|
+
workflow_session = await self.aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
1711
|
+
else:
|
|
1712
|
+
workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
|
|
1713
|
+
self._update_metadata(session=workflow_session)
|
|
1714
|
+
|
|
1715
|
+
# Update session state from DB
|
|
1716
|
+
_session_state = session_state or {}
|
|
1717
|
+
_session_state = self._load_session_state(session=workflow_session, session_state=_session_state)
|
|
1718
|
+
|
|
1719
|
+
return workflow_session, _session_state
|
|
1720
|
+
|
|
1591
1721
|
async def _aexecute(
|
|
1592
1722
|
self,
|
|
1593
|
-
|
|
1723
|
+
session_id: str,
|
|
1724
|
+
user_id: Optional[str],
|
|
1594
1725
|
execution_input: WorkflowExecutionInput,
|
|
1595
1726
|
workflow_run_response: WorkflowRunOutput,
|
|
1596
1727
|
session_state: Optional[Dict[str, Any]] = None,
|
|
@@ -1599,6 +1730,11 @@ class Workflow:
|
|
|
1599
1730
|
"""Execute a specific pipeline by name asynchronously"""
|
|
1600
1731
|
from inspect import isasyncgenfunction, iscoroutinefunction, isgeneratorfunction
|
|
1601
1732
|
|
|
1733
|
+
# Read existing session from database
|
|
1734
|
+
workflow_session, session_state = await self._aload_or_create_session(
|
|
1735
|
+
session_id=session_id, user_id=user_id, session_state=session_state
|
|
1736
|
+
)
|
|
1737
|
+
|
|
1602
1738
|
workflow_run_response.status = RunStatus.running
|
|
1603
1739
|
|
|
1604
1740
|
# Register run for cancellation tracking
|
|
@@ -1667,12 +1803,12 @@ class Workflow:
|
|
|
1667
1803
|
|
|
1668
1804
|
step_output = await step.aexecute( # type: ignore[union-attr]
|
|
1669
1805
|
step_input,
|
|
1670
|
-
session_id=
|
|
1806
|
+
session_id=session_id,
|
|
1671
1807
|
user_id=self.user_id,
|
|
1672
1808
|
workflow_run_response=workflow_run_response,
|
|
1673
1809
|
session_state=session_state,
|
|
1674
1810
|
store_executor_outputs=self.store_executor_outputs,
|
|
1675
|
-
workflow_session=
|
|
1811
|
+
workflow_session=workflow_session,
|
|
1676
1812
|
add_workflow_history_to_steps=self.add_workflow_history_to_steps
|
|
1677
1813
|
if self.add_workflow_history_to_steps
|
|
1678
1814
|
else None,
|
|
@@ -1742,34 +1878,40 @@ class Workflow:
|
|
|
1742
1878
|
workflow_run_response.content = f"Workflow execution failed: {e}"
|
|
1743
1879
|
raise e
|
|
1744
1880
|
|
|
1745
|
-
self._update_session_metrics(session=
|
|
1746
|
-
|
|
1881
|
+
self._update_session_metrics(session=workflow_session, workflow_run_response=workflow_run_response)
|
|
1882
|
+
workflow_session.upsert_run(run=workflow_run_response)
|
|
1747
1883
|
if self._has_async_db():
|
|
1748
|
-
await self.asave_session(session=
|
|
1884
|
+
await self.asave_session(session=workflow_session)
|
|
1749
1885
|
else:
|
|
1750
|
-
self.save_session(session=
|
|
1886
|
+
self.save_session(session=workflow_session)
|
|
1751
1887
|
# Always clean up the run tracking
|
|
1752
1888
|
cleanup_run(workflow_run_response.run_id) # type: ignore
|
|
1753
1889
|
|
|
1754
1890
|
# Log Workflow Telemetry
|
|
1755
1891
|
if self.telemetry:
|
|
1756
|
-
await self._alog_workflow_telemetry(session_id=
|
|
1892
|
+
await self._alog_workflow_telemetry(session_id=session_id, run_id=workflow_run_response.run_id)
|
|
1757
1893
|
|
|
1758
1894
|
return workflow_run_response
|
|
1759
1895
|
|
|
1760
1896
|
async def _aexecute_stream(
|
|
1761
1897
|
self,
|
|
1762
|
-
|
|
1898
|
+
session_id: str,
|
|
1899
|
+
user_id: Optional[str],
|
|
1763
1900
|
execution_input: WorkflowExecutionInput,
|
|
1764
1901
|
workflow_run_response: WorkflowRunOutput,
|
|
1765
1902
|
session_state: Optional[Dict[str, Any]] = None,
|
|
1766
|
-
|
|
1903
|
+
stream_events: bool = False,
|
|
1767
1904
|
websocket_handler: Optional[WebSocketHandler] = None,
|
|
1768
1905
|
**kwargs: Any,
|
|
1769
1906
|
) -> AsyncIterator[WorkflowRunOutputEvent]:
|
|
1770
1907
|
"""Execute a specific pipeline by name with event streaming"""
|
|
1771
1908
|
from inspect import isasyncgenfunction, iscoroutinefunction, isgeneratorfunction
|
|
1772
1909
|
|
|
1910
|
+
# Read existing session from database
|
|
1911
|
+
workflow_session, session_state = await self._aload_or_create_session(
|
|
1912
|
+
session_id=session_id, user_id=user_id, session_state=session_state
|
|
1913
|
+
)
|
|
1914
|
+
|
|
1773
1915
|
workflow_run_response.status = RunStatus.running
|
|
1774
1916
|
|
|
1775
1917
|
# Register run for cancellation tracking
|
|
@@ -1828,12 +1970,22 @@ class Workflow:
|
|
|
1828
1970
|
|
|
1829
1971
|
early_termination = False
|
|
1830
1972
|
|
|
1973
|
+
# Track partial step data in case of cancellation
|
|
1974
|
+
current_step_name = ""
|
|
1975
|
+
current_step = None
|
|
1976
|
+
partial_step_content = ""
|
|
1977
|
+
|
|
1831
1978
|
for i, step in enumerate(self.steps): # type: ignore[arg-type]
|
|
1832
1979
|
if workflow_run_response.run_id:
|
|
1833
1980
|
raise_if_cancelled(workflow_run_response.run_id)
|
|
1834
1981
|
step_name = getattr(step, "name", f"step_{i + 1}")
|
|
1835
1982
|
log_debug(f"Async streaming step {i + 1}/{self._get_step_count()}: {step_name}")
|
|
1836
1983
|
|
|
1984
|
+
current_step_name = step_name
|
|
1985
|
+
current_step = step
|
|
1986
|
+
# Reset partial data for this step
|
|
1987
|
+
partial_step_content = ""
|
|
1988
|
+
|
|
1837
1989
|
# Create enhanced StepInput
|
|
1838
1990
|
step_input = self._create_step_input(
|
|
1839
1991
|
execution_input=execution_input,
|
|
@@ -1847,15 +1999,15 @@ class Workflow:
|
|
|
1847
1999
|
# Execute step with streaming and yield all events
|
|
1848
2000
|
async for event in step.aexecute_stream( # type: ignore[union-attr]
|
|
1849
2001
|
step_input,
|
|
1850
|
-
session_id=
|
|
2002
|
+
session_id=session_id,
|
|
1851
2003
|
user_id=self.user_id,
|
|
1852
|
-
|
|
2004
|
+
stream_events=stream_events,
|
|
1853
2005
|
stream_executor_events=self.stream_executor_events,
|
|
1854
2006
|
workflow_run_response=workflow_run_response,
|
|
1855
2007
|
session_state=session_state,
|
|
1856
2008
|
step_index=i,
|
|
1857
2009
|
store_executor_outputs=self.store_executor_outputs,
|
|
1858
|
-
workflow_session=
|
|
2010
|
+
workflow_session=workflow_session,
|
|
1859
2011
|
add_workflow_history_to_steps=self.add_workflow_history_to_steps
|
|
1860
2012
|
if self.add_workflow_history_to_steps
|
|
1861
2013
|
else None,
|
|
@@ -1863,6 +2015,10 @@ class Workflow:
|
|
|
1863
2015
|
):
|
|
1864
2016
|
if workflow_run_response.run_id:
|
|
1865
2017
|
raise_if_cancelled(workflow_run_response.run_id)
|
|
2018
|
+
|
|
2019
|
+
# Accumulate partial data from streaming events
|
|
2020
|
+
partial_step_content = self._accumulate_partial_step_data(event, partial_step_content) # type: ignore
|
|
2021
|
+
|
|
1866
2022
|
if isinstance(event, StepOutput):
|
|
1867
2023
|
step_output = event
|
|
1868
2024
|
collected_step_outputs.append(step_output)
|
|
@@ -1965,7 +2121,7 @@ class Workflow:
|
|
|
1965
2121
|
run_id=workflow_run_response.run_id or "",
|
|
1966
2122
|
workflow_id=self.id,
|
|
1967
2123
|
workflow_name=self.name,
|
|
1968
|
-
session_id=
|
|
2124
|
+
session_id=session_id,
|
|
1969
2125
|
error=str(e),
|
|
1970
2126
|
)
|
|
1971
2127
|
|
|
@@ -1979,11 +2135,34 @@ class Workflow:
|
|
|
1979
2135
|
logger.info(f"Workflow run {workflow_run_response.run_id} was cancelled during streaming")
|
|
1980
2136
|
workflow_run_response.status = RunStatus.cancelled
|
|
1981
2137
|
workflow_run_response.content = str(e)
|
|
2138
|
+
|
|
2139
|
+
# Capture partial progress from the step that was cancelled mid-stream
|
|
2140
|
+
if partial_step_content:
|
|
2141
|
+
logger.info(
|
|
2142
|
+
f"Step with name '{current_step_name}' was cancelled. Setting its partial progress as step output."
|
|
2143
|
+
)
|
|
2144
|
+
partial_step_output = StepOutput(
|
|
2145
|
+
step_name=current_step_name,
|
|
2146
|
+
step_id=getattr(current_step, "step_id", None) if current_step else None,
|
|
2147
|
+
step_type=StepType.STEP,
|
|
2148
|
+
executor_type=getattr(current_step, "executor_type", None) if current_step else None,
|
|
2149
|
+
executor_name=getattr(current_step, "executor_name", None) if current_step else None,
|
|
2150
|
+
content=partial_step_content,
|
|
2151
|
+
success=False,
|
|
2152
|
+
error="Cancelled during execution",
|
|
2153
|
+
)
|
|
2154
|
+
collected_step_outputs.append(partial_step_output)
|
|
2155
|
+
|
|
2156
|
+
# Preserve all progress (completed steps + partial step) before cancellation
|
|
2157
|
+
if collected_step_outputs:
|
|
2158
|
+
workflow_run_response.step_results = collected_step_outputs
|
|
2159
|
+
workflow_run_response.metrics = self._aggregate_workflow_metrics(collected_step_outputs)
|
|
2160
|
+
|
|
1982
2161
|
cancelled_event = WorkflowCancelledEvent(
|
|
1983
2162
|
run_id=workflow_run_response.run_id or "",
|
|
1984
2163
|
workflow_id=self.id,
|
|
1985
2164
|
workflow_name=self.name,
|
|
1986
|
-
session_id=
|
|
2165
|
+
session_id=session_id,
|
|
1987
2166
|
reason=str(e),
|
|
1988
2167
|
)
|
|
1989
2168
|
yield self._handle_event(
|
|
@@ -2000,7 +2179,7 @@ class Workflow:
|
|
|
2000
2179
|
run_id=workflow_run_response.run_id or "",
|
|
2001
2180
|
workflow_id=self.id,
|
|
2002
2181
|
workflow_name=self.name,
|
|
2003
|
-
session_id=
|
|
2182
|
+
session_id=session_id,
|
|
2004
2183
|
error=str(e),
|
|
2005
2184
|
)
|
|
2006
2185
|
|
|
@@ -2024,16 +2203,16 @@ class Workflow:
|
|
|
2024
2203
|
yield self._handle_event(workflow_completed_event, workflow_run_response, websocket_handler=websocket_handler)
|
|
2025
2204
|
|
|
2026
2205
|
# Store the completed workflow response
|
|
2027
|
-
self._update_session_metrics(session=
|
|
2028
|
-
|
|
2206
|
+
self._update_session_metrics(session=workflow_session, workflow_run_response=workflow_run_response)
|
|
2207
|
+
workflow_session.upsert_run(run=workflow_run_response)
|
|
2029
2208
|
if self._has_async_db():
|
|
2030
|
-
await self.asave_session(session=
|
|
2209
|
+
await self.asave_session(session=workflow_session)
|
|
2031
2210
|
else:
|
|
2032
|
-
self.save_session(session=
|
|
2211
|
+
self.save_session(session=workflow_session)
|
|
2033
2212
|
|
|
2034
2213
|
# Log Workflow Telemetry
|
|
2035
2214
|
if self.telemetry:
|
|
2036
|
-
await self._alog_workflow_telemetry(session_id=
|
|
2215
|
+
await self._alog_workflow_telemetry(session_id=session_id, run_id=workflow_run_response.run_id)
|
|
2037
2216
|
|
|
2038
2217
|
# Always clean up the run tracking
|
|
2039
2218
|
cleanup_run(workflow_run_response.run_id) # type: ignore
|
|
@@ -2057,19 +2236,12 @@ class Workflow:
|
|
|
2057
2236
|
|
|
2058
2237
|
self.initialize_workflow()
|
|
2059
2238
|
|
|
2060
|
-
session_id, user_id
|
|
2061
|
-
session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
|
|
2062
|
-
)
|
|
2239
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
2063
2240
|
|
|
2064
2241
|
# Read existing session from database
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2069
|
-
self._update_metadata(session=workflow_session)
|
|
2070
|
-
|
|
2071
|
-
# Update session state from DB
|
|
2072
|
-
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
2242
|
+
workflow_session, session_state = await self._aload_or_create_session(
|
|
2243
|
+
session_id=session_id, user_id=user_id, session_state=session_state
|
|
2244
|
+
)
|
|
2073
2245
|
|
|
2074
2246
|
self._prepare_steps()
|
|
2075
2247
|
|
|
@@ -2114,7 +2286,8 @@ class Workflow:
|
|
|
2114
2286
|
self.save_session(session=workflow_session)
|
|
2115
2287
|
|
|
2116
2288
|
await self._aexecute(
|
|
2117
|
-
|
|
2289
|
+
session_id=session_id,
|
|
2290
|
+
user_id=user_id,
|
|
2118
2291
|
execution_input=inputs,
|
|
2119
2292
|
workflow_run_response=workflow_run_response,
|
|
2120
2293
|
session_state=session_state,
|
|
@@ -2150,7 +2323,7 @@ class Workflow:
|
|
|
2150
2323
|
images: Optional[List[Image]] = None,
|
|
2151
2324
|
videos: Optional[List[Video]] = None,
|
|
2152
2325
|
files: Optional[List[File]] = None,
|
|
2153
|
-
|
|
2326
|
+
stream_events: bool = False,
|
|
2154
2327
|
websocket_handler: Optional[WebSocketHandler] = None,
|
|
2155
2328
|
**kwargs: Any,
|
|
2156
2329
|
) -> WorkflowRunOutput:
|
|
@@ -2160,19 +2333,12 @@ class Workflow:
|
|
|
2160
2333
|
|
|
2161
2334
|
self.initialize_workflow()
|
|
2162
2335
|
|
|
2163
|
-
session_id, user_id
|
|
2164
|
-
session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
|
|
2165
|
-
)
|
|
2336
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
2166
2337
|
|
|
2167
2338
|
# Read existing session from database
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2172
|
-
self._update_metadata(session=workflow_session)
|
|
2173
|
-
|
|
2174
|
-
# Update session state from DB
|
|
2175
|
-
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
2339
|
+
workflow_session, session_state = await self._aload_or_create_session(
|
|
2340
|
+
session_id=session_id, user_id=user_id, session_state=session_state
|
|
2341
|
+
)
|
|
2176
2342
|
|
|
2177
2343
|
self._prepare_steps()
|
|
2178
2344
|
|
|
@@ -2218,10 +2384,11 @@ class Workflow:
|
|
|
2218
2384
|
|
|
2219
2385
|
# Execute with streaming - consume all events (they're auto-broadcast via _handle_event)
|
|
2220
2386
|
async for event in self._aexecute_stream(
|
|
2387
|
+
session_id=session_id,
|
|
2388
|
+
user_id=user_id,
|
|
2221
2389
|
execution_input=inputs,
|
|
2222
|
-
session=workflow_session,
|
|
2223
2390
|
workflow_run_response=workflow_run_response,
|
|
2224
|
-
|
|
2391
|
+
stream_events=stream_events,
|
|
2225
2392
|
session_state=session_state,
|
|
2226
2393
|
websocket_handler=websocket_handler,
|
|
2227
2394
|
**kwargs,
|
|
@@ -2296,6 +2463,7 @@ class Workflow:
|
|
|
2296
2463
|
videos: Optional[List[Video]] = None,
|
|
2297
2464
|
files: Optional[List[File]] = None,
|
|
2298
2465
|
stream: Literal[False] = False,
|
|
2466
|
+
stream_events: Optional[bool] = None,
|
|
2299
2467
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2300
2468
|
background: Optional[bool] = False,
|
|
2301
2469
|
) -> WorkflowRunOutput: ...
|
|
@@ -2313,6 +2481,7 @@ class Workflow:
|
|
|
2313
2481
|
videos: Optional[List[Video]] = None,
|
|
2314
2482
|
files: Optional[List[File]] = None,
|
|
2315
2483
|
stream: Literal[True] = True,
|
|
2484
|
+
stream_events: Optional[bool] = None,
|
|
2316
2485
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2317
2486
|
background: Optional[bool] = False,
|
|
2318
2487
|
) -> Iterator[WorkflowRunOutputEvent]: ...
|
|
@@ -2329,6 +2498,7 @@ class Workflow:
|
|
|
2329
2498
|
videos: Optional[List[Video]] = None,
|
|
2330
2499
|
files: Optional[List[File]] = None,
|
|
2331
2500
|
stream: bool = False,
|
|
2501
|
+
stream_events: Optional[bool] = None,
|
|
2332
2502
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2333
2503
|
background: Optional[bool] = False,
|
|
2334
2504
|
**kwargs: Any,
|
|
@@ -2346,14 +2516,16 @@ class Workflow:
|
|
|
2346
2516
|
run_id = str(uuid4())
|
|
2347
2517
|
|
|
2348
2518
|
self.initialize_workflow()
|
|
2349
|
-
session_id, user_id
|
|
2350
|
-
session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
|
|
2351
|
-
)
|
|
2519
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
2352
2520
|
|
|
2353
2521
|
# Read existing session from database
|
|
2354
2522
|
workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2355
2523
|
self._update_metadata(session=workflow_session)
|
|
2356
2524
|
|
|
2525
|
+
# Initialize session state
|
|
2526
|
+
session_state = self._initialize_session_state(
|
|
2527
|
+
session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_id
|
|
2528
|
+
)
|
|
2357
2529
|
# Update session state from DB
|
|
2358
2530
|
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
2359
2531
|
|
|
@@ -2361,11 +2533,13 @@ class Workflow:
|
|
|
2361
2533
|
|
|
2362
2534
|
# Use simple defaults
|
|
2363
2535
|
stream = stream or self.stream or False
|
|
2364
|
-
|
|
2536
|
+
stream_events = (stream_events or stream_intermediate_steps) or (
|
|
2537
|
+
self.stream_events or self.stream_intermediate_steps
|
|
2538
|
+
)
|
|
2365
2539
|
|
|
2366
|
-
# Can't
|
|
2367
|
-
if
|
|
2368
|
-
|
|
2540
|
+
# Can't stream events if streaming is disabled
|
|
2541
|
+
if stream is False:
|
|
2542
|
+
stream_events = False
|
|
2369
2543
|
|
|
2370
2544
|
log_debug(f"Stream: {stream}")
|
|
2371
2545
|
log_debug(f"Total steps: {self._get_step_count()}")
|
|
@@ -2402,7 +2576,7 @@ class Workflow:
|
|
|
2402
2576
|
session=workflow_session,
|
|
2403
2577
|
execution_input=inputs, # type: ignore[arg-type]
|
|
2404
2578
|
workflow_run_response=workflow_run_response,
|
|
2405
|
-
|
|
2579
|
+
stream_events=stream_events,
|
|
2406
2580
|
session_state=session_state,
|
|
2407
2581
|
**kwargs,
|
|
2408
2582
|
)
|
|
@@ -2428,13 +2602,14 @@ class Workflow:
|
|
|
2428
2602
|
videos: Optional[List[Video]] = None,
|
|
2429
2603
|
files: Optional[List[File]] = None,
|
|
2430
2604
|
stream: Literal[False] = False,
|
|
2605
|
+
stream_events: Optional[bool] = None,
|
|
2431
2606
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2432
2607
|
background: Optional[bool] = False,
|
|
2433
2608
|
websocket: Optional[WebSocket] = None,
|
|
2434
2609
|
) -> WorkflowRunOutput: ...
|
|
2435
2610
|
|
|
2436
2611
|
@overload
|
|
2437
|
-
|
|
2612
|
+
def arun(
|
|
2438
2613
|
self,
|
|
2439
2614
|
input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
|
|
2440
2615
|
additional_data: Optional[Dict[str, Any]] = None,
|
|
@@ -2446,12 +2621,13 @@ class Workflow:
|
|
|
2446
2621
|
videos: Optional[List[Video]] = None,
|
|
2447
2622
|
files: Optional[List[File]] = None,
|
|
2448
2623
|
stream: Literal[True] = True,
|
|
2624
|
+
stream_events: Optional[bool] = None,
|
|
2449
2625
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2450
2626
|
background: Optional[bool] = False,
|
|
2451
2627
|
websocket: Optional[WebSocket] = None,
|
|
2452
2628
|
) -> AsyncIterator[WorkflowRunOutputEvent]: ...
|
|
2453
2629
|
|
|
2454
|
-
|
|
2630
|
+
def arun( # type: ignore
|
|
2455
2631
|
self,
|
|
2456
2632
|
input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
|
|
2457
2633
|
additional_data: Optional[Dict[str, Any]] = None,
|
|
@@ -2463,6 +2639,7 @@ class Workflow:
|
|
|
2463
2639
|
videos: Optional[List[Video]] = None,
|
|
2464
2640
|
files: Optional[List[File]] = None,
|
|
2465
2641
|
stream: bool = False,
|
|
2642
|
+
stream_events: Optional[bool] = None,
|
|
2466
2643
|
stream_intermediate_steps: Optional[bool] = False,
|
|
2467
2644
|
background: Optional[bool] = False,
|
|
2468
2645
|
websocket: Optional[WebSocket] = None,
|
|
@@ -2480,8 +2657,11 @@ class Workflow:
|
|
|
2480
2657
|
|
|
2481
2658
|
if background:
|
|
2482
2659
|
if stream and websocket:
|
|
2660
|
+
# Consider both stream_events and stream_intermediate_steps (deprecated)
|
|
2661
|
+
stream_events = stream_events or stream_intermediate_steps or False
|
|
2662
|
+
|
|
2483
2663
|
# Background + Streaming + WebSocket = Real-time events
|
|
2484
|
-
return
|
|
2664
|
+
return self._arun_background_stream( # type: ignore
|
|
2485
2665
|
input=input,
|
|
2486
2666
|
additional_data=additional_data,
|
|
2487
2667
|
user_id=user_id,
|
|
@@ -2491,7 +2671,7 @@ class Workflow:
|
|
|
2491
2671
|
images=images,
|
|
2492
2672
|
videos=videos,
|
|
2493
2673
|
files=files,
|
|
2494
|
-
|
|
2674
|
+
stream_events=stream_events,
|
|
2495
2675
|
websocket_handler=websocket_handler,
|
|
2496
2676
|
**kwargs,
|
|
2497
2677
|
)
|
|
@@ -2500,7 +2680,7 @@ class Workflow:
|
|
|
2500
2680
|
raise ValueError("Background streaming execution requires a WebSocket for real-time events")
|
|
2501
2681
|
else:
|
|
2502
2682
|
# Background + Non-streaming = Polling (existing)
|
|
2503
|
-
return
|
|
2683
|
+
return self._arun_background( # type: ignore
|
|
2504
2684
|
input=input,
|
|
2505
2685
|
additional_data=additional_data,
|
|
2506
2686
|
user_id=user_id,
|
|
@@ -2518,29 +2698,19 @@ class Workflow:
|
|
|
2518
2698
|
run_id = str(uuid4())
|
|
2519
2699
|
|
|
2520
2700
|
self.initialize_workflow()
|
|
2521
|
-
session_id, user_id
|
|
2522
|
-
session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
|
|
2523
|
-
)
|
|
2524
|
-
|
|
2525
|
-
# Read existing session from database
|
|
2526
|
-
if self._has_async_db():
|
|
2527
|
-
workflow_session = await self.aread_or_create_session(session_id=session_id, user_id=user_id)
|
|
2528
|
-
else:
|
|
2529
|
-
workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
|
|
2530
|
-
self._update_metadata(session=workflow_session)
|
|
2531
|
-
|
|
2532
|
-
# Update session state from DB
|
|
2533
|
-
session_state = self._load_session_state(session=workflow_session, session_state=session_state)
|
|
2701
|
+
session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
|
|
2534
2702
|
|
|
2535
2703
|
log_debug(f"Async Workflow Run Start: {self.name}", center=True)
|
|
2536
2704
|
|
|
2537
2705
|
# Use simple defaults
|
|
2538
2706
|
stream = stream or self.stream or False
|
|
2539
|
-
|
|
2707
|
+
stream_events = (stream_events or stream_intermediate_steps) or (
|
|
2708
|
+
self.stream_events or self.stream_intermediate_steps
|
|
2709
|
+
)
|
|
2540
2710
|
|
|
2541
|
-
# Can't
|
|
2542
|
-
if
|
|
2543
|
-
|
|
2711
|
+
# Can't stream events if streaming is disabled
|
|
2712
|
+
if stream is False:
|
|
2713
|
+
stream_events = False
|
|
2544
2714
|
|
|
2545
2715
|
log_debug(f"Stream: {stream}")
|
|
2546
2716
|
|
|
@@ -2572,21 +2742,23 @@ class Workflow:
|
|
|
2572
2742
|
self.update_agents_and_teams_session_info()
|
|
2573
2743
|
|
|
2574
2744
|
if stream:
|
|
2575
|
-
return self._aexecute_stream(
|
|
2745
|
+
return self._aexecute_stream( # type: ignore
|
|
2576
2746
|
execution_input=inputs,
|
|
2577
2747
|
workflow_run_response=workflow_run_response,
|
|
2578
|
-
|
|
2579
|
-
|
|
2748
|
+
session_id=session_id,
|
|
2749
|
+
user_id=user_id,
|
|
2750
|
+
stream_events=stream_events,
|
|
2580
2751
|
websocket=websocket,
|
|
2581
2752
|
files=files,
|
|
2582
2753
|
session_state=session_state,
|
|
2583
2754
|
**kwargs,
|
|
2584
2755
|
)
|
|
2585
2756
|
else:
|
|
2586
|
-
return
|
|
2757
|
+
return self._aexecute( # type: ignore
|
|
2587
2758
|
execution_input=inputs,
|
|
2588
2759
|
workflow_run_response=workflow_run_response,
|
|
2589
|
-
|
|
2760
|
+
session_id=session_id,
|
|
2761
|
+
user_id=user_id,
|
|
2590
2762
|
websocket=websocket,
|
|
2591
2763
|
files=files,
|
|
2592
2764
|
session_state=session_state,
|
|
@@ -2601,7 +2773,7 @@ class Workflow:
|
|
|
2601
2773
|
if callable(step) and hasattr(step, "__name__"):
|
|
2602
2774
|
step_name = step.__name__
|
|
2603
2775
|
log_debug(f"Step {i + 1}: Wrapping callable function '{step_name}'")
|
|
2604
|
-
prepared_steps.append(Step(name=step_name, description="User-defined callable step", executor=step))
|
|
2776
|
+
prepared_steps.append(Step(name=step_name, description="User-defined callable step", executor=step)) # type: ignore
|
|
2605
2777
|
elif isinstance(step, Agent):
|
|
2606
2778
|
step_name = step.name or f"step_{i + 1}"
|
|
2607
2779
|
log_debug(f"Step {i + 1}: Agent '{step_name}'")
|
|
@@ -2632,6 +2804,7 @@ class Workflow:
|
|
|
2632
2804
|
videos: Optional[List[Video]] = None,
|
|
2633
2805
|
files: Optional[List[File]] = None,
|
|
2634
2806
|
stream: Optional[bool] = None,
|
|
2807
|
+
stream_events: Optional[bool] = None,
|
|
2635
2808
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2636
2809
|
markdown: bool = True,
|
|
2637
2810
|
show_time: bool = True,
|
|
@@ -2650,11 +2823,12 @@ class Workflow:
|
|
|
2650
2823
|
images: Image input
|
|
2651
2824
|
videos: Video input
|
|
2652
2825
|
stream: Whether to stream the response content
|
|
2653
|
-
|
|
2826
|
+
stream_events: Whether to stream intermediate steps
|
|
2654
2827
|
markdown: Whether to render content as markdown
|
|
2655
2828
|
show_time: Whether to show execution time
|
|
2656
2829
|
show_step_details: Whether to show individual step outputs
|
|
2657
2830
|
console: Rich console instance (optional)
|
|
2831
|
+
(deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
|
|
2658
2832
|
"""
|
|
2659
2833
|
if self._has_async_db():
|
|
2660
2834
|
raise Exception("`print_response()` is not supported with an async DB. Please use `aprint_response()`.")
|
|
@@ -2662,8 +2836,19 @@ class Workflow:
|
|
|
2662
2836
|
if stream is None:
|
|
2663
2837
|
stream = self.stream or False
|
|
2664
2838
|
|
|
2665
|
-
|
|
2666
|
-
|
|
2839
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
2840
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
2841
|
+
|
|
2842
|
+
# Can't stream events if streaming is disabled
|
|
2843
|
+
if stream is False:
|
|
2844
|
+
stream_events = False
|
|
2845
|
+
|
|
2846
|
+
if stream_events is None:
|
|
2847
|
+
stream_events = (
|
|
2848
|
+
False
|
|
2849
|
+
if (self.stream_events is None and self.stream_intermediate_steps is None)
|
|
2850
|
+
else (self.stream_intermediate_steps or self.stream_events)
|
|
2851
|
+
)
|
|
2667
2852
|
|
|
2668
2853
|
if stream:
|
|
2669
2854
|
print_response_stream(
|
|
@@ -2676,7 +2861,7 @@ class Workflow:
|
|
|
2676
2861
|
images=images,
|
|
2677
2862
|
videos=videos,
|
|
2678
2863
|
files=files,
|
|
2679
|
-
|
|
2864
|
+
stream_events=stream_events,
|
|
2680
2865
|
markdown=markdown,
|
|
2681
2866
|
show_time=show_time,
|
|
2682
2867
|
show_step_details=show_step_details,
|
|
@@ -2712,6 +2897,7 @@ class Workflow:
|
|
|
2712
2897
|
videos: Optional[List[Video]] = None,
|
|
2713
2898
|
files: Optional[List[File]] = None,
|
|
2714
2899
|
stream: Optional[bool] = None,
|
|
2900
|
+
stream_events: Optional[bool] = None,
|
|
2715
2901
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2716
2902
|
markdown: bool = True,
|
|
2717
2903
|
show_time: bool = True,
|
|
@@ -2729,18 +2915,30 @@ class Workflow:
|
|
|
2729
2915
|
audio: Audio input
|
|
2730
2916
|
images: Image input
|
|
2731
2917
|
videos: Video input
|
|
2732
|
-
stream_intermediate_steps: Whether to stream intermediate steps
|
|
2733
2918
|
stream: Whether to stream the response content
|
|
2919
|
+
stream_events: Whether to stream intermediate steps
|
|
2734
2920
|
markdown: Whether to render content as markdown
|
|
2735
2921
|
show_time: Whether to show execution time
|
|
2736
2922
|
show_step_details: Whether to show individual step outputs
|
|
2737
2923
|
console: Rich console instance (optional)
|
|
2924
|
+
(deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
|
|
2738
2925
|
"""
|
|
2739
2926
|
if stream is None:
|
|
2740
2927
|
stream = self.stream or False
|
|
2741
2928
|
|
|
2742
|
-
|
|
2743
|
-
|
|
2929
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
2930
|
+
stream_events = stream_events or stream_intermediate_steps
|
|
2931
|
+
|
|
2932
|
+
# Can't stream events if streaming is disabled
|
|
2933
|
+
if stream is False:
|
|
2934
|
+
stream_events = False
|
|
2935
|
+
|
|
2936
|
+
if stream_events is None:
|
|
2937
|
+
stream_events = (
|
|
2938
|
+
False
|
|
2939
|
+
if (self.stream_events is None and self.stream_intermediate_steps is None)
|
|
2940
|
+
else (self.stream_intermediate_steps or self.stream_events)
|
|
2941
|
+
)
|
|
2744
2942
|
|
|
2745
2943
|
if stream:
|
|
2746
2944
|
await aprint_response_stream(
|
|
@@ -2753,7 +2951,7 @@ class Workflow:
|
|
|
2753
2951
|
images=images,
|
|
2754
2952
|
videos=videos,
|
|
2755
2953
|
files=files,
|
|
2756
|
-
|
|
2954
|
+
stream_events=stream_events,
|
|
2757
2955
|
markdown=markdown,
|
|
2758
2956
|
show_time=show_time,
|
|
2759
2957
|
show_step_details=show_step_details,
|
|
@@ -2930,7 +3128,7 @@ class Workflow:
|
|
|
2930
3128
|
|
|
2931
3129
|
# If it's a team, update all members
|
|
2932
3130
|
if hasattr(active_executor, "members"):
|
|
2933
|
-
for member in active_executor.members:
|
|
3131
|
+
for member in active_executor.members: # type: ignore
|
|
2934
3132
|
if hasattr(member, "workflow_id"):
|
|
2935
3133
|
member.workflow_id = self.id
|
|
2936
3134
|
|
|
@@ -2986,6 +3184,7 @@ class Workflow:
|
|
|
2986
3184
|
user: str = "User",
|
|
2987
3185
|
emoji: str = ":technologist:",
|
|
2988
3186
|
stream: Optional[bool] = None,
|
|
3187
|
+
stream_events: Optional[bool] = None,
|
|
2989
3188
|
stream_intermediate_steps: Optional[bool] = None,
|
|
2990
3189
|
markdown: bool = True,
|
|
2991
3190
|
show_time: bool = True,
|
|
@@ -3006,11 +3205,12 @@ class Workflow:
|
|
|
3006
3205
|
user: Display name for the user in the CLI prompt. Defaults to "User".
|
|
3007
3206
|
emoji: Emoji to display next to the user name in prompts. Defaults to ":technologist:".
|
|
3008
3207
|
stream: Whether to stream the workflow response. If None, uses workflow default.
|
|
3009
|
-
|
|
3208
|
+
stream_events: Whether to stream intermediate step outputs. If None, uses workflow default.
|
|
3010
3209
|
markdown: Whether to render output as markdown. Defaults to True.
|
|
3011
3210
|
show_time: Whether to display timestamps in the output. Defaults to True.
|
|
3012
3211
|
show_step_details: Whether to show detailed step information. Defaults to True.
|
|
3013
3212
|
exit_on: List of commands that will exit the CLI. Defaults to ["exit", "quit", "bye", "stop"].
|
|
3213
|
+
(deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
|
|
3014
3214
|
**kwargs: Additional keyword arguments passed to the workflow's print_response method.
|
|
3015
3215
|
|
|
3016
3216
|
Returns:
|
|
@@ -3019,11 +3219,14 @@ class Workflow:
|
|
|
3019
3219
|
|
|
3020
3220
|
from rich.prompt import Prompt
|
|
3021
3221
|
|
|
3222
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
3223
|
+
stream_events = stream_events or stream_intermediate_steps or False
|
|
3224
|
+
|
|
3022
3225
|
if input:
|
|
3023
3226
|
self.print_response(
|
|
3024
3227
|
input=input,
|
|
3025
3228
|
stream=stream,
|
|
3026
|
-
|
|
3229
|
+
stream_events=stream_events,
|
|
3027
3230
|
markdown=markdown,
|
|
3028
3231
|
show_time=show_time,
|
|
3029
3232
|
show_step_details=show_step_details,
|
|
@@ -3041,7 +3244,7 @@ class Workflow:
|
|
|
3041
3244
|
self.print_response(
|
|
3042
3245
|
input=message,
|
|
3043
3246
|
stream=stream,
|
|
3044
|
-
|
|
3247
|
+
stream_events=stream_events,
|
|
3045
3248
|
markdown=markdown,
|
|
3046
3249
|
show_time=show_time,
|
|
3047
3250
|
show_step_details=show_step_details,
|
|
@@ -3058,6 +3261,7 @@ class Workflow:
|
|
|
3058
3261
|
user: str = "User",
|
|
3059
3262
|
emoji: str = ":technologist:",
|
|
3060
3263
|
stream: Optional[bool] = None,
|
|
3264
|
+
stream_events: Optional[bool] = None,
|
|
3061
3265
|
stream_intermediate_steps: Optional[bool] = None,
|
|
3062
3266
|
markdown: bool = True,
|
|
3063
3267
|
show_time: bool = True,
|
|
@@ -3078,11 +3282,12 @@ class Workflow:
|
|
|
3078
3282
|
user: Display name for the user in the CLI prompt. Defaults to "User".
|
|
3079
3283
|
emoji: Emoji to display next to the user name in prompts. Defaults to ":technologist:".
|
|
3080
3284
|
stream: Whether to stream the workflow response. If None, uses workflow default.
|
|
3081
|
-
|
|
3285
|
+
stream_events: Whether to stream events from the workflow. If None, uses workflow default.
|
|
3082
3286
|
markdown: Whether to render output as markdown. Defaults to True.
|
|
3083
3287
|
show_time: Whether to display timestamps in the output. Defaults to True.
|
|
3084
3288
|
show_step_details: Whether to show detailed step information. Defaults to True.
|
|
3085
3289
|
exit_on: List of commands that will exit the CLI. Defaults to ["exit", "quit", "bye", "stop"].
|
|
3290
|
+
(deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
|
|
3086
3291
|
**kwargs: Additional keyword arguments passed to the workflow's print_response method.
|
|
3087
3292
|
|
|
3088
3293
|
Returns:
|
|
@@ -3091,11 +3296,14 @@ class Workflow:
|
|
|
3091
3296
|
|
|
3092
3297
|
from rich.prompt import Prompt
|
|
3093
3298
|
|
|
3299
|
+
# Considering both stream_events and stream_intermediate_steps (deprecated)
|
|
3300
|
+
stream_events = stream_events or stream_intermediate_steps or False
|
|
3301
|
+
|
|
3094
3302
|
if input:
|
|
3095
3303
|
await self.aprint_response(
|
|
3096
3304
|
input=input,
|
|
3097
3305
|
stream=stream,
|
|
3098
|
-
|
|
3306
|
+
stream_events=stream_events,
|
|
3099
3307
|
markdown=markdown,
|
|
3100
3308
|
show_time=show_time,
|
|
3101
3309
|
show_step_details=show_step_details,
|
|
@@ -3113,7 +3321,7 @@ class Workflow:
|
|
|
3113
3321
|
await self.aprint_response(
|
|
3114
3322
|
input=message,
|
|
3115
3323
|
stream=stream,
|
|
3116
|
-
|
|
3324
|
+
stream_events=stream_events,
|
|
3117
3325
|
markdown=markdown,
|
|
3118
3326
|
show_time=show_time,
|
|
3119
3327
|
show_step_details=show_step_details,
|