agno 2.3.7__py3-none-any.whl → 2.3.9__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 +391 -335
  2. agno/db/mongo/async_mongo.py +0 -24
  3. agno/db/mongo/mongo.py +0 -16
  4. agno/db/mysql/__init__.py +2 -1
  5. agno/db/mysql/async_mysql.py +2888 -0
  6. agno/db/mysql/mysql.py +17 -27
  7. agno/db/mysql/utils.py +139 -6
  8. agno/db/postgres/async_postgres.py +10 -26
  9. agno/db/postgres/postgres.py +7 -25
  10. agno/db/redis/redis.py +0 -4
  11. agno/db/schemas/evals.py +1 -0
  12. agno/db/singlestore/singlestore.py +5 -12
  13. agno/db/sqlite/async_sqlite.py +2 -26
  14. agno/db/sqlite/sqlite.py +0 -20
  15. agno/eval/__init__.py +10 -0
  16. agno/eval/agent_as_judge.py +860 -0
  17. agno/eval/base.py +29 -0
  18. agno/eval/utils.py +2 -1
  19. agno/exceptions.py +7 -0
  20. agno/knowledge/embedder/openai.py +8 -8
  21. agno/knowledge/knowledge.py +1142 -176
  22. agno/media.py +22 -6
  23. agno/models/aws/claude.py +8 -7
  24. agno/models/base.py +160 -11
  25. agno/models/deepseek/deepseek.py +67 -0
  26. agno/models/google/gemini.py +65 -11
  27. agno/models/google/utils.py +22 -0
  28. agno/models/message.py +2 -0
  29. agno/models/openai/chat.py +4 -0
  30. agno/models/openai/responses.py +3 -2
  31. agno/os/app.py +64 -74
  32. agno/os/interfaces/a2a/router.py +3 -4
  33. agno/os/interfaces/a2a/utils.py +1 -1
  34. agno/os/interfaces/agui/router.py +2 -0
  35. agno/os/middleware/jwt.py +8 -6
  36. agno/os/router.py +3 -1607
  37. agno/os/routers/agents/__init__.py +3 -0
  38. agno/os/routers/agents/router.py +581 -0
  39. agno/os/routers/agents/schema.py +261 -0
  40. agno/os/routers/evals/evals.py +26 -6
  41. agno/os/routers/evals/schemas.py +34 -2
  42. agno/os/routers/evals/utils.py +101 -20
  43. agno/os/routers/knowledge/knowledge.py +1 -1
  44. agno/os/routers/teams/__init__.py +3 -0
  45. agno/os/routers/teams/router.py +496 -0
  46. agno/os/routers/teams/schema.py +257 -0
  47. agno/os/routers/workflows/__init__.py +3 -0
  48. agno/os/routers/workflows/router.py +545 -0
  49. agno/os/routers/workflows/schema.py +75 -0
  50. agno/os/schema.py +1 -559
  51. agno/os/utils.py +139 -2
  52. agno/team/team.py +159 -100
  53. agno/tools/file_generation.py +12 -6
  54. agno/tools/firecrawl.py +15 -7
  55. agno/tools/workflow.py +8 -1
  56. agno/utils/hooks.py +64 -5
  57. agno/utils/http.py +2 -2
  58. agno/utils/media.py +11 -1
  59. agno/utils/print_response/agent.py +8 -0
  60. agno/utils/print_response/team.py +8 -0
  61. agno/vectordb/pgvector/pgvector.py +88 -51
  62. agno/workflow/parallel.py +11 -5
  63. agno/workflow/step.py +17 -5
  64. agno/workflow/types.py +38 -2
  65. agno/workflow/workflow.py +12 -4
  66. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/METADATA +8 -3
  67. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/RECORD +70 -58
  68. agno/tools/memori.py +0 -339
  69. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/WHEEL +0 -0
  70. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/licenses/LICENSE +0 -0
  71. {agno-2.3.7.dist-info → agno-2.3.9.dist-info}/top_level.txt +0 -0
agno/team/team.py CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import contextlib
5
5
  import json
6
+ import time
6
7
  import warnings
7
8
  from collections import ChainMap, deque
8
9
  from copy import copy
@@ -10,6 +11,7 @@ from dataclasses import dataclass
10
11
  from os import getenv
11
12
  from textwrap import dedent
12
13
  from typing import (
14
+ TYPE_CHECKING,
13
15
  Any,
14
16
  AsyncIterator,
15
17
  Callable,
@@ -31,12 +33,14 @@ from uuid import uuid4
31
33
 
32
34
  from pydantic import BaseModel
33
35
 
36
+ if TYPE_CHECKING:
37
+ from agno.eval.base import BaseEval
38
+
34
39
  from agno.agent import Agent
35
40
  from agno.compression.manager import CompressionManager
36
41
  from agno.db.base import AsyncBaseDb, BaseDb, SessionType, UserMemory
37
42
  from agno.exceptions import (
38
43
  InputCheckError,
39
- ModelProviderError,
40
44
  OutputCheckError,
41
45
  RunCancelledException,
42
46
  )
@@ -115,7 +119,6 @@ from agno.utils.events import (
115
119
  create_team_run_cancelled_event,
116
120
  create_team_run_completed_event,
117
121
  create_team_run_content_completed_event,
118
- create_team_run_error_event,
119
122
  create_team_run_output_content_event,
120
123
  create_team_run_started_event,
121
124
  create_team_session_summary_completed_event,
@@ -124,7 +127,13 @@ from agno.utils.events import (
124
127
  create_team_tool_call_started_event,
125
128
  handle_event,
126
129
  )
127
- from agno.utils.hooks import copy_args_for_background, filter_hook_args, normalize_hooks, should_run_hook_in_background
130
+ from agno.utils.hooks import (
131
+ copy_args_for_background,
132
+ filter_hook_args,
133
+ normalize_post_hooks,
134
+ normalize_pre_hooks,
135
+ should_run_hook_in_background,
136
+ )
128
137
  from agno.utils.knowledge import get_agentic_or_user_search_filters
129
138
  from agno.utils.log import (
130
139
  log_debug,
@@ -267,6 +276,8 @@ class Team:
267
276
  system_message: Optional[Union[str, Callable, Message]] = None
268
277
  # Role for the system message
269
278
  system_message_role: str = "system"
279
+ # Introduction for the team
280
+ introduction: Optional[str] = None
270
281
 
271
282
  # If True, resolve the session_state, dependencies, and metadata in the user and system messages
272
283
  resolve_in_context: bool = True
@@ -343,9 +354,9 @@ class Team:
343
354
 
344
355
  # --- Team Hooks ---
345
356
  # Functions called right after team session is loaded, before processing starts
346
- pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None
357
+ pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]] = None
347
358
  # Functions called after output is generated but before the response is returned
348
- post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None
359
+ post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]] = None
349
360
  # If True, run hooks as FastAPI background tasks (non-blocking). Set by AgentOS.
350
361
  _run_hooks_in_background: Optional[bool] = None
351
362
 
@@ -487,6 +498,7 @@ class Team:
487
498
  add_member_tools_to_context: bool = False,
488
499
  system_message: Optional[Union[str, Callable, Message]] = None,
489
500
  system_message_role: str = "system",
501
+ introduction: Optional[str] = None,
490
502
  additional_input: Optional[List[Union[str, Dict, BaseModel, Message]]] = None,
491
503
  dependencies: Optional[Dict[str, Any]] = None,
492
504
  add_dependencies_to_context: bool = False,
@@ -513,8 +525,8 @@ class Team:
513
525
  tool_call_limit: Optional[int] = None,
514
526
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
515
527
  tool_hooks: Optional[List[Callable]] = None,
516
- pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None,
517
- post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None,
528
+ pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]] = None,
529
+ post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail, "BaseEval"]]] = None,
518
530
  input_schema: Optional[Type[BaseModel]] = None,
519
531
  output_schema: Optional[Type[BaseModel]] = None,
520
532
  parser_model: Optional[Union[Model, str]] = None,
@@ -612,6 +624,7 @@ class Team:
612
624
  self.add_member_tools_to_context = add_member_tools_to_context
613
625
  self.system_message = system_message
614
626
  self.system_message_role = system_message_role
627
+ self.introduction = introduction
615
628
  self.additional_input = additional_input
616
629
 
617
630
  self.dependencies = dependencies
@@ -1554,7 +1567,12 @@ class Team:
1554
1567
 
1555
1568
  # 4. Start memory creation in background thread
1556
1569
  memory_future = None
1557
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
1570
+ if (
1571
+ run_messages.user_message is not None
1572
+ and self.memory_manager is not None
1573
+ and self.enable_user_memories
1574
+ and not self.enable_agentic_memory
1575
+ ):
1558
1576
  log_debug("Starting memory creation in background thread.")
1559
1577
  memory_future = self.background_executor.submit(
1560
1578
  self._make_memories, run_messages=run_messages, user_id=user_id
@@ -1761,7 +1779,12 @@ class Team:
1761
1779
 
1762
1780
  # 4. Start memory creation in background thread
1763
1781
  memory_future = None
1764
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
1782
+ if (
1783
+ run_messages.user_message is not None
1784
+ and self.memory_manager is not None
1785
+ and self.enable_user_memories
1786
+ and not self.enable_agentic_memory
1787
+ ):
1765
1788
  log_debug("Starting memory creation in background thread.")
1766
1789
  memory_future = self.background_executor.submit(
1767
1790
  self._make_memories, run_messages=run_messages, user_id=user_id
@@ -1963,7 +1986,7 @@ class Team:
1963
1986
  session_id: Optional[str] = None,
1964
1987
  session_state: Optional[Dict[str, Any]] = None,
1965
1988
  user_id: Optional[str] = None,
1966
- retries: Optional[int] = None,
1989
+ run_id: Optional[str] = None,
1967
1990
  audio: Optional[Sequence[Audio]] = None,
1968
1991
  images: Optional[Sequence[Image]] = None,
1969
1992
  videos: Optional[Sequence[Video]] = None,
@@ -1991,7 +2014,7 @@ class Team:
1991
2014
  session_state: Optional[Dict[str, Any]] = None,
1992
2015
  run_context: Optional[RunContext] = None,
1993
2016
  user_id: Optional[str] = None,
1994
- retries: Optional[int] = None,
2017
+ run_id: Optional[str] = None,
1995
2018
  audio: Optional[Sequence[Audio]] = None,
1996
2019
  images: Optional[Sequence[Image]] = None,
1997
2020
  videos: Optional[Sequence[Video]] = None,
@@ -2019,8 +2042,8 @@ class Team:
2019
2042
  session_id: Optional[str] = None,
2020
2043
  session_state: Optional[Dict[str, Any]] = None,
2021
2044
  run_context: Optional[RunContext] = None,
2045
+ run_id: Optional[str] = None,
2022
2046
  user_id: Optional[str] = None,
2023
- retries: Optional[int] = None,
2024
2047
  audio: Optional[Sequence[Audio]] = None,
2025
2048
  images: Optional[Sequence[Image]] = None,
2026
2049
  videos: Optional[Sequence[Video]] = None,
@@ -2041,8 +2064,8 @@ class Team:
2041
2064
  if self._has_async_db():
2042
2065
  raise Exception("run() is not supported with an async DB. Please use arun() instead.")
2043
2066
 
2044
- # Create a run_id for this specific run and register immediately for cancellation tracking
2045
- run_id = str(uuid4())
2067
+ # Set the id for the run and register it immediately for cancellation tracking
2068
+ run_id = run_id or str(uuid4())
2046
2069
  register_run(run_id)
2047
2070
 
2048
2071
  # Initialize Team
@@ -2059,6 +2082,7 @@ class Team:
2059
2082
  DeprecationWarning,
2060
2083
  stacklevel=2,
2061
2084
  )
2085
+ yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
2062
2086
 
2063
2087
  background_tasks = kwargs.pop("background_tasks", None)
2064
2088
  if background_tasks is not None:
@@ -2072,9 +2096,9 @@ class Team:
2072
2096
  # Normalise hook & guardails
2073
2097
  if not self._hooks_normalised:
2074
2098
  if self.pre_hooks:
2075
- self.pre_hooks = normalize_hooks(self.pre_hooks) # type: ignore
2099
+ self.pre_hooks = normalize_pre_hooks(self.pre_hooks) # type: ignore
2076
2100
  if self.post_hooks:
2077
- self.post_hooks = normalize_hooks(self.post_hooks) # type: ignore
2101
+ self.post_hooks = normalize_post_hooks(self.post_hooks) # type: ignore
2078
2102
  self._hooks_normalised = True
2079
2103
 
2080
2104
  session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
@@ -2196,18 +2220,11 @@ class Team:
2196
2220
  run_response.metrics = Metrics()
2197
2221
  run_response.metrics.start_timer()
2198
2222
 
2199
- # If no retries are set, use the team's default retries
2200
- retries = retries if retries is not None else self.retries
2201
-
2202
- # Run the team
2203
- last_exception = None
2204
- num_attempts = retries + 1
2205
-
2206
- yield_run_output = bool(yield_run_output or yield_run_response) # For backwards compatibility
2223
+ # Set up retry logic
2224
+ num_attempts = self.retries + 1
2207
2225
 
2208
2226
  for attempt in range(num_attempts):
2209
- # Initialize the current run
2210
-
2227
+ log_debug(f"Retrying Team run {run_id}. Attempt {attempt + 1} of {num_attempts}...")
2211
2228
  # Run the team
2212
2229
  try:
2213
2230
  if stream:
@@ -2246,18 +2263,6 @@ class Team:
2246
2263
  except (InputCheckError, OutputCheckError) as e:
2247
2264
  log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
2248
2265
  raise e
2249
- except ModelProviderError as e:
2250
- import time
2251
-
2252
- log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
2253
-
2254
- last_exception = e
2255
- if attempt < num_attempts - 1: # Don't sleep on the last attempt
2256
- if self.exponential_backoff:
2257
- delay = 2**attempt * self.delay_between_retries
2258
- else:
2259
- delay = self.delay_between_retries
2260
- time.sleep(delay)
2261
2266
  except KeyboardInterrupt:
2262
2267
  run_response.content = "Operation cancelled by user"
2263
2268
  run_response.status = RunStatus.cancelled
@@ -2270,21 +2275,24 @@ class Team:
2270
2275
  )
2271
2276
  else:
2272
2277
  return run_response
2278
+ except Exception as e:
2279
+ # Check if this is the last attempt
2280
+ if attempt < num_attempts - 1:
2281
+ # Calculate delay with exponential backoff if enabled
2282
+ if self.exponential_backoff:
2283
+ delay = self.delay_between_retries * (2**attempt)
2284
+ else:
2285
+ delay = self.delay_between_retries
2273
2286
 
2274
- # If we get here, all retries failed
2275
- if last_exception is not None:
2276
- log_error(
2277
- f"Failed after {num_attempts} attempts. Last error using {last_exception.model_name}({last_exception.model_id})"
2278
- )
2279
- if stream:
2280
- return generator_wrapper(create_team_run_error_event(run_response, error=str(last_exception)))
2281
-
2282
- raise last_exception
2283
- else:
2284
- if stream:
2285
- return generator_wrapper(create_team_run_error_event(run_response, error=str(last_exception)))
2287
+ log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
2288
+ time.sleep(delay)
2289
+ else:
2290
+ # Final attempt failed - re-raise the exception
2291
+ log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
2292
+ raise e
2286
2293
 
2287
- raise Exception(f"Failed after {num_attempts} attempts.")
2294
+ # If we get here, all retries failed
2295
+ raise Exception(f"Failed after {num_attempts} attempts.")
2288
2296
 
2289
2297
  async def _arun(
2290
2298
  self,
@@ -2411,7 +2419,12 @@ class Team:
2411
2419
 
2412
2420
  # 6. Start memory creation in background task
2413
2421
  memory_task = None
2414
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2422
+ if (
2423
+ run_messages.user_message is not None
2424
+ and self.memory_manager is not None
2425
+ and self.enable_user_memories
2426
+ and not self.enable_agentic_memory
2427
+ ):
2415
2428
  log_debug("Starting memory creation in background task.")
2416
2429
  memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
2417
2430
 
@@ -2649,7 +2662,12 @@ class Team:
2649
2662
 
2650
2663
  # 7. Start memory creation in background task
2651
2664
  memory_task = None
2652
- if run_messages.user_message is not None and self.memory_manager is not None and not self.enable_agentic_memory:
2665
+ if (
2666
+ run_messages.user_message is not None
2667
+ and self.memory_manager is not None
2668
+ and self.enable_user_memories
2669
+ and not self.enable_agentic_memory
2670
+ ):
2653
2671
  log_debug("Starting memory creation in background task.")
2654
2672
  memory_task = asyncio.create_task(self._amake_memories(run_messages=run_messages, user_id=user_id))
2655
2673
 
@@ -2865,9 +2883,9 @@ class Team:
2865
2883
  stream_intermediate_steps: Optional[bool] = None,
2866
2884
  session_id: Optional[str] = None,
2867
2885
  session_state: Optional[Dict[str, Any]] = None,
2886
+ run_id: Optional[str] = None,
2868
2887
  run_context: Optional[RunContext] = None,
2869
2888
  user_id: Optional[str] = None,
2870
- retries: Optional[int] = None,
2871
2889
  audio: Optional[Sequence[Audio]] = None,
2872
2890
  images: Optional[Sequence[Image]] = None,
2873
2891
  videos: Optional[Sequence[Video]] = None,
@@ -2893,9 +2911,9 @@ class Team:
2893
2911
  stream_intermediate_steps: Optional[bool] = None,
2894
2912
  session_id: Optional[str] = None,
2895
2913
  session_state: Optional[Dict[str, Any]] = None,
2914
+ run_id: Optional[str] = None,
2896
2915
  run_context: Optional[RunContext] = None,
2897
2916
  user_id: Optional[str] = None,
2898
- retries: Optional[int] = None,
2899
2917
  audio: Optional[Sequence[Audio]] = None,
2900
2918
  images: Optional[Sequence[Image]] = None,
2901
2919
  videos: Optional[Sequence[Video]] = None,
@@ -2922,9 +2940,9 @@ class Team:
2922
2940
  stream_intermediate_steps: Optional[bool] = None,
2923
2941
  session_id: Optional[str] = None,
2924
2942
  session_state: Optional[Dict[str, Any]] = None,
2943
+ run_id: Optional[str] = None,
2925
2944
  run_context: Optional[RunContext] = None,
2926
2945
  user_id: Optional[str] = None,
2927
- retries: Optional[int] = None,
2928
2946
  audio: Optional[Sequence[Audio]] = None,
2929
2947
  images: Optional[Sequence[Image]] = None,
2930
2948
  videos: Optional[Sequence[Video]] = None,
@@ -2943,8 +2961,8 @@ class Team:
2943
2961
  ) -> Union[TeamRunOutput, AsyncIterator[Union[RunOutputEvent, TeamRunOutputEvent]]]:
2944
2962
  """Run the Team asynchronously and return the response."""
2945
2963
 
2946
- # Create a run_id for this specific run and register immediately for cancellation tracking
2947
- run_id = str(uuid4())
2964
+ # Set the id for the run and register it immediately for cancellation tracking
2965
+ run_id = run_id or str(uuid4())
2948
2966
  register_run(run_id)
2949
2967
 
2950
2968
  if (add_history_to_context or self.add_history_to_context) and not self.db and not self.parent_team_id:
@@ -2959,6 +2977,8 @@ class Team:
2959
2977
  stacklevel=2,
2960
2978
  )
2961
2979
 
2980
+ yield_run_output = yield_run_output or yield_run_response # For backwards compatibility
2981
+
2962
2982
  background_tasks = kwargs.pop("background_tasks", None)
2963
2983
  if background_tasks is not None:
2964
2984
  from fastapi import BackgroundTasks
@@ -2971,9 +2991,9 @@ class Team:
2971
2991
  # Normalise hook & guardails
2972
2992
  if not self._hooks_normalised:
2973
2993
  if self.pre_hooks:
2974
- self.pre_hooks = normalize_hooks(self.pre_hooks, async_mode=True) # type: ignore
2994
+ self.pre_hooks = normalize_pre_hooks(self.pre_hooks, async_mode=True) # type: ignore
2975
2995
  if self.post_hooks:
2976
- self.post_hooks = normalize_hooks(self.post_hooks, async_mode=True) # type: ignore
2996
+ self.post_hooks = normalize_post_hooks(self.post_hooks, async_mode=True) # type: ignore
2977
2997
  self._hooks_normalised = True
2978
2998
 
2979
2999
  session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
@@ -3084,20 +3104,16 @@ class Team:
3084
3104
  run_response.metrics = Metrics()
3085
3105
  run_response.metrics.start_timer()
3086
3106
 
3087
- # If no retries are set, use the team's default retries
3088
- retries = retries if retries is not None else self.retries
3089
-
3090
- # Run the team
3091
- last_exception = None
3092
- num_attempts = retries + 1
3093
-
3094
3107
  yield_run_output = bool(yield_run_output or yield_run_response) # For backwards compatibility
3095
3108
 
3109
+ # Resolve retry parameters
3110
+ num_attempts = self.retries + 1
3111
+
3096
3112
  for attempt in range(num_attempts):
3097
3113
  # Run the team
3098
3114
  try:
3099
3115
  if stream:
3100
- response_iterator = self._arun_stream(
3116
+ return self._arun_stream( # type: ignore
3101
3117
  input=validated_input,
3102
3118
  run_response=run_response,
3103
3119
  run_context=run_context,
@@ -3113,7 +3129,6 @@ class Team:
3113
3129
  background_tasks=background_tasks,
3114
3130
  **kwargs,
3115
3131
  )
3116
- return response_iterator # type: ignore
3117
3132
  else:
3118
3133
  return self._arun( # type: ignore
3119
3134
  input=validated_input,
@@ -3133,17 +3148,6 @@ class Team:
3133
3148
  except (InputCheckError, OutputCheckError) as e:
3134
3149
  log_error(f"Validation failed: {str(e)} | Check: {e.check_trigger}")
3135
3150
  raise e
3136
- except ModelProviderError as e:
3137
- log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}")
3138
- last_exception = e
3139
- if attempt < num_attempts - 1: # Don't sleep on the last attempt
3140
- if self.exponential_backoff:
3141
- delay = 2**attempt * self.delay_between_retries
3142
- else:
3143
- delay = self.delay_between_retries
3144
- import time
3145
-
3146
- time.sleep(delay)
3147
3151
  except KeyboardInterrupt:
3148
3152
  run_response.content = "Operation cancelled by user"
3149
3153
  run_response.status = RunStatus.cancelled
@@ -3156,21 +3160,25 @@ class Team:
3156
3160
  )
3157
3161
  else:
3158
3162
  return run_response
3163
+ except Exception as e:
3164
+ # Check if this is the last attempt
3165
+ if attempt < num_attempts - 1:
3166
+ # Calculate delay with exponential backoff if enabled
3167
+ if self.exponential_backoff:
3168
+ delay = self.delay_between_retries * (2**attempt)
3169
+ else:
3170
+ delay = self.delay_between_retries
3159
3171
 
3160
- # If we get here, all retries failed
3161
- if last_exception is not None:
3162
- log_error(
3163
- f"Failed after {num_attempts} attempts. Last error using {last_exception.model_name}({last_exception.model_id})"
3164
- )
3165
- if stream:
3166
- return async_generator_wrapper(create_team_run_error_event(run_response, error=str(last_exception)))
3167
-
3168
- raise last_exception
3169
- else:
3170
- if stream:
3171
- return async_generator_wrapper(create_team_run_error_event(run_response, error=str(last_exception)))
3172
+ log_warning(f"Attempt {attempt + 1}/{num_attempts} failed: {str(e)}. Retrying in {delay}s...")
3173
+ time.sleep(delay)
3174
+ continue
3175
+ else:
3176
+ # Final attempt failed - re-raise the exception
3177
+ log_error(f"All {num_attempts} attempts failed. Final error: {str(e)}")
3178
+ raise e
3172
3179
 
3173
- raise Exception(f"Failed after {num_attempts} attempts.")
3180
+ # If we get here, all retries failed
3181
+ raise Exception(f"Failed after {num_attempts} attempts.")
3174
3182
 
3175
3183
  def _update_run_response(
3176
3184
  self,
@@ -3821,7 +3829,12 @@ class Team:
3821
3829
  user_message_str = (
3822
3830
  run_messages.user_message.get_content_string() if run_messages.user_message is not None else None
3823
3831
  )
3824
- if user_message_str is not None and user_message_str.strip() != "" and self.memory_manager is not None:
3832
+ if (
3833
+ user_message_str is not None
3834
+ and user_message_str.strip() != ""
3835
+ and self.memory_manager is not None
3836
+ and self.enable_user_memories
3837
+ ):
3825
3838
  log_debug("Managing user memories")
3826
3839
  self.memory_manager.create_user_memories(
3827
3840
  message=user_message_str,
@@ -3837,7 +3850,12 @@ class Team:
3837
3850
  user_message_str = (
3838
3851
  run_messages.user_message.get_content_string() if run_messages.user_message is not None else None
3839
3852
  )
3840
- if user_message_str is not None and user_message_str.strip() != "" and self.memory_manager is not None:
3853
+ if (
3854
+ user_message_str is not None
3855
+ and user_message_str.strip() != ""
3856
+ and self.memory_manager is not None
3857
+ and self.enable_user_memories
3858
+ ):
3841
3859
  log_debug("Managing user memories")
3842
3860
  await self.memory_manager.acreate_user_memories(
3843
3861
  message=user_message_str,
@@ -4244,6 +4262,7 @@ class Team:
4244
4262
  session_id: Optional[str] = None,
4245
4263
  session_state: Optional[Dict[str, Any]] = None,
4246
4264
  user_id: Optional[str] = None,
4265
+ run_id: Optional[str] = None,
4247
4266
  audio: Optional[Sequence[Audio]] = None,
4248
4267
  images: Optional[Sequence[Image]] = None,
4249
4268
  videos: Optional[Sequence[Video]] = None,
@@ -4316,6 +4335,7 @@ class Team:
4316
4335
  session_id=session_id,
4317
4336
  session_state=session_state,
4318
4337
  user_id=user_id,
4338
+ run_id=run_id,
4319
4339
  audio=audio,
4320
4340
  images=images,
4321
4341
  videos=videos,
@@ -4344,6 +4364,7 @@ class Team:
4344
4364
  session_id=session_id,
4345
4365
  session_state=session_state,
4346
4366
  user_id=user_id,
4367
+ run_id=run_id,
4347
4368
  audio=audio,
4348
4369
  images=images,
4349
4370
  videos=videos,
@@ -4367,6 +4388,7 @@ class Team:
4367
4388
  session_id: Optional[str] = None,
4368
4389
  session_state: Optional[Dict[str, Any]] = None,
4369
4390
  user_id: Optional[str] = None,
4391
+ run_id: Optional[str] = None,
4370
4392
  audio: Optional[Sequence[Audio]] = None,
4371
4393
  images: Optional[Sequence[Image]] = None,
4372
4394
  videos: Optional[Sequence[Video]] = None,
@@ -4434,6 +4456,7 @@ class Team:
4434
4456
  session_id=session_id,
4435
4457
  session_state=session_state,
4436
4458
  user_id=user_id,
4459
+ run_id=run_id,
4437
4460
  audio=audio,
4438
4461
  images=images,
4439
4462
  videos=videos,
@@ -4462,6 +4485,7 @@ class Team:
4462
4485
  session_id=session_id,
4463
4486
  session_state=session_state,
4464
4487
  user_id=user_id,
4488
+ run_id=run_id,
4465
4489
  audio=audio,
4466
4490
  images=images,
4467
4491
  videos=videos,
@@ -8207,6 +8231,20 @@ class Team:
8207
8231
  metadata=self.metadata,
8208
8232
  created_at=int(time()),
8209
8233
  )
8234
+ if self.introduction is not None:
8235
+ from uuid import uuid4
8236
+
8237
+ team_session.upsert_run(
8238
+ TeamRunOutput(
8239
+ run_id=str(uuid4()),
8240
+ team_id=self.id,
8241
+ session_id=session_id,
8242
+ user_id=user_id,
8243
+ team_name=self.name,
8244
+ content=self.introduction,
8245
+ messages=[Message(role=self.model.assistant_message_role, content=self.introduction)], # type: ignore
8246
+ )
8247
+ )
8210
8248
 
8211
8249
  # Cache the session if relevant
8212
8250
  if team_session is not None and self.cache_session:
@@ -8239,15 +8277,34 @@ class Team:
8239
8277
  # Create new session if none found
8240
8278
  if team_session is None:
8241
8279
  log_debug(f"Creating new TeamSession: {session_id}")
8280
+ session_data = {}
8281
+ if self.session_state is not None:
8282
+ from copy import deepcopy
8283
+
8284
+ session_data["session_state"] = deepcopy(self.session_state)
8242
8285
  team_session = TeamSession(
8243
8286
  session_id=session_id,
8244
8287
  team_id=self.id,
8245
8288
  user_id=user_id,
8246
8289
  team_data=self._get_team_data(),
8247
- session_data={},
8290
+ session_data=session_data,
8248
8291
  metadata=self.metadata,
8249
8292
  created_at=int(time()),
8250
8293
  )
8294
+ if self.introduction is not None:
8295
+ from uuid import uuid4
8296
+
8297
+ team_session.upsert_run(
8298
+ TeamRunOutput(
8299
+ run_id=str(uuid4()),
8300
+ team_id=self.id,
8301
+ session_id=session_id,
8302
+ user_id=user_id,
8303
+ team_name=self.name,
8304
+ content=self.introduction,
8305
+ messages=[Message(role=self.model.assistant_message_role, content=self.introduction)], # type: ignore
8306
+ )
8307
+ )
8251
8308
 
8252
8309
  # Cache the session if relevant
8253
8310
  if team_session is not None and self.cache_session:
@@ -8897,12 +8954,13 @@ class Team:
8897
8954
  Optional[List[UserMemory]]: The user memories.
8898
8955
  """
8899
8956
  if self.memory_manager is None:
8900
- return None
8957
+ self._set_memory_manager()
8958
+
8901
8959
  user_id = user_id if user_id is not None else self.user_id
8902
8960
  if user_id is None:
8903
8961
  user_id = "default"
8904
8962
 
8905
- return self.memory_manager.get_user_memories(user_id=user_id)
8963
+ return self.memory_manager.get_user_memories(user_id=user_id) # type: ignore
8906
8964
 
8907
8965
  async def aget_user_memories(self, user_id: Optional[str] = None) -> Optional[List[UserMemory]]:
8908
8966
  """Get the user memories for the given user ID.
@@ -8913,12 +8971,13 @@ class Team:
8913
8971
  Optional[List[UserMemory]]: The user memories.
8914
8972
  """
8915
8973
  if self.memory_manager is None:
8916
- return None
8974
+ self._set_memory_manager()
8975
+
8917
8976
  user_id = user_id if user_id is not None else self.user_id
8918
8977
  if user_id is None:
8919
8978
  user_id = "default"
8920
8979
 
8921
- return await self.memory_manager.aget_user_memories(user_id=user_id)
8980
+ return await self.memory_manager.aget_user_memories(user_id=user_id) # type: ignore
8922
8981
 
8923
8982
  ###########################################################################
8924
8983
  # Handle reasoning content
@@ -108,14 +108,16 @@ class FileGenerationTools(Toolkit):
108
108
  # Save file to disk (if output_directory is set)
109
109
  file_path = self._save_file_to_disk(json_content, filename)
110
110
 
111
+ content_bytes = json_content.encode("utf-8")
112
+
111
113
  # Create FileArtifact
112
114
  file_artifact = File(
113
115
  id=str(uuid4()),
114
- content=json_content,
116
+ content=content_bytes,
115
117
  mime_type="application/json",
116
118
  file_type="json",
117
119
  filename=filename,
118
- size=len(json_content.encode("utf-8")),
120
+ size=len(content_bytes),
119
121
  filepath=file_path if file_path else None,
120
122
  )
121
123
 
@@ -195,14 +197,16 @@ class FileGenerationTools(Toolkit):
195
197
  # Save file to disk (if output_directory is set)
196
198
  file_path = self._save_file_to_disk(csv_content, filename)
197
199
 
200
+ content_bytes = csv_content.encode("utf-8")
201
+
198
202
  # Create FileArtifact
199
203
  file_artifact = File(
200
204
  id=str(uuid4()),
201
- content=csv_content,
205
+ content=content_bytes,
202
206
  mime_type="text/csv",
203
207
  file_type="csv",
204
208
  filename=filename,
205
- size=len(csv_content.encode("utf-8")),
209
+ size=len(content_bytes),
206
210
  filepath=file_path if file_path else None,
207
211
  )
208
212
 
@@ -325,14 +329,16 @@ class FileGenerationTools(Toolkit):
325
329
  # Save file to disk (if output_directory is set)
326
330
  file_path = self._save_file_to_disk(content, filename)
327
331
 
332
+ content_bytes = content.encode("utf-8")
333
+
328
334
  # Create FileArtifact
329
335
  file_artifact = File(
330
336
  id=str(uuid4()),
331
- content=content,
337
+ content=content_bytes,
332
338
  mime_type="text/plain",
333
339
  file_type="txt",
334
340
  filename=filename,
335
- size=len(content.encode("utf-8")),
341
+ size=len(content_bytes),
336
342
  filepath=file_path if file_path else None,
337
343
  )
338
344