agno 2.3.26__py3-none-any.whl → 2.4.0__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.
Files changed (128) hide show
  1. agno/agent/__init__.py +4 -0
  2. agno/agent/agent.py +1368 -541
  3. agno/agent/remote.py +13 -0
  4. agno/db/base.py +339 -0
  5. agno/db/postgres/async_postgres.py +116 -12
  6. agno/db/postgres/postgres.py +1229 -25
  7. agno/db/postgres/schemas.py +48 -1
  8. agno/db/sqlite/async_sqlite.py +119 -4
  9. agno/db/sqlite/schemas.py +51 -0
  10. agno/db/sqlite/sqlite.py +1173 -13
  11. agno/db/utils.py +37 -1
  12. agno/knowledge/__init__.py +4 -0
  13. agno/knowledge/chunking/code.py +1 -1
  14. agno/knowledge/chunking/semantic.py +1 -1
  15. agno/knowledge/chunking/strategy.py +4 -0
  16. agno/knowledge/filesystem.py +412 -0
  17. agno/knowledge/knowledge.py +2767 -2254
  18. agno/knowledge/protocol.py +134 -0
  19. agno/knowledge/reader/arxiv_reader.py +2 -2
  20. agno/knowledge/reader/base.py +9 -7
  21. agno/knowledge/reader/csv_reader.py +5 -5
  22. agno/knowledge/reader/docx_reader.py +2 -2
  23. agno/knowledge/reader/field_labeled_csv_reader.py +2 -2
  24. agno/knowledge/reader/firecrawl_reader.py +2 -2
  25. agno/knowledge/reader/json_reader.py +2 -2
  26. agno/knowledge/reader/markdown_reader.py +2 -2
  27. agno/knowledge/reader/pdf_reader.py +5 -4
  28. agno/knowledge/reader/pptx_reader.py +2 -2
  29. agno/knowledge/reader/reader_factory.py +110 -0
  30. agno/knowledge/reader/s3_reader.py +2 -2
  31. agno/knowledge/reader/tavily_reader.py +2 -2
  32. agno/knowledge/reader/text_reader.py +2 -2
  33. agno/knowledge/reader/web_search_reader.py +2 -2
  34. agno/knowledge/reader/website_reader.py +5 -3
  35. agno/knowledge/reader/wikipedia_reader.py +2 -2
  36. agno/knowledge/reader/youtube_reader.py +2 -2
  37. agno/knowledge/utils.py +37 -29
  38. agno/learn/__init__.py +6 -0
  39. agno/learn/machine.py +35 -0
  40. agno/learn/schemas.py +82 -11
  41. agno/learn/stores/__init__.py +3 -0
  42. agno/learn/stores/decision_log.py +1156 -0
  43. agno/learn/stores/learned_knowledge.py +6 -6
  44. agno/models/anthropic/claude.py +24 -0
  45. agno/models/aws/bedrock.py +20 -0
  46. agno/models/base.py +48 -4
  47. agno/models/cohere/chat.py +25 -0
  48. agno/models/google/gemini.py +50 -5
  49. agno/models/litellm/chat.py +38 -0
  50. agno/models/openai/chat.py +7 -0
  51. agno/models/openrouter/openrouter.py +46 -0
  52. agno/models/response.py +16 -0
  53. agno/os/app.py +83 -44
  54. agno/os/middleware/__init__.py +2 -0
  55. agno/os/middleware/trailing_slash.py +27 -0
  56. agno/os/router.py +1 -0
  57. agno/os/routers/agents/router.py +29 -16
  58. agno/os/routers/agents/schema.py +6 -4
  59. agno/os/routers/components/__init__.py +3 -0
  60. agno/os/routers/components/components.py +466 -0
  61. agno/os/routers/evals/schemas.py +4 -3
  62. agno/os/routers/health.py +3 -3
  63. agno/os/routers/knowledge/knowledge.py +3 -3
  64. agno/os/routers/memory/schemas.py +4 -2
  65. agno/os/routers/metrics/metrics.py +9 -11
  66. agno/os/routers/metrics/schemas.py +10 -6
  67. agno/os/routers/registry/__init__.py +3 -0
  68. agno/os/routers/registry/registry.py +337 -0
  69. agno/os/routers/teams/router.py +20 -8
  70. agno/os/routers/teams/schema.py +6 -4
  71. agno/os/routers/traces/traces.py +5 -5
  72. agno/os/routers/workflows/router.py +38 -11
  73. agno/os/routers/workflows/schema.py +1 -1
  74. agno/os/schema.py +92 -26
  75. agno/os/utils.py +84 -19
  76. agno/reasoning/anthropic.py +2 -2
  77. agno/reasoning/azure_ai_foundry.py +2 -2
  78. agno/reasoning/deepseek.py +2 -2
  79. agno/reasoning/default.py +6 -7
  80. agno/reasoning/gemini.py +2 -2
  81. agno/reasoning/helpers.py +6 -7
  82. agno/reasoning/manager.py +4 -10
  83. agno/reasoning/ollama.py +2 -2
  84. agno/reasoning/openai.py +2 -2
  85. agno/reasoning/vertexai.py +2 -2
  86. agno/registry/__init__.py +3 -0
  87. agno/registry/registry.py +68 -0
  88. agno/run/agent.py +57 -0
  89. agno/run/base.py +7 -0
  90. agno/run/team.py +57 -0
  91. agno/skills/agent_skills.py +10 -3
  92. agno/team/__init__.py +3 -1
  93. agno/team/team.py +1145 -326
  94. agno/tools/duckduckgo.py +25 -71
  95. agno/tools/exa.py +0 -21
  96. agno/tools/function.py +35 -83
  97. agno/tools/knowledge.py +9 -4
  98. agno/tools/mem0.py +11 -10
  99. agno/tools/memory.py +47 -46
  100. agno/tools/parallel.py +0 -7
  101. agno/tools/reasoning.py +30 -23
  102. agno/tools/tavily.py +4 -1
  103. agno/tools/websearch.py +93 -0
  104. agno/tools/website.py +1 -1
  105. agno/tools/wikipedia.py +1 -1
  106. agno/tools/workflow.py +48 -47
  107. agno/utils/agent.py +42 -5
  108. agno/utils/events.py +160 -2
  109. agno/utils/print_response/agent.py +0 -31
  110. agno/utils/print_response/team.py +0 -2
  111. agno/utils/print_response/workflow.py +0 -2
  112. agno/utils/team.py +61 -11
  113. agno/vectordb/lancedb/lance_db.py +4 -1
  114. agno/vectordb/mongodb/mongodb.py +1 -1
  115. agno/vectordb/qdrant/qdrant.py +4 -4
  116. agno/workflow/__init__.py +3 -1
  117. agno/workflow/condition.py +0 -21
  118. agno/workflow/loop.py +0 -21
  119. agno/workflow/parallel.py +0 -21
  120. agno/workflow/router.py +0 -21
  121. agno/workflow/step.py +117 -24
  122. agno/workflow/steps.py +0 -21
  123. agno/workflow/workflow.py +427 -63
  124. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/METADATA +46 -76
  125. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/RECORD +128 -117
  126. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/WHEEL +0 -0
  127. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/licenses/LICENSE +0 -0
  128. {agno-2.3.26.dist-info → agno-2.4.0.dist-info}/top_level.txt +0 -0
