agno 2.1.9__py3-none-any.whl → 2.2.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 (83) hide show
  1. agno/agent/agent.py +2048 -1204
  2. agno/culture/__init__.py +3 -0
  3. agno/culture/manager.py +954 -0
  4. agno/db/async_postgres/async_postgres.py +232 -0
  5. agno/db/async_postgres/schemas.py +15 -0
  6. agno/db/async_postgres/utils.py +58 -0
  7. agno/db/base.py +83 -6
  8. agno/db/dynamo/dynamo.py +162 -0
  9. agno/db/dynamo/schemas.py +44 -0
  10. agno/db/dynamo/utils.py +59 -0
  11. agno/db/firestore/firestore.py +231 -0
  12. agno/db/firestore/schemas.py +10 -0
  13. agno/db/firestore/utils.py +96 -0
  14. agno/db/gcs_json/gcs_json_db.py +190 -0
  15. agno/db/gcs_json/utils.py +58 -0
  16. agno/db/in_memory/in_memory_db.py +118 -0
  17. agno/db/in_memory/utils.py +58 -0
  18. agno/db/json/json_db.py +129 -0
  19. agno/db/json/utils.py +58 -0
  20. agno/db/mongo/mongo.py +222 -0
  21. agno/db/mongo/schemas.py +10 -0
  22. agno/db/mongo/utils.py +59 -0
  23. agno/db/mysql/mysql.py +232 -1
  24. agno/db/mysql/schemas.py +14 -0
  25. agno/db/mysql/utils.py +58 -0
  26. agno/db/postgres/postgres.py +242 -0
  27. agno/db/postgres/schemas.py +15 -0
  28. agno/db/postgres/utils.py +58 -0
  29. agno/db/redis/redis.py +181 -0
  30. agno/db/redis/schemas.py +14 -0
  31. agno/db/redis/utils.py +58 -0
  32. agno/db/schemas/__init__.py +2 -1
  33. agno/db/schemas/culture.py +120 -0
  34. agno/db/singlestore/schemas.py +14 -0
  35. agno/db/singlestore/singlestore.py +231 -0
  36. agno/db/singlestore/utils.py +58 -0
  37. agno/db/sqlite/schemas.py +14 -0
  38. agno/db/sqlite/sqlite.py +274 -7
  39. agno/db/sqlite/utils.py +62 -0
  40. agno/db/surrealdb/models.py +51 -1
  41. agno/db/surrealdb/surrealdb.py +154 -0
  42. agno/db/surrealdb/utils.py +61 -1
  43. agno/knowledge/reader/field_labeled_csv_reader.py +0 -2
  44. agno/memory/manager.py +28 -11
  45. agno/models/anthropic/claude.py +2 -2
  46. agno/models/message.py +0 -1
  47. agno/models/ollama/chat.py +7 -2
  48. agno/os/app.py +29 -7
  49. agno/os/interfaces/a2a/router.py +2 -2
  50. agno/os/interfaces/agui/router.py +2 -2
  51. agno/os/router.py +7 -7
  52. agno/os/routers/evals/schemas.py +31 -31
  53. agno/os/routers/health.py +6 -2
  54. agno/os/routers/knowledge/schemas.py +49 -47
  55. agno/os/routers/memory/schemas.py +16 -16
  56. agno/os/routers/metrics/schemas.py +16 -16
  57. agno/os/routers/session/session.py +382 -7
  58. agno/os/schema.py +254 -231
  59. agno/os/utils.py +1 -1
  60. agno/run/agent.py +49 -1
  61. agno/run/team.py +43 -0
  62. agno/session/summary.py +45 -13
  63. agno/session/team.py +90 -5
  64. agno/team/team.py +1118 -857
  65. agno/tools/gmail.py +59 -14
  66. agno/utils/agent.py +372 -0
  67. agno/utils/events.py +144 -2
  68. agno/utils/print_response/agent.py +10 -6
  69. agno/utils/print_response/team.py +6 -4
  70. agno/utils/print_response/workflow.py +7 -5
  71. agno/utils/team.py +9 -8
  72. agno/workflow/condition.py +17 -9
  73. agno/workflow/loop.py +18 -10
  74. agno/workflow/parallel.py +14 -6
  75. agno/workflow/router.py +17 -9
  76. agno/workflow/step.py +14 -6
  77. agno/workflow/steps.py +14 -6
  78. agno/workflow/workflow.py +245 -122
  79. {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/METADATA +60 -23
  80. {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/RECORD +83 -79
  81. {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/WHEEL +0 -0
  82. {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/licenses/LICENSE +0 -0
  83. {agno-2.1.9.dist-info → agno-2.2.0.dist-info}/top_level.txt +0 -0
agno/workflow/workflow.py CHANGED
@@ -147,6 +147,8 @@ class Workflow:
147
147
  # Stream the response from the Workflow
148
148
  stream: Optional[bool] = None
149
149
  # Stream the intermediate steps from the Workflow
150
+ stream_events: bool = False
151
+ # [Deprecated] Stream the intermediate steps from the Workflow
150
152
  stream_intermediate_steps: bool = False
151
153
  # Stream events from executors (agents/teams/functions) within steps
152
154
  stream_executor_events: bool = True
@@ -190,6 +192,7 @@ class Workflow:
190
192
  user_id: Optional[str] = None,
191
193
  debug_mode: Optional[bool] = False,
192
194
  stream: Optional[bool] = None,
195
+ stream_events: bool = False,
193
196
  stream_intermediate_steps: bool = False,
194
197
  stream_executor_events: bool = True,
195
198
  store_events: bool = False,
@@ -214,6 +217,7 @@ class Workflow:
214
217
  self.store_events = store_events
215
218
  self.events_to_skip = events_to_skip or []
216
219
  self.stream = stream
220
+ self.stream_events = stream_events
217
221
  self.stream_intermediate_steps = stream_intermediate_steps
218
222
  self.stream_executor_events = stream_executor_events
219
223
  self.store_executor_outputs = store_executor_outputs
@@ -344,10 +348,8 @@ class Workflow:
344
348
  self,
345
349
  session_id: Optional[str] = None,
346
350
  user_id: Optional[str] = None,
347
- session_state: Optional[Dict[str, Any]] = None,
348
- run_id: Optional[str] = None,
349
- ) -> Tuple[str, Optional[str], Dict[str, Any]]:
350
- """Initialize the session for the agent."""
351
+ ) -> Tuple[str, Optional[str]]:
352
+ """Initialize the session for the workflow."""
351
353
 
352
354
  if session_id is None:
353
355
  if self.session_id:
@@ -360,27 +362,25 @@ class Workflow:
360
362
  log_debug(f"Session ID: {session_id}", center=True)
361
363
 
362
364
  # Use the default user_id when necessary
363
- if user_id is None:
365
+ if user_id is None or user_id == "":
364
366
  user_id = self.user_id
365
367
 
366
- # Determine the session_state with proper precedence
367
- if session_state is None:
368
- session_state = self.session_state or {}
369
- else:
370
- # If run session_state is provided, merge agent defaults under it
371
- # This ensures run state takes precedence over agent defaults
372
- if self.session_state:
373
- from agno.utils.merge_dict import merge_dictionaries
374
-
375
- base_state = self.session_state.copy()
376
- merge_dictionaries(base_state, session_state)
377
- session_state.clear()
378
- session_state.update(base_state)
368
+ return session_id, user_id
379
369
 
380
- if user_id is not None:
370
+ def _initialize_session_state(
371
+ self,
372
+ session_state: Dict[str, Any],
373
+ user_id: Optional[str] = None,
374
+ session_id: Optional[str] = None,
375
+ run_id: Optional[str] = None,
376
+ ) -> Dict[str, Any]:
377
+ """Initialize the session state for the workflow."""
378
+ if user_id:
381
379
  session_state["current_user_id"] = user_id
382
380
  if session_id is not None:
383
381
  session_state["current_session_id"] = session_id
382
+ if run_id is not None:
383
+ session_state["current_run_id"] = run_id
384
384
 
385
385
  session_state.update(
386
386
  {
@@ -392,7 +392,7 @@ class Workflow:
392
392
  if self.name:
393
393
  session_state["workflow_name"] = self.name
394
394
 
395
- return session_id, user_id, session_state # type: ignore
395
+ return session_state
396
396
 
397
397
  def _generate_workflow_session_name(self) -> str:
398
398
  """Generate a name for the workflow session"""
@@ -502,6 +502,62 @@ class Workflow:
502
502
  raise Exception("Session not found")
503
503
  return session.session_data.get("session_state", {}) if session.session_data else {}
504
504
 
505
+ def update_session_state(
506
+ self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
507
+ ) -> Dict[str, Any]:
508
+ """
509
+ Update the session state for the given session ID.
510
+ Args:
511
+ session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
512
+ session_id: The session ID to update. If not provided, the current cached session ID is used.
513
+ Returns:
514
+ dict: The updated session state.
515
+ """
516
+ session_id = session_id or self.session_id
517
+ if session_id is None:
518
+ raise Exception("Session ID is not set")
519
+ session = self.get_session(session_id=session_id) # type: ignore
520
+ if session is None:
521
+ raise Exception("Session not found")
522
+
523
+ if session.session_data is not None and "session_state" not in session.session_data:
524
+ session.session_data["session_state"] = {}
525
+
526
+ for key, value in session_state_updates.items():
527
+ session.session_data["session_state"][key] = value # type: ignore
528
+
529
+ self.save_session(session=session)
530
+
531
+ return session.session_data["session_state"] # type: ignore
532
+
533
+ async def aupdate_session_state(
534
+ self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
535
+ ) -> Dict[str, Any]:
536
+ """
537
+ Update the session state for the given session ID (async).
538
+ Args:
539
+ session_state_updates: The updates to apply to the session state. Should be a dictionary of key-value pairs.
540
+ session_id: The session ID to update. If not provided, the current cached session ID is used.
541
+ Returns:
542
+ dict: The updated session state.
543
+ """
544
+ session_id = session_id or self.session_id
545
+ if session_id is None:
546
+ raise Exception("Session ID is not set")
547
+ session = await self.aget_session(session_id=session_id) # type: ignore
548
+ if session is None:
549
+ raise Exception("Session not found")
550
+
551
+ if session.session_data is not None and "session_state" not in session.session_data:
552
+ session.session_data["session_state"] = {} # type: ignore
553
+
554
+ for key, value in session_state_updates.items():
555
+ session.session_data["session_state"][key] = value # type: ignore
556
+
557
+ await self.asave_session(session=session)
558
+
559
+ return session.session_data["session_state"] # type: ignore
560
+
505
561
  async def adelete_session(self, session_id: str):
506
562
  """Delete the current session and save to storage"""
507
563
  if self.db is None:
@@ -615,12 +671,17 @@ class Workflow:
615
671
  if workflow_session is None:
616
672
  # Creating new session if none found
617
673
  log_debug(f"Creating new WorkflowSession: {session_id}")
674
+ session_data = {}
675
+ if self.session_state is not None:
676
+ from copy import deepcopy
677
+
678
+ session_data["session_state"] = deepcopy(self.session_state)
618
679
  workflow_session = WorkflowSession(
619
680
  session_id=session_id,
620
681
  workflow_id=self.id,
621
682
  user_id=user_id,
622
683
  workflow_data=self._get_workflow_data(),
623
- session_data={},
684
+ session_data=session_data,
624
685
  metadata=self.metadata,
625
686
  created_at=int(time()),
626
687
  )
@@ -859,7 +920,7 @@ class Workflow:
859
920
  else:
860
921
  step_type = STEP_TYPE_MAPPING[type(step)]
861
922
  step_dict = {
862
- "name": step.name if hasattr(step, "name") else step.__name__,
923
+ "name": step.name if hasattr(step, "name") else step.__name__, # type: ignore
863
924
  "description": step.description if hasattr(step, "description") else "User-defined callable step",
864
925
  "type": step_type.value,
865
926
  }
@@ -1283,7 +1344,7 @@ class Workflow:
1283
1344
  execution_input: WorkflowExecutionInput,
1284
1345
  workflow_run_response: WorkflowRunOutput,
1285
1346
  session_state: Optional[Dict[str, Any]] = None,
1286
- stream_intermediate_steps: bool = False,
1347
+ stream_events: bool = False,
1287
1348
  **kwargs: Any,
1288
1349
  ) -> Iterator[WorkflowRunOutputEvent]:
1289
1350
  """Execute a specific pipeline by name with event streaming"""
@@ -1359,7 +1420,7 @@ class Workflow:
1359
1420
  step_input,
1360
1421
  session_id=session.session_id,
1361
1422
  user_id=self.user_id,
1362
- stream_intermediate_steps=stream_intermediate_steps,
1423
+ stream_events=stream_events,
1363
1424
  stream_executor_events=self.stream_executor_events,
1364
1425
  workflow_run_response=workflow_run_response,
1365
1426
  session_state=session_state,
@@ -1588,9 +1649,31 @@ class Workflow:
1588
1649
  # For regular async functions, use the same signature inspection logic in fallback
1589
1650
  return await func(**call_kwargs) # type: ignore
1590
1651
 
1652
+ async def _aload_or_create_session(
1653
+ self, session_id: str, user_id: Optional[str], session_state: Optional[Dict[str, Any]]
1654
+ ) -> Tuple[WorkflowSession, Dict[str, Any]]:
1655
+ """Load or create session from database, update metadata, and prepare session state.
1656
+
1657
+ Returns:
1658
+ Tuple of (workflow_session, prepared_session_state)
1659
+ """
1660
+ # Read existing session from database
1661
+ if self._has_async_db():
1662
+ workflow_session = await self.aread_or_create_session(session_id=session_id, user_id=user_id)
1663
+ else:
1664
+ workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
1665
+ self._update_metadata(session=workflow_session)
1666
+
1667
+ # Update session state from DB
1668
+ _session_state = session_state or {}
1669
+ _session_state = self._load_session_state(session=workflow_session, session_state=_session_state)
1670
+
1671
+ return workflow_session, _session_state
1672
+
1591
1673
  async def _aexecute(
1592
1674
  self,
1593
- session: WorkflowSession,
1675
+ session_id: str,
1676
+ user_id: Optional[str],
1594
1677
  execution_input: WorkflowExecutionInput,
1595
1678
  workflow_run_response: WorkflowRunOutput,
1596
1679
  session_state: Optional[Dict[str, Any]] = None,
@@ -1599,6 +1682,11 @@ class Workflow:
1599
1682
  """Execute a specific pipeline by name asynchronously"""
1600
1683
  from inspect import isasyncgenfunction, iscoroutinefunction, isgeneratorfunction
1601
1684
 
1685
+ # Read existing session from database
1686
+ workflow_session, session_state = await self._aload_or_create_session(
1687
+ session_id=session_id, user_id=user_id, session_state=session_state
1688
+ )
1689
+
1602
1690
  workflow_run_response.status = RunStatus.running
1603
1691
 
1604
1692
  # Register run for cancellation tracking
@@ -1667,12 +1755,12 @@ class Workflow:
1667
1755
 
1668
1756
  step_output = await step.aexecute( # type: ignore[union-attr]
1669
1757
  step_input,
1670
- session_id=session.session_id,
1758
+ session_id=session_id,
1671
1759
  user_id=self.user_id,
1672
1760
  workflow_run_response=workflow_run_response,
1673
1761
  session_state=session_state,
1674
1762
  store_executor_outputs=self.store_executor_outputs,
1675
- workflow_session=session,
1763
+ workflow_session=workflow_session,
1676
1764
  add_workflow_history_to_steps=self.add_workflow_history_to_steps
1677
1765
  if self.add_workflow_history_to_steps
1678
1766
  else None,
@@ -1742,34 +1830,40 @@ class Workflow:
1742
1830
  workflow_run_response.content = f"Workflow execution failed: {e}"
1743
1831
  raise e
1744
1832
 
1745
- self._update_session_metrics(session=session, workflow_run_response=workflow_run_response)
1746
- session.upsert_run(run=workflow_run_response)
1833
+ self._update_session_metrics(session=workflow_session, workflow_run_response=workflow_run_response)
1834
+ workflow_session.upsert_run(run=workflow_run_response)
1747
1835
  if self._has_async_db():
1748
- await self.asave_session(session=session)
1836
+ await self.asave_session(session=workflow_session)
1749
1837
  else:
1750
- self.save_session(session=session)
1838
+ self.save_session(session=workflow_session)
1751
1839
  # Always clean up the run tracking
1752
1840
  cleanup_run(workflow_run_response.run_id) # type: ignore
1753
1841
 
1754
1842
  # Log Workflow Telemetry
1755
1843
  if self.telemetry:
1756
- await self._alog_workflow_telemetry(session_id=session.session_id, run_id=workflow_run_response.run_id)
1844
+ await self._alog_workflow_telemetry(session_id=session_id, run_id=workflow_run_response.run_id)
1757
1845
 
1758
1846
  return workflow_run_response
1759
1847
 
1760
1848
  async def _aexecute_stream(
1761
1849
  self,
1762
- session: WorkflowSession,
1850
+ session_id: str,
1851
+ user_id: Optional[str],
1763
1852
  execution_input: WorkflowExecutionInput,
1764
1853
  workflow_run_response: WorkflowRunOutput,
1765
1854
  session_state: Optional[Dict[str, Any]] = None,
1766
- stream_intermediate_steps: bool = False,
1855
+ stream_events: bool = False,
1767
1856
  websocket_handler: Optional[WebSocketHandler] = None,
1768
1857
  **kwargs: Any,
1769
1858
  ) -> AsyncIterator[WorkflowRunOutputEvent]:
1770
1859
  """Execute a specific pipeline by name with event streaming"""
1771
1860
  from inspect import isasyncgenfunction, iscoroutinefunction, isgeneratorfunction
1772
1861
 
1862
+ # Read existing session from database
1863
+ workflow_session, session_state = await self._aload_or_create_session(
1864
+ session_id=session_id, user_id=user_id, session_state=session_state
1865
+ )
1866
+
1773
1867
  workflow_run_response.status = RunStatus.running
1774
1868
 
1775
1869
  # Register run for cancellation tracking
@@ -1847,15 +1941,15 @@ class Workflow:
1847
1941
  # Execute step with streaming and yield all events
1848
1942
  async for event in step.aexecute_stream( # type: ignore[union-attr]
1849
1943
  step_input,
1850
- session_id=session.session_id,
1944
+ session_id=session_id,
1851
1945
  user_id=self.user_id,
1852
- stream_intermediate_steps=stream_intermediate_steps,
1946
+ stream_events=stream_events,
1853
1947
  stream_executor_events=self.stream_executor_events,
1854
1948
  workflow_run_response=workflow_run_response,
1855
1949
  session_state=session_state,
1856
1950
  step_index=i,
1857
1951
  store_executor_outputs=self.store_executor_outputs,
1858
- workflow_session=session,
1952
+ workflow_session=workflow_session,
1859
1953
  add_workflow_history_to_steps=self.add_workflow_history_to_steps
1860
1954
  if self.add_workflow_history_to_steps
1861
1955
  else None,
@@ -1965,7 +2059,7 @@ class Workflow:
1965
2059
  run_id=workflow_run_response.run_id or "",
1966
2060
  workflow_id=self.id,
1967
2061
  workflow_name=self.name,
1968
- session_id=session.session_id,
2062
+ session_id=session_id,
1969
2063
  error=str(e),
1970
2064
  )
1971
2065
 
@@ -1983,7 +2077,7 @@ class Workflow:
1983
2077
  run_id=workflow_run_response.run_id or "",
1984
2078
  workflow_id=self.id,
1985
2079
  workflow_name=self.name,
1986
- session_id=session.session_id,
2080
+ session_id=session_id,
1987
2081
  reason=str(e),
1988
2082
  )
1989
2083
  yield self._handle_event(
@@ -2000,7 +2094,7 @@ class Workflow:
2000
2094
  run_id=workflow_run_response.run_id or "",
2001
2095
  workflow_id=self.id,
2002
2096
  workflow_name=self.name,
2003
- session_id=session.session_id,
2097
+ session_id=session_id,
2004
2098
  error=str(e),
2005
2099
  )
2006
2100
 
@@ -2024,16 +2118,16 @@ class Workflow:
2024
2118
  yield self._handle_event(workflow_completed_event, workflow_run_response, websocket_handler=websocket_handler)
2025
2119
 
2026
2120
  # Store the completed workflow response
2027
- self._update_session_metrics(session=session, workflow_run_response=workflow_run_response)
2028
- session.upsert_run(run=workflow_run_response)
2121
+ self._update_session_metrics(session=workflow_session, workflow_run_response=workflow_run_response)
2122
+ workflow_session.upsert_run(run=workflow_run_response)
2029
2123
  if self._has_async_db():
2030
- await self.asave_session(session=session)
2124
+ await self.asave_session(session=workflow_session)
2031
2125
  else:
2032
- self.save_session(session=session)
2126
+ self.save_session(session=workflow_session)
2033
2127
 
2034
2128
  # Log Workflow Telemetry
2035
2129
  if self.telemetry:
2036
- await self._alog_workflow_telemetry(session_id=session.session_id, run_id=workflow_run_response.run_id)
2130
+ await self._alog_workflow_telemetry(session_id=session_id, run_id=workflow_run_response.run_id)
2037
2131
 
2038
2132
  # Always clean up the run tracking
2039
2133
  cleanup_run(workflow_run_response.run_id) # type: ignore
@@ -2057,19 +2151,12 @@ class Workflow:
2057
2151
 
2058
2152
  self.initialize_workflow()
2059
2153
 
2060
- session_id, user_id, session_state = self._initialize_session(
2061
- session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
2062
- )
2154
+ session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
2063
2155
 
2064
2156
  # Read existing session from database
2065
- if self._has_async_db():
2066
- workflow_session = await self.aread_or_create_session(session_id=session_id, user_id=user_id)
2067
- else:
2068
- workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
2069
- self._update_metadata(session=workflow_session)
2070
-
2071
- # Update session state from DB
2072
- session_state = self._load_session_state(session=workflow_session, session_state=session_state)
2157
+ workflow_session, session_state = await self._aload_or_create_session(
2158
+ session_id=session_id, user_id=user_id, session_state=session_state
2159
+ )
2073
2160
 
2074
2161
  self._prepare_steps()
2075
2162
 
@@ -2114,7 +2201,8 @@ class Workflow:
2114
2201
  self.save_session(session=workflow_session)
2115
2202
 
2116
2203
  await self._aexecute(
2117
- session=workflow_session,
2204
+ session_id=session_id,
2205
+ user_id=user_id,
2118
2206
  execution_input=inputs,
2119
2207
  workflow_run_response=workflow_run_response,
2120
2208
  session_state=session_state,
@@ -2150,7 +2238,7 @@ class Workflow:
2150
2238
  images: Optional[List[Image]] = None,
2151
2239
  videos: Optional[List[Video]] = None,
2152
2240
  files: Optional[List[File]] = None,
2153
- stream_intermediate_steps: bool = False,
2241
+ stream_events: bool = False,
2154
2242
  websocket_handler: Optional[WebSocketHandler] = None,
2155
2243
  **kwargs: Any,
2156
2244
  ) -> WorkflowRunOutput:
@@ -2160,19 +2248,12 @@ class Workflow:
2160
2248
 
2161
2249
  self.initialize_workflow()
2162
2250
 
2163
- session_id, user_id, session_state = self._initialize_session(
2164
- session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
2165
- )
2251
+ session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
2166
2252
 
2167
2253
  # Read existing session from database
2168
- if self._has_async_db():
2169
- workflow_session = await self.aread_or_create_session(session_id=session_id, user_id=user_id)
2170
- else:
2171
- workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
2172
- self._update_metadata(session=workflow_session)
2173
-
2174
- # Update session state from DB
2175
- session_state = self._load_session_state(session=workflow_session, session_state=session_state)
2254
+ workflow_session, session_state = await self._aload_or_create_session(
2255
+ session_id=session_id, user_id=user_id, session_state=session_state
2256
+ )
2176
2257
 
2177
2258
  self._prepare_steps()
2178
2259
 
@@ -2218,10 +2299,11 @@ class Workflow:
2218
2299
 
2219
2300
  # Execute with streaming - consume all events (they're auto-broadcast via _handle_event)
2220
2301
  async for event in self._aexecute_stream(
2302
+ session_id=session_id,
2303
+ user_id=user_id,
2221
2304
  execution_input=inputs,
2222
- session=workflow_session,
2223
2305
  workflow_run_response=workflow_run_response,
2224
- stream_intermediate_steps=stream_intermediate_steps,
2306
+ stream_events=stream_events,
2225
2307
  session_state=session_state,
2226
2308
  websocket_handler=websocket_handler,
2227
2309
  **kwargs,
@@ -2296,6 +2378,7 @@ class Workflow:
2296
2378
  videos: Optional[List[Video]] = None,
2297
2379
  files: Optional[List[File]] = None,
2298
2380
  stream: Literal[False] = False,
2381
+ stream_events: Optional[bool] = None,
2299
2382
  stream_intermediate_steps: Optional[bool] = None,
2300
2383
  background: Optional[bool] = False,
2301
2384
  ) -> WorkflowRunOutput: ...
@@ -2313,6 +2396,7 @@ class Workflow:
2313
2396
  videos: Optional[List[Video]] = None,
2314
2397
  files: Optional[List[File]] = None,
2315
2398
  stream: Literal[True] = True,
2399
+ stream_events: Optional[bool] = None,
2316
2400
  stream_intermediate_steps: Optional[bool] = None,
2317
2401
  background: Optional[bool] = False,
2318
2402
  ) -> Iterator[WorkflowRunOutputEvent]: ...
@@ -2329,6 +2413,7 @@ class Workflow:
2329
2413
  videos: Optional[List[Video]] = None,
2330
2414
  files: Optional[List[File]] = None,
2331
2415
  stream: bool = False,
2416
+ stream_events: Optional[bool] = None,
2332
2417
  stream_intermediate_steps: Optional[bool] = None,
2333
2418
  background: Optional[bool] = False,
2334
2419
  **kwargs: Any,
@@ -2346,14 +2431,16 @@ class Workflow:
2346
2431
  run_id = str(uuid4())
2347
2432
 
2348
2433
  self.initialize_workflow()
2349
- session_id, user_id, session_state = self._initialize_session(
2350
- session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
2351
- )
2434
+ session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
2352
2435
 
2353
2436
  # Read existing session from database
2354
2437
  workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
2355
2438
  self._update_metadata(session=workflow_session)
2356
2439
 
2440
+ # Initialize session state
2441
+ session_state = self._initialize_session_state(
2442
+ session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_id
2443
+ )
2357
2444
  # Update session state from DB
2358
2445
  session_state = self._load_session_state(session=workflow_session, session_state=session_state)
2359
2446
 
@@ -2361,11 +2448,13 @@ class Workflow:
2361
2448
 
2362
2449
  # Use simple defaults
2363
2450
  stream = stream or self.stream or False
2364
- stream_intermediate_steps = stream_intermediate_steps or self.stream_intermediate_steps or False
2451
+ stream_events = (stream_events or stream_intermediate_steps) or (
2452
+ self.stream_events or self.stream_intermediate_steps
2453
+ )
2365
2454
 
2366
- # Can't have stream_intermediate_steps if stream is False
2367
- if not stream:
2368
- stream_intermediate_steps = False
2455
+ # Can't stream events if streaming is disabled
2456
+ if stream is False:
2457
+ stream_events = False
2369
2458
 
2370
2459
  log_debug(f"Stream: {stream}")
2371
2460
  log_debug(f"Total steps: {self._get_step_count()}")
@@ -2402,7 +2491,7 @@ class Workflow:
2402
2491
  session=workflow_session,
2403
2492
  execution_input=inputs, # type: ignore[arg-type]
2404
2493
  workflow_run_response=workflow_run_response,
2405
- stream_intermediate_steps=stream_intermediate_steps,
2494
+ stream_events=stream_events,
2406
2495
  session_state=session_state,
2407
2496
  **kwargs,
2408
2497
  )
@@ -2428,13 +2517,14 @@ class Workflow:
2428
2517
  videos: Optional[List[Video]] = None,
2429
2518
  files: Optional[List[File]] = None,
2430
2519
  stream: Literal[False] = False,
2520
+ stream_events: Optional[bool] = None,
2431
2521
  stream_intermediate_steps: Optional[bool] = None,
2432
2522
  background: Optional[bool] = False,
2433
2523
  websocket: Optional[WebSocket] = None,
2434
2524
  ) -> WorkflowRunOutput: ...
2435
2525
 
2436
2526
  @overload
2437
- async def arun(
2527
+ def arun(
2438
2528
  self,
2439
2529
  input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
2440
2530
  additional_data: Optional[Dict[str, Any]] = None,
@@ -2446,12 +2536,13 @@ class Workflow:
2446
2536
  videos: Optional[List[Video]] = None,
2447
2537
  files: Optional[List[File]] = None,
2448
2538
  stream: Literal[True] = True,
2539
+ stream_events: Optional[bool] = None,
2449
2540
  stream_intermediate_steps: Optional[bool] = None,
2450
2541
  background: Optional[bool] = False,
2451
2542
  websocket: Optional[WebSocket] = None,
2452
2543
  ) -> AsyncIterator[WorkflowRunOutputEvent]: ...
2453
2544
 
2454
- async def arun(
2545
+ def arun( # type: ignore
2455
2546
  self,
2456
2547
  input: Optional[Union[str, Dict[str, Any], List[Any], BaseModel, List[Message]]] = None,
2457
2548
  additional_data: Optional[Dict[str, Any]] = None,
@@ -2463,6 +2554,7 @@ class Workflow:
2463
2554
  videos: Optional[List[Video]] = None,
2464
2555
  files: Optional[List[File]] = None,
2465
2556
  stream: bool = False,
2557
+ stream_events: Optional[bool] = None,
2466
2558
  stream_intermediate_steps: Optional[bool] = False,
2467
2559
  background: Optional[bool] = False,
2468
2560
  websocket: Optional[WebSocket] = None,
@@ -2480,8 +2572,11 @@ class Workflow:
2480
2572
 
2481
2573
  if background:
2482
2574
  if stream and websocket:
2575
+ # Consider both stream_events and stream_intermediate_steps (deprecated)
2576
+ stream_events = stream_events or stream_intermediate_steps or False
2577
+
2483
2578
  # Background + Streaming + WebSocket = Real-time events
2484
- return await self._arun_background_stream(
2579
+ return self._arun_background_stream( # type: ignore
2485
2580
  input=input,
2486
2581
  additional_data=additional_data,
2487
2582
  user_id=user_id,
@@ -2491,7 +2586,7 @@ class Workflow:
2491
2586
  images=images,
2492
2587
  videos=videos,
2493
2588
  files=files,
2494
- stream_intermediate_steps=stream_intermediate_steps or False,
2589
+ stream_events=stream_events,
2495
2590
  websocket_handler=websocket_handler,
2496
2591
  **kwargs,
2497
2592
  )
@@ -2500,7 +2595,7 @@ class Workflow:
2500
2595
  raise ValueError("Background streaming execution requires a WebSocket for real-time events")
2501
2596
  else:
2502
2597
  # Background + Non-streaming = Polling (existing)
2503
- return await self._arun_background(
2598
+ return self._arun_background( # type: ignore
2504
2599
  input=input,
2505
2600
  additional_data=additional_data,
2506
2601
  user_id=user_id,
@@ -2518,29 +2613,19 @@ class Workflow:
2518
2613
  run_id = str(uuid4())
2519
2614
 
2520
2615
  self.initialize_workflow()
2521
- session_id, user_id, session_state = self._initialize_session(
2522
- session_id=session_id, user_id=user_id, session_state=session_state, run_id=run_id
2523
- )
2524
-
2525
- # Read existing session from database
2526
- if self._has_async_db():
2527
- workflow_session = await self.aread_or_create_session(session_id=session_id, user_id=user_id)
2528
- else:
2529
- workflow_session = self.read_or_create_session(session_id=session_id, user_id=user_id)
2530
- self._update_metadata(session=workflow_session)
2531
-
2532
- # Update session state from DB
2533
- session_state = self._load_session_state(session=workflow_session, session_state=session_state)
2616
+ session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
2534
2617
 
2535
2618
  log_debug(f"Async Workflow Run Start: {self.name}", center=True)
2536
2619
 
2537
2620
  # Use simple defaults
2538
2621
  stream = stream or self.stream or False
2539
- stream_intermediate_steps = stream_intermediate_steps or self.stream_intermediate_steps or False
2622
+ stream_events = (stream_events or stream_intermediate_steps) or (
2623
+ self.stream_events or self.stream_intermediate_steps
2624
+ )
2540
2625
 
2541
- # Can't have stream_intermediate_steps if stream is False
2542
- if not stream:
2543
- stream_intermediate_steps = False
2626
+ # Can't stream events if streaming is disabled
2627
+ if stream is False:
2628
+ stream_events = False
2544
2629
 
2545
2630
  log_debug(f"Stream: {stream}")
2546
2631
 
@@ -2572,21 +2657,23 @@ class Workflow:
2572
2657
  self.update_agents_and_teams_session_info()
2573
2658
 
2574
2659
  if stream:
2575
- return self._aexecute_stream(
2660
+ return self._aexecute_stream( # type: ignore
2576
2661
  execution_input=inputs,
2577
2662
  workflow_run_response=workflow_run_response,
2578
- session=workflow_session,
2579
- stream_intermediate_steps=stream_intermediate_steps,
2663
+ session_id=session_id,
2664
+ user_id=user_id,
2665
+ stream_events=stream_events,
2580
2666
  websocket=websocket,
2581
2667
  files=files,
2582
2668
  session_state=session_state,
2583
2669
  **kwargs,
2584
2670
  )
2585
2671
  else:
2586
- return await self._aexecute(
2672
+ return self._aexecute( # type: ignore
2587
2673
  execution_input=inputs,
2588
2674
  workflow_run_response=workflow_run_response,
2589
- session=workflow_session,
2675
+ session_id=session_id,
2676
+ user_id=user_id,
2590
2677
  websocket=websocket,
2591
2678
  files=files,
2592
2679
  session_state=session_state,
@@ -2601,7 +2688,7 @@ class Workflow:
2601
2688
  if callable(step) and hasattr(step, "__name__"):
2602
2689
  step_name = step.__name__
2603
2690
  log_debug(f"Step {i + 1}: Wrapping callable function '{step_name}'")
2604
- prepared_steps.append(Step(name=step_name, description="User-defined callable step", executor=step))
2691
+ prepared_steps.append(Step(name=step_name, description="User-defined callable step", executor=step)) # type: ignore
2605
2692
  elif isinstance(step, Agent):
2606
2693
  step_name = step.name or f"step_{i + 1}"
2607
2694
  log_debug(f"Step {i + 1}: Agent '{step_name}'")
@@ -2632,6 +2719,7 @@ class Workflow:
2632
2719
  videos: Optional[List[Video]] = None,
2633
2720
  files: Optional[List[File]] = None,
2634
2721
  stream: Optional[bool] = None,
2722
+ stream_events: Optional[bool] = None,
2635
2723
  stream_intermediate_steps: Optional[bool] = None,
2636
2724
  markdown: bool = True,
2637
2725
  show_time: bool = True,
@@ -2650,11 +2738,12 @@ class Workflow:
2650
2738
  images: Image input
2651
2739
  videos: Video input
2652
2740
  stream: Whether to stream the response content
2653
- stream_intermediate_steps: Whether to stream intermediate steps
2741
+ stream_events: Whether to stream intermediate steps
2654
2742
  markdown: Whether to render content as markdown
2655
2743
  show_time: Whether to show execution time
2656
2744
  show_step_details: Whether to show individual step outputs
2657
2745
  console: Rich console instance (optional)
2746
+ (deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
2658
2747
  """
2659
2748
  if self._has_async_db():
2660
2749
  raise Exception("`print_response()` is not supported with an async DB. Please use `aprint_response()`.")
@@ -2662,8 +2751,19 @@ class Workflow:
2662
2751
  if stream is None:
2663
2752
  stream = self.stream or False
2664
2753
 
2665
- if stream_intermediate_steps is None:
2666
- stream_intermediate_steps = self.stream_intermediate_steps or False
2754
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
2755
+ stream_events = stream_events or stream_intermediate_steps
2756
+
2757
+ # Can't stream events if streaming is disabled
2758
+ if stream is False:
2759
+ stream_events = False
2760
+
2761
+ if stream_events is None:
2762
+ stream_events = (
2763
+ False
2764
+ if (self.stream_events is None and self.stream_intermediate_steps is None)
2765
+ else (self.stream_intermediate_steps or self.stream_events)
2766
+ )
2667
2767
 
2668
2768
  if stream:
2669
2769
  print_response_stream(
@@ -2676,7 +2776,7 @@ class Workflow:
2676
2776
  images=images,
2677
2777
  videos=videos,
2678
2778
  files=files,
2679
- stream_intermediate_steps=stream_intermediate_steps,
2779
+ stream_events=stream_events,
2680
2780
  markdown=markdown,
2681
2781
  show_time=show_time,
2682
2782
  show_step_details=show_step_details,
@@ -2712,6 +2812,7 @@ class Workflow:
2712
2812
  videos: Optional[List[Video]] = None,
2713
2813
  files: Optional[List[File]] = None,
2714
2814
  stream: Optional[bool] = None,
2815
+ stream_events: Optional[bool] = None,
2715
2816
  stream_intermediate_steps: Optional[bool] = None,
2716
2817
  markdown: bool = True,
2717
2818
  show_time: bool = True,
@@ -2729,18 +2830,30 @@ class Workflow:
2729
2830
  audio: Audio input
2730
2831
  images: Image input
2731
2832
  videos: Video input
2732
- stream_intermediate_steps: Whether to stream intermediate steps
2733
2833
  stream: Whether to stream the response content
2834
+ stream_events: Whether to stream intermediate steps
2734
2835
  markdown: Whether to render content as markdown
2735
2836
  show_time: Whether to show execution time
2736
2837
  show_step_details: Whether to show individual step outputs
2737
2838
  console: Rich console instance (optional)
2839
+ (deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
2738
2840
  """
2739
2841
  if stream is None:
2740
2842
  stream = self.stream or False
2741
2843
 
2742
- if stream_intermediate_steps is None:
2743
- stream_intermediate_steps = self.stream_intermediate_steps or False
2844
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
2845
+ stream_events = stream_events or stream_intermediate_steps
2846
+
2847
+ # Can't stream events if streaming is disabled
2848
+ if stream is False:
2849
+ stream_events = False
2850
+
2851
+ if stream_events is None:
2852
+ stream_events = (
2853
+ False
2854
+ if (self.stream_events is None and self.stream_intermediate_steps is None)
2855
+ else (self.stream_intermediate_steps or self.stream_events)
2856
+ )
2744
2857
 
2745
2858
  if stream:
2746
2859
  await aprint_response_stream(
@@ -2753,7 +2866,7 @@ class Workflow:
2753
2866
  images=images,
2754
2867
  videos=videos,
2755
2868
  files=files,
2756
- stream_intermediate_steps=stream_intermediate_steps,
2869
+ stream_events=stream_events,
2757
2870
  markdown=markdown,
2758
2871
  show_time=show_time,
2759
2872
  show_step_details=show_step_details,
@@ -2930,7 +3043,7 @@ class Workflow:
2930
3043
 
2931
3044
  # If it's a team, update all members
2932
3045
  if hasattr(active_executor, "members"):
2933
- for member in active_executor.members:
3046
+ for member in active_executor.members: # type: ignore
2934
3047
  if hasattr(member, "workflow_id"):
2935
3048
  member.workflow_id = self.id
2936
3049
 
@@ -2986,6 +3099,7 @@ class Workflow:
2986
3099
  user: str = "User",
2987
3100
  emoji: str = ":technologist:",
2988
3101
  stream: Optional[bool] = None,
3102
+ stream_events: Optional[bool] = None,
2989
3103
  stream_intermediate_steps: Optional[bool] = None,
2990
3104
  markdown: bool = True,
2991
3105
  show_time: bool = True,
@@ -3006,11 +3120,12 @@ class Workflow:
3006
3120
  user: Display name for the user in the CLI prompt. Defaults to "User".
3007
3121
  emoji: Emoji to display next to the user name in prompts. Defaults to ":technologist:".
3008
3122
  stream: Whether to stream the workflow response. If None, uses workflow default.
3009
- stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
3123
+ stream_events: Whether to stream intermediate step outputs. If None, uses workflow default.
3010
3124
  markdown: Whether to render output as markdown. Defaults to True.
3011
3125
  show_time: Whether to display timestamps in the output. Defaults to True.
3012
3126
  show_step_details: Whether to show detailed step information. Defaults to True.
3013
3127
  exit_on: List of commands that will exit the CLI. Defaults to ["exit", "quit", "bye", "stop"].
3128
+ (deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
3014
3129
  **kwargs: Additional keyword arguments passed to the workflow's print_response method.
3015
3130
 
3016
3131
  Returns:
@@ -3019,11 +3134,14 @@ class Workflow:
3019
3134
 
3020
3135
  from rich.prompt import Prompt
3021
3136
 
3137
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
3138
+ stream_events = stream_events or stream_intermediate_steps or False
3139
+
3022
3140
  if input:
3023
3141
  self.print_response(
3024
3142
  input=input,
3025
3143
  stream=stream,
3026
- stream_intermediate_steps=stream_intermediate_steps,
3144
+ stream_events=stream_events,
3027
3145
  markdown=markdown,
3028
3146
  show_time=show_time,
3029
3147
  show_step_details=show_step_details,
@@ -3041,7 +3159,7 @@ class Workflow:
3041
3159
  self.print_response(
3042
3160
  input=message,
3043
3161
  stream=stream,
3044
- stream_intermediate_steps=stream_intermediate_steps,
3162
+ stream_events=stream_events,
3045
3163
  markdown=markdown,
3046
3164
  show_time=show_time,
3047
3165
  show_step_details=show_step_details,
@@ -3058,6 +3176,7 @@ class Workflow:
3058
3176
  user: str = "User",
3059
3177
  emoji: str = ":technologist:",
3060
3178
  stream: Optional[bool] = None,
3179
+ stream_events: Optional[bool] = None,
3061
3180
  stream_intermediate_steps: Optional[bool] = None,
3062
3181
  markdown: bool = True,
3063
3182
  show_time: bool = True,
@@ -3078,11 +3197,12 @@ class Workflow:
3078
3197
  user: Display name for the user in the CLI prompt. Defaults to "User".
3079
3198
  emoji: Emoji to display next to the user name in prompts. Defaults to ":technologist:".
3080
3199
  stream: Whether to stream the workflow response. If None, uses workflow default.
3081
- stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
3200
+ stream_events: Whether to stream events from the workflow. If None, uses workflow default.
3082
3201
  markdown: Whether to render output as markdown. Defaults to True.
3083
3202
  show_time: Whether to display timestamps in the output. Defaults to True.
3084
3203
  show_step_details: Whether to show detailed step information. Defaults to True.
3085
3204
  exit_on: List of commands that will exit the CLI. Defaults to ["exit", "quit", "bye", "stop"].
3205
+ (deprecated) stream_intermediate_steps: Whether to stream intermediate step outputs. If None, uses workflow default.
3086
3206
  **kwargs: Additional keyword arguments passed to the workflow's print_response method.
3087
3207
 
3088
3208
  Returns:
@@ -3091,11 +3211,14 @@ class Workflow:
3091
3211
 
3092
3212
  from rich.prompt import Prompt
3093
3213
 
3214
+ # Considering both stream_events and stream_intermediate_steps (deprecated)
3215
+ stream_events = stream_events or stream_intermediate_steps or False
3216
+
3094
3217
  if input:
3095
3218
  await self.aprint_response(
3096
3219
  input=input,
3097
3220
  stream=stream,
3098
- stream_intermediate_steps=stream_intermediate_steps,
3221
+ stream_events=stream_events,
3099
3222
  markdown=markdown,
3100
3223
  show_time=show_time,
3101
3224
  show_step_details=show_step_details,
@@ -3113,7 +3236,7 @@ class Workflow:
3113
3236
  await self.aprint_response(
3114
3237
  input=message,
3115
3238
  stream=stream,
3116
- stream_intermediate_steps=stream_intermediate_steps,
3239
+ stream_events=stream_events,
3117
3240
  markdown=markdown,
3118
3241
  show_time=show_time,
3119
3242
  show_step_details=show_step_details,