22#if defined(YAZE_WITH_JSON)
23using Json = nlohmann::json;
25absl::Time ParseTimestamp(
const Json& value) {
26 if (!value.is_string()) {
30 if (absl::ParseTime(absl::RFC3339_full, value.get<std::string>(), &parsed,
39 json[
"headers"] = table.
headers;
40 json[
"rows"] = table.
rows;
44std::optional<cli::agent::ChatMessage::TableData> ParseTableData(
46 if (!json.is_object()) {
51 if (json.contains(
"headers") && json[
"headers"].is_array()) {
52 for (
const auto& header : json[
"headers"]) {
53 if (header.is_string()) {
54 table.
headers.push_back(header.get<std::string>());
59 if (json.contains(
"rows") && json[
"rows"].is_array()) {
60 for (
const auto& row : json[
"rows"]) {
61 if (!row.is_array()) {
64 std::vector<std::string> row_values;
65 for (
const auto& value : row) {
66 if (value.is_string()) {
67 row_values.push_back(value.get<std::string>());
69 row_values.push_back(value.dump());
72 table.
rows.push_back(std::move(row_values));
85 json[
"id"] = proposal.
id;
93std::optional<cli::agent::ChatMessage::ProposalSummary> ParseProposal(
95 if (!json.is_object()) {
100 summary.
id = json.value(
"id",
"");
103 if (json.contains(
"sandbox_rom_path") && json[
"sandbox_rom_path"].is_string()) {
106 if (json.contains(
"proposal_json_path") &&
107 json[
"proposal_json_path"].is_string()) {
110 if (summary.
id.empty()) {
129 const std::filesystem::path& path) {
130#if defined(YAZE_WITH_JSON)
133 std::ifstream file(path);
141 }
catch (
const std::exception& e) {
142 return absl::InternalError(
143 absl::StrFormat(
"Failed to parse chat history: %s", e.what()));
146 if (!json.contains(
"messages") || !json[
"messages"].is_array()) {
150 for (
const auto& item : json[
"messages"]) {
151 if (!item.is_object()) {
156 std::string sender = item.value(
"sender",
"agent");
157 message.
sender = sender ==
"user"
160 message.
message = item.value(
"message",
"");
161 message.
timestamp = ParseTimestamp(item[
"timestamp"]);
162 message.
is_internal = item.value(
"is_internal",
false);
164 if (item.contains(
"json_pretty") && item[
"json_pretty"].is_string()) {
165 message.
json_pretty = item[
"json_pretty"].get<std::string>();
167 if (item.contains(
"table_data")) {
168 message.
table_data = ParseTableData(item[
"table_data"]);
170 if (item.contains(
"metrics") && item[
"metrics"].is_object()) {
172 const auto& metrics_json = item[
"metrics"];
173 metrics.
turn_index = metrics_json.value(
"turn_index", 0);
175 metrics_json.value(
"total_user_messages", 0);
177 metrics_json.value(
"total_agent_messages", 0);
182 metrics_json.value(
"total_elapsed_seconds", 0.0);
184 metrics_json.value(
"average_latency_seconds", 0.0);
187 if (item.contains(
"proposal")) {
188 message.
proposal = ParseProposal(item[
"proposal"]);
191 snapshot.
history.push_back(std::move(message));
194 if (json.contains(
"collaboration") && json[
"collaboration"].is_object()) {
195 const auto& collab_json = json[
"collaboration"];
199 collab_json.value(
"session_name",
"");
201 if (collab_json.contains(
"participants") &&
202 collab_json[
"participants"].is_array()) {
203 for (
const auto& participant : collab_json[
"participants"]) {
204 if (participant.is_string()) {
206 participant.get<std::string>());
210 if (collab_json.contains(
"last_synced")) {
212 ParseTimestamp(collab_json[
"last_synced"]);
221 if (json.contains(
"multimodal") && json[
"multimodal"].is_object()) {
222 const auto& multimodal_json = json[
"multimodal"];
223 if (multimodal_json.contains(
"last_capture_path") &&
224 multimodal_json[
"last_capture_path"].is_string()) {
225 std::string path_value =
226 multimodal_json[
"last_capture_path"].get<std::string>();
227 if (!path_value.empty()) {
229 std::filesystem::path(path_value);
233 multimodal_json.value(
"status_message",
"");
234 if (multimodal_json.contains(
"last_updated")) {
236 ParseTimestamp(multimodal_json[
"last_updated"]);
243 return absl::UnimplementedError(
244 "Chat history persistence requires YAZE_WITH_GRPC=ON");
249 const std::filesystem::path& path,
const Snapshot& snapshot) {
250#if defined(YAZE_WITH_JSON)
253 json[
"messages"] = Json::array();
255 for (
const auto& message : snapshot.
history) {
260 entry[
"message"] = message.message;
261 entry[
"timestamp"] = absl::FormatTime(absl::RFC3339_full,
263 absl::UTCTimeZone());
264 entry[
"is_internal"] = message.is_internal;
266 if (message.json_pretty.has_value()) {
267 entry[
"json_pretty"] = *message.json_pretty;
269 if (message.table_data.has_value()) {
270 entry[
"table_data"] = SerializeTableData(*message.table_data);
272 if (message.metrics.has_value()) {
273 const auto& metrics = *message.metrics;
275 metrics_json[
"turn_index"] = metrics.turn_index;
276 metrics_json[
"total_user_messages"] = metrics.total_user_messages;
277 metrics_json[
"total_agent_messages"] = metrics.total_agent_messages;
278 metrics_json[
"total_tool_calls"] = metrics.total_tool_calls;
279 metrics_json[
"total_commands"] = metrics.total_commands;
280 metrics_json[
"total_proposals"] = metrics.total_proposals;
281 metrics_json[
"total_elapsed_seconds"] = metrics.total_elapsed_seconds;
282 metrics_json[
"average_latency_seconds"] =
283 metrics.average_latency_seconds;
284 entry[
"metrics"] = metrics_json;
286 if (message.proposal.has_value()) {
287 entry[
"proposal"] = SerializeProposal(*message.proposal);
290 json[
"messages"].push_back(std::move(entry));
299 collab_json[
"last_synced"] = absl::FormatTime(
301 absl::UTCTimeZone());
303 json[
"collaboration"] = std::move(collab_json);
305 Json multimodal_json;
307 multimodal_json[
"last_capture_path"] =
310 multimodal_json[
"last_capture_path"] =
"";
314 multimodal_json[
"last_updated"] = absl::FormatTime(
316 absl::UTCTimeZone());
318 json[
"multimodal"] = std::move(multimodal_json);
321 auto directory = path.parent_path();
322 if (!directory.empty()) {
323 std::filesystem::create_directories(directory, ec);
325 return absl::InternalError(absl::StrFormat(
326 "Unable to create chat history directory: %s", ec.message()));
330 std::ofstream file(path);
331 if (!file.is_open()) {
332 return absl::InternalError(
"Cannot write chat history file");
335 file << json.dump(2);
336 return absl::OkStatus();
340 return absl::UnimplementedError(
341 "Chat history persistence requires YAZE_WITH_GRPC=ON");