Enhance AI Agent Integration and Tool Command Functionality
- Added support for JSON in CMake configuration for AI integrations. - Implemented new tool commands: resource-list and dungeon-list-sprites. - Created ToolDispatcher for managing tool command execution. - Refactored CMake structure to include agent sources and improve build configuration. - Updated agent roadmap and README documentation to reflect current status and next steps.
This commit is contained in:
30
.gitignore
vendored
30
.gitignore
vendored
@@ -17,4 +17,32 @@ src/app/emu/cpu/internal/old_cpu.cc
|
|||||||
build-windows
|
build-windows
|
||||||
src/lib/libpng
|
src/lib/libpng
|
||||||
src/lib/zlib
|
src/lib/zlib
|
||||||
assets/layouts/ow_toolset.zeml
|
assets/layouts/ow_toolset.zeml
|
||||||
|
build_minimal/
|
||||||
|
build_test/
|
||||||
|
src/lib/cmake/glew/CopyImportedTargetProperties.cmake
|
||||||
|
src/lib/cmake/glew/glew-config.cmake
|
||||||
|
src/lib/cmake/glew/glew-targets-release.cmake
|
||||||
|
src/lib/cmake/glew/glew-targets.cmake
|
||||||
|
src/lib/cmake/SDL2/sdl2-config-version.cmake
|
||||||
|
src/lib/cmake/SDL2/sdl2-config.cmake
|
||||||
|
src/lib/GL/glew.h
|
||||||
|
src/lib/GL/glxew.h
|
||||||
|
src/lib/GL/wglew.h
|
||||||
|
src/lib/libGLEW.2.2.0.dylib
|
||||||
|
src/lib/libGLEW.2.2.dylib
|
||||||
|
src/lib/libGLEW.a
|
||||||
|
src/lib/libGLEW.dylib
|
||||||
|
src/lib/libSDL2_test.a
|
||||||
|
src/lib/libSDL2-2.0.0.dylib
|
||||||
|
src/lib/libSDL2.a
|
||||||
|
src/lib/libSDL2.dylib
|
||||||
|
src/lib/libSDL2main.a
|
||||||
|
src/app/gui/modules/component.h
|
||||||
|
src/ios/macOS/Info-macOS.plist
|
||||||
|
src/ios/macOS/MainMenu.storyboard
|
||||||
|
yaze_log.txt
|
||||||
|
build_test/
|
||||||
|
compile_commands.json
|
||||||
|
build*/
|
||||||
|
*.sfc
|
||||||
@@ -55,9 +55,17 @@ option(YAZE_ENABLE_EXPERIMENTAL_TESTS "Enable experimental/unstable tests" ON)
|
|||||||
option(YAZE_ENABLE_UI_TESTS "Enable ImGui Test Engine UI testing" ON)
|
option(YAZE_ENABLE_UI_TESTS "Enable ImGui Test Engine UI testing" ON)
|
||||||
option(YAZE_MINIMAL_BUILD "Minimal build for CI (disable optional features)" OFF)
|
option(YAZE_MINIMAL_BUILD "Minimal build for CI (disable optional features)" OFF)
|
||||||
|
|
||||||
|
# Optional JSON support (required for Gemini and structured agent responses)
|
||||||
|
option(YAZE_WITH_JSON "Enable JSON support for AI integrations" OFF)
|
||||||
|
|
||||||
# Optional gRPC support for ImGuiTestHarness (z3ed agent mode)
|
# Optional gRPC support for ImGuiTestHarness (z3ed agent mode)
|
||||||
option(YAZE_WITH_GRPC "Enable gRPC-based ImGuiTestHarness for automated GUI testing (experimental)" OFF)
|
option(YAZE_WITH_GRPC "Enable gRPC-based ImGuiTestHarness for automated GUI testing (experimental)" OFF)
|
||||||
|
|
||||||
|
if(YAZE_WITH_GRPC AND NOT YAZE_WITH_JSON)
|
||||||
|
message(STATUS "Enabling JSON support because gRPC is enabled")
|
||||||
|
set(YAZE_WITH_JSON ON CACHE BOOL "Enable JSON support" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Configure minimal builds for CI/CD
|
# Configure minimal builds for CI/CD
|
||||||
if(YAZE_MINIMAL_BUILD)
|
if(YAZE_MINIMAL_BUILD)
|
||||||
set(YAZE_ENABLE_UI_TESTS OFF CACHE BOOL "Disabled for minimal build" FORCE)
|
set(YAZE_ENABLE_UI_TESTS OFF CACHE BOOL "Disabled for minimal build" FORCE)
|
||||||
@@ -120,6 +128,12 @@ endif()
|
|||||||
# Create a common interface target for shared settings
|
# Create a common interface target for shared settings
|
||||||
add_library(yaze_common INTERFACE)
|
add_library(yaze_common INTERFACE)
|
||||||
target_compile_features(yaze_common INTERFACE cxx_std_23)
|
target_compile_features(yaze_common INTERFACE cxx_std_23)
|
||||||
|
target_include_directories(yaze_common INTERFACE ${CMAKE_SOURCE_DIR}/third_party/httplib)
|
||||||
|
|
||||||
|
if(YAZE_WITH_JSON)
|
||||||
|
set(JSON_BuildTests OFF CACHE INTERNAL "Disable nlohmann_json tests")
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/json ${CMAKE_BINARY_DIR}/third_party/json EXCLUDE_FROM_ALL)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Platform-specific configurations
|
# Platform-specific configurations
|
||||||
if(YAZE_PLATFORM_LINUX)
|
if(YAZE_PLATFORM_LINUX)
|
||||||
@@ -180,7 +194,6 @@ else()
|
|||||||
target_compile_definitions(yaze_common INTERFACE
|
target_compile_definitions(yaze_common INTERFACE
|
||||||
_SILENCE_CXX23_DEPRECATION_WARNING
|
_SILENCE_CXX23_DEPRECATION_WARNING
|
||||||
_SILENCE_ALL_CXX23_DEPRECATION_WARNINGS
|
_SILENCE_ALL_CXX23_DEPRECATION_WARNINGS
|
||||||
ABSL_HAVE_INTRINSIC_INT128=1 # Enable intrinsic int128 support
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
@@ -97,3 +97,24 @@ This vision will be realized through a shared interface available in both the `z
|
|||||||
6. **Performance and Cost-Saving**:
|
6. **Performance and Cost-Saving**:
|
||||||
- Implement a response cache to reduce latency and API costs.
|
- Implement a response cache to reduce latency and API costs.
|
||||||
- Add token usage tracking and reporting.
|
- Add token usage tracking and reporting.
|
||||||
|
|
||||||
|
## Current Status & Next Steps (As of Oct 3, Session 2)
|
||||||
|
|
||||||
|
We have made significant progress in laying the foundation for the conversational agent.
|
||||||
|
|
||||||
|
### ✅ Completed
|
||||||
|
- **Initial `ConversationalAgentService`**: The basic service is in place.
|
||||||
|
- **TUI Chat Stub**: A functional `agent chat` command exists.
|
||||||
|
- **GUI Chat Widget Stub**: An `AgentChatWidget` is integrated into the main GUI.
|
||||||
|
- **Initial Agent "Tools"**: `resource-list` and `dungeon-list-sprites` commands are implemented.
|
||||||
|
- **Tool Use Foundation**: The `ToolDispatcher` is implemented, and the AI services are aware of the new tool call format.
|
||||||
|
|
||||||
|
### ⚠️ Current Blocker: Build Configuration
|
||||||
|
We are currently facing a linker error when building the main `yaze` application with gRPC support. The `ToolDispatcher` is unable to find the definitions for the `HandleResourceListCommand` and `HandleDungeonListSpritesCommand` functions.
|
||||||
|
|
||||||
|
**Root Cause**: These handler functions are only compiled as part of the `z3ed` target, not the `yaze` target. The `ToolDispatcher`, which is now included in the `yaze` build, depends on them.
|
||||||
|
|
||||||
|
### 🚀 Next Steps
|
||||||
|
1. **Resolve the Build Issue**: The immediate next step is to fix the linker error. This will likely involve a thoughtful refactoring of our CMake configuration to better share sources between the `yaze` and `z3ed` targets.
|
||||||
|
2. **Simplify CMake Structure**: As discussed, the current structure of including `.cmake` files from various subdirectories is becoming difficult to manage. We should consider flattening this into a more centralized source list in the main `src/CMakeLists.txt`.
|
||||||
|
3. **Continue with Tool Integration**: Once the build is fixed, we can proceed with integrating the tool execution results back into the conversational loop.
|
||||||
|
|||||||
@@ -116,15 +116,6 @@ Here are some example prompts you can try with either Ollama or Gemini:
|
|||||||
2. **[E6-z3ed-cli-design.md](E6-z3ed-cli-design.md)** - Detailed architecture and design philosophy.
|
2. **[E6-z3ed-cli-design.md](E6-z3ed-cli-design.md)** - Detailed architecture and design philosophy.
|
||||||
3. **[E6-z3ed-reference.md](E6-z3ed-reference.md)** - Complete command reference and API documentation.
|
3. **[E6-z3ed-reference.md](E6-z3ed-reference.md)** - Complete command reference and API documentation.
|
||||||
|
|
||||||
### Quick References
|
|
||||||
- **[QUICK_REFERENCE.md](QUICK_REFERENCE.md)** - Condensed command cheatsheet.
|
|
||||||
- **[QUICK-START-GEMINI.md](QUICK-START-GEMINI.md)** - Gemini API setup and testing guide.
|
|
||||||
|
|
||||||
### Implementation Guides
|
|
||||||
- **[LLM-INTEGRATION-PLAN.md](LLM-INTEGRATION-PLAN.md)** - (Archive) Original LLM integration roadmap.
|
|
||||||
- **[IT-05-IMPLEMENTATION-GUIDE.md](IT-05-IMPLEMENTATION-GUIDE.md)** - Test introspection API (complete ✅).
|
|
||||||
- **[IT-08-IMPLEMENTATION-GUIDE.md](IT-08-IMPLEMENTATION-GUIDE.md)** - Enhanced error reporting (complete ✅).
|
|
||||||
|
|
||||||
## Current Status (October 2025)
|
## Current Status (October 2025)
|
||||||
|
|
||||||
The project is currently focused on implementing a conversational AI agent. See [AGENT-ROADMAP.md](AGENT-ROADMAP.md) for a detailed breakdown of what's complete, in progress, and planned.
|
The project is currently focused on implementing a conversational AI agent. See [AGENT-ROADMAP.md](AGENT-ROADMAP.md) for a detailed breakdown of what's complete, in progress, and planned.
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ else()
|
|||||||
message(STATUS "NFD disabled for minimal build")
|
message(STATUS "NFD disabled for minimal build")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(YAZE_BUILD_APP OR YAZE_BUILD_Z3ED)
|
||||||
|
include(cli/agent.cmake)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (YAZE_BUILD_APP)
|
if (YAZE_BUILD_APP)
|
||||||
include(app/app.cmake)
|
include(app/app.cmake)
|
||||||
endif()
|
endif()
|
||||||
@@ -171,9 +175,6 @@ if (YAZE_BUILD_LIB)
|
|||||||
${YAZE_APP_EMU_SRC}
|
${YAZE_APP_EMU_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${YAZE_UTIL_SRC}
|
${YAZE_UTIL_SRC}
|
||||||
# CLI service sources (needed for ProposalDrawer)
|
|
||||||
cli/service/planning/proposal_registry.cc
|
|
||||||
cli/service/rom/rom_sandbox_manager.cc
|
|
||||||
# cli/service/gui_automation_client.cc # Moved to yaze_c
|
# cli/service/gui_automation_client.cc # Moved to yaze_c
|
||||||
cli/service/testing/test_workflow_generator.cc
|
cli/service/testing/test_workflow_generator.cc
|
||||||
)
|
)
|
||||||
@@ -225,6 +226,7 @@ if (YAZE_BUILD_LIB)
|
|||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
yaze_core PUBLIC
|
yaze_core PUBLIC
|
||||||
asar-static
|
asar-static
|
||||||
|
yaze_agent
|
||||||
${ABSL_TARGETS}
|
${ABSL_TARGETS}
|
||||||
${SDL_TARGETS}
|
${SDL_TARGETS}
|
||||||
${CMAKE_DL_LIBS}
|
${CMAKE_DL_LIBS}
|
||||||
|
|||||||
@@ -18,10 +18,6 @@ if (APPLE)
|
|||||||
${YAZE_UTIL_SRC}
|
${YAZE_UTIL_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
# CLI service sources (needed for ProposalDrawer)
|
|
||||||
cli/service/planning/proposal_registry.cc
|
|
||||||
cli/service/rom/rom_sandbox_manager.cc
|
|
||||||
cli/service/planning/policy_evaluator.cc
|
|
||||||
# Bundled Resources
|
# Bundled Resources
|
||||||
${YAZE_RESOURCE_FILES}
|
${YAZE_RESOURCE_FILES}
|
||||||
)
|
)
|
||||||
@@ -57,10 +53,6 @@ else()
|
|||||||
${YAZE_UTIL_SRC}
|
${YAZE_UTIL_SRC}
|
||||||
${YAZE_GUI_SRC}
|
${YAZE_GUI_SRC}
|
||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
# CLI service sources (needed for ProposalDrawer)
|
|
||||||
cli/service/planning/proposal_registry.cc
|
|
||||||
cli/service/rom/rom_sandbox_manager.cc
|
|
||||||
cli/service/planning/policy_evaluator.cc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add asset files for Windows/Linux builds
|
# Add asset files for Windows/Linux builds
|
||||||
@@ -133,6 +125,8 @@ target_link_libraries(
|
|||||||
ImGui
|
ImGui
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(yaze PRIVATE yaze_agent)
|
||||||
|
|
||||||
# Enable policy framework in main yaze target
|
# Enable policy framework in main yaze target
|
||||||
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_POLICY_FRAMEWORK=1)
|
target_compile_definitions(yaze PRIVATE YAZE_ENABLE_POLICY_FRAMEWORK=1)
|
||||||
|
|
||||||
@@ -274,20 +268,6 @@ if(YAZE_WITH_GRPC)
|
|||||||
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_script_parser.cc
|
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_script_parser.cc
|
||||||
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_script_parser.h)
|
${CMAKE_SOURCE_DIR}/src/app/core/testing/test_script_parser.h)
|
||||||
|
|
||||||
# Add AI agent sources
|
|
||||||
target_sources(yaze PRIVATE
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/agent/conversational_agent_service.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/ai/service_factory.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/ai/ai_service.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/ai/ollama_ai_service.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/ai/gemini_ai_service.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/ai/prompt_builder.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/planning/tile16_proposal_generator.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/resources/resource_context_builder.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/resources/resource_catalog.cc
|
|
||||||
${CMAKE_SOURCE_DIR}/src/cli/service/agent/tool_dispatcher.cc
|
|
||||||
)
|
|
||||||
|
|
||||||
# Link gRPC libraries
|
# Link gRPC libraries
|
||||||
target_link_libraries(yaze PRIVATE
|
target_link_libraries(yaze PRIVATE
|
||||||
grpc++
|
grpc++
|
||||||
|
|||||||
46
src/cli/agent.cmake
Normal file
46
src/cli/agent.cmake
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
set(YAZE_AGENT_SOURCES
|
||||||
|
cli/handlers/agent/tool_commands.cc
|
||||||
|
cli/service/agent/conversational_agent_service.cc
|
||||||
|
cli/service/agent/tool_dispatcher.cc
|
||||||
|
cli/service/ai/ai_service.cc
|
||||||
|
cli/service/ai/ollama_ai_service.cc
|
||||||
|
cli/service/ai/prompt_builder.cc
|
||||||
|
cli/service/ai/service_factory.cc
|
||||||
|
cli/service/planning/policy_evaluator.cc
|
||||||
|
cli/service/planning/proposal_registry.cc
|
||||||
|
cli/service/planning/tile16_proposal_generator.cc
|
||||||
|
cli/service/resources/resource_catalog.cc
|
||||||
|
cli/service/resources/resource_context_builder.cc
|
||||||
|
cli/service/rom/rom_sandbox_manager.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
if(YAZE_WITH_JSON)
|
||||||
|
list(APPEND YAZE_AGENT_SOURCES cli/service/ai/gemini_ai_service.cc)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(yaze_agent STATIC ${YAZE_AGENT_SOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(yaze_agent
|
||||||
|
PUBLIC
|
||||||
|
yaze_common
|
||||||
|
${ABSL_TARGETS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(yaze_agent
|
||||||
|
PUBLIC
|
||||||
|
${CMAKE_SOURCE_DIR}/src
|
||||||
|
${CMAKE_SOURCE_DIR}/incl
|
||||||
|
${CMAKE_SOURCE_DIR}/third_party/httplib
|
||||||
|
${CMAKE_SOURCE_DIR}/src/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
if(SDL2_INCLUDE_DIR)
|
||||||
|
target_include_directories(yaze_agent PUBLIC ${SDL2_INCLUDE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(YAZE_WITH_JSON)
|
||||||
|
target_link_libraries(yaze_agent PUBLIC nlohmann_json::nlohmann_json)
|
||||||
|
target_compile_definitions(yaze_agent PUBLIC YAZE_WITH_JSON)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(yaze_agent PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||||
@@ -470,160 +470,6 @@ absl::Status HandleDescribeCommand(const std::vector<std::string>& arg_vec) {
|
|||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status HandleResourceListCommand(
|
|
||||||
const std::vector<std::string>& arg_vec) {
|
|
||||||
std::string type;
|
|
||||||
std::string format = "table";
|
|
||||||
|
|
||||||
for (size_t i = 0; i < arg_vec.size(); ++i) {
|
|
||||||
const std::string& token = arg_vec[i];
|
|
||||||
if (token == "--type") {
|
|
||||||
if (i + 1 < arg_vec.size()) {
|
|
||||||
type = arg_vec[++i];
|
|
||||||
} else {
|
|
||||||
return absl::InvalidArgumentError("--type requires a value.");
|
|
||||||
}
|
|
||||||
} else if (absl::StartsWith(token, "--type=")) {
|
|
||||||
type = token.substr(7);
|
|
||||||
} else if (token == "--format") {
|
|
||||||
if (i + 1 < arg_vec.size()) {
|
|
||||||
format = arg_vec[++i];
|
|
||||||
} else {
|
|
||||||
return absl::InvalidArgumentError("--format requires a value.");
|
|
||||||
}
|
|
||||||
} else if (absl::StartsWith(token, "--format=")) {
|
|
||||||
format = token.substr(9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type.empty()) {
|
|
||||||
return absl::InvalidArgumentError(
|
|
||||||
"Usage: agent resource-list --type <type> [--format <table|json>]");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Load the ROM
|
|
||||||
std::string rom_path = absl::GetFlag(FLAGS_rom);
|
|
||||||
if (rom_path.empty()) {
|
|
||||||
return absl::FailedPreconditionError(
|
|
||||||
"No ROM loaded. Use --rom=<path> to specify ROM file.");
|
|
||||||
}
|
|
||||||
Rom rom;
|
|
||||||
auto status = rom.LoadFromFile(rom_path);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return absl::FailedPreconditionError(absl::StrFormat(
|
|
||||||
"Failed to load ROM from '%s': %s", rom_path, status.message()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Get labels using ResourceContextBuilder
|
|
||||||
ResourceContextBuilder context_builder(&rom);
|
|
||||||
auto labels_or = context_builder.GetLabels(type);
|
|
||||||
if (!labels_or.ok()) {
|
|
||||||
return labels_or.status();
|
|
||||||
}
|
|
||||||
auto labels = labels_or.value();
|
|
||||||
|
|
||||||
// 3. Format and print output
|
|
||||||
if (format == "json") {
|
|
||||||
std::cout << "{\n";
|
|
||||||
bool first = true;
|
|
||||||
for (const auto& [key, value] : labels) {
|
|
||||||
if (!first) {
|
|
||||||
std::cout << ",\n";
|
|
||||||
}
|
|
||||||
std::cout << " \"" << key << "\": \"" << value << "\"";
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
std::cout << "\n}\n";
|
|
||||||
} else { // Table format
|
|
||||||
std::cout << "=== " << absl::AsciiStrToUpper(type) << " Labels ===\n";
|
|
||||||
for (const auto& [key, value] : labels) {
|
|
||||||
std::cout << absl::StrFormat(" %-10s : %s\n", key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status HandleDungeonListSpritesCommand(
|
|
||||||
const std::vector<std::string>& arg_vec) {
|
|
||||||
std::string room_id_str;
|
|
||||||
std::string format = "table";
|
|
||||||
|
|
||||||
for (size_t i = 0; i < arg_vec.size(); ++i) {
|
|
||||||
const std::string& token = arg_vec[i];
|
|
||||||
if (token == "--room") {
|
|
||||||
if (i + 1 < arg_vec.size()) {
|
|
||||||
room_id_str = arg_vec[++i];
|
|
||||||
} else {
|
|
||||||
return absl::InvalidArgumentError("--room requires a value.");
|
|
||||||
}
|
|
||||||
} else if (absl::StartsWith(token, "--room=")) {
|
|
||||||
room_id_str = token.substr(7);
|
|
||||||
} else if (token == "--format") {
|
|
||||||
if (i + 1 < arg_vec.size()) {
|
|
||||||
format = arg_vec[++i];
|
|
||||||
} else {
|
|
||||||
return absl::InvalidArgumentError("--format requires a value.");
|
|
||||||
}
|
|
||||||
} else if (absl::StartsWith(token, "--format=")) {
|
|
||||||
format = token.substr(9);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (room_id_str.empty()) {
|
|
||||||
return absl::InvalidArgumentError(
|
|
||||||
"Usage: agent dungeon-list-sprites --room <id> [--format "
|
|
||||||
"<table|json>]");
|
|
||||||
}
|
|
||||||
|
|
||||||
int room_id;
|
|
||||||
if (!absl::SimpleHexAtoi(room_id_str, &room_id)) {
|
|
||||||
return absl::InvalidArgumentError("Invalid room ID format. Must be hex.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Load the ROM
|
|
||||||
std::string rom_path = absl::GetFlag(FLAGS_rom);
|
|
||||||
if (rom_path.empty()) {
|
|
||||||
return absl::FailedPreconditionError(
|
|
||||||
"No ROM loaded. Use --rom=<path> to specify ROM file.");
|
|
||||||
}
|
|
||||||
Rom rom;
|
|
||||||
auto status = rom.LoadFromFile(rom_path);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return absl::FailedPreconditionError(absl::StrFormat(
|
|
||||||
"Failed to load ROM from '%s': %s", rom_path, status.message()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Load dungeon room and get sprites
|
|
||||||
auto room = zelda3::LoadRoomFromRom(&rom, room_id);
|
|
||||||
const auto& sprites = room.GetSprites();
|
|
||||||
|
|
||||||
// 3. Format and print output
|
|
||||||
if (format == "json") {
|
|
||||||
std::cout << "[\n";
|
|
||||||
for (size_t i = 0; i < sprites.size(); ++i) {
|
|
||||||
const auto& sprite = sprites[i];
|
|
||||||
std::cout << " {\n";
|
|
||||||
std::cout << " \"id\": " << sprite.id() << ",\n";
|
|
||||||
std::cout << " \"x\": " << sprite.x() << ",\n";
|
|
||||||
std::cout << " \"y\": " << sprite.y() << "\n";
|
|
||||||
std::cout << " }" << (i == sprites.size() - 1 ? "" : ",");
|
|
||||||
std::cout << "\n";
|
|
||||||
}
|
|
||||||
std::cout << "]\n";
|
|
||||||
} else { // Table format
|
|
||||||
std::cout << "=== Sprites in Room " << room_id_str << " ===\n";
|
|
||||||
std::cout << absl::StrFormat("%-10s %-5s %-5s\n", "ID (Hex)", "X", "Y");
|
|
||||||
std::cout << std::string(22, '-') << "\n";
|
|
||||||
for (const auto& sprite : sprites) {
|
|
||||||
std::cout << absl::StrFormat("0x%-8X %-5d %-5d\n", sprite.id(),
|
|
||||||
sprite.x(), sprite.y());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status HandleChatCommand() {
|
absl::Status HandleChatCommand() {
|
||||||
tui::ChatTUI chat_tui;
|
tui::ChatTUI chat_tui;
|
||||||
chat_tui.Run();
|
chat_tui.Run();
|
||||||
|
|||||||
180
src/cli/handlers/agent/tool_commands.cc
Normal file
180
src/cli/handlers/agent/tool_commands.cc
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
#include "cli/handlers/agent/commands.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "absl/flags/declare.h"
|
||||||
|
#include "absl/flags/flag.h"
|
||||||
|
#include "absl/status/status.h"
|
||||||
|
#include "absl/status/statusor.h"
|
||||||
|
#include "absl/strings/ascii.h"
|
||||||
|
#include "absl/strings/match.h"
|
||||||
|
#include "absl/strings/numbers.h"
|
||||||
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "app/rom.h"
|
||||||
|
#include "app/zelda3/dungeon/room.h"
|
||||||
|
#include "cli/service/resources/resource_context_builder.h"
|
||||||
|
|
||||||
|
ABSL_DECLARE_FLAG(std::string, rom);
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace cli {
|
||||||
|
namespace agent {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
absl::StatusOr<Rom> LoadRomFromFlag() {
|
||||||
|
std::string rom_path = absl::GetFlag(FLAGS_rom);
|
||||||
|
if (rom_path.empty()) {
|
||||||
|
return absl::FailedPreconditionError(
|
||||||
|
"No ROM loaded. Use --rom=<path> to specify ROM file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Rom rom;
|
||||||
|
auto status = rom.LoadFromFile(rom_path);
|
||||||
|
if (!status.ok()) {
|
||||||
|
return absl::FailedPreconditionError(absl::StrFormat(
|
||||||
|
"Failed to load ROM from '%s': %s", rom_path, status.message()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return rom;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
absl::Status HandleResourceListCommand(
|
||||||
|
const std::vector<std::string>& arg_vec) {
|
||||||
|
std::string type;
|
||||||
|
std::string format = "table";
|
||||||
|
|
||||||
|
for (size_t i = 0; i < arg_vec.size(); ++i) {
|
||||||
|
const std::string& token = arg_vec[i];
|
||||||
|
if (token == "--type") {
|
||||||
|
if (i + 1 >= arg_vec.size()) {
|
||||||
|
return absl::InvalidArgumentError("--type requires a value.");
|
||||||
|
}
|
||||||
|
type = arg_vec[++i];
|
||||||
|
} else if (absl::StartsWith(token, "--type=")) {
|
||||||
|
type = token.substr(7);
|
||||||
|
} else if (token == "--format") {
|
||||||
|
if (i + 1 >= arg_vec.size()) {
|
||||||
|
return absl::InvalidArgumentError("--format requires a value.");
|
||||||
|
}
|
||||||
|
format = arg_vec[++i];
|
||||||
|
} else if (absl::StartsWith(token, "--format=")) {
|
||||||
|
format = token.substr(9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type.empty()) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
"Usage: agent resource-list --type <type> [--format <table|json>]");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rom_or = LoadRomFromFlag();
|
||||||
|
if (!rom_or.ok()) {
|
||||||
|
return rom_or.status();
|
||||||
|
}
|
||||||
|
Rom rom = std::move(rom_or.value());
|
||||||
|
|
||||||
|
ResourceContextBuilder context_builder(&rom);
|
||||||
|
auto labels_or = context_builder.GetLabels(type);
|
||||||
|
if (!labels_or.ok()) {
|
||||||
|
return labels_or.status();
|
||||||
|
}
|
||||||
|
auto labels = std::move(labels_or.value());
|
||||||
|
|
||||||
|
if (format == "json") {
|
||||||
|
std::cout << "{\n";
|
||||||
|
bool first = true;
|
||||||
|
for (const auto& [key, value] : labels) {
|
||||||
|
if (!first) {
|
||||||
|
std::cout << ",\n";
|
||||||
|
}
|
||||||
|
std::cout << " \"" << key << "\": \"" << value << "\"";
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
std::cout << "\n}\n";
|
||||||
|
} else {
|
||||||
|
std::cout << "=== " << absl::AsciiStrToUpper(type) << " Labels ===\n";
|
||||||
|
for (const auto& [key, value] : labels) {
|
||||||
|
std::cout << absl::StrFormat(" %-10s : %s\n", key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status HandleDungeonListSpritesCommand(
|
||||||
|
const std::vector<std::string>& arg_vec) {
|
||||||
|
std::string room_id_str;
|
||||||
|
std::string format = "table";
|
||||||
|
|
||||||
|
for (size_t i = 0; i < arg_vec.size(); ++i) {
|
||||||
|
const std::string& token = arg_vec[i];
|
||||||
|
if (token == "--room") {
|
||||||
|
if (i + 1 >= arg_vec.size()) {
|
||||||
|
return absl::InvalidArgumentError("--room requires a value.");
|
||||||
|
}
|
||||||
|
room_id_str = arg_vec[++i];
|
||||||
|
} else if (absl::StartsWith(token, "--room=")) {
|
||||||
|
room_id_str = token.substr(7);
|
||||||
|
} else if (token == "--format") {
|
||||||
|
if (i + 1 >= arg_vec.size()) {
|
||||||
|
return absl::InvalidArgumentError("--format requires a value.");
|
||||||
|
}
|
||||||
|
format = arg_vec[++i];
|
||||||
|
} else if (absl::StartsWith(token, "--format=")) {
|
||||||
|
format = token.substr(9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (room_id_str.empty()) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
"Usage: agent dungeon-list-sprites --room <id> [--format <table|json>]");
|
||||||
|
}
|
||||||
|
|
||||||
|
int room_id;
|
||||||
|
if (!absl::SimpleHexAtoi(room_id_str, &room_id)) {
|
||||||
|
return absl::InvalidArgumentError(
|
||||||
|
"Invalid room ID format. Must be hex.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rom_or = LoadRomFromFlag();
|
||||||
|
if (!rom_or.ok()) {
|
||||||
|
return rom_or.status();
|
||||||
|
}
|
||||||
|
Rom rom = std::move(rom_or.value());
|
||||||
|
|
||||||
|
auto room = zelda3::LoadRoomFromRom(&rom, room_id);
|
||||||
|
const auto& sprites = room.GetSprites();
|
||||||
|
|
||||||
|
if (format == "json") {
|
||||||
|
std::cout << "[\n";
|
||||||
|
for (size_t i = 0; i < sprites.size(); ++i) {
|
||||||
|
const auto& sprite = sprites[i];
|
||||||
|
std::cout << " {\n";
|
||||||
|
std::cout << " \"id\": " << sprite.id() << ",\n";
|
||||||
|
std::cout << " \"x\": " << sprite.x() << ",\n";
|
||||||
|
std::cout << " \"y\": " << sprite.y() << "\n";
|
||||||
|
std::cout << " }" << (i + 1 == sprites.size() ? "" : ",") << "\n";
|
||||||
|
}
|
||||||
|
std::cout << "]\n";
|
||||||
|
} else {
|
||||||
|
std::cout << "=== Sprites in Room " << room_id_str << " ===\n";
|
||||||
|
std::cout << absl::StrFormat("%-10s %-5s %-5s\n", "ID (Hex)", "X", "Y");
|
||||||
|
std::cout << std::string(22, '-') << "\n";
|
||||||
|
for (const auto& sprite : sprites) {
|
||||||
|
std::cout << absl::StrFormat("0x%-8X %-5d %-5d\n", sprite.id(),
|
||||||
|
sprite.x(), sprite.y());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace agent
|
||||||
|
} // namespace cli
|
||||||
|
} // namespace yaze
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
#include "cli/service/agent/tool_dispatcher.h"
|
#include "cli/service/agent/tool_dispatcher.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "cli/handlers/agent/commands.h"
|
#include "cli/handlers/agent/commands.h"
|
||||||
|
|
||||||
@@ -15,24 +18,29 @@ absl::StatusOr<std::string> ToolDispatcher::Dispatch(
|
|||||||
args.push_back(value);
|
args.push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture stdout
|
||||||
|
std::stringstream buffer;
|
||||||
|
auto old_cout_buf = std::cout.rdbuf();
|
||||||
|
std::cout.rdbuf(buffer.rdbuf());
|
||||||
|
|
||||||
|
absl::Status status;
|
||||||
if (tool_call.tool_name == "resource-list") {
|
if (tool_call.tool_name == "resource-list") {
|
||||||
// Note: This is a simplified approach for now. A more robust solution
|
status = HandleResourceListCommand(args);
|
||||||
// would capture stdout instead of relying on the handler to return a string.
|
|
||||||
auto status = HandleResourceListCommand(args);
|
|
||||||
if (!status.ok()) {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
return "Successfully listed resources.";
|
|
||||||
} else if (tool_call.tool_name == "dungeon-list-sprites") {
|
} else if (tool_call.tool_name == "dungeon-list-sprites") {
|
||||||
auto status = HandleDungeonListSpritesCommand(args);
|
status = HandleDungeonListSpritesCommand(args);
|
||||||
if (!status.ok()) {
|
} else {
|
||||||
return status;
|
status = absl::UnimplementedError(
|
||||||
}
|
absl::StrFormat("Unknown tool: %s", tool_call.tool_name));
|
||||||
return "Successfully listed sprites.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return absl::UnimplementedError(
|
// Restore stdout
|
||||||
absl::StrFormat("Unknown tool: %s", tool_call.tool_name));
|
std::cout.rdbuf(old_cout_buf);
|
||||||
|
|
||||||
|
if (!status.ok()) {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace agent
|
} // namespace agent
|
||||||
|
|||||||
@@ -1,23 +1,32 @@
|
|||||||
#include "cli/service/ai/service_factory.h"
|
#include "cli/service/ai/service_factory.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "cli/service/ai/ai_service.h"
|
#include "cli/service/ai/ai_service.h"
|
||||||
#include "cli/service/ai/gemini_ai_service.h"
|
|
||||||
#include "cli/service/ai/ollama_ai_service.h"
|
#include "cli/service/ai/ollama_ai_service.h"
|
||||||
|
|
||||||
|
#ifdef YAZE_WITH_JSON
|
||||||
|
#include "cli/service/ai/gemini_ai_service.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace cli {
|
namespace cli {
|
||||||
|
|
||||||
std::unique_ptr<AIService> CreateAIService() {
|
std::unique_ptr<AIService> CreateAIService() {
|
||||||
// Priority: Ollama (local) > Gemini (remote) > Mock (testing)
|
// Priority: Ollama (local) > Gemini (remote) > Mock (testing)
|
||||||
const char* provider_env = std::getenv("YAZE_AI_PROVIDER");
|
const char* provider_env = std::getenv("YAZE_AI_PROVIDER");
|
||||||
const char* gemini_key = std::getenv("GEMINI_API_KEY");
|
|
||||||
const char* ollama_model = std::getenv("OLLAMA_MODEL");
|
const char* ollama_model = std::getenv("OLLAMA_MODEL");
|
||||||
|
const std::string provider = provider_env ? provider_env : "";
|
||||||
|
const bool gemini_requested = provider == "gemini";
|
||||||
|
|
||||||
|
#ifdef YAZE_WITH_JSON
|
||||||
|
const char* gemini_key = std::getenv("GEMINI_API_KEY");
|
||||||
const char* gemini_model = std::getenv("GEMINI_MODEL");
|
const char* gemini_model = std::getenv("GEMINI_MODEL");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Explicit provider selection
|
// Explicit provider selection
|
||||||
if (provider_env && std::string(provider_env) == "ollama") {
|
if (provider == "ollama") {
|
||||||
OllamaConfig config;
|
OllamaConfig config;
|
||||||
|
|
||||||
// Allow model override via env
|
// Allow model override via env
|
||||||
@@ -39,6 +48,7 @@ std::unique_ptr<AIService> CreateAIService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gemini if API key provided
|
// Gemini if API key provided
|
||||||
|
#ifdef YAZE_WITH_JSON
|
||||||
if (gemini_key && std::strlen(gemini_key) > 0) {
|
if (gemini_key && std::strlen(gemini_key) > 0) {
|
||||||
GeminiConfig config(gemini_key);
|
GeminiConfig config(gemini_key);
|
||||||
|
|
||||||
@@ -59,6 +69,11 @@ std::unique_ptr<AIService> CreateAIService() {
|
|||||||
std::cout << "🤖 Using Gemini AI with model: " << config.model << std::endl;
|
std::cout << "🤖 Using Gemini AI with model: " << config.model << std::endl;
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (gemini_requested || std::getenv("GEMINI_API_KEY")) {
|
||||||
|
std::cerr << "⚠️ Gemini support not available: rebuild with YAZE_WITH_JSON=ON" << std::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Default: Mock service for testing
|
// Default: Mock service for testing
|
||||||
std::cout << "🤖 Using MockAIService (no LLM configured)" << std::endl;
|
std::cout << "🤖 Using MockAIService (no LLM configured)" << std::endl;
|
||||||
|
|||||||
@@ -48,13 +48,6 @@ add_executable(
|
|||||||
cli/handlers/agent/test_common.cc
|
cli/handlers/agent/test_common.cc
|
||||||
cli/handlers/agent/test_commands.cc
|
cli/handlers/agent/test_commands.cc
|
||||||
cli/handlers/agent/gui_commands.cc
|
cli/handlers/agent/gui_commands.cc
|
||||||
cli/service/ai/ai_service.cc
|
|
||||||
cli/service/ai/ollama_ai_service.cc
|
|
||||||
cli/service/ai/prompt_builder.cc
|
|
||||||
cli/service/planning/proposal_registry.cc
|
|
||||||
cli/service/resources/resource_catalog.cc
|
|
||||||
cli/service/rom/rom_sandbox_manager.cc
|
|
||||||
cli/service/planning/policy_evaluator.cc
|
|
||||||
cli/service/testing/test_suite.h
|
cli/service/testing/test_suite.h
|
||||||
cli/service/testing/test_suite_loader.cc
|
cli/service/testing/test_suite_loader.cc
|
||||||
cli/service/testing/test_suite_loader.h
|
cli/service/testing/test_suite_loader.h
|
||||||
@@ -62,17 +55,11 @@ add_executable(
|
|||||||
cli/service/testing/test_suite_reporter.h
|
cli/service/testing/test_suite_reporter.h
|
||||||
cli/service/testing/test_suite_writer.cc
|
cli/service/testing/test_suite_writer.cc
|
||||||
cli/service/testing/test_suite_writer.h
|
cli/service/testing/test_suite_writer.h
|
||||||
cli/service/ai/gemini_ai_service.cc
|
|
||||||
cli/service/planning/tile16_proposal_generator.h
|
cli/service/planning/tile16_proposal_generator.h
|
||||||
cli/service/planning/tile16_proposal_generator.cc
|
|
||||||
cli/service/resources/resource_context_builder.h
|
cli/service/resources/resource_context_builder.h
|
||||||
cli/service/resources/resource_context_builder.cc
|
|
||||||
cli/service/agent/conversational_agent_service.h
|
cli/service/agent/conversational_agent_service.h
|
||||||
cli/service/agent/conversational_agent_service.cc
|
|
||||||
cli/service/ai/service_factory.h
|
cli/service/ai/service_factory.h
|
||||||
cli/service/ai/service_factory.cc
|
|
||||||
cli/service/agent/tool_dispatcher.h
|
cli/service/agent/tool_dispatcher.h
|
||||||
cli/service/agent/tool_dispatcher.cc
|
|
||||||
app/rom.cc
|
app/rom.cc
|
||||||
app/core/project.cc
|
app/core/project.cc
|
||||||
app/core/asar_wrapper.cc
|
app/core/asar_wrapper.cc
|
||||||
@@ -85,13 +72,8 @@ add_executable(
|
|||||||
${IMGUI_SRC}
|
${IMGUI_SRC}
|
||||||
)
|
)
|
||||||
|
|
||||||
option(YAZE_WITH_JSON "Build with JSON support" OFF)
|
|
||||||
if(YAZE_WITH_JSON)
|
if(YAZE_WITH_JSON)
|
||||||
add_subdirectory(${CMAKE_SOURCE_DIR}/third_party/json ${CMAKE_BINARY_DIR}/third_party/json)
|
|
||||||
target_compile_definitions(z3ed PRIVATE YAZE_WITH_JSON)
|
target_compile_definitions(z3ed PRIVATE YAZE_WITH_JSON)
|
||||||
target_link_libraries(z3ed PRIVATE nlohmann_json::nlohmann_json)
|
|
||||||
list(APPEND Z3ED_SRC_FILES cli/service/ai/gemini_ai_service.cc)
|
|
||||||
list(APPEND Z3ED_SRC_FILES cli/service/ai/prompt_builder.cc)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -144,6 +126,7 @@ target_include_directories(
|
|||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
z3ed PUBLIC
|
z3ed PUBLIC
|
||||||
asar-static
|
asar-static
|
||||||
|
yaze_agent
|
||||||
ftxui::component
|
ftxui::component
|
||||||
ftxui::screen
|
ftxui::screen
|
||||||
ftxui::dom
|
ftxui::dom
|
||||||
|
|||||||
Reference in New Issue
Block a user