agno/utils/team.py CHANGED
@@ -73,14 +73,40 @@ def add_interaction_to_team_run_context(
73
73
  log_debug(f"Updated team run context with member name: {member_name}")
74
74
 
75
75
 
76
- def get_team_member_interactions_str(team_run_context: Dict[str, Any]) -> str:
76
+ def get_team_member_interactions_str(
77
+ team_run_context: Dict[str, Any],
78
+ max_interactions: Optional[int] = None,
79
+ ) -> str:
80
+ """
81
+ Build a string representation of member interactions from the team run context.
82
+
83
+ Args:
84
+ team_run_context: The context containing member responses
85
+ max_interactions: Maximum number of recent interactions to include.
86
+ None means include all interactions.
87
+ If set, only the most recent N interactions are included.
88
+
89
+ Returns:
90
+ A formatted string with member interactions
91
+ """
77
92
  if not team_run_context:
78
93
  return ""
79
94
  team_member_interactions_str = ""
80
95
  if "member_responses" in team_run_context:
81
- team_member_interactions_str += "<member_interaction_context>\nSee below interactions wit other team members.\n"
96
+ member_responses = team_run_context["member_responses"]
97
+
98
+ # If max_interactions is set, only include the most recent N interactions
99
+ if max_interactions is not None and len(member_responses) > max_interactions:
100
+ member_responses = member_responses[-max_interactions:]
82
101
 
83
- for interaction in team_run_context["member_responses"]:
102
+ if not member_responses:
103
+ return ""
104
+
105
+ team_member_interactions_str += (
106
+ "<member_interaction_context>\nSee below interactions with other team members.\n"
107
+ )
108
+
109
+ for interaction in member_responses:
84
110
  response_dict = interaction["run_response"].to_dict()
85
111
  response_content = (
86
112
  response_dict.get("content")
@@ -95,45 +121,69 @@ def get_team_member_interactions_str(team_run_context: Dict[str, Any]) -> str:
95
121
  return team_member_interactions_str
96
122
 
97
123
 
98
- def get_team_run_context_images(team_run_context: Dict[str, Any]) -> List[Image]:
124
+ def get_team_run_context_images(
125
+ team_run_context: Dict[str, Any],
126
+ max_interactions: Optional[int] = None,
127
+ ) -> List[Image]:
99
128
  if not team_run_context:
100
129
  return []
101
130
  images = []
102
131
  if "member_responses" in team_run_context:
103
- for interaction in team_run_context["member_responses"]:
132
+ member_responses = team_run_context["member_responses"]
133
+ if max_interactions is not None and len(member_responses) > max_interactions:
134
+ member_responses = member_responses[-max_interactions:]
135
+ for interaction in member_responses:
104
136
  if interaction["run_response"].images:
105
137
  images.extend(interaction["run_response"].images)
106
138
  return images
107
139
 
108
140
 
109
- def get_team_run_context_videos(team_run_context: Dict[str, Any]) -> List[Video]:
141
+ def get_team_run_context_videos(
142
+ team_run_context: Dict[str, Any],
143
+ max_interactions: Optional[int] = None,
144
+ ) -> List[Video]:
110
145
  if not team_run_context:
111
146
  return []
112
147
  videos = []
113
148
  if "member_responses" in team_run_context:
114
- for interaction in team_run_context["member_responses"]:
149
+ member_responses = team_run_context["member_responses"]
150
+ if max_interactions is not None and len(member_responses) > max_interactions:
151
+ member_responses = member_responses[-max_interactions:]
152
+ for interaction in member_responses:
115
153
  if interaction["run_response"].videos:
116
154
  videos.extend(interaction["run_response"].videos)
117
155
  return videos
118
156
 
119
157
 
120
- def get_team_run_context_audio(team_run_context: Dict[str, Any]) -> List[Audio]:
158
+ def get_team_run_context_audio(
159
+ team_run_context: Dict[str, Any],
160
+ max_interactions: Optional[int] = None,
161
+ ) -> List[Audio]:
121
162
  if not team_run_context:
122
163
  return []
123
164
  audio = []
124
165
  if "member_responses" in team_run_context:
125
- for interaction in team_run_context["member_responses"]:
166
+ member_responses = team_run_context["member_responses"]
167
+ if max_interactions is not None and len(member_responses) > max_interactions:
168
+ member_responses = member_responses[-max_interactions:]
169
+ for interaction in member_responses:
126
170
  if interaction["run_response"].audio:
127
171
  audio.extend(interaction["run_response"].audio)
128
172
  return audio
129
173
 
130
174
 
131
- def get_team_run_context_files(team_run_context: Dict[str, Any]) -> List[File]:
175
+ def get_team_run_context_files(
176
+ team_run_context: Dict[str, Any],
177
+ max_interactions: Optional[int] = None,
178
+ ) -> List[File]:
132
179
  if not team_run_context:
133
180
  return []
134
181
  files = []
135
182
  if "member_responses" in team_run_context:
136
- for interaction in team_run_context["member_responses"]:
183
+ member_responses = team_run_context["member_responses"]
184
+ if max_interactions is not None and len(member_responses) > max_interactions:
185
+ member_responses = member_responses[-max_interactions:]
186
+ for interaction in member_responses:
137
187
  if interaction["run_response"].files:
138
188
  files.extend(interaction["run_response"].files)
139
189
  return files
@@ -282,7 +282,10 @@ class LanceDb(VectorDb):
282
282
  meta_data.update(filters)
283
283
  document.meta_data = meta_data
284
284
 
285
- document.embed(embedder=self.embedder)
285
+ # Only embed if the document doesn't already have an embedding
286
+ # This prevents duplicate embedding when called from async_insert or async_upsert
287
+ if document.embedding is None:
288
+ document.embed(embedder=self.embedder)
286
289
  cleaned_content = document.content.replace("\x00", "\ufffd")
287
290
  # Include content_hash in ID to ensure uniqueness across different content hashes
288
291
  base_id = document.id or md5(cleaned_content.encode()).hexdigest()
@@ -1150,7 +1150,7 @@ class MongoDb(VectorDb):
1150
1150
  if isinstance(filters, List):
1151
1151
  log_warning("Filters Expressions are not supported in MongoDB. No filters will be applied.")
1152
1152
  filters = None
1153
- query_embedding = self.embedder.get_embedding(query)
1153
+ query_embedding = await self.embedder.async_get_embedding(query)
1154
1154
  if query_embedding is None:
1155
1155
  logger.error(f"Failed to generate embedding for query: {query}")
1156
1156
  return []
@@ -423,12 +423,12 @@ class Qdrant(VectorDb):
423
423
  # Fall back to individual embedding
424
424
  for doc in documents:
425
425
  if self.search_type in [SearchType.vector, SearchType.hybrid]:
426
- doc.embed(embedder=self.embedder)
426
+ await doc.async_embed(embedder=self.embedder)
427
427
  else:
428
428
  # Use individual embedding
429
429
  for doc in documents:
430
430
  if self.search_type in [SearchType.vector, SearchType.hybrid]:
431
- doc.embed(embedder=self.embedder)
431
+ await doc.async_embed(embedder=self.embedder)
432
432
 
433
433
  async def process_document(document):
434
434
  cleaned_content = document.content.replace("\x00", "\ufffd")
@@ -634,7 +634,7 @@ class Qdrant(VectorDb):
634
634
  limit: int,
635
635
  formatted_filters: Optional[models.Filter],
636
636
  ) -> List[models.ScoredPoint]:
637
- dense_embedding = self.embedder.get_embedding(query)
637
+ dense_embedding = await self.embedder.async_get_embedding(query)
638
638
 
639
639
  # TODO(v2.0.0): Remove this conditional and always use named vectors
640
640
  if self.use_named_vectors:
@@ -683,7 +683,7 @@ class Qdrant(VectorDb):
683
683
  limit: int,
684
684
  formatted_filters: Optional[models.Filter],
685
685
  ) -> List[models.ScoredPoint]:
