agno 2.2.13__py3-none-any.whl → 2.3.1__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 (92) hide show
  1. agno/agent/agent.py +197 -110
  2. agno/api/api.py +2 -0
  3. agno/db/base.py +26 -0
  4. agno/db/dynamo/dynamo.py +8 -0
  5. agno/db/dynamo/schemas.py +1 -0
  6. agno/db/firestore/firestore.py +8 -0
  7. agno/db/firestore/schemas.py +1 -0
  8. agno/db/gcs_json/gcs_json_db.py +8 -0
  9. agno/db/in_memory/in_memory_db.py +8 -1
  10. agno/db/json/json_db.py +8 -0
  11. agno/db/migrations/manager.py +199 -0
  12. agno/db/migrations/versions/__init__.py +0 -0
  13. agno/db/migrations/versions/v2_3_0.py +938 -0
  14. agno/db/mongo/async_mongo.py +16 -6
  15. agno/db/mongo/mongo.py +11 -0
  16. agno/db/mongo/schemas.py +3 -0
  17. agno/db/mongo/utils.py +17 -0
  18. agno/db/mysql/mysql.py +76 -3
  19. agno/db/mysql/schemas.py +20 -10
  20. agno/db/postgres/async_postgres.py +99 -25
  21. agno/db/postgres/postgres.py +75 -6
  22. agno/db/postgres/schemas.py +30 -20
  23. agno/db/redis/redis.py +15 -2
  24. agno/db/redis/schemas.py +4 -0
  25. agno/db/schemas/memory.py +13 -0
  26. agno/db/singlestore/schemas.py +11 -0
  27. agno/db/singlestore/singlestore.py +79 -5
  28. agno/db/sqlite/async_sqlite.py +97 -19
  29. agno/db/sqlite/schemas.py +10 -0
  30. agno/db/sqlite/sqlite.py +79 -2
  31. agno/db/surrealdb/surrealdb.py +8 -0
  32. agno/knowledge/chunking/semantic.py +7 -2
  33. agno/knowledge/embedder/nebius.py +1 -1
  34. agno/knowledge/knowledge.py +57 -86
  35. agno/knowledge/reader/csv_reader.py +7 -9
  36. agno/knowledge/reader/docx_reader.py +5 -5
  37. agno/knowledge/reader/field_labeled_csv_reader.py +16 -18
  38. agno/knowledge/reader/json_reader.py +5 -4
  39. agno/knowledge/reader/markdown_reader.py +8 -8
  40. agno/knowledge/reader/pdf_reader.py +11 -11
  41. agno/knowledge/reader/pptx_reader.py +5 -5
  42. agno/knowledge/reader/s3_reader.py +3 -3
  43. agno/knowledge/reader/text_reader.py +8 -8
  44. agno/knowledge/reader/web_search_reader.py +1 -48
  45. agno/knowledge/reader/website_reader.py +10 -10
  46. agno/models/anthropic/claude.py +319 -28
  47. agno/models/aws/claude.py +32 -0
  48. agno/models/azure/openai_chat.py +19 -10
  49. agno/models/base.py +612 -545
  50. agno/models/cerebras/cerebras.py +8 -11
  51. agno/models/cohere/chat.py +27 -1
  52. agno/models/google/gemini.py +39 -7
  53. agno/models/groq/groq.py +25 -11
  54. agno/models/meta/llama.py +20 -9
  55. agno/models/meta/llama_openai.py +3 -19
  56. agno/models/nebius/nebius.py +4 -4
  57. agno/models/openai/chat.py +30 -14
  58. agno/models/openai/responses.py +10 -13
  59. agno/models/response.py +1 -0
  60. agno/models/vertexai/claude.py +26 -0
  61. agno/os/app.py +8 -19
  62. agno/os/router.py +54 -0
  63. agno/os/routers/knowledge/knowledge.py +2 -2
  64. agno/os/schema.py +2 -2
  65. agno/session/agent.py +57 -92
  66. agno/session/summary.py +1 -1
  67. agno/session/team.py +62 -112
  68. agno/session/workflow.py +353 -57
  69. agno/team/team.py +227 -125
  70. agno/tools/models/nebius.py +5 -5
  71. agno/tools/models_labs.py +20 -10
  72. agno/tools/nano_banana.py +151 -0
  73. agno/tools/yfinance.py +12 -11
  74. agno/utils/http.py +111 -0
  75. agno/utils/media.py +11 -0
  76. agno/utils/models/claude.py +8 -0
  77. agno/utils/print_response/agent.py +33 -12
  78. agno/utils/print_response/team.py +22 -12
  79. agno/vectordb/couchbase/couchbase.py +6 -2
  80. agno/workflow/condition.py +13 -0
  81. agno/workflow/loop.py +13 -0
  82. agno/workflow/parallel.py +13 -0
  83. agno/workflow/router.py +13 -0
  84. agno/workflow/step.py +120 -20
  85. agno/workflow/steps.py +13 -0
  86. agno/workflow/workflow.py +76 -63
  87. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/METADATA +6 -2
  88. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/RECORD +91 -88
  89. agno/tools/googlesearch.py +0 -98
  90. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/WHEEL +0 -0
  91. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/licenses/LICENSE +0 -0
  92. {agno-2.2.13.dist-info → agno-2.3.1.dist-info}/top_level.txt +0 -0
