agno 1.7.2__py3-none-any.whl → 1.7.4__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 (63) hide show
  1. agno/agent/agent.py +264 -155
  2. agno/api/schemas/agent.py +1 -0
  3. agno/api/schemas/team.py +1 -0
  4. agno/app/base.py +0 -22
  5. agno/app/discord/client.py +134 -56
  6. agno/app/fastapi/app.py +0 -11
  7. agno/app/playground/app.py +3 -24
  8. agno/app/playground/async_router.py +97 -28
  9. agno/app/playground/operator.py +25 -19
  10. agno/app/playground/schemas.py +1 -0
  11. agno/app/playground/sync_router.py +93 -26
  12. agno/document/reader/gcs/__init__.py +0 -0
  13. agno/document/reader/gcs/pdf_reader.py +44 -0
  14. agno/embedder/langdb.py +9 -5
  15. agno/knowledge/document.py +199 -8
  16. agno/knowledge/gcs/__init__.py +0 -0
  17. agno/knowledge/gcs/base.py +39 -0
  18. agno/knowledge/gcs/pdf.py +21 -0
  19. agno/models/langdb/langdb.py +8 -5
  20. agno/run/base.py +2 -0
  21. agno/run/response.py +4 -4
  22. agno/run/team.py +6 -6
  23. agno/run/v2/__init__.py +0 -0
  24. agno/run/v2/workflow.py +563 -0
  25. agno/storage/base.py +4 -4
  26. agno/storage/dynamodb.py +74 -10
  27. agno/storage/firestore.py +6 -1
  28. agno/storage/gcs_json.py +8 -2
  29. agno/storage/json.py +20 -5
  30. agno/storage/mongodb.py +14 -5
  31. agno/storage/mysql.py +56 -17
  32. agno/storage/postgres.py +55 -13
  33. agno/storage/redis.py +25 -5
  34. agno/storage/session/__init__.py +3 -1
  35. agno/storage/session/agent.py +3 -0
  36. agno/storage/session/team.py +3 -0
  37. agno/storage/session/v2/__init__.py +5 -0
  38. agno/storage/session/v2/workflow.py +89 -0
  39. agno/storage/singlestore.py +74 -12
  40. agno/storage/sqlite.py +64 -18
  41. agno/storage/yaml.py +26 -6
  42. agno/team/team.py +198 -243
  43. agno/tools/scrapegraph.py +8 -10
  44. agno/utils/log.py +12 -0
  45. agno/utils/message.py +5 -1
  46. agno/utils/openai.py +20 -5
  47. agno/utils/pprint.py +32 -8
  48. agno/workflow/v2/__init__.py +21 -0
  49. agno/workflow/v2/condition.py +554 -0
  50. agno/workflow/v2/loop.py +602 -0
  51. agno/workflow/v2/parallel.py +659 -0
  52. agno/workflow/v2/router.py +521 -0
  53. agno/workflow/v2/step.py +861 -0
  54. agno/workflow/v2/steps.py +465 -0
  55. agno/workflow/v2/types.py +347 -0
  56. agno/workflow/v2/workflow.py +3134 -0
  57. agno/workflow/workflow.py +15 -147
  58. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/METADATA +1 -1
  59. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/RECORD +63 -45
  60. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/WHEEL +0 -0
  61. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/entry_points.txt +0 -0
  62. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/licenses/LICENSE +0 -0
  63. {agno-1.7.2.dist-info → agno-1.7.4.dist-info}/top_level.txt +0 -0
