server : merge contiguous Responses input items into a single assistant message (#19773)
* server : merge contiguous input items into a single assistant message * cont : simplify tool call msg * cont : reduce and combine content * cont : fix merging content items
This commit is contained in:
parent
e877ad8bd9
commit
34ec1c3f18
|
|
@ -1105,6 +1105,8 @@ json convert_responses_to_chatcmpl(const json & response_body) {
|
||||||
};
|
};
|
||||||
|
|
||||||
for (json item : input_value) {
|
for (json item : input_value) {
|
||||||
|
bool merge_prev = !chatcmpl_messages.empty() && chatcmpl_messages.back().value("role", "") == "assistant";
|
||||||
|
|
||||||
if (exists_and_is_string(item, "content")) {
|
if (exists_and_is_string(item, "content")) {
|
||||||
// #responses_create-input-input_item_list-input_message-content-text_input
|
// #responses_create-input-input_item_list-input_message-content-text_input
|
||||||
// Only "Input message" contains item["content"]::string
|
// Only "Input message" contains item["content"]::string
|
||||||
|
|
@ -1193,7 +1195,7 @@ json convert_responses_to_chatcmpl(const json & response_body) {
|
||||||
item.at("type") == "message"
|
item.at("type") == "message"
|
||||||
) {
|
) {
|
||||||
// #responses_create-input-input_item_list-item-output_message
|
// #responses_create-input-input_item_list-item-output_message
|
||||||
std::vector<json> chatcmpl_content;
|
auto chatcmpl_content = json::array();
|
||||||
|
|
||||||
for (const auto & output_text : item.at("content")) {
|
for (const auto & output_text : item.at("content")) {
|
||||||
const std::string type = json_value(output_text, "type", std::string());
|
const std::string type = json_value(output_text, "type", std::string());
|
||||||
|
|
@ -1210,10 +1212,19 @@ json convert_responses_to_chatcmpl(const json & response_body) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
item.erase("status");
|
if (merge_prev) {
|
||||||
item.erase("type");
|
auto & prev_msg = chatcmpl_messages.back();
|
||||||
item["content"] = chatcmpl_content;
|
if (!exists_and_is_array(prev_msg, "content")) {
|
||||||
chatcmpl_messages.push_back(item);
|
prev_msg["content"] = json::array();
|
||||||
|
}
|
||||||
|
auto & prev_content = prev_msg["content"];
|
||||||
|
prev_content.insert(prev_content.end(), chatcmpl_content.begin(), chatcmpl_content.end());
|
||||||
|
} else {
|
||||||
|
item.erase("status");
|
||||||
|
item.erase("type");
|
||||||
|
item["content"] = chatcmpl_content;
|
||||||
|
chatcmpl_messages.push_back(item);
|
||||||
|
}
|
||||||
} else if (exists_and_is_string(item, "arguments") &&
|
} else if (exists_and_is_string(item, "arguments") &&
|
||||||
exists_and_is_string(item, "call_id") &&
|
exists_and_is_string(item, "call_id") &&
|
||||||
exists_and_is_string(item, "name") &&
|
exists_and_is_string(item, "name") &&
|
||||||
|
|
@ -1221,24 +1232,27 @@ json convert_responses_to_chatcmpl(const json & response_body) {
|
||||||
item.at("type") == "function_call"
|
item.at("type") == "function_call"
|
||||||
) {
|
) {
|
||||||
// #responses_create-input-input_item_list-item-function_tool_call
|
// #responses_create-input-input_item_list-item-function_tool_call
|
||||||
json msg = json {
|
json tool_call = {
|
||||||
{"role", "assistant"},
|
{"function", json {
|
||||||
{"tool_calls", json::array({ json {
|
{"arguments", item.at("arguments")},
|
||||||
{"function", json {
|
{"name", item.at("name")},
|
||||||
{"arguments", item.at("arguments")},
|
}},
|
||||||
{"name", item.at("name")},
|
{"id", item.at("call_id")},
|
||||||
}},
|
{"type", "function"},
|
||||||
{"id", item.at("call_id")},
|
|
||||||
{"type", "function"},
|
|
||||||
}})},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!chatcmpl_messages.empty() && chatcmpl_messages.back().contains("reasoning_content")) {
|
if (merge_prev) {
|
||||||
// Move reasoning content from dummy message to tool call message
|
auto & prev_msg = chatcmpl_messages.back();
|
||||||
msg["reasoning_content"] = chatcmpl_messages.back().at("reasoning_content");
|
if (!exists_and_is_array(prev_msg, "tool_calls")) {
|
||||||
chatcmpl_messages.pop_back();
|
prev_msg["tool_calls"] = json::array();
|
||||||
|
}
|
||||||
|
prev_msg["tool_calls"].push_back(tool_call);
|
||||||
|
} else {
|
||||||
|
chatcmpl_messages.push_back(json {
|
||||||
|
{"role", "assistant"},
|
||||||
|
{"tool_calls", json::array({tool_call})}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
chatcmpl_messages.push_back(msg);
|
|
||||||
} else if (exists_and_is_string(item, "call_id") &&
|
} else if (exists_and_is_string(item, "call_id") &&
|
||||||
(exists_and_is_string(item, "output") || exists_and_is_array(item, "output")) &&
|
(exists_and_is_string(item, "output") || exists_and_is_array(item, "output")) &&
|
||||||
exists_and_is_string(item, "type") &&
|
exists_and_is_string(item, "type") &&
|
||||||
|
|
@ -1282,12 +1296,16 @@ json convert_responses_to_chatcmpl(const json & response_body) {
|
||||||
throw std::invalid_argument("item['content']['text'] is not a string");
|
throw std::invalid_argument("item['content']['text'] is not a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pack reasoning content in dummy message
|
if (merge_prev) {
|
||||||
chatcmpl_messages.push_back(json {
|
auto & prev_msg = chatcmpl_messages.back();
|
||||||
{"role", "assistant"},
|
prev_msg["reasoning_content"] = item.at("content")[0].at("text");
|
||||||
{"content", json::array()},
|
} else {
|
||||||
{"reasoning_content", item.at("content")[0].at("text")},
|
chatcmpl_messages.push_back(json {
|
||||||
});
|
{"role", "assistant"},
|
||||||
|
{"content", json::array()},
|
||||||
|
{"reasoning_content", item.at("content")[0].at("text")},
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw std::invalid_argument("Cannot determine type of 'item'");
|
throw std::invalid_argument("Cannot determine type of 'item'");
|
||||||
}
|
}
|
||||||
|
|
@ -1296,20 +1314,6 @@ json convert_responses_to_chatcmpl(const json & response_body) {
|
||||||
throw std::invalid_argument("'input' must be a string or array of objects");
|
throw std::invalid_argument("'input' must be a string or array of objects");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove unused dummy message which contains
|
|
||||||
// reasoning content not followed by tool call
|
|
||||||
chatcmpl_messages.erase(std::remove_if(
|
|
||||||
chatcmpl_messages.begin(),
|
|
||||||
chatcmpl_messages.end(),
|
|
||||||
[](const json & x){ return x.contains("role") &&
|
|
||||||
x.at("role") == "assistant" &&
|
|
||||||
x.contains("content") &&
|
|
||||||
x.at("content") == json::array() &&
|
|
||||||
x.contains("reasoning_content");
|
|
||||||
}),
|
|
||||||
chatcmpl_messages.end()
|
|
||||||
);
|
|
||||||
|
|
||||||
chatcmpl_body["messages"] = chatcmpl_messages;
|
chatcmpl_body["messages"] = chatcmpl_messages;
|
||||||
|
|
||||||
if (response_body.contains("tools")) {
|
if (response_body.contains("tools")) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue