agno 2.2.0__py3-none-any.whl → 2.2.2__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 (71) hide show
  1. agno/agent/agent.py +751 -575
  2. agno/culture/manager.py +22 -24
  3. agno/db/async_postgres/__init__.py +1 -1
  4. agno/db/dynamo/dynamo.py +0 -2
  5. agno/db/firestore/firestore.py +0 -2
  6. agno/db/gcs_json/gcs_json_db.py +0 -4
  7. agno/db/gcs_json/utils.py +0 -24
  8. agno/db/in_memory/in_memory_db.py +0 -3
  9. agno/db/json/json_db.py +4 -10
  10. agno/db/json/utils.py +0 -24
  11. agno/db/mongo/mongo.py +0 -2
  12. agno/db/mysql/mysql.py +0 -3
  13. agno/db/postgres/__init__.py +1 -1
  14. agno/db/{async_postgres → postgres}/async_postgres.py +19 -22
  15. agno/db/postgres/postgres.py +7 -10
  16. agno/db/postgres/utils.py +106 -2
  17. agno/db/redis/redis.py +0 -2
  18. agno/db/singlestore/singlestore.py +0 -3
  19. agno/db/sqlite/__init__.py +2 -1
  20. agno/db/sqlite/async_sqlite.py +2269 -0
  21. agno/db/sqlite/sqlite.py +0 -2
  22. agno/db/sqlite/utils.py +96 -0
  23. agno/db/surrealdb/surrealdb.py +0 -6
  24. agno/knowledge/knowledge.py +14 -3
  25. agno/knowledge/reader/pptx_reader.py +101 -0
  26. agno/knowledge/reader/reader_factory.py +30 -0
  27. agno/knowledge/reader/tavily_reader.py +194 -0
  28. agno/knowledge/types.py +1 -0
  29. agno/memory/manager.py +28 -25
  30. agno/models/anthropic/claude.py +63 -6
  31. agno/models/base.py +255 -36
  32. agno/models/response.py +69 -0
  33. agno/os/router.py +7 -5
  34. agno/os/routers/memory/memory.py +2 -1
  35. agno/os/routers/memory/schemas.py +5 -2
  36. agno/os/schema.py +26 -20
  37. agno/os/utils.py +9 -2
  38. agno/run/agent.py +28 -30
  39. agno/run/base.py +17 -1
  40. agno/run/team.py +28 -29
  41. agno/run/workflow.py +32 -17
  42. agno/session/agent.py +3 -0
  43. agno/session/summary.py +4 -1
  44. agno/session/team.py +1 -1
  45. agno/team/team.py +620 -374
  46. agno/tools/dalle.py +2 -4
  47. agno/tools/eleven_labs.py +23 -25
  48. agno/tools/function.py +40 -0
  49. agno/tools/mcp/__init__.py +10 -0
  50. agno/tools/mcp/mcp.py +324 -0
  51. agno/tools/mcp/multi_mcp.py +347 -0
  52. agno/tools/mcp/params.py +24 -0
  53. agno/tools/slack.py +18 -3
  54. agno/tools/tavily.py +146 -0
  55. agno/utils/agent.py +366 -1
  56. agno/utils/mcp.py +92 -2
  57. agno/utils/media.py +166 -1
  58. agno/utils/message.py +60 -0
  59. agno/utils/print_response/workflow.py +17 -1
  60. agno/utils/team.py +89 -1
  61. agno/workflow/step.py +0 -1
  62. agno/workflow/types.py +10 -15
  63. agno/workflow/workflow.py +86 -1
  64. {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/METADATA +31 -25
  65. {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/RECORD +68 -64
  66. agno/db/async_postgres/schemas.py +0 -139
  67. agno/db/async_postgres/utils.py +0 -347
  68. agno/tools/mcp.py +0 -679
  69. {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/WHEEL +0 -0
  70. {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/licenses/LICENSE +0 -0
  71. {agno-2.2.0.dist-info → agno-2.2.2.dist-info}/top_level.txt +0 -0
agno/team/team.py CHANGED
@@ -59,18 +59,35 @@ from agno.run.cancel import (
59
59
  from agno.run.messages import RunMessages
60
60
  from agno.run.team import TeamRunEvent, TeamRunInput, TeamRunOutput, TeamRunOutputEvent
61
61
  from agno.session import SessionSummaryManager, TeamSession, WorkflowSession
62
+ from agno.session.summary import SessionSummary
62
63
  from agno.tools import Toolkit
63
64
  from agno.tools.function import Function
64
65
  from agno.utils.agent import (
66
+ aget_chat_history_util,
67
+ aget_last_run_output_util,
68
+ aget_run_output_util,
69
+ aget_session_metrics_util,
70
+ aget_session_name_util,
71
+ aget_session_state_util,
72
+ aset_session_name_util,
73
+ aupdate_session_state_util,
65
74
  await_for_background_tasks,
66
75
  await_for_background_tasks_stream,
67
76
  collect_joint_audios,
68
77
  collect_joint_files,
69
78
  collect_joint_images,
70
79
  collect_joint_videos,
80
+ get_chat_history_util,
81
+ get_last_run_output_util,
82
+ get_run_output_util,
83
+ get_session_metrics_util,
84
+ get_session_name_util,
85
+ get_session_state_util,
71
86
  scrub_history_messages_from_run_output,
72
87
  scrub_media_from_run_output,
73
88
  scrub_tool_results_from_run_output,
89
+ set_session_name_util,
90
+ update_session_state_util,
74
91
  wait_for_background_tasks,
75
92
  wait_for_background_tasks_stream,
76
93
  )
@@ -111,7 +128,7 @@ from agno.utils.log import (
111
128
  use_team_logger,
112
129
  )
113
130
  from agno.utils.merge_dict import merge_dictionaries
114
- from agno.utils.message import get_text_from_message
131
+ from agno.utils.message import filter_tool_calls, get_text_from_message
115
132
  from agno.utils.print_response.team import (
116
133
  aprint_response,
117
134
  aprint_response_stream,
@@ -131,7 +148,16 @@ from agno.utils.response import (
131
148
  )
132
149
  from agno.utils.safe_formatter import SafeFormatter
133
150
  from agno.utils.string import generate_id_from_name, parse_response_model_str
134
- from agno.utils.team import format_member_agent_task, get_member_id
151
+ from agno.utils.team import (
152
+ add_interaction_to_team_run_context,
153
+ format_member_agent_task,
154
+ get_member_id,
155
+ get_team_member_interactions_str,
156
+ get_team_run_context_audio,
157
+ get_team_run_context_files,
158
+ get_team_run_context_images,
159
+ get_team_run_context_videos,
160
+ )
135
161
  from agno.utils.timer import Timer
136
162
 
137
163
 
@@ -189,12 +215,6 @@ class Team:
189
215
  # If True, cache the current Team session in memory for faster access
190
216
  cache_session: bool = False
191
217
 
192
- # --- Team history settings ---
193
- # add_history_to_context=true adds messages from the chat history to the messages list sent to the Model. This only applies to the team leader, not the members.
194
- add_history_to_context: bool = False
195
- # Number of historical runs to include in the messages
196
- num_history_runs: int = 3
197
-
198
218
  # Add this flag to control if the workflow should send the team history to the members. This means sending the team-level history to the members, not the agent-level history.
199
219
  add_team_history_to_members: bool = False
200
220
  # Number of historical runs to include in the messages sent to the members
@@ -354,6 +374,14 @@ class Team:
354
374
  # If True, the team adds session summaries to the context
355
375
  add_session_summary_to_context: Optional[bool] = None
356
376
 
377
+ # --- Team History ---
378
+ # add_history_to_context=true adds messages from the chat history to the messages list sent to the Model.
379
+ add_history_to_context: bool = False
380
+ # Number of historical runs to include in the messages
381
+ num_history_runs: int = 3
382
+ # Maximum number of tool calls to include from history (None = no limit)
383
+ max_tool_calls_from_history: Optional[int] = None
384
+
357
385
  # --- Team Storage ---
358
386
  # Metadata stored with this team
359
387
  metadata: Optional[Dict[str, Any]] = None
@@ -421,8 +449,6 @@ class Team:
421
449
  overwrite_db_session_state: bool = False,
422
450
  resolve_in_context: bool = True,
423
451
  cache_session: bool = False,
424
- add_history_to_context: bool = False,
425
- num_history_runs: int = 3,
426
452
  add_team_history_to_members: bool = False,
427
453
  num_team_history_runs: int = 3,
428
454
  search_session_history: Optional[bool] = False,
@@ -458,6 +484,8 @@ class Team:
458
484
  store_tool_messages: bool = True,
459
485
  store_history_messages: bool = True,
460
486
  send_media_to_model: bool = True,
487
+ add_history_to_context: bool = False,
488
+ num_history_runs: int = 3,
461
489
  tools: Optional[List[Union[Toolkit, Callable, Function, Dict]]] = None,
462
490
  tool_call_limit: Optional[int] = None,
463
491
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
@@ -480,6 +508,7 @@ class Team:
480
508
  enable_session_summaries: bool = False,
481
509
  session_summary_manager: Optional[SessionSummaryManager] = None,
482
510
  add_session_summary_to_context: Optional[bool] = None,
511
+ max_tool_calls_from_history: Optional[int] = None,
483
512
  metadata: Optional[Dict[str, Any]] = None,
484
513
  reasoning: bool = False,
485
514
  reasoning_model: Optional[Model] = None,
@@ -591,6 +620,9 @@ class Team:
591
620
  self.enable_session_summaries = enable_session_summaries
592
621
  self.session_summary_manager = session_summary_manager
593
622
  self.add_session_summary_to_context = add_session_summary_to_context
623
+ self.add_history_to_context = add_history_to_context
624
+ self.num_history_runs = num_history_runs
625
+ self.max_tool_calls_from_history = max_tool_calls_from_history
594
626
  self.metadata = metadata
595
627
 
596
628
  self.reasoning = reasoning
@@ -634,21 +666,20 @@ class Team:
634
666
  self.videos: Optional[List[Video]] = None
635
667
 
636
668
  # Team session
637
- self._team_session: Optional[TeamSession] = None
669
+ self._cached_session: Optional[TeamSession] = None
638
670
 
639
671
  self._tool_instructions: Optional[List[str]] = None
640
- self._functions_for_model: Optional[Dict[str, Function]] = None
641
- self._tools_for_model: Optional[List[Dict[str, Any]]] = None
642
672
 
643
673
  # True if we should parse a member response model
644
674
  self._member_response_model: Optional[Type[BaseModel]] = None
645
675
 
646
676
  self._formatter: Optional[SafeFormatter] = None
647
677
 
648
- self._rebuild_tools = True
649
-
650
678
  self._hooks_normalised = False
651
679
 
680
+ # List of MCP tools that were initialized on the last run
681
+ self._mcp_tools_initialized_on_run: List[Any] = []
682
+
652
683
  # Lazy-initialized shared thread pool executor for background tasks (memory, cultural knowledge, etc.)
653
684
  self._background_executor: Optional[Any] = None
654
685
 
@@ -669,6 +700,10 @@ class Team:
669
700
  def should_parse_structured_output(self) -> bool:
670
701
  return self.output_schema is not None and self.parse_response and self.parser_model is None
671
702
 
703
+ @property
704
+ def cached_session(self) -> Optional[TeamSession]:
705
+ return self._cached_session
706
+
672
707
  def set_id(self) -> None:
673
708
  """Set the ID of the team if not set yet.
674
709
 
@@ -751,10 +786,21 @@ class Team:
751
786
  member.team_id = self.id
752
787
  member.set_id()
753
788
 
789
+ # Inherit team models if agent has no explicit model
790
+ for model_type in ["model", "reasoning_model", "parser_model", "output_model"]:
791
+ if getattr(member, model_type) is None and getattr(self, model_type) is not None:
792
+ setattr(member, model_type, getattr(self, model_type))
793
+ log_info(
794
+ f"Agent '{member.name or member.id}' inheriting {model_type} from Team: {getattr(self, model_type).id}"
795
+ )
796
+
754
797
  elif isinstance(member, Team):
755
798
  member.parent_team_id = self.id
799
+ # Initialize the sub-team's model first so it has its model set
800
+ member._set_default_model()
801
+ # Then let the sub-team initialize its own members so they inherit from the sub-team
756
802
  for sub_member in member.members:
757
- self._initialize_member(sub_member, debug_mode=debug_mode)
803
+ member._initialize_member(sub_member, debug_mode=debug_mode)
758
804
 
759
805
  def _set_default_model(self) -> None:
760
806
  # Set the default model
@@ -886,7 +932,8 @@ class Team:
886
932
  def set_tools(self, tools: List[Union[Toolkit, Callable, Function, Dict]]):
887
933
  self.tools = tools
888
934
 
889
- def cancel_run(self, run_id: str) -> bool:
935
+ @staticmethod
936
+ def cancel_run(run_id: str) -> bool:
890
937
  """Cancel a running team execution.
891
938
 
892
939
  Args:
@@ -897,6 +944,21 @@ class Team:
897
944
  """
898
945
  return cancel_run_global(run_id)
899
946
 
947
+ async def _connect_mcp_tools(self) -> None:
948
+ """Connect the MCP tools to the agent."""
949
+ if self.tools is not None:
950
+ for tool in self.tools:
951
+ if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"] and not tool.initialized: # type: ignore
952
+ # Connect the MCP server
953
+ await tool.connect() # type: ignore
954
+ self._mcp_tools_initialized_on_run.append(tool)
955
+
956
+ async def _disconnect_mcp_tools(self) -> None:
957
+ """Disconnect the MCP tools from the agent."""
958
+ for tool in self._mcp_tools_initialized_on_run:
959
+ await tool.close()
960
+ self._mcp_tools_initialized_on_run = []
961
+
900
962
  def _execute_pre_hooks(
901
963
  self,
902
964
  hooks: Optional[List[Callable[..., Any]]],
@@ -1006,7 +1068,9 @@ class Team:
1006
1068
  # Filter arguments to only include those that the hook accepts
1007
1069
  filtered_args = filter_hook_args(hook, all_args)
1008
1070
 
1009
- if asyncio.iscoroutinefunction(hook):
1071
+ from inspect import iscoroutinefunction
1072
+
1073
+ if iscoroutinefunction(hook):
1010
1074
  await hook(**filtered_args)
1011
1075
  else:
1012
1076
  # Synchronous function
@@ -1137,7 +1201,9 @@ class Team:
1137
1201
  # Filter arguments to only include those that the hook accepts
1138
1202
  filtered_args = filter_hook_args(hook, all_args)
1139
1203
 
1140
- if asyncio.iscoroutinefunction(hook):
1204
+ from inspect import iscoroutinefunction
1205
+
1206
+ if iscoroutinefunction(hook):
1141
1207
  await hook(**filtered_args)
1142
1208
  else:
1143
1209
  hook(**filtered_args)
@@ -1218,7 +1284,7 @@ class Team:
1218
1284
  # Initialize team run context
1219
1285
  team_run_context: Dict[str, Any] = {}
1220
1286
 
1221
- self.determine_tools_for_model(
1287
+ _tools = self._determine_tools_for_model(
1222
1288
  model=self.model,
1223
1289
  run_response=run_response,
1224
1290
  team_run_context=team_run_context,
@@ -1257,6 +1323,7 @@ class Team:
1257
1323
  add_dependencies_to_context=add_dependencies_to_context,
1258
1324
  add_session_state_to_context=add_session_state_to_context,
1259
1325
  metadata=metadata,
1326
+ tools=_tools,
1260
1327
  **kwargs,
1261
1328
  )
1262
1329
  if len(run_messages.messages) == 0:
@@ -1286,8 +1353,7 @@ class Team:
1286
1353
  model_response: ModelResponse = self.model.response(
1287
1354
  messages=run_messages.messages,
1288
1355
  response_format=response_format,
1289
- tools=self._tools_for_model,
1290
- functions=self._functions_for_model,
1356
+ tools=_tools,
1291
1357
  tool_choice=self.tool_choice,
1292
1358
  tool_call_limit=self.tool_call_limit,
1293
1359
  send_media_to_model=self.send_media_to_model,
@@ -1320,6 +1386,9 @@ class Team:
1320
1386
  hooks=self.post_hooks, # type: ignore
1321
1387
  run_output=run_response,
1322
1388
  session=session,
1389
+ session_state=session_state,
1390
+ dependencies=dependencies,
1391
+ metadata=metadata,
1323
1392
  user_id=user_id,
1324
1393
  debug_mode=debug_mode,
1325
1394
  **kwargs,
@@ -1427,7 +1496,7 @@ class Team:
1427
1496
  # Initialize team run context
1428
1497
  team_run_context: Dict[str, Any] = {}
1429
1498
 
1430
- self.determine_tools_for_model(
1499
+ _tools = self._determine_tools_for_model(
1431
1500
  model=self.model,
1432
1501
  run_response=run_response,
1433
1502
  team_run_context=team_run_context,
@@ -1466,6 +1535,7 @@ class Team:
1466
1535
  add_dependencies_to_context=add_dependencies_to_context,
1467
1536
  add_session_state_to_context=add_session_state_to_context,
1468
1537
  metadata=metadata,
1538
+ tools=_tools,
1469
1539
  **kwargs,
1470
1540
  )
1471
1541
  if len(run_messages.messages) == 0:
@@ -1509,6 +1579,7 @@ class Team:
1509
1579
  session=session,
1510
1580
  run_response=run_response,
1511
1581
  run_messages=run_messages,
1582
+ tools=_tools,
1512
1583
  response_format=response_format,
1513
1584
  stream_events=stream_events,
1514
1585
  ):
@@ -1519,6 +1590,7 @@ class Team:
1519
1590
  session=session,
1520
1591
  run_response=run_response,
1521
1592
  run_messages=run_messages,
1593
+ tools=_tools,
1522
1594
  response_format=response_format,
1523
1595
  stream_events=stream_events,
1524
1596
  ):
@@ -2032,7 +2104,8 @@ class Team:
2032
2104
  # 4. Determine tools for model
2033
2105
  team_run_context: Dict[str, Any] = {}
2034
2106
  self.model = cast(Model, self.model)
2035
- self.determine_tools_for_model(
2107
+ await self._check_and_refresh_mcp_tools()
2108
+ _tools = self._determine_tools_for_model(
2036
2109
  model=self.model,
2037
2110
  run_response=run_response,
2038
2111
  team_run_context=team_run_context,
@@ -2070,6 +2143,8 @@ class Team:
2070
2143
  dependencies=dependencies,
2071
2144
  add_dependencies_to_context=add_dependencies_to_context,
2072
2145
  add_session_state_to_context=add_session_state_to_context,
2146
+ metadata=metadata,
2147
+ tools=_tools,
2073
2148
  **kwargs,
2074
2149
  )
2075
2150
 
@@ -2077,8 +2152,6 @@ class Team:
2077
2152
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
2078
2153
 
2079
2154
  # 6. Start memory creation in background task
2080
- import asyncio
2081
-
2082
2155
  memory_task = None
2083
2156
  if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2084
2157
  log_debug("Starting memory creation in background task.")
@@ -2098,8 +2171,7 @@ class Team:
2098
2171
  # 8. Get the model response for the team leader
2099
2172
  model_response = await self.model.aresponse(
2100
2173
  messages=run_messages.messages,
2101
- tools=self._tools_for_model,
2102
- functions=self._functions_for_model,
2174
+ tools=_tools,
2103
2175
  tool_choice=self.tool_choice,
2104
2176
  tool_call_limit=self.tool_call_limit,
2105
2177
  response_format=response_format,
@@ -2133,6 +2205,9 @@ class Team:
2133
2205
  hooks=self.post_hooks, # type: ignore
2134
2206
  run_output=run_response,
2135
2207
  session=team_session,
2208
+ session_state=session_state,
2209
+ dependencies=dependencies,
2210
+ metadata=metadata,
2136
2211
  user_id=user_id,
2137
2212
  debug_mode=debug_mode,
2138
2213
  **kwargs,
@@ -2177,6 +2252,7 @@ class Team:
2177
2252
 
2178
2253
  return run_response
2179
2254
  finally:
2255
+ await self._disconnect_mcp_tools()
2180
2256
  # Cancel the memory task if it's still running
2181
2257
  if memory_task is not None and not memory_task.done():
2182
2258
  memory_task.cancel()
@@ -2265,7 +2341,8 @@ class Team:
2265
2341
  # 5. Determine tools for model
2266
2342
  team_run_context: Dict[str, Any] = {}
2267
2343
  self.model = cast(Model, self.model)
2268
- self.determine_tools_for_model(
2344
+ await self._check_and_refresh_mcp_tools()
2345
+ _tools = self._determine_tools_for_model(
2269
2346
  model=self.model,
2270
2347
  run_response=run_response,
2271
2348
  team_run_context=team_run_context,
@@ -2302,14 +2379,13 @@ class Team:
2302
2379
  add_dependencies_to_context=add_dependencies_to_context,
2303
2380
  add_session_state_to_context=add_session_state_to_context,
2304
2381
  metadata=metadata,
2382
+ tools=_tools,
2305
2383
  **kwargs,
2306
2384
  )
2307
2385
 
2308
2386
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
2309
2387
 
2310
2388
  # 7. Start memory creation in background task
2311
- import asyncio
2312
-
2313
2389
  memory_task = None
2314
2390
  if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2315
2391
  log_debug("Starting memory creation in background task.")
@@ -2349,6 +2425,7 @@ class Team:
2349
2425
  session=team_session,
2350
2426
  run_response=run_response,
2351
2427
  run_messages=run_messages,
2428
+ tools=_tools,
2352
2429
  response_format=response_format,
2353
2430
  stream_events=stream_events,
2354
2431
  ):
@@ -2359,6 +2436,7 @@ class Team:
2359
2436
  session=team_session,
2360
2437
  run_response=run_response,
2361
2438
  run_messages=run_messages,
2439
+ tools=_tools,
2362
2440
  response_format=response_format,
2363
2441
  stream_events=stream_events,
2364
2442
  ):
@@ -2500,6 +2578,7 @@ class Team:
2500
2578
  await self._acleanup_and_store(run_response=run_response, session=team_session)
2501
2579
 
2502
2580
  finally:
2581
+ await self._disconnect_mcp_tools()
2503
2582
  # Cancel the memory task if it's still running
2504
2583
  if memory_task is not None and not memory_task.done():
2505
2584
  memory_task.cancel()
@@ -2861,6 +2940,7 @@ class Team:
2861
2940
  session: TeamSession,
2862
2941
  run_response: TeamRunOutput,
2863
2942
  run_messages: RunMessages,
2943
+ tools: Optional[List[Union[Function, dict]]] = None,
2864
2944
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2865
2945
  stream_events: bool = False,
2866
2946
  ) -> Iterator[Union[TeamRunOutputEvent, RunOutputEvent]]:
@@ -2880,8 +2960,7 @@ class Team:
2880
2960
  for model_response_event in self.model.response_stream(
2881
2961
  messages=run_messages.messages,
2882
2962
  response_format=response_format,
2883
- tools=self._tools_for_model,
2884
- functions=self._functions_for_model,
2963
+ tools=tools,
2885
2964
  tool_choice=self.tool_choice,
2886
2965
  tool_call_limit=self.tool_call_limit,
2887
2966
  stream_model_response=stream_model_response,
@@ -2943,6 +3022,7 @@ class Team:
2943
3022
  session: TeamSession,
2944
3023
  run_response: TeamRunOutput,
2945
3024
  run_messages: RunMessages,
3025
+ tools: Optional[List[Union[Function, dict]]] = None,
2946
3026
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2947
3027
  stream_events: bool = False,
2948
3028
  ) -> AsyncIterator[Union[TeamRunOutputEvent, RunOutputEvent]]:
@@ -2962,8 +3042,7 @@ class Team:
2962
3042
  model_stream = self.model.aresponse_stream(
2963
3043
  messages=run_messages.messages,
2964
3044
  response_format=response_format,
2965
- tools=self._tools_for_model,
2966
- functions=self._functions_for_model,
3045
+ tools=tools,
2967
3046
  tool_choice=self.tool_choice,
2968
3047
  tool_call_limit=self.tool_call_limit,
2969
3048
  stream_model_response=stream_model_response,
@@ -3367,7 +3446,7 @@ class Team:
3367
3446
  self._update_session_metrics(session=session)
3368
3447
 
3369
3448
  # Save session to memory
3370
- self.save_session(session=session)
3449
+ await self.asave_session(session=session)
3371
3450
 
3372
3451
  def _make_memories(
3373
3452
  self,
@@ -4870,7 +4949,30 @@ class Team:
4870
4949
  except Exception as e:
4871
4950
  log_warning(f"Failed to resolve context for '{key}': {e}")
4872
4951
 
4873
- def determine_tools_for_model(
4952
+ async def _check_and_refresh_mcp_tools(self) -> None:
4953
+ # Connect MCP tools
4954
+ await self._connect_mcp_tools()
4955
+
4956
+ # Add provided tools
4957
+ if self.tools is not None:
4958
+ for tool in self.tools:
4959
+ if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"]:
4960
+ if tool.refresh_connection: # type: ignore
4961
+ try:
4962
+ is_alive = await tool.is_alive() # type: ignore
4963
+ if not is_alive:
4964
+ await tool.connect(force=True) # type: ignore
4965
+ except (RuntimeError, BaseException) as e:
4966
+ log_warning(f"Failed to check if MCP tool is alive: {e}")
4967
+ continue
4968
+
4969
+ try:
4970
+ await tool.build_tools() # type: ignore
4971
+ except (RuntimeError, BaseException) as e:
4972
+ log_warning(f"Failed to build tools for {str(tool)}: {e}")
4973
+ continue
4974
+
4975
+ def _determine_tools_for_model(
4874
4976
  self,
4875
4977
  model: Model,
4876
4978
  run_response: TeamRunOutput,
@@ -4891,13 +4993,18 @@ class Team:
4891
4993
  add_dependencies_to_context: Optional[bool] = None,
4892
4994
  add_session_state_to_context: Optional[bool] = None,
4893
4995
  metadata: Optional[Dict[str, Any]] = None,
4894
- ) -> None:
4996
+ check_mcp_tools: bool = True,
4997
+ ) -> List[Union[Function, dict]]:
4895
4998
  # Prepare tools
4896
4999
  _tools: List[Union[Toolkit, Callable, Function, Dict]] = []
4897
5000
 
4898
5001
  # Add provided tools
4899
5002
  if self.tools is not None:
4900
5003
  for tool in self.tools:
5004
+ if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"]:
5005
+ # Only add the tool if it successfully connected and built its tools
5006
+ if check_mcp_tools and not tool.initialized: # type: ignore
5007
+ continue
4901
5008
  _tools.append(tool)
4902
5009
 
4903
5010
  if self.read_chat_history:
@@ -4912,7 +5019,7 @@ class Team:
4912
5019
  if self.search_session_history:
4913
5020
  _tools.append(
4914
5021
  self._get_previous_sessions_messages_function(
4915
- num_history_sessions=self.num_history_sessions, user_id=user_id
5022
+ num_history_sessions=self.num_history_sessions, user_id=user_id, async_mode=async_mode
4916
5023
  )
4917
5024
  )
4918
5025
 
@@ -4990,14 +5097,13 @@ class Team:
4990
5097
  if self.get_member_information_tool:
4991
5098
  _tools.append(self.get_member_information)
4992
5099
 
4993
- self._functions_for_model = {}
4994
- self._tools_for_model = []
4995
- self._tool_instructions = []
4996
-
4997
5100
  # Get Agent tools
4998
5101
  if len(_tools) > 0:
4999
5102
  log_debug("Processing tools for model")
5000
5103
 
5104
+ _function_names = []
5105
+ _functions: List[Union[Function, dict]] = []
5106
+
5001
5107
  # Check if we need strict mode for the model
5002
5108
  strict = False
5003
5109
  if self.output_schema is not None and not self.use_json_mode and model.supports_native_structured_outputs:
@@ -5007,25 +5113,25 @@ class Team:
5007
5113
  if isinstance(tool, Dict):
5008
5114
  # If a dict is passed, it is a builtin tool
5009
5115
  # that is run by the model provider and not the Agent
5010
- self._tools_for_model.append(tool)
5116
+ _functions.append(tool)
5011
5117
  log_debug(f"Included builtin tool {tool}")
5012
5118
 
5013
5119
  elif isinstance(tool, Toolkit):
5014
5120
  # For each function in the toolkit and process entrypoint
5015
- for name, func in tool.functions.items():
5016
- # If the function does not exist in self.functions
5017
- if name not in self._functions_for_model:
5018
- func._team = self
5019
- func._session_state = session_state
5020
- func._dependencies = dependencies
5021
- func.process_entrypoint(strict=strict)
5022
- if strict:
5023
- func.strict = True
5024
- if self.tool_hooks:
5025
- func.tool_hooks = self.tool_hooks
5026
- self._functions_for_model[name] = func
5027
- self._tools_for_model.append({"type": "function", "function": func.to_dict()})
5028
- log_debug(f"Added tool {name} from {tool.name}")
5121
+ for name, _func in tool.functions.items():
5122
+ if name in _function_names:
5123
+ continue
5124
+ _function_names.append(name)
5125
+ _func = _func.model_copy(deep=True)
5126
+
5127
+ _func._team = self
5128
+ _func.process_entrypoint(strict=strict)
5129
+ if strict:
5130
+ _func.strict = True
5131
+ if self.tool_hooks:
5132
+ _func.tool_hooks = self.tool_hooks
5133
+ _functions.append(_func)
5134
+ log_debug(f"Added tool {_func.name} from {tool.name}")
5029
5135
 
5030
5136
  # Add instructions from the toolkit
5031
5137
  if tool.add_instructions and tool.instructions is not None:
@@ -5034,18 +5140,18 @@ class Team:
5034
5140
  self._tool_instructions.append(tool.instructions)
5035
5141
 
5036
5142
  elif isinstance(tool, Function):
5037
- if tool.name not in self._functions_for_model:
5038
- tool._team = self
5039
- tool._session_state = session_state
5040
- tool._dependencies = dependencies
5041
- tool.process_entrypoint(strict=strict)
5042
- if strict and tool.strict is None:
5043
- tool.strict = True
5044
- if self.tool_hooks:
5045
- tool.tool_hooks = self.tool_hooks
5046
- self._functions_for_model[tool.name] = tool
5047
- self._tools_for_model.append({"type": "function", "function": tool.to_dict()})
5048
- log_debug(f"Added tool {tool.name}")
5143
+ if tool.name in _function_names:
5144
+ continue
5145
+ _function_names.append(tool.name)
5146
+ tool = tool.model_copy(deep=True)
5147
+ tool._team = self
5148
+ tool.process_entrypoint(strict=strict)
5149
+ if strict and tool.strict is None:
5150
+ tool.strict = True
5151
+ if self.tool_hooks:
5152
+ tool.tool_hooks = self.tool_hooks
5153
+ _functions.append(tool)
5154
+ log_debug(f"Added tool {tool.name}")
5049
5155
 
5050
5156
  # Add instructions from the Function
5051
5157
  if tool.add_instructions and tool.instructions is not None:
@@ -5056,43 +5162,49 @@ class Team:
5056
5162
  elif callable(tool):
5057
5163
  # We add the tools, which are callable functions
5058
5164
  try:
5059
- func = Function.from_callable(tool, strict=strict)
5060
- func._team = self
5061
- func._session_state = session_state
5062
- func._dependencies = dependencies
5165
+ _func = Function.from_callable(tool, strict=strict)
5166
+ _func = _func.model_copy(deep=True)
5167
+ if _func.name in _function_names:
5168
+ continue
5169
+ _function_names.append(_func.name)
5170
+
5171
+ _func._team = self
5063
5172
  if strict:
5064
- func.strict = True
5173
+ _func.strict = True
5065
5174
  if self.tool_hooks:
5066
- func.tool_hooks = self.tool_hooks
5067
- self._functions_for_model[func.name] = func
5068
- self._tools_for_model.append({"type": "function", "function": func.to_dict()})
5069
- log_debug(f"Added tool {func.name}")
5175
+ _func.tool_hooks = self.tool_hooks
5176
+ _functions.append(_func)
5177
+ log_debug(f"Added tool {_func.name}")
5070
5178
  except Exception as e:
5071
5179
  log_warning(f"Could not add tool {tool}: {e}")
5072
5180
 
5073
- if self._functions_for_model:
5181
+ if _functions:
5074
5182
  from inspect import signature
5075
5183
 
5076
5184
  # Check if any functions need media before collecting
5077
5185
  needs_media = any(
5078
5186
  any(param in signature(func.entrypoint).parameters for param in ["images", "videos", "audios", "files"])
5079
- for func in self._functions_for_model.values()
5080
- if func.entrypoint is not None
5187
+ for func in _functions
5188
+ if isinstance(func, Function) and func.entrypoint is not None
5081
5189
  )
5082
5190
 
5083
- if needs_media:
5084
- # Only collect media if functions actually need them
5085
- joint_images = collect_joint_images(run_response.input, session) # type: ignore
5086
- joint_files = collect_joint_files(run_response.input) # type: ignore
5087
- joint_audios = collect_joint_audios(run_response.input, session) # type: ignore
5088
- joint_videos = collect_joint_videos(run_response.input, session) # type: ignore
5191
+ # Only collect media if functions actually need them
5192
+ joint_images = collect_joint_images(run_response.input, session) if needs_media else None # type: ignore
5193
+ joint_files = collect_joint_files(run_response.input) if needs_media else None # type: ignore
5194
+ joint_audios = collect_joint_audios(run_response.input, session) if needs_media else None # type: ignore
5195
+ joint_videos = collect_joint_videos(run_response.input, session) if needs_media else None # type: ignore
5089
5196
 
5090
- for func in self._functions_for_model.values():
5197
+ for func in _functions: # type: ignore
5198
+ if isinstance(func, Function):
5199
+ func._session_state = session_state
5200
+ func._dependencies = dependencies
5091
5201
  func._images = joint_images
5092
5202
  func._files = joint_files
5093
5203
  func._audios = joint_audios
5094
5204
  func._videos = joint_videos
5095
5205
 
5206
+ return _functions
5207
+
5096
5208
  def get_members_system_message_content(self, indent: int = 0) -> str:
5097
5209
  system_message_content = ""
5098
5210
  for idx, member in enumerate(self.members):
@@ -5136,6 +5248,7 @@ class Team:
5136
5248
  files: Optional[Sequence[File]] = None,
5137
5249
  dependencies: Optional[Dict[str, Any]] = None,
5138
5250
  metadata: Optional[Dict[str, Any]] = None,
5251
+ tools: Optional[List[Union[Function, dict]]] = None,
5139
5252
  add_session_state_to_context: Optional[bool] = None,
5140
5253
  ) -> Optional[Message]:
5141
5254
  """Get the system message for the team."""
@@ -5189,7 +5302,7 @@ class Team:
5189
5302
  instructions.extend(_instructions)
5190
5303
 
5191
5304
  # 1.2 Add instructions from the Model
5192
- _model_instructions = self.model.get_instructions_for_model(self._tools_for_model)
5305
+ _model_instructions = self.model.get_instructions_for_model(tools)
5193
5306
  if _model_instructions is not None:
5194
5307
  instructions.extend(_model_instructions)
5195
5308
 
@@ -5392,7 +5505,7 @@ class Team:
5392
5505
  metadata=metadata,
5393
5506
  )
5394
5507
 
5395
- system_message_from_model = self.model.get_system_message_for_model(self._tools_for_model)
5508
+ system_message_from_model = self.model.get_system_message_for_model(tools)
5396
5509
  if system_message_from_model is not None:
5397
5510
  system_message_content += system_message_from_model
5398
5511
 
@@ -5429,6 +5542,7 @@ class Team:
5429
5542
  files: Optional[Sequence[File]] = None,
5430
5543
  dependencies: Optional[Dict[str, Any]] = None,
5431
5544
  metadata: Optional[Dict[str, Any]] = None,
5545
+ tools: Optional[List[Union[Function, dict]]] = None,
5432
5546
  add_session_state_to_context: Optional[bool] = None,
5433
5547
  ) -> Optional[Message]:
5434
5548
  """Get the system message for the team."""
@@ -5482,7 +5596,7 @@ class Team:
5482
5596
  instructions.extend(_instructions)
5483
5597
 
5484
5598
  # 1.2 Add instructions from the Model
5485
- _model_instructions = self.model.get_instructions_for_model(self._tools_for_model)
5599
+ _model_instructions = self.model.get_instructions_for_model(tools)
5486
5600
  if _model_instructions is not None:
5487
5601
  instructions.extend(_model_instructions)
5488
5602
 
@@ -5690,7 +5804,7 @@ class Team:
5690
5804
  metadata=metadata,
5691
5805
  )
5692
5806
 
5693
- system_message_from_model = self.model.get_system_message_for_model(self._tools_for_model)
5807
+ system_message_from_model = self.model.get_system_message_for_model(tools)
5694
5808
  if system_message_from_model is not None:
5695
5809
  system_message_content += system_message_from_model
5696
5810
 
@@ -5737,6 +5851,7 @@ class Team:
5737
5851
  add_dependencies_to_context: Optional[bool] = None,
5738
5852
  add_session_state_to_context: Optional[bool] = None,
5739
5853
  metadata: Optional[Dict[str, Any]] = None,
5854
+ tools: Optional[List[Union[Function, dict]]] = None,
5740
5855
  **kwargs: Any,
5741
5856
  ) -> RunMessages:
5742
5857
  """This function returns a RunMessages object with the following attributes:
@@ -5767,6 +5882,7 @@ class Team:
5767
5882
  dependencies=dependencies,
5768
5883
  metadata=metadata,
5769
5884
  add_session_state_to_context=add_session_state_to_context,
5885
+ tools=tools,
5770
5886
  )
5771
5887
  if system_message is not None:
5772
5888
  run_messages.system_message = system_message
@@ -5824,6 +5940,10 @@ class Team:
5824
5940
  for _msg in history_copy:
5825
5941
  _msg.from_history = True
5826
5942
 
5943
+ # Filter tool calls from history messages
5944
+ if self.max_tool_calls_from_history is not None:
5945
+ filter_tool_calls(history_copy, self.max_tool_calls_from_history)
5946
+
5827
5947
  log_debug(f"Adding {len(history_copy)} messages from history")
5828
5948
 
5829
5949
  # Extend the messages with the history
@@ -5871,6 +5991,7 @@ class Team:
5871
5991
  add_dependencies_to_context: Optional[bool] = None,
5872
5992
  add_session_state_to_context: Optional[bool] = None,
5873
5993
  metadata: Optional[Dict[str, Any]] = None,
5994
+ tools: Optional[List[Union[Function, dict]]] = None,
5874
5995
  **kwargs: Any,
5875
5996
  ) -> RunMessages:
5876
5997
  """This function returns a RunMessages object with the following attributes:
@@ -5901,6 +6022,7 @@ class Team:
5901
6022
  dependencies=dependencies,
5902
6023
  metadata=metadata,
5903
6024
  add_session_state_to_context=add_session_state_to_context,
6025
+ tools=tools,
5904
6026
  )
5905
6027
  if system_message is not None:
5906
6028
  run_messages.system_message = system_message
@@ -5951,6 +6073,10 @@ class Team:
5951
6073
  for _msg in history_copy:
5952
6074
  _msg.from_history = True
5953
6075
 
6076
+ # Filter tool calls from history messages
6077
+ if self.max_tool_calls_from_history is not None:
6078
+ filter_tool_calls(history_copy, self.max_tool_calls_from_history)
6079
+
5954
6080
  log_debug(f"Adding {len(history_copy)} messages from history")
5955
6081
 
5956
6082
  # Extend the messages with the history
@@ -6494,7 +6620,7 @@ class Team:
6494
6620
  return f"Updated session state: {session_state}"
6495
6621
 
6496
6622
  def _get_previous_sessions_messages_function(
6497
- self, num_history_sessions: Optional[int] = 2, user_id: Optional[str] = None
6623
+ self, num_history_sessions: Optional[int] = 2, user_id: Optional[str] = None, async_mode: bool = False
6498
6624
  ):
6499
6625
  """Factory function to create a get_previous_session_messages function.
6500
6626
 
@@ -6571,13 +6697,22 @@ class Team:
6571
6697
  return "Previous session messages not available"
6572
6698
 
6573
6699
  self.db = cast(AsyncBaseDb, self.db)
6574
- selected_sessions = await self.db.get_sessions(
6575
- session_type=SessionType.TEAM,
6576
- limit=num_history_sessions,
6577
- user_id=user_id,
6578
- sort_by="created_at",
6579
- sort_order="desc",
6580
- )
6700
+ if self._has_async_db():
6701
+ selected_sessions = await self.db.get_sessions( # type: ignore
6702
+ session_type=SessionType.TEAM,
6703
+ limit=num_history_sessions,
6704
+ user_id=user_id,
6705
+ sort_by="created_at",
6706
+ sort_order="desc",
6707
+ )
6708
+ else:
6709
+ selected_sessions = self.db.get_sessions( # type: ignore
6710
+ session_type=SessionType.TEAM,
6711
+ limit=num_history_sessions,
6712
+ user_id=user_id,
6713
+ sort_by="created_at",
6714
+ sort_order="desc",
6715
+ )
6581
6716
 
6582
6717
  all_messages = []
6583
6718
  seen_message_pairs = set()
@@ -6657,14 +6792,14 @@ class Team:
6657
6792
  ) -> Optional[str]:
6658
6793
  team_member_interactions_str = None
6659
6794
  if self.share_member_interactions:
6660
- team_member_interactions_str = self._get_team_member_interactions_str(team_run_context=team_run_context) # type: ignore
6661
- if context_images := self._get_team_run_context_images(team_run_context=team_run_context): # type: ignore
6795
+ team_member_interactions_str = get_team_member_interactions_str(team_run_context=team_run_context) # type: ignore
6796
+ if context_images := get_team_run_context_images(team_run_context=team_run_context): # type: ignore
6662
6797
  images.extend(context_images)
6663
- if context_videos := self._get_team_run_context_videos(team_run_context=team_run_context): # type: ignore
6798
+ if context_videos := get_team_run_context_videos(team_run_context=team_run_context): # type: ignore
6664
6799
  videos.extend(context_videos)
6665
- if context_audio := self._get_team_run_context_audio(team_run_context=team_run_context): # type: ignore
6800
+ if context_audio := get_team_run_context_audio(team_run_context=team_run_context): # type: ignore
6666
6801
  audio.extend(context_audio)
6667
- if context_files := self._get_team_run_context_files(team_run_context=team_run_context): # type: ignore
6802
+ if context_files := get_team_run_context_files(team_run_context=team_run_context): # type: ignore
6668
6803
  files.extend(context_files)
6669
6804
  return team_member_interactions_str
6670
6805
 
@@ -6731,6 +6866,11 @@ class Team:
6731
6866
  # 1. Initialize the member agent
6732
6867
  self._initialize_member(member_agent)
6733
6868
 
6869
+ # If team has send_media_to_model=False, ensure member agent also has it set to False
6870
+ # This allows tools to access files while preventing models from receiving them
6871
+ if not self.send_media_to_model:
6872
+ member_agent.send_media_to_model = False
6873
+
6734
6874
  # 2. Handle respond_directly nuances
6735
6875
  if self.respond_directly:
6736
6876
  # Since we return the response directly from the member agent, we need to set the output schema from the team down.
@@ -6802,7 +6942,7 @@ class Team:
6802
6942
  normalized_task = str(member_agent_task.content)
6803
6943
  else:
6804
6944
  normalized_task = ""
6805
- self._add_interaction_to_team_run_context(
6945
+ add_interaction_to_team_run_context(
6806
6946
  team_run_context=team_run_context,
6807
6947
  member_name=member_name,
6808
6948
  task=normalized_task,
@@ -6860,6 +7000,7 @@ class Team:
6860
7000
  use_agent_logger()
6861
7001
 
6862
7002
  member_session_state_copy = copy(session_state)
7003
+
6863
7004
  if stream:
6864
7005
  member_agent_run_response_stream = member_agent.run(
6865
7006
  input=member_agent_task if not history else history,
@@ -6986,6 +7127,7 @@ class Team:
6986
7127
  use_agent_logger()
6987
7128
 
6988
7129
  member_session_state_copy = copy(session_state)
7130
+
6989
7131
  if stream:
6990
7132
  member_agent_run_response_stream = member_agent.arun( # type: ignore
6991
7133
  input=member_agent_task if not history else history,
@@ -7290,6 +7432,7 @@ class Team:
7290
7432
 
7291
7433
  async def run_member_agent(agent=current_agent) -> str:
7292
7434
  member_session_state_copy = copy(session_state)
7435
+
7293
7436
  member_agent_run_response = await agent.arun(
7294
7437
  input=member_agent_task if not history else history,
7295
7438
  user_id=user_id,
@@ -7424,57 +7567,6 @@ class Team:
7424
7567
  log_warning(f"Error upserting session into db: {e}")
7425
7568
  return None
7426
7569
 
7427
- def get_run_output(
7428
- self, run_id: str, session_id: Optional[str] = None
7429
- ) -> Optional[Union[TeamRunOutput, RunOutput]]:
7430
- """
7431
- Get a RunOutput from the database.
7432
-
7433
- Args:
7434
- run_id (str): The run_id to load from storage.
7435
- session_id (Optional[str]): The session_id to load from storage.
7436
- """
7437
- if self._team_session is not None:
7438
- run_response = self._team_session.get_run(run_id=run_id)
7439
- if run_response is not None:
7440
- return run_response
7441
- else:
7442
- log_warning(f"RunOutput {run_id} not found in AgentSession {self._team_session.session_id}")
7443
- return None
7444
- else:
7445
- team_session = self.get_session(session_id=session_id)
7446
- if team_session is not None:
7447
- run_response = team_session.get_run(run_id=run_id)
7448
- if run_response is not None:
7449
- return cast(TeamRunOutput, run_response)
7450
- else:
7451
- log_warning(f"RunOutput {run_id} not found in AgentSession {session_id}")
7452
- return None
7453
-
7454
- def get_last_run_output(self, session_id: Optional[str] = None) -> Optional[TeamRunOutput]:
7455
- """
7456
- Get the last run response from the database.
7457
-
7458
- Args:
7459
- session_id (Optional[str]): The session_id to load from storage.
7460
-
7461
- Returns:
7462
- RunOutput: The last run response from the database.
7463
- """
7464
- if self._team_session is not None and self._team_session.runs is not None and len(self._team_session.runs) > 0:
7465
- run_response = self._team_session.runs[-1]
7466
- if run_response is not None:
7467
- return run_response # type: ignore
7468
- else:
7469
- agent_session = self.get_session(session_id=session_id)
7470
- if agent_session is not None and agent_session.runs is not None and len(agent_session.runs) > 0:
7471
- run_response = agent_session.runs[-1]
7472
- if run_response is not None:
7473
- return run_response # type: ignore
7474
- else:
7475
- log_warning(f"No run responses found in AgentSession {session_id}")
7476
- return None
7477
-
7478
7570
  def _read_or_create_session(self, session_id: str, user_id: Optional[str] = None) -> TeamSession:
7479
7571
  """Load the TeamSession from storage
7480
7572
 
@@ -7486,8 +7578,8 @@ class Team:
7486
7578
  from agno.session.team import TeamSession
7487
7579
 
7488
7580
  # Return existing session if we have one
7489
- if self._team_session is not None and self._team_session.session_id == session_id:
7490
- return self._team_session
7581
+ if self._cached_session is not None and self._cached_session.session_id == session_id:
7582
+ return self._cached_session
7491
7583
 
7492
7584
  # Try to load from database
7493
7585
  team_session = None
@@ -7514,7 +7606,7 @@ class Team:
7514
7606
 
7515
7607
  # Cache the session if relevant
7516
7608
  if team_session is not None and self.cache_session:
7517
- self._team_session = team_session
7609
+ self._cached_session = team_session
7518
7610
 
7519
7611
  return team_session
7520
7612
 
@@ -7529,8 +7621,8 @@ class Team:
7529
7621
  from agno.session.team import TeamSession
7530
7622
 
7531
7623
  # Return existing session if we have one
7532
- if self._team_session is not None and self._team_session.session_id == session_id:
7533
- return self._team_session
7624
+ if self._cached_session is not None and self._cached_session.session_id == session_id:
7625
+ return self._cached_session
7534
7626
 
7535
7627
  # Try to load from database
7536
7628
  team_session = None
@@ -7555,10 +7647,116 @@ class Team:
7555
7647
 
7556
7648
  # Cache the session if relevant
7557
7649
  if team_session is not None and self.cache_session:
7558
- self._team_session = team_session
7650
+ self._cached_session = team_session
7559
7651
 
7560
7652
  return team_session
7561
7653
 
7654
+ def _load_session_state(self, session: TeamSession, session_state: Dict[str, Any]) -> Dict[str, Any]:
7655
+ """Load and return the stored session_state from the database, optionally merging it with the given one"""
7656
+
7657
+ from agno.utils.merge_dict import merge_dictionaries
7658
+
7659
+ # Get the session_state from the database and merge with proper precedence
7660
+ # At this point session_state contains: agent_defaults + run_params
7661
+ if session.session_data is not None and "session_state" in session.session_data:
7662
+ session_state_from_db = session.session_data.get("session_state")
7663
+
7664
+ if (
7665
+ session_state_from_db is not None
7666
+ and isinstance(session_state_from_db, dict)
7667
+ and len(session_state_from_db) > 0
7668
+ and not self.overwrite_db_session_state
7669
+ ):
7670
+ # This preserves precedence: run_params > db_state > agent_defaults
7671
+ merged_state = session_state_from_db.copy()
7672
+ merge_dictionaries(merged_state, session_state)
7673
+ session_state.clear()
7674
+ session_state.update(merged_state)
7675
+
7676
+ # Update the session_state in the session
7677
+ if session.session_data is not None:
7678
+ session.session_data["session_state"] = session_state
7679
+
7680
+ return session_state
7681
+
7682
+ def _update_metadata(self, session: TeamSession):
7683
+ """Update the extra_data in the session"""
7684
+ from agno.utils.merge_dict import merge_dictionaries
7685
+
7686
+ # Read metadata from the database
7687
+ if session.metadata is not None:
7688
+ # If metadata is set in the agent, update the database metadata with the agent's metadata
7689
+ if self.metadata is not None:
7690
+ # Updates agent's session metadata in place
7691
+ merge_dictionaries(session.metadata, self.metadata)
7692
+ # Update the current metadata with the metadata from the database which is updated in place
7693
+ self.metadata = session.metadata
7694
+
7695
+ # -*- Public convenience functions
7696
+ def get_run_output(
7697
+ self, run_id: str, session_id: Optional[str] = None
7698
+ ) -> Optional[Union[TeamRunOutput, RunOutput]]:
7699
+ """
7700
+ Get a RunOutput or TeamRunOutput from the database. Handles cached sessions.
7701
+
7702
+ Args:
7703
+ run_id (str): The run_id to load from storage.
7704
+ session_id (Optional[str]): The session_id to load from storage.
7705
+ """
7706
+ if not session_id and not self.session_id:
7707
+ raise Exception("No session_id provided")
7708
+
7709
+ session_id_to_load = session_id or self.session_id
7710
+ return get_run_output_util(self, run_id=run_id, session_id=session_id_to_load)
7711
+
7712
+ async def aget_run_output(
7713
+ self, run_id: str, session_id: Optional[str] = None
7714
+ ) -> Optional[Union[TeamRunOutput, RunOutput]]:
7715
+ """
7716
+ Get a RunOutput or TeamRunOutput from the database. Handles cached sessions.
7717
+
7718
+ Args:
7719
+ run_id (str): The run_id to load from storage.
7720
+ session_id (Optional[str]): The session_id to load from storage.
7721
+ """
7722
+ if not session_id and not self.session_id:
7723
+ raise Exception("No session_id provided")
7724
+
7725
+ session_id_to_load = session_id or self.session_id
7726
+ return await aget_run_output_util(self, run_id=run_id, session_id=session_id_to_load)
7727
+
7728
+ def get_last_run_output(self, session_id: Optional[str] = None) -> Optional[TeamRunOutput]:
7729
+ """
7730
+ Get the last run response from the database.
7731
+
7732
+ Args:
7733
+ session_id (Optional[str]): The session_id to load from storage.
7734
+
7735
+ Returns:
7736
+ RunOutput: The last run response from the database.
7737
+ """
7738
+ if not session_id and not self.session_id:
7739
+ raise Exception("No session_id provided")
7740
+
7741
+ session_id_to_load = session_id or self.session_id
7742
+ return cast(TeamRunOutput, get_last_run_output_util(self, session_id=session_id_to_load))
7743
+
7744
+ async def aget_last_run_output(self, session_id: Optional[str] = None) -> Optional[TeamRunOutput]:
7745
+ """
7746
+ Get the last run response from the database.
7747
+
7748
+ Args:
7749
+ session_id (Optional[str]): The session_id to load from storage.
7750
+
7751
+ Returns:
7752
+ RunOutput: The last run response from the database.
7753
+ """
7754
+ if not session_id and not self.session_id:
7755
+ raise Exception("No session_id provided")
7756
+
7757
+ session_id_to_load = session_id or self.session_id
7758
+ return cast(TeamRunOutput, await aget_last_run_output_util(self, session_id=session_id_to_load))
7759
+
7562
7760
  def get_session(
7563
7761
  self,
7564
7762
  session_id: Optional[str] = None,
@@ -7572,14 +7770,17 @@ class Team:
7572
7770
  TeamSession: The TeamSession loaded from the database or created if it does not exist.
7573
7771
  """
7574
7772
  if not session_id and not self.session_id:
7575
- return None
7773
+ raise Exception("No session_id provided")
7576
7774
 
7577
7775
  session_id_to_load = session_id or self.session_id
7578
7776
 
7579
7777
  # If there is a cached session, return it
7580
- if self.cache_session and hasattr(self, "_team_session") and self._team_session is not None:
7581
- if self._team_session.session_id == session_id_to_load:
7582
- return self._team_session
7778
+ if self.cache_session and hasattr(self, "_cached_session") and self._cached_session is not None:
7779
+ if self._cached_session.session_id == session_id_to_load:
7780
+ return self._cached_session
7781
+
7782
+ if self._has_async_db():
7783
+ raise ValueError("Async database not supported for get_session")
7583
7784
 
7584
7785
  # Load and return the session from the database
7585
7786
  if self.db is not None:
@@ -7613,30 +7814,44 @@ class Team:
7613
7814
  TeamSession: The TeamSession loaded from the database or created if it does not exist.
7614
7815
  """
7615
7816
  if not session_id and not self.session_id:
7616
- return None
7817
+ raise Exception("No session_id provided")
7617
7818
 
7618
7819
  session_id_to_load = session_id or self.session_id
7619
7820
 
7620
7821
  # If there is a cached session, return it
7621
- if self.cache_session and hasattr(self, "_team_session") and self._team_session is not None:
7622
- if self._team_session.session_id == session_id_to_load:
7623
- return self._team_session
7822
+ if self.cache_session and hasattr(self, "_cached_session") and self._cached_session is not None:
7823
+ if self._cached_session.session_id == session_id_to_load:
7824
+ return self._cached_session
7624
7825
 
7625
7826
  # Load and return the session from the database
7626
7827
  if self.db is not None:
7627
- team_session = cast(TeamSession, await self._aread_session(session_id=session_id_to_load)) # type: ignore
7828
+ loaded_session = None
7829
+ # We have a standalone team, so we are loading a TeamSession
7830
+ if self.workflow_id is None:
7831
+ loaded_session = cast(TeamSession, await self._aread_session(session_id=session_id_to_load)) # type: ignore
7832
+ # We have a workflow team, so we are loading a WorkflowSession
7833
+ else:
7834
+ loaded_session = cast(WorkflowSession, await self._aread_session(session_id=session_id_to_load)) # type: ignore
7628
7835
 
7629
7836
  # Cache the session if relevant
7630
- if team_session is not None and self.cache_session:
7631
- self._team_session = team_session
7837
+ if loaded_session is not None and self.cache_session:
7838
+ self._cached_session = loaded_session
7632
7839
 
7633
- return team_session
7840
+ return loaded_session
7634
7841
 
7635
7842
  log_debug(f"TeamSession {session_id_to_load} not found in db")
7636
7843
  return None
7637
7844
 
7638
7845
  def save_session(self, session: TeamSession) -> None:
7639
- """Save the TeamSession to storage"""
7846
+ """
7847
+ Save the TeamSession to storage
7848
+
7849
+ Args:
7850
+ session: The TeamSession to save.
7851
+ """
7852
+ if self._has_async_db():
7853
+ raise ValueError("Async database not supported for save_session")
7854
+
7640
7855
  if self.db is not None and self.parent_team_id is None and self.workflow_id is None:
7641
7856
  if session.session_data is not None and "session_state" in session.session_data:
7642
7857
  session.session_data["session_state"].pop("current_session_id", None) # type: ignore
@@ -7657,7 +7872,12 @@ class Team:
7657
7872
  log_debug(f"Created or updated TeamSession record: {session.session_id}")
7658
7873
 
7659
7874
  async def asave_session(self, session: TeamSession) -> None:
7660
- """Save the TeamSession to storage"""
7875
+ """
7876
+ Save the TeamSession to storage
7877
+
7878
+ Args:
7879
+ session: The TeamSession to save.
7880
+ """
7661
7881
  if self.db is not None and self.parent_team_id is None and self.workflow_id is None:
7662
7882
  if session.session_data is not None and "session_state" in session.session_data:
7663
7883
  session.session_data["session_state"].pop("current_session_id", None) # type: ignore
@@ -7676,49 +7896,15 @@ class Team:
7676
7896
  self._upsert_session(session=session)
7677
7897
  log_debug(f"Created or updated TeamSession record: {session.session_id}")
7678
7898
 
7679
- def _load_session_state(self, session: TeamSession, session_state: Dict[str, Any]) -> Dict[str, Any]:
7680
- """Load and return the stored session_state from the database, optionally merging it with the given one"""
7681
-
7682
- from agno.utils.merge_dict import merge_dictionaries
7683
-
7684
- # Get the session_state from the database and merge with proper precedence
7685
- # At this point session_state contains: agent_defaults + run_params
7686
- if session.session_data is not None and "session_state" in session.session_data:
7687
- session_state_from_db = session.session_data.get("session_state")
7688
-
7689
- if (
7690
- session_state_from_db is not None
7691
- and isinstance(session_state_from_db, dict)
7692
- and len(session_state_from_db) > 0
7693
- and not self.overwrite_db_session_state
7694
- ):
7695
- # This preserves precedence: run_params > db_state > agent_defaults
7696
- merged_state = session_state_from_db.copy()
7697
- merge_dictionaries(merged_state, session_state)
7698
- session_state.clear()
7699
- session_state.update(merged_state)
7700
-
7701
- # Update the session_state in the session
7702
- if session.session_data is not None:
7703
- session.session_data["session_state"] = session_state
7704
-
7705
- return session_state
7706
-
7707
- def _update_metadata(self, session: TeamSession):
7708
- """Update the extra_data in the session"""
7709
- from agno.utils.merge_dict import merge_dictionaries
7710
-
7711
- # Read metadata from the database
7712
- if session.metadata is not None:
7713
- # If metadata is set in the agent, update the database metadata with the agent's metadata
7714
- if self.metadata is not None:
7715
- # Updates agent's session metadata in place
7716
- merge_dictionaries(session.metadata, self.metadata)
7717
- # Update the current metadata with the metadata from the database which is updated in place
7718
- self.metadata = session.metadata
7899
+ def generate_session_name(self, session: TeamSession) -> str:
7900
+ """
7901
+ Generate a name for the team session
7719
7902
 
7720
- def _generate_session_name(self, session: TeamSession) -> str:
7721
- """Generate a name for the team session"""
7903
+ Args:
7904
+ session: The TeamSession to generate a name for.
7905
+ Returns:
7906
+ str: The generated session name.
7907
+ """
7722
7908
 
7723
7909
  if self.model is None:
7724
7910
  raise Exception("Model not set")
@@ -7746,62 +7932,113 @@ class Team:
7746
7932
  content = generated_name.content
7747
7933
  if content is None:
7748
7934
  log_error("Generated name is None. Trying again.")
7749
- return self._generate_session_name(session=session)
7935
+ return self.generate_session_name(session=session)
7750
7936
  if len(content.split()) > 15:
7751
7937
  log_error("Generated name is too long. Trying again.")
7752
- return self._generate_session_name(session=session)
7938
+ return self.generate_session_name(session=session)
7753
7939
  return content.replace('"', "").strip()
7754
7940
 
7755
7941
  def set_session_name(
7756
7942
  self, session_id: Optional[str] = None, autogenerate: bool = False, session_name: Optional[str] = None
7757
7943
  ) -> TeamSession:
7758
- """Set the session name and save to storage"""
7944
+ """
7945
+ Set the session name and save to storage
7946
+
7947
+ Args:
7948
+ session_id: The session ID to set the name for. If not provided, the current cached session ID is used.
7949
+ autogenerate: Whether to autogenerate the session name.
7950
+ session_name: The session name to set. If not provided, the session name will be autogenerated.
7951
+ Returns:
7952
+ TeamSession: The updated session.
7953
+ """
7759
7954
  session_id = session_id or self.session_id
7760
7955
 
7761
7956
  if session_id is None:
7762
7957
  raise Exception("Session ID is not set")
7763
7958
 
7764
- # -*- Read from storage
7765
- session = self.get_session(session_id=session_id) # type: ignore
7766
-
7767
- if session is None:
7768
- raise Exception("Session not found")
7959
+ return cast(
7960
+ TeamSession,
7961
+ set_session_name_util(self, session_id=session_id, autogenerate=autogenerate, session_name=session_name),
7962
+ )
7769
7963
 
7770
- # -*- Generate name for session
7771
- if autogenerate:
7772
- session_name = self._generate_session_name(session=session)
7773
- log_debug(f"Generated Session Name: {session_name}")
7774
- elif session_name is None:
7775
- raise Exception("Session Name is not set")
7964
+ async def aset_session_name(
7965
+ self, session_id: Optional[str] = None, autogenerate: bool = False, session_name: Optional[str] = None
7966
+ ) -> TeamSession:
7967
+ """
7968
+ Set the session name and save to storage
7776
7969
 
7777
- # -*- Rename session
7778
- if session.session_data is not None:
7779
- session.session_data["session_name"] = session_name
7970
+ Args:
7971
+ session_id: The session ID to set the name for. If not provided, the current cached session ID is used.
7972
+ autogenerate: Whether to autogenerate the session name.
7973
+ session_name: The session name to set. If not provided, the session name will be autogenerated.
7974
+ Returns:
7975
+ TeamSession: The updated session.
7976
+ """
7977
+ session_id = session_id or self.session_id
7780
7978
 
7781
- # -*- Save to storage
7782
- self.save_session(session=session) # type: ignore
7979
+ if session_id is None:
7980
+ raise Exception("Session ID is not set")
7783
7981
 
7784
- return session
7982
+ return cast(
7983
+ TeamSession,
7984
+ await aset_session_name_util(
7985
+ self, session_id=session_id, autogenerate=autogenerate, session_name=session_name
7986
+ ),
7987
+ )
7785
7988
 
7786
7989
  def get_session_name(self, session_id: Optional[str] = None) -> str:
7787
- """Get the session name for the given session ID and user ID."""
7990
+ """
7991
+ Get the session name for the given session ID.
7992
+
7993
+ Args:
7994
+ session_id: The session ID to get the name for. If not provided, the current cached session ID is used.
7995
+ Returns:
7996
+ str: The session name.
7997
+ """
7788
7998
  session_id = session_id or self.session_id
7789
7999
  if session_id is None:
7790
8000
  raise Exception("Session ID is not set")
7791
- session = self.get_session(session_id=session_id) # type: ignore
7792
- if session is None:
7793
- raise Exception("Session not found")
7794
- return session.session_data.get("session_name", "") if session.session_data is not None else ""
8001
+ return get_session_name_util(self, session_id=session_id)
8002
+
8003
+ async def aget_session_name(self, session_id: Optional[str] = None) -> str:
8004
+ """
8005
+ Get the session name for the given session ID.
8006
+
8007
+ Args:
8008
+ session_id: The session ID to get the name for. If not provided, the current cached session ID is used.
8009
+ Returns:
8010
+ str: The session name.
8011
+ """
8012
+ session_id = session_id or self.session_id
8013
+ if session_id is None:
8014
+ raise Exception("Session ID is not set")
8015
+ return await aget_session_name_util(self, session_id=session_id)
7795
8016
 
7796
8017
  def get_session_state(self, session_id: Optional[str] = None) -> Dict[str, Any]:
7797
- """Get the session state for the given session ID and user ID."""
8018
+ """Get the session state for the given session ID.
8019
+
8020
+ Args:
8021
+ session_id: The session ID to get the state for. If not provided, the current cached session ID is used.
8022
+ Returns:
8023
+ Dict[str, Any]: The session state.
8024
+ """
7798
8025
  session_id = session_id or self.session_id
7799
8026
  if session_id is None:
7800
8027
  raise Exception("Session ID is not set")
7801
- session = self.get_session(session_id=session_id) # type: ignore
7802
- if session is None:
7803
- raise Exception("Session not found")
7804
- return session.session_data.get("session_state", {}) if session.session_data is not None else {}
8028
+ return get_session_state_util(self, session_id=session_id)
8029
+
8030
+ async def aget_session_state(self, session_id: Optional[str] = None) -> Dict[str, Any]:
8031
+ """Get the session state for the given session ID.
8032
+
8033
+ Args:
8034
+ session_id: The session ID to get the state for. If not provided, the current cached session ID is used.
8035
+ Returns:
8036
+ Dict[str, Any]: The session state.
8037
+ """
8038
+ session_id = session_id or self.session_id
8039
+ if session_id is None:
8040
+ raise Exception("Session ID is not set")
8041
+ return await aget_session_state_util(self, session_id=session_id)
7805
8042
 
7806
8043
  def update_session_state(self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None) -> str:
7807
8044
  """
@@ -7815,19 +8052,7 @@ class Team:
7815
8052
  session_id = session_id or self.session_id
7816
8053
  if session_id is None:
7817
8054
  raise Exception("Session ID is not set")
7818
- session = self.get_session(session_id=session_id) # type: ignore
7819
- if session is None:
7820
- raise Exception("Session not found")
7821
-
7822
- if session.session_data is not None and "session_state" not in session.session_data:
7823
- session.session_data["session_state"] = {}
7824
-
7825
- for key, value in session_state_updates.items():
7826
- session.session_data["session_state"][key] = value # type: ignore
7827
-
7828
- self.save_session(session=session)
7829
-
7830
- return session.session_data["session_state"] # type: ignore
8055
+ return update_session_state_util(self, session_state_updates=session_state_updates, session_id=session_id)
7831
8056
 
7832
8057
  async def aupdate_session_state(
7833
8058
  self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
@@ -7843,64 +8068,117 @@ class Team:
7843
8068
  session_id = session_id or self.session_id
7844
8069
  if session_id is None:
7845
8070
  raise Exception("Session ID is not set")
7846
- session = await self.aget_session(session_id=session_id) # type: ignore
7847
- if session is None:
7848
- raise Exception("Session not found")
8071
+ return await aupdate_session_state_util(
8072
+ entity=self, session_state_updates=session_state_updates, session_id=session_id
8073
+ )
7849
8074
 
7850
- if session.session_data is not None and "session_state" not in session.session_data:
7851
- session.session_data["session_state"] = {}
8075
+ def get_session_metrics(self, session_id: Optional[str] = None) -> Optional[Metrics]:
8076
+ """Get the session metrics for the given session ID.
7852
8077
 
7853
- for key, value in session_state_updates.items():
7854
- session.session_data["session_state"][key] = value # type: ignore
8078
+ Args:
8079
+ session_id: The session ID to get the metrics for. If not provided, the current cached session ID is used.
8080
+ Returns:
8081
+ Optional[Metrics]: The session metrics.
8082
+ """
8083
+ session_id = session_id or self.session_id
8084
+ if session_id is None:
8085
+ raise Exception("Session ID is not set")
7855
8086
 
7856
- await self.asave_session(session=session)
8087
+ return get_session_metrics_util(self, session_id=session_id)
7857
8088
 
7858
- return session.session_data["session_state"] # type: ignore
8089
+ async def aget_session_metrics(self, session_id: Optional[str] = None) -> Optional[Metrics]:
8090
+ """Get the session metrics for the given session ID.
7859
8091
 
7860
- def get_session_metrics(self, session_id: Optional[str] = None) -> Optional[Metrics]:
7861
- """Get the session metrics for the given session ID and user ID."""
8092
+ Args:
8093
+ session_id: The session ID to get the metrics for. If not provided, the current cached session ID is used.
8094
+ Returns:
8095
+ Optional[Metrics]: The session metrics.
8096
+ """
7862
8097
  session_id = session_id or self.session_id
7863
8098
  if session_id is None:
7864
8099
  raise Exception("Session ID is not set")
7865
8100
 
7866
- session = self.get_session(session_id=session_id) # type: ignore
7867
- if session is None:
7868
- raise Exception("Session not found")
8101
+ return await aget_session_metrics_util(self, session_id=session_id)
7869
8102
 
7870
- if session.session_data is not None:
7871
- if isinstance(session.session_data.get("session_metrics"), dict):
7872
- return Metrics(**session.session_data.get("session_metrics", {}))
7873
- elif isinstance(session.session_data.get("session_metrics"), Metrics):
7874
- return session.session_data.get("session_metrics")
7875
- return None
8103
+ def delete_session(self, session_id: str):
8104
+ """Delete the current session and save to storage"""
8105
+ if self.db is None:
8106
+ return
8107
+
8108
+ self.db.delete_session(session_id=session_id)
7876
8109
 
7877
- def delete_session(self, session_id: str) -> None:
8110
+ async def adelete_session(self, session_id: str):
7878
8111
  """Delete the current session and save to storage"""
7879
- if self.db is not None:
7880
- self.db.delete_session(session_id=session_id)
8112
+ if self.db is None:
8113
+ return
8114
+ await self.db.delete_session(session_id=session_id) # type: ignore
7881
8115
 
7882
8116
  def get_chat_history(self, session_id: Optional[str] = None) -> List[Message]:
7883
- """Read the chat history from the session"""
8117
+ """Read the chat history from the session
8118
+
8119
+ Args:
8120
+ session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
8121
+ Returns:
8122
+ List[Message]: The chat history from the session.
8123
+ """
8124
+ session_id = session_id or self.session_id
8125
+ if session_id is None:
8126
+ raise Exception("Session ID is not set")
8127
+
8128
+ return get_chat_history_util(self, session_id=session_id)
8129
+
8130
+ async def aget_chat_history(self, session_id: Optional[str] = None) -> List[Message]:
8131
+ """Read the chat history from the session
8132
+
8133
+ Args:
8134
+ session_id: The session ID to get the chat history for. If not provided, the current cached session ID is used.
8135
+ Returns:
8136
+ List[Message]: The chat history from the session.
8137
+ """
7884
8138
  session_id = session_id or self.session_id
7885
8139
  if session_id is None:
7886
- log_warning("Session ID is not set, cannot get chat history")
8140
+ raise Exception("Session ID is not set")
8141
+
8142
+ return await aget_chat_history_util(self, session_id=session_id)
8143
+
8144
+ def get_messages_for_session(self, session_id: Optional[str] = None) -> List[Message]:
8145
+ """Get messages for a session
8146
+
8147
+ Args:
8148
+ session_id: The session ID to get the messages for. If not provided, the current cached session ID is used.
8149
+ Returns:
8150
+ List[Message]: The messages for the session.
8151
+ """
8152
+ session_id = session_id or self.session_id
8153
+ if session_id is None:
8154
+ log_warning("Session ID is not set, cannot get messages for session")
7887
8155
  return []
7888
8156
 
7889
8157
  session = self.get_session(session_id=session_id) # type: ignore
7890
8158
 
7891
8159
  if session is None:
7892
- raise Exception("Session not found")
8160
+ log_warning(f"Session {session_id} not found")
8161
+ return []
8162
+
8163
+ # Only filter by agent_id if this is part of a team
8164
+ return session.get_messages_from_last_n_runs(
8165
+ team_id=self.id,
8166
+ )
7893
8167
 
7894
- return session.get_chat_history()
8168
+ async def aget_messages_for_session(self, session_id: Optional[str] = None) -> List[Message]:
8169
+ """Get messages for a session
7895
8170
 
7896
- def get_messages_for_session(self, session_id: Optional[str] = None) -> List[Message]:
7897
- """Get messages for a session"""
8171
+ Args:
8172
+ session_id: The session ID to get the messages for. If not provided, the current cached session ID is used.
8173
+ Returns:
8174
+ List[Message]: The messages for the session.
8175
+ """
7898
8176
  session_id = session_id or self.session_id
7899
8177
  if session_id is None:
7900
8178
  log_warning("Session ID is not set, cannot get messages for session")
7901
8179
  return []
7902
8180
 
7903
- session = self.get_session(session_id=session_id) # type: ignore
8181
+ session = await self.aget_session(session_id=session_id) # type: ignore
7904
8182
 
7905
8183
  if session is None:
7906
8184
  log_warning(f"Session {session_id} not found")
@@ -7911,8 +8189,14 @@ class Team:
7911
8189
  team_id=self.id,
7912
8190
  )
7913
8191
 
7914
- def get_session_summary(self, session_id: Optional[str] = None):
7915
- """Get the session summary for the given session ID and user ID."""
8192
+ def get_session_summary(self, session_id: Optional[str] = None) -> Optional[SessionSummary]:
8193
+ """Get the session summary for the given session ID and user ID.
8194
+
8195
+ Args:
8196
+ session_id: The session ID to get the summary for. If not provided, the current cached session ID is used.
8197
+ Returns:
8198
+ SessionSummary: The session summary.
8199
+ """
7916
8200
  session_id = session_id if session_id is not None else self.session_id
7917
8201
  if session_id is None:
7918
8202
  raise ValueError("Session ID is required")
@@ -7924,8 +8208,33 @@ class Team:
7924
8208
 
7925
8209
  return session.get_session_summary() # type: ignore
7926
8210
 
8211
+ async def aget_session_summary(self, session_id: Optional[str] = None) -> Optional[SessionSummary]:
8212
+ """Get the session summary for the given session ID and user ID.
8213
+
8214
+ Args:
8215
+ session_id: The session ID to get the summary for. If not provided, the current cached session ID is used.
8216
+ Returns:
8217
+ SessionSummary: The session summary.
8218
+ """
8219
+ session_id = session_id if session_id is not None else self.session_id
8220
+ if session_id is None:
8221
+ raise ValueError("Session ID is required")
8222
+
8223
+ session = await self.aget_session(session_id=session_id)
8224
+
8225
+ if session is None:
8226
+ raise Exception(f"Session {session_id} not found")
8227
+
8228
+ return session.get_session_summary() # type: ignore
8229
+
7927
8230
  def get_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
7928
- """Get the user memories for the given user ID."""
8231
+ """Get the user memories for the given user ID.
8232
+
8233
+ Args:
8234
+ user_id: The user ID to get the memories for. If not provided, the current cached user ID is used.
8235
+ Returns:
8236
+ Optional[List[UserMemory]]: The user memories.
8237
+ """
7929
8238
  if self.memory_manager is None:
7930
8239
  return None
7931
8240
  user_id = user_id if user_id is not None else self.user_id
@@ -7934,86 +8243,21 @@ class Team:
7934
8243
 
7935
8244
  return self.memory_manager.get_user_memories(user_id=user_id)
7936
8245
 
7937
- def _add_interaction_to_team_run_context(
7938
- self,
7939
- team_run_context: Dict[str, Any],
7940
- member_name: str,
7941
- task: str,
7942
- run_response: Union[RunOutput, TeamRunOutput],
7943
- ) -> None:
7944
- if "member_responses" not in team_run_context:
7945
- team_run_context["member_responses"] = []
7946
- team_run_context["member_responses"].append(
7947
- {
7948
- "member_name": member_name,
7949
- "task": task,
7950
- "run_response": run_response,
7951
- }
7952
- )
7953
- log_debug(f"Updated team run context with member name: {member_name}")
7954
-
7955
- def _get_team_member_interactions_str(self, team_run_context: Dict[str, Any]) -> str:
7956
- if not team_run_context:
7957
- return ""
7958
- team_member_interactions_str = ""
7959
- if "member_responses" in team_run_context:
7960
- team_member_interactions_str += (
7961
- "<member_interaction_context>\nSee below interactions wit other team members.\n"
7962
- )
8246
+ async def aget_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
8247
+ """Get the user memories for the given user ID.
7963
8248
 
7964
- for interaction in team_run_context["member_responses"]:
7965
- response_dict = interaction["run_response"].to_dict()
7966
- response_content = (
7967
- response_dict.get("content")
7968
- or ",".join([tool.get("content", "") for tool in response_dict.get("tools", [])])
7969
- or ""
7970
- )
7971
- team_member_interactions_str += f"Member: {interaction['member_name']}\n"
7972
- team_member_interactions_str += f"Task: {interaction['task']}\n"
7973
- team_member_interactions_str += f"Response: {response_content}\n"
7974
- team_member_interactions_str += "\n"
7975
- team_member_interactions_str += "</member_interaction_context>\n"
7976
- return team_member_interactions_str
8249
+ Args:
8250
+ user_id: The user ID to get the memories for. If not provided, the current cached user ID is used.
8251
+ Returns:
8252
+ Optional[List[UserMemory]]: The user memories.
8253
+ """
8254
+ if self.memory_manager is None:
8255
+ return None
8256
+ user_id = user_id if user_id is not None else self.user_id
8257
+ if user_id is None:
8258
+ user_id = "default"
7977
8259
 
7978
- def _get_team_run_context_images(self, team_run_context: Dict[str, Any]) -> List[Image]:
7979
- if not team_run_context:
7980
- return []
7981
- images = []
7982
- if "member_responses" in team_run_context:
7983
- for interaction in team_run_context["member_responses"]:
7984
- if interaction["run_response"].images:
7985
- images.extend(interaction["run_response"].images)
7986
- return images
7987
-
7988
- def _get_team_run_context_videos(self, team_run_context: Dict[str, Any]) -> List[Video]:
7989
- if not team_run_context:
7990
- return []
7991
- videos = []
7992
- if "member_responses" in team_run_context:
7993
- for interaction in team_run_context["member_responses"]:
7994
- if interaction["run_response"].videos:
7995
- videos.extend(interaction["run_response"].videos)
7996
- return videos
7997
-
7998
- def _get_team_run_context_audio(self, team_run_context: Dict[str, Any]) -> List[Audio]:
7999
- if not team_run_context:
8000
- return []
8001
- audio = []
8002
- if "member_responses" in team_run_context:
8003
- for interaction in team_run_context["member_responses"]:
8004
- if interaction["run_response"].audio:
8005
- audio.extend(interaction["run_response"].audio)
8006
- return audio
8007
-
8008
- def _get_team_run_context_files(self, team_run_context: Dict[str, Any]) -> List[File]:
8009
- if not team_run_context:
8010
- return []
8011
- files = []
8012
- if "member_responses" in team_run_context:
8013
- for interaction in team_run_context["member_responses"]:
8014
- if interaction["run_response"].files:
8015
- files.extend(interaction["run_response"].files)
8016
- return files
8260
+ return await self.memory_manager.aget_user_memories(user_id=user_id)
8017
8261
 
8018
8262
  ###########################################################################
8019
8263
  # Handle images, videos and audio
@@ -8162,6 +8406,8 @@ class Team:
8162
8406
  document_name = query.replace(" ", "_").replace("?", "").replace("!", "").replace(".", "")
8163
8407
  document_content = json.dumps({"query": query, "result": result})
8164
8408
  log_info(f"Adding document to Knowledge: {document_name}: {document_content}")
8409
+ import asyncio
8410
+
8165
8411
  from agno.knowledge.reader.text_reader import TextReader
8166
8412
 
8167
8413
  asyncio.run(