agno 2.2.8__py3-none-any.whl → 2.2.10__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 (50) hide show
  1. agno/agent/agent.py +37 -19
  2. agno/db/base.py +23 -0
  3. agno/db/dynamo/dynamo.py +20 -25
  4. agno/db/dynamo/schemas.py +1 -0
  5. agno/db/firestore/firestore.py +11 -0
  6. agno/db/gcs_json/gcs_json_db.py +4 -0
  7. agno/db/in_memory/in_memory_db.py +4 -0
  8. agno/db/json/json_db.py +4 -0
  9. agno/db/mongo/async_mongo.py +27 -0
  10. agno/db/mongo/mongo.py +25 -0
  11. agno/db/mysql/mysql.py +26 -1
  12. agno/db/postgres/async_postgres.py +26 -1
  13. agno/db/postgres/postgres.py +26 -1
  14. agno/db/redis/redis.py +4 -0
  15. agno/db/singlestore/singlestore.py +24 -0
  16. agno/db/sqlite/async_sqlite.py +25 -1
  17. agno/db/sqlite/sqlite.py +25 -1
  18. agno/db/surrealdb/surrealdb.py +13 -1
  19. agno/knowledge/reader/docx_reader.py +0 -1
  20. agno/models/azure/ai_foundry.py +2 -1
  21. agno/models/cerebras/cerebras.py +3 -2
  22. agno/models/openai/chat.py +2 -1
  23. agno/models/openai/responses.py +2 -1
  24. agno/os/app.py +127 -65
  25. agno/os/config.py +1 -0
  26. agno/os/interfaces/agui/router.py +9 -0
  27. agno/os/interfaces/agui/utils.py +49 -3
  28. agno/os/mcp.py +8 -8
  29. agno/os/router.py +27 -9
  30. agno/os/routers/evals/evals.py +12 -7
  31. agno/os/routers/memory/memory.py +18 -10
  32. agno/os/routers/metrics/metrics.py +6 -4
  33. agno/os/routers/session/session.py +21 -11
  34. agno/os/utils.py +57 -11
  35. agno/team/team.py +33 -23
  36. agno/vectordb/mongodb/__init__.py +7 -1
  37. agno/vectordb/redis/__init__.py +4 -0
  38. agno/workflow/agent.py +2 -2
  39. agno/workflow/condition.py +26 -4
  40. agno/workflow/loop.py +9 -0
  41. agno/workflow/parallel.py +39 -16
  42. agno/workflow/router.py +25 -4
  43. agno/workflow/step.py +162 -91
  44. agno/workflow/steps.py +9 -0
  45. agno/workflow/workflow.py +26 -22
  46. {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/METADATA +11 -13
  47. {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/RECORD +50 -50
  48. {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/WHEEL +0 -0
  49. {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/licenses/LICENSE +0 -0
  50. {agno-2.2.8.dist-info → agno-2.2.10.dist-info}/top_level.txt +0 -0
agno/team/team.py CHANGED
@@ -802,13 +802,10 @@ class Team:
802
802
  member.team_id = self.id
803
803
  member.set_id()
804
804
 
805
- # Inherit team models if agent has no explicit model
806
- for model_type in ["model", "reasoning_model", "parser_model", "output_model"]:
807
- if getattr(member, model_type) is None and getattr(self, model_type) is not None:
808
- setattr(member, model_type, getattr(self, model_type))
809
- log_info(
810
- f"Agent '{member.name or member.id}' inheriting {model_type} from Team: {getattr(self, model_type).id}"
811
- )
805
+ # Inherit team primary model if agent has no explicit model
806
+ if member.model is None and self.model is not None:
807
+ member.model = self.model
808
+ log_info(f"Agent '{member.name or member.id}' inheriting model from Team: {self.model.id}")
812
809
 
813
810
  elif isinstance(member, Team):
814
811
  member.parent_team_id = self.id
@@ -1458,7 +1455,7 @@ class Team:
1458
1455
  add_session_state_to_context: Optional[bool] = None,
1459
1456
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1460
1457
  stream_events: bool = False,
1461
- yield_run_response: bool = False,
1458
+ yield_run_output: bool = False,
1462
1459
  debug_mode: Optional[bool] = None,
1463
1460
  **kwargs: Any,
1464
1461
  ) -> Iterator[Union[TeamRunOutputEvent, RunOutputEvent, TeamRunOutput]]:
@@ -1701,7 +1698,7 @@ class Team:
1701
1698
  if stream_events:
1702
1699
  yield completed_event
1703
1700
 
1704
- if yield_run_response:
1701
+ if yield_run_output:
1705
1702
  yield run_response
1706
1703
 
1707
1704
  # Log Team Telemetry
@@ -1765,6 +1762,7 @@ class Team:
1765
1762
  stream_intermediate_steps: Optional[bool] = None,
1766
1763
  session_id: Optional[str] = None,
1767
1764
  session_state: Optional[Dict[str, Any]] = None,
1765
+ run_context: Optional[RunContext] = None,
1768
1766
  user_id: Optional[str] = None,
1769
1767
  retries: Optional[int] = None,
1770
1768
  audio: Optional[Sequence[Audio]] = None,
@@ -1778,7 +1776,8 @@ class Team:
1778
1776
  dependencies: Optional[Dict[str, Any]] = None,
1779
1777
  metadata: Optional[Dict[str, Any]] = None,
1780
1778
  debug_mode: Optional[bool] = None,
1781
- yield_run_response: bool = False,
1779
+ yield_run_response: bool = False, # To be deprecated: use yield_run_output instead
1780
+ yield_run_output: bool = False,
1782
1781
  **kwargs: Any,
1783
1782
  ) -> Iterator[Union[RunOutputEvent, TeamRunOutputEvent]]: ...
1784
1783
 
@@ -1791,6 +1790,7 @@ class Team:
1791
1790
  stream_intermediate_steps: Optional[bool] = None,
1792
1791
  session_id: Optional[str] = None,
1793
1792
  session_state: Optional[Dict[str, Any]] = None,
1793
+ run_context: Optional[RunContext] = None,
1794
1794
  user_id: Optional[str] = None,
1795
1795
  retries: Optional[int] = None,
1796
1796
  audio: Optional[Sequence[Audio]] = None,
@@ -1804,7 +1804,8 @@ class Team:
1804
1804
  dependencies: Optional[Dict[str, Any]] = None,
1805
1805
  metadata: Optional[Dict[str, Any]] = None,
1806
1806
  debug_mode: Optional[bool] = None,
1807
- yield_run_response: bool = False,
1807
+ yield_run_response: bool = False, # To be deprecated: use yield_run_output instead
1808
+ yield_run_output: bool = False,
1808
1809
  **kwargs: Any,
1809
1810
  ) -> Union[TeamRunOutput, Iterator[Union[RunOutputEvent, TeamRunOutputEvent]]]:
1810
1811
  """Run the Team and return the response."""
@@ -1863,7 +1864,7 @@ class Team:
1863
1864
  dependencies = dependencies if dependencies is not None else self.dependencies
1864
1865
 
1865
1866
  # Initialize run context
1866
- run_context = RunContext(
1867
+ run_context = run_context or RunContext(
1867
1868
  run_id=run_id,
1868
1869
  session_id=session_id,
1869
1870
  user_id=user_id,
@@ -1948,6 +1949,8 @@ class Team:
1948
1949
  last_exception = None
1949
1950
  num_attempts = retries + 1
1950
1951
 
1952
+ yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
1953
+
1951
1954
  for attempt in range(num_attempts):
1952
1955
  # Initialize the current run
1953
1956
 
@@ -1964,7 +1967,7 @@ class Team:
1964
1967
  add_session_state_to_context=add_session_state,
1965
1968
  response_format=response_format,
1966
1969
  stream_events=stream_events,
1967
- yield_run_response=yield_run_response,
1970
+ yield_run_output=yield_run_output,
1968
1971
  debug_mode=debug_mode,
1969
1972
  **kwargs,
1970
1973
  )
@@ -2270,7 +2273,7 @@ class Team:
2270
2273
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2271
2274
  stream_events: bool = False,
2272
2275
  stream_intermediate_steps: bool = False,
2273
- yield_run_response: bool = False,
2276
+ yield_run_output: bool = False,
2274
2277
  add_dependencies_to_context: Optional[bool] = None,
2275
2278
  add_session_state_to_context: Optional[bool] = None,
2276
2279
  add_history_to_context: Optional[bool] = None,
@@ -2545,7 +2548,7 @@ class Team:
2545
2548
  if stream_events:
2546
2549
  yield completed_event
2547
2550
 
2548
- if yield_run_response:
2551
+ if yield_run_output:
2549
2552
  yield run_response
2550
2553
 
2551
2554
  # Log Team Telemetry
@@ -2592,6 +2595,7 @@ class Team:
2592
2595
  stream_intermediate_steps: Optional[bool] = None,
2593
2596
  session_id: Optional[str] = None,
2594
2597
  session_state: Optional[Dict[str, Any]] = None,
2598
+ run_context: Optional[RunContext] = None,
2595
2599
  user_id: Optional[str] = None,
2596
2600
  retries: Optional[int] = None,
2597
2601
  audio: Optional[Sequence[Audio]] = None,
@@ -2618,6 +2622,7 @@ class Team:
2618
2622
  stream_intermediate_steps: Optional[bool] = None,
2619
2623
  session_id: Optional[str] = None,
2620
2624
  session_state: Optional[Dict[str, Any]] = None,
2625
+ run_context: Optional[RunContext] = None,
2621
2626
  user_id: Optional[str] = None,
2622
2627
  retries: Optional[int] = None,
2623
2628
  audio: Optional[Sequence[Audio]] = None,
@@ -2631,7 +2636,8 @@ class Team:
2631
2636
  dependencies: Optional[Dict[str, Any]] = None,
2632
2637
  metadata: Optional[Dict[str, Any]] = None,
2633
2638
  debug_mode: Optional[bool] = None,
2634
- yield_run_response: bool = False,
2639
+ yield_run_response: bool = False, # To be deprecated: use yield_run_output instead
2640
+ yield_run_output: bool = False,
2635
2641
  **kwargs: Any,
2636
2642
  ) -> AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent]]: ...