686
- dense_embedding = self.embedder.get_embedding(query)
686
+ dense_embedding = await self.embedder.async_get_embedding(query)
687
687
  sparse_embedding = next(iter(self.sparse_encoder.embed([query]))).as_object()
688
688
  call = await self.async_client.query_points(
689
689
  collection_name=self.collection,
agno/workflow/__init__.py CHANGED
@@ -7,7 +7,7 @@ from agno.workflow.router import Router
7
7
  from agno.workflow.step import Step
8
8
  from agno.workflow.steps import Steps
9
9
  from agno.workflow.types import StepInput, StepOutput, WorkflowExecutionInput
10
- from agno.workflow.workflow import Workflow
10
+ from agno.workflow.workflow import Workflow, get_workflow_by_id, get_workflows
11
11
 
12
12
  __all__ = [
13
13
  "Workflow",
@@ -22,4 +22,6 @@ __all__ = [
22
22
  "WorkflowExecutionInput",
23
23
  "StepInput",
24
24
  "StepOutput",
25
+ "get_workflow_by_id",
26
+ "get_workflows",
25
27
  ]
@@ -1,5 +1,4 @@
1
1
  import inspect
2
- import warnings
3
2
  from dataclasses import dataclass
4
3
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
5
4
  from uuid import uuid4
@@ -289,7 +288,6 @@ class Condition:
289
288
  session_id: Optional[str] = None,
290
289
  user_id: Optional[str] = None,
291
290
  stream_events: bool = False,
292
- stream_intermediate_steps: bool = False, # type: ignore
293
291
  stream_executor_events: bool = True,
294
292
  workflow_run_response: Optional[WorkflowRunOutput] = None,
295
293
  step_index: Optional[Union[int, tuple]] = None,
@@ -316,15 +314,6 @@ class Condition:
316
314
  condition_result = self._evaluate_condition(step_input, session_state=session_state)
317
315
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
318
316
 
319
- # Considering both stream_events and stream_intermediate_steps (deprecated)
320
- if stream_intermediate_steps is not None:
321
- warnings.warn(
322
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
323
- DeprecationWarning,
324
- stacklevel=2,
325
- )
326
- stream_events = stream_events or stream_intermediate_steps
327
-
328
317
  if stream_events and workflow_run_response:
329
318
  # Yield condition started event
330
319
  yield ConditionExecutionStartedEvent(
@@ -582,7 +571,6 @@ class Condition:
582
571
  session_id: Optional[str] = None,
583
572
  user_id: Optional[str] = None,
584
573
  stream_events: bool = False,
585
- stream_intermediate_steps: bool = False,
586
574
  stream_executor_events: bool = True,
587
575
  workflow_run_response: Optional[WorkflowRunOutput] = None,
588
576
  step_index: Optional[Union[int, tuple]] = None,
@@ -609,15 +597,6 @@ class Condition:
609
597
  condition_result = await self._aevaluate_condition(step_input, session_state=session_state)
610
598
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
611
599
 
612
- # Considering both stream_events and stream_intermediate_steps (deprecated)
613
- if stream_intermediate_steps is not None:
614
- warnings.warn(
615
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
616
- DeprecationWarning,
617
- stacklevel=2,
618
- )
619
- stream_events = stream_events or stream_intermediate_steps
620
-
621
600
  if stream_events and workflow_run_response:
622
601
  # Yield condition started event
623
602
  yield ConditionExecutionStartedEvent(
agno/workflow/loop.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import inspect
2
- import warnings
3
2
  from dataclasses import dataclass
4
3
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
5
4
  from uuid import uuid4
@@ -241,7 +240,6 @@ class Loop:
241
240
  session_id: Optional[str] = None,
242
241
  user_id: Optional[str] = None,
243
242
  stream_events: bool = False,
244
- stream_intermediate_steps: bool = False,
245
243
  stream_executor_events: bool = True,
246
244
  workflow_run_response: Optional[WorkflowRunOutput] = None,
247
245
  step_index: Optional[Union[int, tuple]] = None,
@@ -262,15 +260,6 @@ class Loop:
262
260
 
263
261
  loop_step_id = str(uuid4())
264
262
 
265
- # Considering both stream_events and stream_intermediate_steps (deprecated)
266
- if stream_intermediate_steps is not None:
267
- warnings.warn(
268
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
269
- DeprecationWarning,
270
- stacklevel=2,
271
- )
272
- stream_events = stream_events or stream_intermediate_steps
273
-
274
263
  if stream_events and workflow_run_response:
275
264
  # Yield loop started event
276
265
  yield LoopExecutionStartedEvent(
@@ -566,7 +555,6 @@ class Loop:
566
555
  session_id: Optional[str] = None,
567
556
  user_id: Optional[str] = None,
568
557
  stream_events: bool = False,
569
- stream_intermediate_steps: bool = False,
570
558
  stream_executor_events: bool = True,
571
559
  workflow_run_response: Optional[WorkflowRunOutput] = None,
572
560
  step_index: Optional[Union[int, tuple]] = None,
@@ -587,15 +575,6 @@ class Loop:
587
575
  # Prepare steps first
588
576
  self._prepare_steps()
589
577
 
590
- # Considering both stream_events and stream_intermediate_steps (deprecated)
591
- if stream_intermediate_steps is not None:
592
- warnings.warn(
593
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
594
- DeprecationWarning,
595
- stacklevel=2,
596
- )
597
- stream_events = stream_events or stream_intermediate_steps
598
-
599
578
  if stream_events and workflow_run_response:
600
579
  # Yield loop started event
601
580
  yield LoopExecutionStartedEvent(
agno/workflow/parallel.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import asyncio
2
- import warnings
3
2
  from concurrent.futures import ThreadPoolExecutor, as_completed
4
3
  from contextvars import copy_context
5
4
  from copy import deepcopy
@@ -329,7 +328,6 @@ class Parallel:
329
328
  session_id: Optional[str] = None,
330
329
  user_id: Optional[str] = None,
331
330
  stream_events: bool = False,
332
- stream_intermediate_steps: bool = False,
333
331
  stream_executor_events: bool = True,
334
332
  workflow_run_response: Optional[WorkflowRunOutput] = None,
335
333
  step_index: Optional[Union[int, tuple]] = None,
@@ -361,15 +359,6 @@ class Parallel:
361
359
  else:
362
360
  session_state_copies.append({})
363
361
 
364
- # Considering both stream_events and stream_intermediate_steps (deprecated)
365
- if stream_intermediate_steps is not None:
366
- warnings.warn(
367
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
368
- DeprecationWarning,
369
- stacklevel=2,
370
- )
371
- stream_events = stream_events or stream_intermediate_steps
372
-
373
362
  if stream_events and workflow_run_response:
374
363
  # Yield parallel step started event
375
364
  yield ParallelExecutionStartedEvent(
@@ -665,7 +654,6 @@ class Parallel:
665
654
  session_id: Optional[str] = None,
666
655
  user_id: Optional[str] = None,
667
656
  stream_events: bool = False,
668
- stream_intermediate_steps: bool = False,
669
657
  stream_executor_events: bool = True,
670
658
  workflow_run_response: Optional[WorkflowRunOutput] = None,
671
659
  step_index: Optional[Union[int, tuple]] = None,
@@ -697,15 +685,6 @@ class Parallel:
697
685
  else:
698
686
  session_state_copies.append({})
699
687
 
700
- # Considering both stream_events and stream_intermediate_steps (deprecated)
701
- if stream_intermediate_steps is not None:
702
- warnings.warn(
703
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
704
- DeprecationWarning,
705
- stacklevel=2,
706
- )
707
- stream_events = stream_events or stream_intermediate_steps
708
-
709
688
  if stream_events and workflow_run_response:
710
689
  # Yield parallel step started event
711
690
  yield ParallelExecutionStartedEvent(
agno/workflow/router.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import inspect
2
- import warnings
3
2
  from dataclasses import dataclass
4
3
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
5
4
  from uuid import uuid4
@@ -279,7 +278,6 @@ class Router:
279
278
  run_context: Optional[RunContext] = None,
280
279
  session_state: Optional[Dict[str, Any]] = None,
281
280
  stream_events: bool = False,
282
- stream_intermediate_steps: bool = False,
283
281
  stream_executor_events: bool = True,
284
282
  workflow_run_response: Optional[WorkflowRunOutput] = None,
285
283
  step_index: Optional[Union[int, tuple]] = None,
@@ -304,15 +302,6 @@ class Router:
304
302
  steps_to_execute = self._route_steps(step_input, session_state=session_state)
305
303
  log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
306
304
 
307
- # Considering both stream_events and stream_intermediate_steps (deprecated)
308
- if stream_intermediate_steps is not None:
309
- warnings.warn(
310
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
311
- DeprecationWarning,
312
- stacklevel=2,
313
- )
314
- stream_events = stream_events or stream_intermediate_steps
315
-
316
305
  if stream_events and workflow_run_response:
317
306
  # Yield router started event
318
307
  yield RouterExecutionStartedEvent(
@@ -558,7 +547,6 @@ class Router:
558
547
  run_context: Optional[RunContext] = None,
559
548
  session_state: Optional[Dict[str, Any]] = None,
560
549
  stream_events: bool = False,
561
- stream_intermediate_steps: bool = False,
562
550
  stream_executor_events: bool = True,
563
551
  workflow_run_response: Optional[WorkflowRunOutput] = None,
564
552
  step_index: Optional[Union[int, tuple]] = None,
@@ -583,15 +571,6 @@ class Router:
583
571
  steps_to_execute = await self._aroute_steps(step_input, session_state=session_state)
584
572
  log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
585
573
 
586
- # Considering both stream_events and stream_intermediate_steps (deprecated)
587
- if stream_intermediate_steps is not None:
588
- warnings.warn(
589
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
590
- DeprecationWarning,
591
- stacklevel=2,
592
- )
593
- stream_events = stream_events or stream_intermediate_steps
594
-
595
574
  if stream_events and workflow_run_response:
596
575
  # Yield router started event
597
576
  yield RouterExecutionStartedEvent(
agno/workflow/step.py CHANGED
@@ -1,5 +1,4 @@
1
1
  import inspect
2
- import warnings
3
2
  from copy import copy
4
3
  from dataclasses import dataclass
5
4
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union, cast
@@ -9,9 +8,11 @@ from pydantic import BaseModel
9
8
  from typing_extensions import TypeGuard
10
9
 
11
10
  from agno.agent import Agent
11
+ from agno.db.base import BaseDb
12
12
  from agno.media import Audio, Image, Video
13
13
  from agno.models.message import Message
14
14
  from agno.models.metrics import Metrics
15
+ from agno.registry import Registry
15
16
  from agno.run import RunContext
16
17
  from agno.run.agent import RunContentEvent, RunOutput
17
18
  from agno.run.base import BaseRunOutputEvent
@@ -61,7 +62,6 @@ class Step:
61
62
 
62
63
  # Step configuration
63
64
  max_retries: int = 3
64
- timeout_seconds: Optional[int] = None
65
65
 
66
66
  skip_on_failure: bool = False
67
67
 
@@ -83,7 +83,6 @@ class Step:
83
83
  step_id: Optional[str] = None,
84
84
  description: Optional[str] = None,
85
85
  max_retries: int = 3,
86
- timeout_seconds: Optional[int] = None,
87
86
  skip_on_failure: bool = False,
88
87
  strict_input_validation: bool = False,
89
88
  add_workflow_history: Optional[bool] = None,
@@ -104,7 +103,6 @@ class Step:
104
103
  self.step_id = step_id
105
104
  self.description = description
106
105
  self.max_retries = max_retries
107
- self.timeout_seconds = timeout_seconds
108
106
  self.skip_on_failure = skip_on_failure
109
107
  self.strict_input_validation = strict_input_validation
110
108
  self.add_workflow_history = add_workflow_history
@@ -117,6 +115,121 @@ class Step:
117
115
  # Set the active executor
118
116
  self._set_active_executor()
119
117
 
118
+ def to_dict(self) -> Dict[str, Any]:
119
+ """Convert step to a dictionary representation."""
120
+ result = {
121
+ "name": self.name,
122
+ "step_id": self.step_id,
123
+ "description": self.description,
124
+ "max_retries": self.max_retries,
125
+ "skip_on_failure": self.skip_on_failure,
126
+ "strict_input_validation": self.strict_input_validation,
127
+ "add_workflow_history": self.add_workflow_history,
128
+ "num_history_runs": self.num_history_runs,
129
+ }
130
+
131
+ if self.agent is not None:
132
+ result["agent_id"] = self.agent.id
133
+ if self.team is not None:
134
+ result["team_id"] = self.team.id
135
+ # TODO: Add support for custom executors
136
+
137
+ return result
138
+
139
+ @classmethod
140
+ def from_dict(
141
+ cls,
142
+ data: Dict[str, Any],
143
+ registry: Optional[Registry] = None,
144
+ db: Optional["BaseDb"] = None,
145
+ links: Optional[List[Dict[str, Any]]] = None,
146
+ ) -> "Step":
147
+ """
148
+ Create a Step from a dictionary.
149
+
150
+ Args:
151
+ data: Dictionary containing step configuration
152
+ registry: Optional registry for rehydrating non-serializable objects
153
+ db: Optional database for loading agents/teams in steps
154
+ links: Optional links for this step version
155
+
156
+ Returns:
157
+ Step: Reconstructed step instance
158
+ """
159
+ config = data.copy()
160
+
161
+ agent = None
162
+ team = None
163
+ executor = None
164
+
165
+ # --- Handle Agent reconstruction ---
166
+ if "agent_id" in config and config["agent_id"]:
167
+ from agno.agent.agent import get_agent_by_id
168
+
169
+ agent_id = config.get("agent_id")
170
+ if db is not None and agent_id is not None:
171
+ agent = get_agent_by_id(db=db, id=agent_id, registry=registry)
172
+
173
+ # --- Handle Team reconstruction ---
174
+ # if "team_id" in config and config["team_id"] and registry:
175
+ # from agno.team.team import get_team_by_id
176
+ # team = get_team_by_id(db=db, id=config["team_id"])
177
+
178
+ # --- Handle Executor reconstruction ---
179
+ # TODO: Implement executor reconstruction
180
+ # if "executor_ref" in config and config["executor_ref"] and registry:
181
+ # executor = registry.rehydrate_function(config["executor_ref"])
182
+
183
+ return cls(
184
+ name=config.get("name"),
185
+ step_id=config.get("step_id"),
186
+ description=config.get("description"),
187
+ max_retries=config.get("max_retries", 3),
188
+ skip_on_failure=config.get("skip_on_failure", False),
189
+ strict_input_validation=config.get("strict_input_validation", False),
190
+ add_workflow_history=config.get("add_workflow_history"),
191
+ num_history_runs=config.get("num_history_runs", 3),
192
+ agent=agent,
193
+ team=team,
194
+ executor=executor,
195
+ )
196
+
197
+ def get_links(self, position: int = 0) -> List[Dict[str, Any]]:
198
+ """Get links for this step's agent/team.
199
+
200
+ Args:
201
+ position: Position of this step in the workflow.
202
+
203
+ Returns:
204
+ List of link dictionaries for the links table.
205
+ """
206
+ links = []
207
+ link_key = self.step_id or self.name
208
+
209
+ if self.agent is not None:
210
+ links.append(
211
+ {
212
+ "link_kind": "step_agent",
213
+ "link_key": link_key,
214
+ "child_component_id": self.agent.id,
215
+ "child_version": None,
216
+ "position": position,
217
+ }
218
+ )
219
+
220
+ if self.team is not None:
221
+ links.append(
222
+ {
223
+ "link_kind": "step_team",
224
+ "link_key": link_key,
225
+ "child_component_id": self.team.id,
226
+ "child_version": None,
227
+ "position": position,
228
+ }
229
+ )
230
+
231
+ return links
232
+
120
233
  @property
121
234
  def executor_name(self) -> str:
122
235
  """Get the name of the current executor"""
@@ -464,7 +577,6 @@ class Step:
464
577
  session_id: Optional[str] = None,
465
578
  user_id: Optional[str] = None,
466
579
  stream_events: bool = False,
467
- stream_intermediate_steps: bool = False,
468
580
  stream_executor_events: bool = True,
469
581
  workflow_run_response: Optional["WorkflowRunOutput"] = None,
470
582
  run_context: Optional[RunContext] = None,
@@ -492,15 +604,6 @@ class Step:
492
604
  else:
493
605
  session_state_copy = copy(session_state) if session_state is not None else {}
494
606
 
495
- # Considering both stream_events and stream_intermediate_steps (deprecated)
496
- if stream_intermediate_steps is not None:
497
- warnings.warn(
498
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
499
- DeprecationWarning,
500
- stacklevel=2,
501
- )
502
- stream_events = stream_events or stream_intermediate_steps
503
-
504
607
  # Emit StepStartedEvent
505
608
  if stream_events and workflow_run_response:
506
609
  yield StepStartedEvent(
@@ -949,7 +1052,6 @@ class Step:
949
1052
  session_id: Optional[str] = None,
950
1053
  user_id: Optional[str] = None,
951
1054
  stream_events: bool = False,
952
- stream_intermediate_steps: bool = False,
953
1055
  stream_executor_events: bool = True,
954
1056
  workflow_run_response: Optional["WorkflowRunOutput"] = None,
955
1057
  run_context: Optional[RunContext] = None,
@@ -977,15 +1079,6 @@ class Step:
977
1079
  else:
978
1080
  session_state_copy = copy(session_state) if session_state is not None else {}
979
1081
 
980
- # Considering both stream_events and stream_intermediate_steps (deprecated)
981
- if stream_intermediate_steps is not None:
982
- warnings.warn(
983
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
984
- DeprecationWarning,
985
- stacklevel=2,
986
- )
987
- stream_events = stream_events or stream_intermediate_steps
988
-
989
1082
  if stream_events and workflow_run_response:
990
1083
  # Emit StepStartedEvent
991
1084
  yield StepStartedEvent(
agno/workflow/steps.py CHANGED
@@ -1,4 +1,3 @@
1
- import warnings
2
1
  from dataclasses import dataclass
3
2
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
4
3
  from uuid import uuid4
@@ -216,7 +215,6 @@ class Steps:
216
215
  session_id: Optional[str] = None,
217
216
  user_id: Optional[str] = None,
218
217
  stream_events: bool = False,
219
- stream_intermediate_steps: bool = False,
220
218
  stream_executor_events: bool = True,
221
219
  step_index: Optional[Union[int, tuple]] = None,
222
220
  store_executor_outputs: bool = True,
@@ -233,15 +231,6 @@ class Steps:
233
231
 
234
232
  self._prepare_steps()
235
233
 
236
- # Considering both stream_events and stream_intermediate_steps (deprecated)
237
- if stream_intermediate_steps is not None:
238
- warnings.warn(
239
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
240
- DeprecationWarning,
241
- stacklevel=2,
242
- )
243
- stream_events = stream_events or stream_intermediate_steps
244
-
245
234
  if stream_events:
246
235
  # Yield steps execution started event
247
236
  yield StepsExecutionStartedEvent(
@@ -467,7 +456,6 @@ class Steps:
467
456
  session_id: Optional[str] = None,
468
457
  user_id: Optional[str] = None,
469
458
  stream_events: bool = False,
470
- stream_intermediate_steps: bool = False,
471
459
  stream_executor_events: bool = True,
472
460
  step_index: Optional[Union[int, tuple]] = None,
473
461
  store_executor_outputs: bool = True,
@@ -484,15 +472,6 @@ class Steps:
484
472
 
485
473
  self._prepare_steps()
486
474
 
487
- # Considering both stream_events and stream_intermediate_steps (deprecated)
488
- if stream_intermediate_steps is not None:
489
- warnings.warn(
490
- "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
491
- DeprecationWarning,
492
- stacklevel=2,
493
- )
494
- stream_events = stream_events or stream_intermediate_steps
495
-
496
475
  if stream_events:
497
476
  # Yield steps execution started event
498
477
  yield StepsExecutionStartedEvent(