agno 2.4.6__py3-none-any.whl → 2.4.8__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 +5 -1
- agno/db/base.py +2 -0
- agno/db/postgres/postgres.py +5 -5
- agno/db/singlestore/singlestore.py +4 -5
- agno/db/sqlite/sqlite.py +4 -4
- agno/knowledge/embedder/aws_bedrock.py +325 -106
- agno/knowledge/knowledge.py +83 -1853
- agno/knowledge/loaders/__init__.py +29 -0
- agno/knowledge/loaders/azure_blob.py +423 -0
- agno/knowledge/loaders/base.py +187 -0
- agno/knowledge/loaders/gcs.py +267 -0
- agno/knowledge/loaders/github.py +415 -0
- agno/knowledge/loaders/s3.py +281 -0
- agno/knowledge/loaders/sharepoint.py +439 -0
- agno/knowledge/reader/website_reader.py +2 -2
- agno/knowledge/remote_knowledge.py +151 -0
- agno/knowledge/reranker/aws_bedrock.py +299 -0
- agno/learn/machine.py +5 -6
- agno/learn/stores/session_context.py +10 -2
- agno/models/azure/openai_chat.py +6 -11
- agno/models/neosantara/__init__.py +5 -0
- agno/models/neosantara/neosantara.py +42 -0
- agno/models/utils.py +5 -0
- agno/os/app.py +4 -1
- agno/os/interfaces/agui/router.py +1 -1
- agno/os/routers/components/components.py +2 -0
- agno/os/routers/knowledge/knowledge.py +0 -1
- agno/os/routers/registry/registry.py +340 -192
- agno/os/routers/workflows/router.py +7 -1
- agno/os/schema.py +104 -0
- agno/registry/registry.py +4 -0
- agno/run/workflow.py +3 -0
- agno/session/workflow.py +1 -1
- agno/skills/utils.py +100 -2
- agno/team/team.py +6 -3
- agno/tools/mcp/mcp.py +26 -1
- agno/vectordb/lancedb/lance_db.py +22 -7
- agno/workflow/__init__.py +4 -0
- agno/workflow/cel.py +299 -0
- agno/workflow/condition.py +280 -58
- agno/workflow/loop.py +177 -46
- agno/workflow/parallel.py +75 -4
- agno/workflow/router.py +260 -44
- agno/workflow/step.py +14 -7
- agno/workflow/steps.py +43 -0
- agno/workflow/workflow.py +104 -46
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/METADATA +25 -37
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/RECORD +51 -39
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/WHEEL +0 -0
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/licenses/LICENSE +0 -0
- {agno-2.4.6.dist-info → agno-2.4.8.dist-info}/top_level.txt +0 -0
agno/workflow/loop.py
CHANGED
|
@@ -3,6 +3,7 @@ from dataclasses import dataclass
|
|
|
3
3
|
from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
|
|
4
4
|
from uuid import uuid4
|
|
5
5
|
|
|
6
|
+
from agno.registry import Registry
|
|
6
7
|
from agno.run.agent import RunOutputEvent
|
|
7
8
|
from agno.run.base import RunContext
|
|
8
9
|
from agno.run.team import TeamRunOutputEvent
|
|
@@ -16,6 +17,7 @@ from agno.run.workflow import (
|
|
|
16
17
|
)
|
|
17
18
|
from agno.session.workflow import WorkflowSession
|
|
18
19
|
from agno.utils.log import log_debug, logger
|
|
20
|
+
from agno.workflow.cel import CEL_AVAILABLE, evaluate_cel_loop_end_condition, is_cel_expression
|
|
19
21
|
from agno.workflow.step import Step
|
|
20
22
|
from agno.workflow.types import StepInput, StepOutput, StepType
|
|
21
23
|
|
|
@@ -36,7 +38,27 @@ WorkflowSteps = List[
|
|
|
36
38
|
|
|
37
39
|
@dataclass
|
|
38
40
|
class Loop:
|
|
39
|
-
"""A loop of steps that execute in order
|
|
41
|
+
"""A loop of steps that execute in order.
|
|
42
|
+
|
|
43
|
+
The end_condition can be:
|
|
44
|
+
- A callable function that takes List[StepOutput] and returns bool
|
|
45
|
+
- A CEL (Common Expression Language) expression string
|
|
46
|
+
- None (loop runs for max_iterations)
|
|
47
|
+
|
|
48
|
+
CEL expressions for end_condition have access to:
|
|
49
|
+
- current_iteration: Current iteration number (1-indexed, after completion)
|
|
50
|
+
- max_iterations: Maximum iterations configured for the loop
|
|
51
|
+
- all_success: Boolean - True if all steps in this iteration succeeded
|
|
52
|
+
- last_step_content: Content string from the last step in the iteration
|
|
53
|
+
- step_outputs: Map of step name to content string from the current iteration
|
|
54
|
+
|
|
55
|
+
Example CEL expressions:
|
|
56
|
+
- 'current_iteration >= 2'
|
|
57
|
+
- 'current_iteration >= max_iterations'
|
|
58
|
+
- 'all_success'
|
|
59
|
+
- 'last_step_content.contains("DONE")'
|
|
60
|
+
- 'all_success && current_iteration >= 2'
|
|
61
|
+
"""
|
|
40
62
|
|
|
41
63
|
steps: WorkflowSteps
|
|
42
64
|
|
|
@@ -44,7 +66,7 @@ class Loop:
|
|
|
44
66
|
description: Optional[str] = None
|
|
45
67
|
|
|
46
68
|
max_iterations: int = 3 # Default to 3
|
|
47
|
-
end_condition: Optional[Callable[[List[StepOutput]], bool]] = None
|
|
69
|
+
end_condition: Optional[Union[Callable[[List[StepOutput]], bool], str]] = None
|
|
48
70
|
|
|
49
71
|
def __init__(
|
|
50
72
|
self,
|
|
@@ -52,7 +74,7 @@ class Loop:
|
|
|
52
74
|
name: Optional[str] = None,
|
|
53
75
|
description: Optional[str] = None,
|
|
54
76
|
max_iterations: int = 3,
|
|
55
|
-
end_condition: Optional[Callable[[List[StepOutput]], bool]] = None,
|
|
77
|
+
end_condition: Optional[Union[Callable[[List[StepOutput]], bool], str]] = None,
|
|
56
78
|
):
|
|
57
79
|
self.steps = steps
|
|
58
80
|
self.name = name
|
|
@@ -60,6 +82,142 @@ class Loop:
|
|
|
60
82
|
self.max_iterations = max_iterations
|
|
61
83
|
self.end_condition = end_condition
|
|
62
84
|
|
|
85
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
86
|
+
result: Dict[str, Any] = {
|
|
87
|
+
"type": "Loop",
|
|
88
|
+
"name": self.name,
|
|
89
|
+
"description": self.description,
|
|
90
|
+
"steps": [step.to_dict() for step in self.steps if hasattr(step, "to_dict")],
|
|
91
|
+
"max_iterations": self.max_iterations,
|
|
92
|
+
}
|
|
93
|
+
# Serialize end_condition
|
|
94
|
+
if self.end_condition is None:
|
|
95
|
+
result["end_condition"] = None
|
|
96
|
+
result["end_condition_type"] = None
|
|
97
|
+
elif isinstance(self.end_condition, str):
|
|
98
|
+
result["end_condition"] = self.end_condition
|
|
99
|
+
result["end_condition_type"] = "cel"
|
|
100
|
+
elif callable(self.end_condition):
|
|
101
|
+
result["end_condition"] = self.end_condition.__name__
|
|
102
|
+
result["end_condition_type"] = "function"
|
|
103
|
+
else:
|
|
104
|
+
raise ValueError(f"Invalid end_condition type: {type(self.end_condition).__name__}")
|
|
105
|
+
|
|
106
|
+
return result
|
|
107
|
+
|
|
108
|
+
@classmethod
|
|
109
|
+
def from_dict(
|
|
110
|
+
cls,
|
|
111
|
+
data: Dict[str, Any],
|
|
112
|
+
registry: Optional["Registry"] = None,
|
|
113
|
+
db: Optional[Any] = None,
|
|
114
|
+
links: Optional[List[Dict[str, Any]]] = None,
|
|
115
|
+
) -> "Loop":
|
|
116
|
+
from agno.workflow.condition import Condition
|
|
117
|
+
from agno.workflow.parallel import Parallel
|
|
118
|
+
from agno.workflow.router import Router
|
|
119
|
+
from agno.workflow.steps import Steps
|
|
120
|
+
|
|
121
|
+
def deserialize_step(step_data: Dict[str, Any]) -> Any:
|
|
122
|
+
step_type = step_data.get("type", "Step")
|
|
123
|
+
if step_type == "Loop":
|
|
124
|
+
return cls.from_dict(step_data, registry=registry, db=db, links=links)
|
|
125
|
+
elif step_type == "Parallel":
|
|
126
|
+
return Parallel.from_dict(step_data, registry=registry, db=db, links=links)
|
|
127
|
+
elif step_type == "Steps":
|
|
128
|
+
return Steps.from_dict(step_data, registry=registry, db=db, links=links)
|
|
129
|
+
elif step_type == "Condition":
|
|
130
|
+
return Condition.from_dict(step_data, registry=registry, db=db, links=links)
|
|
131
|
+
elif step_type == "Router":
|
|
132
|
+
return Router.from_dict(step_data, registry=registry, db=db, links=links)
|
|
133
|
+
else:
|
|
134
|
+
return Step.from_dict(step_data, registry=registry, db=db, links=links)
|
|
135
|
+
|
|
136
|
+
# Deserialize end_condition
|
|
137
|
+
end_condition_data = data.get("end_condition")
|
|
138
|
+
end_condition_type = data.get("end_condition_type")
|
|
139
|
+
end_condition: Optional[Union[Callable[[List[StepOutput]], bool], str]] = None
|
|
140
|
+
|
|
141
|
+
if end_condition_data is None:
|
|
142
|
+
end_condition = None
|
|
143
|
+
elif isinstance(end_condition_data, str):
|
|
144
|
+
if end_condition_type == "cel" or (end_condition_type is None and is_cel_expression(end_condition_data)):
|
|
145
|
+
end_condition = end_condition_data
|
|
146
|
+
else:
|
|
147
|
+
if registry:
|
|
148
|
+
end_condition = registry.get_function(end_condition_data)
|
|
149
|
+
if end_condition is None:
|
|
150
|
+
raise ValueError(f"End condition function '{end_condition_data}' not found in registry")
|
|
151
|
+
else:
|
|
152
|
+
raise ValueError(f"Registry required to deserialize end_condition function '{end_condition_data}'")
|
|
153
|
+
|
|
154
|
+
return cls(
|
|
155
|
+
name=data.get("name"),
|
|
156
|
+
description=data.get("description"),
|
|
157
|
+
steps=[deserialize_step(step) for step in data.get("steps", [])],
|
|
158
|
+
max_iterations=data.get("max_iterations", 3),
|
|
159
|
+
end_condition=end_condition,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def _evaluate_end_condition(self, iteration_results: List[StepOutput], current_iteration: int = 0) -> bool:
|
|
163
|
+
"""Evaluate the end condition and return whether the loop should stop."""
|
|
164
|
+
if self.end_condition is None:
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
if isinstance(self.end_condition, str):
|
|
168
|
+
if not CEL_AVAILABLE:
|
|
169
|
+
logger.error(
|
|
170
|
+
"CEL expression used but cel-python is not installed. Install with: pip install cel-python"
|
|
171
|
+
)
|
|
172
|
+
return False
|
|
173
|
+
try:
|
|
174
|
+
return evaluate_cel_loop_end_condition(
|
|
175
|
+
self.end_condition, iteration_results, current_iteration, self.max_iterations
|
|
176
|
+
)
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.warning(f"CEL end condition evaluation failed: {e}")
|
|
179
|
+
return False
|
|
180
|
+
|
|
181
|
+
if callable(self.end_condition):
|
|
182
|
+
try:
|
|
183
|
+
return self.end_condition(iteration_results)
|
|
184
|
+
except Exception as e:
|
|
185
|
+
logger.warning(f"End condition evaluation failed: {e}")
|
|
186
|
+
return False
|
|
187
|
+
|
|
188
|
+
return False
|
|
189
|
+
|
|
190
|
+
async def _aevaluate_end_condition(self, iteration_results: List[StepOutput], current_iteration: int = 0) -> bool:
|
|
191
|
+
"""Async evaluate the end condition."""
|
|
192
|
+
if self.end_condition is None:
|
|
193
|
+
return False
|
|
194
|
+
|
|
195
|
+
if isinstance(self.end_condition, str):
|
|
196
|
+
if not CEL_AVAILABLE:
|
|
197
|
+
logger.error(
|
|
198
|
+
"CEL expression used but cel-python is not installed. Install with: pip install cel-python"
|
|
199
|
+
)
|
|
200
|
+
return False
|
|
201
|
+
try:
|
|
202
|
+
return evaluate_cel_loop_end_condition(
|
|
203
|
+
self.end_condition, iteration_results, current_iteration, self.max_iterations
|
|
204
|
+
)
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.warning(f"CEL end condition evaluation failed: {e}")
|
|
207
|
+
return False
|
|
208
|
+
|
|
209
|
+
if callable(self.end_condition):
|
|
210
|
+
try:
|
|
211
|
+
if inspect.iscoroutinefunction(self.end_condition):
|
|
212
|
+
return await self.end_condition(iteration_results)
|
|
213
|
+
else:
|
|
214
|
+
return self.end_condition(iteration_results)
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.warning(f"End condition evaluation failed: {e}")
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
return False
|
|
220
|
+
|
|
63
221
|
def _prepare_steps(self):
|
|
64
222
|
"""Prepare the steps for execution - mirrors workflow logic"""
|
|
65
223
|
from agno.agent.agent import Agent
|
|
@@ -204,13 +362,8 @@ class Loop:
|
|
|
204
362
|
iteration += 1
|
|
205
363
|
|
|
206
364
|
# Check end condition
|
|
207
|
-
if self.
|
|
208
|
-
|
|
209
|
-
should_break = self.end_condition(iteration_results)
|
|
210
|
-
if should_break:
|
|
211
|
-
break
|
|
212
|
-
except Exception as e:
|
|
213
|
-
logger.warning(f"End condition evaluation failed: {e}")
|
|
365
|
+
if self._evaluate_end_condition(iteration_results, iteration):
|
|
366
|
+
break
|
|
214
367
|
|
|
215
368
|
# Break out of iteration loop if early termination was requested
|
|
216
369
|
if early_termination:
|
|
@@ -365,16 +518,13 @@ class Loop:
|
|
|
365
518
|
)
|
|
366
519
|
|
|
367
520
|
all_results.append(iteration_results)
|
|
521
|
+
iteration += 1
|
|
368
522
|
|
|
369
523
|
# Check end condition
|
|
370
524
|
should_continue = True
|
|
371
|
-
if self.
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
should_continue = not should_break
|
|
375
|
-
log_debug(f"End condition returned: {should_break}, should_continue: {should_continue}")
|
|
376
|
-
except Exception as e:
|
|
377
|
-
logger.warning(f"End condition evaluation failed: {e}")
|
|
525
|
+
if self._evaluate_end_condition(iteration_results, iteration):
|
|
526
|
+
should_continue = False
|
|
527
|
+
log_debug("End condition met, loop will stop")
|
|
378
528
|
|
|
379
529
|
if early_termination:
|
|
380
530
|
should_continue = False
|
|
@@ -389,7 +539,7 @@ class Loop:
|
|
|
389
539
|
session_id=workflow_run_response.session_id or "",
|
|
390
540
|
step_name=self.name,
|
|
391
541
|
step_index=step_index,
|
|
392
|
-
iteration=iteration
|
|
542
|
+
iteration=iteration,
|
|
393
543
|
max_iterations=self.max_iterations,
|
|
394
544
|
iteration_results=iteration_results,
|
|
395
545
|
should_continue=should_continue,
|
|
@@ -397,10 +547,8 @@ class Loop:
|
|
|
397
547
|
parent_step_id=parent_step_id,
|
|
398
548
|
)
|
|
399
549
|
|
|
400
|
-
iteration += 1
|
|
401
|
-
|
|
402
550
|
if not should_continue:
|
|
403
|
-
log_debug(f"Loop ending early
|
|
551
|
+
log_debug(f"Loop ending early at iteration {iteration}")
|
|
404
552
|
break
|
|
405
553
|
|
|
406
554
|
log_debug(f"Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
|
|
@@ -515,23 +663,14 @@ class Loop:
|
|
|
515
663
|
iteration += 1
|
|
516
664
|
|
|
517
665
|
# Check end condition
|
|
518
|
-
if self.
|
|
519
|
-
|
|
520
|
-
if inspect.iscoroutinefunction(self.end_condition):
|
|
521
|
-
should_break = await self.end_condition(iteration_results)
|
|
522
|
-
else:
|
|
523
|
-
should_break = self.end_condition(iteration_results)
|
|
524
|
-
if should_break:
|
|
525
|
-
break
|
|
526
|
-
except Exception as e:
|
|
527
|
-
logger.warning(f"End condition evaluation failed: {e}")
|
|
666
|
+
if await self._aevaluate_end_condition(iteration_results, iteration):
|
|
667
|
+
break
|
|
528
668
|
|
|
529
669
|
# Break out of iteration loop if early termination was requested
|
|
530
670
|
if early_termination:
|
|
531
671
|
log_debug(f"Loop ending early due to step termination request at iteration {iteration}")
|
|
532
672
|
break
|
|
533
673
|
|
|
534
|
-
# Use workflow logger for async loop completion
|
|
535
674
|
log_debug(f"Async Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
|
|
536
675
|
|
|
537
676
|
# Return flattened results from all iterations
|
|
@@ -680,19 +819,13 @@ class Loop:
|
|
|
680
819
|
)
|
|
681
820
|
|
|
682
821
|
all_results.append(iteration_results)
|
|
822
|
+
iteration += 1
|
|
683
823
|
|
|
684
824
|
# Check end condition
|
|
685
825
|
should_continue = True
|
|
686
|
-
if self.
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
should_break = await self.end_condition(iteration_results)
|
|
690
|
-
else:
|
|
691
|
-
should_break = self.end_condition(iteration_results)
|
|
692
|
-
should_continue = not should_break
|
|
693
|
-
log_debug(f"End condition returned: {should_break}, should_continue: {should_continue}")
|
|
694
|
-
except Exception as e:
|
|
695
|
-
logger.warning(f"End condition evaluation failed: {e}")
|
|
826
|
+
if await self._aevaluate_end_condition(iteration_results, iteration):
|
|
827
|
+
should_continue = False
|
|
828
|
+
log_debug("End condition met, loop will stop")
|
|
696
829
|
|
|
697
830
|
if early_termination:
|
|
698
831
|
should_continue = False
|
|
@@ -707,7 +840,7 @@ class Loop:
|
|
|
707
840
|
session_id=workflow_run_response.session_id or "",
|
|
708
841
|
step_name=self.name,
|
|
709
842
|
step_index=step_index,
|
|
710
|
-
iteration=iteration
|
|
843
|
+
iteration=iteration,
|
|
711
844
|
max_iterations=self.max_iterations,
|
|
712
845
|
iteration_results=iteration_results,
|
|
713
846
|
should_continue=should_continue,
|
|
@@ -715,10 +848,8 @@ class Loop:
|
|
|
715
848
|
parent_step_id=parent_step_id,
|
|
716
849
|
)
|
|
717
850
|
|
|
718
|
-
iteration += 1
|
|
719
|
-
|
|
720
851
|
if not should_continue:
|
|
721
|
-
log_debug(f"Loop ending early
|
|
852
|
+
log_debug(f"Loop ending early at iteration {iteration}")
|
|
722
853
|
break
|
|
723
854
|
|
|
724
855
|
log_debug(f"Loop End: {self.name} ({iteration} iterations)", center=True, symbol="=")
|
agno/workflow/parallel.py
CHANGED
|
@@ -7,6 +7,7 @@ from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List
|
|
|
7
7
|
from uuid import uuid4
|
|
8
8
|
|
|
9
9
|
from agno.models.metrics import Metrics
|
|
10
|
+
from agno.registry import Registry
|
|
10
11
|
from agno.run.agent import RunOutputEvent
|
|
11
12
|
from agno.run.base import RunContext
|
|
12
13
|
from agno.run.team import TeamRunOutputEvent
|
|
@@ -40,7 +41,16 @@ WorkflowSteps = List[
|
|
|
40
41
|
|
|
41
42
|
@dataclass
|
|
42
43
|
class Parallel:
|
|
43
|
-
"""A list of steps that execute in parallel
|
|
44
|
+
"""A list of steps that execute in parallel.
|
|
45
|
+
|
|
46
|
+
Unlike sequential constructs (Steps, Loop), Parallel uses variadic arguments
|
|
47
|
+
to emphasize that the steps are independent and unordered.
|
|
48
|
+
|
|
49
|
+
Supports flexible calling conventions:
|
|
50
|
+
Parallel(step1, step2, step3) # Steps only (name defaults to "Parallel")
|
|
51
|
+
Parallel(step1, step2, name="my_parallel") # Name as keyword (at end)
|
|
52
|
+
Parallel("my_parallel", step1, step2) # Name as first positional arg
|
|
53
|
+
"""
|
|
44
54
|
|
|
45
55
|
steps: WorkflowSteps
|
|
46
56
|
|
|
@@ -49,14 +59,75 @@ class Parallel:
|
|
|
49
59
|
|
|
50
60
|
def __init__(
|
|
51
61
|
self,
|
|
52
|
-
*
|
|
62
|
+
*args: Union[str, WorkflowSteps],
|
|
53
63
|
name: Optional[str] = None,
|
|
54
64
|
description: Optional[str] = None,
|
|
55
65
|
):
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
resolved_name = name
|
|
67
|
+
resolved_steps: List[Any] = []
|
|
68
|
+
|
|
69
|
+
if args:
|
|
70
|
+
first_arg = args[0]
|
|
71
|
+
# Check if first argument is a plain string (likely a name, not a step)
|
|
72
|
+
if isinstance(first_arg, str):
|
|
73
|
+
# First arg is the name, rest are steps
|
|
74
|
+
resolved_name = first_arg
|
|
75
|
+
resolved_steps = list(args[1:])
|
|
76
|
+
else:
|
|
77
|
+
# All args are steps
|
|
78
|
+
resolved_steps = list(args)
|
|
79
|
+
|
|
80
|
+
# If name was provided as keyword, it takes precedence
|
|
81
|
+
if name is not None:
|
|
82
|
+
resolved_name = name
|
|
83
|
+
|
|
84
|
+
self.steps = resolved_steps
|
|
85
|
+
self.name = resolved_name
|
|
58
86
|
self.description = description
|
|
59
87
|
|
|
88
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
89
|
+
return {
|
|
90
|
+
"type": "Parallel",
|
|
91
|
+
"name": self.name,
|
|
92
|
+
"description": self.description,
|
|
93
|
+
"steps": [step.to_dict() for step in self.steps if hasattr(step, "to_dict")],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@classmethod
|
|
97
|
+
def from_dict(
|
|
98
|
+
cls,
|
|
99
|
+
data: Dict[str, Any],
|
|
100
|
+
registry: Optional["Registry"] = None,
|
|
101
|
+
db: Optional[Any] = None,
|
|
102
|
+
links: Optional[List[Dict[str, Any]]] = None,
|
|
103
|
+
) -> "Parallel":
|
|
104
|
+
from agno.workflow.condition import Condition
|
|
105
|
+
from agno.workflow.loop import Loop
|
|
106
|
+
from agno.workflow.router import Router
|
|
107
|
+
from agno.workflow.steps import Steps
|
|
108
|
+
|
|
109
|
+
def deserialize_step(step_data: Dict[str, Any]) -> Any:
|
|
110
|
+
step_type = step_data.get("type", "Step")
|
|
111
|
+
if step_type == "Loop":
|
|
112
|
+
return Loop.from_dict(step_data, registry=registry, db=db, links=links)
|
|
113
|
+
elif step_type == "Parallel":
|
|
114
|
+
return cls.from_dict(step_data, registry=registry, db=db, links=links)
|
|
115
|
+
elif step_type == "Steps":
|
|
116
|
+
return Steps.from_dict(step_data, registry=registry, db=db, links=links)
|
|
117
|
+
elif step_type == "Condition":
|
|
118
|
+
return Condition.from_dict(step_data, registry=registry, db=db, links=links)
|
|
119
|
+
elif step_type == "Router":
|
|
120
|
+
return Router.from_dict(step_data, registry=registry, db=db, links=links)
|
|
121
|
+
else:
|
|
122
|
+
return Step.from_dict(step_data, registry=registry, db=db, links=links)
|
|
123
|
+
|
|
124
|
+
deserialized_steps = [deserialize_step(step) for step in data.get("steps", [])]
|
|
125
|
+
return cls(
|
|
126
|
+
*deserialized_steps,
|
|
127
|
+
name=data.get("name"),
|
|
128
|
+
description=data.get("description"),
|
|
129
|
+
)
|
|
130
|
+
|
|
60
131
|
def _prepare_steps(self):
|
|
61
132
|
"""Prepare the steps for execution - mirrors workflow logic"""
|
|
62
133
|
from agno.agent.agent import Agent
|