@@ -25,6 +25,7 @@ def print_response(
25
25
  show_message: bool = True,
26
26
  show_reasoning: bool = True,
27
27
  show_full_reasoning: bool = False,
28
+ show_member_responses: Optional[bool] = None,
28
29
  tags_to_include_in_markdown: Optional[Set[str]] = None,
29
30
  session_id: Optional[str] = None,
30
31
  session_state: Optional[Dict[str, Any]] = None,
@@ -85,6 +86,7 @@ def print_response(
85
86
  videos=videos,
86
87
  files=files,
87
88
  stream=False,
89
+ stream_events=True,
88
90
  session_id=session_id,
89
91
  session_state=session_state,
90
92
  user_id=user_id,
@@ -152,7 +154,7 @@ def print_response(
152
154
 
153
155
  if isinstance(run_response, TeamRunOutput):
154
156
  # Handle member responses
155
- if team.show_members_responses:
157
+ if show_member_responses:
156
158
  for member_response in run_response.member_responses:
157
159
  # Handle member reasoning
158
160
  reasoning_steps = []
@@ -323,6 +325,7 @@ def print_response_stream(
323
325
  show_message: bool = True,
324
326
  show_reasoning: bool = True,
325
327
  show_full_reasoning: bool = False,
328
+ show_member_responses: Optional[bool] = None,
326
329
  tags_to_include_in_markdown: Optional[Set[str]] = None,
327
330
  session_id: Optional[str] = None,
328
331
  session_state: Optional[Dict[str, Any]] = None,
@@ -412,7 +415,7 @@ def print_response_stream(
412
415
  add_session_state_to_context=add_session_state_to_context,
413
416
  metadata=metadata,
414
417
  debug_mode=debug_mode,
415
- yield_run_response=True,
418
+ yield_run_output=True,
416
419
  **kwargs,
417
420
  )
418
421
 
@@ -470,7 +473,7 @@ def print_response_stream(
470
473
  team_tool_calls.append(tool)
471
474
 
472
475
  # Collect member tool calls, avoiding duplicates
473
- if hasattr(resp, "member_responses") and resp.member_responses:
476
+ if show_member_responses and hasattr(resp, "member_responses") and resp.member_responses:
474
477
  for member_response in resp.member_responses:
475
478
  member_id = None
476
479
  if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
@@ -532,7 +535,9 @@ def print_response_stream(
532
535
  panels.append(status)
533
536
 
534
537
  # Process member responses and their tool calls
535
- for member_response in resp.member_responses if hasattr(resp, "member_responses") else []:
538
+ for member_response in (
539
+ resp.member_responses if show_member_responses and hasattr(resp, "member_responses") else []
540
+ ):
536
541
  member_id = None
537
542
  member_name = "Team Member"
538
543
  if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
@@ -565,7 +570,7 @@ def print_response_stream(
565
570
  panels.append(member_tool_calls_panel)
566
571
 
567
572
  # Process member response content
568
- if team.show_members_responses and member_id is not None:
573
+ if show_member_responses and member_id is not None:
569
574
  show_markdown = False
570
575
  if markdown:
571
576
  show_markdown = True
@@ -708,7 +713,7 @@ def print_response_stream(
708
713
  final_panels.append(thinking_panel)
709
714
 
710
715
  # Add member tool calls and responses in correct order
711
- if run_response is not None and hasattr(run_response, "member_responses"):
716
+ if show_member_responses and run_response is not None and hasattr(run_response, "member_responses"):
712
717
  for i, member_response in enumerate(run_response.member_responses): # type: ignore
713
718
  member_id = None
714
719
  if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
@@ -857,6 +862,7 @@ async def aprint_response(
857
862
  show_message: bool = True,
858
863
  show_reasoning: bool = True,
859
864
  show_full_reasoning: bool = False,
865
+ show_member_responses: Optional[bool] = None,
860
866
  tags_to_include_in_markdown: Optional[Set[str]] = None,
861
867
  session_id: Optional[str] = None,
862
868
  session_state: Optional[Dict[str, Any]] = None,
@@ -917,6 +923,7 @@ async def aprint_response(
917
923
  videos=videos,
918
924
  files=files,
919
925
  stream=False,
926
+ stream_events=True,
920
927
  session_id=session_id,
921
928
  session_state=session_state,
922
929
  user_id=user_id,
@@ -984,7 +991,7 @@ async def aprint_response(
984
991
 
985
992
  if isinstance(run_response, TeamRunOutput):
986
993
  # Handle member responses
987
- if team.show_members_responses:
994
+ if show_member_responses:
988
995
  for member_response in run_response.member_responses:
989
996
  # Handle member reasoning
990
997
  reasoning_steps = []
@@ -1153,6 +1160,7 @@ async def aprint_response_stream(
1153
1160
  show_message: bool = True,
1154
1161
  show_reasoning: bool = True,
1155
1162
  show_full_reasoning: bool = False,
1163
+ show_member_responses: Optional[bool] = None,
1156
1164
  tags_to_include_in_markdown: Optional[Set[str]] = None,
1157
1165
  session_id: Optional[str] = None,
1158
1166
  session_state: Optional[Dict[str, Any]] = None,
@@ -1252,7 +1260,7 @@ async def aprint_response_stream(
1252
1260
  dependencies=dependencies,
1253
1261
  metadata=metadata,
1254
1262
  debug_mode=debug_mode,
1255
- yield_run_response=True,
1263
+ yield_run_output=True,
1256
1264
  **kwargs,
1257
1265
  ):
1258
1266
  if team_markdown is None:
@@ -1299,7 +1307,7 @@ async def aprint_response_stream(
1299
1307
  team_tool_calls.append(tool)
1300
1308
 
1301
1309
  # Collect member tool calls, avoiding duplicates
1302
- if hasattr(resp, "member_responses") and resp.member_responses:
1310
+ if show_member_responses and hasattr(resp, "member_responses") and resp.member_responses:
1303
1311
  for member_response in resp.member_responses:
1304
1312
  member_id = None
1305
1313
  if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
@@ -1360,7 +1368,9 @@ async def aprint_response_stream(
1360
1368
  panels.append(status)
1361
1369
 
1362
1370
  # Process member responses and their tool calls
1363
- for member_response in resp.member_responses if hasattr(resp, "member_responses") else []:
1371
+ for member_response in (
1372
+ resp.member_responses if show_member_responses and hasattr(resp, "member_responses") else []
1373
+ ):
1364
1374
  member_id = None
1365
1375
  member_name = "Team Member"
1366
1376
  if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
@@ -1393,7 +1403,7 @@ async def aprint_response_stream(
1393
1403
  panels.append(member_tool_calls_panel)
1394
1404
 
1395
1405
  # Process member response content
1396
- if team.show_members_responses and member_id is not None:
1406
+ if show_member_responses and member_id is not None:
1397
1407
  show_markdown = False
1398
1408
  if markdown:
1399
1409
  show_markdown = True
@@ -1537,7 +1547,7 @@ async def aprint_response_stream(
1537
1547
  final_panels.append(thinking_panel)
1538
1548
 
1539
1549
  # Add member tool calls and responses in correct order
1540
- if run_response is not None and hasattr(run_response, "member_responses"):
1550
+ if show_member_responses and run_response is not None and hasattr(run_response, "member_responses"):
1541
1551
  for i, member_response in enumerate(run_response.member_responses):
1542
1552
  member_id = None
1543
1553
  if isinstance(member_response, RunOutput) and member_response.agent_id is not None:
@@ -6,7 +6,6 @@ from typing import Any, Dict, List, Optional, Union
6
6
  from agno.filters import FilterExpr
7
7
  from agno.knowledge.document import Document
8
8
  from agno.knowledge.embedder import Embedder
9
- from agno.knowledge.embedder.openai import OpenAIEmbedder
10
9
  from agno.utils.log import log_debug, log_info, log_warning, logger
11
10
  from agno.vectordb.base import VectorDb
12
11
 
@@ -62,7 +61,7 @@ class CouchbaseSearch(VectorDb):
62
61
  couchbase_connection_string: str,
63
62
  cluster_options: ClusterOptions,
64
63
  search_index: Union[str, SearchIndex],
65
- embedder: Embedder = OpenAIEmbedder(),
64
+ embedder: Optional[Embedder] = None,
66
65
  overwrite: bool = False,
67
66
  is_global_level_index: bool = False,
68
67
  wait_until_index_ready: float = 0,
@@ -97,6 +96,11 @@ class CouchbaseSearch(VectorDb):
97
96
  self.collection_name = collection_name
98
97
  self.connection_string = couchbase_connection_string
99
98
  self.cluster_options = cluster_options
99
+ if embedder is None:
100
+ from agno.knowledge.embedder.openai import OpenAIEmbedder
101
+
102
+ embedder = OpenAIEmbedder()
103
+ log_info("Embedder not provided, using OpenAIEmbedder as default.")
100
104
  self.embedder = embedder
101
105
  self.overwrite = overwrite
102
106
  self.is_global_level_index = is_global_level_index
@@ -1,4 +1,5 @@
1
1
  import inspect
2
+ import warnings
2
3
  from dataclasses import dataclass
3
4
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
4
5
  from uuid import uuid4
@@ -313,6 +314,12 @@ class Condition:
313
314
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
314
315
 
315
316
  # Considering both stream_events and stream_intermediate_steps (deprecated)
317
+ if stream_intermediate_steps is not None:
318
+ warnings.warn(
319
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
320
+ DeprecationWarning,
321
+ stacklevel=2,
322
+ )
316
323
  stream_events = stream_events or stream_intermediate_steps
317
324
 
318
325
  if stream_events and workflow_run_response:
@@ -595,6 +602,12 @@ class Condition:
595
602
  log_debug(f"Condition {self.name} evaluated to: {condition_result}")
596
603
 
597
604
  # Considering both stream_events and stream_intermediate_steps (deprecated)
605
+ if stream_intermediate_steps is not None:
606
+ warnings.warn(
607
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
608
+ DeprecationWarning,
609
+ stacklevel=2,
610
+ )
598
611
  stream_events = stream_events or stream_intermediate_steps
599
612
 
600
613
  if stream_events and workflow_run_response:
agno/workflow/loop.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import inspect
2
+ import warnings
2
3
  from dataclasses import dataclass
3
4
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
4
5
  from uuid import uuid4
@@ -251,6 +252,12 @@ class Loop:
251
252
  loop_step_id = str(uuid4())
252
253
 
253
254
  # Considering both stream_events and stream_intermediate_steps (deprecated)
255
+ if stream_intermediate_steps is not None:
256
+ warnings.warn(
257
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
258
+ DeprecationWarning,
259
+ stacklevel=2,
260
+ )
254
261
  stream_events = stream_events or stream_intermediate_steps
255
262
 
256
263
  if stream_events and workflow_run_response:
@@ -556,6 +563,12 @@ class Loop:
556
563
  self._prepare_steps()
557
564
 
558
565
  # Considering both stream_events and stream_intermediate_steps (deprecated)
566
+ if stream_intermediate_steps is not None:
567
+ warnings.warn(
568
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
569
+ DeprecationWarning,
570
+ stacklevel=2,
571
+ )
559
572
  stream_events = stream_events or stream_intermediate_steps
560
573
 
561
574
  if stream_events and workflow_run_response:
agno/workflow/parallel.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import warnings
2
3
  from concurrent.futures import ThreadPoolExecutor, as_completed
3
4
  from copy import deepcopy
4
5
  from dataclasses import dataclass
@@ -356,6 +357,12 @@ class Parallel:
356
357
  session_state_copies.append({})
357
358
 
358
359
  # Considering both stream_events and stream_intermediate_steps (deprecated)
360
+ if stream_intermediate_steps is not None:
361
+ warnings.warn(
362
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
363
+ DeprecationWarning,
364
+ stacklevel=2,
365
+ )
359
366
  stream_events = stream_events or stream_intermediate_steps
360
367
 
361
368
  if stream_events and workflow_run_response:
@@ -676,6 +683,12 @@ class Parallel:
676
683
  session_state_copies.append({})
677
684
 
678
685
  # Considering both stream_events and stream_intermediate_steps (deprecated)
686
+ if stream_intermediate_steps is not None:
687
+ warnings.warn(
688
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
689
+ DeprecationWarning,
690
+ stacklevel=2,
691
+ )
679
692
  stream_events = stream_events or stream_intermediate_steps
680
693
 
681
694
  if stream_events and workflow_run_response:
agno/workflow/router.py CHANGED
@@ -1,4 +1,5 @@
1
1
  import inspect
2
+ import warnings
2
3
  from dataclasses import dataclass
3
4
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
4
5
  from uuid import uuid4
@@ -300,6 +301,12 @@ class Router:
300
301
  log_debug(f"Router {self.name}: Selected {len(steps_to_execute)} steps to execute")
301
302
 
302
303
  # Considering both stream_events and stream_intermediate_steps (deprecated)
304
+ if stream_intermediate_steps is not None:
305
+ warnings.warn(
306
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
307
+ DeprecationWarning,
308
+ stacklevel=2,
309
+ )
303
310
  stream_events = stream_events or stream_intermediate_steps
304
311
 
305
312
  if stream_events and workflow_run_response:
@@ -567,6 +574,12 @@ class Router:
567
574
  log_debug(f"Router {self.name} selected: {len(steps_to_execute)} steps to execute")
568
575
 
569
576
  # Considering both stream_events and stream_intermediate_steps (deprecated)
577
+ if stream_intermediate_steps is not None:
578
+ warnings.warn(
579
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
580
+ DeprecationWarning,
581
+ stacklevel=2,
582
+ )
570
583
  stream_events = stream_events or stream_intermediate_steps
571
584
 
572
585
  if stream_events and workflow_run_response:
agno/workflow/step.py CHANGED
@@ -1,7 +1,8 @@
1
1
  import inspect
2
+ import warnings
2
3
  from copy import copy
3
4
  from dataclasses import dataclass
4
- from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
5
+ from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union, cast
5
6
  from uuid import uuid4
6
7
 
7
8
  from pydantic import BaseModel
@@ -9,6 +10,7 @@ from typing_extensions import TypeGuard
9
10
 
10
11
  from agno.agent import Agent
11
12
  from agno.media import Audio, Image, Video
13
+ from agno.models.message import Message
12
14
  from agno.models.metrics import Metrics
13
15
  from agno.run import RunContext
14
16
  from agno.run.agent import RunContentEvent, RunOutput
@@ -21,9 +23,11 @@ from agno.run.workflow import (
21
23
  WorkflowRunOutput,
22
24
  WorkflowRunOutputEvent,
23
25
  )
26
+ from agno.session.agent import AgentSession
27
+ from agno.session.team import TeamSession
24
28
  from agno.session.workflow import WorkflowSession
25
29
  from agno.team import Team
26
- from agno.utils.log import log_debug, logger, use_agent_logger, use_team_logger, use_workflow_logger
30
+ from agno.utils.log import log_debug, log_warning, logger, use_agent_logger, use_team_logger, use_workflow_logger
27
31
  from agno.utils.merge_dict import merge_dictionaries
28
32
  from agno.workflow.types import StepInput, StepOutput, StepType
29
33
 
@@ -272,17 +276,18 @@ class Step:
272
276
  elif isinstance(chunk.content, BaseModel):
273
277
  content = chunk.content # type: ignore[assignment]
274
278
  else:
275
- # Safeguard but should never happen
279
+ # Case when parse_response is False and the content is a dict
276
280
  content += str(chunk.content)
277
281
  elif isinstance(chunk, (RunOutput, TeamRunOutput)):
278
282
  # This is the final response from the agent/team
279
283
  content = chunk.content # type: ignore[assignment]
280
- else:
281
- # Non Agent/Team data structure that was yielded
282
- content += str(chunk)
283
284
  # If the chunk is a StepOutput, use it as the final response
284
- if isinstance(chunk, StepOutput):
285
+ elif isinstance(chunk, StepOutput):
285
286
  final_response = chunk
287
+ break
288
+ # Non Agent/Team data structure that was yielded
289
+ else:
290
+ content += str(chunk)
286
291
 
287
292
  except StopIteration as e:
288
293
  if hasattr(e, "value") and isinstance(e.value, StepOutput):
@@ -482,6 +487,12 @@ class Step:
482
487
  session_state_copy = copy(session_state) if session_state is not None else {}
483
488
 
484
489
  # Considering both stream_events and stream_intermediate_steps (deprecated)
490
+ if stream_intermediate_steps is not None:
491
+ warnings.warn(
492
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
493
+ DeprecationWarning,
494
+ stacklevel=2,
495
+ )
485
496
  stream_events = stream_events or stream_intermediate_steps
486
497
 
487
498
  # Emit StepStartedEvent
@@ -537,11 +548,11 @@ class Step:
537
548
  yield enriched_event # type: ignore[misc]
538
549
  elif isinstance(event, (RunOutput, TeamRunOutput)):
539
550
  content = event.content # type: ignore[assignment]
540
- else:
541
- content += str(event)
542
- if isinstance(event, StepOutput):
551
+ elif isinstance(event, StepOutput):
543
552
  final_response = event
544
553
  break
554
+ else:
555
+ content += str(event)
545
556
 
546
557
  # Merge session_state changes back
547
558
  if run_context is None and session_state is not None:
@@ -758,10 +769,12 @@ class Step:
758
769
  content = str(chunk.content)
759
770
  elif isinstance(chunk, (RunOutput, TeamRunOutput)):
760
771
  content = chunk.content # type: ignore[assignment]
772
+ elif isinstance(chunk, StepOutput):
773
+ final_response = chunk
774
+ break
761
775
  else:
762
776
  content += str(chunk)
763
- if isinstance(chunk, StepOutput):
764
- final_response = chunk
777
+
765
778
  else:
766
779
  if _is_async_generator_function(self.active_executor):
767
780
  iterator = await self._acall_custom_function(
@@ -784,10 +797,11 @@ class Step:
784
797
  content = str(chunk.content)
785
798
  elif isinstance(chunk, (RunOutput, TeamRunOutput)):
786
799
  content = chunk.content # type: ignore[assignment]
800
+ elif isinstance(chunk, StepOutput):
801
+ final_response = chunk
802
+ break
787
803
  else:
788
804
  content += str(chunk)
789
- if isinstance(chunk, StepOutput):
790
- final_response = chunk
791
805
 
792
806
  except StopIteration as e:
793
807
  if hasattr(e, "value") and isinstance(e.value, StepOutput):
@@ -948,6 +962,12 @@ class Step:
948
962
  session_state_copy = copy(session_state) if session_state is not None else {}
949
963
 
950
964
  # Considering both stream_events and stream_intermediate_steps (deprecated)
965
+ if stream_intermediate_steps is not None:
966
+ warnings.warn(
967
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
968
+ DeprecationWarning,
969
+ stacklevel=2,
970
+ )
951
971
  stream_events = stream_events or stream_intermediate_steps
952
972
 
953
973
  if stream_events and workflow_run_response:
@@ -1003,11 +1023,11 @@ class Step:
1003
1023
  yield enriched_event # type: ignore[misc]
1004
1024
  elif isinstance(event, (RunOutput, TeamRunOutput)):
1005
1025
  content = event.content # type: ignore[assignment]
1006
- else:
1007
- content += str(event)
1008
- if isinstance(event, StepOutput):
1026
+ elif isinstance(event, StepOutput):
1009
1027
  final_response = event
1010
1028
  break
1029
+ else:
1030
+ content += str(event)
1011
1031
  if not final_response:
1012
1032
  final_response = StepOutput(content=content)
1013
1033
  elif _is_async_callable(self.active_executor):
@@ -1054,11 +1074,14 @@ class Step:
1054
1074
  yield enriched_event # type: ignore[misc]
1055
1075
  elif isinstance(event, (RunOutput, TeamRunOutput)):
1056
1076
  content = event.content # type: ignore[assignment]
1057
- else:
1058
- content += str(event)
1059
- if isinstance(event, StepOutput):
1077
+ elif isinstance(event, StepOutput):
1060
1078
  final_response = event
1061
1079
  break
1080
+ else:
1081
+ if isinstance(content, str):
1082
+ content += str(event)
1083
+ else:
1084
+ content = str(event)
1062
1085
  if not final_response:
1063
1086
  final_response = StepOutput(content=content)
1064
1087
  else:
@@ -1202,6 +1225,83 @@ class Step:
1202
1225
 
1203
1226
  return
1204
1227
 
1228
+ def get_chat_history(self, session_id: str, last_n_runs: Optional[int] = None) -> List[Message]:
1229
+ """Return the step's Agent or Team chat history for the given session.
1230
+
1231
+ Args:
1232
+ session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
1233
+ last_n_runs: Number of recent runs to include. If None, all runs will be considered.
1234
+
1235
+ Returns:
1236
+ List[Message]: The step's Agent or Team chat history for the given session.
1237
+ """
1238
+ session: Union[AgentSession, TeamSession, WorkflowSession, None] = None
1239
+
1240
+ if self.agent:
1241
+ session = self.agent.get_session(session_id=session_id)
1242
+ if not session:
1243
+ log_warning("Session not found")
1244
+ return []
1245
+
1246
+ if not isinstance(session, WorkflowSession):
1247
+ raise ValueError("The provided session is not a WorkflowSession")
1248
+
1249
+ session = cast(WorkflowSession, session)
1250
+ return session.get_messages(last_n_runs=last_n_runs, agent_id=self.agent.id)
1251
+
1252
+ elif self.team:
1253
+ session = self.team.get_session(session_id=session_id)
1254
+ if not session:
1255
+ log_warning("Session not found")
1256
+ return []
1257
+
1258
+ if not isinstance(session, WorkflowSession):
1259
+ raise ValueError("The provided session is not a WorkflowSession")
1260
+
1261
+ session = cast(WorkflowSession, session)
1262
+ return session.get_messages(last_n_runs=last_n_runs, team_id=self.team.id)
1263
+
1264
+ return []
1265
+
1266
+ async def aget_chat_history(
1267
+ self, session_id: Optional[str] = None, last_n_runs: Optional[int] = None
1268
+ ) -> List[Message]:
1269
+ """Return the step's Agent or Team chat history for the given session.
1270
+
1271
+ Args:
1272
+ session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
1273
+ last_n_runs: Number of recent runs to include. If None, all runs will be considered.
1274
+
1275
+ Returns:
1276
+ List[Message]: The step's Agent or Team chat history for the given session.
1277
+ """
1278
+ session: Union[AgentSession, TeamSession, WorkflowSession, None] = None
1279
+
1280
+ if self.agent:
1281
+ session = await self.agent.aget_session(session_id=session_id)
1282
+ if not session:
1283
+ log_warning("Session not found")
1284
+ return []
1285
+
1286
+ if not isinstance(session, WorkflowSession):
1287
+ raise ValueError("The provided session is not a WorkflowSession")
1288
+
1289
+ session = cast(WorkflowSession, session)
1290
+ return session.get_messages(last_n_runs=last_n_runs, agent_id=self.agent.id)
1291
+
1292
+ elif self.team:
1293
+ session = await self.team.aget_session(session_id=session_id)
1294
+ if not session:
1295
+ log_warning("Session not found")
1296
+ return []
1297
+
1298
+ if not isinstance(session, WorkflowSession):
1299
+ raise ValueError("The provided session is not a WorkflowSession")
1300
+
1301
+ return session.get_messages(last_n_runs=last_n_runs, team_id=self.team.id)
1302
+
1303
+ return []
1304
+
1205
1305
  def _store_executor_response(
1206
1306
  self, workflow_run_response: "WorkflowRunOutput", executor_run_response: Union[RunOutput, TeamRunOutput]
1207
1307
  ) -> None:
agno/workflow/steps.py CHANGED
@@ -1,3 +1,4 @@
1
+ import warnings
1
2
  from dataclasses import dataclass
2
3
  from typing import Any, AsyncIterator, Awaitable, Callable, Dict, Iterator, List, Optional, Union
3
4
  from uuid import uuid4
@@ -229,6 +230,12 @@ class Steps:
229
230
  self._prepare_steps()
230
231
 
231
232
  # Considering both stream_events and stream_intermediate_steps (deprecated)
233
+ if stream_intermediate_steps is not None:
234
+ warnings.warn(
235
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
236
+ DeprecationWarning,
237
+ stacklevel=2,
238
+ )
232
239
  stream_events = stream_events or stream_intermediate_steps
233
240
 
234
241
  if stream_events:
@@ -468,6 +475,12 @@ class Steps:
468
475
  self._prepare_steps()
469
476
 
470
477
  # Considering both stream_events and stream_intermediate_steps (deprecated)
478
+ if stream_intermediate_steps is not None:
479
+ warnings.warn(
480
+ "The 'stream_intermediate_steps' parameter is deprecated and will be removed in future versions. Use 'stream_events' instead.",
481
+ DeprecationWarning,
482
+ stacklevel=2,
483
+ )
471
484
  stream_events = stream_events or stream_intermediate_steps
472
485
 
473
486
  if stream_events: