yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
tool_registry.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <string>
5#include <string_view>
6
7#include "absl/container/flat_hash_set.h"
8#include "absl/strings/str_cat.h"
9#include "absl/strings/str_split.h"
11
12namespace yaze {
13namespace cli {
14namespace agent {
15
16namespace {
17
18std::string StripTokenDelimiters(std::string token) {
19 while (!token.empty() && (token.front() == '[' || token.front() == '(' ||
20 token.front() == ',')) {
21 token.erase(token.begin());
22 }
23 while (!token.empty() &&
24 (token.back() == ']' || token.back() == ')' || token.back() == ',')) {
25 token.pop_back();
26 }
27 return token;
28}
29
30std::vector<std::string> SplitUsageTokens(std::string_view usage) {
31 std::vector<std::string> tokens;
32 // Older Abseil (e.g. distro packages on Ubuntu 22.04 CI) does not accept
33 // absl::SkipEmpty with string_view + char overload; split an owned string.
34 const std::string usage_owned(usage);
35 for (absl::string_view token :
36 absl::StrSplit(usage_owned, ' ', absl::SkipEmpty())) {
37 tokens.emplace_back(token);
38 }
39 return tokens;
40}
41
42std::vector<std::string> InferRequiredArgsFromUsage(const std::string& usage) {
43 std::vector<std::string> required_args;
44 absl::flat_hash_set<std::string> seen;
45 const auto tokens = SplitUsageTokens(usage);
46
47 int optional_depth = 0;
48 for (size_t i = 0; i < tokens.size(); ++i) {
49 const std::string& token = tokens[i];
50 optional_depth +=
51 static_cast<int>(std::count(token.begin(), token.end(), '['));
52 const bool is_optional = optional_depth > 0;
53
54 const std::string cleaned = StripTokenDelimiters(token);
55 if (!is_optional && cleaned.rfind("--", 0) == 0) {
56 std::string arg_name = cleaned.substr(2);
57 const size_t equals_pos = arg_name.find('=');
58 if (equals_pos != std::string::npos) {
59 arg_name = arg_name.substr(0, equals_pos);
60 } else if (i + 1 < tokens.size()) {
61 const std::string next = StripTokenDelimiters(tokens[i + 1]);
62 if (next.empty() || next.front() != '<') {
63 arg_name.clear();
64 }
65 } else {
66 arg_name.clear();
67 }
68
69 if (!arg_name.empty() && seen.insert(arg_name).second) {
70 required_args.push_back(std::move(arg_name));
71 }
72 }
73
74 optional_depth -=
75 static_cast<int>(std::count(token.begin(), token.end(), ']'));
76 }
77
78 return required_args;
79}
80
81std::vector<std::string> InferFlagArgsFromUsage(const std::string& usage) {
82 std::vector<std::string> flag_args;
83 absl::flat_hash_set<std::string> seen;
84 const auto tokens = SplitUsageTokens(usage);
85
86 for (size_t i = 0; i < tokens.size(); ++i) {
87 const std::string cleaned = StripTokenDelimiters(tokens[i]);
88 if (cleaned.rfind("--", 0) != 0) {
89 continue;
90 }
91
92 std::string arg_name = cleaned.substr(2);
93 if (arg_name.find('=') != std::string::npos) {
94 continue;
95 }
96
97 const bool has_value_token =
98 i + 1 < tokens.size() && !StripTokenDelimiters(tokens[i + 1]).empty() &&
99 StripTokenDelimiters(tokens[i + 1]).front() == '<';
100 if (has_value_token) {
101 continue;
102 }
103
104 if (seen.insert(arg_name).second) {
105 flag_args.push_back(std::move(arg_name));
106 }
107 }
108
109 return flag_args;
110}
111
112ToolAccess InferToolAccess(const std::string& tool_name) {
113 static const absl::flat_hash_set<std::string> kMutatingTools = {
114 "build-compile",
115 "build-configure",
116 "build-test",
117 "dungeon-import-custom-collision-json",
118 "dungeon-import-water-fill-json",
119 "dungeon-set-room-property",
120 "emulator-clear-breakpoint",
121 "emulator-pause",
122 "emulator-reset",
123 "emulator-run",
124 "emulator-set-breakpoint",
125 "emulator-step",
126 "emulator-write-memory",
127 "gui-click",
128 "gui-place-tile",
129 "gui-type",
130 "overworld-set-tile",
131 "project-export",
132 "project-import",
133 "project-restore",
134 "project-snapshot",
135 "tools-extract-golden",
136 "tools-patch-v3",
137 };
138
139 return kMutatingTools.contains(tool_name) ? ToolAccess::kMutating
141}
142
144 if (def.access == ToolAccess::kReadOnly) {
145 def.access = InferToolAccess(def.name);
146 }
147 if (def.required_args.empty()) {
149 }
150 if (def.flag_args.empty()) {
152 }
153 return def;
154}
155
156} // namespace
157
159 static ToolRegistry instance;
160 instance.EnsureInitialized();
161 return instance;
162}
163
165 std::call_once(init_once_, [this]() { RegisterBuiltinAgentTools(*this); });
166}
167
169 HandlerFactory factory) {
170 ToolDefinition normalized = NormalizeDefinition(def);
171 const std::string tool_name = normalized.name;
172 tools_[tool_name] = {std::move(normalized), std::move(factory)};
173}
174
175std::vector<ToolDefinition> ToolRegistry::GetAllTools() const {
176 std::vector<ToolDefinition> defs;
177 defs.reserve(tools_.size());
178 for (const auto& [name, entry] : tools_) {
179 defs.push_back(entry.def);
180 }
181 return defs;
182}
183
184std::optional<ToolDefinition> ToolRegistry::GetToolDefinition(
185 const std::string& name) const {
186 auto it = tools_.find(name);
187 if (it != tools_.end()) {
188 return it->second.def;
189 }
190 return std::nullopt;
191}
192
193std::vector<ToolDefinition> ToolRegistry::GetToolsByCategory(
194 const std::string& category) const {
195 std::vector<ToolDefinition> defs;
196 for (const auto& [name, entry] : tools_) {
197 if (entry.def.category == category) {
198 defs.push_back(entry.def);
199 }
200 }
201 return defs;
202}
203
204absl::StatusOr<std::unique_ptr<resources::CommandHandler>>
205ToolRegistry::CreateHandler(const std::string& tool_name) const {
206 auto it = tools_.find(tool_name);
207 if (it == tools_.end()) {
208 return absl::NotFoundError(absl::StrCat("Tool not found: ", tool_name));
209 }
210 return it->second.factory();
211}
212
213} // namespace agent
214} // namespace cli
215} // namespace yaze
Centralized registry for all agent tools.
void RegisterTool(const ToolDefinition &def, HandlerFactory factory)
std::function< std::unique_ptr< resources::CommandHandler >()> HandlerFactory
std::optional< ToolDefinition > GetToolDefinition(const std::string &name) const
std::map< std::string, ToolEntry > tools_
static ToolRegistry & Get()
std::vector< ToolDefinition > GetToolsByCategory(const std::string &category) const
std::vector< ToolDefinition > GetAllTools() const
absl::StatusOr< std::unique_ptr< resources::CommandHandler > > CreateHandler(const std::string &tool_name) const
std::vector< std::string > InferRequiredArgsFromUsage(const std::string &usage)
ToolDefinition NormalizeDefinition(ToolDefinition def)
std::vector< std::string > SplitUsageTokens(std::string_view usage)
ToolAccess InferToolAccess(const std::string &tool_name)
std::vector< std::string > InferFlagArgsFromUsage(const std::string &usage)
void RegisterBuiltinAgentTools(ToolRegistry &registry)
Metadata describing a tool for the LLM.
std::vector< std::string > required_args
std::vector< std::string > flag_args