2637
2643
 
@@ -2644,6 +2650,7 @@ class Team:
2644
2650
  stream_intermediate_steps: Optional[bool] = None,
2645
2651
  session_id: Optional[str] = None,
2646
2652
  session_state: Optional[Dict[str, Any]] = None,
2653
+ run_context: Optional[RunContext] = None,
2647
2654
  user_id: Optional[str] = None,
2648
2655
  retries: Optional[int] = None,
2649
2656
  audio: Optional[Sequence[Audio]] = None,
@@ -2657,7 +2664,8 @@ class Team:
2657
2664
  dependencies: Optional[Dict[str, Any]] = None,
2658
2665
  metadata: Optional[Dict[str, Any]] = None,
2659
2666
  debug_mode: Optional[bool] = None,
2660
- yield_run_response: bool = False,
2667
+ yield_run_response: bool = False, # To be deprecated: use yield_run_output instead
2668
+ yield_run_output: bool = False,
2661
2669
  **kwargs: Any,
2662
2670
  ) -> Union[TeamRunOutput, AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent]]]:
2663
2671
  """Run the Team asynchronously and return the response."""
@@ -2747,7 +2755,7 @@ class Team:
2747
2755
  effective_filters = self._get_effective_filters(knowledge_filters)
2748
2756
 
