agno 2.3.2__py3-none-any.whl → 2.3.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. agno/agent/agent.py +513 -185
  2. agno/compression/__init__.py +3 -0
  3. agno/compression/manager.py +176 -0
  4. agno/db/dynamo/dynamo.py +11 -0
  5. agno/db/firestore/firestore.py +5 -1
  6. agno/db/gcs_json/gcs_json_db.py +5 -2
  7. agno/db/in_memory/in_memory_db.py +5 -2
  8. agno/db/json/json_db.py +5 -1
  9. agno/db/migrations/manager.py +4 -4
  10. agno/db/mongo/async_mongo.py +158 -34
  11. agno/db/mongo/mongo.py +6 -2
  12. agno/db/mysql/mysql.py +48 -54
  13. agno/db/postgres/async_postgres.py +66 -52
  14. agno/db/postgres/postgres.py +42 -50
  15. agno/db/redis/redis.py +5 -0
  16. agno/db/redis/utils.py +5 -5
  17. agno/db/singlestore/singlestore.py +99 -108
  18. agno/db/sqlite/async_sqlite.py +29 -27
  19. agno/db/sqlite/sqlite.py +30 -26
  20. agno/knowledge/reader/pdf_reader.py +2 -2
  21. agno/knowledge/reader/tavily_reader.py +0 -1
  22. agno/memory/__init__.py +14 -1
  23. agno/memory/manager.py +217 -4
  24. agno/memory/strategies/__init__.py +15 -0
  25. agno/memory/strategies/base.py +67 -0
  26. agno/memory/strategies/summarize.py +196 -0
  27. agno/memory/strategies/types.py +37 -0
  28. agno/models/aimlapi/aimlapi.py +18 -0
  29. agno/models/anthropic/claude.py +87 -81
  30. agno/models/aws/bedrock.py +38 -16
  31. agno/models/aws/claude.py +97 -277
  32. agno/models/azure/ai_foundry.py +8 -4
  33. agno/models/base.py +101 -14
  34. agno/models/cerebras/cerebras.py +25 -9
  35. agno/models/cerebras/cerebras_openai.py +22 -2
  36. agno/models/cohere/chat.py +18 -6
  37. agno/models/cometapi/cometapi.py +19 -1
  38. agno/models/deepinfra/deepinfra.py +19 -1
  39. agno/models/fireworks/fireworks.py +19 -1
  40. agno/models/google/gemini.py +583 -21
  41. agno/models/groq/groq.py +23 -6
  42. agno/models/huggingface/huggingface.py +22 -7
  43. agno/models/ibm/watsonx.py +21 -7
  44. agno/models/internlm/internlm.py +19 -1
  45. agno/models/langdb/langdb.py +10 -0
  46. agno/models/litellm/chat.py +17 -7
  47. agno/models/litellm/litellm_openai.py +19 -1
  48. agno/models/message.py +19 -5
  49. agno/models/meta/llama.py +25 -5
  50. agno/models/meta/llama_openai.py +18 -0
  51. agno/models/mistral/mistral.py +13 -5
  52. agno/models/nvidia/nvidia.py +19 -1
  53. agno/models/ollama/chat.py +17 -6
  54. agno/models/openai/chat.py +22 -7
  55. agno/models/openai/responses.py +28 -10
  56. agno/models/openrouter/openrouter.py +20 -0
  57. agno/models/perplexity/perplexity.py +17 -0
  58. agno/models/requesty/requesty.py +18 -0
  59. agno/models/sambanova/sambanova.py +19 -1
  60. agno/models/siliconflow/siliconflow.py +19 -1
  61. agno/models/together/together.py +19 -1
  62. agno/models/vercel/v0.py +19 -1
  63. agno/models/vertexai/claude.py +99 -5
  64. agno/models/xai/xai.py +18 -0
  65. agno/os/interfaces/agui/router.py +1 -0
  66. agno/os/interfaces/agui/utils.py +97 -57
  67. agno/os/router.py +16 -0
  68. agno/os/routers/memory/memory.py +143 -0
  69. agno/os/routers/memory/schemas.py +26 -0
  70. agno/os/schema.py +33 -6
  71. agno/os/utils.py +134 -10
  72. agno/run/base.py +2 -1
  73. agno/run/workflow.py +1 -1
  74. agno/team/team.py +566 -219
  75. agno/tools/mcp/mcp.py +1 -1
  76. agno/utils/agent.py +119 -1
  77. agno/utils/models/ai_foundry.py +9 -2
  78. agno/utils/models/claude.py +12 -5
  79. agno/utils/models/cohere.py +9 -2
  80. agno/utils/models/llama.py +9 -2
  81. agno/utils/models/mistral.py +4 -2
  82. agno/utils/print_response/agent.py +37 -2
  83. agno/utils/print_response/team.py +52 -0
  84. agno/utils/tokens.py +41 -0
  85. agno/workflow/types.py +2 -2
  86. {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/METADATA +45 -40
  87. {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/RECORD +90 -83
  88. {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/WHEEL +0 -0
  89. {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/licenses/LICENSE +0 -0
  90. {agno-2.3.2.dist-info → agno-2.3.4.dist-info}/top_level.txt +0 -0
agno/agent/agent.py CHANGED
@@ -29,6 +29,7 @@ from uuid import uuid4
29
29
 
30
30
  from pydantic import BaseModel
31
31
 
32
+ from agno.compression.manager import CompressionManager
32
33
  from agno.culture.manager import CultureManager
33
34
  from agno.db.base import AsyncBaseDb, BaseDb, SessionType, UserMemory
34
35
  from agno.db.schemas.culture import CulturalKnowledge
@@ -73,6 +74,8 @@ from agno.session.summary import SessionSummary
73
74
  from agno.tools import Toolkit
74
75
  from agno.tools.function import Function
75
76
  from agno.utils.agent import (
77
+ aexecute_instructions,
78
+ aexecute_system_message,
76
79
  aget_last_run_output_util,
77
80
  aget_run_output_util,
78
81
  aget_session_metrics_util,
@@ -86,6 +89,8 @@ from agno.utils.agent import (
86
89
  collect_joint_files,
87
90
  collect_joint_images,
88
91
  collect_joint_videos,
92
+ execute_instructions,
93
+ execute_system_message,
89
94
  get_last_run_output_util,
90
95
  get_run_output_util,
91
96
  get_session_metrics_util,
@@ -365,7 +370,7 @@ class Agent:
365
370
  parse_response: bool = True
366
371
  # Use model enforced structured_outputs if supported (e.g. OpenAIChat)
367
372
  structured_outputs: Optional[bool] = None
368
- # If `output_schema` is set, sets the response mode of the model, i.e. if the model should explicitly respond with a JSON object instead of a Pydantic model
373
+ # Intead of providing the model with the Pydantic output schema, add a JSON description of the output schema to the system message instead.
369
374
  use_json_mode: bool = False
370
375
  # Save the response to a file
371
376
  save_response_to_file: Optional[str] = None
@@ -404,6 +409,12 @@ class Agent:
404
409
  # If True, the agent adds cultural knowledge in the response
405
410
  add_culture_to_context: Optional[bool] = None
406
411
 
412
+ # --- Context Compression ---
413
+ # If True, compress tool call results to save context
414
+ compress_tool_results: bool = False
415
+ # Compression manager for compressing tool call results
416
+ compression_manager: Optional[CompressionManager] = None
417
+
407
418
  # --- Debug ---
408
419
  # Enable debug logs
409
420
  debug_mode: bool = False
@@ -443,6 +454,8 @@ class Agent:
443
454
  enable_session_summaries: bool = False,
444
455
  add_session_summary_to_context: Optional[bool] = None,
445
456
  session_summary_manager: Optional[SessionSummaryManager] = None,
457
+ compress_tool_results: bool = False,
458
+ compression_manager: Optional[CompressionManager] = None,
446
459
  add_history_to_context: bool = False,
447
460
  num_history_runs: Optional[int] = None,
448
461
  num_history_messages: Optional[int] = None,
@@ -546,6 +559,10 @@ class Agent:
546
559
  self.enable_session_summaries = enable_session_summaries
547
560
  self.add_session_summary_to_context = add_session_summary_to_context
548
561
 
562
+ # Context compression settings
563
+ self.compress_tool_results = compress_tool_results
564
+ self.compression_manager = compression_manager
565
+
549
566
  self.add_history_to_context = add_history_to_context
550
567
  self.num_history_runs = num_history_runs
551
568
  self.num_history_messages = num_history_messages
@@ -684,10 +701,6 @@ class Agent:
684
701
  self._background_executor = ThreadPoolExecutor(max_workers=3, thread_name_prefix="agno-bg")
685
702
  return self._background_executor
686
703
 
687
- @property
688
- def should_parse_structured_output(self) -> bool:
689
- return self.output_schema is not None and self.parse_response and self.parser_model is None
690
-
691
704
  @property
692
705
  def cached_session(self) -> Optional[AgentSession]:
693
706
  return self._cached_session
@@ -823,6 +836,19 @@ class Agent:
823
836
  self.enable_session_summaries or self.session_summary_manager is not None
824
837
  )
825
838
 
839
+ def _set_compression_manager(self) -> None:
840
+ if self.compress_tool_results and self.compression_manager is None:
841
+ self.compression_manager = CompressionManager(
842
+ model=self.model,
843
+ )
844
+
845
+ if self.compression_manager is not None and self.compression_manager.model is None:
846
+ self.compression_manager.model = self.model
847
+
848
+ # Check compression flag on the compression manager
849
+ if self.compression_manager is not None and self.compression_manager.compress_tool_results:
850
+ self.compress_tool_results = True
851
+
826
852
  def _has_async_db(self) -> bool:
827
853
  """Return True if the db the agent is equipped with is an Async implementation"""
828
854
  return self.db is not None and isinstance(self.db, AsyncBaseDb)
@@ -837,6 +863,9 @@ class Agent:
837
863
  if self.output_model is not None:
838
864
  self.output_model = get_model(self.output_model)
839
865
 
866
+ if self.compression_manager is not None and self.compression_manager.model is None:
867
+ self.compression_manager.model = self.model
868
+
840
869
  def initialize_agent(self, debug_mode: Optional[bool] = None) -> None:
841
870
  self._set_default_model()
842
871
  self._set_debug(debug_mode=debug_mode)
@@ -852,6 +881,8 @@ class Agent:
852
881
  self._set_culture_manager()
853
882
  if self.enable_session_summaries or self.session_summary_manager is not None:
854
883
  self._set_session_summary_manager()
884
+ if self.compress_tool_results or self.compression_manager is not None:
885
+ self._set_compression_manager()
855
886
 
856
887
  log_debug(f"Agent ID: {self.id}", center=True)
857
888
 
@@ -870,10 +901,15 @@ class Agent:
870
901
  """Connect the MCP tools to the agent."""
871
902
  if self.tools:
872
903
  for tool in self.tools:
873
- if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"] and not tool.initialized: # type: ignore
904
+ # Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
905
+ if (
906
+ hasattr(type(tool), "__mro__")
907
+ and any(c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__)
908
+ and not tool.initialized # type: ignore
909
+ ):
874
910
  # Connect the MCP server
875
911
  await tool.connect() # type: ignore
876
- self._mcp_tools_initialized_on_run.append(tool)
912
+ self._mcp_tools_initialized_on_run.append(tool) # type: ignore
877
913
 
878
914
  async def _disconnect_mcp_tools(self) -> None:
879
915
  """Disconnect the MCP tools from the agent."""
@@ -1041,6 +1077,7 @@ class Agent:
1041
1077
 
1042
1078
  # 6. Generate a response from the Model (includes running function calls)
1043
1079
  self.model = cast(Model, self.model)
1080
+
1044
1081
  model_response: ModelResponse = self.model.response(
1045
1082
  messages=run_messages.messages,
1046
1083
  tools=_tools,
@@ -1049,6 +1086,7 @@ class Agent:
1049
1086
  response_format=response_format,
1050
1087
  run_response=run_response,
1051
1088
  send_media_to_model=self.send_media_to_model,
1089
+ compression_manager=self.compression_manager if self.compress_tool_results else None,
1052
1090
  )
1053
1091
 
1054
1092
  # Check for cancellation after model call
@@ -1058,7 +1096,7 @@ class Agent:
1058
1096
  self._generate_response_with_output_model(model_response, run_messages)
1059
1097
 
1060
1098
  # If a parser model is provided, structure the response separately
1061
- self._parse_response_with_parser_model(model_response, run_messages)
1099
+ self._parse_response_with_parser_model(model_response, run_messages, run_context=run_context)
1062
1100
 
1063
1101
  # 7. Update the RunOutput with the model response
1064
1102
  self._update_run_response(
@@ -1078,7 +1116,7 @@ class Agent:
1078
1116
  store_media_util(run_response, model_response)
1079
1117
 
1080
1118
  # 9. Convert the response to the structured format if needed
1081
- self._convert_response_to_structured_format(run_response)
1119
+ self._convert_response_to_structured_format(run_response, run_context=run_context)
1082
1120
 
1083
1121
  # 10. Execute post-hooks after output is generated but before response is returned
1084
1122
  if self.post_hooks is not None:
@@ -1183,6 +1221,7 @@ class Agent:
1183
1221
  session=session,
1184
1222
  user_id=user_id,
1185
1223
  debug_mode=debug_mode,
1224
+ stream_events=stream_events,
1186
1225
  **kwargs,
1187
1226
  )
1188
1227
  for event in pre_hook_iterator:
@@ -1276,6 +1315,7 @@ class Agent:
1276
1315
  response_format=response_format,
1277
1316
  stream_events=stream_events,
1278
1317
  session_state=run_context.session_state,
1318
+ run_context=run_context,
1279
1319
  ):
1280
1320
  raise_if_cancelled(run_response.run_id) # type: ignore
1281
1321
  yield event
@@ -1293,6 +1333,7 @@ class Agent:
1293
1333
  response_format=response_format,
1294
1334
  stream_events=stream_events,
1295
1335
  session_state=run_context.session_state,
1336
+ run_context=run_context,
1296
1337
  ):
1297
1338
  raise_if_cancelled(run_response.run_id) # type: ignore
1298
1339
  if isinstance(event, RunContentEvent):
@@ -1319,7 +1360,7 @@ class Agent:
1319
1360
 
1320
1361
  # 7. Parse response with parser model if provided
1321
1362
  yield from self._parse_response_with_parser_model_stream(
1322
- session=session, run_response=run_response, stream_events=stream_events
1363
+ session=session, run_response=run_response, stream_events=stream_events, run_context=run_context
1323
1364
  )
1324
1365
 
1325
1366
  # We should break out of the run function
@@ -1357,6 +1398,7 @@ class Agent:
1357
1398
  session=session,
1358
1399
  user_id=user_id,
1359
1400
  debug_mode=debug_mode,
1401
+ stream_events=stream_events,
1360
1402
  **kwargs,
1361
1403
  )
1362
1404
 
@@ -1474,6 +1516,7 @@ class Agent:
1474
1516
  add_session_state_to_context: Optional[bool] = None,
1475
1517
  dependencies: Optional[Dict[str, Any]] = None,
1476
1518
  metadata: Optional[Dict[str, Any]] = None,
1519
+ output_schema: Optional[Type[BaseModel]] = None,
1477
1520
  debug_mode: Optional[bool] = None,
1478
1521
  **kwargs: Any,
1479
1522
  ) -> RunOutput: ...
@@ -1501,6 +1544,7 @@ class Agent:
1501
1544
  add_session_state_to_context: Optional[bool] = None,
1502
1545
  dependencies: Optional[Dict[str, Any]] = None,
1503
1546
  metadata: Optional[Dict[str, Any]] = None,
1547
+ output_schema: Optional[Type[BaseModel]] = None,
1504
1548
  yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
1505
1549
  yield_run_output: bool = False,
1506
1550
  debug_mode: Optional[bool] = None,
@@ -1529,6 +1573,7 @@ class Agent:
1529
1573
  add_session_state_to_context: Optional[bool] = None,
1530
1574
  dependencies: Optional[Dict[str, Any]] = None,
1531
1575
  metadata: Optional[Dict[str, Any]] = None,
1576
+ output_schema: Optional[Type[BaseModel]] = None,
1532
1577
  yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
1533
1578
  yield_run_output: Optional[bool] = None,
1534
1579
  debug_mode: Optional[bool] = None,
@@ -1598,6 +1643,10 @@ class Agent:
1598
1643
  # Determine runtime dependencies
1599
1644
  dependencies = dependencies if dependencies is not None else self.dependencies
1600
1645
 
1646
+ # Resolve output_schema parameter takes precedence, then fall back to self.output_schema
1647
+ if output_schema is None:
1648
+ output_schema = self.output_schema
1649
+
1601
1650
  # Initialize run context
1602
1651
  run_context = run_context or RunContext(
1603
1652
  run_id=run_id,
@@ -1605,7 +1654,10 @@ class Agent:
1605
1654
  user_id=user_id,
1606
1655
  session_state=session_state,
1607
1656
  dependencies=dependencies,
1657
+ output_schema=output_schema,
1608
1658
  )
1659
+ # output_schema parameter takes priority, even if run_context was provided
1660
+ run_context.output_schema = output_schema
1609
1661
 
1610
1662
  # Resolve dependencies
1611
1663
  if run_context.dependencies is not None:
@@ -1649,7 +1701,7 @@ class Agent:
1649
1701
  self.stream_events = self.stream_events or stream_events
1650
1702
 
1651
1703
  # Prepare arguments for the model
1652
- response_format = self._get_response_format() if self.parser_model is None else None
1704
+ response_format = self._get_response_format(run_context=run_context) if self.parser_model is None else None
1653
1705
  self.model = cast(Model, self.model)
1654
1706
 
1655
1707
  # Merge agent metadata with run metadata
@@ -1910,6 +1962,7 @@ class Agent:
1910
1962
  response_format=response_format,
1911
1963
  send_media_to_model=self.send_media_to_model,
1912
1964
  run_response=run_response,
1965
+ compression_manager=self.compression_manager if self.compress_tool_results else None,
1913
1966
  )
