feat: Enhance AgentChatWidget with system prompt management and project settings

- Updated system prompt loading functionality to support both version 1 and version 2 prompts, allowing users to load built-in prompts directly.
- Implemented custom system prompt loading and saving capabilities, enabling users to manage their prompts more effectively.
- Added methods to load and save agent settings from/to project files, including AI provider configurations and custom prompts.
- Improved user feedback with toast notifications for successful and failed operations related to system prompts and settings.
This commit is contained in:
scawful
2025-10-04 21:37:56 -04:00
parent dec8314a55
commit 9b51cd09f6
8 changed files with 227 additions and 53 deletions

View File

@@ -8,6 +8,7 @@
#include <string>
#include <utility>
#include <vector>
#include <fstream>
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
@@ -18,6 +19,7 @@
#include "app/editor/system/proposal_drawer.h"
#include "app/editor/system/toast_manager.h"
#include "app/gui/icons.h"
#include "app/core/project.h"
#include "imgui/imgui.h"
#include "imgui/misc/cpp/imgui_stdlib.h"
@@ -1644,19 +1646,17 @@ void AgentChatWidget::RenderSystemPromptEditor() {
ImGui::BeginChild("SystemPromptEditor", ImVec2(0, 0), false);
// Toolbar
if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load Default")) {
// Load system_prompt_v2.txt
std::string prompt_path = core::GetConfigDirectory() + "/../assets/agent/system_prompt_v2.txt";
std::ifstream file(prompt_path);
if (file.is_open()) {
if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load V1")) {
// Load embedded system_prompt.txt (v1)
std::string prompt_v1 = core::LoadFile("assets/agent/system_prompt.txt");
if (!prompt_v1.empty()) {
// Find or create system prompt tab
bool found = false;
for (auto& tab : open_files_) {
if (tab.is_system_prompt) {
std::stringstream buffer;
buffer << file.rdbuf();
tab.editor.SetText(buffer.str());
tab.filepath = prompt_path;
tab.editor.SetText(prompt_v1);
tab.filepath = ""; // Not saved to disk
tab.filename = "system_prompt_v1.txt (built-in)";
found = true;
break;
}
@@ -1664,39 +1664,80 @@ void AgentChatWidget::RenderSystemPromptEditor() {
if (!found) {
FileEditorTab tab;
tab.filename = "system_prompt_v2.txt";
tab.filepath = prompt_path;
tab.filename = "system_prompt_v1.txt (built-in)";
tab.filepath = "";
tab.is_system_prompt = true;
tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
std::stringstream buffer;
buffer << file.rdbuf();
tab.editor.SetText(buffer.str());
tab.editor.SetText(prompt_v1);
open_files_.push_back(std::move(tab));
active_file_tab_ = static_cast<int>(open_files_.size()) - 1;
}
if (toast_manager_) {
toast_manager_->Show("System prompt loaded", ToastType::kSuccess);
toast_manager_->Show("System prompt V1 loaded", ToastType::kSuccess);
}
} else if (toast_manager_) {
toast_manager_->Show("Could not load system prompt file", ToastType::kError);
toast_manager_->Show("Could not load system prompt V1", ToastType::kError);
}
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SAVE " Save")) {
// Save the current system prompt
if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load V2")) {
// Load embedded system_prompt_v2.txt
std::string prompt_v2 = core::LoadFile("assets/agent/system_prompt_v2.txt");
if (!prompt_v2.empty()) {
// Find or create system prompt tab
bool found = false;
for (auto& tab : open_files_) {
if (tab.is_system_prompt) {
tab.editor.SetText(prompt_v2);
tab.filepath = ""; // Not saved to disk
tab.filename = "system_prompt_v2.txt (built-in)";
found = true;
break;
}
}
if (!found) {
FileEditorTab tab;
tab.filename = "system_prompt_v2.txt (built-in)";
tab.filepath = "";
tab.is_system_prompt = true;
tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
tab.editor.SetText(prompt_v2);
open_files_.push_back(std::move(tab));
active_file_tab_ = static_cast<int>(open_files_.size()) - 1;
}
if (toast_manager_) {
toast_manager_->Show("System prompt V2 loaded", ToastType::kSuccess);
}
} else if (toast_manager_) {
toast_manager_->Show("Could not load system prompt V2", ToastType::kError);
}
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_SAVE " Save to Project")) {
// Save the current system prompt to project directory
for (auto& tab : open_files_) {
if (tab.is_system_prompt && !tab.filepath.empty()) {
std::ofstream file(tab.filepath);
if (file.is_open()) {
file << tab.editor.GetText();
tab.modified = false;
if (toast_manager_) {
toast_manager_->Show("System prompt saved", ToastType::kSuccess);
if (tab.is_system_prompt) {
auto save_path = core::FileDialogWrapper::ShowSaveFileDialog(
"custom_system_prompt", "txt");
if (!save_path.empty()) {
std::ofstream file(save_path);
if (file.is_open()) {
file << tab.editor.GetText();
tab.filepath = save_path;
tab.filename = core::GetFileName(save_path);
tab.modified = false;
if (toast_manager_) {
toast_manager_->Show(absl::StrFormat("System prompt saved to %s", save_path),
ToastType::kSuccess);
}
} else if (toast_manager_) {
toast_manager_->Show("Failed to save system prompt", ToastType::kError);
}
} else if (toast_manager_) {
toast_manager_->Show("Failed to save system prompt", ToastType::kError);
}
break;
}
@@ -1705,10 +1746,56 @@ void AgentChatWidget::RenderSystemPromptEditor() {
ImGui::SameLine();
if (ImGui::Button(ICON_MD_NOTE_ADD " Create New")) {
CreateNewFileInEditor("system_prompt_custom.txt");
if (!open_files_.empty()) {
open_files_.back().is_system_prompt = true;
open_files_.back().editor.SetText("# Custom System Prompt\n\nEnter your custom system prompt here...\n");
FileEditorTab tab;
tab.filename = "custom_system_prompt.txt (unsaved)";
tab.filepath = "";
tab.is_system_prompt = true;
tab.modified = true;
tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
tab.editor.SetText("# Custom System Prompt\n\nEnter your custom system prompt here...\n");
open_files_.push_back(std::move(tab));
active_file_tab_ = static_cast<int>(open_files_.size()) - 1;
}
ImGui::SameLine();
if (ImGui::Button(ICON_MD_FOLDER_OPEN " Load Custom")) {
auto filepath = core::FileDialogWrapper::ShowOpenFileDialog();
if (!filepath.empty()) {
std::ifstream file(filepath);
if (file.is_open()) {
bool found = false;
for (auto& tab : open_files_) {
if (tab.is_system_prompt) {
std::stringstream buffer;
buffer << file.rdbuf();
tab.editor.SetText(buffer.str());
tab.filepath = filepath;
tab.filename = core::GetFileName(filepath);
tab.modified = false;
found = true;
break;
}
}
if (!found) {
FileEditorTab tab;
tab.filename = core::GetFileName(filepath);
tab.filepath = filepath;
tab.is_system_prompt = true;
tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
std::stringstream buffer;
buffer << file.rdbuf();
tab.editor.SetText(buffer.str());
open_files_.push_back(std::move(tab));
active_file_tab_ = static_cast<int>(open_files_.size()) - 1;
}
if (toast_manager_) {
toast_manager_->Show("Custom system prompt loaded", ToastType::kSuccess);
}
} else if (toast_manager_) {
toast_manager_->Show("Could not load file", ToastType::kError);
}
}
}
@@ -1919,5 +2006,83 @@ void AgentChatWidget::CreateNewFileInEditor(const std::string& filename) {
active_file_tab_ = static_cast<int>(open_files_.size()) - 1;
}
void AgentChatWidget::LoadAgentSettingsFromProject(const core::YazeProject& project) {
// Load AI provider settings from project
agent_config_.ai_provider = project.agent_settings.ai_provider;
agent_config_.ai_model = project.agent_settings.ai_model;
agent_config_.ollama_host = project.agent_settings.ollama_host;
agent_config_.gemini_api_key = project.agent_settings.gemini_api_key;
agent_config_.show_reasoning = project.agent_settings.show_reasoning;
agent_config_.verbose = project.agent_settings.verbose;
agent_config_.max_tool_iterations = project.agent_settings.max_tool_iterations;
agent_config_.max_retry_attempts = project.agent_settings.max_retry_attempts;
// Copy to buffer for ImGui
strncpy(agent_config_.provider_buffer, agent_config_.ai_provider.c_str(),
sizeof(agent_config_.provider_buffer) - 1);
strncpy(agent_config_.model_buffer, agent_config_.ai_model.c_str(),
sizeof(agent_config_.model_buffer) - 1);
strncpy(agent_config_.ollama_host_buffer, agent_config_.ollama_host.c_str(),
sizeof(agent_config_.ollama_host_buffer) - 1);
strncpy(agent_config_.gemini_key_buffer, agent_config_.gemini_api_key.c_str(),
sizeof(agent_config_.gemini_key_buffer) - 1);
// Load custom system prompt if specified
if (project.agent_settings.use_custom_prompt &&
!project.agent_settings.custom_system_prompt.empty()) {
std::string prompt_path = project.GetAbsolutePath(project.agent_settings.custom_system_prompt);
std::ifstream file(prompt_path);
if (file.is_open()) {
// Load into system prompt tab
bool found = false;
for (auto& tab : open_files_) {
if (tab.is_system_prompt) {
std::stringstream buffer;
buffer << file.rdbuf();
tab.editor.SetText(buffer.str());
tab.filepath = prompt_path;
tab.filename = core::GetFileName(prompt_path);
found = true;
break;
}
}
if (!found) {
FileEditorTab tab;
tab.filename = core::GetFileName(prompt_path);
tab.filepath = prompt_path;
tab.is_system_prompt = true;
tab.editor.SetLanguageDefinition(TextEditor::LanguageDefinition::CPlusPlus());
std::stringstream buffer;
buffer << file.rdbuf();
tab.editor.SetText(buffer.str());
open_files_.push_back(std::move(tab));
}
}
}
}
void AgentChatWidget::SaveAgentSettingsToProject(core::YazeProject& project) {
// Save AI provider settings to project
project.agent_settings.ai_provider = agent_config_.ai_provider;
project.agent_settings.ai_model = agent_config_.ai_model;
project.agent_settings.ollama_host = agent_config_.ollama_host;
project.agent_settings.gemini_api_key = agent_config_.gemini_api_key;
project.agent_settings.show_reasoning = agent_config_.show_reasoning;
project.agent_settings.verbose = agent_config_.verbose;
project.agent_settings.max_tool_iterations = agent_config_.max_tool_iterations;
project.agent_settings.max_retry_attempts = agent_config_.max_retry_attempts;
// Check if a custom system prompt is loaded
for (const auto& tab : open_files_) {
if (tab.is_system_prompt && !tab.filepath.empty()) {
project.agent_settings.custom_system_prompt =
project.GetRelativePath(tab.filepath);
project.agent_settings.use_custom_prompt = true;
break;
}
}
}
} // namespace editor
} // namespace yaze

View File

@@ -12,6 +12,7 @@
#include "absl/time/time.h"
#include "app/gui/modules/text_editor.h"
#include "cli/service/agent/conversational_agent_service.h"
#include "core/project.h"
namespace yaze {
@@ -176,6 +177,10 @@ public:
// Agent configuration accessors
const AgentConfigState& GetAgentConfig() const { return agent_config_; }
void UpdateAgentConfig(const AgentConfigState& config);
// Load agent settings from project
void LoadAgentSettingsFromProject(const core::YazeProject& project);
void SaveAgentSettingsToProject(core::YazeProject& project);
// Collaboration history management (public so EditorManager can call them)
void SwitchToSharedHistory(const std::string& session_id);

View File

@@ -514,7 +514,7 @@ void EditorManager::Initialize(const std::string& filename) {
context_.shortcut_manager.GetKeys("Open"),
context_.shortcut_manager.GetCallback("Open")},
{absl::StrCat(ICON_MD_HISTORY, " Open Recent"), "", []() {},
[]() { return !manager.GetRecentFiles().empty(); }, recent_files},
[&manager]() { return !manager.GetRecentFiles().empty(); }, recent_files},
{absl::StrCat(ICON_MD_FILE_DOWNLOAD, " Save"),
context_.shortcut_manager.GetKeys("Save"),
context_.shortcut_manager.GetCallback("Save")},

View File

@@ -1,6 +1,7 @@
#include "app/emu/emulator.h"
#include <cstdint>
#include <fstream>
#include <vector>
#include "app/core/platform/file_dialog.h"

View File

@@ -25,6 +25,7 @@
#include "cli/handlers/message.h"
#include "cli/handlers/overworld_inspect.h"
#include "cli/service/resources/resource_context_builder.h"
#include "nlohmann/json.hpp"
#include "util/macro.h"
ABSL_DECLARE_FLAG(std::string, rom);
@@ -1353,22 +1354,20 @@ absl::Status HandleOverworldGetEntranceCommand(
static_cast<uint8_t>(entrance_id)));
if (format == "json") {
std::cout << "{\n";
std::cout << absl::StrFormat(" \"entrance_id\": %d,\n", entrance.entrance_id);
std::cout << absl::StrFormat(" \"map\": \"0x%02X\",\n", entrance.map_id);
std::cout << absl::StrFormat(" \"world\": \"%s\",\n", overworld::WorldName(entrance.world));
std::cout << absl::StrFormat(" \"position\": {\"x\": %d, \"y\": %d},\n", entrance.x, entrance.y);
std::cout << absl::StrFormat(" \"room_id\": \"0x%04X\",\n", entrance.room_id);
std::cout << absl::StrFormat(" \"door_type_1\": \"0x%04X\",\n", entrance.door_type_1);
std::cout << absl::StrFormat(" \"door_type_2\": \"0x%04X\",\n", entrance.door_type_2);
std::cout << absl::StrFormat(" \"is_hole\": %s", entrance.is_hole ? "true" : "false");
nlohmann::json j;
j["entrance_id"] = entrance.entrance_id;
j["map"] = absl::StrFormat("0x%02X", entrance.map_id);
j["world"] = overworld::WorldName(entrance.world);
j["position"] = {{"x", entrance.x}, {"y", entrance.y}};
j["area"] = {{"x", static_cast<int>(entrance.area_x)}, {"y", static_cast<int>(entrance.area_y)}};
j["map_pos"] = absl::StrFormat("0x%04X", entrance.map_pos);
j["is_hole"] = entrance.is_hole;
if (entrance.entrance_name.has_value()) {
std::cout << absl::StrFormat(",\n \"entrance_name\": \"%s\"", *entrance.entrance_name);
j["entrance_name"] = *entrance.entrance_name;
}
if (entrance.room_name.has_value()) {
std::cout << absl::StrFormat(",\n \"room_name\": \"%s\"", *entrance.room_name);
}
std::cout << "\n}\n";
std::cout << j.dump(2) << std::endl;
} else {
std::cout << absl::StrFormat("🚪 Entrance #%d\n", entrance.entrance_id);
if (entrance.entrance_name.has_value()) {
@@ -1377,13 +1376,10 @@ absl::Status HandleOverworldGetEntranceCommand(
std::cout << absl::StrFormat("Map: 0x%02X (%s World)\n", entrance.map_id,
overworld::WorldName(entrance.world));
std::cout << absl::StrFormat("Position: (%d, %d)\n", entrance.x, entrance.y);
std::cout << absl::StrFormat("→ Leads to Room 0x%04X", entrance.room_id);
if (entrance.room_name.has_value()) {
std::cout << " (" << *entrance.room_name << ")";
}
std::cout << "\n";
std::cout << absl::StrFormat("Door Types: 0x%04X / 0x%04X\n",
entrance.door_type_1, entrance.door_type_2);
std::cout << absl::StrFormat("Area: (%d, %d)\n",
static_cast<int>(entrance.area_x),
static_cast<int>(entrance.area_y));
std::cout << absl::StrFormat("Map Pos: 0x%04X\n", entrance.map_pos);
std::cout << absl::StrFormat("Is Hole: %s\n", entrance.is_hole ? "yes" : "no");
}

View File

@@ -1,3 +1,5 @@
#include <fstream>
#include "asar-dll-bindings/c/asar.h"
#include "cli/z3ed.h"
#include "cli/tui/asar_patch.h"

View File

@@ -1,4 +1,7 @@
#include "cli/z3ed.h"
#include <fstream>
#include "absl/flags/flag.h"
#include "absl/flags/declare.h"
#include "absl/strings/str_format.h"

View File

@@ -1,5 +1,7 @@
#include "tui.h"
#include <fstream>
#include <ftxui/component/component.hpp>
#include <ftxui/component/screen_interactive.hpp>
#include <ftxui/dom/elements.hpp>