2749
2757
  # Initialize run context
2750
- run_context = RunContext(
2758
+ run_context = run_context or RunContext(
2751
2759
  run_id=run_id,
2752
2760
  session_id=session_id,
2753
2761
  user_id=user_id,
@@ -2783,6 +2791,8 @@ class Team:
2783
2791
  last_exception = None
2784
2792
  num_attempts = retries + 1
2785
2793
 
2794
+ yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
2795
+
2786
2796
  for attempt in range(num_attempts):
2787
2797
  # Run the team
2788
2798
  try:
@@ -2798,7 +2808,7 @@ class Team:
2798
2808
  add_session_state_to_context=add_session_state,
2799
2809
  response_format=response_format,
2800
2810
  stream_events=stream_events,
2801
- yield_run_response=yield_run_response,
2811
+ yield_run_output=yield_run_output,
2802
2812
  debug_mode=debug_mode,
2803
2813
  **kwargs,
2804
2814
  )
@@ -6988,7 +6998,7 @@ class Team:
6988
6998
  knowledge_filters=run_context.knowledge_filters
6989
6999
  if not member_agent.knowledge_filters and member_agent.knowledge
6990
7000
  else None,
6991
- yield_run_response=True,
7001
+ yield_run_output=True,
6992
7002
  )
6993
7003
  member_agent_run_response = None
