agno 2.2.5__py3-none-any.whl → 2.2.7__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 (57) hide show
  1. agno/agent/agent.py +500 -423
  2. agno/api/os.py +1 -1
  3. agno/culture/manager.py +12 -8
  4. agno/guardrails/prompt_injection.py +1 -0
  5. agno/knowledge/chunking/agentic.py +6 -2
  6. agno/knowledge/embedder/vllm.py +262 -0
  7. agno/knowledge/knowledge.py +37 -5
  8. agno/memory/manager.py +9 -4
  9. agno/models/anthropic/claude.py +1 -2
  10. agno/models/azure/ai_foundry.py +31 -14
  11. agno/models/azure/openai_chat.py +12 -4
  12. agno/models/base.py +106 -65
  13. agno/models/cerebras/cerebras.py +11 -6
  14. agno/models/groq/groq.py +7 -4
  15. agno/models/meta/llama.py +12 -6
  16. agno/models/meta/llama_openai.py +5 -1
  17. agno/models/openai/chat.py +26 -17
  18. agno/models/openai/responses.py +11 -63
  19. agno/models/requesty/requesty.py +5 -2
  20. agno/models/utils.py +254 -8
  21. agno/models/vertexai/claude.py +9 -13
  22. agno/os/app.py +13 -12
  23. agno/os/routers/evals/evals.py +8 -8
  24. agno/os/routers/evals/utils.py +1 -0
  25. agno/os/schema.py +56 -38
  26. agno/os/utils.py +27 -0
  27. agno/run/__init__.py +6 -0
  28. agno/run/agent.py +5 -0
  29. agno/run/base.py +18 -1
  30. agno/run/team.py +13 -9
  31. agno/run/workflow.py +39 -0
  32. agno/session/summary.py +8 -2
  33. agno/session/workflow.py +4 -3
  34. agno/team/team.py +302 -369
  35. agno/tools/exa.py +21 -16
  36. agno/tools/file.py +153 -25
  37. agno/tools/function.py +98 -17
  38. agno/tools/mcp/mcp.py +8 -1
  39. agno/tools/notion.py +204 -0
  40. agno/utils/agent.py +78 -0
  41. agno/utils/events.py +2 -0
  42. agno/utils/hooks.py +1 -1
  43. agno/utils/models/claude.py +25 -8
  44. agno/utils/print_response/workflow.py +115 -16
  45. agno/vectordb/__init__.py +2 -1
  46. agno/vectordb/milvus/milvus.py +5 -0
  47. agno/vectordb/redis/__init__.py +5 -0
  48. agno/vectordb/redis/redisdb.py +687 -0
  49. agno/workflow/__init__.py +2 -0
  50. agno/workflow/agent.py +299 -0
  51. agno/workflow/step.py +13 -2
  52. agno/workflow/workflow.py +969 -72
  53. {agno-2.2.5.dist-info → agno-2.2.7.dist-info}/METADATA +10 -3
  54. {agno-2.2.5.dist-info → agno-2.2.7.dist-info}/RECORD +57 -52
  55. {agno-2.2.5.dist-info → agno-2.2.7.dist-info}/WHEEL +0 -0
  56. {agno-2.2.5.dist-info → agno-2.2.7.dist-info}/licenses/LICENSE +0 -0
  57. {agno-2.2.5.dist-info → agno-2.2.7.dist-info}/top_level.txt +0 -0
agno/agent/agent.py CHANGED
@@ -47,14 +47,15 @@ from agno.models.base import Model
47
47
  from agno.models.message import Message, MessageReferences
48
48
  from agno.models.metrics import Metrics
49
49
  from agno.models.response import ModelResponse, ModelResponseEvent, ToolExecution
50
+ from agno.models.utils import get_model
50
51
  from agno.reasoning.step import NextAction, ReasoningStep, ReasoningSteps
52
+ from agno.run import RunContext, RunStatus
51
53
  from agno.run.agent import (
52
54
  RunEvent,
53
55
  RunInput,
54
56
  RunOutput,
55
57
  RunOutputEvent,
56
58
  )
57
- from agno.run.base import RunStatus
58
59
  from agno.run.cancel import (
59
60
  cancel_run as cancel_run_global,
60
61
  )
@@ -94,7 +95,9 @@ from agno.utils.agent import (
94
95
  scrub_media_from_run_output,
95
96
  scrub_tool_results_from_run_output,
96
97
  set_session_name_util,
98
+ store_media_util,
97
99
  update_session_state_util,
100
+ validate_media_object_id,
98
101
  wait_for_background_tasks,
99
102
  wait_for_background_tasks_stream,
100
103
  )
@@ -262,9 +265,9 @@ class Agent:
262
265
 
263
266
  # --- Agent Hooks ---
264
267
  # Functions called right after agent-session is loaded, before processing starts
265
- pre_hooks: Optional[Union[List[Callable[..., Any]], List[BaseGuardrail]]] = None
268
+ pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None
266
269
  # Functions called after output is generated but before the response is returned
267
- post_hooks: Optional[Union[List[Callable[..., Any]], List[BaseGuardrail]]] = None
270
+ post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None
268
271
 
269
272
  # --- Agent Reasoning ---
270
273
  # Enable reasoning by working through the problem step by step.
@@ -346,7 +349,7 @@ class Agent:
346
349
 
347
350
  # --- Agent Response Model Settings ---
348
351
  # Provide an input schema to validate the input
349
- input_schema: Optional[Union[Type[BaseModel], type]] = None
352
+ input_schema: Optional[Type[BaseModel]] = None
350
353
  # Provide a response model to get the response as a Pydantic model
351
354
  output_schema: Optional[Type[BaseModel]] = None
352
355
  # Provide a secondary model to parse the response from the primary model
@@ -416,7 +419,7 @@ class Agent:
416
419
  def __init__(
417
420
  self,
418
421
  *,
419
- model: Optional[Model] = None,
422
+ model: Optional[Union[Model, str]] = None,
420
423
  name: Optional[str] = None,
421
424
  id: Optional[str] = None,
422
425
  introduction: Optional[str] = None,
@@ -457,10 +460,10 @@ class Agent:
457
460
  tool_call_limit: Optional[int] = None,
458
461
  tool_choice: Optional[Union[str, Dict[str, Any]]] = None,
459
462
  tool_hooks: Optional[List[Callable]] = None,
460
- pre_hooks: Optional[Union[List[Callable[..., Any]], List[BaseGuardrail]]] = None,
461
- post_hooks: Optional[Union[List[Callable[..., Any]], List[BaseGuardrail]]] = None,
463
+ pre_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None,
464
+ post_hooks: Optional[List[Union[Callable[..., Any], BaseGuardrail]]] = None,
462
465
  reasoning: bool = False,
463
- reasoning_model: Optional[Model] = None,
466
+ reasoning_model: Optional[Union[Model, str]] = None,
464
467
  reasoning_agent: Optional[Agent] = None,
465
468
  reasoning_min_steps: int = 1,
466
469
  reasoning_max_steps: int = 10,
@@ -488,12 +491,12 @@ class Agent:
488
491
  retries: int = 0,
489
492
  delay_between_retries: int = 1,
490
493
  exponential_backoff: bool = False,
491
- parser_model: Optional[Model] = None,
494
+ parser_model: Optional[Union[Model, str]] = None,
492
495
  parser_model_prompt: Optional[str] = None,
493
- input_schema: Optional[Union[Type[BaseModel], type]] = None,
496
+ input_schema: Optional[Type[BaseModel]] = None,
494
497
  output_schema: Optional[Type[BaseModel]] = None,
495
498
  parse_response: bool = True,
496
- output_model: Optional[Model] = None,
499
+ output_model: Optional[Union[Model, str]] = None,
497
500
  output_model_prompt: Optional[str] = None,
498
501
  structured_outputs: Optional[bool] = None,
499
502
  use_json_mode: bool = False,
@@ -512,7 +515,7 @@ class Agent:
512
515
  debug_level: Literal[1, 2] = 1,
513
516
  telemetry: bool = True,
514
517
  ):
515
- self.model = model
518
+ self.model = model # type: ignore[assignment]
516
519
  self.name = name
517
520
  self.id = id
518
521
  self.introduction = introduction
@@ -578,7 +581,7 @@ class Agent:
578
581
  self.post_hooks = post_hooks
579
582
 
580
583
  self.reasoning = reasoning
581
- self.reasoning_model = reasoning_model
584
+ self.reasoning_model = reasoning_model # type: ignore[assignment]
582
585
  self.reasoning_agent = reasoning_agent
583
586
  self.reasoning_min_steps = reasoning_min_steps
584
587
  self.reasoning_max_steps = reasoning_max_steps
@@ -609,12 +612,12 @@ class Agent:
609
612
  self.retries = retries
610
613
  self.delay_between_retries = delay_between_retries
611
614
  self.exponential_backoff = exponential_backoff
612
- self.parser_model = parser_model
615
+ self.parser_model = parser_model # type: ignore[assignment]
613
616
  self.parser_model_prompt = parser_model_prompt
614
617
  self.input_schema = input_schema
615
618
  self.output_schema = output_schema
616
619
  self.parse_response = parse_response
617
- self.output_model = output_model
620
+ self.output_model = output_model # type: ignore[assignment]
618
621
  self.output_model_prompt = output_model_prompt
619
622
 
620
623
  self.structured_outputs = structured_outputs
@@ -658,6 +661,8 @@ class Agent:
658
661
  # Lazy-initialized shared thread pool executor for background tasks (memory, cultural knowledge, etc.)
659
662
  self._background_executor: Optional[Any] = None
660
663
 
664
+ self._get_models()
665
+
661
666
  @property
662
667
  def background_executor(self) -> Any:
663
668
  """Lazy initialization of shared thread pool executor for background tasks.
@@ -814,6 +819,16 @@ class Agent:
814
819
  """Return True if the db the agent is equipped with is an Async implementation"""
815
820
  return self.db is not None and isinstance(self.db, AsyncBaseDb)
816
821
 
822
+ def _get_models(self) -> None:
823
+ if self.model is not None:
824
+ self.model = get_model(self.model)
825
+ if self.reasoning_model is not None:
826
+ self.reasoning_model = get_model(self.reasoning_model)
827
+ if self.parser_model is not None:
828
+ self.parser_model = get_model(self.parser_model)
829
+ if self.output_model is not None:
830
+ self.output_model = get_model(self.output_model)
831
+
817
832
  def initialize_agent(self, debug_mode: Optional[bool] = None) -> None:
818
833
  self._set_default_model()
819
834
  self._set_debug(debug_mode=debug_mode)
@@ -900,16 +915,13 @@ class Agent:
900
915
  def _run(
901
916
  self,
902
917
  run_response: RunOutput,
918
+ run_context: RunContext,
903
919
  session: AgentSession,
904
- session_state: Optional[Dict[str, Any]] = None,
905
920
  user_id: Optional[str] = None,
906
- knowledge_filters: Optional[Dict[str, Any]] = None,
907
921
  add_history_to_context: Optional[bool] = None,
908
922
  add_dependencies_to_context: Optional[bool] = None,
909
923
  add_session_state_to_context: Optional[bool] = None,
910
- metadata: Optional[Dict[str, Any]] = None,
911
924
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
912
- dependencies: Optional[Dict[str, Any]] = None,
913
925
  debug_mode: Optional[bool] = None,
914
926
  **kwargs: Any,
915
927
  ) -> RunOutput:
@@ -943,9 +955,7 @@ class Agent:
943
955
  hooks=self.pre_hooks, # type: ignore
944
956
  run_response=run_response,
945
957
  run_input=run_input,
946
- session_state=session_state,
947
- dependencies=dependencies,
948
- metadata=metadata,
958
+ run_context=run_context,
949
959
  session=session,
950
960
  user_id=user_id,
951
961
  debug_mode=debug_mode,
@@ -957,36 +967,32 @@ class Agent:
957
967
  # 2. Determine tools for model
958
968
  processed_tools = self.get_tools(
959
969
  run_response=run_response,
970
+ run_context=run_context,
960
971
  session=session,
961
972
  user_id=user_id,
962
- knowledge_filters=knowledge_filters,
963
973
  )
964
974
  _tools = self._determine_tools_for_model(
965
975
  model=self.model,
966
976
  processed_tools=processed_tools,
967
977
  run_response=run_response,
968
978
  session=session,
969
- session_state=session_state,
970
- dependencies=dependencies,
979
+ run_context=run_context,
971
980
  )
972
981
 
973
982
  # 3. Prepare run messages
974
983
  run_messages: RunMessages = self._get_run_messages(
975
984
  run_response=run_response,
985
+ run_context=run_context,
976
986
  input=run_input.input_content,
977
987
  session=session,
978
- session_state=session_state,
979
988
  user_id=user_id,
980
989
  audio=run_input.audios,
981
990
  images=run_input.images,
982
991
  videos=run_input.videos,
983
992
  files=run_input.files,
984
- knowledge_filters=knowledge_filters,
985
993
  add_history_to_context=add_history_to_context,
986
- dependencies=dependencies,
987
994
  add_dependencies_to_context=add_dependencies_to_context,
988
995
  add_session_state_to_context=add_session_state_to_context,
989
- metadata=metadata,
990
996
  tools=_tools,
991
997
  **kwargs,
992
998
  )
@@ -1061,7 +1067,7 @@ class Agent:
1061
1067
 
1062
1068
  # 8. Store media if enabled
1063
1069
  if self.store_media:
1064
- self._store_media(run_response, model_response)
1070
+ store_media_util(run_response, model_response)
1065
1071
 
1066
1072
  # 9. Convert the response to the structured format if needed
1067
1073
  self._convert_response_to_structured_format(run_response)
@@ -1071,11 +1077,9 @@ class Agent:
1071
1077
  post_hook_iterator = self._execute_post_hooks(
1072
1078
  hooks=self.post_hooks, # type: ignore
1073
1079
  run_output=run_response,
1080
+ run_context=run_context,
1074
1081
  session=session,
1075
1082
  user_id=user_id,
1076
- session_state=session_state,
1077
- dependencies=dependencies,
1078
- metadata=metadata,
1079
1083
  debug_mode=debug_mode,
1080
1084
  **kwargs,
1081
1085
  )
@@ -1099,7 +1103,9 @@ class Agent:
1099
1103
  run_response.status = RunStatus.completed
1100
1104
 
1101
1105
  # 13. Cleanup and store the run response and session
1102
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
1106
+ self._cleanup_and_store(
1107
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
1108
+ )
1103
1109
 
1104
1110
  # Log Agent Telemetry
1105
1111
  self._log_agent_telemetry(session_id=session.session_id, run_id=run_response.run_id)
@@ -1114,7 +1120,9 @@ class Agent:
1114
1120
  run_response.status = RunStatus.cancelled
1115
1121
 
1116
1122
  # Cleanup and store the run response and session
1117
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
1123
+ self._cleanup_and_store(
1124
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
1125
+ )
1118
1126
 
1119
1127
  return run_response
1120
1128
  finally:
@@ -1124,15 +1132,12 @@ class Agent:
1124
1132
  def _run_stream(
1125
1133
  self,
1126
1134
  run_response: RunOutput,
1135
+ run_context: RunContext,
1127
1136
  session: AgentSession,
1128
- session_state: Optional[Dict[str, Any]] = None,
1129
1137
  user_id: Optional[str] = None,
1130
- knowledge_filters: Optional[Dict[str, Any]] = None,
1131
1138
  add_history_to_context: Optional[bool] = None,
1132
1139
  add_dependencies_to_context: Optional[bool] = None,
1133
1140
  add_session_state_to_context: Optional[bool] = None,
1134
- metadata: Optional[Dict[str, Any]] = None,
1135
- dependencies: Optional[Dict[str, Any]] = None,
1136
1141
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1137
1142
  stream_events: bool = False,
1138
1143
  yield_run_response: bool = False,
@@ -1166,9 +1171,7 @@ class Agent:
1166
1171
  hooks=self.pre_hooks, # type: ignore
1167
1172
  run_response=run_response,
1168
1173
  run_input=run_input,
1169
- session_state=session_state,
1170
- dependencies=dependencies,
1171
- metadata=metadata,
1174
+ run_context=run_context,
1172
1175
  session=session,
1173
1176
  user_id=user_id,
1174
1177
  debug_mode=debug_mode,
@@ -1180,17 +1183,16 @@ class Agent:
1180
1183
  # 2. Determine tools for model
1181
1184
  processed_tools = self.get_tools(
1182
1185
  run_response=run_response,
1186
+ run_context=run_context,
1183
1187
  session=session,
1184
1188
  user_id=user_id,
1185
- knowledge_filters=knowledge_filters,
1186
1189
  )
1187
1190
  _tools = self._determine_tools_for_model(
1188
1191
  model=self.model,
1189
1192
  processed_tools=processed_tools,
1190
1193
  run_response=run_response,
1191
1194
  session=session,
1192
- session_state=session_state,
1193
- dependencies=dependencies,
1195
+ run_context=run_context,
1194
1196
  )
1195
1197
 
1196
1198
  # 3. Prepare run messages
@@ -1198,18 +1200,15 @@ class Agent:
1198
1200
  run_response=run_response,
1199
1201
  input=run_input.input_content,
1200
1202
  session=session,
1201
- session_state=session_state,
1203
+ run_context=run_context,
1202
1204
  user_id=user_id,
1203
1205
  audio=run_input.audios,
1204
1206
  images=run_input.images,
1205
1207
  videos=run_input.videos,
1206
1208
  files=run_input.files,
1207
- knowledge_filters=knowledge_filters,
1208
1209
  add_history_to_context=add_history_to_context,
1209
- dependencies=dependencies,
1210
1210
  add_dependencies_to_context=add_dependencies_to_context,
1211
1211
  add_session_state_to_context=add_session_state_to_context,
1212
- metadata=metadata,
1213
1212
  tools=_tools,
1214
1213
  **kwargs,
1215
1214
  )
@@ -1268,6 +1267,7 @@ class Agent:
1268
1267
  tools=_tools,
1269
1268
  response_format=response_format,
1270
1269
  stream_events=stream_events,
1270
+ session_state=run_context.session_state,
1271
1271
  ):
1272
1272
  raise_if_cancelled(run_response.run_id) # type: ignore
1273
1273
  yield event
@@ -1284,6 +1284,7 @@ class Agent:
1284
1284
  tools=_tools,
1285
1285
  response_format=response_format,
1286
1286
  stream_events=stream_events,
1287
+ session_state=run_context.session_state,
1287
1288
  ):
1288
1289
  raise_if_cancelled(run_response.run_id) # type: ignore
1289
1290
  if isinstance(event, RunContentEvent):
@@ -1303,7 +1304,7 @@ class Agent:
1303
1304
  stream_events=stream_events,
1304
1305
  ):
1305
1306
  raise_if_cancelled(run_response.run_id) # type: ignore
1306
- yield event
1307
+ yield event # type: ignore
1307
1308
 
1308
1309
  # Check for cancellation after model processing
1309
1310
  raise_if_cancelled(run_response.run_id) # type: ignore
@@ -1344,9 +1345,7 @@ class Agent:
1344
1345
  yield from self._execute_post_hooks(
1345
1346
  hooks=self.post_hooks, # type: ignore
1346
1347
  run_output=run_response,
1347
- session_state=session_state,
1348
- dependencies=dependencies,
1349
- metadata=metadata,
1348
+ run_context=run_context,
1350
1349
  session=session,
1351
1350
  user_id=user_id,
1352
1351
  debug_mode=debug_mode,
@@ -1387,6 +1386,11 @@ class Agent:
1387
1386
  store_events=self.store_events,
1388
1387
  )
1389
1388
 
1389
+ # Update run_response.session_state before creating RunCompletedEvent
1390
+ # This ensures the event has the final state after all tool modifications
1391
+ if session.session_data is not None and "session_state" in session.session_data:
1392
+ run_response.session_state = session.session_data["session_state"]
1393
+
1390
1394
  # Create the run completed event
1391
1395
  completed_event = handle_event( # type: ignore
1392
1396
  create_run_completed_event(from_run_response=run_response),
@@ -1399,7 +1403,9 @@ class Agent:
1399
1403
  run_response.status = RunStatus.completed
1400
1404
 
1401
1405
  # 10. Cleanup and store the run response and session
1402
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
1406
+ self._cleanup_and_store(
1407
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
1408
+ )
1403
1409
 
1404
1410
  if stream_events:
1405
1411
  yield completed_event # type: ignore
@@ -1430,7 +1436,9 @@ class Agent:
1430
1436
  )
1431
1437
 
1432
1438
  # Cleanup and store the run response and session
1433
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
1439
+ self._cleanup_and_store(
1440
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
1441
+ )
1434
1442
  finally:
1435
1443
  # Always clean up the run tracking
1436
1444
  cleanup_run(run_response.run_id) # type: ignore
@@ -1533,9 +1541,9 @@ class Agent:
1533
1541
  # Normalise hook & guardails
1534
1542
  if not self._hooks_normalised:
1535
1543
  if self.pre_hooks:
1536
- self.pre_hooks = normalize_hooks(self.pre_hooks)
1544
+ self.pre_hooks = normalize_hooks(self.pre_hooks) # type: ignore
1537
1545
  if self.post_hooks:
1538
- self.post_hooks = normalize_hooks(self.post_hooks)
1546
+ self.post_hooks = normalize_hooks(self.post_hooks) # type: ignore
1539
1547
  self._hooks_normalised = True
1540
1548
 
1541
1549
  session_id, user_id = self._initialize_session(session_id=session_id, user_id=user_id)
@@ -1543,7 +1551,7 @@ class Agent:
1543
1551
  # Initialize the Agent
1544
1552
  self.initialize_agent(debug_mode=debug_mode)
1545
1553
 
1546
- image_artifacts, video_artifacts, audio_artifacts, file_artifacts = self._validate_media_object_id(
1554
+ image_artifacts, video_artifacts, audio_artifacts, file_artifacts = validate_media_object_id(
1547
1555
  images=images, videos=videos, audios=audio, files=files
1548
1556
  )
1549
1557
 
@@ -1566,12 +1574,22 @@ class Agent:
1566
1574
  )
1567
1575
  # Update session state from DB
1568
1576
  session_state = self._load_session_state(session=agent_session, session_state=session_state)
1577
+
1569
1578
  # Determine runtime dependencies
1570
- run_dependencies = dependencies if dependencies is not None else self.dependencies
1579
+ dependencies = dependencies if dependencies is not None else self.dependencies
1580
+
1581
+ # Initialize run context
1582
+ run_context = RunContext(
1583
+ run_id=run_id,
1584
+ session_id=session_id,
1585
+ user_id=user_id,
1586
+ session_state=session_state,
1587
+ dependencies=dependencies,
1588
+ )
1571
1589
 
1572
1590
  # Resolve dependencies
1573
- if run_dependencies is not None:
1574
- self._resolve_run_dependencies(dependencies=run_dependencies)
1591
+ if run_context.dependencies is not None:
1592
+ self._resolve_run_dependencies(run_context=run_context)
1575
1593
 
1576
1594
  add_dependencies = (
1577
1595
  add_dependencies_to_context if add_dependencies_to_context is not None else self.add_dependencies_to_context
@@ -1583,12 +1601,9 @@ class Agent:
1583
1601
  )
1584
1602
  add_history = add_history_to_context if add_history_to_context is not None else self.add_history_to_context
1585
1603
 
1586
- # Initialize Knowledge Filters
1587
- effective_filters = knowledge_filters
1588
-
1589
1604
  # When filters are passed manually
1590
1605
  if self.knowledge_filters or knowledge_filters:
1591
- effective_filters = self._get_effective_filters(knowledge_filters)
1606
+ run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
1592
1607
 
1593
1608
  # Use stream override value when necessary
1594
1609
  if stream is None:
@@ -1612,11 +1627,8 @@ class Agent:
1612
1627
  self.model = cast(Model, self.model)
1613
1628
 
1614
1629
  # Merge agent metadata with run metadata
1615
- if self.metadata is not None:
1616
- if metadata is None:
1617
- metadata = self.metadata
1618
- else:
1619
- merge_dictionaries(metadata, self.metadata)
1630
+ if self.metadata is not None and metadata is not None:
1631
+ merge_dictionaries(metadata, self.metadata)
1620
1632
 
1621
1633
  # Create a new run_response for this attempt
1622
1634
  run_response = RunOutput(
@@ -1625,7 +1637,8 @@ class Agent:
1625
1637
  agent_id=self.id,
1626
1638
  user_id=user_id,
1627
1639
  agent_name=self.name,
1628
- metadata=metadata,
1640
+ metadata=run_context.metadata,
1641
+ session_state=run_context.session_state,
1629
1642
  input=run_input,
1630
1643
  )
1631
1644
 
@@ -1647,15 +1660,12 @@ class Agent:
1647
1660
  if stream:
1648
1661
  response_iterator = self._run_stream(
1649
1662
  run_response=run_response,
1663
+ run_context=run_context,
1650
1664
  session=agent_session,
1651
- session_state=session_state,
1652
1665
  user_id=user_id,
1653
- knowledge_filters=effective_filters,
1654
1666
  add_history_to_context=add_history,
1655
1667
  add_dependencies_to_context=add_dependencies,
1656
1668
  add_session_state_to_context=add_session_state,
1657
- metadata=metadata,
1658
- dependencies=run_dependencies,
1659
1669
  response_format=response_format,
1660
1670
  stream_events=stream_events,
1661
1671
  yield_run_response=yield_run_response,
@@ -1666,15 +1676,12 @@ class Agent:
1666
1676
  else:
1667
1677
  response = self._run(
1668
1678
  run_response=run_response,
1679
+ run_context=run_context,
1669
1680
  session=agent_session,
1670
- session_state=session_state,
1671
1681
  user_id=user_id,
1672
- knowledge_filters=effective_filters,
1673
1682
  add_history_to_context=add_history,
1674
1683
  add_dependencies_to_context=add_dependencies,
1675
1684
  add_session_state_to_context=add_session_state,
1676
- metadata=metadata,
1677
- dependencies=run_dependencies,
1678
1685
  response_format=response_format,
1679
1686
  debug_mode=debug_mode,
1680
1687
  **kwargs,
@@ -1727,16 +1734,13 @@ class Agent:
1727
1734
  async def _arun(
1728
1735
  self,
1729
1736
  run_response: RunOutput,
1737
+ run_context: RunContext,
1730
1738
  session_id: str,
1731
- session_state: Optional[Dict[str, Any]] = None,
1732
1739
  user_id: Optional[str] = None,
1733
- knowledge_filters: Optional[Dict[str, Any]] = None,
1734
1740
  add_history_to_context: Optional[bool] = None,
1735
1741
  add_dependencies_to_context: Optional[bool] = None,
1736
1742
  add_session_state_to_context: Optional[bool] = None,
1737
- metadata: Optional[Dict[str, Any]] = None,
1738
1743
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
1739
- dependencies: Optional[Dict[str, Any]] = None,
1740
1744
  debug_mode: Optional[bool] = None,
1741
1745
  **kwargs: Any,
1742
1746
  ) -> RunOutput:
@@ -1771,16 +1775,21 @@ class Agent:
1771
1775
  # 2. Update metadata and session state
1772
1776
  self._update_metadata(session=agent_session)
1773
1777
  # Initialize session state
1774
- session_state = self._initialize_session_state(
1775
- session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_response.run_id
1778
+ run_context.session_state = self._initialize_session_state(
1779
+ session_state=run_context.session_state or {},
1780
+ user_id=user_id,
1781
+ session_id=session_id,
1782
+ run_id=run_response.run_id,
1776
1783
  )
1777
1784
  # Update session state from DB
1778
- if session_state is not None:
1779
- session_state = self._load_session_state(session=agent_session, session_state=session_state)
1785
+ if run_context.session_state is not None:
1786
+ run_context.session_state = self._load_session_state(
1787
+ session=agent_session, session_state=run_context.session_state
1788
+ )
1780
1789
 
1781
1790
  # 3. Resolve dependencies
1782
- if dependencies is not None:
1783
- await self._aresolve_run_dependencies(dependencies=dependencies)
1791
+ if run_context.dependencies is not None:
1792
+ await self._aresolve_run_dependencies(run_context=run_context)
1784
1793
 
1785
1794
  # 4. Execute pre-hooks
1786
1795
  run_input = cast(RunInput, run_response.input)
@@ -1790,10 +1799,8 @@ class Agent:
1790
1799
  pre_hook_iterator = self._aexecute_pre_hooks(
1791
1800
  hooks=self.pre_hooks, # type: ignore
1792
1801
  run_response=run_response,
1802
+ run_context=run_context,
1793
1803
  run_input=run_input,
1794
- session_state=session_state,
1795
- dependencies=dependencies,
1796
- metadata=metadata,
1797
1804
  session=agent_session,
1798
1805
  user_id=user_id,
1799
1806
  debug_mode=debug_mode,
@@ -1807,36 +1814,33 @@ class Agent:
1807
1814
  self.model = cast(Model, self.model)
1808
1815
  processed_tools = await self.aget_tools(
1809
1816
  run_response=run_response,
1817
+ run_context=run_context,
1810
1818
  session=agent_session,
1811
1819
  user_id=user_id,
1812
- knowledge_filters=knowledge_filters,
1813
1820
  )
1821
+
1814
1822
  _tools = self._determine_tools_for_model(
1815
1823
  model=self.model,
1816
1824
  processed_tools=processed_tools,
1817
1825
  run_response=run_response,
1826
+ run_context=run_context,
1818
1827
  session=agent_session,
1819
- session_state=session_state,
1820
- dependencies=dependencies,
1821
1828
  )
1822
1829
 
1823
1830
  # 6. Prepare run messages
1824
1831
  run_messages: RunMessages = await self._aget_run_messages(
1825
1832
  run_response=run_response,
1833
+ run_context=run_context,
1826
1834
  input=run_input.input_content,
1827
1835
  session=agent_session,
1828
- session_state=session_state,
1829
1836
  user_id=user_id,
1830
1837
  audio=run_input.audios,
1831
1838
  images=run_input.images,
1832
1839
  videos=run_input.videos,
1833
1840
  files=run_input.files,
1834
- knowledge_filters=knowledge_filters,
1835
1841
  add_history_to_context=add_history_to_context,
1836
- dependencies=dependencies,
1837
1842
  add_dependencies_to_context=add_dependencies_to_context,
1838
1843
  add_session_state_to_context=add_session_state_to_context,
1839
- metadata=metadata,
1840
1844
  tools=_tools,
1841
1845
  **kwargs,
1842
1846
  )
@@ -1877,6 +1881,7 @@ class Agent:
1877
1881
  tool_call_limit=self.tool_call_limit,
1878
1882
  response_format=response_format,
1879
1883
  send_media_to_model=self.send_media_to_model,
1884
+ run_response=run_response,
1880
1885
  )
1881
1886
 
1882
1887
  # Check for cancellation after model call
@@ -1909,16 +1914,14 @@ class Agent:
1909
1914
 
1910
1915
  # 12. Store media if enabled
1911
1916
  if self.store_media:
1912
- self._store_media(run_response, model_response)
1917
+ store_media_util(run_response, model_response)
1913
1918
 
1914
1919
  # 13. Execute post-hooks (after output is generated but before response is returned)
1915
1920
  if self.post_hooks is not None:
1916
1921
  async for _ in self._aexecute_post_hooks(
1917
1922
  hooks=self.post_hooks, # type: ignore
1918
1923
  run_output=run_response,
1919
- session_state=session_state,
1920
- dependencies=dependencies,
1921
- metadata=metadata,
1924
+ run_context=run_context,
1922
1925
  session=agent_session,
1923
1926
  user_id=user_id,
1924
1927
  debug_mode=debug_mode,
@@ -1944,7 +1947,12 @@ class Agent:
1944
1947
  run_response.status = RunStatus.completed
1945
1948
 
1946
1949
  # 16. Cleanup and store the run response and session
1947
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
1950
+ await self._acleanup_and_store(
1951
+ run_response=run_response,
1952
+ session=agent_session,
1953
+ run_context=run_context,
1954
+ user_id=user_id,
1955
+ )
1948
1956
 
1949
1957
  # Log Agent Telemetry
1950
1958
  await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
@@ -1960,7 +1968,12 @@ class Agent:
1960
1968
  run_response.status = RunStatus.cancelled
1961
1969
 
1962
1970
  # Cleanup and store the run response and session
1963
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
1971
+ await self._acleanup_and_store(
1972
+ run_response=run_response,
1973
+ session=agent_session,
1974
+ run_context=run_context,
1975
+ user_id=user_id,
1976
+ )
1964
1977
 
1965
1978
  return run_response
1966
1979
 
@@ -1988,15 +2001,12 @@ class Agent:
1988
2001
  async def _arun_stream(
1989
2002
  self,
1990
2003
  run_response: RunOutput,
2004
+ run_context: RunContext,
1991
2005
  session_id: str,
1992
- session_state: Optional[Dict[str, Any]] = None,
1993
2006
  user_id: Optional[str] = None,
1994
- knowledge_filters: Optional[Dict[str, Any]] = None,
1995
- dependencies: Optional[Dict[str, Any]] = None,
1996
2007
  add_history_to_context: Optional[bool] = None,
1997
2008
  add_dependencies_to_context: Optional[bool] = None,
1998
2009
  add_session_state_to_context: Optional[bool] = None,
1999
- metadata: Optional[Dict[str, Any]] = None,
2000
2010
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2001
2011
  stream_events: bool = False,
2002
2012
  yield_run_response: Optional[bool] = None,
@@ -2037,16 +2047,21 @@ class Agent:
2037
2047
  # 2. Update metadata and session state
2038
2048
  self._update_metadata(session=agent_session)
2039
2049
  # Initialize session state
2040
- session_state = self._initialize_session_state(
2041
- session_state=session_state or {}, user_id=user_id, session_id=session_id, run_id=run_response.run_id
2050
+ run_context.session_state = self._initialize_session_state(
2051
+ session_state=run_context.session_state or {},
2052
+ user_id=user_id,
2053
+ session_id=session_id,
2054
+ run_id=run_response.run_id,
2042
2055
  )
2043
2056
  # Update session state from DB
2044
- if session_state is not None:
2045
- session_state = self._load_session_state(session=agent_session, session_state=session_state)
2057
+ if run_context.session_state is not None:
2058
+ run_context.session_state = self._load_session_state(
2059
+ session=agent_session, session_state=run_context.session_state
2060
+ )
2046
2061
 
2047
2062
  # 3. Resolve dependencies
2048
- if dependencies is not None:
2049
- await self._aresolve_run_dependencies(dependencies=dependencies)
2063
+ if run_context.dependencies is not None:
2064
+ await self._aresolve_run_dependencies(run_context=run_context)
2050
2065
 
2051
2066
  # 4. Execute pre-hooks
2052
2067
  run_input = cast(RunInput, run_response.input)
@@ -2056,11 +2071,9 @@ class Agent:
2056
2071
  pre_hook_iterator = self._aexecute_pre_hooks(
2057
2072
  hooks=self.pre_hooks, # type: ignore
2058
2073
  run_response=run_response,
2074
+ run_context=run_context,
2059
2075
  run_input=run_input,
2060
2076
  session=agent_session,
2061
- session_state=session_state,
2062
- dependencies=dependencies,
2063
- metadata=metadata,
2064
2077
  user_id=user_id,
2065
2078
  debug_mode=debug_mode,
2066
2079
  **kwargs,
@@ -2072,36 +2085,33 @@ class Agent:
2072
2085
  self.model = cast(Model, self.model)
2073
2086
  processed_tools = await self.aget_tools(
2074
2087
  run_response=run_response,
2088
+ run_context=run_context,
2075
2089
  session=agent_session,
2076
2090
  user_id=user_id,
2077
- knowledge_filters=knowledge_filters,
2078
2091
  )
2092
+
2079
2093
  _tools = self._determine_tools_for_model(
2080
2094
  model=self.model,
2081
2095
  processed_tools=processed_tools,
2082
2096
  run_response=run_response,
2097
+ run_context=run_context,
2083
2098
  session=agent_session,
2084
- session_state=session_state,
2085
- dependencies=dependencies,
2086
2099
  )
2087
2100
 
2088
2101
  # 6. Prepare run messages
2089
2102
  run_messages: RunMessages = await self._aget_run_messages(
2090
2103
  run_response=run_response,
2104
+ run_context=run_context,
2091
2105
  input=run_input.input_content,
2092
2106
  session=agent_session,
2093
- session_state=session_state,
2094
2107
  user_id=user_id,
2095
2108
  audio=run_input.audios,
2096
2109
  images=run_input.images,
2097
2110
  videos=run_input.videos,
2098
2111
  files=run_input.files,
2099
- knowledge_filters=knowledge_filters,
2100
2112
  add_history_to_context=add_history_to_context,
2101
- dependencies=dependencies,
2102
2113
  add_dependencies_to_context=add_dependencies_to_context,
2103
2114
  add_session_state_to_context=add_session_state_to_context,
2104
- metadata=metadata,
2105
2115
  tools=_tools,
2106
2116
  **kwargs,
2107
2117
  )
@@ -2148,6 +2158,7 @@ class Agent:
2148
2158
  tools=_tools,
2149
2159
  response_format=response_format,
2150
2160
  stream_events=stream_events,
2161
+ session_state=run_context.session_state,
2151
2162
  ):
2152
2163
  raise_if_cancelled(run_response.run_id) # type: ignore
2153
2164
  yield event
@@ -2164,6 +2175,7 @@ class Agent:
2164
2175
  tools=_tools,
2165
2176
  response_format=response_format,
2166
2177
  stream_events=stream_events,
2178
+ session_state=run_context.session_state,
2167
2179
  ):
2168
2180
  raise_if_cancelled(run_response.run_id) # type: ignore
2169
2181
  if isinstance(event, RunContentEvent):
@@ -2223,9 +2235,7 @@ class Agent:
2223
2235
  async for event in self._aexecute_post_hooks(
2224
2236
  hooks=self.post_hooks, # type: ignore
2225
2237
  run_output=run_response,
2226
- session_state=session_state,
2227
- dependencies=dependencies,
2228
- metadata=metadata,
2238
+ run_context=run_context,
2229
2239
  session=agent_session,
2230
2240
  user_id=user_id,
2231
2241
  debug_mode=debug_mode,
@@ -2270,6 +2280,11 @@ class Agent:
2270
2280
  store_events=self.store_events,
2271
2281
  )
2272
2282
 
2283
+ # Update run_response.session_state before creating RunCompletedEvent
2284
+ # This ensures the event has the final state after all tool modifications
2285
+ if agent_session.session_data is not None and "session_state" in agent_session.session_data:
2286
+ run_response.session_state = agent_session.session_data["session_state"]
2287
+
2273
2288
  # Create the run completed event
2274
2289
  completed_event = handle_event(
2275
2290
  create_run_completed_event(from_run_response=run_response),
@@ -2282,7 +2297,12 @@ class Agent:
2282
2297
  run_response.status = RunStatus.completed
2283
2298
 
2284
2299
  # 13. Cleanup and store the run response and session
2285
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
2300
+ await self._acleanup_and_store(
2301
+ run_response=run_response,
2302
+ session=agent_session,
2303
+ run_context=run_context,
2304
+ user_id=user_id,
2305
+ )
2286
2306
 
2287
2307
  if stream_events:
2288
2308
  yield completed_event # type: ignore
@@ -2313,7 +2333,12 @@ class Agent:
2313
2333
  )
2314
2334
 
2315
2335
  # Cleanup and store the run response and session
2316
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
2336
+ await self._acleanup_and_store(
2337
+ run_response=run_response,
2338
+ session=agent_session,
2339
+ run_context=run_context,
2340
+ user_id=user_id,
2341
+ )
2317
2342
  finally:
2318
2343
  # Always disconnect MCP tools
2319
2344
  await self._disconnect_mcp_tools()
@@ -2426,12 +2451,12 @@ class Agent:
2426
2451
  # 2. Validate input against input_schema if provided
2427
2452
  validated_input = self._validate_input(input)
2428
2453
 
2429
- # Normalise hook & guardails
2454
+ # Normalise hooks & guardails
2430
2455
  if not self._hooks_normalised:
2431
2456
  if self.pre_hooks:
2432
- self.pre_hooks = normalize_hooks(self.pre_hooks, async_mode=True)
2457
+ self.pre_hooks = normalize_hooks(self.pre_hooks, async_mode=True) # type: ignore
2433
2458
  if self.post_hooks:
2434
- self.post_hooks = normalize_hooks(self.post_hooks, async_mode=True)
2459
+ self.post_hooks = normalize_hooks(self.post_hooks, async_mode=True) # type: ignore
2435
2460
  self._hooks_normalised = True
2436
2461
 
2437
2462
  # Initialize session
@@ -2440,12 +2465,12 @@ class Agent:
2440
2465
  # Initialize the Agent
2441
2466
  self.initialize_agent(debug_mode=debug_mode)
2442
2467
 
2443
- image_artifacts, video_artifacts, audio_artifacts, file_artifacts = self._validate_media_object_id(
2468
+ image_artifacts, video_artifacts, audio_artifacts, file_artifacts = validate_media_object_id(
2444
2469
  images=images, videos=videos, audios=audio, files=files
2445
2470
  )
2446
2471
 
2447
2472
  # Resolve variables
2448
- run_dependencies = dependencies if dependencies is not None else self.dependencies
2473
+ dependencies = dependencies if dependencies is not None else self.dependencies
2449
2474
  add_dependencies = (
2450
2475
  add_dependencies_to_context if add_dependencies_to_context is not None else self.add_dependencies_to_context
2451
2476
  )
@@ -2487,9 +2512,9 @@ class Agent:
2487
2512
  self.model = cast(Model, self.model)
2488
2513
 
2489
2514
  # Get knowledge filters
2490
- effective_filters = knowledge_filters
2515
+ knowledge_filters = knowledge_filters
2491
2516
  if self.knowledge_filters or knowledge_filters:
2492
- effective_filters = self._get_effective_filters(knowledge_filters)
2517
+ knowledge_filters = self._get_effective_filters(knowledge_filters)
2493
2518
 
2494
2519
  # Merge agent metadata with run metadata
2495
2520
  if self.metadata is not None:
@@ -2498,6 +2523,17 @@ class Agent:
2498
2523
  else:
2499
2524
  merge_dictionaries(metadata, self.metadata)
2500
2525
 
2526
+ # Initialize run context
2527
+ run_context = RunContext(
2528
+ run_id=run_id,
2529
+ session_id=session_id,
2530
+ user_id=user_id,
2531
+ session_state=session_state,
2532
+ dependencies=dependencies,
2533
+ knowledge_filters=knowledge_filters,
2534
+ metadata=metadata,
2535
+ )
2536
+
2501
2537
  # If no retries are set, use the agent's default retries
2502
2538
  retries = retries if retries is not None else self.retries
2503
2539
 
@@ -2508,7 +2544,8 @@ class Agent:
2508
2544
  agent_id=self.id,
2509
2545
  user_id=user_id,
2510
2546
  agent_name=self.name,
2511
- metadata=metadata,
2547
+ metadata=run_context.metadata,
2548
+ session_state=run_context.session_state,
2512
2549
  input=run_input,
2513
2550
  )
2514
2551
 
@@ -2528,34 +2565,28 @@ class Agent:
2528
2565
  if stream:
2529
2566
  return self._arun_stream( # type: ignore
2530
2567
  run_response=run_response,
2568
+ run_context=run_context,
2531
2569
  user_id=user_id,
2532
2570
  response_format=response_format,
2533
2571
  stream_events=stream_events,
2534
2572
  yield_run_response=yield_run_response,
2535
- dependencies=run_dependencies,
2536
2573
  session_id=session_id,
2537
- session_state=session_state,
2538
- knowledge_filters=effective_filters,
2539
2574
  add_history_to_context=add_history,
2540
2575
  add_dependencies_to_context=add_dependencies,
2541
2576
  add_session_state_to_context=add_session_state,
2542
- metadata=metadata,
2543
2577
  debug_mode=debug_mode,
2544
2578
  **kwargs,
2545
2579
  ) # type: ignore[assignment]
2546
2580
  else:
2547
2581
  return self._arun( # type: ignore
2548
2582
  run_response=run_response,
2583
+ run_context=run_context,
2549
2584
  user_id=user_id,
2550
2585
  response_format=response_format,
2551
- dependencies=run_dependencies,
2552
2586
  session_id=session_id,
2553
- session_state=session_state,
2554
- knowledge_filters=effective_filters,
2555
2587
  add_history_to_context=add_history,
2556
2588
  add_dependencies_to_context=add_dependencies,
2557
2589
  add_session_state_to_context=add_session_state,
2558
- metadata=metadata,
2559
2590
  debug_mode=debug_mode,
2560
2591
  **kwargs,
2561
2592
  )
@@ -2646,7 +2677,7 @@ class Agent:
2646
2677
  self,
2647
2678
  run_response: Optional[RunOutput] = None,
2648
2679
  *,
2649
- run_id: Optional[str] = None,
2680
+ run_id: Optional[str] = None, # type: ignore
2650
2681
  updated_tools: Optional[List[ToolExecution]] = None,
2651
2682
  stream: Optional[bool] = None,
2652
2683
  stream_events: Optional[bool] = False,
@@ -2687,6 +2718,7 @@ class Agent:
2687
2718
  raise Exception("continue_run() is not supported with an async DB. Please use acontinue_arun() instead.")
2688
2719
 
2689
2720
  session_id = run_response.session_id if run_response else session_id
2721
+ run_id: str = run_response.run_id if run_response else run_id # type: ignore
2690
2722
 
2691
2723
  session_id, user_id = self._initialize_session(
2692
2724
  session_id=session_id,
@@ -2706,24 +2738,32 @@ class Agent:
2706
2738
  # Update session state from DB
2707
2739
  session_state = self._load_session_state(session=agent_session, session_state=session_state)
2708
2740
 
2709
- run_dependencies = dependencies if dependencies is not None else self.dependencies
2741
+ dependencies = dependencies if dependencies is not None else self.dependencies
2710
2742
 
2711
- # Resolve dependencies
2712
- if run_dependencies is not None:
2713
- self._resolve_run_dependencies(dependencies=run_dependencies)
2743
+ # Initialize run context
2744
+ run_context = RunContext(
2745
+ run_id=run_id, # type: ignore
2746
+ session_id=session_id,
2747
+ user_id=user_id,
2748
+ session_state=session_state,
2749
+ dependencies=dependencies,
2750
+ )
2714
2751
 
2715
- effective_filters = knowledge_filters
2752
+ # Resolve dependencies
2753
+ if run_context.dependencies is not None:
2754
+ self._resolve_run_dependencies(run_context=run_context)
2716
2755
 
2717
2756
  # When filters are passed manually
2718
- if self.knowledge_filters or knowledge_filters:
2719
- effective_filters = self._get_effective_filters(knowledge_filters)
2757
+ if self.knowledge_filters or run_context.knowledge_filters or knowledge_filters:
2758
+ run_context.knowledge_filters = self._get_effective_filters(knowledge_filters)
2720
2759
 
2721
2760
  # Merge agent metadata with run metadata
2761
+ run_context.metadata = metadata
2722
2762
  if self.metadata is not None:
2723
- if metadata is None:
2724
- metadata = self.metadata
2763
+ if run_context.metadata is None:
2764
+ run_context.metadata = self.metadata
2725
2765
  else:
2726
- merge_dictionaries(metadata, self.metadata)
2766
+ merge_dictionaries(run_context.metadata, self.metadata)
2727
2767
 
2728
2768
  # If no retries are set, use the agent's default retries
2729
2769
  retries = retries if retries is not None else self.retries
@@ -2774,17 +2814,17 @@ class Agent:
2774
2814
 
2775
2815
  processed_tools = self.get_tools(
2776
2816
  run_response=run_response,
2817
+ run_context=run_context,
2777
2818
  session=agent_session,
2778
2819
  user_id=user_id,
2779
- knowledge_filters=effective_filters,
2780
2820
  )
2821
+
2781
2822
  _tools = self._determine_tools_for_model(
2782
2823
  model=self.model,
2783
2824
  processed_tools=processed_tools,
2784
2825
  run_response=run_response,
2826
+ run_context=run_context,
2785
2827
  session=agent_session,
2786
- session_state=session_state,
2787
- dependencies=run_dependencies,
2788
2828
  )
2789
2829
 
2790
2830
  last_exception = None
@@ -2807,12 +2847,10 @@ class Agent:
2807
2847
  response_iterator = self._continue_run_stream(
2808
2848
  run_response=run_response,
2809
2849
  run_messages=run_messages,
2850
+ run_context=run_context,
2810
2851
  tools=_tools,
2811
2852
  user_id=user_id,
2812
2853
  session=agent_session,
2813
- session_state=session_state,
2814
- dependencies=run_dependencies,
2815
- metadata=metadata,
2816
2854
  response_format=response_format,
2817
2855
  stream_events=stream_events,
2818
2856
  debug_mode=debug_mode,
@@ -2823,12 +2861,10 @@ class Agent:
2823
2861
  response = self._continue_run(
2824
2862
  run_response=run_response,
2825
2863
  run_messages=run_messages,
2864
+ run_context=run_context,
2826
2865
  tools=_tools,
2827
2866
  user_id=user_id,
2828
2867
  session=agent_session,
2829
- session_state=session_state,
2830
- dependencies=run_dependencies,
2831
- metadata=metadata,
2832
2868
  response_format=response_format,
2833
2869
  debug_mode=debug_mode,
2834
2870
  **kwargs,
@@ -2875,11 +2911,9 @@ class Agent:
2875
2911
  self,
2876
2912
  run_response: RunOutput,
2877
2913
  run_messages: RunMessages,
2914
+ run_context: RunContext,
2878
2915
  session: AgentSession,
2879
2916
  tools: List[Union[Function, dict]],
2880
- session_state: Optional[Dict[str, Any]] = None,
2881
- dependencies: Optional[Dict[str, Any]] = None,
2882
- metadata: Optional[Dict[str, Any]] = None,
2883
2917
  user_id: Optional[str] = None,
2884
2918
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
2885
2919
  debug_mode: Optional[bool] = None,
@@ -2936,18 +2970,16 @@ class Agent:
2936
2970
 
2937
2971
  # 5. Store media if enabled
2938
2972
  if self.store_media:
2939
- self._store_media(run_response, model_response)
2973
+ store_media_util(run_response, model_response)
2940
2974
 
2941
2975
  # 6. Execute post-hooks
2942
2976
  if self.post_hooks is not None:
2943
2977
  post_hook_iterator = self._execute_post_hooks(
2944
2978
  hooks=self.post_hooks, # type: ignore
2945
2979
  run_output=run_response,
2980
+ run_context=run_context,
2946
2981
  session=session,
2947
2982
  user_id=user_id,
2948
- session_state=session_state,
2949
- dependencies=dependencies,
2950
- metadata=metadata,
2951
2983
  debug_mode=debug_mode,
2952
2984
  **kwargs,
2953
2985
  )
@@ -2969,7 +3001,9 @@ class Agent:
2969
3001
  run_response.status = RunStatus.completed
2970
3002
 
2971
3003
  # 8. Cleanup and store the run response and session
2972
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
3004
+ self._cleanup_and_store(
3005
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
3006
+ )
2973
3007
 
2974
3008
  # Log Agent Telemetry
2975
3009
  self._log_agent_telemetry(session_id=session.session_id, run_id=run_response.run_id)
@@ -2982,7 +3016,9 @@ class Agent:
2982
3016
  run_response.content = str(e)
2983
3017
 
2984
3018
  # Cleanup and store the run response and session
2985
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
3019
+ self._cleanup_and_store(
3020
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
3021
+ )
2986
3022
 
2987
3023
  return run_response
2988
3024
  finally:
@@ -2993,14 +3029,12 @@ class Agent:
2993
3029
  self,
2994
3030
  run_response: RunOutput,
2995
3031
  run_messages: RunMessages,
3032
+ run_context: RunContext,
2996
3033
  session: AgentSession,
2997
3034
  tools: List[Union[Function, dict]],
2998
- session_state: Optional[Dict[str, Any]] = None,
2999
- metadata: Optional[Dict[str, Any]] = None,
3000
3035
  user_id: Optional[str] = None,
3001
3036
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3002
3037
  stream_events: bool = False,
3003
- dependencies: Optional[Dict[str, Any]] = None,
3004
3038
  debug_mode: Optional[bool] = None,
3005
3039
  **kwargs,
3006
3040
  ) -> Iterator[RunOutputEvent]:
@@ -3016,8 +3050,8 @@ class Agent:
3016
3050
  """
3017
3051
 
3018
3052
  # 1. Resolve dependencies
3019
- if dependencies is not None:
3020
- self._resolve_run_dependencies(dependencies=dependencies)
3053
+ if run_context.dependencies is not None:
3054
+ self._resolve_run_dependencies(run_context=run_context)
3021
3055
 
3022
3056
  # Start the Run by yielding a RunContinued event
3023
3057
  if stream_events:
@@ -3042,6 +3076,7 @@ class Agent:
3042
3076
  tools=tools,
3043
3077
  response_format=response_format,
3044
3078
  stream_events=stream_events,
3079
+ session_state=run_context.session_state,
3045
3080
  ):
3046
3081
  yield event
3047
3082
 
@@ -3072,9 +3107,7 @@ class Agent:
3072
3107
  hooks=self.post_hooks, # type: ignore
3073
3108
  run_output=run_response,
3074
3109
  session=session,
3075
- session_state=session_state,
3076
- dependencies=dependencies,
3077
- metadata=metadata,
3110
+ run_context=run_context,
3078
3111
  user_id=user_id,
3079
3112
  debug_mode=debug_mode,
3080
3113
  **kwargs,
@@ -3110,6 +3143,11 @@ class Agent:
3110
3143
  store_events=self.store_events,
3111
3144
  )
3112
3145
 
3146
+ # Update run_response.session_state before creating RunCompletedEvent
3147
+ # This ensures the event has the final state after all tool modifications
3148
+ if session.session_data is not None and "session_state" in session.session_data:
3149
+ run_response.session_state = session.session_data["session_state"]
3150
+
3113
3151
  # Create the run completed event
3114
3152
  completed_event = handle_event(
3115
3153
  create_run_completed_event(run_response),
@@ -3122,7 +3160,9 @@ class Agent:
3122
3160
  run_response.status = RunStatus.completed
3123
3161
 
3124
3162
  # 5. Cleanup and store the run response and session
3125
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
3163
+ self._cleanup_and_store(
3164
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
3165
+ )
3126
3166
 
3127
3167
  if stream_events:
3128
3168
  yield completed_event # type: ignore
@@ -3147,7 +3187,9 @@ class Agent:
3147
3187
  )
3148
3188
 
3149
3189
  # Cleanup and store the run response and session
3150
- self._cleanup_and_store(run_response=run_response, session=session, user_id=user_id)
3190
+ self._cleanup_and_store(
3191
+ run_response=run_response, session=session, run_context=run_context, user_id=user_id
3192
+ )
3151
3193
  finally:
3152
3194
  # Always clean up the run tracking
3153
3195
  cleanup_run(run_response.run_id) # type: ignore
@@ -3194,7 +3236,7 @@ class Agent:
3194
3236
  self,
3195
3237
  run_response: Optional[RunOutput] = None,
3196
3238
  *,
3197
- run_id: Optional[str] = None,
3239
+ run_id: Optional[str] = None, # type: ignore
3198
3240
  updated_tools: Optional[List[ToolExecution]] = None,
3199
3241
  stream: Optional[bool] = None,
3200
3242
  stream_events: Optional[bool] = None,
@@ -3237,11 +3279,12 @@ class Agent:
3237
3279
  session_id=session_id,
3238
3280
  user_id=user_id,
3239
3281
  )
3282
+ run_id: str = run_id or run_response.run_id if run_response else run_id # type: ignore
3240
3283
 
3241
3284
  # Initialize the Agent
3242
3285
  self.initialize_agent(debug_mode=debug_mode)
3243
3286
 
3244
- run_dependencies = dependencies if dependencies is not None else self.dependencies
3287
+ dependencies = dependencies if dependencies is not None else self.dependencies
3245
3288
 
3246
3289
  # If no retries are set, use the agent's default retries
3247
3290
  retries = retries if retries is not None else self.retries
@@ -3268,9 +3311,9 @@ class Agent:
3268
3311
  self.stream_events = self.stream_events or stream_events
3269
3312
 
3270
3313
  # Get knowledge filters
3271
- effective_filters = knowledge_filters
3314
+ knowledge_filters = knowledge_filters
3272
3315
  if self.knowledge_filters or knowledge_filters:
3273
- effective_filters = self._get_effective_filters(knowledge_filters)
3316
+ knowledge_filters = self._get_effective_filters(knowledge_filters)
3274
3317
 
3275
3318
  # Merge agent metadata with run metadata
3276
3319
  if self.metadata is not None:
@@ -3283,6 +3326,17 @@ class Agent:
3283
3326
  response_format = self._get_response_format()
3284
3327
  self.model = cast(Model, self.model)
3285
3328
 
3329
+ # Initialize run context
3330
+ run_context = RunContext(
3331
+ run_id=run_id, # type: ignore
3332
+ session_id=session_id,
3333
+ user_id=user_id,
3334
+ session_state={},
3335
+ dependencies=dependencies,
3336
+ knowledge_filters=knowledge_filters,
3337
+ metadata=metadata,
3338
+ )
3339
+
3286
3340
  last_exception = None
3287
3341
  num_attempts = retries + 1
3288
3342
  for attempt in range(num_attempts):
@@ -3290,15 +3344,13 @@ class Agent:
3290
3344
  if stream:
3291
3345
  return self._acontinue_run_stream(
3292
3346
  run_response=run_response,
3347
+ run_context=run_context,
3293
3348
  updated_tools=updated_tools,
3294
- knowledge_filters=effective_filters,
3295
3349
  run_id=run_id,
3296
3350
  user_id=user_id,
3297
3351
  session_id=session_id,
3298
3352
  response_format=response_format,
3299
- dependencies=run_dependencies,
3300
3353
  stream_events=stream_events,
3301
- metadata=metadata,
3302
3354
  yield_run_response=yield_run_response,
3303
3355
  debug_mode=debug_mode,
3304
3356
  **kwargs,
@@ -3307,13 +3359,11 @@ class Agent:
3307
3359
  return self._acontinue_run( # type: ignore
3308
3360
  session_id=session_id,
3309
3361
  run_response=run_response,
3362
+ run_context=run_context,
3310
3363
  updated_tools=updated_tools,
3311
- knowledge_filters=effective_filters,
3312
3364
  run_id=run_id,
3313
3365
  user_id=user_id,
3314
3366
  response_format=response_format,
3315
- dependencies=run_dependencies,
3316
- metadata=metadata,
3317
3367
  debug_mode=debug_mode,
3318
3368
  **kwargs,
3319
3369
  )
@@ -3357,14 +3407,12 @@ class Agent:
3357
3407
  async def _acontinue_run(
3358
3408
  self,
3359
3409
  session_id: str,
3410
+ run_context: RunContext,
3360
3411
  run_response: Optional[RunOutput] = None,
3361
3412
  updated_tools: Optional[List[ToolExecution]] = None,
3362
- knowledge_filters: Optional[Dict[str, Any]] = None,
3363
3413
  run_id: Optional[str] = None,
3364
3414
  user_id: Optional[str] = None,
3365
3415
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3366
- dependencies: Optional[Dict[str, Any]] = None,
3367
- metadata: Optional[Dict[str, Any]] = None,
3368
3416
  debug_mode: Optional[bool] = None,
3369
3417
  **kwargs,
3370
3418
  ) -> RunOutput:
@@ -3392,18 +3440,20 @@ class Agent:
3392
3440
  agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
3393
3441
 
3394
3442
  # 2. Resolve dependencies
3395
- if dependencies is not None:
3396
- await self._aresolve_run_dependencies(dependencies=dependencies)
3443
+ if run_context.dependencies is not None:
3444
+ await self._aresolve_run_dependencies(run_context=run_context)
3397
3445
 
3398
3446
  # 3. Update metadata and session state
3399
3447
  self._update_metadata(session=agent_session)
3400
3448
  # Initialize session state
3401
- session_state = self._initialize_session_state(
3449
+ run_context.session_state = self._initialize_session_state(
3402
3450
  session_state={}, user_id=user_id, session_id=session_id, run_id=run_id
3403
3451
  )
3404
3452
  # Update session state from DB
3405
- if session_state is not None:
3406
- session_state = self._load_session_state(session=agent_session, session_state=session_state)
3453
+ if run_context.session_state is not None:
3454
+ run_context.session_state = self._load_session_state(
3455
+ session=agent_session, session_state=run_context.session_state
3456
+ )
3407
3457
 
3408
3458
  # 4. Prepare run response
3409
3459
  if run_response is not None:
@@ -3430,17 +3480,17 @@ class Agent:
3430
3480
  self.model = cast(Model, self.model)
3431
3481
  processed_tools = await self.aget_tools(
3432
3482
  run_response=run_response,
3483
+ run_context=run_context,
3433
3484
  session=agent_session,
3434
3485
  user_id=user_id,
3435
- knowledge_filters=knowledge_filters,
3436
3486
  )
3487
+
3437
3488
  _tools = self._determine_tools_for_model(
3438
3489
  model=self.model,
3439
3490
  processed_tools=processed_tools,
3440
3491
  run_response=run_response,
3492
+ run_context=run_context,
3441
3493
  session=agent_session,
3442
- session_state=session_state,
3443
- dependencies=dependencies,
3444
3494
  )
3445
3495
 
3446
3496
  # 6. Prepare run messages
@@ -3490,7 +3540,7 @@ class Agent:
3490
3540
 
3491
3541
  # 11. Store media if enabled
3492
3542
  if self.store_media:
3493
- self._store_media(run_response, model_response)
3543
+ store_media_util(run_response, model_response)
3494
3544
 
3495
3545
  raise_if_cancelled(run_response.run_id) # type: ignore
3496
3546
 
@@ -3499,12 +3549,10 @@ class Agent:
3499
3549
  async for _ in self._aexecute_post_hooks(
3500
3550
  hooks=self.post_hooks, # type: ignore
3501
3551
  run_output=run_response,
3552
+ run_context=run_context,
3502
3553
  session=agent_session,
3503
3554
  user_id=user_id,
3504
3555
  debug_mode=debug_mode,
3505
- session_state=session_state,
3506
- dependencies=dependencies,
3507
- metadata=metadata,
3508
3556
  **kwargs,
3509
3557
  ):
3510
3558
  pass
@@ -3526,7 +3574,12 @@ class Agent:
3526
3574
  run_response.status = RunStatus.completed
3527
3575
 
3528
3576
  # 14. Cleanup and store the run response and session
3529
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
3577
+ await self._acleanup_and_store(
3578
+ run_response=run_response,
3579
+ session=agent_session,
3580
+ run_context=run_context,
3581
+ user_id=user_id,
3582
+ )
3530
3583
 
3531
3584
  # Log Agent Telemetry
3532
3585
  await self._alog_agent_telemetry(session_id=agent_session.session_id, run_id=run_response.run_id)
@@ -3542,7 +3595,12 @@ class Agent:
3542
3595
  run_response.status = RunStatus.cancelled
3543
3596
 
3544
3597
  # Cleanup and store the run response and session
3545
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
3598
+ await self._acleanup_and_store(
3599
+ run_response=run_response,
3600
+ session=agent_session,
3601
+ run_context=run_context,
3602
+ user_id=user_id,
3603
+ )
3546
3604
 
3547
3605
  return run_response
3548
3606
  finally:
@@ -3555,16 +3613,14 @@ class Agent:
3555
3613
  async def _acontinue_run_stream(
3556
3614
  self,
3557
3615
  session_id: str,
3616
+ run_context: RunContext,
3558
3617
  run_response: Optional[RunOutput] = None,
3559
3618
  updated_tools: Optional[List[ToolExecution]] = None,
3560
- knowledge_filters: Optional[Dict[str, Any]] = None,
3561
3619
  run_id: Optional[str] = None,
3562
3620
  user_id: Optional[str] = None,
3563
3621
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
3564
3622
  stream_events: bool = False,
3565
3623
  yield_run_response: Optional[bool] = None,
3566
- dependencies: Optional[Dict[str, Any]] = None,
3567
- metadata: Optional[Dict[str, Any]] = None,
3568
3624
  debug_mode: Optional[bool] = None,
3569
3625
  **kwargs,
3570
3626
  ) -> AsyncIterator[Union[RunOutputEvent, RunOutput]]:
@@ -3586,8 +3642,8 @@ class Agent:
3586
3642
  log_debug(f"Agent Run Continue: {run_response.run_id if run_response else run_id}", center=True) # type: ignore
3587
3643
 
3588
3644
  # 1. Resolve dependencies
3589
- if dependencies is not None:
3590
- await self._aresolve_run_dependencies(dependencies=dependencies)
3645
+ if run_context.dependencies is not None:
3646
+ await self._aresolve_run_dependencies(run_context=run_context)
3591
3647
 
3592
3648
  # 2. Read existing session from db
3593
3649
  agent_session = await self._aread_or_create_session(session_id=session_id, user_id=user_id)
@@ -3595,12 +3651,14 @@ class Agent:
3595
3651
  # 3. Update session state and metadata
3596
3652
  self._update_metadata(session=agent_session)
3597
3653
  # Initialize session state
3598
- session_state = self._initialize_session_state(
3654
+ run_context.session_state = self._initialize_session_state(
3599
3655
  session_state={}, user_id=user_id, session_id=session_id, run_id=run_id
3600
3656
  )
3601
3657
  # Update session state from DB
3602
- if session_state is not None:
3603
- session_state = self._load_session_state(session=agent_session, session_state=session_state)
3658
+ if run_context.session_state is not None:
3659
+ run_context.session_state = self._load_session_state(
3660
+ session=agent_session, session_state=run_context.session_state
3661
+ )
3604
3662
 
3605
3663
  # 4. Prepare run response
3606
3664
  if run_response is not None:
@@ -3627,17 +3685,17 @@ class Agent:
3627
3685
  self.model = cast(Model, self.model)
3628
3686
  processed_tools = await self.aget_tools(
3629
3687
  run_response=run_response,
3688
+ run_context=run_context,
3630
3689
  session=agent_session,
3631
3690
  user_id=user_id,
3632
- knowledge_filters=knowledge_filters,
3633
3691
  )
3692
+
3634
3693
  _tools = self._determine_tools_for_model(
3635
3694
  model=self.model,
3636
3695
  processed_tools=processed_tools,
3637
3696
  run_response=run_response,
3697
+ run_context=run_context,
3638
3698
  session=agent_session,
3639
- session_state=session_state,
3640
- dependencies=dependencies,
3641
3699
  )
3642
3700
 
3643
3701
  # 6. Prepare run messages
@@ -3742,11 +3800,9 @@ class Agent:
3742
3800
  async for event in self._aexecute_post_hooks(
3743
3801
  hooks=self.post_hooks, # type: ignore
3744
3802
  run_output=run_response,
3803
+ run_context=run_context,
3745
3804
  session=agent_session,
3746
3805
  user_id=user_id,
3747
- session_state=session_state,
3748
- dependencies=dependencies,
3749
- metadata=metadata,
3750
3806
  debug_mode=debug_mode,
3751
3807
  **kwargs,
3752
3808
  ):
@@ -3780,6 +3836,11 @@ class Agent:
3780
3836
  store_events=self.store_events,
3781
3837
  )
3782
3838
 
3839
+ # Update run_response.session_state before creating RunCompletedEvent
3840
+ # This ensures the event has the final state after all tool modifications
3841
+ if agent_session.session_data is not None and "session_state" in agent_session.session_data:
3842
+ run_response.session_state = agent_session.session_data["session_state"]
3843
+
3783
3844
  # Create the run completed event
3784
3845
  completed_event = handle_event(
3785
3846
  create_run_completed_event(run_response),
@@ -3792,7 +3853,9 @@ class Agent:
3792
3853
  run_response.status = RunStatus.completed
3793
3854
 
3794
3855
  # 10. Cleanup and store the run response and session
3795
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
3856
+ await self._acleanup_and_store(
3857
+ run_response=run_response, session=agent_session, run_context=run_context, user_id=user_id
3858
+ )
3796
3859
 
3797
3860
  if stream_events:
3798
3861
  yield completed_event # type: ignore
@@ -3819,7 +3882,12 @@ class Agent:
3819
3882
  )
3820
3883
 
3821
3884
  # Cleanup and store the run response and session
3822
- await self._acleanup_and_store(run_response=run_response, session=agent_session, user_id=user_id)
3885
+ await self._acleanup_and_store(
3886
+ run_response=run_response,
3887
+ session=agent_session,
3888
+ run_context=run_context,
3889
+ user_id=user_id,
3890
+ )
3823
3891
  finally:
3824
3892
  # Always disconnect MCP tools
3825
3893
  await self._disconnect_mcp_tools()
@@ -3833,9 +3901,7 @@ class Agent:
3833
3901
  run_response: RunOutput,
3834
3902
  run_input: RunInput,
3835
3903
  session: AgentSession,
3836
- session_state: Optional[Dict[str, Any]] = None,
3837
- dependencies: Optional[Dict[str, Any]] = None,
3838
- metadata: Optional[Dict[str, Any]] = None,
3904
+ run_context: RunContext,
3839
3905
  user_id: Optional[str] = None,
3840
3906
  debug_mode: Optional[bool] = None,
3841
3907
  **kwargs: Any,
@@ -3847,11 +3913,12 @@ class Agent:
3847
3913
  # Prepare all possible arguments once
3848
3914
  all_args = {
3849
3915
  "run_input": run_input,
3916
+ "run_context": run_context,
3850
3917
  "agent": self,
3851
3918
  "session": session,
3852
- "session_state": session_state,
3853
- "dependencies": dependencies,
3854
- "metadata": metadata,
3919
+ "session_state": run_context.session_state,
3920
+ "dependencies": run_context.dependencies,
3921
+ "metadata": run_context.metadata,
3855
3922
  "user_id": user_id,
3856
3923
  "debug_mode": debug_mode or self.debug_mode,
3857
3924
  }
@@ -3902,10 +3969,8 @@ class Agent:
3902
3969
  hooks: Optional[List[Callable[..., Any]]],
3903
3970
  run_response: RunOutput,
3904
3971
  run_input: RunInput,
3972
+ run_context: RunContext,
3905
3973
  session: AgentSession,
3906
- session_state: Optional[Dict[str, Any]] = None,
3907
- dependencies: Optional[Dict[str, Any]] = None,
3908
- metadata: Optional[Dict[str, Any]] = None,
3909
3974
  user_id: Optional[str] = None,
3910
3975
  debug_mode: Optional[bool] = None,
3911
3976
  **kwargs: Any,
@@ -3919,9 +3984,10 @@ class Agent:
3919
3984
  "run_input": run_input,
3920
3985
  "agent": self,
3921
3986
  "session": session,
3922
- "session_state": session_state,
3923
- "dependencies": dependencies,
3924
- "metadata": metadata,
3987
+ "run_context": run_context,
3988
+ "session_state": run_context.session_state,
3989
+ "dependencies": run_context.dependencies,
3990
+ "metadata": run_context.metadata,
3925
3991
  "user_id": user_id,
3926
3992
  "debug_mode": debug_mode or self.debug_mode,
3927
3993
  }
@@ -3976,9 +4042,7 @@ class Agent:
3976
4042
  hooks: Optional[List[Callable[..., Any]]],
3977
4043
  run_output: RunOutput,
3978
4044
  session: AgentSession,
3979
- session_state: Optional[Dict[str, Any]] = None,
3980
- dependencies: Optional[Dict[str, Any]] = None,
3981
- metadata: Optional[Dict[str, Any]] = None,
4045
+ run_context: RunContext,
3982
4046
  user_id: Optional[str] = None,
3983
4047
  debug_mode: Optional[bool] = None,
3984
4048
  **kwargs: Any,
@@ -3992,10 +4056,11 @@ class Agent:
3992
4056
  "run_output": run_output,
3993
4057
  "agent": self,
3994
4058
  "session": session,
4059
+ "session_state": run_context.session_state,
4060
+ "dependencies": run_context.dependencies,
4061
+ "metadata": run_context.metadata,
3995
4062
  "user_id": user_id,
3996
- "session_state": session_state,
3997
- "dependencies": dependencies,
3998
- "metadata": metadata,
4063
+ "run_context": run_context,
3999
4064
  "debug_mode": debug_mode or self.debug_mode,
4000
4065
  }
4001
4066
  all_args.update(kwargs)
@@ -4038,10 +4103,8 @@ class Agent:
4038
4103
  self,
4039
4104
  hooks: Optional[List[Callable[..., Any]]],
4040
4105
  run_output: RunOutput,
4106
+ run_context: RunContext,
4041
4107
  session: AgentSession,
4042
- session_state: Optional[Dict[str, Any]] = None,
4043
- dependencies: Optional[Dict[str, Any]] = None,
4044
- metadata: Optional[Dict[str, Any]] = None,
4045
4108
  user_id: Optional[str] = None,
4046
4109
  debug_mode: Optional[bool] = None,
4047
4110
  **kwargs: Any,
@@ -4055,10 +4118,11 @@ class Agent:
4055
4118
  "run_output": run_output,
4056
4119
  "agent": self,
4057
4120
  "session": session,
4121
+ "run_context": run_context,
4122
+ "session_state": run_context.session_state,
4123
+ "dependencies": run_context.dependencies,
4124
+ "metadata": run_context.metadata,
4058
4125
  "user_id": user_id,
4059
- "session_state": session_state,
4060
- "dependencies": dependencies,
4061
- "metadata": metadata,
4062
4126
  "debug_mode": debug_mode or self.debug_mode,
4063
4127
  }
4064
4128
  all_args.update(kwargs)
@@ -4547,25 +4611,6 @@ class Agent:
4547
4611
  _t.requires_user_input = False
4548
4612
  _t.answered = True
4549
4613
 
4550
- def _store_media(self, run_response: RunOutput, model_response: ModelResponse):
4551
- """Store media from model response in run_response for persistence"""
4552
- # Handle generated media fields from ModelResponse (generated media)
4553
- if model_response.images is not None:
4554
- for image in model_response.images:
4555
- self._add_image(image, run_response) # Generated images go to run_response.images
4556
-
4557
- if model_response.videos is not None:
4558
- for video in model_response.videos:
4559
- self._add_video(video, run_response) # Generated videos go to run_response.videos
4560
-
4561
- if model_response.audios is not None:
4562
- for audio in model_response.audios:
4563
- self._add_audio(audio, run_response) # Generated audio go to run_response.audio
4564
-
4565
- if model_response.files is not None:
4566
- for file in model_response.files:
4567
- self._add_file(file, run_response) # Generated files go to run_response.files
4568
-
4569
4614
  def _update_run_response(
4570
4615
  self,
4571
4616
  model_response: ModelResponse,
@@ -4651,6 +4696,7 @@ class Agent:
4651
4696
  tools: Optional[List[Union[Function, dict]]] = None,
4652
4697
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
4653
4698
  stream_events: bool = False,
4699
+ session_state: Optional[Dict[str, Any]] = None,
4654
4700
  ) -> Iterator[RunOutputEvent]:
4655
4701
  self.model = cast(Model, self.model)
4656
4702
 
@@ -4683,6 +4729,7 @@ class Agent:
4683
4729
  reasoning_state=reasoning_state,
4684
4730
  parse_structured_output=self.should_parse_structured_output,
4685
4731
  stream_events=stream_events,
4732
+ session_state=session_state,
4686
4733
  )
4687
4734
 
4688
4735
  # Determine reasoning completed
@@ -4729,6 +4776,7 @@ class Agent:
4729
4776
  tools: Optional[List[Union[Function, dict]]] = None,
4730
4777
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
4731
4778
  stream_events: bool = False,
4779
+ session_state: Optional[Dict[str, Any]] = None,
4732
4780
  ) -> AsyncIterator[RunOutputEvent]:
4733
4781
  self.model = cast(Model, self.model)
4734
4782
 
@@ -4763,6 +4811,7 @@ class Agent:
4763
4811
  reasoning_state=reasoning_state,
4764
4812
  parse_structured_output=self.should_parse_structured_output,
4765
4813
  stream_events=stream_events,
4814
+ session_state=session_state,
4766
4815
  ):
4767
4816
  yield event
4768
4817
 
@@ -4810,9 +4859,14 @@ class Agent:
4810
4859
  reasoning_state: Optional[Dict[str, Any]] = None,
4811
4860
  parse_structured_output: bool = False,
4812
4861
  stream_events: bool = False,
4862
+ session_state: Optional[Dict[str, Any]] = None,
4813
4863
  ) -> Iterator[RunOutputEvent]:
4814
- if isinstance(model_response_event, tuple(get_args(RunOutputEvent))) or isinstance(
4815
- model_response_event, tuple(get_args(TeamRunOutputEvent))
4864
+ from agno.run.workflow import WorkflowRunOutputEvent
4865
+
4866
+ if (
4867
+ isinstance(model_response_event, tuple(get_args(RunOutputEvent)))
4868
+ or isinstance(model_response_event, tuple(get_args(TeamRunOutputEvent)))
4869
+ or isinstance(model_response_event, tuple(get_args(WorkflowRunOutputEvent)))
4816
4870
  ):
4817
4871
  if model_response_event.event == RunEvent.custom_event: # type: ignore
4818
4872
  model_response_event.agent_id = self.id # type: ignore
@@ -4981,7 +5035,9 @@ class Agent:
4981
5035
  # Store media in run_response if store_media is enabled
4982
5036
  if self.store_media:
4983
5037
  for image in model_response_event.images:
4984
- self._add_image(image, run_response)
5038
+ if run_response.images is None:
5039
+ run_response.images = []
5040
+ run_response.images.append(image)
4985
5041
 
4986
5042
  # Handle tool interruption events
4987
5043
  elif model_response_event.event == ModelResponseEvent.tool_call_paused.value:
@@ -5018,23 +5074,39 @@ class Agent:
5018
5074
 
5019
5075
  # If the model response is a tool_call_completed, update the existing tool call in the run_response
5020
5076
  elif model_response_event.event == ModelResponseEvent.tool_call_completed.value:
5021
- if model_response_event.updated_session_state is not None and session.session_data is not None:
5022
- merge_dictionaries(
5023
- session.session_data["session_state"],
5024
- model_response_event.updated_session_state,
5025
- )
5077
+ if model_response_event.updated_session_state is not None:
5078
+ # update the session_state for RunOutput
5079
+ if session_state is not None:
5080
+ merge_dictionaries(session_state, model_response_event.updated_session_state)
5081
+ # update the DB session
5082
+ if session.session_data is not None and session.session_data.get("session_state") is not None:
5083
+ merge_dictionaries(
5084
+ session.session_data["session_state"], model_response_event.updated_session_state
5085
+ )
5026
5086
 
5027
5087
  if model_response_event.images is not None:
5028
5088
  for image in model_response_event.images:
5029
- self._add_image(image, run_response)
5089
+ if run_response.images is None:
5090
+ run_response.images = []
5091
+ run_response.images.append(image)
5030
5092
 
5031
5093
  if model_response_event.videos is not None:
5032
5094
  for video in model_response_event.videos:
5033
- self._add_video(video, run_response)
5095
+ if run_response.videos is None:
5096
+ run_response.videos = []
5097
+ run_response.videos.append(video)
5034
5098
 
5035
5099
  if model_response_event.audios is not None:
5036
5100
  for audio in model_response_event.audios:
5037
- self._add_audio(audio, run_response)
5101
+ if run_response.audio is None:
5102
+ run_response.audio = []
5103
+ run_response.audio.append(audio)
5104
+
5105
+ if model_response_event.files is not None:
5106
+ for file_obj in model_response_event.files:
5107
+ if run_response.files is None:
5108
+ run_response.files = []
5109
+ run_response.files.append(file_obj)
5038
5110
 
5039
5111
  reasoning_step: Optional[ReasoningStep] = None
5040
5112
 
@@ -5251,12 +5323,16 @@ class Agent:
5251
5323
  def get_tools(
5252
5324
  self,
5253
5325
  run_response: RunOutput,
5326
+ run_context: RunContext,
5254
5327
  session: AgentSession,
5255
5328
  user_id: Optional[str] = None,
5256
5329
  knowledge_filters: Optional[Dict[str, Any]] = None,
5257
5330
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5258
5331
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5259
5332
 
5333
+ # Consider both run_context.knowledge_filters and knowledge_filters (deprecated)
5334
+ run_context.knowledge_filters = run_context.knowledge_filters or knowledge_filters
5335
+
5260
5336
  # Add provided tools
5261
5337
  if self.tools is not None:
5262
5338
  # If not running in async mode, raise if any tool is async
@@ -5302,7 +5378,7 @@ class Agent:
5302
5378
  self._search_knowledge_base_with_agentic_filters_function(
5303
5379
  run_response=run_response,
5304
5380
  async_mode=False,
5305
- knowledge_filters=knowledge_filters,
5381
+ knowledge_filters=run_context.knowledge_filters,
5306
5382
  )
5307
5383
  )
5308
5384
  else:
@@ -5310,7 +5386,7 @@ class Agent:
5310
5386
  self._get_search_knowledge_base_function(
5311
5387
  run_response=run_response,
5312
5388
  async_mode=False,
5313
- knowledge_filters=knowledge_filters,
5389
+ knowledge_filters=run_context.knowledge_filters,
5314
5390
  )
5315
5391
  )
5316
5392
 
@@ -5322,6 +5398,7 @@ class Agent:
5322
5398
  async def aget_tools(
5323
5399
  self,
5324
5400
  run_response: RunOutput,
5401
+ run_context: RunContext,
5325
5402
  session: AgentSession,
5326
5403
  user_id: Optional[str] = None,
5327
5404
  knowledge_filters: Optional[Dict[str, Any]] = None,
@@ -5329,6 +5406,9 @@ class Agent:
5329
5406
  ) -> List[Union[Toolkit, Callable, Function, Dict]]:
5330
5407
  agent_tools: List[Union[Toolkit, Callable, Function, Dict]] = []
5331
5408
 
5409
+ # Consider both run_context.knowledge_filters and knowledge_filters (deprecated)
5410
+ run_context.knowledge_filters = run_context.knowledge_filters or knowledge_filters
5411
+
5332
5412
  # Connect MCP tools
5333
5413
  await self._connect_mcp_tools()
5334
5414
 
@@ -5386,7 +5466,7 @@ class Agent:
5386
5466
  self._search_knowledge_base_with_agentic_filters_function(
5387
5467
  run_response=run_response,
5388
5468
  async_mode=True,
5389
- knowledge_filters=knowledge_filters,
5469
+ knowledge_filters=run_context.knowledge_filters,
5390
5470
  )
5391
5471
  )
5392
5472
  else:
@@ -5394,7 +5474,7 @@ class Agent:
5394
5474
  self._get_search_knowledge_base_function(
5395
5475
  run_response=run_response,
5396
5476
  async_mode=True,
5397
- knowledge_filters=knowledge_filters,
5477
+ knowledge_filters=run_context.knowledge_filters,
5398
5478
  )
5399
5479
  )
5400
5480
 
@@ -5408,9 +5488,8 @@ class Agent:
5408
5488
  model: Model,
5409
5489
  processed_tools: List[Union[Toolkit, Callable, Function, Dict]],
5410
5490
  run_response: RunOutput,
5491
+ run_context: RunContext,
5411
5492
  session: AgentSession,
5412
- session_state: Optional[Dict[str, Any]] = None,
5413
- dependencies: Optional[Dict[str, Any]] = None,
5414
5493
  ) -> List[Union[Function, dict]]:
5415
5494
  _function_names = []
5416
5495
  _functions: List[Union[Function, dict]] = []
@@ -5515,8 +5594,9 @@ class Agent:
5515
5594
 
5516
5595
  for func in _functions: # type: ignore
5517
5596
  if isinstance(func, Function):
5518
- func._session_state = session_state
5519
- func._dependencies = dependencies
5597
+ func._run_context = run_context
5598
+ func._session_state = run_context.session_state
5599
+ func._dependencies = run_context.dependencies
5520
5600
  func._images = joint_images
5521
5601
  func._files = joint_files
5522
5602
  func._audios = joint_audios
@@ -5566,49 +5646,70 @@ class Agent:
5566
5646
  log_debug("Model does not support structured or JSON schema outputs.")
5567
5647
  return json_response_format
5568
5648
 
5569
- def _resolve_run_dependencies(self, dependencies: Dict[str, Any]) -> None:
5649
+ def _resolve_run_dependencies(self, run_context: RunContext) -> None:
5570
5650
  from inspect import iscoroutine, iscoroutinefunction, signature
5571
5651
 
5572
5652
  # Dependencies should already be resolved in run() method
5573
5653
  log_debug("Resolving dependencies")
5574
- if not isinstance(dependencies, dict):
5575
- log_warning("Dependencies is not a dict")
5654
+ if not isinstance(run_context.dependencies, dict):
5655
+ log_warning("Run dependencies are not a dict")
5576
5656
  return
5577
5657
 
5578
- for key, value in dependencies.items():
5658
+ for key, value in run_context.dependencies.items():
5579
5659
  if iscoroutine(value) or iscoroutinefunction(value):
5580
5660
  log_warning(f"Dependency {key} is a coroutine. Use agent.arun() or agent.aprint_response() instead.")
5581
5661
  continue
5582
5662
  elif callable(value):
5583
5663
  try:
5584
5664
  sig = signature(value)
5585
- result = value(agent=self) if "agent" in sig.parameters else value()
5665
+
5666
+ # Build kwargs for the function
5667
+ kwargs: Dict[str, Any] = {}
5668
+ if "agent" in sig.parameters:
5669
+ kwargs["agent"] = self
5670
+ if "run_context" in sig.parameters:
5671
+ kwargs["run_context"] = run_context
5672
+
5673
+ # Run the function
5674
+ result = value(**kwargs)
5675
+
5676
+ # Carry the result in the run context
5586
5677
  if result is not None:
5587
- dependencies[key] = result
5678
+ run_context.dependencies[key] = result
5679
+
5588
5680
  except Exception as e:
5589
5681
  log_warning(f"Failed to resolve dependencies for '{key}': {e}")
5590
5682
  else:
5591
- dependencies[key] = value
5683
+ run_context.dependencies[key] = value
5592
5684
 
5593
- async def _aresolve_run_dependencies(self, dependencies: Dict[str, Any]) -> None:
5685
+ async def _aresolve_run_dependencies(self, run_context: RunContext) -> None:
5594
5686
  from inspect import iscoroutine, iscoroutinefunction, signature
5595
5687
 
5596
5688
  log_debug("Resolving context (async)")
5597
- if not isinstance(dependencies, dict):
5598
- log_warning("Context is not a dict")
5689
+ if not isinstance(run_context.dependencies, dict):
5690
+ log_warning("Run dependencies are not a dict")
5599
5691
  return
5600
5692
 
5601
- for key, value in dependencies.items():
5693
+ for key, value in run_context.dependencies.items():
5602
5694
  if not callable(value):
5603
- dependencies[key] = value
5695
+ run_context.dependencies[key] = value
5604
5696
  continue
5605
5697
  try:
5606
5698
  sig = signature(value)
5607
- result = value(agent=self) if "agent" in sig.parameters else value()
5608
5699
 
5700
+ # Build kwargs for the function
5701
+ kwargs: Dict[str, Any] = {}
5702
+ if "agent" in sig.parameters:
5703
+ kwargs["agent"] = self
5704
+ if "run_context" in sig.parameters:
5705
+ kwargs["run_context"] = run_context
5706
+
5707
+ # Run the function
5708
+ result = value(**kwargs)
5609
5709
  if iscoroutine(result) or iscoroutinefunction(result):
5610
- result = await result
5611
- dependencies[key] = result
5710
+ result = await result # type: ignore
5711
+
5712
+ run_context.dependencies[key] = result
5612
5713
  except Exception as e:
5613
5714
  log_warning(f"Failed to resolve context for '{key}': {e}")
5614
5715
 
@@ -6477,10 +6578,10 @@ class Agent:
6477
6578
  def _format_message_with_state_variables(
6478
6579
  self,
6479
6580
  message: Any,
6480
- user_id: Optional[str] = None,
6481
6581
  session_state: Optional[Dict[str, Any]] = None,
6482
6582
  dependencies: Optional[Dict[str, Any]] = None,
6483
6583
  metadata: Optional[Dict[str, Any]] = None,
6584
+ user_id: Optional[str] = None,
6484
6585
  ) -> Any:
6485
6586
  """Format a message with the session state variables."""
6486
6587
  import re
@@ -6497,6 +6598,7 @@ class Agent:
6497
6598
  metadata or {},
6498
6599
  {"user_id": user_id} if user_id is not None else {},
6499
6600
  )
6601
+
6500
6602
  converted_msg = deepcopy(message)
6501
6603
  for var_name in format_variables.keys():
6502
6604
  # Only convert standalone {var_name} patterns, not nested ones
@@ -6516,12 +6618,13 @@ class Agent:
6516
6618
  def get_system_message(
6517
6619
  self,
6518
6620
  session: AgentSession,
6519
- session_state: Optional[Dict[str, Any]] = None,
6621
+ run_context: Optional[RunContext] = None,
6520
6622
  user_id: Optional[str] = None,
6521
6623
  tools: Optional[List[Union[Function, dict]]] = None,
6522
- dependencies: Optional[Dict[str, Any]] = None,
6523
- metadata: Optional[Dict[str, Any]] = None,
6524
6624
  add_session_state_to_context: Optional[bool] = None,
6625
+ session_state: Optional[Dict[str, Any]] = None, # Deprecated
6626
+ dependencies: Optional[Dict[str, Any]] = None, # Deprecated
6627
+ metadata: Optional[Dict[str, Any]] = None, # Deprecated
6525
6628
  ) -> Optional[Message]:
6526
6629
  """Return the system message for the Agent.
6527
6630
 
@@ -6530,6 +6633,12 @@ class Agent:
6530
6633
  3. Build and return the default system message for the Agent.
6531
6634
  """
6532
6635
 
6636
+ # Consider both run_context and session_state, dependencies, metadata (deprecated fields)
6637
+ if run_context is not None:
6638
+ session_state = run_context.session_state or session_state
6639
+ dependencies = run_context.dependencies or dependencies
6640
+ metadata = run_context.metadata or metadata
6641
+
6533
6642
  # 1. If the system_message is provided, use that.
6534
6643
  if self.system_message is not None:
6535
6644
  if isinstance(self.system_message, Message):
@@ -6543,14 +6652,13 @@ class Agent:
6543
6652
  if not isinstance(sys_message_content, str):
6544
6653
  raise Exception("system_message must return a string")
6545
6654
 
6546
- # Format the system message with the session state variables
6547
6655
  if self.resolve_in_context:
6548
6656
  sys_message_content = self._format_message_with_state_variables(
6549
6657
  sys_message_content,
6550
6658
  user_id=user_id,
6659
+ session_state=session_state,
6551
6660
  dependencies=dependencies,
6552
6661
  metadata=metadata,
6553
- session_state=session_state,
6554
6662
  )
6555
6663
 
6556
6664
  # type: ignore
@@ -6582,6 +6690,11 @@ class Agent:
6582
6690
  if "session_state" in signature.parameters:
6583
6691
  instruction_args["session_state"] = session_state or {}
6584
6692
 
6693
+ # Check for run_context parameter
6694
+ if "run_context" in signature.parameters:
6695
+ instruction_args["run_context"] = run_context or None
6696
+
6697
+ # Run the instructions function
6585
6698
  _instructions = self.instructions(**instruction_args)
6586
6699
 
6587
6700
  if isinstance(_instructions, str):
@@ -6859,11 +6972,13 @@ class Agent:
6859
6972
  async def aget_system_message(
6860
6973
  self,
6861
6974
  session: AgentSession,
6862
- session_state: Optional[Dict[str, Any]] = None,
6975
+ run_context: Optional[RunContext] = None,
6863
6976
  user_id: Optional[str] = None,
6864
6977
  tools: Optional[List[Union[Function, dict]]] = None,
6865
- dependencies: Optional[Dict[str, Any]] = None,
6866
- metadata: Optional[Dict[str, Any]] = None,
6978
+ add_session_state_to_context: Optional[bool] = None,
6979
+ session_state: Optional[Dict[str, Any]] = None, # Deprecated
6980
+ dependencies: Optional[Dict[str, Any]] = None, # Deprecated
6981
+ metadata: Optional[Dict[str, Any]] = None, # Deprecated
6867
6982
  ) -> Optional[Message]:
6868
6983
  """Return the system message for the Agent.
6869
6984
 
@@ -6872,6 +6987,12 @@ class Agent:
6872
6987
  3. Build and return the default system message for the Agent.
6873
6988
  """
6874
6989
 
6990
+ # Consider both run_context and session_state, dependencies, metadata (deprecated fields)
6991
+ if run_context is not None:
6992
+ session_state = run_context.session_state or session_state
6993
+ dependencies = run_context.dependencies or dependencies
6994
+ metadata = run_context.metadata or metadata
6995
+
6875
6996
  # 1. If the system_message is provided, use that.
6876
6997
  if self.system_message is not None:
6877
6998
  if isinstance(self.system_message, Message):
@@ -6984,7 +7105,7 @@ class Agent:
6984
7105
 
6985
7106
  # 3.2.5 Add information about agentic filters if enabled
6986
7107
  if self.knowledge is not None and self.enable_agentic_knowledge_filters:
6987
- valid_filters = getattr(self.knowledge, "valid_metadata_filters", None)
7108
+ valid_filters = await self.knowledge.aget_valid_filters()
6988
7109
  if valid_filters:
6989
7110
  valid_filters_str = ", ".join(valid_filters)
6990
7111
  additional_information.append(
@@ -7191,7 +7312,7 @@ class Agent:
7191
7312
  system_message_content += f"{get_response_model_format_prompt(self.output_schema)}"
7192
7313
 
7193
7314
  # 3.3.15 Add the session state to the system message
7194
- if self.add_session_state_to_context and session_state is not None:
7315
+ if add_session_state_to_context and session_state is not None:
7195
7316
  system_message_content += self._get_formatted_session_state_for_system_message(session_state)
7196
7317
 
7197
7318
  # Return the system message
@@ -7208,17 +7329,18 @@ class Agent:
7208
7329
  self,
7209
7330
  *,
7210
7331
  run_response: RunOutput,
7332
+ run_context: Optional[RunContext] = None,
7211
7333
  session_state: Optional[Dict[str, Any]] = None,
7334
+ dependencies: Optional[Dict[str, Any]] = None,
7335
+ metadata: Optional[Dict[str, Any]] = None,
7212
7336
  user_id: Optional[str] = None,
7213
7337
  input: Optional[Union[str, List, Dict, Message, BaseModel, List[Message]]] = None,
7214
7338
  audio: Optional[Sequence[Audio]] = None,
7215
7339
  images: Optional[Sequence[Image]] = None,
7216
7340
  videos: Optional[Sequence[Video]] = None,
7217
7341
  files: Optional[Sequence[File]] = None,
7218
- knowledge_filters: Optional[Dict[str, Any]] = None,
7219
- dependencies: Optional[Dict[str, Any]] = None,
7220
7342
  add_dependencies_to_context: Optional[bool] = None,
7221
- metadata: Optional[Dict[str, Any]] = None,
7343
+ knowledge_filters: Optional[Dict[str, Any]] = None,
7222
7344
  **kwargs: Any,
7223
7345
  ) -> Optional[Message]:
7224
7346
  """Return the user message for the Agent.
@@ -7227,6 +7349,13 @@ class Agent:
7227
7349
  2. If build_user_context is False or if the message is a list, return the message as is.
7228
7350
  3. Build the default user message for the Agent
7229
7351
  """
7352
+ # Consider both run_context and session_state, dependencies, metadata, knowledge_filters (deprecated fields)
7353
+ if run_context is not None:
7354
+ session_state = run_context.session_state or session_state
7355
+ dependencies = run_context.dependencies or dependencies
7356
+ metadata = run_context.metadata or metadata
7357
+ knowledge_filters = run_context.knowledge_filters or knowledge_filters
7358
+
7230
7359
  # Get references from the knowledge base to use in the user message
7231
7360
  references = None
7232
7361
 
@@ -7234,7 +7363,7 @@ class Agent:
7234
7363
  if not self.build_user_context:
7235
7364
  return Message(
7236
7365
  role=self.user_message_role or "user",
7237
- content=input,
7366
+ content=input, # type: ignore
7238
7367
  images=None if not self.send_media_to_model else images,
7239
7368
  audio=None if not self.send_media_to_model else audio,
7240
7369
  videos=None if not self.send_media_to_model else videos,
@@ -7375,20 +7504,17 @@ class Agent:
7375
7504
  self,
7376
7505
  *,
7377
7506
  run_response: RunOutput,
7507
+ run_context: RunContext,
7378
7508
  input: Union[str, List, Dict, Message, BaseModel, List[Message]],
7379
7509
  session: AgentSession,
7380
- session_state: Optional[Dict[str, Any]] = None,
7381
7510
  user_id: Optional[str] = None,
7382
7511
  audio: Optional[Sequence[Audio]] = None,
7383
7512
  images: Optional[Sequence[Image]] = None,
7384
7513
  videos: Optional[Sequence[Video]] = None,
7385
7514
  files: Optional[Sequence[File]] = None,
7386
- knowledge_filters: Optional[Dict[str, Any]] = None,
7387
7515
  add_history_to_context: Optional[bool] = None,
7388
- dependencies: Optional[Dict[str, Any]] = None,
7389
7516
  add_dependencies_to_context: Optional[bool] = None,
7390
7517
  add_session_state_to_context: Optional[bool] = None,
7391
- metadata: Optional[Dict[str, Any]] = None,
7392
7518
  tools: Optional[List[Union[Function, dict]]] = None,
7393
7519
  **kwargs: Any,
7394
7520
  ) -> RunMessages:
@@ -7422,11 +7548,12 @@ class Agent:
7422
7548
  # 1. Add system message to run_messages
7423
7549
  system_message = self.get_system_message(
7424
7550
  session=session,
7425
- session_state=session_state,
7551
+ run_context=run_context,
7552
+ session_state=run_context.session_state,
7553
+ dependencies=run_context.dependencies,
7554
+ metadata=run_context.metadata,
7426
7555
  user_id=user_id,
7427
7556
  tools=tools,
7428
- dependencies=dependencies,
7429
- metadata=metadata,
7430
7557
  add_session_state_to_context=add_session_state_to_context,
7431
7558
  )
7432
7559
  if system_message is not None:
@@ -7511,16 +7638,13 @@ class Agent:
7511
7638
  ):
7512
7639
  user_message = self._get_user_message(
7513
7640
  run_response=run_response,
7514
- session_state=session_state,
7641
+ run_context=run_context,
7515
7642
  input=input,
7516
7643
  audio=audio,
7517
7644
  images=images,
7518
7645
  videos=videos,
7519
7646
  files=files,
7520
- knowledge_filters=knowledge_filters,
7521
- dependencies=dependencies,
7522
7647
  add_dependencies_to_context=add_dependencies_to_context,
7523
- metadata=metadata,
7524
7648
  **kwargs,
7525
7649
  )
7526
7650
 
@@ -7585,6 +7709,7 @@ class Agent:
7585
7709
  run_response: RunOutput,
7586
7710
  input: Union[str, List, Dict, Message, BaseModel, List[Message]],
7587
7711
  session: AgentSession,
7712
+ run_context: Optional[RunContext] = None,
7588
7713
  session_state: Optional[Dict[str, Any]] = None,
7589
7714
  user_id: Optional[str] = None,
7590
7715
  audio: Optional[Sequence[Audio]] = None,
@@ -7624,17 +7749,25 @@ class Agent:
7624
7749
  )
7625
7750
  """
7626
7751
 
7752
+ # Consider both run_context and session_state, dependencies, metadata (deprecated fields)
7753
+ if run_context is not None:
7754
+ session_state = run_context.session_state or session_state
7755
+ dependencies = run_context.dependencies or dependencies
7756
+ metadata = run_context.metadata or metadata
7757
+
7627
7758
  # Initialize the RunMessages object (no media here - that's in RunInput now)
7628
7759
  run_messages = RunMessages()
7629
7760
 
7630
7761
  # 1. Add system message to run_messages
7631
7762
  system_message = await self.aget_system_message(
7632
7763
  session=session,
7764
+ run_context=run_context,
7633
7765
  session_state=session_state,
7634
7766
  user_id=user_id,
7635
7767
  tools=tools,
7636
7768
  dependencies=dependencies,
7637
7769
  metadata=metadata,
7770
+ add_session_state_to_context=add_session_state_to_context,
7638
7771
  )
7639
7772
  if system_message is not None:
7640
7773
  run_messages.system_message = system_message
@@ -7718,16 +7851,17 @@ class Agent:
7718
7851
  ):
7719
7852
  user_message = self._get_user_message(
7720
7853
  run_response=run_response,
7854
+ run_context=run_context,
7721
7855
  session_state=session_state,
7856
+ dependencies=dependencies,
7857
+ metadata=metadata,
7722
7858
  input=input,
7723
7859
  audio=audio,
7724
7860
  images=images,
7725
7861
  videos=videos,
7726
7862
  files=files,
7727
7863
  knowledge_filters=knowledge_filters,
7728
- dependencies=dependencies,
7729
7864
  add_dependencies_to_context=add_dependencies_to_context,
7730
- metadata=metadata,
7731
7865
  **kwargs,
7732
7866
  )
7733
7867
 
@@ -7967,7 +8101,7 @@ class Agent:
7967
8101
 
7968
8102
  # Validate the filters against known valid filter keys
7969
8103
  if self.knowledge is not None:
7970
- valid_filters, invalid_keys = self.knowledge.validate_filters(filters) # type: ignore
8104
+ valid_filters, invalid_keys = await self.knowledge.async_validate_filters(filters) # type: ignore
7971
8105
 
7972
8106
  # Warn about invalid filter keys
7973
8107
  if invalid_keys: # type: ignore
@@ -8203,38 +8337,6 @@ class Agent:
8203
8337
 
8204
8338
  return metrics
8205
8339
 
8206
- ###########################################################################
8207
- # Handle images, videos and audio
8208
- ###########################################################################
8209
-
8210
- def _add_image(self, image: Image, run_response: RunOutput) -> None:
8211
- """Add an image to both the agent's stateful storage and the current run response"""
8212
- # Add to run response
8213
- if run_response.images is None:
8214
- run_response.images = []
8215
- run_response.images.append(image)
8216
-
8217
- def _add_video(self, video: Video, run_response: RunOutput) -> None:
8218
- """Add a video to both the agent's stateful storage and the current run response"""
8219
- # Add to run response
8220
- if run_response.videos is None:
8221
- run_response.videos = []
8222
- run_response.videos.append(video)
8223
-
8224
- def _add_audio(self, audio: Audio, run_response: RunOutput) -> None:
8225
- """Add audio to both the agent's stateful storage and the current run response"""
8226
- # Add to run response
8227
- if run_response.audio is None:
8228
- run_response.audio = []
8229
- run_response.audio.append(audio)
8230
-
8231
- def _add_file(self, file: File, run_response: RunOutput) -> None:
8232
- """Add file to both the agent's stateful storage and the current run response"""
8233
- # Add to run response
8234
- if run_response.files is None:
8235
- run_response.files = []
8236
- run_response.files.append(file)
8237
-
8238
8340
  ###########################################################################
8239
8341
  # Reasoning
8240
8342
  ###########################################################################
@@ -8454,7 +8556,7 @@ class Agent:
8454
8556
  store_events=self.store_events,
8455
8557
  )
8456
8558
  else:
8457
- log_warning(
8559
+ log_info(
8458
8560
  f"Reasoning model: {reasoning_model.__class__.__name__} is not a native reasoning model, defaulting to manual Chain-of-Thought reasoning"
8459
8561
  )
8460
8562
  use_default_reasoning = True
@@ -8747,7 +8849,7 @@ class Agent:
8747
8849
  store_events=self.store_events,
8748
8850
  )
8749
8851
  else:
8750
- log_warning(
8852
+ log_info(
8751
8853
  f"Reasoning model: {reasoning_model.__class__.__name__} is not a native reasoning model, defaulting to manual Chain-of-Thought reasoning"
8752
8854
  )
8753
8855
  use_default_reasoning = True
@@ -10002,7 +10104,13 @@ class Agent:
10002
10104
 
10003
10105
  return effective_filters
10004
10106
 
10005
- def _cleanup_and_store(self, run_response: RunOutput, session: AgentSession, user_id: Optional[str] = None) -> None:
10107
+ def _cleanup_and_store(
10108
+ self,
10109
+ run_response: RunOutput,
10110
+ session: AgentSession,
10111
+ run_context: Optional[RunContext] = None,
10112
+ user_id: Optional[str] = None,
10113
+ ) -> None:
10006
10114
  # Scrub the stored run based on storage flags
10007
10115
  self._scrub_run_output_for_storage(run_response)
10008
10116
 
@@ -10010,6 +10118,11 @@ class Agent:
10010
10118
  if run_response.metrics:
10011
10119
  run_response.metrics.stop_timer()
10012
10120
 
10121
+ # Update run_response.session_state before saving
10122
+ # This ensures RunOutput reflects all tool modifications
10123
+ if session.session_data is not None and run_context is not None and run_context.session_state is not None:
10124
+ run_response.session_state = run_context.session_state
10125
+
10013
10126
  # Optional: Save output to file if save_response_to_file is set
10014
10127
  self.save_run_response_to_file(
10015
10128
  run_response=run_response,
@@ -10028,7 +10141,11 @@ class Agent:
10028
10141
  self.save_session(session=session)
10029
10142
 
10030
10143
  async def _acleanup_and_store(
10031
- self, run_response: RunOutput, session: AgentSession, user_id: Optional[str] = None
10144
+ self,
10145
+ run_response: RunOutput,
10146
+ session: AgentSession,
10147
+ run_context: Optional[RunContext] = None,
10148
+ user_id: Optional[str] = None,
10032
10149
  ) -> None:
10033
10150
  # Scrub the stored run based on storage flags
10034
10151
  self._scrub_run_output_for_storage(run_response)
@@ -10037,6 +10154,11 @@ class Agent:
10037
10154
  if run_response.metrics:
10038
10155
  run_response.metrics.stop_timer()
10039
10156
 
10157
+ # Update run_response.session_state from session before saving
10158
+ # This ensures RunOutput reflects all tool modifications
10159
+ if session.session_data is not None and run_context is not None and run_context.session_state is not None:
10160
+ run_response.session_state = run_context.session_state
10161
+
10040
10162
  # Optional: Save output to file if save_response_to_file is set
10041
10163
  self.save_run_response_to_file(
10042
10164
  run_response=run_response,
@@ -10051,7 +10173,14 @@ class Agent:
10051
10173
  # Calculate session metrics
10052
10174
  self._update_session_metrics(session=session, run_response=run_response)
10053
10175
 
10054
- # Save session to storage
10176
+ # Update session state before saving the session
10177
+ if run_context is not None and run_context.session_state is not None:
10178
+ if session.session_data is not None:
10179
+ session.session_data["session_state"] = run_context.session_state
10180
+ else:
10181
+ session.session_data = {"session_state": run_context.session_state}
10182
+
10183
+ # Save session to memory
10055
10184
  await self.asave_session(session=session)
10056
10185
 
10057
10186
  def _scrub_run_output_for_storage(self, run_response: RunOutput) -> None:
@@ -10067,58 +10196,6 @@ class Agent:
10067
10196
  if not self.store_history_messages:
10068
10197
  scrub_history_messages_from_run_output(run_response)
10069
10198
 
10070
- def _validate_media_object_id(
10071
- self,
10072
- images: Optional[Sequence[Image]] = None,
10073
- videos: Optional[Sequence[Video]] = None,
10074
- audios: Optional[Sequence[Audio]] = None,
10075
- files: Optional[Sequence[File]] = None,
10076
- ) -> tuple:
10077
- """Convert raw Image/Video/Audio objects - now unified, so just return as-is."""
10078
- # With unified classes, no conversion needed - just ensure IDs are set
10079
- image_list = None
10080
- if images:
10081
- image_list = []
10082
- for img in images:
10083
- # Ensure ID is set (validation should handle this, but double-check)
10084
- if not img.id:
10085
- from uuid import uuid4
10086
-
10087
- img.id = str(uuid4())
10088
- image_list.append(img)
10089
-
10090
- video_list = None
10091
- if videos:
10092
- video_list = []
10093
- for vid in videos:
10094
- if not vid.id:
10095
- from uuid import uuid4
10096
-
10097
- vid.id = str(uuid4())
10098
- video_list.append(vid)
10099
-
10100
- audio_list = None
10101
- if audios:
10102
- audio_list = []
10103
- for aud in audios:
10104
- if not aud.id:
10105
- from uuid import uuid4
10106
-
10107
- aud.id = str(uuid4())
10108
- audio_list.append(aud)
10109
-
10110
- file_list = None
10111
- if files:
10112
- file_list = []
10113
- for file in files:
10114
- if not file.id:
10115
- from uuid import uuid4
10116
-
10117
- file.id = str(uuid4())
10118
- file_list.append(file)
10119
-
10120
- return image_list, video_list, audio_list, file_list
10121
-
10122
10199
  def cli_app(
10123
10200
  self,
10124
10201
  input: Optional[str] = None,