Skip to content

Commit 87c2630

Browse files
authored
allow missing content in message if tool_calls provided (#12293)
1 parent 2b3a25c commit 87c2630

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

common/chat.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,9 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
6060
}
6161
msg.role = message.at("role");
6262

63-
if (message.contains("content")) {
63+
auto has_content = message.contains("content");
64+
auto has_tool_calls = message.contains("tool_calls");
65+
if (has_content) {
6466
const auto & content = message.at("content");
6567
if (content.is_string()) {
6668
msg.content = content;
@@ -81,19 +83,8 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
8183
} else if (!content.is_null()) {
8284
throw std::runtime_error("Invalid 'content' type: expected string or array, got " + content.dump() + " (ref: https://github.com/ggml-org/llama.cpp/issues/8367)");
8385
}
84-
} else {
85-
throw std::runtime_error("Expected 'content' (ref: https://github.com/ggml-org/llama.cpp/issues/8367)");
86-
}
87-
if (message.contains("reasoning_content")) {
88-
msg.reasoning_content = message.at("reasoning_content");
89-
}
90-
if (message.contains("name")) {
91-
msg.tool_name = message.at("name");
92-
}
93-
if (message.contains("tool_call_id")) {
94-
msg.tool_call_id = message.at("tool_call_id");
9586
}
96-
if (message.contains("tool_calls")) {
87+
if (has_tool_calls) {
9788
for (const auto & tool_call : message.at("tool_calls")) {
9889
common_chat_tool_call tc;
9990
if (!tool_call.contains("type")) {
@@ -118,6 +109,18 @@ std::vector<common_chat_msg> common_chat_msgs_parse_oaicompat(const json & messa
118109
msg.tool_calls.push_back(tc);
119110
}
120111
}
112+
if (!has_content && !has_tool_calls) {
113+
throw std::runtime_error("Expected 'content' or 'tool_calls' (ref: https://github.com/ggml-org/llama.cpp/issues/8367 & https://github.com/ggml-org/llama.cpp/issues/12279)");
114+
}
115+
if (message.contains("reasoning_content")) {
116+
msg.reasoning_content = message.at("reasoning_content");
117+
}
118+
if (message.contains("name")) {
119+
msg.tool_name = message.at("name");
120+
}
121+
if (message.contains("tool_call_id")) {
122+
msg.tool_call_id = message.at("tool_call_id");
123+
}
121124

122125
msgs.push_back(msg);
123126
}

tests/test-chat.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,21 @@ static void test_msgs_oaicompat_json_conversion() {
480480
"]"
481481
),
482482
common_chat_msgs_to_json_oaicompat<json>({message_assist_call_python}).dump(2));
483+
484+
auto res = common_chat_msgs_parse_oaicompat(json::parse("[{\"role\": \"assistant\", \"tool_calls\": []}]"));
485+
assert_equals<size_t>(1, res.size());
486+
assert_equals<std::string>(res[0].role, "assistant");
487+
assert_equals(true, res[0].content.empty());
488+
assert_equals(true, res[0].tool_calls.empty());
489+
490+
try {
491+
common_chat_msgs_parse_oaicompat(json::parse("[{\"role\": \"assistant\"}]"));
492+
throw std::runtime_error("Expected exception");
493+
} catch (const std::exception & e) {
494+
if (std::string(e.what()).find("'content'") == std::string::npos) {
495+
throw std::runtime_error("Expected exception about missing 'content'");
496+
}
497+
}
483498
}
484499

485500
static void test_tools_oaicompat_json_conversion() {

0 commit comments

Comments
 (0)