1914
1967
 
1915
1968
  # Check for cancellation after model call
@@ -1919,13 +1972,16 @@ class Agent:
1919
1972
  await self._agenerate_response_with_output_model(model_response=model_response, run_messages=run_messages)
1920
1973
 
1921
1974
  # If a parser model is provided, structure the response separately
1922
- await self._aparse_response_with_parser_model(model_response=model_response, run_messages=run_messages)
1975
+ await self._aparse_response_with_parser_model(
1976
+ model_response=model_response, run_messages=run_messages, run_context=run_context
1977
+ )
1923
1978
 
1924
1979
  # 10. Update the RunOutput with the model response
1925
1980
  self._update_run_response(
1926
1981
  model_response=model_response,
1927
1982
  run_response=run_response,
1928
1983
  run_messages=run_messages,
1984
+ run_context=run_context,
1929
1985
  )
1930
1986
 
1931
1987
  # We should break out of the run function
@@ -1938,7 +1994,7 @@ class Agent:
1938
1994
  )
1939
1995
 
1940
1996
  # 11. Convert the response to the structured format if needed
1941
- self._convert_response_to_structured_format(run_response)
1997
+ self._convert_response_to_structured_format(run_response, run_context=run_context)
1942
1998
 
1943
1999
  # 12. Store media if enabled
