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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "app/emu/emulator.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "app/core/platform/file_dialog.h"
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <fstream>
|
||||
|
||||
#include "asar-dll-bindings/c/asar.h"
|
||||
#include "cli/z3ed.h"
|
||||
#include "cli/tui/asar_patch.h"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user