Skip to content

Commit cfa861a

Browse files
authored
Merge pull request #760 from henfiber/accept-content-array
Accept array in chat message content field
2 parents ff0c02e + 1562542 commit cfa861a

File tree

2 files changed

+53
-10
lines changed

2 files changed

+53
-10
lines changed

llama.cpp/server/utils.h

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,24 @@ inline std::string format_chat(const struct llama_model * model, const std::stri
215215

216216
for (size_t i = 0; i < messages.size(); ++i) {
217217
auto &curr_msg = messages[i];
218-
str[i*2 + 0] = json_value(curr_msg, "role", std::string(""));
219-
str[i*2 + 1] = json_value(curr_msg, "content", std::string(""));
220-
alloc_size += str[i*2 + 1].length();
221-
chat[i].role = str[i*2 + 0].c_str();
218+
str[i*2 + 0] = json_value(curr_msg, "role", std::string(""));
219+
220+
// Handle content as string or array
221+
std::string content;
222+
if (curr_msg.contains("content") && curr_msg["content"].is_array()) {
223+
for (auto &part : curr_msg["content"]) {
224+
if (part.contains("type") && part["type"] == "text" && part.contains("text") && part["text"].is_string()) {
225+
content += part["text"].get<std::string>();
226+
}
227+
// TODO: handle image_url data (?)
228+
}
229+
} else {
230+
content = json_value(curr_msg, "content", std::string(""));
231+
}
232+
233+
str[i*2 + 1] = content;
234+
alloc_size += str[i*2 + 1].length();
235+
chat[i].role = str[i*2 + 0].c_str();
222236
chat[i].content = str[i*2 + 1].c_str();
223237
}
224238

llamafile/server/v1_chat_completions.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,41 @@ Client::get_v1_chat_completions_params(V1ChatCompletionParams* params)
244244
return send_error(400, "message must have string role");
245245
if (!is_legal_role(message["role"].getString()))
246246
return send_error(400, "message role not system user assistant");
247-
if (!message["content"].isString())
248-
return send_error(400, "message must have string content");
249-
if (message["content"].getString().empty())
250-
return send_error(400, "message must not have empty content");
251-
params->messages.emplace_back(message["role"].getString(),
252-
message["content"].getString());
247+
248+
// Accept string or array for content
249+
if (message["content"].isString()) {
250+
if (message["content"].getString().empty())
251+
return send_error(400, "message must not have empty content");
252+
params->messages.emplace_back(message["role"].getString(),
253+
message["content"].getString());
254+
} else if (message["content"].isArray()) {
255+
std::string combined_content;
256+
std::vector<Json>& content_array = message["content"].getArray();
257+
if (content_array.empty())
258+
return send_error(400, "message content array must not be empty");
259+
for (Json& part : content_array) {
260+
if (!part.isObject() || !part["type"].isString())
261+
return send_error(400, "content array items must be objects with type");
262+
std::string type = part["type"].getString();
263+
if (type == "text") {
264+
if (!part["text"].isString())
265+
return send_error(400, "text part must have string text");
266+
combined_content += part["text"].getString();
267+
} else if (type == "image_url") {
268+
if (!part["image_url"].isObject() || !part["image_url"]["url"].isString())
269+
return send_error(400, "image_url part must have url string");
270+
// TODO collect image data (?)
271+
// std::string image_url = part["image_url"]["url"].getString();
272+
} else {
273+
return send_error(400, "unsupported content part type");
274+
}
275+
}
276+
if (combined_content.empty())
277+
return send_error(400, "message must not have empty content");
278+
params->messages.emplace_back(message["role"].getString(), combined_content);
279+
} else {
280+
return send_error(400, "message content must be string or array");
281+
}
253282
}
254283

255284
// n: integer|null

0 commit comments

Comments
 (0)