agno 2.1.3__py3-none-any.whl → 2.1.4__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 +39 -69
- agno/db/dynamo/dynamo.py +8 -6
- agno/db/dynamo/schemas.py +1 -10
- agno/db/dynamo/utils.py +2 -2
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/os/app.py +7 -3
- agno/os/interfaces/agui/utils.py +6 -0
- agno/os/schema.py +2 -0
- agno/os/utils.py +2 -1
- agno/session/workflow.py +69 -1
- agno/team/team.py +29 -79
- agno/tools/function.py +36 -18
- agno/tools/google_drive.py +270 -0
- agno/utils/print_response/workflow.py +112 -12
- agno/workflow/condition.py +25 -0
- agno/workflow/loop.py +25 -0
- agno/workflow/parallel.py +137 -113
- agno/workflow/router.py +25 -0
- agno/workflow/step.py +138 -25
- agno/workflow/steps.py +25 -0
- agno/workflow/types.py +26 -1
- agno/workflow/workflow.py +225 -7
- {agno-2.1.3.dist-info → agno-2.1.4.dist-info}/METADATA +1 -1
- {agno-2.1.3.dist-info → agno-2.1.4.dist-info}/RECORD +27 -26
- {agno-2.1.3.dist-info → agno-2.1.4.dist-info}/WHEEL +0 -0
- {agno-2.1.3.dist-info → agno-2.1.4.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.3.dist-info → agno-2.1.4.dist-info}/top_level.txt +0 -0
agno/workflow/step.py
CHANGED
|
@@ -17,6 +17,7 @@ from agno.run.workflow import (
|
|
|
17
17
|
WorkflowRunOutput,
|
|
18
18
|
WorkflowRunOutputEvent,
|
|
19
19
|
)
|
|
20
|
+
from agno.session.workflow import WorkflowSession
|
|
20
21
|
from agno.team import Team
|
|
21
22
|
from agno.utils.log import log_debug, logger, use_agent_logger, use_team_logger, use_workflow_logger
|
|
22
23
|
from agno.utils.merge_dict import merge_dictionaries
|
|
@@ -60,6 +61,9 @@ class Step:
|
|
|
60
61
|
# If False, only warn about missing inputs
|
|
61
62
|
strict_input_validation: bool = False
|
|
62
63
|
|
|
64
|
+
add_workflow_history: Optional[bool] = None
|
|
65
|
+
num_history_runs: int = 3
|
|
66
|
+
|
|
63
67
|
_retry_count: int = 0
|
|
64
68
|
|
|
65
69
|
def __init__(
|
|
@@ -74,6 +78,8 @@ class Step:
|
|
|
74
78
|
timeout_seconds: Optional[int] = None,
|
|
75
79
|
skip_on_failure: bool = False,
|
|
76
80
|
strict_input_validation: bool = False,
|
|
81
|
+
add_workflow_history: Optional[bool] = None,
|
|
82
|
+
num_history_runs: int = 3,
|
|
77
83
|
):
|
|
78
84
|
# Auto-detect name for function executors if not provided
|
|
79
85
|
if name is None and executor is not None:
|
|
@@ -93,6 +99,8 @@ class Step:
|
|
|
93
99
|
self.timeout_seconds = timeout_seconds
|
|
94
100
|
self.skip_on_failure = skip_on_failure
|
|
95
101
|
self.strict_input_validation = strict_input_validation
|
|
102
|
+
self.add_workflow_history = add_workflow_history
|
|
103
|
+
self.num_history_runs = num_history_runs
|
|
96
104
|
self.step_id = step_id
|
|
97
105
|
|
|
98
106
|
if step_id is None:
|
|
@@ -204,6 +212,9 @@ class Step:
|
|
|
204
212
|
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
205
213
|
session_state: Optional[Dict[str, Any]] = None,
|
|
206
214
|
store_executor_outputs: bool = True,
|
|
215
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
216
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
217
|
+
num_history_runs: int = 3,
|
|
207
218
|
) -> StepOutput:
|
|
208
219
|
"""Execute the step with StepInput, returning final StepOutput (non-streaming)"""
|
|
209
220
|
log_debug(f"Executing step: {self.name}")
|
|
@@ -211,6 +222,8 @@ class Step:
|
|
|
211
222
|
if step_input.previous_step_outputs:
|
|
212
223
|
step_input.previous_step_content = step_input.get_last_step_content()
|
|
213
224
|
|
|
225
|
+
if workflow_session:
|
|
226
|
+
step_input.workflow_session = workflow_session
|
|
214
227
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
215
228
|
|
|
216
229
|
# Execute with retries
|
|
@@ -292,8 +305,22 @@ class Step:
|
|
|
292
305
|
if isinstance(self.active_executor, Team):
|
|
293
306
|
kwargs["store_member_responses"] = True
|
|
294
307
|
|
|
308
|
+
num_history_runs = self.num_history_runs if self.num_history_runs else num_history_runs
|
|
309
|
+
|
|
310
|
+
use_history = (
|
|
311
|
+
self.add_workflow_history
|
|
312
|
+
if self.add_workflow_history is not None
|
|
313
|
+
else add_workflow_history_to_steps
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
final_message = message
|
|
317
|
+
if use_history and workflow_session:
|
|
318
|
+
history_messages = workflow_session.get_workflow_history_context(num_runs=num_history_runs)
|
|
319
|
+
if history_messages:
|
|
320
|
+
final_message = f"{history_messages}{message}"
|
|
321
|
+
|
|
295
322
|
response = self.active_executor.run( # type: ignore
|
|
296
|
-
input=
|
|
323
|
+
input=final_message, # type: ignore
|
|
297
324
|
images=images,
|
|
298
325
|
videos=videos,
|
|
299
326
|
audio=audios,
|
|
@@ -348,6 +375,32 @@ class Step:
|
|
|
348
375
|
except Exception:
|
|
349
376
|
return False
|
|
350
377
|
|
|
378
|
+
def _enrich_event_with_context(
|
|
379
|
+
self,
|
|
380
|
+
event: Any,
|
|
381
|
+
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
382
|
+
step_index: Optional[Union[int, tuple]] = None,
|
|
383
|
+
) -> Any:
|
|
384
|
+
"""Enrich event with step and workflow context information"""
|
|
385
|
+
if workflow_run_response is None:
|
|
386
|
+
return event
|
|
387
|
+
|
|
388
|
+
if hasattr(event, 'workflow_id'):
|
|
389
|
+
event.workflow_id = workflow_run_response.workflow_id
|
|
390
|
+
if hasattr(event, 'workflow_run_id'):
|
|
391
|
+
event.workflow_run_id = workflow_run_response.run_id
|
|
392
|
+
if hasattr(event, "step_id"):
|
|
393
|
+
event.step_id = self.step_id
|
|
394
|
+
if hasattr(event, "step_name") and self.name is not None:
|
|
395
|
+
if getattr(event, "step_name", None) is None:
|
|
396
|
+
event.step_name = self.name
|
|
397
|
+
# Only set step_index if it's not already set (preserve parallel.py's tuples)
|
|
398
|
+
if hasattr(event, "step_index") and step_index is not None:
|
|
399
|
+
if event.step_index is None:
|
|
400
|
+
event.step_index = step_index
|
|
401
|
+
|
|
402
|
+
return event
|
|
403
|
+
|
|
351
404
|
def execute_stream(
|
|
352
405
|
self,
|
|
353
406
|
step_input: StepInput,
|
|
@@ -359,12 +412,17 @@ class Step:
|
|
|
359
412
|
step_index: Optional[Union[int, tuple]] = None,
|
|
360
413
|
store_executor_outputs: bool = True,
|
|
361
414
|
parent_step_id: Optional[str] = None,
|
|
415
|
+
workflow_session: Optional["WorkflowSession"] = None,
|
|
416
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
417
|
+
num_history_runs: int = 3,
|
|
362
418
|
) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
|
|
363
419
|
"""Execute the step with event-driven streaming support"""
|
|
364
420
|
|
|
365
421
|
if step_input.previous_step_outputs:
|
|
366
422
|
step_input.previous_step_content = step_input.get_last_step_content()
|
|
367
423
|
|
|
424
|
+
if workflow_session:
|
|
425
|
+
step_input.workflow_session = workflow_session
|
|
368
426
|
# Create session_state copy once to avoid duplication
|
|
369
427
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
370
428
|
|
|
@@ -413,7 +471,11 @@ class Step:
|
|
|
413
471
|
final_response = event
|
|
414
472
|
break
|
|
415
473
|
else:
|
|
416
|
-
|
|
474
|
+
# Enrich event with workflow context before yielding
|
|
475
|
+
enriched_event = self._enrich_event_with_context(
|
|
476
|
+
event, workflow_run_response, step_index
|
|
477
|
+
)
|
|
478
|
+
yield enriched_event # type: ignore[misc]
|
|
417
479
|
|
|
418
480
|
# Merge session_state changes back
|
|
419
481
|
if session_state is not None:
|
|
@@ -463,8 +525,22 @@ class Step:
|
|
|
463
525
|
if isinstance(self.active_executor, Team):
|
|
464
526
|
kwargs["store_member_responses"] = True
|
|
465
527
|
|
|
528
|
+
num_history_runs = self.num_history_runs if self.num_history_runs else num_history_runs
|
|
529
|
+
|
|
530
|
+
use_history = (
|
|
531
|
+
self.add_workflow_history
|
|
532
|
+
if self.add_workflow_history is not None
|
|
533
|
+
else add_workflow_history_to_steps
|
|
534
|
+
)
|
|
535
|
+
|
|
536
|
+
final_message = message
|
|
537
|
+
if use_history and workflow_session:
|
|
538
|
+
history_messages = workflow_session.get_workflow_history_context(num_runs=num_history_runs)
|
|
539
|
+
if history_messages:
|
|
540
|
+
final_message = f"{history_messages}{message}"
|
|
541
|
+
|
|
466
542
|
response_stream = self.active_executor.run( # type: ignore[call-overload, misc]
|
|
467
|
-
input=
|
|
543
|
+
input=final_message,
|
|
468
544
|
images=images,
|
|
469
545
|
videos=videos,
|
|
470
546
|
audio=audios,
|
|
@@ -474,14 +550,6 @@ class Step:
|
|
|
474
550
|
session_state=session_state_copy, # Send a copy to the executor
|
|
475
551
|
stream=True,
|
|
476
552
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
477
|
-
# Pass workflow context directly via kwargs
|
|
478
|
-
workflow_context={
|
|
479
|
-
"workflow_id": workflow_run_response.workflow_id if workflow_run_response else None,
|
|
480
|
-
"workflow_run_id": workflow_run_response.run_id if workflow_run_response else None,
|
|
481
|
-
"step_id": self.step_id,
|
|
482
|
-
"step_name": self.name,
|
|
483
|
-
"step_index": step_index,
|
|
484
|
-
},
|
|
485
553
|
yield_run_response=True,
|
|
486
554
|
**kwargs,
|
|
487
555
|
)
|
|
@@ -491,7 +559,10 @@ class Step:
|
|
|
491
559
|
if isinstance(event, RunOutput) or isinstance(event, TeamRunOutput):
|
|
492
560
|
active_executor_run_response = event
|
|
493
561
|
break
|
|
494
|
-
|
|
562
|
+
enriched_event = self._enrich_event_with_context(
|
|
563
|
+
event, workflow_run_response, step_index
|
|
564
|
+
)
|
|
565
|
+
yield enriched_event # type: ignore[misc]
|
|
495
566
|
|
|
496
567
|
if session_state is not None:
|
|
497
568
|
# Update workflow session state
|
|
@@ -558,6 +629,9 @@ class Step:
|
|
|
558
629
|
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
559
630
|
session_state: Optional[Dict[str, Any]] = None,
|
|
560
631
|
store_executor_outputs: bool = True,
|
|
632
|
+
workflow_session: Optional["WorkflowSession"] = None,
|
|
633
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
634
|
+
num_history_runs: int = 3,
|
|
561
635
|
) -> StepOutput:
|
|
562
636
|
"""Execute the step with StepInput, returning final StepOutput (non-streaming)"""
|
|
563
637
|
logger.info(f"Executing async step (non-streaming): {self.name}")
|
|
@@ -566,6 +640,8 @@ class Step:
|
|
|
566
640
|
if step_input.previous_step_outputs:
|
|
567
641
|
step_input.previous_step_content = step_input.get_last_step_content()
|
|
568
642
|
|
|
643
|
+
if workflow_session:
|
|
644
|
+
step_input.workflow_session = workflow_session
|
|
569
645
|
# Create session_state copy once to avoid duplication
|
|
570
646
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
571
647
|
|
|
@@ -670,8 +746,22 @@ class Step:
|
|
|
670
746
|
if isinstance(self.active_executor, Team):
|
|
671
747
|
kwargs["store_member_responses"] = True
|
|
672
748
|
|
|
749
|
+
num_history_runs = self.num_history_runs if self.num_history_runs else num_history_runs
|
|
750
|
+
|
|
751
|
+
use_history = (
|
|
752
|
+
self.add_workflow_history
|
|
753
|
+
if self.add_workflow_history is not None
|
|
754
|
+
else add_workflow_history_to_steps
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
final_message = message
|
|
758
|
+
if use_history and workflow_session:
|
|
759
|
+
history_messages = workflow_session.get_workflow_history_context(num_runs=num_history_runs)
|
|
760
|
+
if history_messages:
|
|
761
|
+
final_message = f"{history_messages}{message}"
|
|
762
|
+
|
|
673
763
|
response = await self.active_executor.arun( # type: ignore
|
|
674
|
-
input=
|
|
764
|
+
input=final_message, # type: ignore
|
|
675
765
|
images=images,
|
|
676
766
|
videos=videos,
|
|
677
767
|
audio=audios,
|
|
@@ -724,12 +814,18 @@ class Step:
|
|
|
724
814
|
step_index: Optional[Union[int, tuple]] = None,
|
|
725
815
|
store_executor_outputs: bool = True,
|
|
726
816
|
parent_step_id: Optional[str] = None,
|
|
817
|
+
workflow_session: Optional["WorkflowSession"] = None,
|
|
818
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
819
|
+
num_history_runs: int = 3,
|
|
727
820
|
) -> AsyncIterator[Union[WorkflowRunOutputEvent, StepOutput]]:
|
|
728
821
|
"""Execute the step with event-driven streaming support"""
|
|
729
822
|
|
|
730
823
|
if step_input.previous_step_outputs:
|
|
731
824
|
step_input.previous_step_content = step_input.get_last_step_content()
|
|
732
825
|
|
|
826
|
+
if workflow_session:
|
|
827
|
+
step_input.workflow_session = workflow_session
|
|
828
|
+
|
|
733
829
|
# Create session_state copy once to avoid duplication
|
|
734
830
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
735
831
|
|
|
@@ -776,7 +872,11 @@ class Step:
|
|
|
776
872
|
final_response = event
|
|
777
873
|
break
|
|
778
874
|
else:
|
|
779
|
-
|
|
875
|
+
# Enrich event with workflow context before yielding
|
|
876
|
+
enriched_event = self._enrich_event_with_context(
|
|
877
|
+
event, workflow_run_response, step_index
|
|
878
|
+
)
|
|
879
|
+
yield enriched_event # type: ignore[misc]
|
|
780
880
|
if not final_response:
|
|
781
881
|
final_response = StepOutput(content=content)
|
|
782
882
|
elif inspect.iscoroutinefunction(self.active_executor):
|
|
@@ -803,7 +903,11 @@ class Step:
|
|
|
803
903
|
final_response = event
|
|
804
904
|
break
|
|
805
905
|
else:
|
|
806
|
-
|
|
906
|
+
# Enrich event with workflow context before yielding
|
|
907
|
+
enriched_event = self._enrich_event_with_context(
|
|
908
|
+
event, workflow_run_response, step_index
|
|
909
|
+
)
|
|
910
|
+
yield enriched_event # type: ignore[misc]
|
|
807
911
|
if not final_response:
|
|
808
912
|
final_response = StepOutput(content=content)
|
|
809
913
|
else:
|
|
@@ -843,8 +947,22 @@ class Step:
|
|
|
843
947
|
if isinstance(self.active_executor, Team):
|
|
844
948
|
kwargs["store_member_responses"] = True
|
|
845
949
|
|
|
950
|
+
num_history_runs = self.num_history_runs if self.num_history_runs else num_history_runs
|
|
951
|
+
|
|
952
|
+
use_history = (
|
|
953
|
+
self.add_workflow_history
|
|
954
|
+
if self.add_workflow_history is not None
|
|
955
|
+
else add_workflow_history_to_steps
|
|
956
|
+
)
|
|
957
|
+
|
|
958
|
+
final_message = message
|
|
959
|
+
if use_history and workflow_session:
|
|
960
|
+
history_messages = workflow_session.get_workflow_history_context(num_runs=num_history_runs)
|
|
961
|
+
if history_messages:
|
|
962
|
+
final_message = f"{history_messages}{message}"
|
|
963
|
+
|
|
846
964
|
response_stream = self.active_executor.arun( # type: ignore
|
|
847
|
-
input=
|
|
965
|
+
input=final_message,
|
|
848
966
|
images=images,
|
|
849
967
|
videos=videos,
|
|
850
968
|
audio=audios,
|
|
@@ -854,14 +972,6 @@ class Step:
|
|
|
854
972
|
session_state=session_state_copy,
|
|
855
973
|
stream=True,
|
|
856
974
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
857
|
-
# Pass workflow context directly via kwargs
|
|
858
|
-
workflow_context={
|
|
859
|
-
"workflow_id": workflow_run_response.workflow_id if workflow_run_response else None,
|
|
860
|
-
"workflow_run_id": workflow_run_response.run_id if workflow_run_response else None,
|
|
861
|
-
"step_id": self.step_id,
|
|
862
|
-
"step_name": self.name,
|
|
863
|
-
"step_index": step_index,
|
|
864
|
-
},
|
|
865
975
|
yield_run_response=True,
|
|
866
976
|
**kwargs,
|
|
867
977
|
)
|
|
@@ -871,7 +981,10 @@ class Step:
|
|
|
871
981
|
if isinstance(event, RunOutput) or isinstance(event, TeamRunOutput):
|
|
872
982
|
active_executor_run_response = event
|
|
873
983
|
break
|
|
874
|
-
|
|
984
|
+
enriched_event = self._enrich_event_with_context(
|
|
985
|
+
event, workflow_run_response, step_index
|
|
986
|
+
)
|
|
987
|
+
yield enriched_event # type: ignore[misc]
|
|
875
988
|
|
|
876
989
|
if session_state is not None:
|
|
877
990
|
# Update workflow session state
|
agno/workflow/steps.py
CHANGED
|
@@ -10,6 +10,7 @@ from agno.run.workflow import (
|
|
|
10
10
|
WorkflowRunOutput,
|
|
11
11
|
WorkflowRunOutputEvent,
|
|
12
12
|
)
|
|
13
|
+
from agno.session.workflow import WorkflowSession
|
|
13
14
|
from agno.utils.log import log_debug, logger
|
|
14
15
|
from agno.workflow.step import Step, StepInput, StepOutput, StepType
|
|
15
16
|
|
|
@@ -119,6 +120,9 @@ class Steps:
|
|
|
119
120
|
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
120
121
|
session_state: Optional[Dict[str, Any]] = None,
|
|
121
122
|
store_executor_outputs: bool = True,
|
|
123
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
124
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
125
|
+
num_history_runs: int = 3,
|
|
122
126
|
) -> StepOutput:
|
|
123
127
|
"""Execute all steps in sequence and return the final result"""
|
|
124
128
|
log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
|
|
@@ -148,6 +152,9 @@ class Steps:
|
|
|
148
152
|
workflow_run_response=workflow_run_response,
|
|
149
153
|
store_executor_outputs=store_executor_outputs,
|
|
150
154
|
session_state=session_state,
|
|
155
|
+
workflow_session=workflow_session,
|
|
156
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
157
|
+
num_history_runs=num_history_runs,
|
|
151
158
|
)
|
|
152
159
|
|
|
153
160
|
# Handle both single StepOutput and List[StepOutput] (from Loop/Condition/Router steps)
|
|
@@ -204,6 +211,9 @@ class Steps:
|
|
|
204
211
|
step_index: Optional[Union[int, tuple]] = None,
|
|
205
212
|
store_executor_outputs: bool = True,
|
|
206
213
|
parent_step_id: Optional[str] = None,
|
|
214
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
215
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
216
|
+
num_history_runs: int = 3,
|
|
207
217
|
) -> Iterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
|
|
208
218
|
"""Execute all steps in sequence with streaming support"""
|
|
209
219
|
log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
|
|
@@ -260,6 +270,9 @@ class Steps:
|
|
|
260
270
|
step_index=child_step_index,
|
|
261
271
|
store_executor_outputs=store_executor_outputs,
|
|
262
272
|
parent_step_id=steps_id,
|
|
273
|
+
workflow_session=workflow_session,
|
|
274
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
275
|
+
num_history_runs=num_history_runs,
|
|
263
276
|
):
|
|
264
277
|
if isinstance(event, StepOutput):
|
|
265
278
|
step_outputs_for_step.append(event)
|
|
@@ -337,6 +350,9 @@ class Steps:
|
|
|
337
350
|
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
338
351
|
session_state: Optional[Dict[str, Any]] = None,
|
|
339
352
|
store_executor_outputs: bool = True,
|
|
353
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
354
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
355
|
+
num_history_runs: int = 3,
|
|
340
356
|
) -> StepOutput:
|
|
341
357
|
"""Execute all steps in sequence asynchronously and return the final result"""
|
|
342
358
|
log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
|
|
@@ -366,6 +382,9 @@ class Steps:
|
|
|
366
382
|
workflow_run_response=workflow_run_response,
|
|
367
383
|
store_executor_outputs=store_executor_outputs,
|
|
368
384
|
session_state=session_state,
|
|
385
|
+
workflow_session=workflow_session,
|
|
386
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
387
|
+
num_history_runs=num_history_runs,
|
|
369
388
|
)
|
|
370
389
|
|
|
371
390
|
# Handle both single StepOutput and List[StepOutput] (from Loop/Condition/Router steps)
|
|
@@ -421,6 +440,9 @@ class Steps:
|
|
|
421
440
|
step_index: Optional[Union[int, tuple]] = None,
|
|
422
441
|
store_executor_outputs: bool = True,
|
|
423
442
|
parent_step_id: Optional[str] = None,
|
|
443
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
444
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
445
|
+
num_history_runs: int = 3,
|
|
424
446
|
) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
|
|
425
447
|
"""Execute all steps in sequence with async streaming support"""
|
|
426
448
|
log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
|
|
@@ -477,6 +499,9 @@ class Steps:
|
|
|
477
499
|
step_index=child_step_index,
|
|
478
500
|
store_executor_outputs=store_executor_outputs,
|
|
479
501
|
parent_step_id=steps_id,
|
|
502
|
+
workflow_session=workflow_session,
|
|
503
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
504
|
+
num_history_runs=num_history_runs,
|
|
480
505
|
):
|
|
481
506
|
if isinstance(event, StepOutput):
|
|
482
507
|
step_outputs_for_step.append(event)
|
agno/workflow/types.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import Any, Dict, List, Optional, Union
|
|
4
|
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
5
5
|
|
|
6
6
|
from fastapi import WebSocket
|
|
7
7
|
from pydantic import BaseModel
|
|
8
8
|
|
|
9
9
|
from agno.media import Audio, File, Image, Video
|
|
10
10
|
from agno.models.metrics import Metrics
|
|
11
|
+
from agno.session.workflow import WorkflowSession
|
|
11
12
|
from agno.utils.log import log_warning
|
|
12
13
|
from agno.utils.serialize import json_serializer
|
|
13
14
|
|
|
@@ -80,6 +81,8 @@ class StepInput:
|
|
|
80
81
|
audio: Optional[List[Audio]] = None
|
|
81
82
|
files: Optional[List[File]] = None
|
|
82
83
|
|
|
84
|
+
workflow_session: Optional["WorkflowSession"] = None
|
|
85
|
+
|
|
83
86
|
def get_input_as_string(self) -> Optional[str]:
|
|
84
87
|
"""Convert input to string representation"""
|
|
85
88
|
if self.input is None:
|
|
@@ -171,6 +174,28 @@ class StepInput:
|
|
|
171
174
|
# Use the helper method to get the deepest content
|
|
172
175
|
return self._get_deepest_step_content(last_output) # type: ignore[return-value]
|
|
173
176
|
|
|
177
|
+
def get_workflow_history(self, num_runs: Optional[int] = None) -> List[Tuple[str, str]]:
|
|
178
|
+
"""Get workflow conversation history as structured data for custom function steps
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
num_runs: Number of recent runs to include. If None, returns all available history.
|
|
182
|
+
"""
|
|
183
|
+
if not self.workflow_session:
|
|
184
|
+
return []
|
|
185
|
+
|
|
186
|
+
return self.workflow_session.get_workflow_history(num_runs=num_runs)
|
|
187
|
+
|
|
188
|
+
def get_workflow_history_context(self, num_runs: Optional[int] = None) -> Optional[str]:
|
|
189
|
+
"""Get formatted workflow conversation history context for custom function steps
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
num_runs: Number of recent runs to include. If None, returns all available history.
|
|
193
|
+
"""
|
|
194
|
+
if not self.workflow_session:
|
|
195
|
+
return None
|
|
196
|
+
|
|
197
|
+
return self.workflow_session.get_workflow_history_context(num_runs=num_runs)
|
|
198
|
+
|
|
174
199
|
def to_dict(self) -> Dict[str, Any]:
|
|
175
200
|
"""Convert to dictionary"""
|
|
176
201
|
# Handle the unified message field
|