openai-agents 0.2.6__py3-none-any.whl → 0.6.8__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.
- agents/__init__.py +105 -4
- agents/_debug.py +15 -4
- agents/_run_impl.py +1203 -96
- agents/agent.py +294 -21
- agents/apply_diff.py +329 -0
- agents/editor.py +47 -0
- agents/exceptions.py +35 -0
- agents/extensions/experimental/__init__.py +6 -0
- agents/extensions/experimental/codex/__init__.py +92 -0
- agents/extensions/experimental/codex/codex.py +89 -0
- agents/extensions/experimental/codex/codex_options.py +35 -0
- agents/extensions/experimental/codex/codex_tool.py +1142 -0
- agents/extensions/experimental/codex/events.py +162 -0
- agents/extensions/experimental/codex/exec.py +263 -0
- agents/extensions/experimental/codex/items.py +245 -0
- agents/extensions/experimental/codex/output_schema_file.py +50 -0
- agents/extensions/experimental/codex/payloads.py +31 -0
- agents/extensions/experimental/codex/thread.py +214 -0
- agents/extensions/experimental/codex/thread_options.py +54 -0
- agents/extensions/experimental/codex/turn_options.py +36 -0
- agents/extensions/handoff_filters.py +13 -1
- agents/extensions/memory/__init__.py +120 -0
- agents/extensions/memory/advanced_sqlite_session.py +1285 -0
- agents/extensions/memory/async_sqlite_session.py +239 -0
- agents/extensions/memory/dapr_session.py +423 -0
- agents/extensions/memory/encrypt_session.py +185 -0
- agents/extensions/memory/redis_session.py +261 -0
- agents/extensions/memory/sqlalchemy_session.py +334 -0
- agents/extensions/models/litellm_model.py +449 -36
- agents/extensions/models/litellm_provider.py +3 -1
- agents/function_schema.py +47 -5
- agents/guardrail.py +16 -2
- agents/{handoffs.py → handoffs/__init__.py} +89 -47
- agents/handoffs/history.py +268 -0
- agents/items.py +238 -13
- agents/lifecycle.py +75 -14
- agents/mcp/server.py +280 -37
- agents/mcp/util.py +24 -3
- agents/memory/__init__.py +22 -2
- agents/memory/openai_conversations_session.py +91 -0
- agents/memory/openai_responses_compaction_session.py +249 -0
- agents/memory/session.py +19 -261
- agents/memory/sqlite_session.py +275 -0
- agents/memory/util.py +20 -0
- agents/model_settings.py +18 -3
- agents/models/__init__.py +13 -0
- agents/models/chatcmpl_converter.py +303 -50
- agents/models/chatcmpl_helpers.py +63 -0
- agents/models/chatcmpl_stream_handler.py +290 -68
- agents/models/default_models.py +58 -0
- agents/models/interface.py +4 -0
- agents/models/openai_chatcompletions.py +103 -48
- agents/models/openai_provider.py +10 -4
- agents/models/openai_responses.py +167 -46
- agents/realtime/__init__.py +4 -0
- agents/realtime/_util.py +14 -3
- agents/realtime/agent.py +7 -0
- agents/realtime/audio_formats.py +53 -0
- agents/realtime/config.py +78 -10
- agents/realtime/events.py +18 -0
- agents/realtime/handoffs.py +2 -2
- agents/realtime/items.py +17 -1
- agents/realtime/model.py +13 -0
- agents/realtime/model_events.py +12 -0
- agents/realtime/model_inputs.py +18 -1
- agents/realtime/openai_realtime.py +700 -151
- agents/realtime/session.py +309 -32
- agents/repl.py +7 -3
- agents/result.py +197 -38
- agents/run.py +1053 -178
- agents/run_context.py +13 -2
- agents/stream_events.py +1 -0
- agents/strict_schema.py +14 -0
- agents/tool.py +413 -15
- agents/tool_context.py +22 -1
- agents/tool_guardrails.py +279 -0
- agents/tracing/__init__.py +2 -0
- agents/tracing/config.py +9 -0
- agents/tracing/create.py +4 -0
- agents/tracing/processor_interface.py +84 -11
- agents/tracing/processors.py +65 -54
- agents/tracing/provider.py +64 -7
- agents/tracing/spans.py +105 -0
- agents/tracing/traces.py +116 -16
- agents/usage.py +134 -12
- agents/util/_json.py +19 -1
- agents/util/_transforms.py +12 -2
- agents/voice/input.py +5 -4
- agents/voice/models/openai_stt.py +17 -9
- agents/voice/pipeline.py +2 -0
- agents/voice/pipeline_config.py +4 -0
- {openai_agents-0.2.6.dist-info → openai_agents-0.6.8.dist-info}/METADATA +44 -19
- openai_agents-0.6.8.dist-info/RECORD +134 -0
- {openai_agents-0.2.6.dist-info → openai_agents-0.6.8.dist-info}/WHEEL +1 -1
- openai_agents-0.2.6.dist-info/RECORD +0 -103
- {openai_agents-0.2.6.dist-info → openai_agents-0.6.8.dist-info}/licenses/LICENSE +0 -0
agents/agent.py
CHANGED
|
@@ -13,21 +13,38 @@ from typing_extensions import NotRequired, TypeAlias, TypedDict
|
|
|
13
13
|
from .agent_output import AgentOutputSchemaBase
|
|
14
14
|
from .guardrail import InputGuardrail, OutputGuardrail
|
|
15
15
|
from .handoffs import Handoff
|
|
16
|
-
from .items import ItemHelpers
|
|
17
16
|
from .logger import logger
|
|
18
17
|
from .mcp import MCPUtil
|
|
19
18
|
from .model_settings import ModelSettings
|
|
19
|
+
from .models.default_models import (
|
|
20
|
+
get_default_model_settings,
|
|
21
|
+
gpt_5_reasoning_settings_required,
|
|
22
|
+
is_gpt_5_default,
|
|
23
|
+
)
|
|
20
24
|
from .models.interface import Model
|
|
21
25
|
from .prompts import DynamicPromptFunction, Prompt, PromptUtil
|
|
22
26
|
from .run_context import RunContextWrapper, TContext
|
|
23
|
-
from .tool import
|
|
27
|
+
from .tool import (
|
|
28
|
+
FunctionTool,
|
|
29
|
+
FunctionToolResult,
|
|
30
|
+
Tool,
|
|
31
|
+
ToolErrorFunction,
|
|
32
|
+
default_tool_error_function,
|
|
33
|
+
function_tool,
|
|
34
|
+
)
|
|
35
|
+
from .tool_context import ToolContext
|
|
24
36
|
from .util import _transforms
|
|
25
37
|
from .util._types import MaybeAwaitable
|
|
26
38
|
|
|
27
39
|
if TYPE_CHECKING:
|
|
28
|
-
from .
|
|
40
|
+
from openai.types.responses.response_function_tool_call import ResponseFunctionToolCall
|
|
41
|
+
|
|
42
|
+
from .lifecycle import AgentHooks, RunHooks
|
|
29
43
|
from .mcp import MCPServer
|
|
30
|
-
from .
|
|
44
|
+
from .memory.session import Session
|
|
45
|
+
from .result import RunResult, RunResultStreaming
|
|
46
|
+
from .run import RunConfig
|
|
47
|
+
from .stream_events import StreamEvent
|
|
31
48
|
|
|
32
49
|
|
|
33
50
|
@dataclass
|
|
@@ -52,6 +69,19 @@ ToolsToFinalOutputFunction: TypeAlias = Callable[
|
|
|
52
69
|
"""
|
|
53
70
|
|
|
54
71
|
|
|
72
|
+
class AgentToolStreamEvent(TypedDict):
|
|
73
|
+
"""Streaming event emitted when an agent is invoked as a tool."""
|
|
74
|
+
|
|
75
|
+
event: StreamEvent
|
|
76
|
+
"""The streaming event from the nested agent run."""
|
|
77
|
+
|
|
78
|
+
agent: Agent[Any]
|
|
79
|
+
"""The nested agent emitting the event."""
|
|
80
|
+
|
|
81
|
+
tool_call: ResponseFunctionToolCall | None
|
|
82
|
+
"""The originating tool call, if available."""
|
|
83
|
+
|
|
84
|
+
|
|
55
85
|
class StopAtTools(TypedDict):
|
|
56
86
|
stop_at_tool_names: list[str]
|
|
57
87
|
"""A list of tool names, any of which will stop the agent from running further."""
|
|
@@ -168,10 +198,10 @@ class Agent(AgentBase, Generic[TContext]):
|
|
|
168
198
|
"""The model implementation to use when invoking the LLM.
|
|
169
199
|
|
|
170
200
|
By default, if not set, the agent will use the default model configured in
|
|
171
|
-
`
|
|
201
|
+
`agents.models.get_default_model()` (currently "gpt-4.1").
|
|
172
202
|
"""
|
|
173
203
|
|
|
174
|
-
model_settings: ModelSettings = field(default_factory=
|
|
204
|
+
model_settings: ModelSettings = field(default_factory=get_default_model_settings)
|
|
175
205
|
"""Configures model-specific tuning parameters (e.g. temperature, top_p).
|
|
176
206
|
"""
|
|
177
207
|
|
|
@@ -205,8 +235,9 @@ class Agent(AgentBase, Generic[TContext]):
|
|
|
205
235
|
This lets you configure how tool use is handled.
|
|
206
236
|
- "run_llm_again": The default behavior. Tools are run, and then the LLM receives the results
|
|
207
237
|
and gets to respond.
|
|
208
|
-
- "stop_on_first_tool": The output
|
|
209
|
-
|
|
238
|
+
- "stop_on_first_tool": The output from the first tool call is treated as the final result.
|
|
239
|
+
In other words, it isn’t sent back to the LLM for further processing but is used directly
|
|
240
|
+
as the final output.
|
|
210
241
|
- A StopAtTools object: The agent will stop running if any of the tools listed in
|
|
211
242
|
`stop_at_tool_names` is called.
|
|
212
243
|
The final output will be the output of the first matching tool call.
|
|
@@ -223,6 +254,139 @@ class Agent(AgentBase, Generic[TContext]):
|
|
|
223
254
|
"""Whether to reset the tool choice to the default value after a tool has been called. Defaults
|
|
224
255
|
to True. This ensures that the agent doesn't enter an infinite loop of tool usage."""
|
|
225
256
|
|
|
257
|
+
def __post_init__(self):
|
|
258
|
+
from typing import get_origin
|
|
259
|
+
|
|
260
|
+
if not isinstance(self.name, str):
|
|
261
|
+
raise TypeError(f"Agent name must be a string, got {type(self.name).__name__}")
|
|
262
|
+
|
|
263
|
+
if self.handoff_description is not None and not isinstance(self.handoff_description, str):
|
|
264
|
+
raise TypeError(
|
|
265
|
+
f"Agent handoff_description must be a string or None, "
|
|
266
|
+
f"got {type(self.handoff_description).__name__}"
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
if not isinstance(self.tools, list):
|
|
270
|
+
raise TypeError(f"Agent tools must be a list, got {type(self.tools).__name__}")
|
|
271
|
+
|
|
272
|
+
if not isinstance(self.mcp_servers, list):
|
|
273
|
+
raise TypeError(
|
|
274
|
+
f"Agent mcp_servers must be a list, got {type(self.mcp_servers).__name__}"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
if not isinstance(self.mcp_config, dict):
|
|
278
|
+
raise TypeError(
|
|
279
|
+
f"Agent mcp_config must be a dict, got {type(self.mcp_config).__name__}"
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
if (
|
|
283
|
+
self.instructions is not None
|
|
284
|
+
and not isinstance(self.instructions, str)
|
|
285
|
+
and not callable(self.instructions)
|
|
286
|
+
):
|
|
287
|
+
raise TypeError(
|
|
288
|
+
f"Agent instructions must be a string, callable, or None, "
|
|
289
|
+
f"got {type(self.instructions).__name__}"
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
self.prompt is not None
|
|
294
|
+
and not callable(self.prompt)
|
|
295
|
+
and not hasattr(self.prompt, "get")
|
|
296
|
+
):
|
|
297
|
+
raise TypeError(
|
|
298
|
+
f"Agent prompt must be a Prompt, DynamicPromptFunction, or None, "
|
|
299
|
+
f"got {type(self.prompt).__name__}"
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
if not isinstance(self.handoffs, list):
|
|
303
|
+
raise TypeError(f"Agent handoffs must be a list, got {type(self.handoffs).__name__}")
|
|
304
|
+
|
|
305
|
+
if self.model is not None and not isinstance(self.model, str):
|
|
306
|
+
from .models.interface import Model
|
|
307
|
+
|
|
308
|
+
if not isinstance(self.model, Model):
|
|
309
|
+
raise TypeError(
|
|
310
|
+
f"Agent model must be a string, Model, or None, got {type(self.model).__name__}"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
if not isinstance(self.model_settings, ModelSettings):
|
|
314
|
+
raise TypeError(
|
|
315
|
+
f"Agent model_settings must be a ModelSettings instance, "
|
|
316
|
+
f"got {type(self.model_settings).__name__}"
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
if (
|
|
320
|
+
# The user sets a non-default model
|
|
321
|
+
self.model is not None
|
|
322
|
+
and (
|
|
323
|
+
# The default model is gpt-5
|
|
324
|
+
is_gpt_5_default() is True
|
|
325
|
+
# However, the specified model is not a gpt-5 model
|
|
326
|
+
and (
|
|
327
|
+
isinstance(self.model, str) is False
|
|
328
|
+
or gpt_5_reasoning_settings_required(self.model) is False # type: ignore
|
|
329
|
+
)
|
|
330
|
+
# The model settings are not customized for the specified model
|
|
331
|
+
and self.model_settings == get_default_model_settings()
|
|
332
|
+
)
|
|
333
|
+
):
|
|
334
|
+
# In this scenario, we should use a generic model settings
|
|
335
|
+
# because non-gpt-5 models are not compatible with the default gpt-5 model settings.
|
|
336
|
+
# This is a best-effort attempt to make the agent work with non-gpt-5 models.
|
|
337
|
+
self.model_settings = ModelSettings()
|
|
338
|
+
|
|
339
|
+
if not isinstance(self.input_guardrails, list):
|
|
340
|
+
raise TypeError(
|
|
341
|
+
f"Agent input_guardrails must be a list, got {type(self.input_guardrails).__name__}"
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
if not isinstance(self.output_guardrails, list):
|
|
345
|
+
raise TypeError(
|
|
346
|
+
f"Agent output_guardrails must be a list, "
|
|
347
|
+
f"got {type(self.output_guardrails).__name__}"
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
if self.output_type is not None:
|
|
351
|
+
from .agent_output import AgentOutputSchemaBase
|
|
352
|
+
|
|
353
|
+
if not (
|
|
354
|
+
isinstance(self.output_type, (type, AgentOutputSchemaBase))
|
|
355
|
+
or get_origin(self.output_type) is not None
|
|
356
|
+
):
|
|
357
|
+
raise TypeError(
|
|
358
|
+
f"Agent output_type must be a type, AgentOutputSchemaBase, or None, "
|
|
359
|
+
f"got {type(self.output_type).__name__}"
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
if self.hooks is not None:
|
|
363
|
+
from .lifecycle import AgentHooksBase
|
|
364
|
+
|
|
365
|
+
if not isinstance(self.hooks, AgentHooksBase):
|
|
366
|
+
raise TypeError(
|
|
367
|
+
f"Agent hooks must be an AgentHooks instance or None, "
|
|
368
|
+
f"got {type(self.hooks).__name__}"
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
if (
|
|
372
|
+
not (
|
|
373
|
+
isinstance(self.tool_use_behavior, str)
|
|
374
|
+
and self.tool_use_behavior in ["run_llm_again", "stop_on_first_tool"]
|
|
375
|
+
)
|
|
376
|
+
and not isinstance(self.tool_use_behavior, dict)
|
|
377
|
+
and not callable(self.tool_use_behavior)
|
|
378
|
+
):
|
|
379
|
+
raise TypeError(
|
|
380
|
+
f"Agent tool_use_behavior must be 'run_llm_again', 'stop_on_first_tool', "
|
|
381
|
+
f"StopAtTools dict, or callable, got {type(self.tool_use_behavior).__name__}"
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
if not isinstance(self.reset_tool_choice, bool):
|
|
385
|
+
raise TypeError(
|
|
386
|
+
f"Agent reset_tool_choice must be a boolean, "
|
|
387
|
+
f"got {type(self.reset_tool_choice).__name__}"
|
|
388
|
+
)
|
|
389
|
+
|
|
226
390
|
def clone(self, **kwargs: Any) -> Agent[TContext]:
|
|
227
391
|
"""Make a copy of the agent, with the given arguments changed.
|
|
228
392
|
Notes:
|
|
@@ -242,7 +406,19 @@ class Agent(AgentBase, Generic[TContext]):
|
|
|
242
406
|
self,
|
|
243
407
|
tool_name: str | None,
|
|
244
408
|
tool_description: str | None,
|
|
245
|
-
custom_output_extractor:
|
|
409
|
+
custom_output_extractor: (
|
|
410
|
+
Callable[[RunResult | RunResultStreaming], Awaitable[str]] | None
|
|
411
|
+
) = None,
|
|
412
|
+
is_enabled: bool
|
|
413
|
+
| Callable[[RunContextWrapper[Any], AgentBase[Any]], MaybeAwaitable[bool]] = True,
|
|
414
|
+
on_stream: Callable[[AgentToolStreamEvent], MaybeAwaitable[None]] | None = None,
|
|
415
|
+
run_config: RunConfig | None = None,
|
|
416
|
+
max_turns: int | None = None,
|
|
417
|
+
hooks: RunHooks[TContext] | None = None,
|
|
418
|
+
previous_response_id: str | None = None,
|
|
419
|
+
conversation_id: str | None = None,
|
|
420
|
+
session: Session | None = None,
|
|
421
|
+
failure_error_function: ToolErrorFunction | None = default_tool_error_function,
|
|
246
422
|
) -> Tool:
|
|
247
423
|
"""Transform this agent into a tool, callable by other agents.
|
|
248
424
|
|
|
@@ -258,38 +434,135 @@ class Agent(AgentBase, Generic[TContext]):
|
|
|
258
434
|
when to use it.
|
|
259
435
|
custom_output_extractor: A function that extracts the output from the agent. If not
|
|
260
436
|
provided, the last message from the agent will be used.
|
|
437
|
+
is_enabled: Whether the tool is enabled. Can be a bool or a callable that takes the run
|
|
438
|
+
context and agent and returns whether the tool is enabled. Disabled tools are hidden
|
|
439
|
+
from the LLM at runtime.
|
|
440
|
+
on_stream: Optional callback (sync or async) to receive streaming events from the nested
|
|
441
|
+
agent run. The callback receives an `AgentToolStreamEvent` containing the nested
|
|
442
|
+
agent, the originating tool call (when available), and each stream event. When
|
|
443
|
+
provided, the nested agent is executed in streaming mode.
|
|
444
|
+
failure_error_function: If provided, generate an error message when the tool (agent) run
|
|
445
|
+
fails. The message is sent to the LLM. If None, the exception is raised instead.
|
|
261
446
|
"""
|
|
262
447
|
|
|
263
448
|
@function_tool(
|
|
264
449
|
name_override=tool_name or _transforms.transform_string_function_style(self.name),
|
|
265
450
|
description_override=tool_description or "",
|
|
451
|
+
is_enabled=is_enabled,
|
|
452
|
+
failure_error_function=failure_error_function,
|
|
266
453
|
)
|
|
267
|
-
async def run_agent(context:
|
|
268
|
-
from .run import Runner
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
454
|
+
async def run_agent(context: ToolContext, input: str) -> Any:
|
|
455
|
+
from .run import DEFAULT_MAX_TURNS, Runner
|
|
456
|
+
|
|
457
|
+
resolved_max_turns = max_turns if max_turns is not None else DEFAULT_MAX_TURNS
|
|
458
|
+
run_result: RunResult | RunResultStreaming
|
|
459
|
+
|
|
460
|
+
if on_stream is not None:
|
|
461
|
+
run_result = Runner.run_streamed(
|
|
462
|
+
starting_agent=self,
|
|
463
|
+
input=input,
|
|
464
|
+
context=context.context,
|
|
465
|
+
run_config=run_config,
|
|
466
|
+
max_turns=resolved_max_turns,
|
|
467
|
+
hooks=hooks,
|
|
468
|
+
previous_response_id=previous_response_id,
|
|
469
|
+
conversation_id=conversation_id,
|
|
470
|
+
session=session,
|
|
471
|
+
)
|
|
472
|
+
# Dispatch callbacks in the background so slow handlers do not block
|
|
473
|
+
# event consumption.
|
|
474
|
+
event_queue: asyncio.Queue[AgentToolStreamEvent | None] = asyncio.Queue()
|
|
475
|
+
|
|
476
|
+
async def _run_handler(payload: AgentToolStreamEvent) -> None:
|
|
477
|
+
"""Execute the user callback while capturing exceptions."""
|
|
478
|
+
try:
|
|
479
|
+
maybe_result = on_stream(payload)
|
|
480
|
+
if inspect.isawaitable(maybe_result):
|
|
481
|
+
await maybe_result
|
|
482
|
+
except Exception:
|
|
483
|
+
logger.exception(
|
|
484
|
+
"Error while handling on_stream event for agent tool %s.",
|
|
485
|
+
self.name,
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
async def dispatch_stream_events() -> None:
|
|
489
|
+
while True:
|
|
490
|
+
payload = await event_queue.get()
|
|
491
|
+
is_sentinel = payload is None # None marks the end of the stream.
|
|
492
|
+
try:
|
|
493
|
+
if payload is not None:
|
|
494
|
+
await _run_handler(payload)
|
|
495
|
+
finally:
|
|
496
|
+
event_queue.task_done()
|
|
497
|
+
|
|
498
|
+
if is_sentinel:
|
|
499
|
+
break
|
|
500
|
+
|
|
501
|
+
dispatch_task = asyncio.create_task(dispatch_stream_events())
|
|
502
|
+
|
|
503
|
+
try:
|
|
504
|
+
from .stream_events import AgentUpdatedStreamEvent
|
|
505
|
+
|
|
506
|
+
current_agent = run_result.current_agent
|
|
507
|
+
async for event in run_result.stream_events():
|
|
508
|
+
if isinstance(event, AgentUpdatedStreamEvent):
|
|
509
|
+
current_agent = event.new_agent
|
|
510
|
+
|
|
511
|
+
payload: AgentToolStreamEvent = {
|
|
512
|
+
"event": event,
|
|
513
|
+
"agent": current_agent,
|
|
514
|
+
"tool_call": context.tool_call,
|
|
515
|
+
}
|
|
516
|
+
await event_queue.put(payload)
|
|
517
|
+
finally:
|
|
518
|
+
await event_queue.put(None)
|
|
519
|
+
await event_queue.join()
|
|
520
|
+
await dispatch_task
|
|
521
|
+
else:
|
|
522
|
+
run_result = await Runner.run(
|
|
523
|
+
starting_agent=self,
|
|
524
|
+
input=input,
|
|
525
|
+
context=context.context,
|
|
526
|
+
run_config=run_config,
|
|
527
|
+
max_turns=resolved_max_turns,
|
|
528
|
+
hooks=hooks,
|
|
529
|
+
previous_response_id=previous_response_id,
|
|
530
|
+
conversation_id=conversation_id,
|
|
531
|
+
session=session,
|
|
532
|
+
)
|
|
275
533
|
if custom_output_extractor:
|
|
276
|
-
return await custom_output_extractor(
|
|
534
|
+
return await custom_output_extractor(run_result)
|
|
277
535
|
|
|
278
|
-
return
|
|
536
|
+
return run_result.final_output
|
|
279
537
|
|
|
280
538
|
return run_agent
|
|
281
539
|
|
|
282
540
|
async def get_system_prompt(self, run_context: RunContextWrapper[TContext]) -> str | None:
|
|
283
|
-
"""Get the system prompt for the agent."""
|
|
284
541
|
if isinstance(self.instructions, str):
|
|
285
542
|
return self.instructions
|
|
286
543
|
elif callable(self.instructions):
|
|
544
|
+
# Inspect the signature of the instructions function
|
|
545
|
+
sig = inspect.signature(self.instructions)
|
|
546
|
+
params = list(sig.parameters.values())
|
|
547
|
+
|
|
548
|
+
# Enforce exactly 2 parameters
|
|
549
|
+
if len(params) != 2:
|
|
550
|
+
raise TypeError(
|
|
551
|
+
f"'instructions' callable must accept exactly 2 arguments (context, agent), "
|
|
552
|
+
f"but got {len(params)}: {[p.name for p in params]}"
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Call the instructions function properly
|
|
287
556
|
if inspect.iscoroutinefunction(self.instructions):
|
|
288
557
|
return await cast(Awaitable[str], self.instructions(run_context, self))
|
|
289
558
|
else:
|
|
290
559
|
return cast(str, self.instructions(run_context, self))
|
|
560
|
+
|
|
291
561
|
elif self.instructions is not None:
|
|
292
|
-
logger.error(
|
|
562
|
+
logger.error(
|
|
563
|
+
f"Instructions must be a string or a callable function, "
|
|
564
|
+
f"got {type(self.instructions).__name__}"
|
|
565
|
+
)
|
|
293
566
|
|
|
294
567
|
return None
|
|
295
568
|
|