|
71 | 71 | _construct_responses_api_input, |
72 | 72 | _convert_dict_to_message, |
73 | 73 | _convert_message_to_dict, |
| 74 | + _convert_responses_chunk_to_generation_chunk, |
74 | 75 | _convert_to_openai_response_format, |
75 | 76 | _create_usage_metadata, |
76 | 77 | _create_usage_metadata_responses, |
@@ -1924,6 +1925,185 @@ def test__construct_lc_result_from_responses_api_no_usage_metadata() -> None: |
1924 | 1925 | assert cast(AIMessage, result.generations[0].message).usage_metadata is None |
1925 | 1926 |
|
1926 | 1927 |
|
| 1928 | +def test__construct_lc_result_from_responses_api_apply_patch_response() -> None: |
| 1929 | + """Test a response with apply_patch output.""" |
| 1930 | + apply_patch_call = MagicMock() |
| 1931 | + apply_patch_call.type = "apply_patch_call" |
| 1932 | + apply_patch_call.model_dump.return_value = { |
| 1933 | + "type": "apply_patch_call", |
| 1934 | + "call_id": "call_123", |
| 1935 | + "operation": { |
| 1936 | + "type": "create_file", |
| 1937 | + "path": "hello.txt", |
| 1938 | + "diff": "+hello\\n", |
| 1939 | + }, |
| 1940 | + "status": "completed", |
| 1941 | + } |
| 1942 | + |
| 1943 | + response = MagicMock() |
| 1944 | + response.error = None |
| 1945 | + response.id = "resp_123" |
| 1946 | + response.output = [apply_patch_call] |
| 1947 | + response.usage = None |
| 1948 | + response.service_tier = None |
| 1949 | + response.text = None |
| 1950 | + response.model_dump.return_value = { |
| 1951 | + "id": "resp_123", |
| 1952 | + "created_at": 1234567890, |
| 1953 | + "model": "gpt-4o", |
| 1954 | + "object": "response", |
| 1955 | + "status": "completed", |
| 1956 | + } |
| 1957 | + |
| 1958 | + result = _construct_lc_result_from_responses_api(response) |
| 1959 | + |
| 1960 | + message = cast(AIMessage, result.generations[0].message) |
| 1961 | + assert message.content == [ |
| 1962 | + { |
| 1963 | + "type": "apply_patch_call", |
| 1964 | + "call_id": "call_123", |
| 1965 | + "operation": { |
| 1966 | + "type": "create_file", |
| 1967 | + "path": "hello.txt", |
| 1968 | + "diff": "+hello\\n", |
| 1969 | + }, |
| 1970 | + "status": "completed", |
| 1971 | + } |
| 1972 | + ] |
| 1973 | + assert message.tool_calls == [] |
| 1974 | + assert message.invalid_tool_calls == [] |
| 1975 | + |
| 1976 | + |
| 1977 | +def test__construct_lc_result_from_responses_api_apply_patch_call_output() -> None: |
| 1978 | + """Test a response with apply_patch_call_output output.""" |
| 1979 | + apply_patch_call_output = MagicMock() |
| 1980 | + apply_patch_call_output.type = "apply_patch_call_output" |
| 1981 | + apply_patch_call_output.model_dump.return_value = { |
| 1982 | + "type": "apply_patch_call_output", |
| 1983 | + "call_id": "call_123", |
| 1984 | + "status": "completed", |
| 1985 | + "output": "Created hello.txt", |
| 1986 | + } |
| 1987 | + |
| 1988 | + response = MagicMock() |
| 1989 | + response.error = None |
| 1990 | + response.id = "resp_123" |
| 1991 | + response.output = [apply_patch_call_output] |
| 1992 | + response.usage = None |
| 1993 | + response.service_tier = None |
| 1994 | + response.text = None |
| 1995 | + response.model_dump.return_value = { |
| 1996 | + "id": "resp_123", |
| 1997 | + "created_at": 1234567890, |
| 1998 | + "model": "gpt-4o", |
| 1999 | + "object": "response", |
| 2000 | + "status": "completed", |
| 2001 | + } |
| 2002 | + |
| 2003 | + result = _construct_lc_result_from_responses_api(response) |
| 2004 | + |
| 2005 | + message = result.generations[0].message |
| 2006 | + assert message.content == [ |
| 2007 | + { |
| 2008 | + "type": "apply_patch_call_output", |
| 2009 | + "call_id": "call_123", |
| 2010 | + "status": "completed", |
| 2011 | + "output": "Created hello.txt", |
| 2012 | + } |
| 2013 | + ] |
| 2014 | + |
| 2015 | + |
| 2016 | +def test__construct_responses_api_input_apply_patch_round_trip() -> None: |
| 2017 | + """Test apply_patch content blocks are preserved when sent back as input.""" |
| 2018 | + messages = [ |
| 2019 | + AIMessage( |
| 2020 | + content=[ |
| 2021 | + { |
| 2022 | + "type": "apply_patch_call", |
| 2023 | + "call_id": "call_123", |
| 2024 | + "operation": { |
| 2025 | + "type": "create_file", |
| 2026 | + "path": "hello.txt", |
| 2027 | + "diff": "+hello\\n", |
| 2028 | + }, |
| 2029 | + "status": "completed", |
| 2030 | + } |
| 2031 | + ] |
| 2032 | + ), |
| 2033 | + HumanMessage( |
| 2034 | + content=[ |
| 2035 | + { |
| 2036 | + "type": "apply_patch_call_output", |
| 2037 | + "call_id": "call_123", |
| 2038 | + "status": "completed", |
| 2039 | + "output": "Created hello.txt", |
| 2040 | + } |
| 2041 | + ] |
| 2042 | + ), |
| 2043 | + ] |
| 2044 | + |
| 2045 | + result = _construct_responses_api_input(messages) |
| 2046 | + |
| 2047 | + assert result == [ |
| 2048 | + { |
| 2049 | + "type": "apply_patch_call", |
| 2050 | + "call_id": "call_123", |
| 2051 | + "operation": { |
| 2052 | + "type": "create_file", |
| 2053 | + "path": "hello.txt", |
| 2054 | + "diff": "+hello\\n", |
| 2055 | + }, |
| 2056 | + "status": "completed", |
| 2057 | + }, |
| 2058 | + { |
| 2059 | + "type": "apply_patch_call_output", |
| 2060 | + "call_id": "call_123", |
| 2061 | + "status": "completed", |
| 2062 | + "output": "Created hello.txt", |
| 2063 | + }, |
| 2064 | + ] |
| 2065 | + |
| 2066 | + |
| 2067 | +def test__convert_responses_chunk_to_generation_chunk_apply_patch_response() -> None: |
| 2068 | + """Test streamed apply_patch output item is preserved in message chunks.""" |
| 2069 | + chunk = MagicMock() |
| 2070 | + chunk.type = "response.output_item.done" |
| 2071 | + chunk.output_index = 0 |
| 2072 | + chunk.item.type = "apply_patch_call" |
| 2073 | + chunk.item.model_dump.return_value = { |
| 2074 | + "type": "apply_patch_call", |
| 2075 | + "call_id": "call_123", |
| 2076 | + "operation": { |
| 2077 | + "type": "create_file", |
| 2078 | + "path": "hello.txt", |
| 2079 | + "diff": "+hello\\n", |
| 2080 | + }, |
| 2081 | + "status": "completed", |
| 2082 | + } |
| 2083 | + |
| 2084 | + _, _, _, generation_chunk = _convert_responses_chunk_to_generation_chunk( |
| 2085 | + chunk, |
| 2086 | + current_index=-1, |
| 2087 | + current_output_index=-1, |
| 2088 | + current_sub_index=-1, |
| 2089 | + ) |
| 2090 | + |
| 2091 | + assert generation_chunk is not None |
| 2092 | + assert generation_chunk.message.content == [ |
| 2093 | + { |
| 2094 | + "type": "apply_patch_call", |
| 2095 | + "call_id": "call_123", |
| 2096 | + "operation": { |
| 2097 | + "type": "create_file", |
| 2098 | + "path": "hello.txt", |
| 2099 | + "diff": "+hello\\n", |
| 2100 | + }, |
| 2101 | + "status": "completed", |
| 2102 | + "index": 0, |
| 2103 | + } |
| 2104 | + ] |
| 2105 | + |
| 2106 | + |
1927 | 2107 | def test__construct_lc_result_from_responses_api_web_search_response() -> None: |
1928 | 2108 | """Test a response with web search output.""" |
1929 | 2109 | from openai.types.responses.response_function_web_search import ( |
@@ -3668,6 +3848,18 @@ def test_get_request_payload_responses_api_input_file_blocks_passthrough() -> No |
3668 | 3848 | ] |
3669 | 3849 |
|
3670 | 3850 |
|
| 3851 | +def test_apply_patch_passthrough() -> None: |
| 3852 | + """Test that apply_patch dict is passed through as a built-in tool.""" |
| 3853 | + llm = ChatOpenAI(model="gpt-4o", api_key=SecretStr("test-api-key")) |
| 3854 | + bound = llm.bind_tools([{"type": "apply_patch"}]) |
| 3855 | + payload = bound._get_request_payload( # type: ignore[attr-defined] |
| 3856 | + "test", |
| 3857 | + **bound.kwargs, # type: ignore[attr-defined] |
| 3858 | + ) |
| 3859 | + assert {"type": "apply_patch"} in payload["tools"] |
| 3860 | + assert "input" in payload |
| 3861 | + |
| 3862 | + |
3671 | 3863 | def test_tool_search_passthrough() -> None: |
3672 | 3864 | """Test that tool_search dict is passed through as a built-in tool.""" |
3673 | 3865 | llm = ChatOpenAI(model="gpt-4o") |
|
0 commit comments