7#include "absl/strings/str_format.h"
8#include "absl/time/time.h"
14#include "imgui/imgui.h"
15#include "imgui/misc/cpp/imgui_stdlib.h"
19#include "nlohmann/json.hpp"
58 if (message.empty())
return;
75 LOG_ERROR(
"AgentChat",
"Agent Error: %s", response.status().ToString().c_str());
85 ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4, 4));
92 float input_height = ImGui::GetTextLineHeightWithSpacing() * 4 + 20.0f;
93 float toolbar_height = ImGui::GetFrameHeightWithSpacing() + 8.0f;
94 float history_height = available_height > 0
95 ? (available_height - input_height - toolbar_height)
96 : -input_height - toolbar_height;
98 if (ImGui::BeginChild(
"##ChatHistory", ImVec2(0, history_height),
true)) {
102 (
auto_scroll_ && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())) {
103 ImGui::SetScrollHereY(1.0f);
112 ImGui::PopStyleVar();
122 std::string filepath =
".yaze/agent_chat_history.json";
123 if (
auto status =
SaveHistory(filepath); !status.ok()) {
136 std::string filepath =
".yaze/agent_chat_history.json";
137 if (
auto status =
LoadHistory(filepath); !status.ok()) {
161 if (history.empty()) {
162 ImGui::TextDisabled(
"Start a conversation with the agent...");
165 for (
size_t i = 0; i < history.size(); ++i) {
180 ImGui::PushID(index);
183 float wrap_width = ImGui::GetContentRegionAvail().x * 0.85f;
184 ImGui::SetCursorPosX(is_user ? (ImGui::GetWindowContentRegionMax().x - wrap_width - 10) : 10);
190 std::string timestamp =
191 absl::FormatTime(
"%H:%M:%S", msg.
timestamp, absl::LocalTimeZone());
192 ImGui::TextColored(ImVec4(0.5f, 0.5f, 0.5f, 1.0f),
"[%s]", timestamp.c_str());
198 ImGui::TextColored(ImVec4(0.4f, 0.8f, 1.0f, 1.0f),
"%s You",
ICON_MD_PERSON);
200 ImGui::TextColored(ImVec4(0.6f, 1.0f, 0.6f, 1.0f),
"%s Agent",
ICON_MD_SMART_TOY);
204 ImVec4 bg_col = is_user ? ImVec4(0.2f, 0.2f, 0.25f, 1.0f) : ImVec4(0.25f, 0.25f, 0.25f, 1.0f);
205 ImGui::PushStyleColor(ImGuiCol_ChildBg, bg_col);
206 ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 8.0f);
208 std::string content_id =
"msg_content_" + std::to_string(index);
209 if (ImGui::BeginChild(content_id.c_str(), ImVec2(wrap_width, 0),
true, ImGuiWindowFlags_AlwaysUseWindowPadding)) {
213 }
else if (!is_user && msg.
json_pretty.has_value()) {
214 ImGui::TextWrapped(
"%s", msg.
json_pretty.value().c_str());
218 for (
const auto& block : blocks) {
222 ImGui::TextWrapped(
"%s", block.content.c_str());
239 ImGui::PopStyleVar();
240 ImGui::PopStyleColor();
256 if (dots == 0) ImGui::Text(
".");
257 else if (dots == 1) ImGui::Text(
"..");
258 else if (dots == 2) ImGui::Text(
"...");
267 ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CtrlEnterForNewLine;
269 ImGui::PushItemWidth(-1);
270 if (ImGui::IsWindowAppearing()) {
271 ImGui::SetKeyboardFocusHere();
279 while (!msg.empty() && std::isspace(msg.back())) msg.pop_back();
284 ImGui::SetKeyboardFocusHere(-1);
288 ImGui::PopItemWidth();
294 if (msg.
message.find(
"Proposal:") != std::string::npos) {
296 if (ImGui::Button(
"View Proposal")) {
306 ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.1f, 0.1f, 0.1f, 1.0f));
307 if (ImGui::BeginChild(absl::StrCat(
"code_", msg_index).c_str(), ImVec2(0, 0),
true, ImGuiWindowFlags_AlwaysAutoResize)) {
308 if (!language.empty()) {
309 ImGui::TextDisabled(
"%s", language.c_str());
313 ImGui::SetClipboardText(code.c_str());
317 ImGui::TextUnformatted(code.c_str());
320 ImGui::PopStyleColor();
336 std::vector<ContentBlock> blocks;
340 while (pos < content.length()) {
341 size_t code_start = content.find(
"```", pos);
342 if (code_start == std::string::npos) {
349 if (code_start > pos) {
353 size_t code_end = content.find(
"```", code_start + 3);
354 if (code_end == std::string::npos) {
361 std::string language;
362 size_t newline = content.find(
'\n', code_start + 3);
363 size_t content_start = code_start + 3;
364 if (newline != std::string::npos && newline < code_end) {
365 language = content.substr(code_start + 3, newline - (code_start + 3));
366 content_start = newline + 1;
369 std::string code = content.substr(content_start, code_end - content_start);
384 if (ImGui::BeginTable(
"ToolResultTable",
static_cast<int>(table.
headers.size()),
385 ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
386 ImGuiTableFlags_ScrollY)) {
388 for (
const auto& header : table.
headers) {
389 ImGui::TableSetupColumn(header.c_str());
391 ImGui::TableHeadersRow();
394 for (
const auto& row : table.
rows) {
395 ImGui::TableNextRow();
396 for (
size_t col = 0; col < std::min(row.size(), table.
headers.size()); ++col) {
397 ImGui::TableSetColumnIndex(
static_cast<int>(col));
398 ImGui::TextWrapped(
"%s", row[col].c_str());
415 if (meta.tool_names.empty() && meta.tool_iterations == 0) {
423 ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.15f, 0.15f, 0.18f, 1.0f));
424 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.2f, 0.2f, 0.25f, 1.0f));
427 meta.tool_iterations, meta.latency_seconds);
429 if (ImGui::TreeNode(
"##ToolTimeline",
"%s", header.c_str())) {
431 if (!meta.tool_names.empty()) {
432 ImGui::TextDisabled(
"Tools called:");
433 for (
const auto& tool : meta.tool_names) {
434 ImGui::BulletText(
"%s", tool.c_str());
440 ImGui::TextDisabled(
"Provider: %s", meta.provider.c_str());
441 if (!meta.model.empty()) {
442 ImGui::TextDisabled(
"Model: %s", meta.model.c_str());
448 ImGui::PopStyleColor(2);
453 std::ifstream file(filepath);
454 if (!file.is_open()) {
455 return absl::NotFoundError(
456 absl::StrFormat(
"Could not open file: %s", filepath));
468 return absl::OkStatus();
469 }
catch (
const nlohmann::json::exception& e) {
470 return absl::InvalidArgumentError(
471 absl::StrFormat(
"Failed to parse JSON: %s", e.what()));
474 return absl::UnimplementedError(
"JSON support not available");
481 std::filesystem::path path(filepath);
482 if (path.has_parent_path()) {
483 std::filesystem::create_directories(path.parent_path());
486 std::ofstream file(filepath);
487 if (!file.is_open()) {
488 return absl::InternalError(
489 absl::StrFormat(
"Could not create file: %s", filepath));
497 j[
"messages"] = nlohmann::json::array();
499 for (
const auto& msg : history) {
500 nlohmann::json msg_json;
502 msg_json[
"message"] = msg.message;
503 msg_json[
"timestamp"] = absl::FormatTime(msg.timestamp);
504 j[
"messages"].push_back(msg_json);
509 return absl::OkStatus();
510 }
catch (
const nlohmann::json::exception& e) {
511 return absl::InternalError(
512 absl::StrFormat(
"Failed to serialize JSON: %s", e.what()));
515 return absl::UnimplementedError(
"JSON support not available");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
absl::StatusOr< ChatMessage > SendMessage(const std::string &message)
const std::vector< ChatMessage > & GetHistory() const
std::vector< ContentBlock > ParseMessageContent(const std::string &content)
float thinking_animation_
void RenderCodeBlock(const std::string &code, const std::string &language, int msg_index)
absl::Status LoadHistory(const std::string &filepath)
void RenderThinkingIndicator()
void SendMessage(const std::string &message)
void RenderTableData(const cli::agent::ChatMessage::TableData &table)
void SetContext(AgentUIContext *context)
void RenderToolTimeline(const cli::agent::ChatMessage &msg)
std::string last_plan_summary_
void Initialize(ToastManager *toast_manager, ProposalDrawer *proposal_drawer)
std::vector< AutomationTelemetry > telemetry_history_
ToastManager * toast_manager_
AgentUIContext * context_
void UpdateHarnessTelemetry(const AutomationTelemetry &telemetry)
void SetRomContext(Rom *rom)
void SetLastPlanSummary(const std::string &summary)
void Draw(float available_height=0.0f)
bool waiting_for_response_
cli::agent::ConversationalAgentService agent_service_
void RenderMessage(const cli::agent::ChatMessage &msg, int index)
ProposalDrawer * proposal_drawer_
void RenderProposalQuickActions(const cli::agent::ChatMessage &msg, int index)
void HandleAgentResponse(const absl::StatusOr< cli::agent::ChatMessage > &response)
cli::agent::ConversationalAgentService * GetAgentService()
absl::Status SaveHistory(const std::string &filepath)
Unified context for agent UI components.
ImGui drawer for displaying and managing agent proposals.
void Show(const std::string &message, ToastType type=ToastType::kInfo, float ttl_seconds=3.0f)
#define ICON_MD_FOLDER_OPEN
#define ICON_MD_BUILD_CIRCLE
#define ICON_MD_CONTENT_COPY
#define ICON_MD_DELETE_FOREVER
#define ICON_MD_SMART_TOY
#define LOG_ERROR(category, format,...)
std::vector< std::string > headers
std::vector< std::vector< std::string > > rows
std::optional< ModelMetadata > model_metadata
std::optional< TableData > table_data
std::optional< std::string > json_pretty