agno 2.1.3__py3-none-any.whl → 2.1.5__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 +1779 -577
- agno/db/async_postgres/__init__.py +3 -0
- agno/db/async_postgres/async_postgres.py +1668 -0
- agno/db/async_postgres/schemas.py +124 -0
- agno/db/async_postgres/utils.py +289 -0
- agno/db/base.py +237 -2
- agno/db/dynamo/dynamo.py +10 -8
- agno/db/dynamo/schemas.py +1 -10
- agno/db/dynamo/utils.py +2 -2
- agno/db/firestore/firestore.py +2 -2
- agno/db/firestore/utils.py +4 -2
- agno/db/gcs_json/gcs_json_db.py +2 -2
- agno/db/in_memory/in_memory_db.py +2 -2
- agno/db/json/json_db.py +2 -2
- agno/db/migrations/v1_to_v2.py +30 -13
- agno/db/mongo/mongo.py +18 -6
- agno/db/mysql/mysql.py +35 -13
- agno/db/postgres/postgres.py +29 -6
- agno/db/redis/redis.py +2 -2
- agno/db/singlestore/singlestore.py +2 -2
- agno/db/sqlite/sqlite.py +34 -12
- agno/db/sqlite/utils.py +8 -3
- agno/eval/accuracy.py +50 -43
- agno/eval/performance.py +6 -3
- agno/eval/reliability.py +6 -3
- agno/eval/utils.py +33 -16
- agno/exceptions.py +8 -2
- agno/knowledge/embedder/fastembed.py +1 -1
- agno/knowledge/knowledge.py +260 -46
- agno/knowledge/reader/pdf_reader.py +4 -6
- agno/knowledge/reader/reader_factory.py +2 -3
- agno/memory/manager.py +241 -33
- agno/models/anthropic/claude.py +37 -0
- agno/os/app.py +15 -10
- agno/os/interfaces/a2a/router.py +3 -5
- agno/os/interfaces/agui/router.py +4 -1
- agno/os/interfaces/agui/utils.py +33 -6
- agno/os/interfaces/slack/router.py +2 -4
- agno/os/mcp.py +98 -41
- agno/os/router.py +23 -0
- agno/os/routers/evals/evals.py +52 -20
- agno/os/routers/evals/utils.py +14 -14
- agno/os/routers/knowledge/knowledge.py +130 -9
- agno/os/routers/knowledge/schemas.py +57 -0
- agno/os/routers/memory/memory.py +116 -44
- agno/os/routers/metrics/metrics.py +16 -6
- agno/os/routers/session/session.py +65 -22
- agno/os/schema.py +38 -0
- agno/os/utils.py +69 -13
- agno/reasoning/anthropic.py +80 -0
- agno/reasoning/gemini.py +73 -0
- agno/reasoning/openai.py +5 -0
- agno/reasoning/vertexai.py +76 -0
- agno/session/workflow.py +69 -1
- agno/team/team.py +934 -241
- agno/tools/function.py +36 -18
- agno/tools/google_drive.py +270 -0
- agno/tools/googlesheets.py +20 -5
- agno/tools/mcp_toolbox.py +3 -3
- agno/tools/scrapegraph.py +1 -1
- agno/utils/models/claude.py +3 -1
- agno/utils/print_response/workflow.py +112 -12
- agno/utils/streamlit.py +1 -1
- agno/vectordb/base.py +22 -1
- agno/vectordb/cassandra/cassandra.py +9 -0
- agno/vectordb/chroma/chromadb.py +26 -6
- agno/vectordb/clickhouse/clickhousedb.py +9 -1
- agno/vectordb/couchbase/couchbase.py +11 -0
- agno/vectordb/lancedb/lance_db.py +20 -0
- agno/vectordb/langchaindb/langchaindb.py +11 -0
- agno/vectordb/lightrag/lightrag.py +9 -0
- agno/vectordb/llamaindex/llamaindexdb.py +15 -1
- agno/vectordb/milvus/milvus.py +23 -0
- agno/vectordb/mongodb/mongodb.py +22 -0
- agno/vectordb/pgvector/pgvector.py +19 -0
- agno/vectordb/pineconedb/pineconedb.py +35 -4
- agno/vectordb/qdrant/qdrant.py +24 -0
- agno/vectordb/singlestore/singlestore.py +25 -17
- agno/vectordb/surrealdb/surrealdb.py +18 -1
- agno/vectordb/upstashdb/upstashdb.py +26 -1
- agno/vectordb/weaviate/weaviate.py +18 -0
- agno/workflow/condition.py +29 -0
- agno/workflow/loop.py +29 -0
- agno/workflow/parallel.py +141 -113
- agno/workflow/router.py +29 -0
- agno/workflow/step.py +146 -25
- agno/workflow/steps.py +29 -0
- agno/workflow/types.py +26 -1
- agno/workflow/workflow.py +507 -22
- {agno-2.1.3.dist-info → agno-2.1.5.dist-info}/METADATA +100 -41
- {agno-2.1.3.dist-info → agno-2.1.5.dist-info}/RECORD +94 -86
- {agno-2.1.3.dist-info → agno-2.1.5.dist-info}/WHEEL +0 -0
- {agno-2.1.3.dist-info → agno-2.1.5.dist-info}/licenses/LICENSE +0 -0
- {agno-2.1.3.dist-info → agno-2.1.5.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,23 +375,55 @@ 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,
|
|
354
407
|
session_id: Optional[str] = None,
|
|
355
408
|
user_id: Optional[str] = None,
|
|
356
409
|
stream_intermediate_steps: bool = False,
|
|
410
|
+
stream_executor_events: bool = True,
|
|
357
411
|
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
358
412
|
session_state: Optional[Dict[str, Any]] = None,
|
|
359
413
|
step_index: Optional[Union[int, tuple]] = None,
|
|
360
414
|
store_executor_outputs: bool = True,
|
|
361
415
|
parent_step_id: Optional[str] = None,
|
|
416
|
+
workflow_session: Optional["WorkflowSession"] = None,
|
|
417
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
418
|
+
num_history_runs: int = 3,
|
|
362
419
|
) -> Iterator[Union[WorkflowRunOutputEvent, StepOutput]]:
|
|
363
420
|
"""Execute the step with event-driven streaming support"""
|
|
364
421
|
|
|
365
422
|
if step_input.previous_step_outputs:
|
|
366
423
|
step_input.previous_step_content = step_input.get_last_step_content()
|
|
367
424
|
|
|
425
|
+
if workflow_session:
|
|
426
|
+
step_input.workflow_session = workflow_session
|
|
368
427
|
# Create session_state copy once to avoid duplication
|
|
369
428
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
370
429
|
|
|
@@ -413,7 +472,13 @@ class Step:
|
|
|
413
472
|
final_response = event
|
|
414
473
|
break
|
|
415
474
|
else:
|
|
416
|
-
|
|
475
|
+
# Enrich event with workflow context before yielding
|
|
476
|
+
enriched_event = self._enrich_event_with_context(
|
|
477
|
+
event, workflow_run_response, step_index
|
|
478
|
+
)
|
|
479
|
+
# Only yield executor events if stream_executor_events is True
|
|
480
|
+
if stream_executor_events:
|
|
481
|
+
yield enriched_event # type: ignore[misc]
|
|
417
482
|
|
|
418
483
|
# Merge session_state changes back
|
|
419
484
|
if session_state is not None:
|
|
@@ -463,8 +528,22 @@ class Step:
|
|
|
463
528
|
if isinstance(self.active_executor, Team):
|
|
464
529
|
kwargs["store_member_responses"] = True
|
|
465
530
|
|
|
531
|
+
num_history_runs = self.num_history_runs if self.num_history_runs else num_history_runs
|
|
532
|
+
|
|
533
|
+
use_history = (
|
|
534
|
+
self.add_workflow_history
|
|
535
|
+
if self.add_workflow_history is not None
|
|
536
|
+
else add_workflow_history_to_steps
|
|
537
|
+
)
|
|
538
|
+
|
|
539
|
+
final_message = message
|
|
540
|
+
if use_history and workflow_session:
|
|
541
|
+
history_messages = workflow_session.get_workflow_history_context(num_runs=num_history_runs)
|
|
542
|
+
if history_messages:
|
|
543
|
+
final_message = f"{history_messages}{message}"
|
|
544
|
+
|
|
466
545
|
response_stream = self.active_executor.run( # type: ignore[call-overload, misc]
|
|
467
|
-
input=
|
|
546
|
+
input=final_message,
|
|
468
547
|
images=images,
|
|
469
548
|
videos=videos,
|
|
470
549
|
audio=audios,
|
|
@@ -474,14 +553,6 @@ class Step:
|
|
|
474
553
|
session_state=session_state_copy, # Send a copy to the executor
|
|
475
554
|
stream=True,
|
|
476
555
|
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
556
|
yield_run_response=True,
|
|
486
557
|
**kwargs,
|
|
487
558
|
)
|
|
@@ -491,7 +562,10 @@ class Step:
|
|
|
491
562
|
if isinstance(event, RunOutput) or isinstance(event, TeamRunOutput):
|
|
492
563
|
active_executor_run_response = event
|
|
493
564
|
break
|
|
494
|
-
|
|
565
|
+
enriched_event = self._enrich_event_with_context(event, workflow_run_response, step_index)
|
|
566
|
+
# Only yield executor events if stream_executor_events is True
|
|
567
|
+
if stream_executor_events:
|
|
568
|
+
yield enriched_event # type: ignore[misc]
|
|
495
569
|
|
|
496
570
|
if session_state is not None:
|
|
497
571
|
# Update workflow session state
|
|
@@ -558,6 +632,9 @@ class Step:
|
|
|
558
632
|
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
559
633
|
session_state: Optional[Dict[str, Any]] = None,
|
|
560
634
|
store_executor_outputs: bool = True,
|
|
635
|
+
workflow_session: Optional["WorkflowSession"] = None,
|
|
636
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
637
|
+
num_history_runs: int = 3,
|
|
561
638
|
) -> StepOutput:
|
|
562
639
|
"""Execute the step with StepInput, returning final StepOutput (non-streaming)"""
|
|
563
640
|
logger.info(f"Executing async step (non-streaming): {self.name}")
|
|
@@ -566,6 +643,8 @@ class Step:
|
|
|
566
643
|
if step_input.previous_step_outputs:
|
|
567
644
|
step_input.previous_step_content = step_input.get_last_step_content()
|
|
568
645
|
|
|
646
|
+
if workflow_session:
|
|
647
|
+
step_input.workflow_session = workflow_session
|
|
569
648
|
# Create session_state copy once to avoid duplication
|
|
570
649
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
571
650
|
|
|
@@ -670,8 +749,22 @@ class Step:
|
|
|
670
749
|
if isinstance(self.active_executor, Team):
|
|
671
750
|
kwargs["store_member_responses"] = True
|
|
672
751
|
|
|
752
|
+
num_history_runs = self.num_history_runs if self.num_history_runs else num_history_runs
|
|
753
|
+
|
|
754
|
+
use_history = (
|
|
755
|
+
self.add_workflow_history
|
|
756
|
+
if self.add_workflow_history is not None
|
|
757
|
+
else add_workflow_history_to_steps
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
final_message = message
|
|
761
|
+
if use_history and workflow_session:
|
|
762
|
+
history_messages = workflow_session.get_workflow_history_context(num_runs=num_history_runs)
|
|
763
|
+
if history_messages:
|
|
764
|
+
final_message = f"{history_messages}{message}"
|
|
765
|
+
|
|
673
766
|
response = await self.active_executor.arun( # type: ignore
|
|
674
|
-
input=
|
|
767
|
+
input=final_message, # type: ignore
|
|
675
768
|
images=images,
|
|
676
769
|
videos=videos,
|
|
677
770
|
audio=audios,
|
|
@@ -719,17 +812,24 @@ class Step:
|
|
|
719
812
|
session_id: Optional[str] = None,
|
|
720
813
|
user_id: Optional[str] = None,
|
|
721
814
|
stream_intermediate_steps: bool = False,
|
|
815
|
+
stream_executor_events: bool = True,
|
|
722
816
|
workflow_run_response: Optional["WorkflowRunOutput"] = None,
|
|
723
817
|
session_state: Optional[Dict[str, Any]] = None,
|
|
724
818
|
step_index: Optional[Union[int, tuple]] = None,
|
|
725
819
|
store_executor_outputs: bool = True,
|
|
726
820
|
parent_step_id: Optional[str] = None,
|
|
821
|
+
workflow_session: Optional["WorkflowSession"] = None,
|
|
822
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
823
|
+
num_history_runs: int = 3,
|
|
727
824
|
) -> AsyncIterator[Union[WorkflowRunOutputEvent, StepOutput]]:
|
|
728
825
|
"""Execute the step with event-driven streaming support"""
|
|
729
826
|
|
|
730
827
|
if step_input.previous_step_outputs:
|
|
731
828
|
step_input.previous_step_content = step_input.get_last_step_content()
|
|
732
829
|
|
|
830
|
+
if workflow_session:
|
|
831
|
+
step_input.workflow_session = workflow_session
|
|
832
|
+
|
|
733
833
|
# Create session_state copy once to avoid duplication
|
|
734
834
|
session_state_copy = copy(session_state) if session_state is not None else {}
|
|
735
835
|
|
|
@@ -776,7 +876,13 @@ class Step:
|
|
|
776
876
|
final_response = event
|
|
777
877
|
break
|
|
778
878
|
else:
|
|
779
|
-
|
|
879
|
+
# Enrich event with workflow context before yielding
|
|
880
|
+
enriched_event = self._enrich_event_with_context(
|
|
881
|
+
event, workflow_run_response, step_index
|
|
882
|
+
)
|
|
883
|
+
# Only yield executor events if stream_executor_events is True
|
|
884
|
+
if stream_executor_events:
|
|
885
|
+
yield enriched_event # type: ignore[misc]
|
|
780
886
|
if not final_response:
|
|
781
887
|
final_response = StepOutput(content=content)
|
|
782
888
|
elif inspect.iscoroutinefunction(self.active_executor):
|
|
@@ -803,7 +909,13 @@ class Step:
|
|
|
803
909
|
final_response = event
|
|
804
910
|
break
|
|
805
911
|
else:
|
|
806
|
-
|
|
912
|
+
# Enrich event with workflow context before yielding
|
|
913
|
+
enriched_event = self._enrich_event_with_context(
|
|
914
|
+
event, workflow_run_response, step_index
|
|
915
|
+
)
|
|
916
|
+
# Only yield executor events if stream_executor_events is True
|
|
917
|
+
if stream_executor_events:
|
|
918
|
+
yield enriched_event # type: ignore[misc]
|
|
807
919
|
if not final_response:
|
|
808
920
|
final_response = StepOutput(content=content)
|
|
809
921
|
else:
|
|
@@ -843,8 +955,22 @@ class Step:
|
|
|
843
955
|
if isinstance(self.active_executor, Team):
|
|
844
956
|
kwargs["store_member_responses"] = True
|
|
845
957
|
|
|
958
|
+
num_history_runs = self.num_history_runs if self.num_history_runs else num_history_runs
|
|
959
|
+
|
|
960
|
+
use_history = (
|
|
961
|
+
self.add_workflow_history
|
|
962
|
+
if self.add_workflow_history is not None
|
|
963
|
+
else add_workflow_history_to_steps
|
|
964
|
+
)
|
|
965
|
+
|
|
966
|
+
final_message = message
|
|
967
|
+
if use_history and workflow_session:
|
|
968
|
+
history_messages = workflow_session.get_workflow_history_context(num_runs=num_history_runs)
|
|
969
|
+
if history_messages:
|
|
970
|
+
final_message = f"{history_messages}{message}"
|
|
971
|
+
|
|
846
972
|
response_stream = self.active_executor.arun( # type: ignore
|
|
847
|
-
input=
|
|
973
|
+
input=final_message,
|
|
848
974
|
images=images,
|
|
849
975
|
videos=videos,
|
|
850
976
|
audio=audios,
|
|
@@ -854,14 +980,6 @@ class Step:
|
|
|
854
980
|
session_state=session_state_copy,
|
|
855
981
|
stream=True,
|
|
856
982
|
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
983
|
yield_run_response=True,
|
|
866
984
|
**kwargs,
|
|
867
985
|
)
|
|
@@ -871,7 +989,10 @@ class Step:
|
|
|
871
989
|
if isinstance(event, RunOutput) or isinstance(event, TeamRunOutput):
|
|
872
990
|
active_executor_run_response = event
|
|
873
991
|
break
|
|
874
|
-
|
|
992
|
+
enriched_event = self._enrich_event_with_context(event, workflow_run_response, step_index)
|
|
993
|
+
# Only yield executor events if stream_executor_events is True
|
|
994
|
+
if stream_executor_events:
|
|
995
|
+
yield enriched_event # type: ignore[misc]
|
|
875
996
|
|
|
876
997
|
if session_state is not None:
|
|
877
998
|
# 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)
|
|
@@ -201,9 +208,13 @@ class Steps:
|
|
|
201
208
|
session_id: Optional[str] = None,
|
|
202
209
|
user_id: Optional[str] = None,
|
|
203
210
|
stream_intermediate_steps: bool = False,
|
|
211
|
+
stream_executor_events: bool = True,
|
|
204
212
|
step_index: Optional[Union[int, tuple]] = None,
|
|
205
213
|
store_executor_outputs: bool = True,
|
|
206
214
|
parent_step_id: Optional[str] = None,
|
|
215
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
216
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
217
|
+
num_history_runs: int = 3,
|
|
207
218
|
) -> Iterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
|
|
208
219
|
"""Execute all steps in sequence with streaming support"""
|
|
209
220
|
log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
|
|
@@ -256,10 +267,14 @@ class Steps:
|
|
|
256
267
|
user_id=user_id,
|
|
257
268
|
session_state=session_state,
|
|
258
269
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
270
|
+
stream_executor_events=stream_executor_events,
|
|
259
271
|
workflow_run_response=workflow_run_response,
|
|
260
272
|
step_index=child_step_index,
|
|
261
273
|
store_executor_outputs=store_executor_outputs,
|
|
262
274
|
parent_step_id=steps_id,
|
|
275
|
+
workflow_session=workflow_session,
|
|
276
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
277
|
+
num_history_runs=num_history_runs,
|
|
263
278
|
):
|
|
264
279
|
if isinstance(event, StepOutput):
|
|
265
280
|
step_outputs_for_step.append(event)
|
|
@@ -337,6 +352,9 @@ class Steps:
|
|
|
337
352
|
workflow_run_response: Optional[WorkflowRunOutput] = None,
|
|
338
353
|
session_state: Optional[Dict[str, Any]] = None,
|
|
339
354
|
store_executor_outputs: bool = True,
|
|
355
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
356
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
357
|
+
num_history_runs: int = 3,
|
|
340
358
|
) -> StepOutput:
|
|
341
359
|
"""Execute all steps in sequence asynchronously and return the final result"""
|
|
342
360
|
log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
|
|
@@ -366,6 +384,9 @@ class Steps:
|
|
|
366
384
|
workflow_run_response=workflow_run_response,
|
|
367
385
|
store_executor_outputs=store_executor_outputs,
|
|
368
386
|
session_state=session_state,
|
|
387
|
+
workflow_session=workflow_session,
|
|
388
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
389
|
+
num_history_runs=num_history_runs,
|
|
369
390
|
)
|
|
370
391
|
|
|
371
392
|
# Handle both single StepOutput and List[StepOutput] (from Loop/Condition/Router steps)
|
|
@@ -418,9 +439,13 @@ class Steps:
|
|
|
418
439
|
session_id: Optional[str] = None,
|
|
419
440
|
user_id: Optional[str] = None,
|
|
420
441
|
stream_intermediate_steps: bool = False,
|
|
442
|
+
stream_executor_events: bool = True,
|
|
421
443
|
step_index: Optional[Union[int, tuple]] = None,
|
|
422
444
|
store_executor_outputs: bool = True,
|
|
423
445
|
parent_step_id: Optional[str] = None,
|
|
446
|
+
workflow_session: Optional[WorkflowSession] = None,
|
|
447
|
+
add_workflow_history_to_steps: Optional[bool] = False,
|
|
448
|
+
num_history_runs: int = 3,
|
|
424
449
|
) -> AsyncIterator[Union[WorkflowRunOutputEvent, TeamRunOutputEvent, RunOutputEvent, StepOutput]]:
|
|
425
450
|
"""Execute all steps in sequence with async streaming support"""
|
|
426
451
|
log_debug(f"Steps Start: {self.name} ({len(self.steps)} steps)", center=True, symbol="-")
|
|
@@ -473,10 +498,14 @@ class Steps:
|
|
|
473
498
|
user_id=user_id,
|
|
474
499
|
session_state=session_state,
|
|
475
500
|
stream_intermediate_steps=stream_intermediate_steps,
|
|
501
|
+
stream_executor_events=stream_executor_events,
|
|
476
502
|
workflow_run_response=workflow_run_response,
|
|
477
503
|
step_index=child_step_index,
|
|
478
504
|
store_executor_outputs=store_executor_outputs,
|
|
479
505
|
parent_step_id=steps_id,
|
|
506
|
+
workflow_session=workflow_session,
|
|
507
|
+
add_workflow_history_to_steps=add_workflow_history_to_steps,
|
|
508
|
+
num_history_runs=num_history_runs,
|
|
480
509
|
):
|
|
481
510
|
if isinstance(event, StepOutput):
|
|
482
511
|
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
|