yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tool_dispatcher.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <chrono>
5#include <future>
6#include <memory>
7#include <sstream>
8#include <string>
9
10#include "absl/strings/ascii.h"
11#include "absl/strings/str_cat.h"
14
15namespace yaze {
16namespace cli {
17namespace agent {
18
19namespace {
20
21// Convert tool call arguments map to command-line style vector
22std::vector<std::string> ConvertArgsToVector(
23 const std::map<std::string, std::string>& args) {
24 std::vector<std::string> result;
25
26 for (const auto& [key, value] : args) {
27 // Convert to --key=value format
28 result.push_back(absl::StrCat("--", key, "=", value));
29 }
30
31 // Always request JSON format for tool calls (easier for AI to parse)
32 bool has_format = false;
33 for (const auto& arg : result) {
34 if (arg.find("--format=") == 0) {
35 has_format = true;
36 break;
37 }
38 }
39 if (!has_format) {
40 result.push_back("--format=json");
41 }
42
43 return result;
44}
45
46} // namespace
47
49 // Check preferences based on category
50 if (def.category == "resource") return preferences_.resources;
51 if (def.category == "dungeon") return preferences_.dungeon;
52 if (def.category == "overworld") return preferences_.overworld;
53 if (def.category == "message" || def.category == "dialogue") return preferences_.messages; // Merge for simplicity or split if needed
54 if (def.category == "gui") return preferences_.gui;
55 if (def.category == "music") return preferences_.music;
56 if (def.category == "sprite") return preferences_.sprite;
57 if (def.category == "emulator") return preferences_.emulator;
58 if (def.category == "filesystem") return preferences_.filesystem;
59 if (def.category == "build") return preferences_.build;
60 if (def.category == "memory") return preferences_.memory_inspector;
61 if (def.category == "meta") return preferences_.meta_tools;
62 if (def.category == "tools") return preferences_.test_helpers; // "tools" category in old mapping
63 if (def.category == "visual") return preferences_.visual_analysis;
64 if (def.category == "codegen") return preferences_.code_gen;
65 if (def.category == "project") return preferences_.project;
66
67 return true; // Default enable
68}
69
70absl::StatusOr<std::string> ToolDispatcher::Dispatch(const ToolCall& call) {
71 auto tool_def_or = ToolRegistry::Get().GetToolDefinition(call.tool_name);
72 if (!tool_def_or) {
73 return absl::InvalidArgumentError(
74 absl::StrCat("Unknown tool: ", call.tool_name));
75 }
76 const ToolDefinition& tool_def = tool_def_or.value();
77
78 if (!IsToolEnabled(tool_def)) {
79 return absl::FailedPreconditionError(absl::StrCat(
80 "Tool '", call.tool_name, "' disabled by current agent configuration"));
81 }
82
83 // Create handler from registry
84 auto handler_or = ToolRegistry::Get().CreateHandler(call.tool_name);
85 if (!handler_or.ok()) {
86 return handler_or.status();
87 }
88 auto handler = std::move(handler_or.value());
89
90 // Set contexts for the handler
91 handler->SetRomContext(rom_context_);
92 handler->SetProjectContext(project_context_);
93 handler->SetAsarWrapper(asar_wrapper_);
94
95 // Convert arguments to command-line style
96 std::vector<std::string> args = ConvertArgsToVector(call.args);
97
98 // Check if ROM context is required but not available
99 if (tool_def.requires_rom && !rom_context_) {
100 return absl::FailedPreconditionError(
101 absl::StrCat("Tool '", call.tool_name,
102 "' requires ROM context but none is available"));
103 }
104
105 // Check if Project context is required but not available
106 if (tool_def.requires_project && !project_context_) {
107 return absl::FailedPreconditionError(
108 absl::StrCat("Tool '", call.tool_name,
109 "' requires Project context but none is available"));
110 }
111
112 // Execute the command handler
113 std::stringstream output_buffer;
114 std::streambuf* old_cout = std::cout.rdbuf(output_buffer.rdbuf());
115
116 absl::Status status = handler->Run(args, rom_context_);
117
118 std::cout.rdbuf(old_cout);
119
120 if (!status.ok()) {
121 return status;
122 }
123
124 std::string output = output_buffer.str();
125 if (output.empty()) {
126 return absl::InternalError(
127 absl::StrCat("Tool '", call.tool_name, "' produced no output"));
128 }
129
130 return output;
131}
132
133std::vector<ToolDispatcher::ToolInfo> ToolDispatcher::GetAvailableTools() const {
134 std::vector<ToolInfo> tools;
135 auto all_defs = ToolRegistry::Get().GetAllTools();
136
137 for (const auto& def : all_defs) {
138 if (IsToolEnabled(def)) {
139 tools.push_back({def.name, def.category, def.description, def.usage, def.examples, def.requires_rom, def.requires_project});
140 }
141 }
142 return tools;
143}
144
145std::optional<ToolDispatcher::ToolInfo> ToolDispatcher::GetToolInfo(
146 const std::string& tool_name) const {
147 auto def = ToolRegistry::Get().GetToolDefinition(tool_name);
148 if (def) {
149 return ToolInfo{def->name, def->category, def->description, def->usage, def->examples, def->requires_rom, def->requires_project};
150 }
151 return std::nullopt;
152}
153
154std::vector<ToolDispatcher::ToolInfo> ToolDispatcher::SearchTools(
155 const std::string& query) const {
156 std::vector<ToolInfo> matches;
157 std::string lower_query = absl::AsciiStrToLower(query);
158
159 auto all_tools = GetAvailableTools();
160 for (const auto& tool : all_tools) {
161 std::string lower_name = absl::AsciiStrToLower(tool.name);
162 std::string lower_desc = absl::AsciiStrToLower(tool.description);
163 std::string lower_category = absl::AsciiStrToLower(tool.category);
164
165 if (lower_name.find(lower_query) != std::string::npos ||
166 lower_desc.find(lower_query) != std::string::npos ||
167 lower_category.find(lower_query) != std::string::npos) {
168 matches.push_back(tool);
169 }
170 }
171
172 return matches;
173}
174
176 const BatchToolCall& batch) {
177 BatchResult result;
178 result.results.resize(batch.calls.size());
179 result.statuses.resize(batch.calls.size());
180
181 auto start_time = std::chrono::high_resolution_clock::now();
182
183 if (batch.parallel && batch.calls.size() > 1) {
184 // Parallel execution using std::async
185 std::vector<std::future<absl::StatusOr<std::string>>> futures;
186 futures.reserve(batch.calls.size());
187
188 for (const auto& call : batch.calls) {
189 futures.push_back(std::async(std::launch::async, [this, &call]() {
190 return this->Dispatch(call);
191 }));
192 }
193
194 // Collect results
195 for (size_t i = 0; i < futures.size(); ++i) {
196 auto status_or = futures[i].get();
197 if (status_or.ok()) {
198 result.results[i] = std::move(status_or.value());
199 result.statuses[i] = absl::OkStatus();
200 result.successful_count++;
201 } else {
202 result.results[i] = "";
203 result.statuses[i] = status_or.status();
204 result.failed_count++;
205 }
206 }
207 } else {
208 // Sequential execution
209 for (size_t i = 0; i < batch.calls.size(); ++i) {
210 auto status_or = Dispatch(batch.calls[i]);
211 if (status_or.ok()) {
212 result.results[i] = std::move(status_or.value());
213 result.statuses[i] = absl::OkStatus();
214 result.successful_count++;
215 } else {
216 result.results[i] = "";
217 result.statuses[i] = status_or.status();
218 result.failed_count++;
219 }
220 }
221 }
222
223 auto end_time = std::chrono::high_resolution_clock::now();
225 std::chrono::duration<double, std::milli>(end_time - start_time).count();
226
227 return result;
228}
229
230} // namespace agent
231} // namespace cli
232} // namespace yaze
std::optional< ToolInfo > GetToolInfo(const std::string &tool_name) const
Get detailed information about a specific tool.
std::vector< ToolInfo > SearchTools(const std::string &query) const
Search tools by keyword.
project::YazeProject * project_context_
bool IsToolEnabled(const ToolDefinition &def) const
BatchResult DispatchBatch(const BatchToolCall &batch)
Execute multiple tool calls in a batch.
std::vector< ToolInfo > GetAvailableTools() const
Get list of all available tools.
absl::StatusOr< std::string > Dispatch(const ::yaze::cli::ToolCall &tool_call)
std::optional< ToolDefinition > GetToolDefinition(const std::string &name) const
static ToolRegistry & Get()
std::vector< ToolDefinition > GetAllTools() const
absl::StatusOr< std::unique_ptr< resources::CommandHandler > > CreateHandler(const std::string &tool_name) const
std::vector< std::string > ConvertArgsToVector(const std::map< std::string, std::string > &args)
std::map< std::string, std::string > args
Definition common.h:14
std::string tool_name
Definition common.h:13
Metadata describing a tool for the LLM.
Tool information for discoverability.