6994
7004
  for member_agent_run_output_event in member_agent_run_response_stream:
@@ -7118,7 +7128,7 @@ class Team:
7118
7128
  knowledge_filters=run_context.knowledge_filters
7119
7129
  if not member_agent.knowledge_filters and member_agent.knowledge
7120
7130
  else None,
7121
- yield_run_response=True,
7131
+ yield_run_output=True,
7122
7132
  )
7123
7133
  member_agent_run_response = None
7124
7134
  async for member_agent_run_response_event in member_agent_run_response_stream:
@@ -7234,7 +7244,7 @@ class Team:
7234
7244
  add_dependencies_to_context=add_dependencies_to_context,
7235
7245
  add_session_state_to_context=add_session_state_to_context,
7236
7246
  metadata=run_context.metadata,
7237
- yield_run_response=True,
7247
+ yield_run_output=True,
7238
7248
  )
7239
7249
  member_agent_run_response = None
7240
7250
  for member_agent_run_response_chunk in member_agent_run_response_stream:
@@ -7350,7 +7360,7 @@ class Team:
7350
7360
  add_dependencies_to_context=add_dependencies_to_context,
7351
7361
  add_session_state_to_context=add_session_state_to_context,
7352
7362
  metadata=run_context.metadata,
7353
- yield_run_response=True,
7363
+ yield_run_output=True,
7354
7364
  )
7355
7365
  member_agent_run_response = None
7356
7366
  try:
@@ -1,3 +1,9 @@
1
1
  from agno.vectordb.mongodb.mongodb import MongoDb
2
2
 
3
- __all__ = ["MongoDb"]
3
+ # Alias to avoid name collision with the main MongoDb class
4
+ MongoVectorDb = MongoDb
5
+
6
+ __all__ = [
7
+ "MongoVectorDb",
8
+ "MongoDb",
9
+ ]
@@ -1,5 +1,9 @@
1
1
  from agno.vectordb.redis.redisdb import RedisDB
2
2
 
3
+ # Backward compatibility alias
4
+ RedisVectorDb = RedisDB
5
+
3
6
  __all__ = [
7
+ "RedisVectorDb",
4
8
  "RedisDB",
5
9
  ]
agno/workflow/agent.py CHANGED
@@ -268,7 +268,7 @@ Guidelines:
268
268
  user_id=session_from_db.user_id,
269
269
  execution_input=workflow_execution_input,
270
270
  workflow_run_response=workflow_run_response,
271
- session_state=run_context.session_state,
271
+ run_context=run_context,
272
272
  stream_events=True,
273
273
  websocket_handler=websocket_handler,
274
274
  ):
@@ -286,7 +286,7 @@ Guidelines:
286
286
  user_id=session_from_db.user_id,
287
287
  execution_input=workflow_execution_input,
288
288
  workflow_run_response=workflow_run_response,
289
- session_state=run_context.session_state,
289
+ run_context=run_context,
290
290
  )
291
291
 
292
292
  if isinstance(result.content, str):
@@ -4,6 +4,7 @@ from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List
4
4
  from uuid import uuid4
5
5
 
6
6
  from agno.run.agent import RunOutputEvent
7
+ from agno.run.base import RunContext
7
8
  from agno.run.team import TeamRunOutputEvent
