agno 2.2.1__py3-none-any.whl → 2.2.3__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 (69) hide show
  1. agno/agent/agent.py +735 -574
  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/__init__.py +15 -1
  12. agno/db/mongo/async_mongo.py +1999 -0
  13. agno/db/mongo/mongo.py +0 -2
  14. agno/db/mysql/mysql.py +0 -3
  15. agno/db/postgres/__init__.py +1 -1
  16. agno/db/{async_postgres → postgres}/async_postgres.py +19 -22
  17. agno/db/postgres/postgres.py +7 -10
  18. agno/db/postgres/utils.py +106 -2
  19. agno/db/redis/redis.py +0 -2
  20. agno/db/singlestore/singlestore.py +0 -3
  21. agno/db/sqlite/__init__.py +2 -1
  22. agno/db/sqlite/async_sqlite.py +2269 -0
  23. agno/db/sqlite/sqlite.py +0 -2
  24. agno/db/sqlite/utils.py +96 -0
  25. agno/db/surrealdb/surrealdb.py +0 -6
  26. agno/knowledge/knowledge.py +3 -3
  27. agno/knowledge/reader/reader_factory.py +16 -0
  28. agno/knowledge/reader/tavily_reader.py +194 -0
  29. agno/memory/manager.py +28 -25
  30. agno/models/anthropic/claude.py +63 -6
  31. agno/models/base.py +251 -32
  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 +25 -20
  37. agno/os/utils.py +9 -2
  38. agno/run/agent.py +23 -30
  39. agno/run/base.py +17 -1
  40. agno/run/team.py +23 -29
  41. agno/run/workflow.py +17 -12
  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 +599 -367
  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/print_response/workflow.py +17 -1
  59. agno/utils/team.py +89 -1
  60. agno/workflow/step.py +0 -1
  61. agno/workflow/types.py +10 -15
  62. {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/METADATA +28 -25
  63. {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/RECORD +66 -62
  64. agno/db/async_postgres/schemas.py +0 -139
  65. agno/db/async_postgres/utils.py +0 -347
  66. agno/tools/mcp.py +0 -679
  67. {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/WHEEL +0 -0
  68. {agno-2.2.1.dist-info → agno-2.2.3.dist-info}/licenses/LICENSE +0 -0
  69. {agno-2.2.1.dist-info → agno-2.2.3.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
  )
@@ -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
 
@@ -640,21 +666,20 @@ class Team:
640
666
  self.videos: Optional[List[Video]] = None
641
667
 
642
668
  # Team session
643
- self._team_session: Optional[TeamSession] = None
669
+ self._cached_session: Optional[TeamSession] = None
644
670
 
645
671
  self._tool_instructions: Optional[List[str]] = None
646
- self._functions_for_model: Optional[Dict[str, Function]] = None
647
- self._tools_for_model: Optional[List[Dict[str, Any]]] = None
648
672
 
649
673
  # True if we should parse a member response model
650
674
  self._member_response_model: Optional[Type[BaseModel]] = None
651
675
 
652
676
  self._formatter: Optional[SafeFormatter] = None
653
677
 
654
- self._rebuild_tools = True
655
-
656
678
  self._hooks_normalised = False
657
679
 
680
+ # List of MCP tools that were initialized on the last run
681
+ self._mcp_tools_initialized_on_run: List[Any] = []
682
+
658
683
  # Lazy-initialized shared thread pool executor for background tasks (memory, cultural knowledge, etc.)
659
684
  self._background_executor: Optional[Any] = None
660
685
 
@@ -675,6 +700,10 @@ class Team:
675
700
  def should_parse_structured_output(self) -> bool:
676
701
  return self.output_schema is not None and self.parse_response and self.parser_model is None
677
702
 
703
+ @property
704
+ def cached_session(self) -> Optional[TeamSession]:
705
+ return self._cached_session
706
+
678
707
  def set_id(self) -> None:
679
708
  """Set the ID of the team if not set yet.
680
709
 
@@ -757,10 +786,21 @@ class Team:
757
786
  member.team_id = self.id
758
787
  member.set_id()
759
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
+
760
797
  elif isinstance(member, Team):
761
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
762
802
  for sub_member in member.members:
763
- self._initialize_member(sub_member, debug_mode=debug_mode)
803
+ member._initialize_member(sub_member, debug_mode=debug_mode)
764
804
 
765
805
  def _set_default_model(self) -> None:
766
806
  # Set the default model
@@ -892,7 +932,8 @@ class Team:
892
932
  def set_tools(self, tools: List[Union[Toolkit, Callable, Function, Dict]]):
893
933
  self.tools = tools
894
934
 
895
- def cancel_run(self, run_id: str) -> bool:
935
+ @staticmethod
936
+ def cancel_run(run_id: str) -> bool:
896
937
  """Cancel a running team execution.
897
938
 
898
939
  Args:
@@ -903,6 +944,21 @@ class Team:
903
944
  """
904
945
  return cancel_run_global(run_id)
905
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
+
906
962
  def _execute_pre_hooks(
907
963
  self,
908
964
  hooks: Optional[List[Callable[..., Any]]],
@@ -1012,7 +1068,9 @@ class Team:
1012
1068
  # Filter arguments to only include those that the hook accepts
1013
1069
  filtered_args = filter_hook_args(hook, all_args)
1014
1070
 
1015
- if asyncio.iscoroutinefunction(hook):
1071
+ from inspect import iscoroutinefunction
1072
+
1073
+ if iscoroutinefunction(hook):
1016
1074
  await hook(**filtered_args)
1017
1075
  else:
1018
1076
  # Synchronous function
@@ -1143,7 +1201,9 @@ class Team:
1143
1201
  # Filter arguments to only include those that the hook accepts
1144
1202
  filtered_args = filter_hook_args(hook, all_args)
1145
1203
 
1146
- if asyncio.iscoroutinefunction(hook):
1204
+ from inspect import iscoroutinefunction
1205
+
1206
+ if iscoroutinefunction(hook):
1147
1207
  await hook(**filtered_args)
1148
1208
  else:
1149
1209
  hook(**filtered_args)
@@ -1224,7 +1284,7 @@ class Team:
1224
1284
  # Initialize team run context
1225
1285
  team_run_context: Dict[str, Any] = {}
1226
1286
 
1227
- self.determine_tools_for_model(
1287
+ _tools = self._determine_tools_for_model(
1228
1288
  model=self.model,
1229
1289
  run_response=run_response,
1230
1290
  team_run_context=team_run_context,
@@ -1263,6 +1323,7 @@ class Team:
1263
1323
  add_dependencies_to_context=add_dependencies_to_context,
1264
1324
  add_session_state_to_context=add_session_state_to_context,
1265
1325
  metadata=metadata,
1326
+ tools=_tools,
1266
1327
  **kwargs,
1267
1328
  )
1268
1329
  if len(run_messages.messages) == 0:
@@ -1292,8 +1353,7 @@ class Team:
1292
1353
  model_response: ModelResponse = self.model.response(
1293
1354
  messages=run_messages.messages,
1294
1355
  response_format=response_format,
1295
- tools=self._tools_for_model,
1296
- functions=self._functions_for_model,
1356
+ tools=_tools,
1297
1357
  tool_choice=self.tool_choice,
1298
1358
  tool_call_limit=self.tool_call_limit,
1299
1359
  send_media_to_model=self.send_media_to_model,
@@ -1436,7 +1496,7 @@ class Team:
1436
1496
  # Initialize team run context
1437
1497
  team_run_context: Dict[str, Any] = {}
1438
1498
 
1439
- self.determine_tools_for_model(
1499
+ _tools = self._determine_tools_for_model(
1440
1500
  model=self.model,
1441
1501
  run_response=run_response,
1442
1502
  team_run_context=team_run_context,
@@ -1475,6 +1535,7 @@ class Team:
1475
1535
  add_dependencies_to_context=add_dependencies_to_context,
1476
1536
  add_session_state_to_context=add_session_state_to_context,
1477
1537
  metadata=metadata,
1538
+ tools=_tools,
1478
1539
  **kwargs,
1479
1540
  )
1480
1541
  if len(run_messages.messages) == 0:
@@ -1518,6 +1579,7 @@ class Team:
1518
1579
  session=session,
1519
1580
  run_response=run_response,
1520
1581
  run_messages=run_messages,
1582
+ tools=_tools,
1521
1583
  response_format=response_format,
1522
1584
  stream_events=stream_events,
1523
1585
  ):
@@ -1528,6 +1590,7 @@ class Team:
1528
1590
  session=session,
1529
1591
  run_response=run_response,
1530
1592
  run_messages=run_messages,
1593
+ tools=_tools,
1531
1594
  response_format=response_format,
1532
1595
  stream_events=stream_events,
1533
1596
  ):
@@ -2041,7 +2104,8 @@ class Team:
2041
2104
  # 4. Determine tools for model
2042
2105
  team_run_context: Dict[str, Any] = {}
2043
2106
  self.model = cast(Model, self.model)
2044
- self.determine_tools_for_model(
2107
+ await self._check_and_refresh_mcp_tools()
2108
+ _tools = self._determine_tools_for_model(
2045
2109
  model=self.model,
2046
2110
  run_response=run_response,
2047
2111
  team_run_context=team_run_context,
@@ -2079,6 +2143,8 @@ class Team:
2079
2143
  dependencies=dependencies,
2080
2144
  add_dependencies_to_context=add_dependencies_to_context,
2081
2145
  add_session_state_to_context=add_session_state_to_context,
2146
+ metadata=metadata,
2147
+ tools=_tools,
2082
2148
  **kwargs,
2083
2149
  )
2084
2150
 
@@ -2086,8 +2152,6 @@ class Team:
2086
2152
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
2087
2153
 
2088
2154
  # 6. Start memory creation in background task
2089
- import asyncio
2090
-
2091
2155
  memory_task = None
2092
2156
  if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2093
2157
  log_debug("Starting memory creation in background task.")
@@ -2107,8 +2171,7 @@ class Team:
2107
2171
  # 8. Get the model response for the team leader
2108
2172
  model_response = await self.model.aresponse(
2109
2173
  messages=run_messages.messages,
2110
- tools=self._tools_for_model,
2111
- functions=self._functions_for_model,
2174
+ tools=_tools,
2112
2175
  tool_choice=self.tool_choice,
2113
2176
  tool_call_limit=self.tool_call_limit,
2114
2177
  response_format=response_format,
@@ -2189,6 +2252,7 @@ class Team:
2189
2252
 
2190
2253
  return run_response
2191
2254
  finally:
2255
+ await self._disconnect_mcp_tools()
2192
2256
  # Cancel the memory task if it's still running
2193
2257
  if memory_task is not None and not memory_task.done():
2194
2258
  memory_task.cancel()
@@ -2277,7 +2341,8 @@ class Team:
2277
2341
  # 5. Determine tools for model
2278
2342
  team_run_context: Dict[str, Any] = {}
2279
2343
  self.model = cast(Model, self.model)
2280
- self.determine_tools_for_model(
2344
+ await self._check_and_refresh_mcp_tools()
2345
+ _tools = self._determine_tools_for_model(
2281
2346
  model=self.model,
2282
2347
  run_response=run_response,
2283
2348
  team_run_context=team_run_context,
@@ -2314,14 +2379,13 @@ class Team:
2314
2379
  add_dependencies_to_context=add_dependencies_to_context,
2315
2380
  add_session_state_to_context=add_session_state_to_context,
2316
2381
  metadata=metadata,
2382
+ tools=_tools,
2317
2383
  **kwargs,
2318
2384
  )
2319
2385
 
2320
2386
  log_debug(f"Team Run Start: {run_response.run_id}", center=True)
2321
2387
 
2322
2388
  # 7. Start memory creation in background task
2323
- import asyncio
2324
-
2325
2389
  memory_task = None
2326
2390
  if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2327
2391
  log_debug("Starting memory creation in background task.")
@@ -2361,6 +2425,7 @@ class Team:
2361
2425
  session=team_session,
2362
2426
  run_response=run_response,
2363
2427
  run_messages=run_messages,
2428
+ tools=_tools,
2364
2429
  response_format=response_format,
2365
2430
  stream_events=stream_events,
2366
2431
  ):
@@ -2371,6 +2436,7 @@ class Team:
2371
2436
  session=team_session,
2372
2437
  run_response=run_response,
2373
2438
  run_messages=run_messages,
2439
+ tools=_tools,
2374
2440
  response_format=response_format,
2375
2441
  stream_events=stream_events,
2376
2442
  ):
@@ -2512,6 +2578,7 @@ class Team:
2512
2578
  await self._acleanup_and_store(run_response=run_response, session=team_session)
2513
2579
 
2514
2580
  finally:
2581
+ await self._disconnect_mcp_tools()
2515
2582
  # Cancel the memory task if it's still running
2516
2583
  if memory_task is not None and not memory_task.done():
2517
2584
  memory_task.cancel()
@@ -2873,6 +2940,7 @@ class Team:
2873
2940
  session: TeamSession,
2874
2941
  run_response: TeamRunOutput,
2875
2942
  run_messages: RunMessages,
2943
+ tools: Optional[List[Union[Function, dict]]] = None,
2876
2944
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2877
2945
  stream_events: bool = False,
2878
2946
  ) -> Iterator[Union[TeamRunOutputEvent, RunOutputEvent]]:
@@ -2892,8 +2960,7 @@ class Team:
2892
2960
  for model_response_event in self.model.response_stream(
2893
2961
  messages=run_messages.messages,
2894
2962
  response_format=response_format,
2895
- tools=self._tools_for_model,
2896
- functions=self._functions_for_model,
2963
+ tools=tools,
2897
2964
  tool_choice=self.tool_choice,
2898
2965
  tool_call_limit=self.tool_call_limit,
2899
2966
  stream_model_response=stream_model_response,
@@ -2955,6 +3022,7 @@ class Team:
2955
3022
  session: TeamSession,
2956
3023
  run_response: TeamRunOutput,
2957
3024
  run_messages: RunMessages,
3025
+ tools: Optional[List[Union[Function, dict]]] = None,
2958
3026
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2959
3027
  stream_events: bool = False,
2960
3028
  ) -> AsyncIterator[Union[TeamRunOutputEvent, RunOutputEvent]]:
@@ -2974,8 +3042,7 @@ class Team:
2974
3042
  model_stream = self.model.aresponse_stream(
2975
3043
  messages=run_messages.messages,
2976
3044
  response_format=response_format,
2977
- tools=self._tools_for_model,
2978
- functions=self._functions_for_model,
3045
+ tools=tools,
2979
3046
  tool_choice=self.tool_choice,
2980
3047
  tool_call_limit=self.tool_call_limit,
2981
3048
  stream_model_response=stream_model_response,
@@ -3379,7 +3446,7 @@ class Team:
3379
3446
  self._update_session_metrics(session=session)
3380
3447
 
3381
3448
  # Save session to memory
3382
- self.save_session(session=session)
3449
+ await self.asave_session(session=session)
3383
3450
 
3384
3451
  def _make_memories(
3385
3452
  self,
@@ -4882,7 +4949,30 @@ class Team:
4882
4949
  except Exception as e:
4883
4950
  log_warning(f"Failed to resolve context for '{key}': {e}")
4884
4951
 
4885
- 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(
4886
4976
  self,
4887
4977
  model: Model,
4888
4978
  run_response: TeamRunOutput,
@@ -4903,13 +4993,18 @@ class Team:
4903
4993
  add_dependencies_to_context: Optional[bool] = None,
4904
4994
  add_session_state_to_context: Optional[bool] = None,
4905
4995
  metadata: Optional[Dict[str, Any]] = None,
4906
- ) -> None:
4996
+ check_mcp_tools: bool = True,
4997
+ ) -> List[Union[Function, dict]]:
4907
4998
  # Prepare tools
4908
4999
  _tools: List[Union[Toolkit, Callable, Function, Dict]] = []
4909
5000
 
4910
5001
  # Add provided tools
4911
5002
  if self.tools is not None:
4912
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
4913
5008
  _tools.append(tool)
4914
5009
 
4915
5010
  if self.read_chat_history:
@@ -4924,7 +5019,7 @@ class Team:
4924
5019
  if self.search_session_history:
4925
5020
  _tools.append(
4926
5021
  self._get_previous_sessions_messages_function(
4927
- 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
4928
5023
  )
4929
5024
  )
4930
5025
 
@@ -5002,14 +5097,13 @@ class Team:
5002
5097
  if self.get_member_information_tool:
5003
5098
  _tools.append(self.get_member_information)
5004
5099
 
5005
- self._functions_for_model = {}
5006
- self._tools_for_model = []
5007
- self._tool_instructions = []
5008
-
5009
5100
  # Get Agent tools
5010
5101
  if len(_tools) > 0:
5011
5102
  log_debug("Processing tools for model")
5012
5103
 
5104
+ _function_names = []
5105
+ _functions: List[Union[Function, dict]] = []
5106
+
5013
5107
  # Check if we need strict mode for the model
5014
5108
  strict = False
5015
5109
  if self.output_schema is not None and not self.use_json_mode and model.supports_native_structured_outputs:
@@ -5019,25 +5113,25 @@ class Team:
5019
5113
  if isinstance(tool, Dict):
5020
5114
  # If a dict is passed, it is a builtin tool
5021
5115
  # that is run by the model provider and not the Agent
5022
- self._tools_for_model.append(tool)
5116
+ _functions.append(tool)
5023
5117
  log_debug(f"Included builtin tool {tool}")
5024
5118
 
5025
5119
  elif isinstance(tool, Toolkit):
5026
5120
  # For each function in the toolkit and process entrypoint
5027
- for name, func in tool.functions.items():
5028
- # If the function does not exist in self.functions
5029
- if name not in self._functions_for_model:
5030
- func._team = self
5031
- func._session_state = session_state
5032
- func._dependencies = dependencies
5033
- func.process_entrypoint(strict=strict)
5034
- if strict:
5035
- func.strict = True
5036
- if self.tool_hooks:
5037
- func.tool_hooks = self.tool_hooks
5038
- self._functions_for_model[name] = func
5039
- self._tools_for_model.append({"type": "function", "function": func.to_dict()})
5040
- 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}")
5041
5135
 
5042
5136
  # Add instructions from the toolkit
5043
5137
  if tool.add_instructions and tool.instructions is not None:
@@ -5046,18 +5140,18 @@ class Team:
5046
5140
  self._tool_instructions.append(tool.instructions)
5047
5141
 
5048
5142
  elif isinstance(tool, Function):
5049
- if tool.name not in self._functions_for_model:
5050
- tool._team = self
5051
- tool._session_state = session_state
5052
- tool._dependencies = dependencies
5053
- tool.process_entrypoint(strict=strict)
5054
- if strict and tool.strict is None:
5055
- tool.strict = True
5056
- if self.tool_hooks:
5057
- tool.tool_hooks = self.tool_hooks
5058
- self._functions_for_model[tool.name] = tool
5059
- self._tools_for_model.append({"type": "function", "function": tool.to_dict()})
5060
- 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}")
5061
5155
 
5062
5156
  # Add instructions from the Function
5063
5157
  if tool.add_instructions and tool.instructions is not None:
@@ -5068,43 +5162,49 @@ class Team:
5068
5162
  elif callable(tool):
5069
5163
  # We add the tools, which are callable functions
5070
5164
  try:
5071
- func = Function.from_callable(tool, strict=strict)
5072
- func._team = self
5073
- func._session_state = session_state
5074
- 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
5075
5172
  if strict:
5076
- func.strict = True
5173
+ _func.strict = True
5077
5174
  if self.tool_hooks:
5078
- func.tool_hooks = self.tool_hooks
5079
- self._functions_for_model[func.name] = func
5080
- self._tools_for_model.append({"type": "function", "function": func.to_dict()})
5081
- 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}")
5082
5178
  except Exception as e:
5083
5179
  log_warning(f"Could not add tool {tool}: {e}")
5084
5180
 
5085
- if self._functions_for_model:
5181
+ if _functions:
5086
5182
  from inspect import signature
5087
5183
 
5088
5184
  # Check if any functions need media before collecting
5089
5185
  needs_media = any(
5090
5186
  any(param in signature(func.entrypoint).parameters for param in ["images", "videos", "audios", "files"])
5091
- for func in self._functions_for_model.values()
5092
- if func.entrypoint is not None
5187
+ for func in _functions
5188
+ if isinstance(func, Function) and func.entrypoint is not None
5093
5189
  )
5094
5190
 
5095
- if needs_media:
5096
- # Only collect media if functions actually need them
5097
- joint_images = collect_joint_images(run_response.input, session) # type: ignore
5098
- joint_files = collect_joint_files(run_response.input) # type: ignore
5099
- joint_audios = collect_joint_audios(run_response.input, session) # type: ignore
5100
- 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
5101
5196
 
5102
- 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
5103
5201
  func._images = joint_images
5104
5202
  func._files = joint_files
5105
5203
  func._audios = joint_audios
5106
5204
  func._videos = joint_videos
5107
5205
 
5206
+ return _functions
5207
+
5108
5208
  def get_members_system_message_content(self, indent: int = 0) -> str:
5109
5209
  system_message_content = ""
5110
5210
  for idx, member in enumerate(self.members):
@@ -5148,6 +5248,7 @@ class Team:
5148
5248
  files: Optional[Sequence[File]] = None,
5149
5249
  dependencies: Optional[Dict[str, Any]] = None,
5150
5250
  metadata: Optional[Dict[str, Any]] = None,
5251
+ tools: Optional[List[Union[Function, dict]]] = None,
5151
5252
  add_session_state_to_context: Optional[bool] = None,
5152
5253
  ) -> Optional[Message]:
5153
5254
  """Get the system message for the team."""
@@ -5201,7 +5302,7 @@ class Team:
5201
5302
  instructions.extend(_instructions)
5202
5303
 
5203
5304
  # 1.2 Add instructions from the Model
5204
- _model_instructions = self.model.get_instructions_for_model(self._tools_for_model)
5305
+ _model_instructions = self.model.get_instructions_for_model(tools)
5205
5306
  if _model_instructions is not None:
5206
5307
  instructions.extend(_model_instructions)
5207
5308
 
@@ -5404,7 +5505,7 @@ class Team:
5404
5505
  metadata=metadata,
5405
5506
  )
5406
5507
 
5407
- 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)
5408
5509
  if system_message_from_model is not None:
5409
5510
  system_message_content += system_message_from_model
5410
5511
 
@@ -5441,6 +5542,7 @@ class Team:
5441
5542
  files: Optional[Sequence[File]] = None,
5442
5543
  dependencies: Optional[Dict[str, Any]] = None,
5443
5544
  metadata: Optional[Dict[str, Any]] = None,
5545
+ tools: Optional[List[Union[Function, dict]]] = None,
5444
5546
  add_session_state_to_context: Optional[bool] = None,
5445
5547
  ) -> Optional[Message]:
5446
5548
  """Get the system message for the team."""
@@ -5494,7 +5596,7 @@ class Team:
5494
5596
  instructions.extend(_instructions)
5495
5597
 
5496
5598
  # 1.2 Add instructions from the Model
5497
- _model_instructions = self.model.get_instructions_for_model(self._tools_for_model)
5599
+ _model_instructions = self.model.get_instructions_for_model(tools)
5498
5600
  if _model_instructions is not None:
5499
5601
  instructions.extend(_model_instructions)
5500
5602
 
@@ -5702,7 +5804,7 @@ class Team:
5702
5804
  metadata=metadata,
5703
5805
  )
5704
5806
 
5705
- 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)
5706
5808
  if system_message_from_model is not None:
5707
5809
  system_message_content += system_message_from_model
5708
5810
 
@@ -5749,6 +5851,7 @@ class Team:
5749
5851
  add_dependencies_to_context: Optional[bool] = None,
5750
5852
  add_session_state_to_context: Optional[bool] = None,
5751
5853
  metadata: Optional[Dict[str, Any]] = None,
5854
+ tools: Optional[List[Union[Function, dict]]] = None,
5752
5855
  **kwargs: Any,
5753
5856
  ) -> RunMessages:
5754
5857
  """This function returns a RunMessages object with the following attributes:
@@ -5779,6 +5882,7 @@ class Team:
5779
5882
  dependencies=dependencies,
5780
5883
  metadata=metadata,
5781
5884
  add_session_state_to_context=add_session_state_to_context,
5885
+ tools=tools,
5782
5886
  )
5783
5887
  if system_message is not None:
5784
5888
  run_messages.system_message = system_message
@@ -5887,6 +5991,7 @@ class Team:
5887
5991
  add_dependencies_to_context: Optional[bool] = None,
5888
5992
  add_session_state_to_context: Optional[bool] = None,
5889
5993
  metadata: Optional[Dict[str, Any]] = None,
5994
+ tools: Optional[List[Union[Function, dict]]] = None,
5890
5995
  **kwargs: Any,
5891
5996
  ) -> RunMessages:
5892
5997
  """This function returns a RunMessages object with the following attributes:
@@ -5917,6 +6022,7 @@ class Team:
5917
6022
  dependencies=dependencies,
5918
6023
  metadata=metadata,
5919
6024
  add_session_state_to_context=add_session_state_to_context,
6025
+ tools=tools,
5920
6026
  )
5921
6027
  if system_message is not None:
5922
6028
  run_messages.system_message = system_message
@@ -6514,7 +6620,7 @@ class Team:
6514
6620
  return f"Updated session state: {session_state}"
6515
6621
 
6516
6622
  def _get_previous_sessions_messages_function(
6517
- 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
6518
6624
  ):
6519
6625
  """Factory function to create a get_previous_session_messages function.
6520
6626
 
@@ -6591,13 +6697,22 @@ class Team:
6591
6697
  return "Previous session messages not available"
6592
6698
 
6593
6699
  self.db = cast(AsyncBaseDb, self.db)
6594
- selected_sessions = await self.db.get_sessions(
6595
- session_type=SessionType.TEAM,
6596
- limit=num_history_sessions,
6597
- user_id=user_id,
6598
- sort_by="created_at",
6599
- sort_order="desc",
6600
- )
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
+ )
6601
6716
 
6602
6717
  all_messages = []
6603
6718
  seen_message_pairs = set()
@@ -6677,14 +6792,14 @@ class Team:
6677
6792
  ) -> Optional[str]:
6678
6793
  team_member_interactions_str = None
6679
6794
  if self.share_member_interactions:
6680
- team_member_interactions_str = self._get_team_member_interactions_str(team_run_context=team_run_context) # type: ignore
6681
- 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
6682
6797
  images.extend(context_images)
6683
- 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
6684
6799
  videos.extend(context_videos)
6685
- 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
6686
6801
  audio.extend(context_audio)
6687
- 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
6688
6803
  files.extend(context_files)
6689
6804
  return team_member_interactions_str
6690
6805
 
@@ -6751,6 +6866,11 @@ class Team:
6751
6866
  # 1. Initialize the member agent
6752
6867
  self._initialize_member(member_agent)
6753
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
+
6754
6874
  # 2. Handle respond_directly nuances
6755
6875
  if self.respond_directly:
6756
6876
  # Since we return the response directly from the member agent, we need to set the output schema from the team down.
@@ -6822,7 +6942,7 @@ class Team:
6822
6942
  normalized_task = str(member_agent_task.content)
6823
6943
  else:
6824
6944
  normalized_task = ""
6825
- self._add_interaction_to_team_run_context(
6945
+ add_interaction_to_team_run_context(
6826
6946
  team_run_context=team_run_context,
6827
6947
  member_name=member_name,
6828
6948
  task=normalized_task,
@@ -6880,6 +7000,7 @@ class Team:
6880
7000
  use_agent_logger()
6881
7001
 
6882
7002
  member_session_state_copy = copy(session_state)
7003
+
6883
7004
  if stream:
6884
7005
  member_agent_run_response_stream = member_agent.run(
6885
7006
  input=member_agent_task if not history else history,
@@ -7006,6 +7127,7 @@ class Team:
7006
7127
  use_agent_logger()
7007
7128
 
7008
7129
  member_session_state_copy = copy(session_state)
7130
+
7009
7131
  if stream:
7010
7132
  member_agent_run_response_stream = member_agent.arun( # type: ignore
7011
7133
  input=member_agent_task if not history else history,
@@ -7310,6 +7432,7 @@ class Team:
7310
7432
 
7311
7433
  async def run_member_agent(agent=current_agent) -> str:
7312
7434
  member_session_state_copy = copy(session_state)
7435
+
7313
7436
  member_agent_run_response = await agent.arun(
7314
7437
  input=member_agent_task if not history else history,
7315
7438
  user_id=user_id,
@@ -7444,57 +7567,6 @@ class Team:
7444
7567
  log_warning(f"Error upserting session into db: {e}")
7445
7568
  return None
7446
7569
 
7447
- def get_run_output(
7448
- self, run_id: str, session_id: Optional[str] = None
7449
- ) -> Optional[Union[TeamRunOutput, RunOutput]]:
7450
- """
7451
- Get a RunOutput from the database.
7452
-
7453
- Args:
7454
- run_id (str): The run_id to load from storage.
7455
- session_id (Optional[str]): The session_id to load from storage.
7456
- """
7457
- if self._team_session is not None:
7458
- run_response = self._team_session.get_run(run_id=run_id)
7459
- if run_response is not None:
7460
- return run_response
7461
- else:
7462
- log_warning(f"RunOutput {run_id} not found in AgentSession {self._team_session.session_id}")
7463
- return None
7464
- else:
7465
- team_session = self.get_session(session_id=session_id)
7466
- if team_session is not None:
7467
- run_response = team_session.get_run(run_id=run_id)
7468
- if run_response is not None:
7469
- return cast(TeamRunOutput, run_response)
7470
- else:
7471
- log_warning(f"RunOutput {run_id} not found in AgentSession {session_id}")
7472
- return None
7473
-
7474
- def get_last_run_output(self, session_id: Optional[str] = None) -> Optional[TeamRunOutput]:
7475
- """
7476
- Get the last run response from the database.
7477
-
7478
- Args:
7479
- session_id (Optional[str]): The session_id to load from storage.
7480
-
7481
- Returns:
7482
- RunOutput: The last run response from the database.
7483
- """
7484
- if self._team_session is not None and self._team_session.runs is not None and len(self._team_session.runs) > 0:
7485
- run_response = self._team_session.runs[-1]
7486
- if run_response is not None:
7487
- return run_response # type: ignore
7488
- else:
7489
- agent_session = self.get_session(session_id=session_id)
7490
- if agent_session is not None and agent_session.runs is not None and len(agent_session.runs) > 0:
7491
- run_response = agent_session.runs[-1]
7492
- if run_response is not None:
7493
- return run_response # type: ignore
7494
- else:
7495
- log_warning(f"No run responses found in AgentSession {session_id}")
7496
- return None
7497
-
7498
7570
  def _read_or_create_session(self, session_id: str, user_id: Optional[str] = None) -> TeamSession:
7499
7571
  """Load the TeamSession from storage
7500
7572
 
@@ -7506,8 +7578,8 @@ class Team:
7506
7578
  from agno.session.team import TeamSession
7507
7579
 
7508
7580
  # Return existing session if we have one
7509
- if self._team_session is not None and self._team_session.session_id == session_id:
7510
- 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
7511
7583
 
7512
7584
  # Try to load from database
7513
7585
  team_session = None
@@ -7534,7 +7606,7 @@ class Team:
7534
7606
 
7535
7607
  # Cache the session if relevant
7536
7608
  if team_session is not None and self.cache_session:
7537
- self._team_session = team_session
7609
+ self._cached_session = team_session
7538
7610
 
7539
7611
  return team_session
7540
7612
 
@@ -7549,8 +7621,8 @@ class Team:
7549
7621
  from agno.session.team import TeamSession
7550
7622
 
7551
7623
  # Return existing session if we have one
7552
- if self._team_session is not None and self._team_session.session_id == session_id:
7553
- 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
7554
7626
 
7555
7627
  # Try to load from database
7556
7628
  team_session = None
@@ -7575,10 +7647,116 @@ class Team:
7575
7647
 
7576
7648
  # Cache the session if relevant
7577
7649
  if team_session is not None and self.cache_session:
7578
- self._team_session = team_session
7650
+ self._cached_session = team_session
7579
7651
 
7580
7652
  return team_session
7581
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
+
7582
7760
  def get_session(
7583
7761
  self,
7584
7762
  session_id: Optional[str] = None,
@@ -7592,14 +7770,17 @@ class Team:
7592
7770
  TeamSession: The TeamSession loaded from the database or created if it does not exist.
7593
7771
  """
7594
7772
  if not session_id and not self.session_id:
7595
- return None
7773
+ raise Exception("No session_id provided")
7596
7774
 
7597
7775
  session_id_to_load = session_id or self.session_id
7598
7776
 
7599
7777
  # If there is a cached session, return it
7600
- if self.cache_session and hasattr(self, "_team_session") and self._team_session is not None:
7601
- if self._team_session.session_id == session_id_to_load:
7602
- 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")
7603
7784
 
7604
7785
  # Load and return the session from the database
7605
7786
  if self.db is not None:
@@ -7633,30 +7814,44 @@ class Team:
7633
7814
  TeamSession: The TeamSession loaded from the database or created if it does not exist.
7634
7815
  """
7635
7816
  if not session_id and not self.session_id:
7636
- return None
7817
+ raise Exception("No session_id provided")
7637
7818
 
7638
7819
  session_id_to_load = session_id or self.session_id
7639
7820
 
7640
7821
  # If there is a cached session, return it
7641
- if self.cache_session and hasattr(self, "_team_session") and self._team_session is not None:
7642
- if self._team_session.session_id == session_id_to_load:
7643
- 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
7644
7825
 
7645
7826
  # Load and return the session from the database
7646
7827
  if self.db is not None:
7647
- 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
7648
7835
 
7649
7836
  # Cache the session if relevant
7650
- if team_session is not None and self.cache_session:
7651
- self._team_session = team_session
7837
+ if loaded_session is not None and self.cache_session:
7838
+ self._cached_session = loaded_session
7652
7839
 
7653
- return team_session
7840
+ return loaded_session
7654
7841
 
7655
7842
  log_debug(f"TeamSession {session_id_to_load} not found in db")
7656
7843
  return None
7657
7844
 
7658
7845
  def save_session(self, session: TeamSession) -> None:
7659
- """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
+
7660
7855
  if self.db is not None and self.parent_team_id is None and self.workflow_id is None:
7661
7856
  if session.session_data is not None and "session_state" in session.session_data:
7662
7857
  session.session_data["session_state"].pop("current_session_id", None) # type: ignore
@@ -7677,7 +7872,12 @@ class Team:
7677
7872
  log_debug(f"Created or updated TeamSession record: {session.session_id}")
7678
7873
 
7679
7874
  async def asave_session(self, session: TeamSession) -> None:
7680
- """Save the TeamSession to storage"""
7875
+ """
7876
+ Save the TeamSession to storage
7877
+
7878
+ Args:
7879
+ session: The TeamSession to save.
7880
+ """
7681
7881
  if self.db is not None and self.parent_team_id is None and self.workflow_id is None:
7682
7882
  if session.session_data is not None and "session_state" in session.session_data:
7683
7883
  session.session_data["session_state"].pop("current_session_id", None) # type: ignore
@@ -7696,49 +7896,15 @@ class Team:
7696
7896
  self._upsert_session(session=session)
7697
7897
  log_debug(f"Created or updated TeamSession record: {session.session_id}")
7698
7898
 
7699
- def _load_session_state(self, session: TeamSession, session_state: Dict[str, Any]) -> Dict[str, Any]:
7700
- """Load and return the stored session_state from the database, optionally merging it with the given one"""
7701
-
7702
- from agno.utils.merge_dict import merge_dictionaries
7703
-
7704
- # Get the session_state from the database and merge with proper precedence
7705
- # At this point session_state contains: agent_defaults + run_params
7706
- if session.session_data is not None and "session_state" in session.session_data:
7707
- session_state_from_db = session.session_data.get("session_state")
7708
-
7709
- if (
7710
- session_state_from_db is not None
7711
- and isinstance(session_state_from_db, dict)
7712
- and len(session_state_from_db) > 0
7713
- and not self.overwrite_db_session_state
7714
- ):
7715
- # This preserves precedence: run_params > db_state > agent_defaults
7716
- merged_state = session_state_from_db.copy()
7717
- merge_dictionaries(merged_state, session_state)
7718
- session_state.clear()
7719
- session_state.update(merged_state)
7720
-
7721
- # Update the session_state in the session
7722
- if session.session_data is not None:
7723
- session.session_data["session_state"] = session_state
7724
-
7725
- return session_state
7726
-
7727
- def _update_metadata(self, session: TeamSession):
7728
- """Update the extra_data in the session"""
7729
- from agno.utils.merge_dict import merge_dictionaries
7730
-
7731
- # Read metadata from the database
7732
- if session.metadata is not None:
7733
- # If metadata is set in the agent, update the database metadata with the agent's metadata
7734
- if self.metadata is not None:
7735
- # Updates agent's session metadata in place
7736
- merge_dictionaries(session.metadata, self.metadata)
7737
- # Update the current metadata with the metadata from the database which is updated in place
7738
- self.metadata = session.metadata
7899
+ def generate_session_name(self, session: TeamSession) -> str:
7900
+ """
7901
+ Generate a name for the team session
7739
7902
 
7740
- def _generate_session_name(self, session: TeamSession) -> str:
7741
- """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
+ """
7742
7908
 
7743
7909
  if self.model is None:
7744
7910
  raise Exception("Model not set")
@@ -7766,62 +7932,113 @@ class Team:
7766
7932
  content = generated_name.content
7767
7933
  if content is None:
7768
7934
  log_error("Generated name is None. Trying again.")
7769
- return self._generate_session_name(session=session)
7935
+ return self.generate_session_name(session=session)
7770
7936
  if len(content.split()) > 15:
7771
7937
  log_error("Generated name is too long. Trying again.")
7772
- return self._generate_session_name(session=session)
7938
+ return self.generate_session_name(session=session)
7773
7939
  return content.replace('"', "").strip()
7774
7940
 
7775
7941
  def set_session_name(
7776
7942
  self, session_id: Optional[str] = None, autogenerate: bool = False, session_name: Optional[str] = None
7777
7943
  ) -> TeamSession:
7778
- """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
+ """
7779
7954
  session_id = session_id or self.session_id
7780
7955
 
7781
7956
  if session_id is None:
7782
7957
  raise Exception("Session ID is not set")
7783
7958
 
7784
- # -*- Read from storage
7785
- session = self.get_session(session_id=session_id) # type: ignore
7786
-
7787
- if session is None:
7788
- 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
+ )
7789
7963
 
7790
- # -*- Generate name for session
7791
- if autogenerate:
7792
- session_name = self._generate_session_name(session=session)
7793
- log_debug(f"Generated Session Name: {session_name}")
7794
- elif session_name is None:
7795
- 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
7796
7969
 
7797
- # -*- Rename session
7798
- if session.session_data is not None:
7799
- 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
7800
7978
 
7801
- # -*- Save to storage
7802
- self.save_session(session=session) # type: ignore
7979
+ if session_id is None:
7980
+ raise Exception("Session ID is not set")
7803
7981
 
7804
- 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
+ )
7805
7988
 
7806
7989
  def get_session_name(self, session_id: Optional[str] = None) -> str:
7807
- """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
+ """
7808
7998
  session_id = session_id or self.session_id
7809
7999
  if session_id is None:
7810
8000
  raise Exception("Session ID is not set")
7811
- session = self.get_session(session_id=session_id) # type: ignore
7812
- if session is None:
7813
- raise Exception("Session not found")
7814
- 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)
7815
8016
 
7816
8017
  def get_session_state(self, session_id: Optional[str] = None) -> Dict[str, Any]:
7817
- """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
+ """
7818
8025
  session_id = session_id or self.session_id
7819
8026
  if session_id is None:
7820
8027
  raise Exception("Session ID is not set")
7821
- session = self.get_session(session_id=session_id) # type: ignore
7822
- if session is None:
7823
- raise Exception("Session not found")
7824
- 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)
7825
8042
 
7826
8043
  def update_session_state(self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None) -> str:
7827
8044
  """
@@ -7835,19 +8052,7 @@ class Team:
7835
8052
  session_id = session_id or self.session_id
7836
8053
  if session_id is None:
7837
8054
  raise Exception("Session ID is not set")
7838
- session = self.get_session(session_id=session_id) # type: ignore
7839
- if session is None:
7840
- raise Exception("Session not found")
7841
-
7842
- if session.session_data is not None and "session_state" not in session.session_data:
7843
- session.session_data["session_state"] = {}
7844
-
7845
- for key, value in session_state_updates.items():
7846
- session.session_data["session_state"][key] = value # type: ignore
7847
-
7848
- self.save_session(session=session)
7849
-
7850
- 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)
7851
8056
 
7852
8057
  async def aupdate_session_state(
7853
8058
  self, session_state_updates: Dict[str, Any], session_id: Optional[str] = None
@@ -7863,64 +8068,117 @@ class Team:
7863
8068
  session_id = session_id or self.session_id
7864
8069
  if session_id is None:
7865
8070
  raise Exception("Session ID is not set")
7866
- session = await self.aget_session(session_id=session_id) # type: ignore
7867
- if session is None:
7868
- 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
+ )
7869
8074
 
7870
- if session.session_data is not None and "session_state" not in session.session_data:
7871
- 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.
7872
8077
 
7873
- for key, value in session_state_updates.items():
7874
- 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")
7875
8086
 
7876
- await self.asave_session(session=session)
8087
+ return get_session_metrics_util(self, session_id=session_id)
7877
8088
 
7878
- 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.
7879
8091
 
7880
- def get_session_metrics(self, session_id: Optional[str] = None) -> Optional[Metrics]:
7881
- """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
+ """
7882
8097
  session_id = session_id or self.session_id
7883
8098
  if session_id is None:
7884
8099
  raise Exception("Session ID is not set")
7885
8100
 
7886
- session = self.get_session(session_id=session_id) # type: ignore
7887
- if session is None:
7888
- raise Exception("Session not found")
8101
+ return await aget_session_metrics_util(self, session_id=session_id)
7889
8102
 
7890
- if session.session_data is not None:
7891
- if isinstance(session.session_data.get("session_metrics"), dict):
7892
- return Metrics(**session.session_data.get("session_metrics", {}))
7893
- elif isinstance(session.session_data.get("session_metrics"), Metrics):
7894
- return session.session_data.get("session_metrics")
7895
- 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
7896
8107
 
7897
- def delete_session(self, session_id: str) -> None:
8108
+ self.db.delete_session(session_id=session_id)
8109
+
8110
+ async def adelete_session(self, session_id: str):
7898
8111
  """Delete the current session and save to storage"""
7899
- if self.db is not None:
7900
- 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
7901
8115
 
7902
8116
  def get_chat_history(self, session_id: Optional[str] = None) -> List[Message]:
7903
- """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
+ """
8138
+ session_id = session_id or self.session_id
8139
+ if session_id is None:
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
+ """
7904
8152
  session_id = session_id or self.session_id
7905
8153
  if session_id is None:
7906
- log_warning("Session ID is not set, cannot get chat history")
8154
+ log_warning("Session ID is not set, cannot get messages for session")
7907
8155
  return []
7908
8156
 
7909
8157
  session = self.get_session(session_id=session_id) # type: ignore
7910
8158
 
7911
8159
  if session is None:
7912
- raise Exception("Session not found")
8160
+ log_warning(f"Session {session_id} not found")
8161
+ return []
7913
8162
 
7914
- return session.get_chat_history()
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
+ )
7915
8167
 
7916
- def get_messages_for_session(self, session_id: Optional[str] = None) -> List[Message]:
7917
- """Get messages for a session"""
8168
+ async def aget_messages_for_session(self, session_id: Optional[str] = None) -> List[Message]:
8169
+ """Get messages for a session
8170
+
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
+ """
7918
8176
  session_id = session_id or self.session_id
7919
8177
  if session_id is None:
7920
8178
  log_warning("Session ID is not set, cannot get messages for session")
7921
8179
  return []
7922
8180
 
7923
- session = self.get_session(session_id=session_id) # type: ignore
8181
+ session = await self.aget_session(session_id=session_id) # type: ignore
7924
8182
 
7925
8183
  if session is None:
7926
8184
  log_warning(f"Session {session_id} not found")
@@ -7931,8 +8189,14 @@ class Team:
7931
8189
  team_id=self.id,
7932
8190
  )
7933
8191
 
7934
- def get_session_summary(self, session_id: Optional[str] = None):
7935
- """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
+ """
7936
8200
  session_id = session_id if session_id is not None else self.session_id
7937
8201
  if session_id is None:
7938
8202
  raise ValueError("Session ID is required")
@@ -7944,8 +8208,33 @@ class Team:
7944
8208
 
7945
8209
  return session.get_session_summary() # type: ignore
7946
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
+
7947
8230
  def get_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
7948
- """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
+ """
7949
8238
  if self.memory_manager is None:
7950
8239
  return None
7951
8240
  user_id = user_id if user_id is not None else self.user_id
@@ -7954,86 +8243,21 @@ class Team:
7954
8243
 
7955
8244
  return self.memory_manager.get_user_memories(user_id=user_id)
7956
8245
 
7957
- def _add_interaction_to_team_run_context(
7958
- self,
7959
- team_run_context: Dict[str, Any],
7960
- member_name: str,
7961
- task: str,
7962
- run_response: Union[RunOutput, TeamRunOutput],
7963
- ) -> None:
7964
- if "member_responses" not in team_run_context:
7965
- team_run_context["member_responses"] = []
7966
- team_run_context["member_responses"].append(
7967
- {
7968
- "member_name": member_name,
7969
- "task": task,
7970
- "run_response": run_response,
7971
- }
7972
- )
7973
- log_debug(f"Updated team run context with member name: {member_name}")
7974
-
7975
- def _get_team_member_interactions_str(self, team_run_context: Dict[str, Any]) -> str:
7976
- if not team_run_context:
7977
- return ""
7978
- team_member_interactions_str = ""
7979
- if "member_responses" in team_run_context:
7980
- team_member_interactions_str += (
7981
- "<member_interaction_context>\nSee below interactions wit other team members.\n"
7982
- )
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.
7983
8248
 
7984
- for interaction in team_run_context["member_responses"]:
7985
- response_dict = interaction["run_response"].to_dict()
7986
- response_content = (
7987
- response_dict.get("content")
7988
- or ",".join([tool.get("content", "") for tool in response_dict.get("tools", [])])
7989
- or ""
7990
- )
7991
- team_member_interactions_str += f"Member: {interaction['member_name']}\n"
7992
- team_member_interactions_str += f"Task: {interaction['task']}\n"
7993
- team_member_interactions_str += f"Response: {response_content}\n"
7994
- team_member_interactions_str += "\n"
7995
- team_member_interactions_str += "</member_interaction_context>\n"
7996
- 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"
7997
8259
 
7998
- def _get_team_run_context_images(self, team_run_context: Dict[str, Any]) -> List[Image]:
7999
- if not team_run_context:
8000
- return []
8001
- images = []
8002
- if "member_responses" in team_run_context:
8003
- for interaction in team_run_context["member_responses"]:
8004
- if interaction["run_response"].images:
8005
- images.extend(interaction["run_response"].images)
8006
- return images
8007
-
8008
- def _get_team_run_context_videos(self, team_run_context: Dict[str, Any]) -> List[Video]:
8009
- if not team_run_context:
8010
- return []
8011
- videos = []
8012
- if "member_responses" in team_run_context:
8013
- for interaction in team_run_context["member_responses"]:
8014
- if interaction["run_response"].videos:
8015
- videos.extend(interaction["run_response"].videos)
8016
- return videos
8017
-
8018
- def _get_team_run_context_audio(self, team_run_context: Dict[str, Any]) -> List[Audio]:
8019
- if not team_run_context:
8020
- return []
8021
- audio = []
8022
- if "member_responses" in team_run_context:
8023
- for interaction in team_run_context["member_responses"]:
8024
- if interaction["run_response"].audio:
8025
- audio.extend(interaction["run_response"].audio)
8026
- return audio
8027
-
8028
- def _get_team_run_context_files(self, team_run_context: Dict[str, Any]) -> List[File]:
8029
- if not team_run_context:
8030
- return []
8031
- files = []
8032
- if "member_responses" in team_run_context:
8033
- for interaction in team_run_context["member_responses"]:
8034
- if interaction["run_response"].files:
8035
- files.extend(interaction["run_response"].files)
8036
- return files
8260
+ return await self.memory_manager.aget_user_memories(user_id=user_id)
8037
8261
 
8038
8262
  ###########################################################################
8039
8263
  # Handle images, videos and audio
@@ -8182,6 +8406,8 @@ class Team:
8182
8406
  document_name = query.replace(" ", "_").replace("?", "").replace("!", "").replace(".", "")
8183
8407
  document_content = json.dumps({"query": query, "result": result})
8184
8408
  log_info(f"Adding document to Knowledge: {document_name}: {document_content}")
8409
+ import asyncio
8410
+
8185
8411
  from agno.knowledge.reader.text_reader import TextReader
8186
8412
 
8187
8413
  asyncio.run(
@@ -8275,7 +8501,7 @@ class Team:
8275
8501
  log_warning("No valid filters remain after validation. Search will proceed without filters.")
8276
8502
 
8277
8503
  if self.knowledge_retriever is not None and callable(self.knowledge_retriever):
8278
- from inspect import signature
8504
+ from inspect import isawaitable, signature
8279
8505
 
8280
8506
  try:
8281
8507
  sig = signature(self.knowledge_retriever)
@@ -8285,7 +8511,13 @@ class Team:
8285
8511
  if "filters" in sig.parameters:
8286
8512
  knowledge_retriever_kwargs["filters"] = filters
8287
8513
  knowledge_retriever_kwargs.update({"query": query, "num_documents": num_documents, **kwargs})
8288
- return self.knowledge_retriever(**knowledge_retriever_kwargs)
8514
+
8515
+ result = self.knowledge_retriever(**knowledge_retriever_kwargs)
8516
+
8517
+ if isawaitable(result):
8518
+ result = await result
8519
+
8520
+ return result
8289
8521
  except Exception as e:
8290
8522
  log_warning(f"Knowledge retriever failed: {e}")
8291
8523
  raise e