Entropic 2.14.3
Local-first agentic inference engine
Loading...
Searching...
No Matches
messages_json.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
10
11#include <nlohmann/json.hpp>
12
13#include <string>
14#include <unordered_map>
15
16namespace entropic {
17
18namespace {
19
27ContentPart parse_content_part(const nlohmann::json& part) {
28 ContentPart cp;
29 auto type_str = part.value("type", "text");
30 if (type_str == "image") {
32 cp.image_path = part.value("path", "");
33 cp.image_url = part.value("url", "");
34 // gh#165 (v2.13.0): geometry is set by the image preprocessor, so a
35 // restore that dropped it would silently re-run preprocessing on a
36 // conversation that had already paid for it. Absent = 0 = "not yet
37 // processed", which is the struct's own default.
38 cp.width = part.value("width", 0);
39 cp.height = part.value("height", 0);
40 } else {
42 cp.text = part.value("text", "");
43 }
44 return cp;
45}
46
60void parse_metadata(const nlohmann::json& m,
61 std::unordered_map<std::string, std::string>& out) {
62 if (!m.contains("metadata") || !m["metadata"].is_object()) { return; }
63 for (const auto& [k, v] : m["metadata"].items()) {
64 out[k] = v.is_string() ? v.get<std::string>() : v.dump();
65 }
66}
67
75Message parse_one_message(const nlohmann::json& m) {
76 Message msg;
77 msg.role = m.value("role", "user");
78 if (m.contains("content") && m["content"].is_array()) {
79 // The INBOUND multimodal shape (entropic_run_messages): parts live
80 // in `content` and the text is derived from them.
81 for (const auto& part : m["content"]) {
82 msg.content_parts.push_back(parse_content_part(part));
83 }
84 msg.content = extract_text(msg.content_parts);
85 } else {
86 msg.content = m.value("content", "");
87 // gh#165 (v2.13.0): the OUTBOUND shape `serialize_messages` emits —
88 // `content` stays a string (so a consumer reading it is unaffected)
89 // and parts ride alongside. Reading both here is what closes the
90 // get -> set -> get round trip.
91 if (m.contains("content_parts") && m["content_parts"].is_array()) {
92 for (const auto& part : m["content_parts"]) {
93 msg.content_parts.push_back(parse_content_part(part));
94 }
95 }
96 }
97 parse_metadata(m, msg.metadata);
98 return msg;
99}
100
101} // namespace
102
110std::vector<Message> parse_messages_json(const char* json_str) {
111 std::vector<Message> messages;
112 if (json_str == nullptr) { return messages; }
113 auto arr = nlohmann::json::parse(json_str);
114 if (!arr.is_array()) { return messages; }
115 for (const auto& m : arr) {
116 messages.push_back(parse_one_message(m));
117 }
118 return messages;
119}
120
128bool any_message_has_images(const std::vector<Message>& messages) {
129 for (const auto& m : messages) {
130 if (has_images(m.content_parts)) { return true; }
131 }
132 return false;
133}
134
135} // namespace entropic
Multimodal content types for messages.
Shared parser: messages-JSON wire format → vector<Message>.
Activate model on GPU (WARM → ACTIVE).
@ IMAGE
Image content (local path or data URI)
@ TEXT
Plain text content.
bool any_message_has_images(const std::vector< Message > &messages)
Convenience: true if any message carries image content_parts.
bool has_images(const std::vector< ContentPart > &parts)
Check if content parts contain any image parts.
Definition content.cpp:41
std::string extract_text(const std::vector< ContentPart > &parts)
Extract concatenated text from content parts.
Definition content.cpp:20
@ optional
optional: every escape asks the host's path approver
std::vector< Message > parse_messages_json(const char *json_str)
Parse a JSON array of messages into a vector of Message.