1944
2000
  if self.store_media:
@@ -2105,6 +2161,7 @@ class Agent:
2105
2161
  session=agent_session,
2106
2162
  user_id=user_id,
2107
2163
  debug_mode=debug_mode,
2164
+ stream_events=stream_events,
2108
2165
  **kwargs,
2109
2166
  )
2110
2167
  async for event in pre_hook_iterator:
@@ -2188,6 +2245,7 @@ class Agent:
2188
2245
  response_format=response_format,
2189
2246
  stream_events=stream_events,
2190
2247
  session_state=run_context.session_state,
2248
+ run_context=run_context,
2191
2249
  ):
2192
2250
  raise_if_cancelled(run_response.run_id) # type: ignore
2193
2251
  yield event
@@ -2205,6 +2263,7 @@ class Agent:
2205
2263
  response_format=response_format,
2206
2264
  stream_events=stream_events,
2207
2265
  session_state=run_context.session_state,
2266
+ run_context=run_context,
2208
2267
  ):
2209
2268
  raise_if_cancelled(run_response.run_id) # type: ignore
2210
2269
  if isinstance(event, RunContentEvent):
@@ -2231,7 +2290,7 @@ class Agent:
2231
2290
 
2232
2291
  # 10. Parse response with parser model if provided
2233
2292
  async for event in self._aparse_response_with_parser_model_stream(
2234
- session=agent_session, run_response=run_response, stream_events=stream_events
2293
+ session=agent_session, run_response=run_response, stream_events=stream_events, run_context=run_context
2235
2294
  ):
2236
2295
  yield event
2237
2296
 
@@ -2268,6 +2327,7 @@ class Agent:
2268
2327
  session=agent_session,
2269
2328
  user_id=user_id,
2270
2329
  debug_mode=debug_mode,
2330
+ stream_events=stream_events,
2271
2331
  **kwargs,
2272
2332
  ):
2273
2333
  yield event
@@ -2413,6 +2473,7 @@ class Agent:
2413
2473
  add_session_state_to_context: Optional[bool] = None,
2414
2474
  dependencies: Optional[Dict[str, Any]] = None,
2415
2475
  metadata: Optional[Dict[str, Any]] = None,
2476
+ output_schema: Optional[Type[BaseModel]] = None,
2416
2477
  debug_mode: Optional[bool] = None,
2417
2478
  **kwargs: Any,
2418
2479
  ) -> RunOutput: ...
@@ -2439,6 +2500,7 @@ class Agent:
2439
2500
  add_session_state_to_context: Optional[bool] = None,
2440
2501
  dependencies: Optional[Dict[str, Any]] = None,
2441
2502
  metadata: Optional[Dict[str, Any]] = None,
2503
+ output_schema: Optional[Type[BaseModel]] = None,
2442
2504
  yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
2443
2505
  yield_run_output: Optional[bool] = None,
2444
2506
  debug_mode: Optional[bool] = None,
@@ -2467,6 +2529,7 @@ class Agent:
2467
2529
  add_session_state_to_context: Optional[bool] = None,
2468
2530
  dependencies: Optional[Dict[str, Any]] = None,
2469
2531
  metadata: Optional[Dict[str, Any]] = None,
2532
+ output_schema: Optional[Type[BaseModel]] = None,
2470
2533
  yield_run_response: Optional[bool] = None, # To be deprecated: use yield_run_output instead
2471
2534
  yield_run_output: Optional[bool] = None,
2472
2535
  debug_mode: Optional[bool] = None,
@@ -2554,8 +2617,6 @@ class Agent:
2554
2617
  self.stream = self.stream or stream
2555
2618
  self.stream_events = self.stream_events or stream_events
2556
2619
 
2557
- # Prepare arguments for the model
2558
- response_format = self._get_response_format() if self.parser_model is None else None
2559
2620
  self.model = cast(Model, self.model)
2560
2621
 
2561
2622
  # Get knowledge filters
@@ -2570,6 +2631,10 @@ class Agent:
2570
2631
  else:
2571
2632
  merge_dictionaries(metadata, self.metadata)
2572
2633
 
2634
+ # Resolve output_schema parameter takes precedence, then fall back to self.output_schema
2635
+ if output_schema is None:
2636
+ output_schema = self.output_schema
2637
+
2573
2638
  # Initialize run context
2574
2639
  run_context = run_context or RunContext(
2575
2640
  run_id=run_id,
@@ -2579,7 +2644,13 @@ class Agent:
2579
2644
  dependencies=dependencies,
2580
2645
  knowledge_filters=knowledge_filters,
2581
2646
  metadata=metadata,
2647
+ output_schema=output_schema,
2582
2648
  )
2649
+ # output_schema parameter takes priority, even if run_context was provided
2650
+ run_context.output_schema = output_schema
2651
+
2652
+ # Prepare arguments for the model (must be after run_context is fully initialized)
2653
+ response_format = self._get_response_format(run_context=run_context) if self.parser_model is None else None
2583
2654
 
2584
2655
  # If no retries are set, use the agent's default retries
2585
2656
  retries = retries if retries is not None else self.retries
@@ -2866,7 +2937,7 @@ class Agent:
2866
2937
 
2867
2938
  # Prepare arguments for the model
2868
2939
  self._set_default_model()
2869
- response_format = self._get_response_format()
2940
+ response_format = self._get_response_format(run_context=run_context)
2870
2941
  self.model = cast(Model, self.model)
2871
2942
 
2872
2943
  processed_tools = self.get_tools(
@@ -3023,7 +3094,7 @@ class Agent:
3023
3094
  return self._handle_agent_run_paused(run_response=run_response, session=session, user_id=user_id)
3024
3095
 
3025
3096
  # 4. Convert the response to the structured format if needed
3026
- self._convert_response_to_structured_format(run_response)
3097
+ self._convert_response_to_structured_format(run_response, run_context=run_context)
3027
3098
 
3028
3099
  # 5. Store media if enabled
3029
3100
  if self.store_media:
@@ -3134,6 +3205,7 @@ class Agent:
3134
3205
  response_format=response_format,
3135
3206
  stream_events=stream_events,
3136
3207
  session_state=run_context.session_state,
3208
+ run_context=run_context,
3137
3209
  ):
3138
3210
  yield event
3139
3211
 
@@ -3167,6 +3239,7 @@ class Agent:
3167
3239
  run_context=run_context,
3168
3240
  user_id=user_id,
3169
3241
  debug_mode=debug_mode,
3242
+ stream_events=stream_events,
3170
3243
  **kwargs,
3171
3244
  )
3172
3245
 
@@ -3388,7 +3461,7 @@ class Agent:
3388
3461
  merge_dictionaries(metadata, self.metadata)
3389
3462
 
3390
3463
  # Prepare arguments for the model
3391
- response_format = self._get_response_format()
3464
+ response_format = self._get_response_format(run_context=run_context)
3392
3465
  self.model = cast(Model, self.model)
3393
3466
 
3394
3467
  # Initialize run context
@@ -3585,13 +3658,16 @@ class Agent:
3585
3658
  await self._agenerate_response_with_output_model(model_response=model_response, run_messages=run_messages)
3586
3659
 
3587
3660
  # If a parser model is provided, structure the response separately
3588
- await self._aparse_response_with_parser_model(model_response=model_response, run_messages=run_messages)
3661
+ await self._aparse_response_with_parser_model(
3662
+ model_response=model_response, run_messages=run_messages, run_context=run_context
3663
+ )
3589
3664
 
3590
3665
  # 9. Update the RunOutput with the model response
3591
3666
  self._update_run_response(
3592
3667
  model_response=model_response,
3593
3668
  run_response=run_response,
3594
3669
  run_messages=run_messages,
3670
+ run_context=run_context,
3595
3671
  )
3596
3672
 
3597
3673
  # Break out of the run function if a tool call is paused
@@ -3601,7 +3677,7 @@ class Agent:
3601
3677
  )
3602
3678
 
3603
3679
  # 10. Convert the response to the structured format if needed
3604
- self._convert_response_to_structured_format(run_response)
3680
+ self._convert_response_to_structured_format(run_response, run_context=run_context)
3605
3681
 
3606
3682
  # 11. Store media if enabled
3607
3683
  if self.store_media:
@@ -3797,6 +3873,7 @@ class Agent:
3797
3873
  tools=_tools,
3798
3874
  response_format=response_format,
3799
3875
  stream_events=stream_events,
3876
+ run_context=run_context,
3800
3877
  ):
3801
3878
  raise_if_cancelled(run_response.run_id) # type: ignore
3802
3879
  yield event
@@ -3813,6 +3890,7 @@ class Agent:
3813
3890
  tools=_tools,
3814
3891
  response_format=response_format,
3815
3892
  stream_events=stream_events,
3893
+ run_context=run_context,
3816
3894
  ):
3817
3895
  raise_if_cancelled(run_response.run_id) # type: ignore
3818
3896
  if isinstance(event, RunContentEvent):
@@ -3839,7 +3917,7 @@ class Agent:
3839
3917
 
3840
3918
  # Parse response with parser model if provided
3841
3919
  async for event in self._aparse_response_with_parser_model_stream(
3842
- session=agent_session, run_response=run_response, stream_events=stream_events
3920
+ session=agent_session, run_response=run_response, stream_events=stream_events, run_context=run_context
3843
3921
  ):
3844
3922
  yield event
3845
3923
 
@@ -3869,6 +3947,7 @@ class Agent:
3869
3947
  session=agent_session,
3870
3948
  user_id=user_id,
3871
3949
  debug_mode=debug_mode,
3950
+ stream_events=stream_events,
3872
3951
  **kwargs,
3873
3952
  ):
3874
3953
  yield event
@@ -3969,6 +4048,7 @@ class Agent:
3969
4048
  run_context: RunContext,
3970
4049
  user_id: Optional[str] = None,
3971
4050
  debug_mode: Optional[bool] = None,
4051
+ stream_events: bool = False,
3972
4052
  **kwargs: Any,
3973
4053
  ) -> Iterator[RunOutputEvent]:
3974
4054
  """Execute multiple pre-hook functions in succession."""
@@ -3990,25 +4070,10 @@ class Agent:
3990
4070
  all_args.update(kwargs)
3991
4071
 
3992
4072
  for i, hook in enumerate(hooks):
3993
- yield handle_event( # type: ignore
3994
- run_response=run_response,
3995
- event=create_pre_hook_started_event(
3996
- from_run_response=run_response,
3997
- run_input=run_input,
3998
- pre_hook_name=hook.__name__,
3999
- ),
4000
- events_to_skip=self.events_to_skip, # type: ignore
4001
- store_events=self.store_events,
4002
- )
4003
- try:
4004
- # Filter arguments to only include those that the hook accepts
4005
- filtered_args = filter_hook_args(hook, all_args)
4006
-
4007
- hook(**filtered_args)
4008
-
4073
+ if stream_events:
4009
4074
  yield handle_event( # type: ignore
4010
4075
  run_response=run_response,
4011
- event=create_pre_hook_completed_event(
4076
+ event=create_pre_hook_started_event(
4012
4077
  from_run_response=run_response,
4013
4078
  run_input=run_input,
4014
4079
  pre_hook_name=hook.__name__,
@@ -4016,6 +4081,23 @@ class Agent:
4016
4081
  events_to_skip=self.events_to_skip, # type: ignore
4017
4082
  store_events=self.store_events,
4018
4083
  )
4084
+ try:
4085
+ # Filter arguments to only include those that the hook accepts
4086
+ filtered_args = filter_hook_args(hook, all_args)
4087
+
4088
+ hook(**filtered_args)
4089
+
4090
+ if stream_events:
4091
+ yield handle_event( # type: ignore
4092
+ run_response=run_response,
4093
+ event=create_pre_hook_completed_event(
4094
+ from_run_response=run_response,
4095
+ run_input=run_input,
4096
+ pre_hook_name=hook.__name__,
4097
+ ),
4098
+ events_to_skip=self.events_to_skip, # type: ignore
4099
+ store_events=self.store_events,
4100
+ )
4019
4101
 
4020
4102
  except (InputCheckError, OutputCheckError) as e:
4021
4103
  raise e
@@ -4038,6 +4120,7 @@ class Agent:
4038
4120
  session: AgentSession,
4039
4121
  user_id: Optional[str] = None,
4040
4122
  debug_mode: Optional[bool] = None,
4123
+ stream_events: bool = False,
4041
4124
  **kwargs: Any,
4042
4125
  ) -> AsyncIterator[RunOutputEvent]:
4043
4126
  """Execute multiple pre-hook functions in succession (async version)."""
@@ -4059,16 +4142,17 @@ class Agent:
4059
4142
  all_args.update(kwargs)
4060
4143
 
4061
4144
  for i, hook in enumerate(hooks):
4062
- yield handle_event( # type: ignore
4063
- run_response=run_response,
4064
- event=create_pre_hook_started_event(
4065
- from_run_response=run_response,
4066
- run_input=run_input,
4067
- pre_hook_name=hook.__name__,
4068
- ),
4069
- events_to_skip=self.events_to_skip, # type: ignore
4070
- store_events=self.store_events,
4071
- )
4145
+ if stream_events:
4146
+ yield handle_event( # type: ignore
4147
+ run_response=run_response,
4148
+ event=create_pre_hook_started_event(
4149
+ from_run_response=run_response,
4150
+ run_input=run_input,
4151
+ pre_hook_name=hook.__name__,
4152
+ ),
4153
+ events_to_skip=self.events_to_skip, # type: ignore
4154
+ store_events=self.store_events,
4155
+ )
4072
4156
  try:
4073
4157
  # Filter arguments to only include those that the hook accepts
4074
4158
  filtered_args = filter_hook_args(hook, all_args)
@@ -4079,16 +4163,17 @@ class Agent:
4079
4163
  # Synchronous function
4080
4164
  hook(**filtered_args)
4081
4165
 
4082
- yield handle_event( # type: ignore
4083
- run_response=run_response,
4084
- event=create_pre_hook_completed_event(
4085
- from_run_response=run_response,
4086
- run_input=run_input,
4087
- pre_hook_name=hook.__name__,
4088
- ),
4089
- events_to_skip=self.events_to_skip, # type: ignore
4090
- store_events=self.store_events,
4091
- )
4166
+ if stream_events:
4167
+ yield handle_event( # type: ignore
4168
+ run_response=run_response,
4169
+ event=create_pre_hook_completed_event(
4170
+ from_run_response=run_response,
4171
+ run_input=run_input,
4172
+ pre_hook_name=hook.__name__,
4173
+ ),
4174
+ events_to_skip=self.events_to_skip, # type: ignore
4175
+ store_events=self.store_events,
4176
+ )
4092
4177
 
4093
4178
  except (InputCheckError, OutputCheckError) as e:
4094
4179
  raise e
@@ -4110,6 +4195,7 @@ class Agent:
4110
4195
  run_context: RunContext,
4111
4196
  user_id: Optional[str] = None,
4112
4197
  debug_mode: Optional[bool] = None,
4198
+ stream_events: bool = False,
4113
4199
  **kwargs: Any,
4114
4200
  ) -> Iterator[RunOutputEvent]:
4115
4201
  """Execute multiple post-hook functions in succession."""
@@ -4131,30 +4217,32 @@ class Agent:
4131
4217
  all_args.update(kwargs)
4132
4218
 
4133
4219
  for i, hook in enumerate(hooks):
4134
- yield handle_event( # type: ignore
4135
- run_response=run_output,
4136
- event=create_post_hook_started_event(
4137
- from_run_response=run_output,
4138
- post_hook_name=hook.__name__,
4139
- ),
4140
- events_to_skip=self.events_to_skip, # type: ignore
4141
- store_events=self.store_events,
4142
- )
4143
- try:
4144
- # Filter arguments to only include those that the hook accepts
4145
- filtered_args = filter_hook_args(hook, all_args)
4146
-
4147
- hook(**filtered_args)
4148
-
4220
+ if stream_events:
4149
4221
  yield handle_event( # type: ignore
4150
4222
  run_response=run_output,
4151
- event=create_post_hook_completed_event(
4223
+ event=create_post_hook_started_event(
4152
4224
  from_run_response=run_output,
4153
4225
  post_hook_name=hook.__name__,
4154
4226
  ),
4155
4227
  events_to_skip=self.events_to_skip, # type: ignore
4156
4228
  store_events=self.store_events,
4157
4229
  )
4230
+ try:
4231
+ # Filter arguments to only include those that the hook accepts
4232
+ filtered_args = filter_hook_args(hook, all_args)
4233
+
4234
+ hook(**filtered_args)
4235
+
4236
+ if stream_events:
4237
+ yield handle_event( # type: ignore
4238
+ run_response=run_output,
4239
+ event=create_post_hook_completed_event(
4240
+ from_run_response=run_output,
4241
+ post_hook_name=hook.__name__,
4242
+ ),
4243
+ events_to_skip=self.events_to_skip, # type: ignore
4244
+ store_events=self.store_events,
4245
+ )
4158
4246
  except (InputCheckError, OutputCheckError) as e:
4159
4247
  raise e
4160
4248
  except Exception as e:
@@ -4172,6 +4260,7 @@ class Agent:
4172
4260
  session: AgentSession,
4173
4261
  user_id: Optional[str] = None,
4174
4262
  debug_mode: Optional[bool] = None,
4263
+ stream_events: bool = False,
4175
4264
  **kwargs: Any,
4176
4265
  ) -> AsyncIterator[RunOutputEvent]:
4177
4266
  """Execute multiple post-hook functions in succession (async version)."""
@@ -4193,15 +4282,16 @@ class Agent:
4193
4282
  all_args.update(kwargs)
4194
4283
 
4195
4284
  for i, hook in enumerate(hooks):
4196
- yield handle_event( # type: ignore
4197
- run_response=run_output,
4198
- event=create_post_hook_started_event(
4199
- from_run_response=run_output,
4200
- post_hook_name=hook.__name__,
4201
- ),
4202
- events_to_skip=self.events_to_skip, # type: ignore
4203
- store_events=self.store_events,
4204
- )
4285
+ if stream_events:
4286
+ yield handle_event( # type: ignore
4287
+ run_response=run_output,
4288
+ event=create_post_hook_started_event(
4289
+ from_run_response=run_output,
4290
+ post_hook_name=hook.__name__,
4291
+ ),
4292
+ events_to_skip=self.events_to_skip, # type: ignore
4293
+ store_events=self.store_events,
4294
+ )
4205
4295
  try:
4206
4296
  # Filter arguments to only include those that the hook accepts
4207
4297
  filtered_args = filter_hook_args(hook, all_args)
@@ -4212,15 +4302,16 @@ class Agent:
4212
4302
  else:
4213
4303
  hook(**filtered_args)
4214
4304
 
4215
- yield handle_event( # type: ignore
4216
- run_response=run_output,
4217
- event=create_post_hook_completed_event(
4218
- from_run_response=run_output,
4219
- post_hook_name=hook.__name__,
4220
- ),
4221
- events_to_skip=self.events_to_skip, # type: ignore
4222
- store_events=self.store_events,
4223
- )
4305
+ if stream_events:
4306
+ yield handle_event( # type: ignore
4307
+ run_response=run_output,
4308
+ event=create_post_hook_completed_event(
4309
+ from_run_response=run_output,
4310
+ post_hook_name=hook.__name__,
4311
+ ),
4312
+ events_to_skip=self.events_to_skip, # type: ignore
4313
+ store_events=self.store_events,
4314
+ )
4224
4315
 
4225
4316
  except (InputCheckError, OutputCheckError) as e:
4226
4317
  raise e
@@ -4327,18 +4418,23 @@ class Agent:
4327
4418
 
4328
4419
  log_debug(f"Agent Run Paused: {run_response.run_id}", center=True, symbol="*")
4329
4420
 
4330
- def _convert_response_to_structured_format(self, run_response: Union[RunOutput, ModelResponse]):
4421
+ def _convert_response_to_structured_format(
4422
+ self, run_response: Union[RunOutput, ModelResponse], run_context: Optional[RunContext] = None
4423
+ ):
4424
+ # Get output_schema from run_context
4425
+ output_schema = run_context.output_schema if run_context else None
4426
+
4331
4427
  # Convert the response to the structured format if needed
4332
- if self.output_schema is not None and not isinstance(run_response.content, self.output_schema):
4428
+ if output_schema is not None and not isinstance(run_response.content, output_schema):
4333
4429
  if isinstance(run_response.content, str) and self.parse_response:
4334
4430
  try:
4335
- structured_output = parse_response_model_str(run_response.content, self.output_schema)
4431
+ structured_output = parse_response_model_str(run_response.content, output_schema)
4336
4432
 
4337
4433
  # Update RunOutput
4338
4434
  if structured_output is not None:
4339
4435
  run_response.content = structured_output
4340
4436
  if isinstance(run_response, RunOutput):
4341
- run_response.content_type = self.output_schema.__name__
4437
+ run_response.content_type = output_schema.__name__
4342
4438
  else:
4343
4439
  log_warning("Failed to convert response to output_schema")
4344
4440
  except Exception as e:
@@ -4681,15 +4777,19 @@ class Agent:
4681
4777
  model_response: ModelResponse,
4682
4778
  run_response: RunOutput,
4683
4779
  run_messages: RunMessages,
4780
+ run_context: Optional[RunContext] = None,
4684
4781
  ):
4782
+ # Get output_schema from run_context
4783
+ output_schema = run_context.output_schema if run_context else None
4784
+
4685
4785
  # Handle structured outputs
4686
- if self.output_schema is not None and model_response.parsed is not None:
4786
+ if output_schema is not None and model_response.parsed is not None:
4687
4787
  # We get native structured outputs from the model
4688
- if self._model_should_return_structured_output():
4788
+ if self._model_should_return_structured_output(run_context=run_context):
4689
4789
  # Update the run_response content with the structured output
4690
4790
  run_response.content = model_response.parsed
4691
4791
  # Update the run_response content_type with the structured output class name
4692
- run_response.content_type = self.output_schema.__name__
4792
+ run_response.content_type = output_schema.__name__
4693
4793
  else:
4694
4794
  # Update the run_response content with the model response content
4695
4795
  run_response.content = model_response.content
@@ -4762,6 +4862,7 @@ class Agent:
4762
4862
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
4763
4863
  stream_events: bool = False,
4764
4864
  session_state: Optional[Dict[str, Any]] = None,
4865
+ run_context: Optional[RunContext] = None,
4765
4866
  ) -> Iterator[RunOutputEvent]:
4766
4867
  self.model = cast(Model, self.model)
4767
4868
 
@@ -4771,8 +4872,12 @@ class Agent:
4771
4872
  }
4772
4873
  model_response = ModelResponse(content="")
4773
4874
 
4875
+ # Get output_schema from run_context
4876
+ output_schema = run_context.output_schema if run_context else None
4877
+ should_parse_structured_output = output_schema is not None and self.parse_response and self.parser_model is None
4878
+
4774
4879
  stream_model_response = True
4775
- if self.should_parse_structured_output:
4880
+ if should_parse_structured_output:
4776
4881
  log_debug("Response model set, model response is not streamed.")
4777
4882
  stream_model_response = False
4778
4883
 
@@ -4785,6 +4890,7 @@ class Agent:
4785
4890
  stream_model_response=stream_model_response,
4786
4891
  run_response=run_response,
4787
4892
  send_media_to_model=self.send_media_to_model,
4893
+ compression_manager=self.compression_manager if self.compress_tool_results else None,
4788
4894
  ):
4789
4895
  yield from self._handle_model_response_chunk(
4790
4896
  session=session,
@@ -4792,9 +4898,10 @@ class Agent:
4792
4898
  model_response=model_response,
4793
4899
  model_response_event=model_response_event,
4794
4900
  reasoning_state=reasoning_state,
4795
- parse_structured_output=self.should_parse_structured_output,
4901
+ parse_structured_output=should_parse_structured_output,
4796
4902
  stream_events=stream_events,
4797
4903
  session_state=session_state,
4904
+ run_context=run_context,
4798
4905
  )
4799
4906
 
4800
4907
  # Determine reasoning completed
@@ -4842,6 +4949,7 @@ class Agent:
4842
4949
  response_format: Optional[Union[Dict, Type[BaseModel]]] = None,
4843
4950
  stream_events: bool = False,
4844
4951
  session_state: Optional[Dict[str, Any]] = None,
4952
+ run_context: Optional[RunContext] = None,
4845
4953
  ) -> AsyncIterator[RunOutputEvent]:
4846
4954
  self.model = cast(Model, self.model)
4847
4955
 
@@ -4851,8 +4959,12 @@ class Agent:
4851
4959
  }
4852
4960
  model_response = ModelResponse(content="")
4853
4961
 
4962
+ # Get output_schema from run_context
4963
+ output_schema = run_context.output_schema if run_context else None
4964
+ should_parse_structured_output = output_schema is not None and self.parse_response and self.parser_model is None
4965
+
4854
4966
  stream_model_response = True
4855
- if self.should_parse_structured_output:
4967
+ if should_parse_structured_output:
4856
4968
  log_debug("Response model set, model response is not streamed.")
4857
4969
  stream_model_response = False
4858
4970
 
@@ -4865,6 +4977,7 @@ class Agent:
4865
4977
  stream_model_response=stream_model_response,
4866
4978
  run_response=run_response,
4867
4979
  send_media_to_model=self.send_media_to_model,
4980
+ compression_manager=self.compression_manager if self.compress_tool_results else None,
4868
4981
  ) # type: ignore
4869
4982
 
4870
4983
  async for model_response_event in model_response_stream: # type: ignore
@@ -4874,9 +4987,10 @@ class Agent:
4874
4987
  model_response=model_response,
4875
4988
  model_response_event=model_response_event,
4876
4989
  reasoning_state=reasoning_state,
4877
- parse_structured_output=self.should_parse_structured_output,
4990
+ parse_structured_output=should_parse_structured_output,
4878
4991
  stream_events=stream_events,
4879
4992
  session_state=session_state,
4993
+ run_context=run_context,
4880
4994
  ):
4881
4995
  yield event
4882
4996
 
@@ -4925,6 +5039,7 @@ class Agent:
4925
5039
  parse_structured_output: bool = False,
4926
5040
  stream_events: bool = False,
4927
5041
  session_state: Optional[Dict[str, Any]] = None,
5042
+ run_context: Optional[RunContext] = None,
4928
5043
  ) -> Iterator[RunOutputEvent]:
4929
5044
  from agno.run.workflow import WorkflowRunOutputEvent
4930
5045
 
@@ -4956,9 +5071,11 @@ class Agent:
4956
5071
  if model_response_event.content is not None:
4957
5072
  if parse_structured_output:
4958
5073
  model_response.content = model_response_event.content
4959
- self._convert_response_to_structured_format(model_response)
5074
+ self._convert_response_to_structured_format(model_response, run_context=run_context)
4960
5075
 
4961
- content_type = self.output_schema.__name__ # type: ignore
5076
+ # Get output_schema from run_context
5077
+ output_schema = run_context.output_schema if run_context else None
5078
+ content_type = output_schema.__name__ # type: ignore
4962
5079
  run_response.content = model_response.content
4963
5080
  run_response.content_type = content_type
4964
5081
  else:
@@ -5472,7 +5589,12 @@ class Agent:
5472
5589
  # Add provided tools
5473
5590
  if self.tools is not None:
5474
5591
  for tool in self.tools:
5475
- if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"]:
5592
+ # Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
5593
+ is_mcp_tool = hasattr(type(tool), "__mro__") and any(
5594
+ c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
5595
+ )
5596
+
5597
+ if is_mcp_tool:
5476
5598
  if tool.refresh_connection: # type: ignore
5477
5599
  try:
5478
5600
  is_alive = await tool.is_alive() # type: ignore
@@ -5492,9 +5614,8 @@ class Agent:
5492
5614
  if check_mcp_tools and not tool.initialized: # type: ignore
5493
5615
  continue
5494
5616
 
5495
- agent_tools.append(tool)
5496
- else:
5497
- agent_tools.append(tool)
5617
+ # Add the tool (MCP tools that passed checks, or any non-MCP tool)
5618
+ agent_tools.append(tool)
5498
5619
 
5499
5620
  # Add tools for accessing memory
5500
5621
  if self.read_chat_history:
@@ -5556,10 +5677,13 @@ class Agent:
5556
5677
  if processed_tools is not None and len(processed_tools) > 0:
5557
5678
  log_debug("Processing tools for model")
5558
5679
 
5680
+ # Get output_schema from run_context
5681
+ output_schema = run_context.output_schema if run_context else None
5682
+
5559
5683
  # Check if we need strict mode for the functions for the model
5560
5684
  strict = False
5561
5685
  if (
5562
- self.output_schema is not None
5686
+ output_schema is not None
5563
5687
  and (self.structured_outputs or (not self.use_json_mode))
5564
5688
  and model.supports_native_structured_outputs
5565
5689
  ):
@@ -5661,17 +5785,25 @@ class Agent:
5661
5785
 
5662
5786
  return _functions
5663
5787
 
5664
- def _model_should_return_structured_output(self):
5788
+ def _model_should_return_structured_output(self, run_context: Optional[RunContext] = None):
5789
+ # Get output_schema from run_context
5790
+ output_schema = run_context.output_schema if run_context else None
5791
+
5665
5792
  self.model = cast(Model, self.model)
5666
5793
  return bool(
5667
5794
  self.model.supports_native_structured_outputs
5668
- and self.output_schema is not None
5795
+ and output_schema is not None
5669
5796
  and (not self.use_json_mode or self.structured_outputs)
5670
5797
  )
5671
5798
 
5672
- def _get_response_format(self, model: Optional[Model] = None) -> Optional[Union[Dict, Type[BaseModel]]]:
5799
+ def _get_response_format(
5800
+ self, model: Optional[Model] = None, run_context: Optional[RunContext] = None
5801
+ ) -> Optional[Union[Dict, Type[BaseModel]]]:
5802
+ # Get output_schema from run_context
5803
+ output_schema = run_context.output_schema if run_context else None
5804
+
5673
5805
  model = cast(Model, model or self.model)
5674
- if self.output_schema is None:
5806
+ if output_schema is None:
5675
5807
  return None
5676
5808
  else:
5677
5809
  json_response_format = {"type": "json_object"}
@@ -5679,7 +5811,7 @@ class Agent:
5679
5811
  if model.supports_native_structured_outputs:
5680
5812
  if not self.use_json_mode or self.structured_outputs:
5681
5813
  log_debug("Setting Model.response_format to Agent.output_schema")
5682
- return self.output_schema
5814
+ return output_schema
5683
5815
  else:
5684
5816
  log_debug(
5685
5817
  "Model supports native structured outputs but it is not enabled. Using JSON mode instead."
@@ -5692,8 +5824,8 @@ class Agent:
5692
5824
  return {
5693
5825
  "type": "json_schema",
5694
5826
  "json_schema": {
5695
- "name": self.output_schema.__name__,
5696
- "schema": self.output_schema.model_json_schema(),
5827
+ "name": output_schema.__name__,
5828
+ "schema": output_schema.model_json_schema(),
5697
5829
  },
5698
5830
  }
5699
5831
  else:
@@ -6767,6 +6899,9 @@ class Agent:
6767
6899
  dependencies = run_context.dependencies or dependencies
6768
6900
  metadata = run_context.metadata or metadata
6769
6901
 
6902
+ # Get output_schema from run_context
6903
+ output_schema = run_context.output_schema if run_context else None
6904
+
6770
6905
  # 1. If the system_message is provided, use that.
6771
6906
  if self.system_message is not None:
6772
6907
  if isinstance(self.system_message, Message):
@@ -6776,7 +6911,9 @@ class Agent:
6776
6911
  if isinstance(self.system_message, str):
6777
6912
  sys_message_content = self.system_message
6778
6913
  elif callable(self.system_message):
6779
- sys_message_content = self.system_message(agent=self)
6914
+ sys_message_content = execute_system_message(
6915
+ agent=self, system_message=self.system_message, session_state=session_state, run_context=run_context
6916
+ )
6780
6917
  if not isinstance(sys_message_content, str):
6781
6918
  raise Exception("system_message must return a string")
6782
6919
 
@@ -6805,25 +6942,9 @@ class Agent:
6805
6942
  if self.instructions is not None:
6806
6943
  _instructions = self.instructions
6807
6944
  if callable(self.instructions):
6808
- import inspect
6809
-
6810
- signature = inspect.signature(self.instructions)
6811
- instruction_args: Dict[str, Any] = {}
6812
-
6813
- # Check for agent parameter
6814
- if "agent" in signature.parameters:
6815
- instruction_args["agent"] = self
6816
-
6817
- # Check for session_state parameter
6818
- if "session_state" in signature.parameters:
6819
- instruction_args["session_state"] = session_state or {}
6820
-
6821
- # Check for run_context parameter
6822
- if "run_context" in signature.parameters:
6823
- instruction_args["run_context"] = run_context or None
6824
-
6825
- # Run the instructions function
6826
- _instructions = self.instructions(**instruction_args)
6945
+ _instructions = execute_instructions(
6946
+ agent=self, instructions=self.instructions, session_state=session_state, run_context=run_context
6947
+ )
6827
6948
 
6828
6949
  if isinstance(_instructions, str):
6829
6950
  instructions.append(_instructions)
@@ -6838,7 +6959,7 @@ class Agent:
6838
6959
  # 3.2 Build a list of additional information for the system message
6839
6960
  additional_information: List[str] = []
6840
6961
  # 3.2.1 Add instructions for using markdown
6841
- if self.markdown and self.output_schema is None:
6962
+ if self.markdown and output_schema is None:
6842
6963
  additional_information.append("Use markdown to format your answers.")
6843
6964
  # 3.2.2 Add the current datetime
6844
6965
  if self.add_datetime_to_context:
@@ -7073,18 +7194,18 @@ class Agent:
7073
7194
  # 3.3.13 Add the JSON output prompt if output_schema is provided and the model does not support native structured outputs or JSON schema outputs
7074
7195
  # or if use_json_mode is True
7075
7196
  if (
7076
- self.output_schema is not None
7197
+ output_schema is not None
7077
7198
  and self.parser_model is None
7078
7199
  and not (
7079
7200
  (self.model.supports_native_structured_outputs or self.model.supports_json_schema_outputs)
7080
7201
  and (not self.use_json_mode or self.structured_outputs is True)
7081
7202
  )
7082
7203
  ):
7083
- system_message_content += f"{get_json_output_prompt(self.output_schema)}" # type: ignore
7204
+ system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
7084
7205
 
7085
7206
  # 3.3.14 Add the response model format prompt if output_schema is provided
7086
- if self.output_schema is not None and self.parser_model is not None:
7087
- system_message_content += f"{get_response_model_format_prompt(self.output_schema)}"
7207
+ if output_schema is not None and self.parser_model is not None:
7208
+ system_message_content += f"{get_response_model_format_prompt(output_schema)}"
7088
7209
 
7089
7210
  # 3.3.15 Add the session state to the system message
7090
7211
  if add_session_state_to_context and session_state is not None:
@@ -7121,6 +7242,9 @@ class Agent:
7121
7242
  dependencies = run_context.dependencies or dependencies
7122
7243
  metadata = run_context.metadata or metadata
7123
7244
 
7245
+ # Get output_schema from run_context
7246
+ output_schema = run_context.output_schema if run_context else None
7247
+
7124
7248
  # 1. If the system_message is provided, use that.
7125
7249
  if self.system_message is not None:
7126
7250
  if isinstance(self.system_message, Message):
@@ -7130,7 +7254,9 @@ class Agent:
7130
7254
  if isinstance(self.system_message, str):
7131
7255
  sys_message_content = self.system_message
7132
7256
  elif callable(self.system_message):
7133
- sys_message_content = self.system_message(agent=self)
7257
+ sys_message_content = await aexecute_system_message(
7258
+ agent=self, system_message=self.system_message, session_state=session_state, run_context=run_context
7259
+ )
7134
7260
  if not isinstance(sys_message_content, str):
7135
7261
  raise Exception("system_message must return a string")
7136
7262
 
@@ -7160,20 +7286,9 @@ class Agent:
7160
7286
  if self.instructions is not None:
7161
7287
  _instructions = self.instructions
7162
7288
  if callable(self.instructions):
7163
- import inspect
7164
-
7165
- signature = inspect.signature(self.instructions)
7166
- instruction_args: Dict[str, Any] = {}
7167
-
7168
- # Check for agent parameter
7169
- if "agent" in signature.parameters:
7170
- instruction_args["agent"] = self
7171
-
7172
- # Check for session_state parameter
7173
- if "session_state" in signature.parameters:
7174
- instruction_args["session_state"] = session_state or {}
7175
-
7176
- _instructions = self.instructions(**instruction_args)
7289
+ _instructions = await aexecute_instructions(
7290
+ agent=self, instructions=self.instructions, session_state=session_state, run_context=run_context
7291
+ )
7177
7292
 
7178
7293
  if isinstance(_instructions, str):
7179
7294
  instructions.append(_instructions)
@@ -7188,7 +7303,7 @@ class Agent:
7188
7303
  # 3.2 Build a list of additional information for the system message
7189
7304
  additional_information: List[str] = []
7190
7305
  # 3.2.1 Add instructions for using markdown
7191
- if self.markdown and self.output_schema is None:
7306
+ if self.markdown and output_schema is None:
7192
7307
  additional_information.append("Use markdown to format your answers.")
7193
7308
  # 3.2.2 Add the current datetime
7194
7309
  if self.add_datetime_to_context:
@@ -7426,18 +7541,18 @@ class Agent:
7426
7541
  # 3.3.13 Add the JSON output prompt if output_schema is provided and the model does not support native structured outputs or JSON schema outputs
7427
7542
  # or if use_json_mode is True
7428
7543
  if (
7429
- self.output_schema is not None
7544
+ output_schema is not None
7430
7545
  and self.parser_model is None
7431
7546
  and not (
7432
7547
  (self.model.supports_native_structured_outputs or self.model.supports_json_schema_outputs)
7433
7548
  and (not self.use_json_mode or self.structured_outputs is True)
7434
7549
  )
7435
7550
  ):
7436
- system_message_content += f"{get_json_output_prompt(self.output_schema)}" # type: ignore
7551
+ system_message_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
7437
7552
 
7438
7553
  # 3.3.14 Add the response model format prompt if output_schema is provided
7439
- if self.output_schema is not None and self.parser_model is not None:
7440
- system_message_content += f"{get_response_model_format_prompt(self.output_schema)}"
7554
+ if output_schema is not None and self.parser_model is not None:
7555
+ system_message_content += f"{get_response_model_format_prompt(output_schema)}"
7441
7556
 
7442
7557
  # 3.3.15 Add the session state to the system message
7443
7558
  if add_session_state_to_context and session_state is not None:
@@ -7627,6 +7742,180 @@ class Agent:
7627
7742
  **kwargs,
7628
7743
  )
