8#include "absl/strings/str_format.h"
9#include "absl/time/time.h"
10#include "imgui/imgui.h"
11#include "imgui/misc/cpp/imgui_stdlib.h"
15#include "nlohmann/json.hpp"
24std::string ResolveAgentChatHistoryPath() {
27 return (*agent_dir /
"agent_chat_history.json").string();
31 return (*docs_dir /
"agent_chat_history.json").string();
35 return (*temp_dir /
"agent_chat_history.json").string();
37 return (std::filesystem::current_path() /
"agent_chat_history.json").string();
43 : scroll_to_bottom_(false),
45 show_timestamps_(true),
46 show_reasoning_(false),
47 message_spacing_(12.0f),
60 agent_service_ = std::make_unique<cli::agent::ConversationalAgentService>();
77 ImGui::Begin(
"Agent Chat", p_open);
80 "Build with -DZ3ED_AI=ON to enable the conversational agent.");
85 ImGui::SetNextWindowSize(ImVec2(600, 500), ImGuiCond_FirstUseEver);
86 if (!ImGui::Begin(
"Z3ED Agent Chat", p_open)) {
96 ImGui::BeginChild(
"ChatHistory",
97 ImVec2(0, -ImGui::GetFrameHeightWithSpacing() - 60),
true,
98 ImGuiWindowFlags_AlwaysVerticalScrollbar);
103 (
auto_scroll_ && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) {
104 ImGui::SetScrollHereY(1.0f);
118 if (ImGui::Button(
"Clear History")) {
123 if (ImGui::Button(
"Save History")) {
124 std::string filepath = ResolveAgentChatHistoryPath();
125 if (
auto status =
SaveHistory(filepath); !status.ok()) {
126 std::cerr <<
"Failed to save history: " << status.message() << std::endl;
128 std::cout <<
"Saved chat history to: " << filepath << std::endl;
133 if (ImGui::Button(
"Load History")) {
134 std::string filepath = ResolveAgentChatHistoryPath();
135 if (
auto status =
LoadHistory(filepath); !status.ok()) {
136 std::cerr <<
"Failed to load history: " << status.message() << std::endl;
157 if (history.empty()) {
160 "No messages yet. Type a message below to start chatting!");
164 for (
size_t i = 0; i < history.size(); ++i) {
180 std::string timestamp =
181 absl::FormatTime(
"%H:%M:%S", msg.
timestamp, absl::LocalTimeZone());
187 const char* sender_label = is_user ?
"You" :
"Agent";
189 ImGui::TextColored(sender_color,
"%s:", sender_label);
192 ImGui::Indent(20.0f);
197 }
else if (!is_user && msg.
json_pretty.has_value()) {
198 ImGui::TextWrapped(
"%s", msg.
json_pretty.value().c_str());
201 ImGui::TextWrapped(
"%s", msg.
message.c_str());
204 ImGui::Unindent(20.0f);
214 if (ImGui::BeginTable(
"ToolResultTable", table.
headers.size(),
215 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
216 ImGuiTableFlags_ScrollY)) {
218 for (
const auto& header : table.
headers) {
219 ImGui::TableSetupColumn(header.c_str());
221 ImGui::TableHeadersRow();
224 for (
const auto& row : table.
rows) {
225 ImGui::TableNextRow();
226 for (
size_t col = 0; col < std::min(row.size(), table.
headers.size());
228 ImGui::TableSetColumnIndex(col);
229 ImGui::TextWrapped(
"%s", row[col].c_str());
239 ImGui::Text(
"Message:");
242 ImGui::PushItemWidth(-1);
243 bool enter_pressed = ImGui::InputTextMultiline(
245 ImGuiInputTextFlags_EnterReturnsTrue);
246 ImGui::PopItemWidth();
249 if (ImGui::Button(
"Send", ImVec2(100, 0)) || enter_pressed) {
253 ImGui::SetKeyboardFocusHere(-1);
259 "Tip: Press Enter to send (Shift+Enter for newline)");
271 std::cerr <<
"Error processing message: " << result.status() << std::endl;
287#if defined(Z3ED_AI) && defined(YAZE_WITH_JSON)
289 return absl::FailedPreconditionError(
"Agent service not initialized");
292 std::ifstream file(filepath);
293 if (!file.is_open()) {
294 return absl::NotFoundError(
295 absl::StrFormat(
"Could not open file: %s", filepath));
306 return absl::OkStatus();
307 }
catch (
const nlohmann::json::exception& e) {
308 return absl::InvalidArgumentError(
309 absl::StrFormat(
"Failed to parse JSON: %s", e.what()));
312 return absl::UnimplementedError(
"AI features not available");
317#if defined(Z3ED_AI) && defined(YAZE_WITH_JSON)
319 return absl::FailedPreconditionError(
"Agent service not initialized");
322 std::filesystem::path path(filepath);
323 if (path.has_parent_path()) {
325 std::filesystem::create_directories(path.parent_path(), ec);
327 return absl::InternalError(absl::StrFormat(
328 "Failed to create history directory: %s", ec.message()));
332 std::ofstream file(filepath);
333 if (!file.is_open()) {
334 return absl::InternalError(
335 absl::StrFormat(
"Could not create file: %s", filepath));
343 j[
"messages"] = nlohmann::json::array();
345 for (
const auto& msg : history) {
346 nlohmann::json msg_json;
350 msg_json[
"message"] = msg.message;
351 msg_json[
"timestamp"] = absl::FormatTime(msg.timestamp);
352 j[
"messages"].push_back(msg_json);
357 return absl::OkStatus();
358 }
catch (
const nlohmann::json::exception& e) {
359 return absl::InternalError(
360 absl::StrFormat(
"Failed to serialize JSON: %s", e.what()));
363 return absl::UnimplementedError(
"AI features not available");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
std::vector< std::string > headers
std::vector< std::vector< std::string > > rows
std::optional< TableData > table_data
std::optional< std::string > json_pretty