425 lines
11 KiB
Python
425 lines
11 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from typing import Any, cast
|
|
|
|
import pytest
|
|
|
|
from agents import (
|
|
Agent,
|
|
AgentHookContext,
|
|
FunctionTool,
|
|
HandoffInputData,
|
|
ItemHelpers,
|
|
ModelRetrySettings,
|
|
ModelSettings,
|
|
MultiProvider,
|
|
RunConfig,
|
|
RunContextWrapper,
|
|
RunResult,
|
|
RunResultStreaming,
|
|
SessionSettings,
|
|
ToolExecutionConfig,
|
|
ToolGuardrailFunctionOutput,
|
|
ToolInputGuardrailData,
|
|
ToolOutputGuardrailData,
|
|
Usage,
|
|
tool_input_guardrail,
|
|
tool_output_guardrail,
|
|
)
|
|
from agents.tool_context import ToolContext
|
|
|
|
|
|
def test_run_config_positional_arguments_remain_backward_compatible() -> None:
|
|
async def keep_handoff_input(data: HandoffInputData) -> HandoffInputData:
|
|
return data
|
|
|
|
config = RunConfig(None, MultiProvider(), None, keep_handoff_input)
|
|
|
|
assert config.handoff_input_filter is keep_handoff_input
|
|
assert config.session_settings is None
|
|
|
|
|
|
def test_run_config_session_settings_positional_binding_is_preserved() -> None:
|
|
session_settings = SessionSettings(limit=123)
|
|
config = RunConfig(
|
|
None,
|
|
MultiProvider(),
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
True,
|
|
"Agent workflow",
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
session_settings,
|
|
)
|
|
|
|
assert config.session_settings == session_settings
|
|
assert config.reasoning_item_id_policy is None
|
|
|
|
|
|
def test_run_config_reasoning_item_id_policy_positional_binding() -> None:
|
|
session_settings = SessionSettings(limit=123)
|
|
config = RunConfig(
|
|
None,
|
|
MultiProvider(),
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
True,
|
|
"Agent workflow",
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
session_settings,
|
|
"omit",
|
|
)
|
|
|
|
assert config.session_settings == session_settings
|
|
assert config.reasoning_item_id_policy == "omit"
|
|
assert config.sandbox is None
|
|
assert config.tool_execution is None
|
|
|
|
|
|
def test_run_config_tool_execution_append_preserves_sandbox_position() -> None:
|
|
session_settings = SessionSettings(limit=123)
|
|
tool_execution = ToolExecutionConfig(max_function_tool_concurrency=2)
|
|
config = RunConfig(
|
|
None,
|
|
MultiProvider(),
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
True,
|
|
"Agent workflow",
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
session_settings,
|
|
"omit",
|
|
None,
|
|
tool_execution,
|
|
)
|
|
|
|
assert config.session_settings == session_settings
|
|
assert config.reasoning_item_id_policy == "omit"
|
|
assert config.sandbox is None
|
|
assert config.tool_execution is tool_execution
|
|
|
|
|
|
def test_run_config_tool_not_found_behavior_append_preserves_tool_execution_position() -> None:
|
|
session_settings = SessionSettings(limit=123)
|
|
tool_execution = ToolExecutionConfig(max_function_tool_concurrency=2)
|
|
config = RunConfig(
|
|
None,
|
|
MultiProvider(),
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
None,
|
|
None,
|
|
False,
|
|
None,
|
|
True,
|
|
"Agent workflow",
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
session_settings,
|
|
"omit",
|
|
None,
|
|
tool_execution,
|
|
"return_error_to_model",
|
|
)
|
|
|
|
assert config.session_settings == session_settings
|
|
assert config.reasoning_item_id_policy == "omit"
|
|
assert config.sandbox is None
|
|
assert config.tool_execution is tool_execution
|
|
assert config.tool_not_found_behavior == "return_error_to_model"
|
|
|
|
|
|
def test_tool_execution_config_pre_approval_append_preserves_max_concurrency() -> None:
|
|
config = ToolExecutionConfig(2, True)
|
|
|
|
assert config.max_function_tool_concurrency == 2
|
|
assert config.pre_approval_tool_input_guardrails is True
|
|
|
|
|
|
def test_tool_execution_config_rejects_non_bool_pre_approval_guardrails() -> None:
|
|
with pytest.raises(
|
|
ValueError,
|
|
match="tool_execution.pre_approval_tool_input_guardrails must be a bool",
|
|
):
|
|
ToolExecutionConfig(pre_approval_tool_input_guardrails=cast(Any, "true"))
|
|
|
|
|
|
def test_model_settings_context_management_append_preserves_retry_position() -> None:
|
|
retry = ModelRetrySettings(max_retries=1)
|
|
settings = ModelSettings(
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
retry,
|
|
)
|
|
|
|
assert settings.retry is retry
|
|
assert settings.context_management is None
|
|
|
|
|
|
def test_function_tool_positional_arguments_keep_guardrail_positions() -> None:
|
|
async def invoke(_ctx: ToolContext[Any], _args: str) -> str:
|
|
return "ok"
|
|
|
|
@tool_input_guardrail
|
|
def allow_input(_data: ToolInputGuardrailData) -> ToolGuardrailFunctionOutput:
|
|
return ToolGuardrailFunctionOutput.allow()
|
|
|
|
@tool_output_guardrail
|
|
def allow_output(_data: ToolOutputGuardrailData) -> ToolGuardrailFunctionOutput:
|
|
return ToolGuardrailFunctionOutput.allow()
|
|
|
|
input_guardrails = [allow_input]
|
|
output_guardrails = [allow_output]
|
|
|
|
tool = FunctionTool(
|
|
"tool_name",
|
|
"tool_description",
|
|
{"type": "object", "properties": {}},
|
|
invoke,
|
|
True,
|
|
True,
|
|
input_guardrails,
|
|
output_guardrails,
|
|
)
|
|
|
|
assert tool.needs_approval is False
|
|
assert tool.tool_input_guardrails is not None
|
|
assert tool.tool_output_guardrails is not None
|
|
assert tool.tool_input_guardrails[0] is allow_input
|
|
assert tool.tool_output_guardrails[0] is allow_output
|
|
assert tool.timeout_seconds is None
|
|
assert tool.timeout_behavior == "error_as_result"
|
|
assert tool.timeout_error_function is None
|
|
|
|
|
|
def test_agent_hook_context_third_positional_argument_is_turn_input() -> None:
|
|
turn_input = ItemHelpers.input_to_new_input_list("hello")
|
|
context = AgentHookContext(None, Usage(), turn_input)
|
|
|
|
assert context.turn_input == turn_input
|
|
assert isinstance(context._approvals, dict)
|
|
|
|
|
|
def test_tool_context_v070_positional_constructor_still_works() -> None:
|
|
usage = Usage()
|
|
context = ToolContext(None, usage, "tool_name", "call_id", '{"x":1}', None)
|
|
|
|
assert context.usage is usage
|
|
assert context.tool_name == "tool_name"
|
|
assert context.tool_call_id == "call_id"
|
|
assert context.tool_arguments == '{"x":1}'
|
|
assert context.agent is None
|
|
|
|
|
|
def test_tool_context_supports_agent_keyword_argument() -> None:
|
|
usage = Usage()
|
|
agent = Agent(name="agent")
|
|
context = ToolContext(None, usage, "tool_name", "call_id", '{"x":1}', None, agent=agent)
|
|
|
|
assert context.usage is usage
|
|
assert context.tool_name == "tool_name"
|
|
assert context.tool_call_id == "call_id"
|
|
assert context.tool_arguments == '{"x":1}'
|
|
assert context.agent is agent
|
|
|
|
|
|
def test_run_result_v070_positional_constructor_still_works() -> None:
|
|
result = RunResult(
|
|
"x",
|
|
[],
|
|
[],
|
|
"ok",
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
RunContextWrapper(context=None),
|
|
Agent(name="agent"),
|
|
)
|
|
assert result.final_output == "ok"
|
|
assert result.interruptions == []
|
|
|
|
|
|
def test_run_result_streaming_v070_positional_constructor_still_works() -> None:
|
|
result = RunResultStreaming(
|
|
"x",
|
|
[],
|
|
[],
|
|
"ok",
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
RunContextWrapper(context=None),
|
|
Agent(name="agent"),
|
|
0,
|
|
1,
|
|
None,
|
|
None,
|
|
)
|
|
assert result.final_output == "ok"
|
|
assert result.interruptions == []
|
|
|
|
|
|
def test_run_result_streaming_v070_optional_positional_constructor_still_works() -> None:
|
|
event_queue: asyncio.Queue[Any] = asyncio.Queue()
|
|
input_guardrail_queue: asyncio.Queue[Any] = asyncio.Queue()
|
|
result = RunResultStreaming(
|
|
"x",
|
|
[],
|
|
[],
|
|
"ok",
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
RunContextWrapper(context=None),
|
|
Agent(name="agent"),
|
|
0,
|
|
1,
|
|
None,
|
|
None,
|
|
True,
|
|
[],
|
|
event_queue,
|
|
input_guardrail_queue,
|
|
None,
|
|
)
|
|
assert result.is_complete is True
|
|
assert result.run_loop_task is None
|
|
assert result._event_queue is event_queue
|
|
assert result._input_guardrail_queue is input_guardrail_queue
|
|
assert result.interruptions == []
|
|
|
|
|
|
def test_run_result_streaming_accepts_legacy_run_impl_task_keyword() -> None:
|
|
sentinel_task = cast(Any, object())
|
|
result = RunResultStreaming(
|
|
input="x",
|
|
new_items=[],
|
|
raw_responses=[],
|
|
final_output="ok",
|
|
input_guardrail_results=[],
|
|
output_guardrail_results=[],
|
|
tool_input_guardrail_results=[],
|
|
tool_output_guardrail_results=[],
|
|
context_wrapper=RunContextWrapper(context=None),
|
|
current_agent=Agent(name="agent"),
|
|
current_turn=0,
|
|
max_turns=1,
|
|
_current_agent_output_schema=None,
|
|
trace=None,
|
|
_run_impl_task=sentinel_task,
|
|
)
|
|
assert result.run_loop_task is sentinel_task
|
|
|
|
|
|
def test_run_result_streaming_accepts_run_loop_task_keyword() -> None:
|
|
sentinel_task = cast(Any, object())
|
|
result = RunResultStreaming(
|
|
input="x",
|
|
new_items=[],
|
|
raw_responses=[],
|
|
final_output="ok",
|
|
input_guardrail_results=[],
|
|
output_guardrail_results=[],
|
|
tool_input_guardrail_results=[],
|
|
tool_output_guardrail_results=[],
|
|
context_wrapper=RunContextWrapper(context=None),
|
|
current_agent=Agent(name="agent"),
|
|
current_turn=0,
|
|
max_turns=1,
|
|
_current_agent_output_schema=None,
|
|
trace=None,
|
|
run_loop_task=sentinel_task,
|
|
)
|
|
assert result.run_loop_task is sentinel_task
|
|
|
|
|
|
def test_run_result_streaming_v070_run_impl_task_positional_binding_is_preserved() -> None:
|
|
sentinel_task = cast(Any, object())
|
|
event_queue: asyncio.Queue[Any] = asyncio.Queue()
|
|
input_guardrail_queue: asyncio.Queue[Any] = asyncio.Queue()
|
|
result = RunResultStreaming(
|
|
"x",
|
|
[],
|
|
[],
|
|
"ok",
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
RunContextWrapper(context=None),
|
|
Agent(name="agent"),
|
|
0,
|
|
1,
|
|
None,
|
|
None,
|
|
False,
|
|
[],
|
|
event_queue,
|
|
input_guardrail_queue,
|
|
sentinel_task,
|
|
)
|
|
assert result._event_queue is event_queue
|
|
assert result._input_guardrail_queue is input_guardrail_queue
|
|
assert result.run_loop_task is sentinel_task
|