|
11 | 11 | from openai.types.responses import ResponseCompletedEvent |
12 | 12 | from openai.types.shared.reasoning import Reasoning |
13 | 13 |
|
14 | | -from agents import Computer, ComputerTool, ModelSettings, ModelTracing, ToolSearchTool, __version__ |
| 14 | +from agents import ( |
| 15 | + AsyncComputer, |
| 16 | + Computer, |
| 17 | + ComputerTool, |
| 18 | + ModelSettings, |
| 19 | + ModelTracing, |
| 20 | + ToolSearchTool, |
| 21 | + __version__, |
| 22 | +) |
15 | 23 | from agents.exceptions import UserError |
16 | 24 | from agents.models.openai_responses import ( |
17 | 25 | _HEADERS_OVERRIDE as RESP_HEADERS, |
@@ -932,6 +940,69 @@ def __init__(self): |
932 | 940 | assert called_kwargs["tools"] == [{"type": "tool_search"}] |
933 | 941 |
|
934 | 942 |
|
| 943 | +@pytest.mark.allow_call_model_methods |
| 944 | +@pytest.mark.asyncio |
| 945 | +async def test_ga_computer_tool_does_not_require_preview_metadata() -> None: |
| 946 | + called_kwargs: dict[str, Any] = {} |
| 947 | + |
| 948 | + class DummyComputer(AsyncComputer): |
| 949 | + async def screenshot(self) -> str: |
| 950 | + return "screenshot" |
| 951 | + |
| 952 | + async def click(self, x: int, y: int, button: str) -> None: |
| 953 | + pass |
| 954 | + |
| 955 | + async def double_click(self, x: int, y: int) -> None: |
| 956 | + pass |
| 957 | + |
| 958 | + async def drag(self, path: list[tuple[int, int]]) -> None: |
| 959 | + pass |
| 960 | + |
| 961 | + async def keypress(self, keys: list[str]) -> None: |
| 962 | + pass |
| 963 | + |
| 964 | + async def move(self, x: int, y: int) -> None: |
| 965 | + pass |
| 966 | + |
| 967 | + async def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: |
| 968 | + pass |
| 969 | + |
| 970 | + async def type(self, text: str) -> None: |
| 971 | + pass |
| 972 | + |
| 973 | + async def wait(self) -> None: |
| 974 | + pass |
| 975 | + |
| 976 | + class DummyResponses: |
| 977 | + async def create(self, **kwargs): |
| 978 | + nonlocal called_kwargs |
| 979 | + called_kwargs = kwargs |
| 980 | + return get_response_obj([]) |
| 981 | + |
| 982 | + class DummyResponsesClient: |
| 983 | + def __init__(self): |
| 984 | + self.responses = DummyResponses() |
| 985 | + |
| 986 | + model = OpenAIResponsesModel( |
| 987 | + model="gpt-5.4", |
| 988 | + openai_client=DummyResponsesClient(), # type: ignore[arg-type] |
| 989 | + model_is_explicit=True, |
| 990 | + ) |
| 991 | + |
| 992 | + await model.get_response( |
| 993 | + system_instructions=None, |
| 994 | + input="hi", |
| 995 | + model_settings=ModelSettings(), |
| 996 | + tools=[ComputerTool(computer=DummyComputer())], |
| 997 | + output_schema=None, |
| 998 | + handoffs=[], |
| 999 | + tracing=ModelTracing.DISABLED, |
| 1000 | + prompt=None, |
| 1001 | + ) |
| 1002 | + |
| 1003 | + assert called_kwargs["tools"] == [{"type": "computer"}] |
| 1004 | + |
| 1005 | + |
935 | 1006 | @pytest.mark.allow_call_model_methods |
936 | 1007 | @pytest.mark.asyncio |
937 | 1008 | async def test_prompt_id_uses_preview_computer_payload_when_prompt_owns_model() -> None: |
@@ -1012,6 +1083,73 @@ def __init__(self): |
1012 | 1083 | ] |
1013 | 1084 |
|
1014 | 1085 |
|
| 1086 | +@pytest.mark.allow_call_model_methods |
| 1087 | +@pytest.mark.asyncio |
| 1088 | +async def test_prompt_id_computer_without_preview_metadata_raises_clear_error() -> None: |
| 1089 | + called_kwargs: dict[str, Any] = {} |
| 1090 | + |
| 1091 | + class DummyComputer(Computer): |
| 1092 | + def screenshot(self) -> str: |
| 1093 | + return "screenshot" |
| 1094 | + |
| 1095 | + def click(self, x: int, y: int, button: str) -> None: |
| 1096 | + pass |
| 1097 | + |
| 1098 | + def double_click(self, x: int, y: int) -> None: |
| 1099 | + pass |
| 1100 | + |
| 1101 | + def drag(self, path: list[tuple[int, int]]) -> None: |
| 1102 | + pass |
| 1103 | + |
| 1104 | + def keypress(self, keys: list[str]) -> None: |
| 1105 | + pass |
| 1106 | + |
| 1107 | + def move(self, x: int, y: int) -> None: |
| 1108 | + pass |
| 1109 | + |
| 1110 | + def scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None: |
| 1111 | + pass |
| 1112 | + |
| 1113 | + def type(self, text: str) -> None: |
| 1114 | + pass |
| 1115 | + |
| 1116 | + def wait(self) -> None: |
| 1117 | + pass |
| 1118 | + |
| 1119 | + class DummyResponses: |
| 1120 | + async def create(self, **kwargs): |
| 1121 | + nonlocal called_kwargs |
| 1122 | + called_kwargs = kwargs |
| 1123 | + return get_response_obj([]) |
| 1124 | + |
| 1125 | + class DummyResponsesClient: |
| 1126 | + def __init__(self): |
| 1127 | + self.responses = DummyResponses() |
| 1128 | + |
| 1129 | + model = OpenAIResponsesModel( |
| 1130 | + model="gpt-5.4", |
| 1131 | + openai_client=DummyResponsesClient(), # type: ignore[arg-type] |
| 1132 | + model_is_explicit=False, |
| 1133 | + ) |
| 1134 | + |
| 1135 | + with pytest.raises( |
| 1136 | + UserError, |
| 1137 | + match="Preview computer tool payloads require `environment` and `dimensions`", |
| 1138 | + ): |
| 1139 | + await model.get_response( |
| 1140 | + system_instructions=None, |
| 1141 | + input="hi", |
| 1142 | + model_settings=ModelSettings(), |
| 1143 | + tools=[ComputerTool(computer=DummyComputer())], |
| 1144 | + output_schema=None, |
| 1145 | + handoffs=[], |
| 1146 | + tracing=ModelTracing.DISABLED, |
| 1147 | + prompt={"id": "pmpt_123"}, |
| 1148 | + ) |
| 1149 | + |
| 1150 | + assert called_kwargs == {} |
| 1151 | + |
| 1152 | + |
1015 | 1153 | @pytest.mark.allow_call_model_methods |
1016 | 1154 | @pytest.mark.asyncio |
1017 | 1155 | async def test_prompt_id_unresolved_computer_uses_preview_payload_shape() -> None: |
|
0 commit comments