feat: Update AI Model References and Enhance Agent Chat Widget Functionality
- Updated AI model references from "gemini-2.0-flash-exp" to "gemini-2.5-flash" across various components, ensuring compatibility with the latest AI capabilities. - Enhanced the AgentChatWidget to improve user experience with new features such as message copying, improved input handling, and session management. - Introduced a chat session management system, allowing users to create and switch between multiple chat sessions seamlessly. - Improved UI elements for better visibility and interaction, including styled buttons and tooltips for enhanced usability.
This commit is contained in:
@@ -114,7 +114,7 @@ struct YazeProject {
|
|||||||
// AI Agent Settings
|
// AI Agent Settings
|
||||||
struct AgentSettings {
|
struct AgentSettings {
|
||||||
std::string ai_provider = "auto"; // auto, gemini, ollama, mock
|
std::string ai_provider = "auto"; // auto, gemini, ollama, mock
|
||||||
std::string ai_model; // e.g., "gemini-2.0-flash-exp", "llama3:latest"
|
std::string ai_model; // e.g., "gemini-2.5-flash", "llama3:latest"
|
||||||
std::string ollama_host = "http://localhost:11434";
|
std::string ollama_host = "http://localhost:11434";
|
||||||
std::string gemini_api_key; // Optional, can use env var
|
std::string gemini_api_key; // Optional, can use env var
|
||||||
std::string custom_system_prompt; // Path to custom prompt (relative to project)
|
std::string custom_system_prompt; // Path to custom prompt (relative to project)
|
||||||
|
|||||||
@@ -103,6 +103,12 @@ AgentChatWidget::AgentChatWidget() {
|
|||||||
memset(input_buffer_, 0, sizeof(input_buffer_));
|
memset(input_buffer_, 0, sizeof(input_buffer_));
|
||||||
history_path_ = ResolveHistoryPath();
|
history_path_ = ResolveHistoryPath();
|
||||||
history_supported_ = AgentChatHistoryCodec::Available();
|
history_supported_ = AgentChatHistoryCodec::Available();
|
||||||
|
|
||||||
|
// Enable panels by default for better visibility
|
||||||
|
show_agent_config_ = true;
|
||||||
|
show_z3ed_commands_ = true;
|
||||||
|
show_rom_sync_ = false; // Keep this one off by default
|
||||||
|
show_snapshot_preview_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentChatWidget::SetRomContext(Rom* rom) {
|
void AgentChatWidget::SetRomContext(Rom* rom) {
|
||||||
@@ -335,26 +341,34 @@ void AgentChatWidget::RenderMessage(const ChatMessage& msg, int index) {
|
|||||||
ImGui::TextDisabled(
|
ImGui::TextDisabled(
|
||||||
"%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone())
|
"%s", absl::FormatTime("%H:%M:%S", msg.timestamp, absl::LocalTimeZone())
|
||||||
.c_str());
|
.c_str());
|
||||||
|
|
||||||
|
// Add copy button for all messages
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.4f, 0.6f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.4f, 0.4f, 0.5f, 0.8f));
|
||||||
|
if (ImGui::SmallButton(ICON_MD_CONTENT_COPY)) {
|
||||||
|
std::string copy_text = msg.message;
|
||||||
|
if (copy_text.empty() && msg.json_pretty.has_value()) {
|
||||||
|
copy_text = *msg.json_pretty;
|
||||||
|
}
|
||||||
|
ImGui::SetClipboardText(copy_text.c_str());
|
||||||
|
if (toast_manager_) {
|
||||||
|
toast_manager_->Show("Message copied", ToastType::kSuccess, 2.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Copy to clipboard");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::Indent();
|
ImGui::Indent();
|
||||||
|
|
||||||
if (msg.json_pretty.has_value()) {
|
|
||||||
if (ImGui::SmallButton("Copy JSON")) {
|
|
||||||
ImGui::SetClipboardText(msg.json_pretty->c_str());
|
|
||||||
if (toast_manager_) {
|
|
||||||
toast_manager_->Show("Copied JSON to clipboard", ToastType::kInfo,
|
|
||||||
2.5f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::TextDisabled("Structured response");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.table_data.has_value()) {
|
if (msg.table_data.has_value()) {
|
||||||
RenderTable(*msg.table_data);
|
RenderTable(*msg.table_data);
|
||||||
} else if (msg.json_pretty.has_value()) {
|
} else if (msg.json_pretty.has_value()) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, kJsonTextColor);
|
// Don't show JSON as a message - it's internal structure
|
||||||
ImGui::TextUnformatted(msg.json_pretty->c_str());
|
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.5f, 0.5f, 0.5f, 0.8f));
|
||||||
|
ImGui::TextDisabled(ICON_MD_DATA_OBJECT " (Structured response)");
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
} else {
|
} else {
|
||||||
ImGui::TextWrapped("%s", msg.message.c_str());
|
ImGui::TextWrapped("%s", msg.message.c_str());
|
||||||
@@ -418,17 +432,27 @@ void AgentChatWidget::RenderProposalQuickActions(const ChatMessage& msg,
|
|||||||
void AgentChatWidget::RenderHistory() {
|
void AgentChatWidget::RenderHistory() {
|
||||||
const auto& history = agent_service_.GetHistory();
|
const auto& history = agent_service_.GetHistory();
|
||||||
float reserved_height = ImGui::GetFrameHeightWithSpacing() * 4.0f;
|
float reserved_height = ImGui::GetFrameHeightWithSpacing() * 4.0f;
|
||||||
reserved_height += 220.0f;
|
reserved_height += 100.0f; // Reduced to 100 for much taller chat area
|
||||||
|
|
||||||
if (ImGui::BeginChild("History", ImVec2(0, -reserved_height), false,
|
// Styled chat history container
|
||||||
ImGuiWindowFlags_AlwaysVerticalScrollbar |
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.08f, 0.08f, 0.10f, 0.95f));
|
||||||
ImGuiWindowFlags_HorizontalScrollbar)) {
|
if (ImGui::BeginChild("History", ImVec2(0, -reserved_height), true,
|
||||||
|
ImGuiWindowFlags_AlwaysVerticalScrollbar)) {
|
||||||
if (history.empty()) {
|
if (history.empty()) {
|
||||||
ImGui::TextDisabled("No messages yet. Start the conversation below.");
|
// Centered empty state
|
||||||
|
ImVec2 text_size = ImGui::CalcTextSize("No messages yet");
|
||||||
|
ImVec2 avail = ImGui::GetContentRegionAvail();
|
||||||
|
ImGui::SetCursorPosX((avail.x - text_size.x) / 2);
|
||||||
|
ImGui::SetCursorPosY((avail.y - text_size.y) / 2);
|
||||||
|
ImGui::TextDisabled(ICON_MD_CHAT " No messages yet");
|
||||||
|
ImGui::SetCursorPosX((avail.x - ImGui::CalcTextSize("Start typing below to begin").x) / 2);
|
||||||
|
ImGui::TextDisabled("Start typing below to begin");
|
||||||
} else {
|
} else {
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8, 12)); // More spacing between messages
|
||||||
for (size_t index = 0; index < history.size(); ++index) {
|
for (size_t index = 0; index < history.size(); ++index) {
|
||||||
RenderMessage(history[index], static_cast<int>(index));
|
RenderMessage(history[index], static_cast<int>(index));
|
||||||
}
|
}
|
||||||
|
ImGui::PopStyleVar();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (history.size() > last_history_size_) {
|
if (history.size() > last_history_size_) {
|
||||||
@@ -436,31 +460,33 @@ void AgentChatWidget::RenderHistory() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
last_history_size_ = history.size();
|
last_history_size_ = history.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentChatWidget::RenderInputBox() {
|
void AgentChatWidget::RenderInputBox() {
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Text("Message:");
|
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_EDIT " Message:");
|
||||||
|
|
||||||
bool submitted = ImGui::InputTextMultiline(
|
bool submitted = ImGui::InputTextMultiline(
|
||||||
"##agent_input", input_buffer_, sizeof(input_buffer_), ImVec2(-1, 80.0f),
|
"##agent_input", input_buffer_, sizeof(input_buffer_), ImVec2(-1, 60.0f),
|
||||||
ImGuiInputTextFlags_AllowTabInput | ImGuiInputTextFlags_EnterReturnsTrue);
|
ImGuiInputTextFlags_None);
|
||||||
|
|
||||||
bool send = submitted;
|
// Check for Ctrl+Enter to send (Enter alone adds newline)
|
||||||
if (submitted && ImGui::GetIO().KeyShift) {
|
bool send = false;
|
||||||
size_t len = std::strlen(input_buffer_);
|
if (ImGui::IsItemFocused()) {
|
||||||
if (len + 1 < sizeof(input_buffer_)) {
|
if (ImGui::IsKeyPressed(ImGuiKey_Enter) && ImGui::GetIO().KeyCtrl) {
|
||||||
input_buffer_[len] = '\n';
|
send = true;
|
||||||
input_buffer_[len + 1] = '\0';
|
|
||||||
}
|
}
|
||||||
ImGui::SetKeyboardFocusHere(-1);
|
|
||||||
send = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
|
// Send button row
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
|
||||||
if (ImGui::Button(absl::StrFormat("%s Send", ICON_MD_SEND).c_str(),
|
if (ImGui::Button(absl::StrFormat("%s Send", ICON_MD_SEND).c_str(),
|
||||||
ImVec2(120, 0)) ||
|
ImVec2(140, 0)) ||
|
||||||
send) {
|
send) {
|
||||||
if (std::strlen(input_buffer_) > 0) {
|
if (std::strlen(input_buffer_) > 0) {
|
||||||
history_dirty_ = true;
|
history_dirty_ = true;
|
||||||
@@ -472,9 +498,114 @@ void AgentChatWidget::RenderInputBox() {
|
|||||||
ImGui::SetKeyboardFocusHere(-1);
|
ImGui::SetKeyboardFocusHere(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Send message (Ctrl+Enter)");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::TextDisabled("Enter to send • Shift+Enter for newline");
|
ImGui::TextDisabled(ICON_MD_INFO " Ctrl+Enter: send • Enter: newline");
|
||||||
|
|
||||||
|
// Action buttons row below
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.5f, 0.0f, 0.7f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.843f, 0.0f, 0.9f));
|
||||||
|
if (ImGui::SmallButton(ICON_MD_DELETE_FOREVER " Clear")) {
|
||||||
|
agent_service_.ResetConversation();
|
||||||
|
if (toast_manager_) {
|
||||||
|
toast_manager_->Show("Conversation cleared", ToastType::kSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Clear all messages from conversation");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.35f, 0.6f, 0.7f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.502f, 0.0f, 0.502f, 0.9f));
|
||||||
|
if (ImGui::SmallButton(ICON_MD_PREVIEW " Proposals")) {
|
||||||
|
if (proposal_drawer_) {
|
||||||
|
// Focus proposal drawer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("View code proposals");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multimodal Vision controls integrated
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.7f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 0.9f));
|
||||||
|
if (ImGui::SmallButton(ICON_MD_PHOTO_CAMERA " Capture")) {
|
||||||
|
// Quick capture with current mode
|
||||||
|
if (multimodal_callbacks_.capture_snapshot) {
|
||||||
|
std::filesystem::path captured_path;
|
||||||
|
auto status = multimodal_callbacks_.capture_snapshot(&captured_path);
|
||||||
|
if (status.ok()) {
|
||||||
|
multimodal_state_.last_capture_path = captured_path;
|
||||||
|
if (toast_manager_) {
|
||||||
|
toast_manager_->Show("Screenshot captured", ToastType::kSuccess);
|
||||||
|
}
|
||||||
|
} else if (toast_manager_) {
|
||||||
|
toast_manager_->Show(absl::StrFormat("Capture failed: %s", status.message()), ToastType::kError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Capture screenshot for vision analysis");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.5f, 0.3f, 0.3f, 0.7f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.863f, 0.078f, 0.235f, 0.9f));
|
||||||
|
if (ImGui::SmallButton(ICON_MD_STOP " Stop")) {
|
||||||
|
// Stop generation (if implemented)
|
||||||
|
if (toast_manager_) {
|
||||||
|
toast_manager_->Show("Stop not yet implemented", ToastType::kWarning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Stop current generation");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.5f, 0.3f, 0.7f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.133f, 0.545f, 0.133f, 0.9f));
|
||||||
|
if (ImGui::SmallButton(ICON_MD_SAVE " Export")) {
|
||||||
|
// Export conversation
|
||||||
|
if (toast_manager_) {
|
||||||
|
toast_manager_->Show("Export not yet implemented", ToastType::kWarning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Export conversation history");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vision prompt (inline when image captured)
|
||||||
|
if (multimodal_state_.last_capture_path.has_value()) {
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextColored(ImVec4(0.196f, 0.6f, 0.8f, 1.0f), ICON_MD_IMAGE " Vision prompt:");
|
||||||
|
ImGui::SetNextItemWidth(-200);
|
||||||
|
ImGui::InputTextWithHint("##quick_vision_prompt", "Ask about the screenshot...",
|
||||||
|
multimodal_prompt_buffer_, sizeof(multimodal_prompt_buffer_));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.8f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
|
||||||
|
if (ImGui::Button(ICON_MD_SEND " Analyze##vision_send", ImVec2(180, 0))) {
|
||||||
|
if (multimodal_callbacks_.send_to_gemini && !multimodal_state_.last_capture_path->empty()) {
|
||||||
|
std::string prompt = multimodal_prompt_buffer_;
|
||||||
|
auto status = multimodal_callbacks_.send_to_gemini(*multimodal_state_.last_capture_path, prompt);
|
||||||
|
if (status.ok() && toast_manager_) {
|
||||||
|
toast_manager_->Show("Vision analysis requested", ToastType::kSuccess);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentChatWidget::Draw() {
|
void AgentChatWidget::Draw() {
|
||||||
@@ -487,7 +618,7 @@ void AgentChatWidget::Draw() {
|
|||||||
// Poll for new messages in collaborative sessions
|
// Poll for new messages in collaborative sessions
|
||||||
PollSharedHistory();
|
PollSharedHistory();
|
||||||
|
|
||||||
ImGui::SetNextWindowSize(ImVec2(1200, 800), ImGuiCond_FirstUseEver);
|
ImGui::SetNextWindowSize(ImVec2(1400, 1000), ImGuiCond_FirstUseEver);
|
||||||
ImGui::Begin(title_.c_str(), &active_, ImGuiWindowFlags_MenuBar);
|
ImGui::Begin(title_.c_str(), &active_, ImGuiWindowFlags_MenuBar);
|
||||||
|
|
||||||
// Simplified menu bar
|
// Simplified menu bar
|
||||||
@@ -524,10 +655,10 @@ void AgentChatWidget::Draw() {
|
|||||||
collaboration_status_color_ = collaboration_state_.active ?
|
collaboration_status_color_ = collaboration_state_.active ?
|
||||||
ImVec4(0.133f, 0.545f, 0.133f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
|
ImVec4(0.133f, 0.545f, 0.133f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
|
||||||
|
|
||||||
// Connection status bar at top
|
// Connection status bar at top (compact)
|
||||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||||
ImVec2 bar_start = ImGui::GetCursorScreenPos();
|
ImVec2 bar_start = ImGui::GetCursorScreenPos();
|
||||||
ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 75);
|
ImVec2 bar_size(ImGui::GetContentRegionAvail().x, 55); // Reduced from 75 to 55
|
||||||
|
|
||||||
// Gradient background
|
// Gradient background
|
||||||
ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f));
|
ImU32 color_top = ImGui::GetColorU32(ImVec4(0.18f, 0.22f, 0.28f, 1.0f));
|
||||||
@@ -546,37 +677,142 @@ void AgentChatWidget::Draw() {
|
|||||||
ImGui::BeginChild("AgentChat_ConnectionBar", bar_size, false, ImGuiWindowFlags_NoScrollbar);
|
ImGui::BeginChild("AgentChat_ConnectionBar", bar_size, false, ImGuiWindowFlags_NoScrollbar);
|
||||||
ImGui::PushID("ConnectionBar");
|
ImGui::PushID("ConnectionBar");
|
||||||
{
|
{
|
||||||
ImGui::Spacing();
|
// Center content vertically in the 55px bar
|
||||||
|
float content_height = ImGui::GetFrameHeight();
|
||||||
|
float vertical_padding = (55.0f - content_height) / 2.0f;
|
||||||
|
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + vertical_padding);
|
||||||
|
|
||||||
// Provider selection
|
// Compact single row layout
|
||||||
ImGui::TextColored(accent_color, ICON_MD_SMART_TOY " Provider:");
|
ImGui::TextColored(accent_color, ICON_MD_SMART_TOY);
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(110);
|
ImGui::SetNextItemWidth(95);
|
||||||
const char* providers[] = { "Mock", "Ollama", "Gemini" };
|
const char* providers[] = { "Mock", "Ollama", "Gemini" };
|
||||||
int current_provider = (agent_config_.ai_provider == "mock") ? 0 :
|
int current_provider = (agent_config_.ai_provider == "mock") ? 0 :
|
||||||
(agent_config_.ai_provider == "ollama") ? 1 : 2;
|
(agent_config_.ai_provider == "ollama") ? 1 : 2;
|
||||||
if (ImGui::Combo("##main_provider", ¤t_provider, providers, 3)) {
|
if (ImGui::Combo("##main_provider", ¤t_provider, providers, 3)) {
|
||||||
agent_config_.ai_provider = (current_provider == 0) ? "mock" :
|
agent_config_.ai_provider = (current_provider == 0) ? "mock" :
|
||||||
(current_provider == 1) ? "ollama" : "gemini";
|
(current_provider == 1) ? "ollama" : "gemini";
|
||||||
|
// Auto-populate default models
|
||||||
|
if (agent_config_.ai_provider == "ollama") {
|
||||||
|
strncpy(agent_config_.model_buffer, "qwen2.5-coder:7b", sizeof(agent_config_.model_buffer) - 1);
|
||||||
|
agent_config_.ai_model = agent_config_.model_buffer;
|
||||||
|
} else if (agent_config_.ai_provider == "gemini") {
|
||||||
|
strncpy(agent_config_.model_buffer, "gemini-2.5-flash", sizeof(agent_config_.model_buffer) - 1);
|
||||||
|
agent_config_.ai_model = agent_config_.model_buffer;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (agent_config_.ai_provider != "mock") {
|
if (agent_config_.ai_provider != "mock") {
|
||||||
ImGui::TextColored(accent_color, ICON_MD_PSYCHOLOGY " Model:");
|
ImGui::SetNextItemWidth(150);
|
||||||
|
ImGui::InputTextWithHint("##main_model", "Model name...", agent_config_.model_buffer, sizeof(agent_config_.model_buffer));
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("AI model name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gemini API key input
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (agent_config_.ai_provider == "gemini") {
|
||||||
|
ImGui::SetNextItemWidth(200);
|
||||||
|
if (ImGui::InputTextWithHint("##main_api_key", "API Key (or load from env)...",
|
||||||
|
agent_config_.gemini_key_buffer,
|
||||||
|
sizeof(agent_config_.gemini_key_buffer),
|
||||||
|
ImGuiInputTextFlags_Password)) {
|
||||||
|
agent_config_.gemini_api_key = agent_config_.gemini_key_buffer;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Gemini API Key (hidden)");
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::SetNextItemWidth(180);
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.5f, 0.7f, 0.7f));
|
||||||
ImGui::InputText("##main_model", agent_config_.model_buffer, sizeof(agent_config_.model_buffer));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.196f, 0.6f, 0.8f, 1.0f));
|
||||||
|
if (ImGui::SmallButton(ICON_MD_REFRESH)) {
|
||||||
|
const char* gemini_key = nullptr;
|
||||||
|
#ifdef _WIN32
|
||||||
|
char* env_key = nullptr;
|
||||||
|
size_t len = 0;
|
||||||
|
if (_dupenv_s(&env_key, &len, "GEMINI_API_KEY") == 0 && env_key != nullptr) {
|
||||||
|
strncpy(agent_config_.gemini_key_buffer, env_key, sizeof(agent_config_.gemini_key_buffer) - 1);
|
||||||
|
agent_config_.gemini_api_key = env_key;
|
||||||
|
free(env_key);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
gemini_key = std::getenv("GEMINI_API_KEY");
|
||||||
|
if (gemini_key) {
|
||||||
|
strncpy(agent_config_.gemini_key_buffer, gemini_key, sizeof(agent_config_.gemini_key_buffer) - 1);
|
||||||
|
agent_config_.gemini_api_key = gemini_key;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (!agent_config_.gemini_api_key.empty() && toast_manager_) {
|
||||||
|
toast_manager_->Show("Key loaded", ToastType::kSuccess, 1.5f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Load from GEMINI_API_KEY");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Checkbox(ICON_MD_VISIBILITY " Reasoning", &agent_config_.show_reasoning);
|
ImGui::Checkbox(ICON_MD_VISIBILITY, &agent_config_.show_reasoning);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Show reasoning");
|
||||||
|
}
|
||||||
|
|
||||||
// Connection status indicator
|
// Session management button
|
||||||
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 100);
|
ImGui::SameLine();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.7f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.5f, 0.5f, 0.7f, 0.9f));
|
||||||
|
if (ImGui::SmallButton(absl::StrFormat("%s %d", ICON_MD_TAB, static_cast<int>(chat_sessions_.size())).c_str())) {
|
||||||
|
ImGui::OpenPopup("SessionsPopup");
|
||||||
|
}
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Manage chat sessions");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sessions popup
|
||||||
|
if (ImGui::BeginPopup("SessionsPopup")) {
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_TAB " Chat Sessions");
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_ADD " New Session", ImVec2(200, 0))) {
|
||||||
|
std::string session_id = absl::StrFormat("session_%d", static_cast<int>(chat_sessions_.size() + 1));
|
||||||
|
std::string session_name = absl::StrFormat("Chat %d", static_cast<int>(chat_sessions_.size() + 1));
|
||||||
|
chat_sessions_.emplace_back(session_id, session_name);
|
||||||
|
active_session_index_ = static_cast<int>(chat_sessions_.size() - 1);
|
||||||
|
if (toast_manager_) {
|
||||||
|
toast_manager_->Show("New session created", ToastType::kSuccess);
|
||||||
|
}
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chat_sessions_.empty()) {
|
||||||
|
ImGui::Spacing();
|
||||||
|
ImGui::TextDisabled("Active Sessions:");
|
||||||
|
ImGui::Separator();
|
||||||
|
for (size_t i = 0; i < chat_sessions_.size(); ++i) {
|
||||||
|
ImGui::PushID(static_cast<int>(i));
|
||||||
|
bool is_active = (active_session_index_ == static_cast<int>(i));
|
||||||
|
if (ImGui::Selectable(absl::StrFormat("%s %s%s", ICON_MD_CHAT,
|
||||||
|
chat_sessions_[i].name,
|
||||||
|
is_active ? " (active)" : "").c_str(),
|
||||||
|
is_active)) {
|
||||||
|
active_session_index_ = static_cast<int>(i);
|
||||||
|
ImGui::CloseCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Session status (right side)
|
||||||
if (collaboration_state_.active) {
|
if (collaboration_state_.active) {
|
||||||
ImGui::TextColored(collaboration_status_color_, ICON_MD_CHECK_CIRCLE " Session Active");
|
ImGui::SameLine(ImGui::GetContentRegionAvail().x - 25);
|
||||||
} else {
|
ImGui::TextColored(collaboration_status_color_, ICON_MD_CHECK_CIRCLE);
|
||||||
ImGui::TextDisabled(ICON_MD_CANCEL " No Session");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::PopID();
|
ImGui::PopID();
|
||||||
@@ -586,9 +822,13 @@ void AgentChatWidget::Draw() {
|
|||||||
|
|
||||||
// Main layout: Chat area (left, 70%) + Control panels (right, 30%)
|
// Main layout: Chat area (left, 70%) + Control panels (right, 30%)
|
||||||
if (ImGui::BeginTable("AgentChat_MainLayout", 2,
|
if (ImGui::BeginTable("AgentChat_MainLayout", 2,
|
||||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInnerV)) {
|
ImGuiTableFlags_Resizable |
|
||||||
|
ImGuiTableFlags_Reorderable |
|
||||||
|
ImGuiTableFlags_BordersInnerV |
|
||||||
|
ImGuiTableFlags_ContextMenuInBody)) {
|
||||||
ImGui::TableSetupColumn("Chat", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
ImGui::TableSetupColumn("Chat", ImGuiTableColumnFlags_WidthStretch, 0.7f);
|
||||||
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch, 0.3f);
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
// LEFT: Chat area (emphasized)
|
// LEFT: Chat area (emphasized)
|
||||||
@@ -603,19 +843,7 @@ void AgentChatWidget::Draw() {
|
|||||||
ImGui::PushID("ControlsColumn");
|
ImGui::PushID("ControlsColumn");
|
||||||
ImGui::BeginChild("AgentChat_ControlPanels", ImVec2(0, 0), false);
|
ImGui::BeginChild("AgentChat_ControlPanels", ImVec2(0, 0), false);
|
||||||
|
|
||||||
// Quick Actions
|
// Collapsible panels (organized by priority)
|
||||||
if (ImGui::CollapsingHeader(ICON_MD_BOLT " Quick Actions", ImGuiTreeNodeFlags_DefaultOpen)) {
|
|
||||||
ImGui::PushID("QuickActions");
|
|
||||||
if (ImGui::Button(ICON_MD_REFRESH " Reset Chat", ImVec2(-1, 0))) {
|
|
||||||
agent_service_.ResetConversation();
|
|
||||||
if (toast_manager_) {
|
|
||||||
toast_manager_->Show("Conversation reset", ToastType::kInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ImGui::PopID();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collapsible panels
|
|
||||||
if (show_agent_config_) {
|
if (show_agent_config_) {
|
||||||
RenderAgentConfigPanel();
|
RenderAgentConfigPanel();
|
||||||
}
|
}
|
||||||
@@ -624,9 +852,11 @@ void AgentChatWidget::Draw() {
|
|||||||
RenderZ3EDCommandPanel();
|
RenderZ3EDCommandPanel();
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderCollaborationPanel();
|
// Multimodal before collaboration (more frequently used)
|
||||||
RenderMultimodalPanel();
|
RenderMultimodalPanel();
|
||||||
|
|
||||||
|
RenderCollaborationPanel();
|
||||||
|
|
||||||
if (show_rom_sync_) {
|
if (show_rom_sync_) {
|
||||||
RenderRomSyncPanel();
|
RenderRomSyncPanel();
|
||||||
}
|
}
|
||||||
@@ -649,29 +879,29 @@ void AgentChatWidget::RenderCollaborationPanel() {
|
|||||||
const bool connected = collaboration_state_.active;
|
const bool connected = collaboration_state_.active;
|
||||||
collaboration_status_color_ = connected ? ImVec4(0.133f, 0.545f, 0.133f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
|
collaboration_status_color_ = connected ? ImVec4(0.133f, 0.545f, 0.133f, 1.0f) : ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
|
||||||
|
|
||||||
// Top section: Mode selector in a styled box
|
if (!ImGui::CollapsingHeader(ICON_MD_PEOPLE " Collaboration & Network")) {
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.18f, 0.22f, 0.8f));
|
ImGui::PopID();
|
||||||
ImGui::BeginChild("Collab_ModeSelector", ImVec2(0, 45), true, ImGuiWindowFlags_NoScrollbar);
|
return;
|
||||||
{
|
|
||||||
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_SETTINGS_ETHERNET " Collaboration Mode:");
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::RadioButton(ICON_MD_FOLDER " Local##collab_mode_local",
|
|
||||||
reinterpret_cast<int*>(&collaboration_state_.mode),
|
|
||||||
static_cast<int>(CollaborationMode::kLocal));
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::RadioButton(ICON_MD_WIFI " Network##collab_mode_network",
|
|
||||||
reinterpret_cast<int*>(&collaboration_state_.mode),
|
|
||||||
static_cast<int>(CollaborationMode::kNetwork));
|
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
|
||||||
ImGui::PopStyleColor();
|
// Mode selector (compact inline)
|
||||||
|
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_SETTINGS_ETHERNET " Mode:");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::RadioButton(ICON_MD_FOLDER " Local##collab_mode_local",
|
||||||
|
reinterpret_cast<int*>(&collaboration_state_.mode),
|
||||||
|
static_cast<int>(CollaborationMode::kLocal));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::RadioButton(ICON_MD_WIFI " Network##collab_mode_network",
|
||||||
|
reinterpret_cast<int*>(&collaboration_state_.mode),
|
||||||
|
static_cast<int>(CollaborationMode::kNetwork));
|
||||||
|
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
// Main content in table layout
|
// Main content in table layout (fixed size to prevent auto-resize)
|
||||||
if (ImGui::BeginTable("Collab_MainTable", 2, ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_Resizable)) {
|
if (ImGui::BeginTable("Collab_MainTable", 2, ImGuiTableFlags_BordersInnerV)) {
|
||||||
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 320);
|
ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 180);
|
||||||
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthStretch);
|
ImGui::TableSetupColumn("Controls", ImGuiTableColumnFlags_WidthFixed,
|
||||||
|
ImGui::GetContentRegionAvail().x - 180);
|
||||||
ImGui::TableNextRow();
|
ImGui::TableNextRow();
|
||||||
|
|
||||||
// LEFT COLUMN: Session Details
|
// LEFT COLUMN: Session Details
|
||||||
@@ -680,7 +910,7 @@ void AgentChatWidget::RenderCollaborationPanel() {
|
|||||||
ImGui::PushID("StatusColumn");
|
ImGui::PushID("StatusColumn");
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.2f, 0.18f, 0.4f));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.15f, 0.2f, 0.18f, 0.4f));
|
||||||
ImGui::BeginChild("Collab_SessionDetails", ImVec2(0, 180), true);
|
ImGui::BeginChild("Collab_SessionDetails", ImVec2(0, 100), true);
|
||||||
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_INFO " Session Status:");
|
ImGui::TextColored(ImVec4(1.0f, 0.843f, 0.0f, 1.0f), ICON_MD_INFO " Session Status:");
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
if (connected) {
|
if (connected) {
|
||||||
@@ -707,7 +937,7 @@ void AgentChatWidget::RenderCollaborationPanel() {
|
|||||||
ImGui::TextWrapped("%s", collaboration_state_.session_id.c_str());
|
ImGui::TextWrapped("%s", collaboration_state_.session_id.c_str());
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.6f));
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.4f, 0.6f, 0.6f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.416f, 0.353f, 0.804f, 1.0f));
|
||||||
if (ImGui::SmallButton(ICON_MD_CONTENT_COPY " Copy##copy_session_id")) {
|
if (ImGui::Button(ICON_MD_CONTENT_COPY " Copy##copy_session_id")) {
|
||||||
ImGui::SetClipboardText(collaboration_state_.session_id.c_str());
|
ImGui::SetClipboardText(collaboration_state_.session_id.c_str());
|
||||||
if (toast_manager_) {
|
if (toast_manager_) {
|
||||||
toast_manager_->Show("Session code copied!", ToastType::kSuccess, 2.0f);
|
toast_manager_->Show("Session code copied!", ToastType::kSuccess, 2.0f);
|
||||||
@@ -1046,7 +1276,7 @@ void AgentChatWidget::RenderMultimodalPanel() {
|
|||||||
ImGui::TextColored(gemini_color, ICON_MD_EDIT " Vision Prompt:");
|
ImGui::TextColored(gemini_color, ICON_MD_EDIT " Vision Prompt:");
|
||||||
ImGui::InputTextMultiline("##mm_gemini_prompt_textarea", multimodal_prompt_buffer_,
|
ImGui::InputTextMultiline("##mm_gemini_prompt_textarea", multimodal_prompt_buffer_,
|
||||||
IM_ARRAYSIZE(multimodal_prompt_buffer_),
|
IM_ARRAYSIZE(multimodal_prompt_buffer_),
|
||||||
ImVec2(-1, 80));
|
ImVec2(-1, 60));
|
||||||
|
|
||||||
if (!can_send)
|
if (!can_send)
|
||||||
ImGui::BeginDisabled();
|
ImGui::BeginDisabled();
|
||||||
@@ -1341,7 +1571,7 @@ void AgentChatWidget::RenderZ3EDCommandPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.14f, 0.12f, 0.18f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.14f, 0.12f, 0.18f, 1.0f));
|
||||||
ImGui::BeginChild("Z3ED_CommandsChild", ImVec2(0, 300), true);
|
ImGui::BeginChild("Z3ED_CommandsChild", ImVec2(0, 200), true);
|
||||||
|
|
||||||
ImGui::TextColored(command_color, ICON_MD_CODE " Command Palette");
|
ImGui::TextColored(command_color, ICON_MD_CODE " Command Palette");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@@ -1438,22 +1668,27 @@ void AgentChatWidget::RenderZ3EDCommandPanel() {
|
|||||||
(void)status; // Acknowledge result
|
(void)status; // Acknowledge result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ImGui::PopStyleColor(2); // FIX: Pop the styles before EndTable
|
||||||
|
|
||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!z3ed_command_state_.command_output.empty()) {
|
if (!z3ed_command_state_.command_output.empty()) {
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::Text(ICON_MD_TERMINAL " Output");
|
ImGui::TextColored(command_color, ICON_MD_OUTPUT " Output:");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::BeginChild("CommandOutput", ImVec2(0, 100), true,
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.1f, 0.1f, 0.12f, 0.8f));
|
||||||
|
ImGui::BeginChild("Z3ED_CommandOutput", ImVec2(0, 100), true,
|
||||||
ImGuiWindowFlags_HorizontalScrollbar);
|
ImGuiWindowFlags_HorizontalScrollbar);
|
||||||
ImGui::TextWrapped("%s", z3ed_command_state_.command_output.c_str());
|
ImGui::TextWrapped("%s", z3ed_command_state_.command_output.c_str());
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
ImGui::PopStyleColor();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
ImGui::PopID(); // FIX: Pop the Z3EDCmdPanel ID
|
||||||
}
|
}
|
||||||
|
|
||||||
void AgentChatWidget::RenderRomSyncPanel() {
|
void AgentChatWidget::RenderRomSyncPanel() {
|
||||||
@@ -1463,7 +1698,7 @@ void AgentChatWidget::RenderRomSyncPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.14f, 0.12f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.18f, 0.14f, 0.12f, 1.0f));
|
||||||
ImGui::BeginChild("RomSync", ImVec2(0, 250), true);
|
ImGui::BeginChild("RomSync", ImVec2(0, 200), true);
|
||||||
|
|
||||||
ImGui::Text(ICON_MD_STORAGE " ROM State");
|
ImGui::Text(ICON_MD_STORAGE " ROM State");
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@@ -1562,7 +1797,7 @@ void AgentChatWidget::RenderSnapshotPreviewPanel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.12f, 0.16f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.12f, 0.12f, 0.16f, 1.0f));
|
||||||
ImGui::BeginChild("SnapshotPreview", ImVec2(0, 300), true);
|
ImGui::BeginChild("SnapshotPreview", ImVec2(0, 200), true);
|
||||||
|
|
||||||
if (multimodal_state_.last_capture_path.has_value()) {
|
if (multimodal_state_.last_capture_path.has_value()) {
|
||||||
ImGui::Text(ICON_MD_IMAGE " Latest Capture");
|
ImGui::Text(ICON_MD_IMAGE " Latest Capture");
|
||||||
|
|||||||
@@ -229,6 +229,25 @@ public:
|
|||||||
void HandleSnapshotReceived(const std::string& snapshot_data, const std::string& snapshot_type);
|
void HandleSnapshotReceived(const std::string& snapshot_data, const std::string& snapshot_type);
|
||||||
void HandleProposalReceived(const std::string& proposal_data);
|
void HandleProposalReceived(const std::string& proposal_data);
|
||||||
|
|
||||||
|
// Chat session management
|
||||||
|
struct ChatSession {
|
||||||
|
std::string id;
|
||||||
|
std::string name;
|
||||||
|
cli::agent::ConversationalAgentService agent_service;
|
||||||
|
size_t last_history_size = 0;
|
||||||
|
bool history_loaded = false;
|
||||||
|
bool history_dirty = false;
|
||||||
|
std::filesystem::path history_path;
|
||||||
|
absl::Time last_persist_time = absl::InfinitePast();
|
||||||
|
|
||||||
|
ChatSession(const std::string& session_id, const std::string& session_name)
|
||||||
|
: id(session_id), name(session_name) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ChatSession> chat_sessions_;
|
||||||
|
int active_session_index_ = 0;
|
||||||
|
|
||||||
|
// Legacy single session support (will migrate to sessions)
|
||||||
cli::agent::ConversationalAgentService agent_service_;
|
cli::agent::ConversationalAgentService agent_service_;
|
||||||
char input_buffer_[1024];
|
char input_buffer_[1024];
|
||||||
bool active_ = false;
|
bool active_ = false;
|
||||||
|
|||||||
@@ -218,8 +218,10 @@ void AgentEditor::DrawConfigurationPanel() {
|
|||||||
toast_manager_->Show("GEMINI_API_KEY not found in environment", ToastType::kWarning);
|
toast_manager_->Show("GEMINI_API_KEY not found in environment", ToastType::kWarning);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Immediately apply to chat widget
|
||||||
|
ApplyConfig(current_config_);
|
||||||
if (toast_manager_) {
|
if (toast_manager_) {
|
||||||
toast_manager_->Show("Gemini API key loaded from environment", ToastType::kSuccess);
|
toast_manager_->Show("Gemini API key loaded and applied", ToastType::kSuccess);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ void EditorManager::Initialize(const std::string& filename) {
|
|||||||
// Create Gemini service
|
// Create Gemini service
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key;
|
config.api_key = api_key;
|
||||||
config.model = "gemini-2.0-flash-exp"; // Use vision-capable model
|
config.model = "gemini-2.5-flash"; // Use vision-capable model
|
||||||
config.verbose = false;
|
config.verbose = false;
|
||||||
|
|
||||||
cli::GeminiAIService gemini_service(config);
|
cli::GeminiAIService gemini_service(config);
|
||||||
@@ -350,10 +350,18 @@ void EditorManager::Initialize(const std::string& filename) {
|
|||||||
|
|
||||||
// Initialize editor selection dialog callback
|
// Initialize editor selection dialog callback
|
||||||
editor_selection_dialog_.SetSelectionCallback([this](EditorType type) {
|
editor_selection_dialog_.SetSelectionCallback([this](EditorType type) {
|
||||||
if (!current_editor_set_) return;
|
|
||||||
|
|
||||||
editor_selection_dialog_.MarkRecentlyUsed(type);
|
editor_selection_dialog_.MarkRecentlyUsed(type);
|
||||||
|
|
||||||
|
// Handle agent editor separately (doesn't require ROM)
|
||||||
|
if (type == EditorType::kAgent) {
|
||||||
|
#ifdef YAZE_WITH_GRPC
|
||||||
|
agent_editor_.set_active(true);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!current_editor_set_) return;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case EditorType::kOverworld:
|
case EditorType::kOverworld:
|
||||||
current_editor_set_->overworld_editor_.set_active(true);
|
current_editor_set_->overworld_editor_.set_active(true);
|
||||||
|
|||||||
@@ -499,16 +499,21 @@ absl::StatusOr<AgentResponse> GeminiAIService::ParseGeminiResponse(
|
|||||||
// Try to parse as JSON object
|
// Try to parse as JSON object
|
||||||
auto parsed_text = nlohmann::json::parse(text_content, nullptr, false);
|
auto parsed_text = nlohmann::json::parse(text_content, nullptr, false);
|
||||||
if (!parsed_text.is_discarded()) {
|
if (!parsed_text.is_discarded()) {
|
||||||
|
// Extract text_response
|
||||||
if (parsed_text.contains("text_response") &&
|
if (parsed_text.contains("text_response") &&
|
||||||
parsed_text["text_response"].is_string()) {
|
parsed_text["text_response"].is_string()) {
|
||||||
agent_response.text_response =
|
agent_response.text_response =
|
||||||
parsed_text["text_response"].get<std::string>();
|
parsed_text["text_response"].get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract reasoning
|
||||||
if (parsed_text.contains("reasoning") &&
|
if (parsed_text.contains("reasoning") &&
|
||||||
parsed_text["reasoning"].is_string()) {
|
parsed_text["reasoning"].is_string()) {
|
||||||
agent_response.reasoning =
|
agent_response.reasoning =
|
||||||
parsed_text["reasoning"].get<std::string>();
|
parsed_text["reasoning"].get<std::string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract commands
|
||||||
if (parsed_text.contains("commands") &&
|
if (parsed_text.contains("commands") &&
|
||||||
parsed_text["commands"].is_array()) {
|
parsed_text["commands"].is_array()) {
|
||||||
for (const auto& cmd : parsed_text["commands"]) {
|
for (const auto& cmd : parsed_text["commands"]) {
|
||||||
@@ -521,6 +526,30 @@ absl::StatusOr<AgentResponse> GeminiAIService::ParseGeminiResponse(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extract tool_calls from the parsed JSON
|
||||||
|
if (parsed_text.contains("tool_calls") &&
|
||||||
|
parsed_text["tool_calls"].is_array()) {
|
||||||
|
for (const auto& call : parsed_text["tool_calls"]) {
|
||||||
|
if (call.contains("tool_name") && call["tool_name"].is_string()) {
|
||||||
|
ToolCall tool_call;
|
||||||
|
tool_call.tool_name = call["tool_name"].get<std::string>();
|
||||||
|
|
||||||
|
if (call.contains("args") && call["args"].is_object()) {
|
||||||
|
for (auto& [key, value] : call["args"].items()) {
|
||||||
|
if (value.is_string()) {
|
||||||
|
tool_call.args[key] = value.get<std::string>();
|
||||||
|
} else if (value.is_number()) {
|
||||||
|
tool_call.args[key] = std::to_string(value.get<double>());
|
||||||
|
} else if (value.is_boolean()) {
|
||||||
|
tool_call.args[key] = value.get<bool>() ? "true" : "false";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
agent_response.tool_calls.push_back(tool_call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If parsing the full object fails, fallback to extracting commands from text
|
// If parsing the full object fails, fallback to extracting commands from text
|
||||||
std::vector<std::string> lines = absl::StrSplit(text_content, '\n');
|
std::vector<std::string> lines = absl::StrSplit(text_content, '\n');
|
||||||
|
|||||||
@@ -76,10 +76,21 @@ std::vector<fs::path> BuildCatalogueSearchPaths(const std::string& explicit_path
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to get executable directory for better path resolution
|
||||||
|
fs::path exe_dir;
|
||||||
|
try {
|
||||||
|
exe_dir = fs::current_path();
|
||||||
|
} catch (...) {
|
||||||
|
exe_dir = ".";
|
||||||
|
}
|
||||||
|
|
||||||
const std::vector<std::string> defaults = {
|
const std::vector<std::string> defaults = {
|
||||||
"assets/agent/prompt_catalogue.yaml",
|
"assets/agent/prompt_catalogue.yaml",
|
||||||
"../assets/agent/prompt_catalogue.yaml",
|
"../assets/agent/prompt_catalogue.yaml",
|
||||||
"../../assets/agent/prompt_catalogue.yaml",
|
"../../assets/agent/prompt_catalogue.yaml",
|
||||||
|
"../../../assets/agent/prompt_catalogue.yaml", // From build/bin/
|
||||||
|
"../../../../assets/agent/prompt_catalogue.yaml", // From build/bin/yaze.app/Contents/MacOS/
|
||||||
|
"../Resources/assets/agent/prompt_catalogue.yaml", // macOS app bundle
|
||||||
"assets/z3ed/prompt_catalogue.yaml",
|
"assets/z3ed/prompt_catalogue.yaml",
|
||||||
"../assets/z3ed/prompt_catalogue.yaml",
|
"../assets/z3ed/prompt_catalogue.yaml",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ TEST_F(AITilePlacementTest, DISABLED_VisionAnalysisBasic) {
|
|||||||
|
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key;
|
config.api_key = api_key;
|
||||||
config.model = "gemini-2.0-flash-exp";
|
config.model = "gemini-2.5-flash";
|
||||||
|
|
||||||
cli::GeminiAIService gemini_service(config);
|
cli::GeminiAIService gemini_service(config);
|
||||||
cli::ai::VisionActionRefiner refiner(&gemini_service);
|
cli::ai::VisionActionRefiner refiner(&gemini_service);
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class GeminiVisionTest : public ::testing::Test {
|
|||||||
TEST_F(GeminiVisionTest, BasicImageAnalysis) {
|
TEST_F(GeminiVisionTest, BasicImageAnalysis) {
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key_;
|
config.api_key = api_key_;
|
||||||
config.model = "gemini-2.0-flash-exp"; // Vision-capable model
|
config.model = "gemini-2.5-flash"; // Vision-capable model
|
||||||
config.verbose = false;
|
config.verbose = false;
|
||||||
|
|
||||||
cli::GeminiAIService service(config);
|
cli::GeminiAIService service(config);
|
||||||
@@ -96,7 +96,7 @@ TEST_F(GeminiVisionTest, BasicImageAnalysis) {
|
|||||||
TEST_F(GeminiVisionTest, ImageWithSpecificPrompt) {
|
TEST_F(GeminiVisionTest, ImageWithSpecificPrompt) {
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key_;
|
config.api_key = api_key_;
|
||||||
config.model = "gemini-2.0-flash-exp";
|
config.model = "gemini-2.5-flash";
|
||||||
config.verbose = false;
|
config.verbose = false;
|
||||||
|
|
||||||
cli::GeminiAIService service(config);
|
cli::GeminiAIService service(config);
|
||||||
@@ -124,7 +124,7 @@ TEST_F(GeminiVisionTest, ImageWithSpecificPrompt) {
|
|||||||
TEST_F(GeminiVisionTest, InvalidImagePath) {
|
TEST_F(GeminiVisionTest, InvalidImagePath) {
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key_;
|
config.api_key = api_key_;
|
||||||
config.model = "gemini-2.0-flash-exp";
|
config.model = "gemini-2.5-flash";
|
||||||
|
|
||||||
cli::GeminiAIService service(config);
|
cli::GeminiAIService service(config);
|
||||||
|
|
||||||
@@ -147,7 +147,7 @@ TEST_F(GeminiVisionTest, ScreenshotCaptureIntegration) {
|
|||||||
|
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key_;
|
config.api_key = api_key_;
|
||||||
config.model = "gemini-2.0-flash-exp";
|
config.model = "gemini-2.5-flash";
|
||||||
config.verbose = false;
|
config.verbose = false;
|
||||||
|
|
||||||
cli::GeminiAIService service(config);
|
cli::GeminiAIService service(config);
|
||||||
@@ -178,7 +178,7 @@ TEST_F(GeminiVisionTest, ScreenshotCaptureIntegration) {
|
|||||||
TEST_F(GeminiVisionTest, MultipleRequestsSequential) {
|
TEST_F(GeminiVisionTest, MultipleRequestsSequential) {
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key_;
|
config.api_key = api_key_;
|
||||||
config.model = "gemini-2.0-flash-exp";
|
config.model = "gemini-2.5-flash";
|
||||||
config.verbose = false;
|
config.verbose = false;
|
||||||
|
|
||||||
cli::GeminiAIService service(config);
|
cli::GeminiAIService service(config);
|
||||||
@@ -203,7 +203,7 @@ TEST_F(GeminiVisionTest, MultipleRequestsSequential) {
|
|||||||
TEST_F(GeminiVisionTest, RateLimitHandling) {
|
TEST_F(GeminiVisionTest, RateLimitHandling) {
|
||||||
cli::GeminiConfig config;
|
cli::GeminiConfig config;
|
||||||
config.api_key = api_key_;
|
config.api_key = api_key_;
|
||||||
config.model = "gemini-2.0-flash-exp";
|
config.model = "gemini-2.5-flash";
|
||||||
config.verbose = false;
|
config.verbose = false;
|
||||||
|
|
||||||
cli::GeminiAIService service(config);
|
cli::GeminiAIService service(config);
|
||||||
|
|||||||
Reference in New Issue
Block a user