9#include "absl/strings/str_format.h"
10#include "absl/time/time.h"
12#include "imgui/imgui.h"
13#include "imgui/misc/cpp/imgui_stdlib.h"
17#include "nlohmann/json.hpp"
26std::string ResolveAgentChatHistoryPath() {
29 return (*agent_dir /
"agent_chat_history.json").string();
33 return (*temp_dir /
"agent_chat_history.json").string();
35 return (std::filesystem::current_path() /
"agent_chat_history.json").string();
41 : scroll_to_bottom_(false),
43 show_timestamps_(true),
44 show_reasoning_(false),
45 message_spacing_(12.0f),
50 agent_service_ = std::make_unique<cli::agent::ConversationalAgentService>();
68 ImGui::Begin(
"Agent Chat", p_open);
71 tr(
"Build with -DZ3ED_AI=ON to enable the conversational agent."));
76 ImGui::SetNextWindowSize(ImVec2(600, 500), ImGuiCond_FirstUseEver);
77 if (!ImGui::Begin(
"Z3ED Agent Chat", p_open)) {
87 ImGui::BeginChild(
"ChatHistory",
88 ImVec2(0, -ImGui::GetFrameHeightWithSpacing() - 60),
true,
89 ImGuiWindowFlags_AlwaysVerticalScrollbar);
94 (
auto_scroll_ && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) {
95 ImGui::SetScrollHereY(1.0f);
119 if (ImGui::Button(tr(
"Clear History"))) {
124 if (ImGui::Button(tr(
"Save History"))) {
125 std::string filepath = ResolveAgentChatHistoryPath();
126 if (
auto status =
SaveHistory(filepath); !status.ok()) {
127 std::cerr <<
"Failed to save history: " << status.message() << std::endl;
129 std::cout <<
"Saved chat history to: " << filepath << std::endl;
134 if (ImGui::Button(tr(
"Load History"))) {
135 std::string filepath = ResolveAgentChatHistoryPath();
136 if (
auto status =
LoadHistory(filepath); !status.ok()) {
137 std::cerr <<
"Failed to load history: " << status.message() << std::endl;
158 if (history.empty()) {
161 tr(
"No messages yet. Type a message below to start chatting!"));
165 for (
size_t i = 0; i < history.size(); ++i) {
181 std::string timestamp =
182 absl::FormatTime(
"%H:%M:%S", msg.
timestamp, absl::LocalTimeZone());
188 const char* sender_label = is_user ?
"You" :
"Agent";
190 ImGui::TextColored(sender_color,
"%s:", sender_label);
193 ImGui::Indent(20.0f);
198 }
else if (!is_user && msg.
json_pretty.has_value()) {
199 ImGui::TextWrapped(
"%s", msg.
json_pretty.value().c_str());
202 ImGui::TextWrapped(
"%s", msg.
message.c_str());
205 ImGui::Unindent(20.0f);
215 if (ImGui::BeginTable(
"ToolResultTable", table.
headers.size(),
216 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
217 ImGuiTableFlags_ScrollY)) {
219 for (
const auto& header : table.
headers) {
220 ImGui::TableSetupColumn(header.c_str());
222 ImGui::TableHeadersRow();
225 for (
const auto& row : table.
rows) {
226 ImGui::TableNextRow();
227 for (
size_t col = 0; col < std::min(row.size(), table.
headers.size());
229 ImGui::TableSetColumnIndex(col);
230 ImGui::TextWrapped(
"%s", row[col].c_str());
240 ImGui::Text(tr(
"Message:"));
243 ImGui::PushItemWidth(-1);
244 bool enter_pressed = ImGui::InputTextMultiline(
246 ImGuiInputTextFlags_EnterReturnsTrue);
247 ImGui::PopItemWidth();
250 if (ImGui::Button(tr(
"Send"), ImVec2(100, 0)) || enter_pressed) {
254 ImGui::SetKeyboardFocusHere(-1);
260 tr(
"Tip: Press Enter to send (Shift+Enter for newline)"));
272 std::cerr <<
"Error processing message: " << result.status() << std::endl;
288#if defined(Z3ED_AI) && defined(YAZE_WITH_JSON)
290 return absl::FailedPreconditionError(
"Agent service not initialized");
293 std::ifstream file(filepath);
294 if (!file.is_open()) {
295 return absl::NotFoundError(
296 absl::StrFormat(
"Could not open file: %s", filepath));
307 return absl::OkStatus();
308 }
catch (
const nlohmann::json::exception& e) {
309 return absl::InvalidArgumentError(
310 absl::StrFormat(
"Failed to parse JSON: %s", e.what()));
313 return absl::UnimplementedError(
"AI features not available");
318#if defined(Z3ED_AI) && defined(YAZE_WITH_JSON)
320 return absl::FailedPreconditionError(
"Agent service not initialized");
323 std::filesystem::path path(filepath);
324 if (path.has_parent_path()) {
326 std::filesystem::create_directories(path.parent_path(), ec);
328 return absl::InternalError(absl::StrFormat(
329 "Failed to create history directory: %s", ec.message()));
333 std::ofstream file(filepath);
334 if (!file.is_open()) {
335 return absl::InternalError(
336 absl::StrFormat(
"Could not create file: %s", filepath));
344 j[
"messages"] = nlohmann::json::array();
346 for (
const auto& msg : history) {
347 nlohmann::json msg_json;
351 msg_json[
"message"] = msg.message;
352 msg_json[
"timestamp"] = absl::FormatTime(msg.timestamp);
353 j[
"messages"].push_back(msg_json);
358 return absl::OkStatus();
359 }
catch (
const nlohmann::json::exception& e) {
360 return absl::InternalError(
361 absl::StrFormat(
"Failed to serialize JSON: %s", e.what()));
364 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...
const Theme & GetCurrentTheme() const
static ThemeManager & Get()
ImVec4 ConvertColorToImVec4(const Color &color)
std::vector< std::string > headers
std::vector< std::vector< std::string > > rows
std::optional< TableData > table_data
std::optional< std::string > json_pretty