agno/agent/agent.py CHANGED
@@ -16,6 +16,7 @@ from typing import (
16
16
  Optional,
17
17
  Sequence,
18
18
  Set,
19
+ Tuple,
19
20
  Type,
20
21
  Union,
21
22
  cast,
@@ -317,9 +318,13 @@ class Agent:
317
318
 
318
319
  # Optional workflow ID. Indicates this agent is part of a workflow.
319
320
  workflow_id: Optional[str] = None
321
+ # Set when this agent is part of a workflow.
322
+ workflow_session_id: Optional[str] = None
320
323
 
321
324
  # Optional team session state. Set by the team leader agent.
322
325
  team_session_state: Optional[Dict[str, Any]] = None
326
+ # Optional workflow session state. Set by the workflow.
327
+ workflow_session_state: Optional[Dict[str, Any]] = None
323
328
 
324
329
  # --- Debug & Monitoring ---
325
330
  # Enable debug logs
@@ -542,7 +547,7 @@ class Agent:
542
547
  self.session_metrics: Optional[SessionMetrics] = None
543
548
 
544
549
  self.run_id: Optional[str] = None
545
- self.run_input: Optional[Union[str, List, Dict, Message]] = None
550
+ self.run_input: Optional[Union[str, List, Dict, Message, BaseModel]] = None
546
551
  self.run_messages: Optional[RunMessages] = None
547
552
  self.run_response: Optional[RunResponse] = None
548
553
 
@@ -621,9 +626,9 @@ class Agent:
621
626
  if self.num_history_responses is not None:
622
627
  self.num_history_runs = self.num_history_responses
623
628
 
624
- def reset_session_state(self) -> None:
625
- self.session_name = None
629
+ def reset_session(self) -> None:
626
630
  self.session_state = None
631
+ self.session_name = None
627
632
  self.session_metrics = None
628
633
  self.images = None
629
634
  self.videos = None
@@ -688,6 +693,72 @@ class Agent:
688
693
  self.tools = tools
689
694
  self._rebuild_tools = True
690
695
 
696
+ def _initialize_session_state(self, user_id: Optional[str] = None, session_id: Optional[str] = None) -> None:
697
+ self.session_state = self.session_state or {}
698
+ if user_id is not None:
699
+ self.session_state["current_user_id"] = user_id
700
+ if self.team_session_state is not None:
701
+ self.team_session_state["current_user_id"] = user_id
702
+ if self.workflow_session_state is not None:
703
+ self.workflow_session_state["current_user_id"] = user_id
704
+ if session_id is not None:
705
+ self.session_state["current_session_id"] = session_id
706
+ if self.team_session_state is not None:
707
+ self.team_session_state["current_session_id"] = session_id
708
+ if self.workflow_session_state is not None:
709
+ self.workflow_session_state["current_user_id"] = user_id
710
+
711
+ def _reset_session_state(self) -> None:
712
+ """Reset the session state for the agent."""
713
+ if self.team_session_state is not None:
714
+ self.team_session_state.pop("current_session_id", None)
715
+ self.team_session_state.pop("current_user_id", None)
716
+ if self.session_state is not None:
717
+ self.session_state.pop("current_session_id", None)
718
+ self.session_state.pop("current_user_id", None)
719
+
720
+ def _initialize_session(
721
+ self,
722
+ session_id: Optional[str] = None,
723
+ user_id: Optional[str] = None,
724
+ session_state: Optional[Dict[str, Any]] = None,
725
+ ) -> Tuple[str, Optional[str]]:
726
+ """Initialize the session for the agent."""
727
+
728
+ self.reset_run_state()
729
+
730
+ # Determine the session_id
731
+ if session_id is not None and session_id != "":
732
+ # Reset session state if a session_id is provided. Session name and session state will be loaded from storage.
733
+ # Only reset session state if the session_id is different from the current session_id
734
+ if self.session_id is not None and session_id != self.session_id:
735
+ self.reset_session()
736
+
737
+ self.session_id = session_id
738
+ else:
739
+ if not (self.session_id is None or self.session_id == ""):
740
+ session_id = self.session_id
741
+ else:
742
+ # Generate a new session_id and store it in the agent
743
+ self.session_id = session_id = str(uuid4())
744
+
745
+ # Use the default user_id when necessary
746
+ if user_id is not None and user_id != "":
747
+ user_id = user_id
748
+ else:
749
+ user_id = self.user_id
750
+
751
+ # Determine the session_state
752
+ if session_state is not None:
753
+ self.session_state = session_state
754
+
755
+ self._initialize_session_state(user_id=user_id, session_id=session_id)
756
+
757
+ # Read existing session from storage
758
+ self.read_from_storage(session_id=session_id)
759
+
760
+ return session_id, user_id
761
+
691
762
  def _run(
692
763
  self,
693
764
  run_response: RunResponse,
@@ -695,6 +766,7 @@ class Agent:
695
766
  session_id: str,
696
767
  user_id: Optional[str] = None,
697
768
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
769
+ refresh_session_before_write: Optional[bool] = False,
698
770
  ) -> RunResponse:
699
771
  """Run the Agent and return the RunResponse.
700
772
 
@@ -762,7 +834,7 @@ class Agent:
762
834
  self._convert_response_to_structured_format(run_response)
763
835
 
764
836
  # 6. Save session to storage
765
- self.write_to_storage(user_id=user_id, session_id=session_id)
837
+ self.write_to_storage(user_id=user_id, session_id=session_id, refresh_session=refresh_session_before_write)
766
838
 
767
839
  # 7. Save output to file if save_response_to_file is set
768
840
  self.save_run_response_to_file(message=run_messages.user_message, session_id=session_id)
@@ -782,6 +854,7 @@ class Agent:
782
854
  user_id: Optional[str] = None,
783
855
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
784
856
  stream_intermediate_steps: bool = False,
857
+ refresh_session_before_write: Optional[bool] = False,
785
858
  ) -> Iterator[RunResponseEvent]:
786
859
  """Run the Agent and yield the RunResponse.
787
860
 
@@ -854,7 +927,7 @@ class Agent:
854
927
  yield self._handle_event(create_run_response_completed_event(from_run_response=run_response), run_response)
855
928
 
856
929
  # 7. Save session to storage
857
- self.write_to_storage(user_id=user_id, session_id=session_id)
930
+ self.write_to_storage(user_id=user_id, session_id=session_id, refresh_session=refresh_session_before_write)
858
931
 
859
932
  # Log Agent Run
860
933
  self._log_agent_run(user_id=user_id, session_id=session_id)
@@ -864,12 +937,13 @@ class Agent:
864
937
  @overload
865
938
  def run(
866
939
  self,
867
- message: Optional[Union[str, List, Dict, Message]] = None,
940
+ message: Optional[Union[str, List, Dict, Message, BaseModel]] = None,
868
941
  *,
869
942
  stream: Literal[False] = False,
870
943
  stream_intermediate_steps: Optional[bool] = None,
871
944
  user_id: Optional[str] = None,
872
945
  session_id: Optional[str] = None,
946
+ session_state: Optional[Dict[str, Any]] = None,
873
947
  audio: Optional[Sequence[Audio]] = None,
874
948
  images: Optional[Sequence[Image]] = None,
875
949
  videos: Optional[Sequence[Video]] = None,
@@ -877,18 +951,20 @@ class Agent:
877
951
  messages: Optional[Sequence[Union[Dict, Message]]] = None,
878
952
  retries: Optional[int] = None,
879
953
  knowledge_filters: Optional[Dict[str, Any]] = None,
954
+ refresh_session_before_write: Optional[bool] = False,
880
955
  **kwargs: Any,
881
956
  ) -> RunResponse: ...
882
957
 
883
958
  @overload
884
959
  def run(
885
960
  self,
886
- message: Optional[Union[str, List, Dict, Message]] = None,
961
+ message: Optional[Union[str, List, Dict, Message, BaseModel]] = None,
887
962
  *,
888
963
  stream: Literal[True] = True,
889
964
  stream_intermediate_steps: Optional[bool] = None,
890
965
  user_id: Optional[str] = None,
891
966
  session_id: Optional[str] = None,
967
+ session_state: Optional[Dict[str, Any]] = None,
892
968
  audio: Optional[Sequence[Audio]] = None,
893
969
  images: Optional[Sequence[Image]] = None,
894
970
  videos: Optional[Sequence[Video]] = None,
@@ -896,17 +972,19 @@ class Agent:
896
972
  messages: Optional[Sequence[Union[Dict, Message]]] = None,
897
973
  retries: Optional[int] = None,
898
974
  knowledge_filters: Optional[Dict[str, Any]] = None,
975
+ refresh_session_before_write: Optional[bool] = False,
899
976
  **kwargs: Any,
900
977
  ) -> Iterator[RunResponseEvent]: ...
901
978
 
902
979
  def run(
903
980
  self,
904
- message: Optional[Union[str, List, Dict, Message]] = None,
981
+ message: Optional[Union[str, List, Dict, Message, BaseModel]] = None,
905
982
  *,
906
983
  stream: Optional[bool] = None,
907
984
  stream_intermediate_steps: Optional[bool] = None,
908
985
  user_id: Optional[str] = None,
909
986
  session_id: Optional[str] = None,
987
+ session_state: Optional[Dict[str, Any]] = None,
910
988
  audio: Optional[Sequence[Audio]] = None,
911
989
  images: Optional[Sequence[Image]] = None,
912
990
  videos: Optional[Sequence[Video]] = None,
@@ -914,39 +992,20 @@ class Agent:
914
992
  messages: Optional[Sequence[Union[Dict, Message]]] = None,
915
993
  retries: Optional[int] = None,
916
994
  knowledge_filters: Optional[Dict[str, Any]] = None,
995
+ refresh_session_before_write: Optional[bool] = False,
917
996
  **kwargs: Any,
918
997
  ) -> Union[RunResponse, Iterator[RunResponseEvent]]:
919
998
  """Run the Agent and return the response."""
920
999
 
921
- self.reset_run_state()
1000
+ session_id, user_id = self._initialize_session(
1001
+ session_id=session_id, user_id=user_id, session_state=session_state
1002
+ )
922
1003
 
923
- if session_id is not None:
924
- # Reset session state if a session_id is provided. Session name and session state will be loaded from storage.
925
- self.reset_session_state()
1004
+ log_debug(f"Session ID: {session_id}", center=True)
926
1005
 
927
1006
  # Initialize the Agent
928
1007
  self.initialize_agent()
929
1008
 
930
- # Initialize Session
931
- # Use the default user_id and session_id when necessary
932
- user_id = user_id if user_id is not None else self.user_id
933
-
934
- if session_id is None or session_id == "":
935
- if not (self.session_id is None or self.session_id == ""):
936
- session_id = self.session_id
937
- else:
938
- # Generate a new session_id and store it in the agent
939
- session_id = str(uuid4())
940
- self.session_id = session_id
941
- else:
942
- self.session_id = session_id
943
-
944
- session_id = cast(str, session_id)
945
-
946
- self._initialize_session_state(user_id=user_id, session_id=session_id)
947
-
948
- log_debug(f"Session ID: {session_id}", center=True)
949
-
950
1009
  # Initialize Knowledge Filters
951
1010
  effective_filters = knowledge_filters
952
1011
 
@@ -983,9 +1042,6 @@ class Agent:
983
1042
  self.stream = self.stream or stream
984
1043
  self.stream_intermediate_steps = self.stream_intermediate_steps or (stream_intermediate_steps and self.stream)
985
1044
 
986
- # Read existing session from storage
987
- self.read_from_storage(session_id=session_id)
988
-
989
1045
  # Read existing session from storage
990
1046
  if self.context is not None:
991
1047
  self.resolve_run_context()
@@ -1066,6 +1122,7 @@ class Agent:
1066
1122
  session_id=session_id,
1067
1123
  response_format=response_format,
1068
1124
  stream_intermediate_steps=stream_intermediate_steps,
1125
+ refresh_session_before_write=refresh_session_before_write,
1069
1126
  )
1070
1127
  return response_iterator
1071
1128
  else:
@@ -1075,6 +1132,7 @@ class Agent:
1075
1132
  user_id=user_id,
1076
1133
  session_id=session_id,
1077
1134
  response_format=response_format,
1135
+ refresh_session_before_write=refresh_session_before_write,
1078
1136
  )
1079
1137
  return response
1080
1138
  except ModelProviderError as e:
@@ -1100,6 +1158,8 @@ class Agent:
1100
1158
  )
1101
1159
  else:
1102
1160
  return self.run_response
1161
+ finally:
1162
+ self._reset_session_state()
1103
1163
 
1104
1164
  # If we get here, all retries failed
1105
1165
  if last_exception is not None:
@@ -1122,6 +1182,7 @@ class Agent:
1122
1182
  session_id: str,
1123
1183
  user_id: Optional[str] = None,
1124
1184
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1185
+ refresh_session_before_write: Optional[bool] = False,
1125
1186
  ) -> RunResponse:
1126
1187
  """Run the Agent and yield the RunResponse.
1127
1188
 
@@ -1188,7 +1249,7 @@ class Agent:
1188
1249
  self._convert_response_to_structured_format(run_response)
1189
1250
 
1190
1251
  # 6. Save session to storage
1191
- self.write_to_storage(user_id=user_id, session_id=session_id)
1252
+ self.write_to_storage(user_id=user_id, session_id=session_id, refresh_session=refresh_session_before_write)
1192
1253
 
1193
1254
  # 7. Save output to file if save_response_to_file is set
1194
1255
  self.save_run_response_to_file(message=run_messages.user_message, session_id=session_id)
@@ -1208,6 +1269,7 @@ class Agent:
1208
1269
  user_id: Optional[str] = None,
1209
1270
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1210
1271
  stream_intermediate_steps: bool = False,
1272
+ refresh_session_before_write: Optional[bool] = False,
1211
1273
  ) -> AsyncIterator[RunResponseEvent]:
1212
1274
  """Run the Agent and yield the RunResponse.
1213
1275
 
@@ -1283,7 +1345,7 @@ class Agent:
1283
1345
  yield self._handle_event(create_run_response_completed_event(from_run_response=run_response), run_response)
1284
1346
 
1285
1347
  # 7. Save session to storage
1286
- self.write_to_storage(user_id=user_id, session_id=session_id)
1348
+ self.write_to_storage(user_id=user_id, session_id=session_id, refresh_session=refresh_session_before_write)
1287
1349
 
1288
1350
  # Log Agent Run
1289
1351
  await self._alog_agent_run(user_id=user_id, session_id=session_id)
@@ -1292,11 +1354,12 @@ class Agent:
1292
1354
 
1293
1355
  async def arun(
1294
1356
  self,
1295
- message: Optional[Union[str, List, Dict, Message]] = None,
1357
+ message: Optional[Union[str, List, Dict, Message, BaseModel]] = None,
1296
1358
  *,
1297
1359
  stream: Optional[bool] = None,
1298
1360
  user_id: Optional[str] = None,
1299
1361
  session_id: Optional[str] = None,
1362
+ session_state: Optional[Dict[str, Any]] = None,
1300
1363
  audio: Optional[Sequence[Audio]] = None,
1301
1364
  images: Optional[Sequence[Image]] = None,
1302
1365
  videos: Optional[Sequence[Video]] = None,
@@ -1305,40 +1368,21 @@ class Agent:
1305
1368
  stream_intermediate_steps: Optional[bool] = None,
1306
1369
  retries: Optional[int] = None,
1307
1370
  knowledge_filters: Optional[Dict[str, Any]] = None,
1371
+ refresh_session_before_write: Optional[bool] = False,
1308
1372
  **kwargs: Any,
1309
1373
  ) -> Any:
1310
1374
  """Async Run the Agent and return the response."""
1311
- self.reset_run_state()
1312
1375
 
1313
- if session_id is not None:
1314
- # Reset session state if a session_id is provided
1315
- self.reset_session_state()
1376
+ session_id, user_id = self._initialize_session(
1377
+ session_id=session_id, user_id=user_id, session_state=session_state
1378
+ )
1379
+
1380
+ log_debug(f"Session ID: {session_id}", center=True)
1316
1381
 
1317
1382
  # Initialize the Agent
1318
1383
  self.initialize_agent()
1319
1384
 
1320
- # Initialize Session
1321
- # Use the default user_id and session_id when necessary
1322
- user_id = user_id if user_id is not None else self.user_id
1323
-
1324
- if session_id is None or session_id == "":
1325
- if not (self.session_id is None or self.session_id == ""):
1326
- session_id = self.session_id
1327
- else:
1328
- # Generate a new session_id and store it in the agent
1329
- session_id = str(uuid4())
1330
- self.session_id = session_id
1331
- else:
1332
- self.session_id = session_id
1333
-
1334
- session_id = cast(str, session_id)
1335
-
1336
- self._initialize_session_state(user_id=user_id, session_id=session_id)
1337
-
1338
- log_debug(f"Session ID: {session_id}", center=True)
1339
-
1340
1385
  effective_filters = knowledge_filters
1341
-
1342
1386
  # When filters are passed manually
1343
1387
  if self.knowledge_filters or knowledge_filters:
1344
1388
  """
@@ -1372,9 +1416,6 @@ class Agent:
1372
1416
  self.stream = self.stream or stream
1373
1417
  self.stream_intermediate_steps = self.stream_intermediate_steps or (stream_intermediate_steps and self.stream)
1374
1418
 
1375
- # Read existing session from storage
1376
- self.read_from_storage(session_id=session_id)
1377
-
1378
1419
  # Read existing session from storage
1379
1420
  if self.context is not None:
1380
1421
  self.resolve_run_context()
@@ -1456,16 +1497,19 @@ class Agent:
1456
1497
  session_id=session_id,
1457
1498
  response_format=response_format,
1458
1499
  stream_intermediate_steps=stream_intermediate_steps,
1500
+ refresh_session_before_write=refresh_session_before_write,
1459
1501
  ) # type: ignore[assignment]
1460
1502
  return response_iterator
1461
1503
  else:
1462
- return await self._arun(
1504
+ response = await self._arun(
1463
1505
  run_response=run_response,
1464
1506
  run_messages=run_messages,
1465
1507
  user_id=user_id,
1466
1508
  session_id=session_id,
1467
1509
  response_format=response_format,
1510
+ refresh_session_before_write=refresh_session_before_write,
1468
1511
  )
1512
+ return response
1469
1513
  except ModelProviderError as e:
1470
1514
  log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
1471
1515
  if isinstance(e, StopAgentRun):
@@ -1489,6 +1533,8 @@ class Agent:
1489
1533
  )
1490
1534
  else:
1491
1535
  return self.run_response
1536
+ finally:
1537
+ self._reset_session_state()
1492
1538
 
1493
1539
  # If we get here, all retries failed
1494
1540
  if last_exception is not None:
@@ -1563,7 +1609,10 @@ class Agent:
1563
1609
  if session_id is not None:
1564
1610
  self.reset_run_state()
1565
1611
  # Reset session state if a session_id is provided. Session name and session state will be loaded from storage.
1566
- self.reset_session_state()
1612
+ self.reset_session()
1613
+ # Only reset session state if the session_id is different from the current session_id
1614
+ if self.session_id is not None and session_id != self.session_id:
1615
+ self.session_state = None
1567
1616
 
1568
1617
  # Initialize the Agent
1569
1618
  self.initialize_agent()
@@ -1582,8 +1631,6 @@ class Agent:
1582
1631
  else:
1583
1632
  self.session_id = session_id
1584
1633
 
1585
- session_id = cast(str, session_id)
1586
-
1587
1634
  self._initialize_session_state(user_id=user_id, session_id=session_id)
1588
1635
 
1589
1636
  log_debug(f"Session ID: {session_id}", center=True)
@@ -1711,7 +1758,6 @@ class Agent:
1711
1758
  response_format=response_format,
1712
1759
  stream_intermediate_steps=stream_intermediate_steps,
1713
1760
  )
1714
-
1715
1761
  return response_iterator
1716
1762
  else:
1717
1763
  response = self._continue_run(
@@ -1744,6 +1790,8 @@ class Agent:
1744
1790
  return self.create_run_response(
1745
1791
  run_state=RunStatus.cancelled, content="Operation cancelled by user", run_response=run_response
1746
1792
  )
1793
+ finally:
1794
+ self._reset_session_state()
1747
1795
 
1748
1796
  # If we get here, all retries failed
1749
1797
  if last_exception is not None:
@@ -1951,7 +1999,10 @@ class Agent:
1951
1999
  if session_id is not None:
1952
2000
  self.reset_run_state()
1953
2001
  # Reset session state if a session_id is provided. Session name and session state will be loaded from storage.
1954
- self.reset_session_state()
2002
+ self.reset_session()
2003
+ # Only reset session state if the session_id is different from the current session_id
2004
+ if self.session_id is not None and session_id != self.session_id:
2005
+ self.session_state = None
1955
2006
 
1956
2007
  # Initialize the Agent
1957
2008
  self.initialize_agent()
@@ -1970,8 +2021,6 @@ class Agent:
1970
2021
  else:
1971
2022
  self.session_id = session_id
1972
2023
 
1973
- session_id = cast(str, session_id)
1974
-
1975
2024
  self._initialize_session_state(user_id=user_id, session_id=session_id)
1976
2025
 
1977
2026
  log_debug(f"Session ID: {session_id}", center=True)
@@ -2108,16 +2157,16 @@ class Agent:
2108
2157
  response_format=response_format,
2109
2158
  stream_intermediate_steps=stream_intermediate_steps,
2110
2159
  )
2111
-
2112
2160
  return response_iterator
2113
2161
  else:
2114
- return await self._acontinue_run(
2162
+ response = await self._acontinue_run(
2115
2163
  run_response=run_response,
2116
2164
  run_messages=run_messages,
2117
2165
  user_id=user_id,
2118
2166
  session_id=session_id,
2119
2167
  response_format=response_format,
2120
2168
  )
2169
+ return response
2121
2170
  except ModelProviderError as e:
2122
2171
  log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
2123
2172
  if isinstance(e, StopAgentRun):
@@ -2140,6 +2189,8 @@ class Agent:
2140
2189
  return self.create_run_response(
2141
2190
  run_state=RunStatus.cancelled, content="Operation cancelled by user", run_response=run_response
2142
2191
  )
2192
+ finally:
2193
+ self._reset_session_state()
2143
2194
 
2144
2195
  # If we get here, all retries failed
2145
2196
  if last_exception is not None:
@@ -3361,17 +3412,6 @@ class Agent:
3361
3412
  rr.model = model
3362
3413
  return rr
3363
3414
 
3364
- def _initialize_session_state(self, user_id: Optional[str] = None, session_id: Optional[str] = None) -> None:
3365
- self.session_state = self.session_state or {}
3366
- if user_id is not None:
3367
- self.session_state["current_user_id"] = user_id
3368
- if self.team_session_state is not None:
3369
- self.team_session_state["current_user_id"] = user_id
3370
- if session_id is not None:
3371
- self.session_state["current_session_id"] = session_id
3372
- if self.team_session_state is not None:
3373
- self.team_session_state["current_session_id"] = session_id
3374
-
3375
3415
  def _make_memories_and_summaries(
3376
3416
  self,
3377
3417
  run_messages: RunMessages,
@@ -3837,6 +3877,8 @@ class Agent:
3837
3877
  session_data["session_state"] = self.session_state
3838
3878
  if self.team_session_state is not None and len(self.team_session_state) > 0:
3839
3879
  session_data["team_session_state"] = self.team_session_state
3880
+ if self.workflow_session_state is not None and len(self.workflow_session_state) > 0:
3881
+ session_data["workflow_session_state"] = self.workflow_session_state
3840
3882
  if self.session_metrics is not None:
3841
3883
  session_data["session_metrics"] = asdict(self.session_metrics) if self.session_metrics is not None else None
3842
3884
  if self.team_data is not None:
@@ -3873,12 +3915,15 @@ class Agent:
3873
3915
  memory_dict = None
3874
3916
 
3875
3917
  self.team_session_id = cast(str, self.team_session_id)
3918
+ self.workflow_session_id = cast(str, self.workflow_session_id)
3919
+
3876
3920
  self.agent_id = cast(str, self.agent_id)
3877
3921
  return AgentSession(
3878
3922
  session_id=session_id,
3879
3923
  agent_id=self.agent_id,
3880
3924
  user_id=user_id,
3881
3925
  team_session_id=self.team_session_id,
3926
+ workflow_session_id=self.workflow_session_id,
3882
3927
  memory=memory_dict,
3883
3928
  agent_data=self.get_agent_data(),
3884
3929
  session_data=self.get_session_data(),
@@ -3889,6 +3934,9 @@ class Agent:
3889
3934
  def load_agent_session(self, session: AgentSession):
3890
3935
  """Load the existing Agent from an AgentSession (from the database)"""
3891
3936
 
3937
+ if not hasattr(session, "memory"):
3938
+ return
3939
+
3892
3940
  from agno.utils.merge_dict import merge_dictionaries
3893
3941
 
3894
3942
  # Get the agent_id, user_id and session_id from the database
@@ -3945,6 +3993,22 @@ class Agent:
3945
3993
  # Update the current team_session_state
3946
3994
  self.team_session_state = team_session_state_from_db
3947
3995
 
3996
+ if "workflow_session_state" in session.session_data:
3997
+ workflow_session_state_from_db = session.session_data.get("workflow_session_state")
3998
+ if (
3999
+ workflow_session_state_from_db is not None
4000
+ and isinstance(workflow_session_state_from_db, dict)
4001
+ and len(workflow_session_state_from_db) > 0
4002
+ ):
4003
+ # If the workflow_session_state is already set, merge the workflow_session_state from the database with the current workflow_session_state
4004
+ if self.workflow_session_state is not None and len(self.workflow_session_state) > 0:
4005
+ # This updates workflow_session_state_from_db
4006
+ # If there are conflicting keys, values from workflow_session_state_from_db will take precedence
4007
+ merge_dictionaries(self.workflow_session_state, workflow_session_state_from_db)
4008
+ else:
4009
+ # Update the current workflow_session_state
4010
+ self.workflow_session_state = workflow_session_state_from_db
4011
+
3948
4012
  # Get the session_metrics from the database
3949
4013
  if "session_metrics" in session.session_data:
3950
4014
  session_metrics_from_db = session.session_data.get("session_metrics")
@@ -4106,13 +4170,56 @@ class Agent:
4106
4170
  self.load_agent_session(session=self.agent_session)
4107
4171
  return self.agent_session
4108
4172
 
4109
- def write_to_storage(self, session_id: str, user_id: Optional[str] = None) -> Optional[AgentSession]:
4173
+ def refresh_from_storage(self, session_id: str) -> None:
4174
+ """Refresh the AgentSession from storage
4175
+
4176
+ Args:
4177
+ session_id: The session_id to refresh from storage.
4178
+ """
4179
+ if not self.storage:
4180
+ return
4181
+
4182
+ agent_session_from_db = self.storage.read(session_id=session_id) # type: ignore
4183
+ if (
4184
+ agent_session_from_db is not None
4185
+ and agent_session_from_db.memory is not None # type: ignore
4186
+ and "runs" in agent_session_from_db.memory # type: ignore
4187
+ ):
4188
+ if isinstance(self.memory, AgentMemory):
4189
+ return
4190
+ try:
4191
+ if self.memory.runs is None: # type: ignore
4192
+ self.memory.runs = {} # type: ignore
4193
+ if session_id not in self.memory.runs: # type: ignore
4194
+ self.memory.runs[session_id] = [] # type: ignore
4195
+ for run in agent_session_from_db.memory["runs"]: # type: ignore
4196
+ run_session_id = run["session_id"]
4197
+ skip = False
4198
+ for existing_run in self.memory.runs[run_session_id]: # type: ignore
4199
+ if existing_run.run_id == run["run_id"]:
4200
+ skip = True
4201
+ break
4202
+ if skip:
4203
+ continue
4204
+ if "team_id" in run:
4205
+ self.memory.runs[run_session_id].append(TeamRunResponse.from_dict(run)) # type: ignore
4206
+ else:
4207
+ self.memory.runs[run_session_id].append(RunResponse.from_dict(run)) # type: ignore
4208
+ except Exception as e:
4209
+ log_warning(f"Failed to load runs from memory: {e}")
4210
+
4211
+ def write_to_storage(
4212
+ self, session_id: str, user_id: Optional[str] = None, refresh_session: Optional[bool] = False
4213
+ ) -> Optional[AgentSession]:
4110
4214
  """Save the AgentSession to storage
4111
4215
 
4112
4216
  Returns:
4113
4217
  Optional[AgentSession]: The saved AgentSession or None if not saved.
4114
4218
  """
4115
4219
  if self.storage is not None:
4220
+ if refresh_session:
4221
+ self.refresh_from_storage(session_id=session_id)
4222
+
4116
4223
  self.agent_session = cast(
4117
4224
  AgentSession,
4118
4225
  self.storage.upsert(session=self.get_agent_session(session_id=session_id, user_id=user_id)),
@@ -4192,22 +4299,23 @@ class Agent:
4192
4299
  self.session_id = str(uuid4())
4193
4300
  self.load_session(force=True)
4194
4301
 
4195
- def format_message_with_state_variables(self, msg: Any) -> Any:
4302
+ def format_message_with_state_variables(self, message: Any) -> Any:
4196
4303
  """Format a message with the session state variables."""
4197
4304
  import re
4198
4305
  import string
4199
4306
 
4200
- if not isinstance(msg, str):
4201
- return msg
4307
+ if not isinstance(message, str):
4308
+ return message
4202
4309
 
4203
4310
  format_variables = ChainMap(
4204
4311
  self.session_state or {},
4205
4312
  self.team_session_state or {},
4313
+ self.workflow_session_state or {},
4206
4314
  self.context or {},
4207
4315
  self.extra_data or {},
4208
4316
  {"user_id": self.user_id} if self.user_id is not None else {},
4209
4317
  )
4210
- converted_msg = msg
4318
+ converted_msg = message
4211
4319
  for var_name in format_variables.keys():
4212
4320
  # Only convert standalone {var_name} patterns, not nested ones
4213
4321
  pattern = r"\{" + re.escape(var_name) + r"\}"
@@ -4221,7 +4329,7 @@ class Agent:
4221
4329
  return result
4222
4330
  except Exception as e:
4223
4331
  log_warning(f"Template substitution failed: {e}")
4224
- return msg
4332
+ return message
4225
4333
 
4226
4334
  def get_system_message(self, session_id: str, user_id: Optional[str] = None) -> Optional[Message]:
4227
4335
  """Return the system message for the Agent.
@@ -4247,6 +4355,7 @@ class Agent:
4247
4355
  # Format the system message with the session state variables
4248
4356
  if self.add_state_in_messages:
4249
4357
  sys_message_content = self.format_message_with_state_variables(sys_message_content)
4358
+ print("HELLO", sys_message_content)
4250
4359
 
4251
4360
  # Add the JSON output prompt if response_model is provided and the model does not support native structured outputs or JSON schema outputs
4252
4361
  # or if use_json_mode is True
@@ -4263,7 +4372,6 @@ class Agent:
4263
4372
 
4264
4373
  # type: ignore
4265
4374
  return Message(role=self.system_message_role, content=sys_message_content)
4266
-
4267
4375
  # 2. If create_default_system_message is False, return None.
4268
4376
  if not self.create_default_system_message:
4269
4377
  return None
@@ -4277,12 +4385,19 @@ class Agent:
4277
4385
  if self.instructions is not None:
4278
4386
  _instructions = self.instructions
4279
4387
  if callable(self.instructions):
4280
- _instructions = self.instructions(agent=self)
4388
+ import inspect
4389
+
4390
+ signature = inspect.signature(self.instructions)
4391
+ if "agent" in signature.parameters:
4392
+ _instructions = self.instructions(agent=self)
4393
+ else:
4394
+ _instructions = self.instructions()
4281
4395
 
4282
4396
  if isinstance(_instructions, str):
4283
4397
  instructions.append(_instructions)
4284
4398
  elif isinstance(_instructions, list):
4285
4399
  instructions.extend(_instructions)
4400
+
4286
4401
  # 3.1.1 Add instructions from the Model
4287
4402
  _model_instructions = self.model.get_instructions_for_model(self._tools_for_model)
4288
4403
  if _model_instructions is not None:
@@ -4532,7 +4647,7 @@ class Agent:
4532
4647
  def get_user_message(
4533
4648
  self,
4534
4649
  *,
4535
- message: Optional[Union[str, List]],
4650
+ message: Optional[Union[str, List, Dict, Message, BaseModel]] = None,
4536
4651
  audio: Optional[Sequence[Audio]] = None,
4537
4652
  images: Optional[Sequence[Image]] = None,
4538
4653
  videos: Optional[Sequence[Video]] = None,
@@ -4605,7 +4720,7 @@ class Agent:
4605
4720
  )
4606
4721
 
4607
4722
  # 2. If create_default_user_message is False or message is a list, return the message as is.
4608
- if not self.create_default_user_message or isinstance(message, list):
4723
+ if not self.create_default_user_message:
4609
4724
  return Message(
4610
4725
  role=self.user_message_role,
4611
4726
  content=message,
@@ -4616,6 +4731,24 @@ class Agent:
4616
4731
  **kwargs,
4617
4732
  )
4618
4733
 
4734
+ # Handle list messages by converting to string
4735
+ if isinstance(message, list):
4736
+ # Convert list to string (join with newlines if all elements are strings)
4737
+ if all(isinstance(item, str) for item in message):
4738
+ message_content = "\n".join(message)
4739
+ else:
4740
+ message_content = str(message)
4741
+
4742
+ return Message(
4743
+ role=self.user_message_role,
4744
+ content=message_content,
4745
+ images=images,
4746
+ audio=audio,
4747
+ videos=videos,
4748
+ files=files,
4749
+ **kwargs,
4750
+ )
4751
+
4619
4752
  # 3. Build the default user message for the Agent
4620
4753
  if message is None:
4621
4754
  # If we have any media, return a message with empty content
@@ -4637,6 +4770,10 @@ class Agent:
4637
4770
  # Format the message with the session state variables
4638
4771
  if self.add_state_in_messages:
4639
4772
  user_msg_content = self.format_message_with_state_variables(message)
4773
+
4774
+ # Convert to string for concatenation operations
4775
+ user_msg_content_str = get_text_from_message(user_msg_content) if user_msg_content is not None else ""
4776
+
4640
4777
  # 4.1 Add references to user message
4641
4778
  if (
4642
4779
  self.add_references
@@ -4644,15 +4781,18 @@ class Agent:
4644
4781
  and references.references is not None
4645
4782
  and len(references.references) > 0
4646
4783
  ):
4647
- user_msg_content += "\n\nUse the following references from the knowledge base if it helps:\n"
4648
- user_msg_content += "<references>\n"
4649
- user_msg_content += self.convert_documents_to_string(references.references) + "\n"
4650
- user_msg_content += "</references>"
4784
+ user_msg_content_str += "\n\nUse the following references from the knowledge base if it helps:\n"
4785
+ user_msg_content_str += "<references>\n"
4786
+ user_msg_content_str += self.convert_documents_to_string(references.references) + "\n"
4787
+ user_msg_content_str += "</references>"
4651
4788
  # 4.2 Add context to user message
4652
4789
  if self.add_context and self.context is not None:
4653
- user_msg_content += "\n\n<context>\n"
4654
- user_msg_content += self.convert_context_to_string(self.context) + "\n"
4655
- user_msg_content += "</context>"
4790
+ user_msg_content_str += "\n\n<context>\n"
4791
+ user_msg_content_str += self.convert_context_to_string(self.context) + "\n"
4792
+ user_msg_content_str += "</context>"
4793
+
4794
+ # Use the string version for the final content
4795
+ user_msg_content = user_msg_content_str
4656
4796
 
4657
4797
  # Return the user message
4658
4798
  return Message(
@@ -4668,7 +4808,7 @@ class Agent:
4668
4808
  def get_run_messages(
4669
4809
  self,
4670
4810
  *,
4671
- message: Optional[Union[str, List, Dict, Message]] = None,
4811
+ message: Optional[Union[str, List, Dict, Message, BaseModel]] = None,
4672
4812
  session_id: str,
4673
4813
  user_id: Optional[str] = None,
4674
4814
  audio: Optional[Sequence[Audio]] = None,
@@ -4795,6 +4935,14 @@ class Agent:
4795
4935
  user_message = Message.model_validate(message)
4796
4936
  except Exception as e:
4797
4937
  log_warning(f"Failed to validate message: {e}")
4938
+ # 4.4 If message is provided as a BaseModel, convert it to a Message
4939
+ elif isinstance(message, BaseModel):
4940
+ try:
4941
+ # Create a user message with the BaseModel content
4942
+ content = message.model_dump_json(indent=2, exclude_none=True)
4943
+ user_message = Message(role=self.user_message_role, content=content)
4944
+ except Exception as e:
4945
+ log_warning(f"Failed to convert BaseModel to message: {e}")
4798
4946
  # Add user message to run_messages
4799
4947
  if user_message is not None:
4800
4948
  run_messages.user_message = user_message
@@ -5398,7 +5546,7 @@ class Agent:
5398
5546
  session_metrics = SessionMetrics()
5399
5547
  assistant_message_role = self.model.assistant_message_role if self.model is not None else "assistant"
5400
5548
  for m in messages:
5401
- if m.role == assistant_message_role and m.metrics is not None:
5549
+ if m.role == assistant_message_role and m.metrics is not None and m.from_history is False:
5402
5550
  session_metrics += m.metrics
5403
5551
  return session_metrics
5404
5552
 
@@ -6628,53 +6776,6 @@ class Agent:
6628
6776
 
6629
6777
  return run_data
6630
6778
 
6631
- def register_agent(self) -> None:
6632
- """Register this agent with Agno's platform."""
6633
- self.set_monitoring()
6634
- if not self.monitoring:
6635
- return
6636
-
6637
- from agno.api.agent import AgentCreate, create_agent
6638
-
6639
- try:
6640
- # Ensure we have a valid session_id
6641
- if not self.session_id:
6642
- self.session_id = str(uuid4())
6643
-
6644
- create_agent(
6645
- agent=AgentCreate(
6646
- name=self.name,
6647
- agent_id=self.agent_id,
6648
- workflow_id=self.workflow_id,
6649
- team_id=self.team_id,
6650
- app_id=self.app_id,
6651
- config=self.get_agent_config_dict(),
6652
- )
6653
- )
6654
- except Exception as e:
6655
- log_warning(f"Could not create Agent: {e}")
6656
-
6657
- async def _aregister_agent(self) -> None:
6658
- self.set_monitoring()
6659
- if not self.monitoring:
6660
- return
6661
-
6662
- from agno.api.agent import AgentCreate, acreate_agent
6663
-
6664
- try:
6665
- await acreate_agent(
6666
- agent=AgentCreate(
6667
- name=self.name,
6668
- agent_id=self.agent_id,
6669
- workflow_id=self.workflow_id,
6670
- team_id=self.team_id,
6671
- app_id=self.app_id,
6672
- config=self.get_agent_config_dict(),
6673
- )
6674
- )
6675
- except Exception as e:
6676
- log_debug(f"Could not create Agent app: {e}")
6677
-
6678
6779
  def _log_agent_run(self, session_id: str, user_id: Optional[str] = None) -> None:
6679
6780
  self.set_monitoring()
6680
6781
 
@@ -6696,6 +6797,7 @@ class Agent:
6696
6797
  session_id=agent_session.session_id,
6697
6798
  agent_data=agent_session.to_dict() if self.monitoring else agent_session.telemetry_data(),
6698
6799
  team_session_id=agent_session.team_session_id,
6800
+ workflow_session_id=agent_session.workflow_session_id,
6699
6801
  ),
6700
6802
  monitor=self.monitoring,
6701
6803
  )
@@ -6723,6 +6825,7 @@ class Agent:
6723
6825
  session_id=agent_session.session_id,
6724
6826
  agent_data=agent_session.to_dict() if self.monitoring else agent_session.telemetry_data(),
6725
6827
  team_session_id=agent_session.team_session_id,
6828
+ workflow_session_id=agent_session.workflow_session_id,
6726
6829
  ),
6727
6830
  monitor=self.monitoring,
6728
6831
  )
@@ -6735,9 +6838,10 @@ class Agent:
6735
6838
 
6736
6839
  def print_response(
6737
6840
  self,
6738
- message: Optional[Union[List, Dict, str, Message]] = None,
6841
+ message: Optional[Union[List, Dict, str, Message, BaseModel]] = None,
6739
6842
  *,
6740
6843
  session_id: Optional[str] = None,
6844
+ session_state: Optional[Dict[str, Any]] = None,
6741
6845
  user_id: Optional[str] = None,
6742
6846
  messages: Optional[List[Union[Dict, Message]]] = None,
6743
6847
  audio: Optional[Sequence[Audio]] = None,
@@ -6806,6 +6910,7 @@ class Agent:
6806
6910
  message=message,
6807
6911
  messages=messages,
6808
6912
  session_id=session_id,
6913
+ session_state=session_state,
6809
6914
  user_id=user_id,
6810
6915
  audio=audio,
6811
6916
  images=images,
@@ -7026,6 +7131,7 @@ class Agent:
7026
7131
  message=message,
7027
7132
  messages=messages,
7028
7133
  session_id=session_id,
7134
+ session_state=session_state,
7029
7135
  user_id=user_id,
7030
7136
  audio=audio,
7031
7137
  images=images,
@@ -7181,10 +7287,11 @@ class Agent:
7181
7287
 
7182
7288
  async def aprint_response(
7183
7289
  self,
7184
- message: Optional[Union[List, Dict, str, Message]] = None,
7290
+ message: Optional[Union[List, Dict, str, Message, BaseModel]] = None,
7185
7291
  *,
7186
7292
  messages: Optional[List[Union[Dict, Message]]] = None,
7187
7293
  session_id: Optional[str] = None,
7294
+ session_state: Optional[Dict[str, Any]] = None,
7188
7295
  user_id: Optional[str] = None,
7189
7296
  audio: Optional[Sequence[Audio]] = None,
7190
7297
  images: Optional[Sequence[Image]] = None,
@@ -7252,6 +7359,7 @@ class Agent:
7252
7359
  message=message,
7253
7360
  messages=messages,
7254
7361
  session_id=session_id,
7362
+ session_state=session_state,
7255
7363
  user_id=user_id,
7256
7364
  audio=audio,
7257
7365
  images=images,
@@ -7475,6 +7583,7 @@ class Agent:
7475
7583
  message=message,
7476
7584
  messages=messages,
7477
7585
  session_id=session_id,
7586
+ session_state=session_state,
7478
7587
  user_id=user_id,
7479
7588
  audio=audio,
7480
7589
  images=images,