8
9
  from agno.run.workflow import (
9
10
  ConditionExecutionCompletedEvent,
@@ -175,6 +176,7 @@ class Condition:
175
176
  user_id: Optional[str] = None,
176
177
  workflow_run_response: Optional[WorkflowRunOutput] = None,
177
178
  store_executor_outputs: bool = True,
179
+ run_context: Optional[RunContext] = None,
178
180
  session_state: Optional[Dict[str, Any]] = None,
179
181
  workflow_session: Optional[WorkflowSession] = None,
180
182
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -188,7 +190,11 @@ class Condition:
188
190
  self._prepare_steps()
189
191
 
190
192
  # Evaluate the condition
191
- condition_result = self._evaluate_condition(step_input, session_state)
193
+ if run_context is not None and run_context.session_state is not None:
194
+ condition_result = self._evaluate_condition(step_input, session_state=run_context.session_state)
195
+ else:
196
+ condition_result = self._evaluate_condition(step_input, session_state=session_state)
197
+
192
198
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
193
199
 
194
200
  if not condition_result:
@@ -214,6 +220,7 @@ class Condition:
214
220
  user_id=user_id,
215
221
  workflow_run_response=workflow_run_response,
216
222
  store_executor_outputs=store_executor_outputs,
223
+ run_context=run_context,
217
224
  session_state=session_state,
218
225
  workflow_session=workflow_session,
219
226
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -284,6 +291,7 @@ class Condition:
284
291
  workflow_run_response: Optional[WorkflowRunOutput] = None,
285
292
  step_index: Optional[Union[int, tuple]] = None,
286
293
  store_executor_outputs: bool = True,
294
+ run_context: Optional[RunContext] = None,
287
295
  session_state: Optional[Dict[str, Any]] = None,
288
296
  parent_step_id: Optional[str] = None,
289
297
  workflow_session: Optional[WorkflowSession] = None,
@@ -298,7 +306,10 @@ class Condition:
298
306
  self._prepare_steps()
299
307
 
300
308
  # Evaluate the condition
301
- condition_result = self._evaluate_condition(step_input, session_state)
309
+ if run_context is not None and run_context.session_state is not None:
310
+ condition_result = self._evaluate_condition(step_input, session_state=run_context.session_state)
311
+ else:
312
+ condition_result = self._evaluate_condition(step_input, session_state=session_state)
302
313
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
303
314
 
304
315
  # Considering both stream_events and stream_intermediate_steps (deprecated)
@@ -363,6 +374,7 @@ class Condition:
363
374
  workflow_run_response=workflow_run_response,
364
375
  step_index=child_step_index,
365
376
  store_executor_outputs=store_executor_outputs,
377
+ run_context=run_context,
366
378
  session_state=session_state,
367
379
  parent_step_id=conditional_step_id,
368
380
  workflow_session=workflow_session,
@@ -447,6 +459,7 @@ class Condition:
447
459
  user_id: Optional[str] = None,
448
460
  workflow_run_response: Optional[WorkflowRunOutput] = None,
449
461
  store_executor_outputs: bool = True,
462
+ run_context: Optional[RunContext] = None,
450
463
  session_state: Optional[Dict[str, Any]] = None,
451
464
  workflow_session: Optional[WorkflowSession] = None,
452
465
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -460,7 +473,10 @@ class Condition:
460
473
  self._prepare_steps()
461
474
 
462
475
  # Evaluate the condition
463
- condition_result = await self._aevaluate_condition(step_input, session_state)
476
+ if run_context is not None and run_context.session_state is not None:
477
+ condition_result = await self._aevaluate_condition(step_input, session_state=run_context.session_state)
478
+ else:
479
+ condition_result = await self._aevaluate_condition(step_input, session_state=session_state)
464
480
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
465
481
 
466
482
  if not condition_result:
@@ -488,6 +504,7 @@ class Condition:
488
504
  user_id=user_id,
489
505
  workflow_run_response=workflow_run_response,
490
506
  store_executor_outputs=store_executor_outputs,
507
+ run_context=run_context,
491
508
  session_state=session_state,
492
509
  workflow_session=workflow_session,
493
510
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -556,6 +573,7 @@ class Condition:
556
573
  workflow_run_response: Optional[WorkflowRunOutput] = None,
557
574
  step_index: Optional[Union[int, tuple]] = None,
558
575
  store_executor_outputs: bool = True,
576
+ run_context: Optional[RunContext] = None,
559
577
  session_state: Optional[Dict[str, Any]] = None,
560
578
  parent_step_id: Optional[str] = None,
561
579
  workflow_session: Optional[WorkflowSession] = None,
@@ -570,7 +588,10 @@ class Condition:
570
588
  self._prepare_steps()
571
589
 
572
590
  # Evaluate the condition
573
- condition_result = await self._aevaluate_condition(step_input, session_state)
591
+ if run_context is not None and run_context.session_state is not None:
592
+ condition_result = await self._aevaluate_condition(step_input, session_state=run_context.session_state)
593
+ else:
594
+ condition_result = await self._aevaluate_condition(step_input, session_state=session_state)
574
595
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
575
596
 
576
597
  # Considering both stream_events and stream_intermediate_steps (deprecated)
@@ -637,6 +658,7 @@ class Condition:
637
658
  workflow_run_response=workflow_run_response,
638
659
  step_index=child_step_index,
639
660
  store_executor_outputs=store_executor_outputs,
661
+ run_context=run_context,
640
662
  session_state=session_state,
641
663
  parent_step_id=conditional_step_id,
642
664
  workflow_session=workflow_session,
agno/workflow/loop.py CHANGED
@@ -4,6 +4,7 @@ from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List
4
4
  from uuid import uuid4
5
5
 
6
6
  from agno.run.agent import RunOutputEvent
7
+ from agno.run.base import RunContext
7
8
  from agno.run.team import TeamRunOutputEvent
8
9
  from agno.run.workflow import (
9
10
  LoopExecutionCompletedEvent,
@@ -132,6 +133,7 @@ class Loop:
132
133
  user_id: Optional[str] = None,
133
134
  workflow_run_response: Optional[WorkflowRunOutput] = None,
134
135
  store_executor_outputs: bool = True,
136
+ run_context: Optional[RunContext] = None,
135
137
  session_state: Optional[Dict[str, Any]] = None,
136
138
  workflow_session: Optional[WorkflowSession] = None,
137
139
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -160,6 +162,7 @@ class Loop:
160
162
  user_id=user_id,
161
163
  workflow_run_response=workflow_run_response,
162
164
  store_executor_outputs=store_executor_outputs,
165
+ run_context=run_context,
163
166
  session_state=session_state,
164
167
  workflow_session=workflow_session,
165
168
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -232,6 +235,7 @@ class Loop:
232
235
  workflow_run_response: Optional[WorkflowRunOutput] = None,
233
236
  step_index: Optional[Union[int, tuple]] = None,
234
237
  store_executor_outputs: bool = True,
238
+ run_context: Optional[RunContext] = None,
235
239
  session_state: Optional[Dict[str, Any]] = None,
236
240
  parent_step_id: Optional[str] = None,
237
241
  workflow_session: Optional[WorkflowSession] = None,
@@ -311,6 +315,7 @@ class Loop:
311
315
  workflow_run_response=workflow_run_response,
312
316
  step_index=composite_step_index,
313
317
  store_executor_outputs=store_executor_outputs,
318
+ run_context=run_context,
314
319
  session_state=session_state,
315
320
  parent_step_id=loop_step_id,
316
321
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -428,6 +433,7 @@ class Loop:
428
433
  user_id: Optional[str] = None,
429
434
  workflow_run_response: Optional[WorkflowRunOutput] = None,
430
435
  store_executor_outputs: bool = True,
436
+ run_context: Optional[RunContext] = None,
431
437
  session_state: Optional[Dict[str, Any]] = None,
432
438
  workflow_session: Optional[WorkflowSession] = None,
433
439
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -458,6 +464,7 @@ class Loop:
458
464
  user_id=user_id,
459
465
  workflow_run_response=workflow_run_response,
460
466
  store_executor_outputs=store_executor_outputs,
467
+ run_context=run_context,
461
468
  session_state=session_state,
462
469
  workflow_session=workflow_session,
463
470
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -533,6 +540,7 @@ class Loop:
533
540
  workflow_run_response: Optional[WorkflowRunOutput] = None,
534
541
  step_index: Optional[Union[int, tuple]] = None,
535
542
  store_executor_outputs: bool = True,
543
+ run_context: Optional[RunContext] = None,
536
544
  session_state: Optional[Dict[str, Any]] = None,
537
545
  parent_step_id: Optional[str] = None,
538
546
  workflow_session: Optional[WorkflowSession] = None,
@@ -612,6 +620,7 @@ class Loop:
612
620
  workflow_run_response=workflow_run_response,
613
621
  step_index=composite_step_index,
614
622
  store_executor_outputs=store_executor_outputs,
623
+ run_context=run_context,
615
624
  session_state=session_state,
616
625
  parent_step_id=loop_step_id,
617
626
  workflow_session=workflow_session,
agno/workflow/parallel.py CHANGED
@@ -7,6 +7,7 @@ from uuid import uuid4
7
7
 
8
8
  from agno.models.metrics import Metrics
9
9
  from agno.run.agent import RunOutputEvent
10
+ from agno.run.base import RunContext
10
11
  from agno.run.team import TeamRunOutputEvent
11
12
  from agno.run.workflow import (
12
13
  ParallelExecutionCompletedEvent,
@@ -200,6 +201,7 @@ class Parallel:
200
201
  user_id: Optional[str] = None,
201
202
  workflow_run_response: Optional[WorkflowRunOutput] = None,
202
203
  store_executor_outputs: bool = True,
204
+ run_context: Optional[RunContext] = None,
203
205
  session_state: Optional[Dict[str, Any]] = None,
204
206
  workflow_session: Optional[WorkflowSession] = None,
205
207
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -214,10 +216,14 @@ class Parallel:
214
216
  # Create individual session_state copies for each step to prevent race conditions
215
217
  session_state_copies = []
216
218
  for _ in range(len(self.steps)):
217
- if session_state is not None:
218
- session_state_copies.append(deepcopy(session_state))
219
+ # If using run context, no need to deepcopy the state. We want the direct reference.
220
+ if run_context is not None and run_context.session_state is not None:
221
+ session_state_copies.append(run_context.session_state)
219
222
  else:
220
- session_state_copies.append({})
223
+ if session_state is not None:
224
+ session_state_copies.append(deepcopy(session_state))
225
+ else:
226
+ session_state_copies.append({})
221
227
 
222
228
  def execute_step_with_index(step_with_index):
223
229
  """Execute a single step and preserve its original index"""
@@ -235,6 +241,7 @@ class Parallel:
235
241
  workflow_session=workflow_session,
236
242
  add_workflow_history_to_steps=add_workflow_history_to_steps,
237
243
  num_history_runs=num_history_runs,
244
+ run_context=run_context,
238
245
  session_state=step_session_state,
239
246
  ) # type: ignore[union-attr]
240
247
  return idx, step_result, step_session_state
@@ -288,7 +295,7 @@ class Parallel:
288
295
  )
289
296
  )
290
297
 
291
- if session_state is not None:
298
+ if run_context is None and session_state is not None:
292
299
  merge_parallel_session_states(session_state, modified_session_states)
293
300
 
294
301
  # Sort by original index to preserve order
@@ -322,6 +329,7 @@ class Parallel:
322
329
  workflow_run_response: Optional[WorkflowRunOutput] = None,
323
330
  step_index: Optional[Union[int, tuple]] = None,
324
331
  store_executor_outputs: bool = True,
332
+ run_context: Optional[RunContext] = None,
325
333
  session_state: Optional[Dict[str, Any]] = None,
326
334
  parent_step_id: Optional[str] = None,
327
335
  workflow_session: Optional[WorkflowSession] = None,
@@ -338,10 +346,14 @@ class Parallel:
338
346
  # Create individual session_state copies for each step to prevent race conditions
339
347
  session_state_copies = []
340
348
  for _ in range(len(self.steps)):
341
- if session_state is not None:
342
- session_state_copies.append(deepcopy(session_state))
349
+ # If using run context, no need to deepcopy the state. We want the direct reference.
350
+ if run_context is not None and run_context.session_state is not None:
351
+ session_state_copies.append(run_context.session_state)
343
352
  else:
344
- session_state_copies.append({})
353
+ if session_state is not None:
354
+ session_state_copies.append(deepcopy(session_state))
355
+ else:
356
+ session_state_copies.append({})
345
357
 
346
358
  # Considering both stream_events and stream_intermediate_steps (deprecated)
347
359
  stream_events = stream_events or stream_intermediate_steps
@@ -468,7 +480,7 @@ class Parallel:
468
480
  logger.error(f"Future completion error: {e}")
469
481
 
470
482
  # Merge all session_state changes back into the original session_state
471
- if session_state is not None:
483
+ if run_context is None and session_state is not None:
472
484
  merge_parallel_session_states(session_state, modified_session_states)
473
485
 
474
486
  # Flatten step_results - handle steps that return List[StepOutput] (like Condition/Loop)
@@ -509,6 +521,7 @@ class Parallel:
509
521
  user_id: Optional[str] = None,
510
522
  workflow_run_response: Optional[WorkflowRunOutput] = None,
511
523
  store_executor_outputs: bool = True,
524
+ run_context: Optional[RunContext] = None,
512
525
  session_state: Optional[Dict[str, Any]] = None,
513
526
  workflow_session: Optional[WorkflowSession] = None,
514
527
  add_workflow_history_to_steps: Optional[bool] = False,
@@ -523,10 +536,14 @@ class Parallel:
523
536
  # Create individual session_state copies for each step to prevent race conditions
524
537
  session_state_copies = []
525
538
  for _ in range(len(self.steps)):
526
- if session_state is not None:
527
- session_state_copies.append(deepcopy(session_state))
539
+ # If using run context, no need to deepcopy the state. We want the direct reference.
540
+ if run_context is not None and run_context.session_state is not None:
541
+ session_state_copies.append(run_context.session_state)
528
542
  else:
529
- session_state_copies.append({})
543
+ if session_state is not None:
544
+ session_state_copies.append(deepcopy(session_state))
545
+ else:
546
+ session_state_copies.append({})
530
547
 
531
548
  async def execute_step_async_with_index(step_with_index):
532
549
  """Execute a single step asynchronously and preserve its original index"""
@@ -598,7 +615,7 @@ class Parallel:
598
615
  log_debug(f"Parallel step {step_name} completed")
599
616
 
600
617
  # Smart merge all session_state changes back into the original session_state
601
- if session_state is not None:
618
+ if run_context is None and session_state is not None:
602
619
  merge_parallel_session_states(session_state, modified_session_states)
603
620
 
604
621
  # Sort by original index to preserve order
@@ -632,6 +649,7 @@ class Parallel:
632
649
  workflow_run_response: Optional[WorkflowRunOutput] = None,
633
650
  step_index: Optional[Union[int, tuple]] = None,
634
651
  store_executor_outputs: bool = True,
652
+ run_context: Optional[RunContext] = None,
635
653
  session_state: Optional[Dict[str, Any]] = None,
636
654
  parent_step_id: Optional[str] = None,
637
655
  workflow_session: Optional[WorkflowSession] = None,
@@ -648,10 +666,14 @@ class Parallel:
648
666
  # Create individual session_state copies for each step to prevent race conditions
649
667
  session_state_copies = []
650
668
  for _ in range(len(self.steps)):
651
- if session_state is not None:
652
- session_state_copies.append(deepcopy(session_state))
669
+ # If using run context, no need to deepcopy the state. We want the direct reference.
670
+ if run_context is not None and run_context.session_state is not None:
671
+ session_state_copies.append(run_context.session_state)
653
672
  else:
654
- session_state_copies.append({})
673
+ if session_state is not None:
674
+ session_state_copies.append(deepcopy(session_state))
675
+ else:
676
+ session_state_copies.append({})
655
677
 
656
678
  # Considering both stream_events and stream_intermediate_steps (deprecated)
657
679
  stream_events = stream_events or stream_intermediate_steps
@@ -705,6 +727,7 @@ class Parallel:
705
727
  step_index=sub_step_index,
706
728
  store_executor_outputs=store_executor_outputs,
707
729
  session_state=step_session_state,
730
+ run_context=run_context,
708
731
  parent_step_id=parallel_step_id,
709
732
  workflow_session=workflow_session,
710
733
  add_workflow_history_to_steps=add_workflow_history_to_steps,
@@ -766,7 +789,7 @@ class Parallel:
766
789
  await asyncio.gather(*tasks, return_exceptions=True)
767
790
 
768
791
  # Merge all session_state changes back into the original session_state
769
- if session_state is not None:
792
+ if run_context is None and session_state is not None:
770
793
  merge_parallel_session_states(session_state, modified_session_states)
771
794
 
772
795
  # Flatten step_results - handle steps that return List[StepOutput] (like Condition/Loop)