Skip to content

Commit 91e84fe

Browse files
authored
Support for Codex CLI by skipping unsupported Responses tools (#23041)
* Support for Codex CLI by skipping unsupported Responses tools * Warn on skipped Responses tools and preserve gpt-oss apply_patch rejection * Revert gpt-oss apply_patch special handling
1 parent 7155a49 commit 91e84fe

2 files changed

Lines changed: 85 additions & 3 deletions

File tree

tests/test-chat.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,6 +1664,83 @@ static void test_convert_responses_to_chatcmpl() {
16641664
assert_equals(false, result.contains("max_output_tokens"));
16651665
assert_equals(100, result.at("max_tokens").get<int>());
16661666
}
1667+
1668+
// Test mixed Responses tools: convert only function tools
1669+
{
1670+
json input = json::parse(R"({
1671+
"input": "Hello",
1672+
"model": "test-model",
1673+
"tools": [
1674+
{
1675+
"type": "web_search"
1676+
},
1677+
{
1678+
"type": "function",
1679+
"name": "get_weather",
1680+
"description": "Get weather for a location",
1681+
"parameters": {
1682+
"type": "object",
1683+
"properties": {
1684+
"location": {
1685+
"type": "string"
1686+
}
1687+
},
1688+
"required": ["location"]
1689+
}
1690+
},
1691+
{
1692+
"type": "image_generation"
1693+
},
1694+
{
1695+
"type": "mcp",
1696+
"server_label": "test-server"
1697+
},
1698+
{
1699+
"type": "namespace",
1700+
"name": "browser"
1701+
}
1702+
]
1703+
})");
1704+
1705+
json result = server_chat_convert_responses_to_chatcmpl(input);
1706+
1707+
assert_equals(true, result.contains("tools"));
1708+
assert_equals(true, result.at("tools").is_array());
1709+
assert_equals((size_t)1, result.at("tools").size());
1710+
1711+
const auto & tool = result.at("tools")[0];
1712+
assert_equals(std::string("function"), tool.at("type").get<std::string>());
1713+
assert_equals(std::string("get_weather"), tool.at("function").at("name").get<std::string>());
1714+
assert_equals(true, tool.at("function").at("strict").get<bool>());
1715+
}
1716+
1717+
// Test non-function Responses tools are ignored
1718+
{
1719+
json input = json::parse(R"({
1720+
"input": "Hello",
1721+
"model": "test-model",
1722+
"tools": [
1723+
{
1724+
"type": "web_search"
1725+
},
1726+
{
1727+
"type": "image_generation"
1728+
},
1729+
{
1730+
"type": "mcp",
1731+
"server_label": "test-server"
1732+
},
1733+
{
1734+
"type": "namespace",
1735+
"name": "browser"
1736+
}
1737+
]
1738+
})");
1739+
1740+
json result = server_chat_convert_responses_to_chatcmpl(input);
1741+
1742+
assert_equals(false, result.contains("tools"));
1743+
}
16671744
}
16681745

16691746
static void test_template_output_peg_parsers(bool detailed_debug) {

tools/server/server-chat.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,11 @@ json server_chat_convert_responses_to_chatcmpl(const json & response_body) {
257257
for (json resp_tool : response_body.at("tools")) {
258258
json chatcmpl_tool;
259259

260-
if (json_value(resp_tool, "type", std::string()) != "function") {
261-
throw std::invalid_argument("'type' of tool must be 'function'");
260+
const std::string type = json_value(resp_tool, "type", std::string());
261+
if (type != "function") {
262+
// Non-function Responses tools have no Chat Completions equivalent.
263+
SRV_WRN("unsupported Responses tool type '%s' skipped\n", type.c_str());
264+
continue;
262265
}
263266
resp_tool.erase("type");
264267
chatcmpl_tool["type"] = "function";
@@ -270,7 +273,9 @@ json server_chat_convert_responses_to_chatcmpl(const json & response_body) {
270273
chatcmpl_tools.push_back(chatcmpl_tool);
271274
}
272275
chatcmpl_body.erase("tools");
273-
chatcmpl_body["tools"] = chatcmpl_tools;
276+
if (!chatcmpl_tools.empty()) {
277+
chatcmpl_body["tools"] = chatcmpl_tools;
278+
}
274279
}
275280

276281
if (response_body.contains("max_output_tokens")) {

0 commit comments

Comments
 (0)