7629
7744
 
7745
+ async def _aget_user_message(
7746
+ self,
7747
+ *,
7748
+ run_response: RunOutput,
7749
+ run_context: Optional[RunContext] = None,
7750
+ session_state: Optional[Dict[str, Any]] = None,
7751
+ dependencies: Optional[Dict[str, Any]] = None,
7752
+ metadata: Optional[Dict[str, Any]] = None,
7753
+ user_id: Optional[str] = None,
7754
+ input: Optional[Union[str, List, Dict, Message, BaseModel, List[Message]]] = None,
7755
+ audio: Optional[Sequence[Audio]] = None,
7756
+ images: Optional[Sequence[Image]] = None,
7757
+ videos: Optional[Sequence[Video]] = None,
7758
+ files: Optional[Sequence[File]] = None,
7759
+ add_dependencies_to_context: Optional[bool] = None,
7760
+ knowledge_filters: Optional[Union[Dict[str, Any], List[FilterExpr]]] = None,
7761
+ **kwargs: Any,
7762
+ ) -> Optional[Message]:
7763
+ """Return the user message for the Agent (async version).
7764
+
7765
+ 1. If the user_message is provided, use that.
7766
+ 2. If build_user_context is False or if the message is a list, return the message as is.
7767
+ 3. Build the default user message for the Agent
7768
+ """
7769
+ # Consider both run_context and session_state, dependencies, metadata, knowledge_filters (deprecated fields)
7770
+ if run_context is not None:
7771
+ session_state = run_context.session_state or session_state
7772
+ dependencies = run_context.dependencies or dependencies
7773
+ metadata = run_context.metadata or metadata
7774
+ knowledge_filters = run_context.knowledge_filters or knowledge_filters
7775
+ # Get references from the knowledge base to use in the user message
7776
+ references = None
7777
+
7778
+ # 1. If build_user_context is False or message is a list, return the message as is.
7779
+ if not self.build_user_context:
7780
+ return Message(
7781
+ role=self.user_message_role or "user",
7782
+ content=input, # type: ignore
7783
+ images=None if not self.send_media_to_model else images,
7784
+ audio=None if not self.send_media_to_model else audio,
7785
+ videos=None if not self.send_media_to_model else videos,
7786
+ files=None if not self.send_media_to_model else files,
7787
+ **kwargs,
7788
+ )
7789
+ # 2. Build the user message for the Agent
7790
+ elif input is None:
7791
+ # If we have any media, return a message with empty content
7792
+ if images is not None or audio is not None or videos is not None or files is not None:
7793
+ return Message(
7794
+ role=self.user_message_role or "user",
7795
+ content="",
7796
+ images=None if not self.send_media_to_model else images,
7797
+ audio=None if not self.send_media_to_model else audio,
7798
+ videos=None if not self.send_media_to_model else videos,
7799
+ files=None if not self.send_media_to_model else files,
7800
+ **kwargs,
7801
+ )
7802
+ else:
7803
+ # If the input is None, return None
7804
+ return None
7805
+
7806
+ else:
7807
+ # Handle list messages by converting to string
7808
+ if isinstance(input, list):
7809
+ # Convert list to string (join with newlines if all elements are strings)
7810
+ if all(isinstance(item, str) for item in input):
7811
+ message_content = "\n".join(input) # type: ignore
7812
+ else:
7813
+ message_content = str(input)
7814
+
7815
+ return Message(
7816
+ role=self.user_message_role,
7817
+ content=message_content,
7818
+ images=None if not self.send_media_to_model else images,
7819
+ audio=None if not self.send_media_to_model else audio,
7820
+ videos=None if not self.send_media_to_model else videos,
7821
+ files=None if not self.send_media_to_model else files,
7822
+ **kwargs,
7823
+ )
7824
+
7825
+ # If message is provided as a Message, use it directly
7826
+ elif isinstance(input, Message):
7827
+ return input
7828
+ # If message is provided as a dict, try to validate it as a Message
7829
+ elif isinstance(input, dict):
7830
+ try:
7831
+ return Message.model_validate(input)
7832
+ except Exception as e:
7833
+ log_warning(f"Failed to validate message: {e}")
7834
+ raise Exception(f"Failed to validate message: {e}")
7835
+
7836
+ # If message is provided as a BaseModel, convert it to a Message
7837
+ elif isinstance(input, BaseModel):
7838
+ try:
7839
+ # Create a user message with the BaseModel content
7840
+ content = input.model_dump_json(indent=2, exclude_none=True)
7841
+ return Message(role=self.user_message_role, content=content)
7842
+ except Exception as e:
7843
+ log_warning(f"Failed to convert BaseModel to message: {e}")
7844
+ raise Exception(f"Failed to convert BaseModel to message: {e}")
7845
+ else:
7846
+ user_msg_content = input
7847
+ if self.add_knowledge_to_context:
7848
+ if isinstance(input, str):
7849
+ user_msg_content = input
7850
+ elif callable(input):
7851
+ user_msg_content = input(agent=self)
7852
+ else:
7853
+ raise Exception("message must be a string or a callable when add_references is True")
7854
+
7855
+ try:
7856
+ retrieval_timer = Timer()
7857
+ retrieval_timer.start()
7858
+ docs_from_knowledge = await self.aget_relevant_docs_from_knowledge(
7859
+ query=user_msg_content, filters=knowledge_filters, **kwargs
7860
+ )
7861
+ if docs_from_knowledge is not None:
7862
+ references = MessageReferences(
7863
+ query=user_msg_content,
7864
+ references=docs_from_knowledge,
7865
+ time=round(retrieval_timer.elapsed, 4),
7866
+ )
7867
+ # Add the references to the run_response
7868
+ if run_response.references is None:
7869
+ run_response.references = []
7870
+ run_response.references.append(references)
7871
+ retrieval_timer.stop()
7872
+ log_debug(f"Time to get references: {retrieval_timer.elapsed:.4f}s")
7873
+ except Exception as e:
7874
+ log_warning(f"Failed to get references: {e}")
7875
+
7876
+ if self.resolve_in_context:
7877
+ user_msg_content = self._format_message_with_state_variables(
7878
+ user_msg_content,
7879
+ user_id=user_id,
7880
+ session_state=session_state,
7881
+ dependencies=dependencies,
7882
+ metadata=metadata,
7883
+ )
7884
+
7885
+ # Convert to string for concatenation operations
7886
+ user_msg_content_str = get_text_from_message(user_msg_content) if user_msg_content is not None else ""
7887
+
7888
+ # 4.1 Add knowledge references to user message
7889
+ if (
7890
+ self.add_knowledge_to_context
7891
+ and references is not None
7892
+ and references.references is not None
7893
+ and len(references.references) > 0
7894
+ ):
7895
+ user_msg_content_str += "\n\nUse the following references from the knowledge base if it helps:\n"
7896
+ user_msg_content_str += "<references>\n"
7897
+ user_msg_content_str += self._convert_documents_to_string(references.references) + "\n"
7898
+ user_msg_content_str += "</references>"
7899
+ # 4.2 Add context to user message
7900
+ if add_dependencies_to_context and dependencies is not None:
7901
+ user_msg_content_str += "\n\n<additional context>\n"
7902
+ user_msg_content_str += self._convert_dependencies_to_string(dependencies) + "\n"
7903
+ user_msg_content_str += "</additional context>"
7904
+
7905
+ # Use the string version for the final content
7906
+ user_msg_content = user_msg_content_str
7907
+
7908
+ # Return the user message
7909
+ return Message(
7910
+ role=self.user_message_role,
7911
+ content=user_msg_content,
7912
+ audio=None if not self.send_media_to_model else audio,
7913
+ images=None if not self.send_media_to_model else images,
7914
+ videos=None if not self.send_media_to_model else videos,
7915
+ files=None if not self.send_media_to_model else files,
7916
+ **kwargs,
7917
+ )
7918
+
7630
7919
  def _get_run_messages(
7631
7920
  self,
7632
7921
  *,
@@ -7976,7 +8265,7 @@ class Agent:
7976
8265
  )
7977
8266
  )
7978
8267
  ):
7979
- user_message = self._get_user_message(
8268
+ user_message = await self._aget_user_message(
7980
8269
  run_response=run_response,
7981
8270
  run_context=run_context,
7982
8271
  session_state=session_state,
@@ -8080,16 +8369,20 @@ class Agent:
8080
8369
  self,
8081
8370
  model_response: ModelResponse,
8082
8371
  response_format: Optional[Union[Dict, Type[BaseModel]]],
8372
+ run_context: Optional[RunContext] = None,
8083
8373
  ) -> List[Message]:
8084
8374
  """Get the messages for the parser model."""
8375
+ # Get output_schema from run_context
8376
+ output_schema = run_context.output_schema if run_context else None
8377
+
8085
8378
  system_content = (
8086
8379
  self.parser_model_prompt
8087
8380
  if self.parser_model_prompt is not None
8088
8381
  else "You are tasked with creating a structured output from the provided user message."
8089
8382
  )
8090
8383
 
8091
- if response_format == {"type": "json_object"} and self.output_schema is not None:
8092
- system_content += f"{get_json_output_prompt(self.output_schema)}" # type: ignore
8384
+ if response_format == {"type": "json_object"} and output_schema is not None:
8385
+ system_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
8093
8386
 
8094
8387
  return [
8095
8388
  Message(role="system", content=system_content),
@@ -8100,16 +8393,20 @@ class Agent:
8100
8393
  self,
8101
8394
  run_response: RunOutput,
8102
8395
  response_format: Optional[Union[Dict, Type[BaseModel]]],
8396
+ run_context: Optional[RunContext] = None,
8103
8397
  ) -> List[Message]:
8104
8398
  """Get the messages for the parser model."""
8399
+ # Get output_schema from run_context
8400
+ output_schema = run_context.output_schema if run_context else None
8401
+
8105
8402
  system_content = (
8106
8403
  self.parser_model_prompt
8107
8404
  if self.parser_model_prompt is not None
8108
8405
  else "You are tasked with creating a structured output from the provided data."
8109
8406
  )
8110
8407
 
8111
- if response_format == {"type": "json_object"} and self.output_schema is not None:
8112
- system_content += f"{get_json_output_prompt(self.output_schema)}" # type: ignore
8408
+ if response_format == {"type": "json_object"} and output_schema is not None:
8409
+ system_content += f"{get_json_output_prompt(output_schema)}" # type: ignore
8113
8410
 
8114
8411
  return [
8115
8412
  Message(role="system", content=system_content),
@@ -9148,14 +9445,21 @@ class Agent:
9148
9445
  else:
9149
9446
  log_warning("Unable to parse response with parser model")
9150
9447
 
9151
- def _parse_response_with_parser_model(self, model_response: ModelResponse, run_messages: RunMessages) -> None:
9448
+ def _parse_response_with_parser_model(
9449
+ self, model_response: ModelResponse, run_messages: RunMessages, run_context: Optional[RunContext] = None
9450
+ ) -> None:
9152
9451
  """Parse the model response using the parser model."""
9153
9452
  if self.parser_model is None:
9154
9453
  return
9155
9454
 
9156
- if self.output_schema is not None:
9157
- parser_response_format = self._get_response_format(self.parser_model)
9158
- messages_for_parser_model = self._get_messages_for_parser_model(model_response, parser_response_format)
9455
+ # Get output_schema from run_context
9456
+ output_schema = run_context.output_schema if run_context else None
9457
+
9458
+ if output_schema is not None:
9459
+ parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
9460
+ messages_for_parser_model = self._get_messages_for_parser_model(
9461
+ model_response, parser_response_format, run_context=run_context
9462
+ )
9159
9463
  parser_model_response: ModelResponse = self.parser_model.response(
9160
9464
  messages=messages_for_parser_model,
9161
9465
  response_format=parser_response_format,
@@ -9170,15 +9474,20 @@ class Agent:
9170
9474
  log_warning("A response model is required to parse the response with a parser model")
9171
9475
 
9172
9476
  async def _aparse_response_with_parser_model(
9173
- self, model_response: ModelResponse, run_messages: RunMessages
9477
+ self, model_response: ModelResponse, run_messages: RunMessages, run_context: Optional[RunContext] = None
9174
9478
  ) -> None:
9175
9479
  """Parse the model response using the parser model."""
9176
9480
  if self.parser_model is None:
9177
9481
  return
9178
9482
 
9179
- if self.output_schema is not None:
9180
- parser_response_format = self._get_response_format(self.parser_model)
9181
- messages_for_parser_model = self._get_messages_for_parser_model(model_response, parser_response_format)
9483
+ # Get output_schema from run_context
9484
+ output_schema = run_context.output_schema if run_context else None
9485
+
9486
+ if output_schema is not None:
9487
+ parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
9488
+ messages_for_parser_model = self._get_messages_for_parser_model(
9489
+ model_response, parser_response_format, run_context=run_context
9490
+ )
9182
9491
  parser_model_response: ModelResponse = await self.parser_model.aresponse(
9183
9492
  messages=messages_for_parser_model,
9184
9493
  response_format=parser_response_format,
@@ -9193,11 +9502,18 @@ class Agent:
9193
9502
  log_warning("A response model is required to parse the response with a parser model")
9194
9503
 
9195
9504
  def _parse_response_with_parser_model_stream(
9196
- self, session: AgentSession, run_response: RunOutput, stream_events: bool = True
9505
+ self,
9506
+ session: AgentSession,
9507
+ run_response: RunOutput,
9508
+ stream_events: bool = True,
9509
+ run_context: Optional[RunContext] = None,
9197
9510
  ):
9198
9511
  """Parse the model response using the parser model"""
9199
9512
  if self.parser_model is not None:
9200
- if self.output_schema is not None:
9513
+ # Get output_schema from run_context
9514
+ output_schema = run_context.output_schema if run_context else None
9515
+
9516
+ if output_schema is not None:
9201
9517
  if stream_events:
9202
9518
  yield handle_event(
9203
9519
  create_parser_model_response_started_event(run_response),
@@ -9207,9 +9523,9 @@ class Agent:
9207
9523
  )
9208
9524
 
9209
9525
  parser_model_response = ModelResponse(content="")
9210
- parser_response_format = self._get_response_format(self.parser_model)
9526
+ parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
9211
9527
  messages_for_parser_model = self._get_messages_for_parser_model_stream(
9212
- run_response, parser_response_format
9528
+ run_response, parser_response_format, run_context=run_context
9213
9529
  )
9214
9530
  for model_response_event in self.parser_model.response_stream(
9215
9531
  messages=messages_for_parser_model,
@@ -9223,6 +9539,7 @@ class Agent:
9223
9539
  model_response_event=model_response_event,
9224
9540
  parse_structured_output=True,
9225
9541
  stream_events=stream_events,
9542
+ run_context=run_context,
9226
9543
  )
9227
9544
 
9228
9545
  parser_model_response_message: Optional[Message] = None
@@ -9248,11 +9565,18 @@ class Agent:
9248
9565
  log_warning("A response model is required to parse the response with a parser model")
9249
9566
 
9250
9567
  async def _aparse_response_with_parser_model_stream(
9251
- self, session: AgentSession, run_response: RunOutput, stream_events: bool = True
9568
+ self,
9569
+ session: AgentSession,
9570
+ run_response: RunOutput,
9571
+ stream_events: bool = True,
9572
+ run_context: Optional[RunContext] = None,
9252
9573
  ):
9253
9574
  """Parse the model response using the parser model stream."""
9254
9575
  if self.parser_model is not None:
9255
- if self.output_schema is not None:
9576
+ # Get output_schema from run_context
9577
+ output_schema = run_context.output_schema if run_context else None
9578
+
9579
+ if output_schema is not None:
9256
9580
  if stream_events:
9257
9581
  yield handle_event(
9258
9582
  create_parser_model_response_started_event(run_response),
@@ -9262,9 +9586,9 @@ class Agent:
9262
9586
  )
9263
9587
 
9264
9588
  parser_model_response = ModelResponse(content="")
9265
- parser_response_format = self._get_response_format(self.parser_model)
9589
+ parser_response_format = self._get_response_format(self.parser_model, run_context=run_context)
9266
9590
  messages_for_parser_model = self._get_messages_for_parser_model_stream(
9267
- run_response, parser_response_format
9591
+ run_response, parser_response_format, run_context=run_context
9268
9592
  )
9269
9593
  model_response_stream = self.parser_model.aresponse_stream(
9270
9594
  messages=messages_for_parser_model,
@@ -9279,6 +9603,7 @@ class Agent:
9279
9603
  model_response_event=model_response_event,
9280
9604
  parse_structured_output=True,
9281
9605
  stream_events=stream_events,
9606
+ run_context=run_context,
9282
9607
  ):
9283
9608
  yield event
9284
9609
 
@@ -10351,7 +10676,10 @@ class Agent:
10351
10676
  for tool in self.tools:
10352
10677
  if isawaitable(tool):
10353
10678
  raise NotImplementedError("Use `acli_app` to use async tools.")
10354
- if tool.__class__.__name__ in ["MCPTools", "MultiMCPTools"]:
10679
+ # Alternate method of using isinstance(tool, (MCPTools, MultiMCPTools)) to avoid imports
10680
+ if hasattr(type(tool), "__mro__") and any(
10681
+ c.__name__ in ["MCPTools", "MultiMCPTools"] for c in type(tool).__mro__
10682
+ ):
10355
10683
  raise NotImplementedError("Use `acli_app` to use MCP tools.")
10356
10684
 
10357
10685
  if input: