yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
prompt_builder.cc
Go to the documentation of this file.
2
3#include <cstdlib>
4#include <filesystem>
5#include <fstream>
6#include <iostream>
7#include <sstream>
8
9#include "absl/strings/ascii.h"
10#include "absl/strings/str_cat.h"
11#include "absl/strings/str_join.h"
14#include "nlohmann/json.hpp"
15#include "util/platform_paths.h"
16
17// yaml-cpp is optional - only include if available
18#ifdef YAZE_HAS_YAML_CPP
19#include "yaml-cpp/yaml.h"
20#endif
21
22namespace yaze {
23namespace cli {
24
25namespace {
26
27#ifdef YAZE_HAS_YAML_CPP
28bool IsYamlBool(const std::string& value) {
29 const std::string lower = absl::AsciiStrToLower(value);
30 return lower == "true" || lower == "false" || lower == "yes" ||
31 lower == "no" || lower == "on" || lower == "off";
32}
33
34nlohmann::json YamlToJson(const YAML::Node& node) {
35 if (!node) {
36 return nlohmann::json();
37 }
38
39 switch (node.Type()) {
40 case YAML::NodeType::Scalar: {
41 const std::string scalar = node.as<std::string>("");
42
43 if (IsYamlBool(scalar)) {
44 return node.as<bool>();
45 }
46
47 if (scalar == "~" || absl::AsciiStrToLower(scalar) == "null") {
48 return nlohmann::json();
49 }
50
51 return scalar;
52 }
53 case YAML::NodeType::Sequence: {
54 nlohmann::json array = nlohmann::json::array();
55 for (const auto& item : node) {
56 array.push_back(YamlToJson(item));
57 }
58 return array;
59 }
60 case YAML::NodeType::Map: {
61 nlohmann::json object = nlohmann::json::object();
62 for (const auto& kv : node) {
63 object[kv.first.as<std::string>()] = YamlToJson(kv.second);
64 }
65 return object;
66 }
67 default:
68 return nlohmann::json();
69 }
70}
71#endif // YAZE_HAS_YAML_CPP
72
73} // namespace
74
76
78 command_docs_.clear();
79 examples_.clear();
80 tool_specs_.clear();
81 tile_reference_.clear();
82 catalogue_loaded_ = false;
83}
84
85absl::StatusOr<std::string> PromptBuilder::ResolveCataloguePath(
86 const std::string& yaml_path) const {
87 // If an explicit path is provided, check it first
88 if (!yaml_path.empty()) {
89 std::error_code ec;
90 if (std::filesystem::exists(yaml_path, ec) && !ec) {
91 return yaml_path;
92 }
93 }
94
95 // Check environment variable override
96 if (const char* env_path = std::getenv("YAZE_AGENT_CATALOGUE")) {
97 if (*env_path != '\0') {
98 std::error_code ec;
99 if (std::filesystem::exists(env_path, ec) && !ec) {
100 return std::string(env_path);
101 }
102 }
103 }
104
105 // Use PlatformPaths to find the asset in standard locations
106 // Try the requested path (default is prompt_catalogue.yaml)
107 std::string relative_path =
108 yaml_path.empty() ? "agent/prompt_catalogue.yaml" : yaml_path;
109
110 auto result = util::PlatformPaths::FindAsset(relative_path);
111 if (result.ok()) {
112 return result->string();
113 }
114
115 return result.status();
116}
117
119 const std::string& yaml_path) {
120#if !defined(YAZE_WITH_JSON) || !defined(YAZE_HAS_YAML_CPP)
121 // Gracefully degrade if JSON or yaml-cpp support not available
122 (void)yaml_path; // Suppress unused parameter warning
123 std::cerr
124 << "⚠️ PromptBuilder requires JSON and yaml-cpp support for catalogue "
125 "loading\n"
126 << " Build with -DYAZE_WITH_JSON=ON (mac-ai preset already does) and "
127 "install yaml-cpp\n"
128 << " AI features will use basic prompts without tool definitions\n";
129 return absl::OkStatus(); // Don't fail, just skip catalogue loading
130#else
131 auto resolved_or = ResolveCataloguePath(yaml_path);
132 if (!resolved_or.ok()) {
134 return resolved_or.status();
135 }
136
137 const std::string& resolved_path = resolved_or.value();
138
139 YAML::Node root;
140 try {
141 root = YAML::LoadFile(resolved_path);
142 } catch (const YAML::BadFile& e) {
144 return absl::NotFoundError(absl::StrCat(
145 "Unable to open prompt catalogue at ", resolved_path, ": ", e.what()));
146 } catch (const YAML::ParserException& e) {
148 return absl::InvalidArgumentError(absl::StrCat(
149 "Failed to parse prompt catalogue at ", resolved_path, ": ", e.what()));
150 }
151
152 nlohmann::json catalogue = YamlToJson(root);
154
155 if (catalogue.contains("commands")) {
156 if (auto status = ParseCommands(catalogue["commands"]); !status.ok()) {
157 return status;
158 }
159 }
160
161 if (catalogue.contains("tools")) {
162 if (auto status = ParseTools(catalogue["tools"]); !status.ok()) {
163 return status;
164 }
165 }
166
167 if (catalogue.contains("examples")) {
168 if (auto status = ParseExamples(catalogue["examples"]); !status.ok()) {
169 return status;
170 }
171 }
172
173 if (catalogue.contains("tile16_reference")) {
174 ParseTileReference(catalogue["tile16_reference"]);
175 }
176
177 catalogue_loaded_ = true;
178 return absl::OkStatus();
179#endif // YAZE_WITH_JSON
180}
181
182absl::Status PromptBuilder::ParseCommands(const nlohmann::json& commands) {
183 if (!commands.is_object()) {
184 return absl::InvalidArgumentError(
185 "commands section must be an object mapping command names to docs");
186 }
187
188 for (const auto& [name, value] : commands.items()) {
189 if (!value.is_string()) {
190 return absl::InvalidArgumentError(
191 absl::StrCat("Command entry for ", name, " must be a string"));
192 }
193 command_docs_[name] = value.get<std::string>();
194 }
195
196 return absl::OkStatus();
197}
198
199absl::Status PromptBuilder::ParseTools(const nlohmann::json& tools) {
200 if (!tools.is_array()) {
201 return absl::InvalidArgumentError("tools section must be an array");
202 }
203
204 for (const auto& tool_json : tools) {
205 if (!tool_json.is_object()) {
206 return absl::InvalidArgumentError(
207 "Each tool entry must be an object with name/description");
208 }
209
211 if (tool_json.contains("name") && tool_json["name"].is_string()) {
212 spec.name = tool_json["name"].get<std::string>();
213 } else {
214 return absl::InvalidArgumentError("Tool entry missing name");
215 }
216
217 if (tool_json.contains("description") &&
218 tool_json["description"].is_string()) {
219 spec.description = tool_json["description"].get<std::string>();
220 }
221
222 if (tool_json.contains("usage_notes") &&
223 tool_json["usage_notes"].is_string()) {
224 spec.usage_notes = tool_json["usage_notes"].get<std::string>();
225 }
226
227 if (tool_json.contains("arguments")) {
228 const auto& args = tool_json["arguments"];
229 if (!args.is_array()) {
230 return absl::InvalidArgumentError(absl::StrCat(
231 "Tool arguments for ", spec.name, " must be an array"));
232 }
233 for (const auto& arg_json : args) {
234 if (!arg_json.is_object()) {
235 return absl::InvalidArgumentError(absl::StrCat(
236 "Argument entries for ", spec.name, " must be objects"));
237 }
238 ToolArgument arg;
239 if (arg_json.contains("name") && arg_json["name"].is_string()) {
240 arg.name = arg_json["name"].get<std::string>();
241 } else {
242 return absl::InvalidArgumentError(absl::StrCat(
243 "Argument entry for ", spec.name, " is missing a name"));
244 }
245 if (arg_json.contains("description") &&
246 arg_json["description"].is_string()) {
247 arg.description = arg_json["description"].get<std::string>();
248 }
249 if (arg_json.contains("required")) {
250 if (!arg_json["required"].is_boolean()) {
251 return absl::InvalidArgumentError(
252 absl::StrCat("Argument 'required' flag for ", spec.name,
253 "::", arg.name, " must be boolean"));
254 }
255 arg.required = arg_json["required"].get<bool>();
256 }
257 if (arg_json.contains("example") && arg_json["example"].is_string()) {
258 arg.example = arg_json["example"].get<std::string>();
259 }
260 spec.arguments.push_back(std::move(arg));
261 }
262 }
263
264 tool_specs_.push_back(std::move(spec));
265 }
266
267 return absl::OkStatus();
268}
269
270absl::Status PromptBuilder::ParseExamples(const nlohmann::json& examples) {
271 if (!examples.is_array()) {
272 return absl::InvalidArgumentError("examples section must be an array");
273 }
274
275 for (const auto& example_json : examples) {
276 if (!example_json.is_object()) {
277 return absl::InvalidArgumentError("Each example entry must be an object");
278 }
279
280 FewShotExample example;
281 if (example_json.contains("user_prompt") &&
282 example_json["user_prompt"].is_string()) {
283 example.user_prompt = example_json["user_prompt"].get<std::string>();
284 } else {
285 return absl::InvalidArgumentError("Example missing user_prompt");
286 }
287
288 if (example_json.contains("text_response") &&
289 example_json["text_response"].is_string()) {
290 example.text_response = example_json["text_response"].get<std::string>();
291 }
292
293 if (example_json.contains("reasoning") &&
294 example_json["reasoning"].is_string()) {
295 example.explanation = example_json["reasoning"].get<std::string>();
296 }
297
298 if (example_json.contains("commands")) {
299 const auto& commands = example_json["commands"];
300 if (!commands.is_array()) {
301 return absl::InvalidArgumentError(absl::StrCat(
302 "Example commands for ", example.user_prompt, " must be an array"));
303 }
304 for (const auto& cmd : commands) {
305 if (!cmd.is_string()) {
306 return absl::InvalidArgumentError(absl::StrCat(
307 "Command entries for ", example.user_prompt, " must be strings"));
308 }
309 example.expected_commands.push_back(cmd.get<std::string>());
310 }
311 }
312
313 if (example_json.contains("tool_calls")) {
314 const auto& calls = example_json["tool_calls"];
315 if (!calls.is_array()) {
316 return absl::InvalidArgumentError(absl::StrCat(
317 "Tool calls for ", example.user_prompt, " must be an array"));
318 }
319 for (const auto& call_json : calls) {
320 if (!call_json.is_object()) {
321 return absl::InvalidArgumentError(
322 absl::StrCat("Tool call entries for ", example.user_prompt,
323 " must be objects"));
324 }
325 ToolCall call;
326 if (call_json.contains("tool_name") &&
327 call_json["tool_name"].is_string()) {
328 call.tool_name = call_json["tool_name"].get<std::string>();
329 } else {
330 return absl::InvalidArgumentError(absl::StrCat(
331 "Tool call missing tool_name in example: ", example.user_prompt));
332 }
333 if (call_json.contains("args")) {
334 const auto& args = call_json["args"];
335 if (!args.is_object()) {
336 return absl::InvalidArgumentError(
337 absl::StrCat("Tool call args for ", example.user_prompt,
338 " must be an object"));
339 }
340 for (const auto& [key, value] : args.items()) {
341 if (!value.is_string()) {
342 return absl::InvalidArgumentError(
343 absl::StrCat("Tool call arg value for ", example.user_prompt,
344 " must be a string"));
345 }
346 call.args[key] = value.get<std::string>();
347 }
348 }
349 example.tool_calls.push_back(std::move(call));
350 }
351 }
352
353 example.explanation =
354 example_json.value("explanation", example.explanation);
355 examples_.push_back(std::move(example));
356 }
357
358 return absl::OkStatus();
359}
360
361void PromptBuilder::ParseTileReference(const nlohmann::json& tile_reference) {
362 if (!tile_reference.is_object()) {
363 return;
364 }
365
366 for (const auto& [alias, value] : tile_reference.items()) {
367 if (value.is_string()) {
368 tile_reference_[alias] = value.get<std::string>();
369 }
370 }
371}
372
373std::string PromptBuilder::LookupTileId(const std::string& alias) const {
374 auto it = tile_reference_.find(alias);
375 if (it != tile_reference_.end()) {
376 return it->second;
377 }
378 return "";
379}
380
382 std::ostringstream oss;
383
384 oss << "# Available z3ed Commands\n\n";
385
386 for (const auto& [cmd, docs] : command_docs_) {
387 oss << "## " << cmd << "\n";
388 oss << docs << "\n\n";
389 }
390
391 return oss.str();
392}
393
395 if (tool_specs_.empty()) {
396 return "";
397 }
398
399 std::ostringstream oss;
400 oss << "# Available Agent Tools\n\n";
401
402 for (const auto& spec : tool_specs_) {
403 oss << "## " << spec.name << "\n";
404 if (!spec.description.empty()) {
405 oss << spec.description << "\n\n";
406 }
407
408 if (!spec.arguments.empty()) {
409 oss << "| Argument | Required | Description | Example |\n";
410 oss << "| --- | --- | --- | --- |\n";
411 for (const auto& arg : spec.arguments) {
412 oss << "| `" << arg.name << "` | " << (arg.required ? "yes" : "no")
413 << " | " << arg.description << " | "
414 << (arg.example.empty() ? "" : "`" + arg.example + "`") << " |\n";
415 }
416 oss << "\n";
417 }
418
419 if (!spec.usage_notes.empty()) {
420 oss << "_Usage:_ " << spec.usage_notes << "\n\n";
421 }
422 }
423
424 return oss.str();
425}
426
428 if (tool_specs_.empty()) {
429 return "[]";
430 }
431
433}
434
436 std::ostringstream oss;
437
438 oss << "# Example Command Sequences\n\n";
439 oss << "Here are proven examples of how to accomplish common tasks:\n\n";
440
441 for (const auto& example : examples_) {
442 oss << "**User Request:** \"" << example.user_prompt << "\"\n";
443 oss << "**Structured Response:**\n";
444
445 nlohmann::json example_json = nlohmann::json::object();
446 if (!example.text_response.empty()) {
447 example_json["text_response"] = example.text_response;
448 }
449 if (!example.expected_commands.empty()) {
450 example_json["commands"] = example.expected_commands;
451 }
452 if (!example.explanation.empty()) {
453 example_json["reasoning"] = example.explanation;
454 }
455 if (!example.tool_calls.empty()) {
456 nlohmann::json calls = nlohmann::json::array();
457 for (const auto& call : example.tool_calls) {
458 nlohmann::json call_json;
459 call_json["tool_name"] = call.tool_name;
460 nlohmann::json args = nlohmann::json::object();
461 for (const auto& [key, value] : call.args) {
462 args[key] = value;
463 }
464 call_json["args"] = std::move(args);
465 calls.push_back(std::move(call_json));
466 }
467 example_json["tool_calls"] = std::move(calls);
468 }
469
470 oss << "```json\n" << example_json.dump(2) << "\n```\n\n";
471 }
472
473 return oss.str();
474}
475
477 // Try to load from file first using FindAsset
478 auto file_path =
479 util::PlatformPaths::FindAsset("agent/tool_calling_instructions.txt");
480 if (file_path.ok()) {
481 std::ifstream file(file_path->string());
482 if (file.is_open()) {
483 std::string content((std::istreambuf_iterator<char>(file)),
484 std::istreambuf_iterator<char>());
485 if (!content.empty()) {
486 std::ostringstream oss;
487 oss << content;
488
489 // Add tool schemas if available
490 if (!tool_specs_.empty()) {
491 oss << "\n\n# Available Tools for ROM Inspection\n\n";
492 oss << "You have access to the following tools to answer "
493 "questions:\n\n";
494 oss << "```json\n";
496 oss << "\n```\n\n";
497 oss << "**Tool Call Example (Initial Request):**\n";
498 oss << "```json\n";
499 oss << R"({
500 "tool_calls": [
501 {
502 "tool_name": "resource-list",
503 "args": {
504 "type": "dungeon"
505 }
506 }
507 ],
508 "reasoning": "I need to call the resource-list tool to get the dungeon information."
509})";
510 oss << "\n```\n\n";
511 oss << "**Tool Result Response (After Tool Executes):**\n";
512 oss << "```json\n";
513 oss << R"({
514 "text_response": "I found the following dungeons in the ROM: Hyrule Castle, Eastern Palace, Desert Palace, Tower of Hera, Palace of Darkness, Swamp Palace, Skull Woods, Thieves' Town, Ice Palace, Misery Mire, Turtle Rock, and Ganon's Tower.",
515 "reasoning": "The tool returned a list of 12 dungeons which I've formatted into a readable response."
516})";
517 oss << "\n```\n";
518 }
519
520 if (!tile_reference_.empty()) {
521 oss << "\n" << BuildTileReferenceSection();
522 }
523
524 return oss.str();
525 }
526 }
527 }
528
529 // Fallback to embedded version if file not found
530 std::ostringstream oss;
531 oss << R"(
532# Critical Constraints
533
5341. **Output Format:** You MUST respond with ONLY a JSON object with the following structure:
535 {
536 "text_response": "Your natural language reply to the user.",
537 "tool_calls": [{ "tool_name": "tool_name", "args": { "arg1": "value1" } }],
538 "commands": ["command1", "command2"],
539 "reasoning": "Your thought process."
540 }
541 - `text_response` is for conversational replies.
542 - `tool_calls` is for asking questions about the ROM. Use the available tools listed below.
543 - `commands` is for generating commands to modify the ROM.
544 - All fields are optional, but you should always provide at least one.
545
5462. **Tool Calling Workflow (CRITICAL):**
547 WHEN YOU CALL A TOOL:
548 a) First response: Include tool_calls with the tool name and arguments
549 b) The tool will execute and you'll receive results in the next message
550 c) Second response: You MUST provide a text_response that answers the user's question using the tool results
551 d) DO NOT call the same tool again unless you need different parameters
552 e) DO NOT leave text_response empty after receiving tool results
553
554 Example conversation flow:
555 User: "What dungeons are in this ROM?"
556 You (first): {"tool_calls": [{"tool_name": "resource-list", "args": {"type": "dungeon"}}]}
557 [Tool executes and returns: {"dungeons": ["Hyrule Castle", "Eastern Palace", ...]}]
558 You (second): {"text_response": "Based on the ROM data, there are 12 dungeons including Hyrule Castle, Eastern Palace, Desert Palace, Tower of Hera, and more."}
559
5603. **Tool Usage:** When the user asks a question about the ROM state, use tool_calls instead of commands
561 - Tools are read-only and return information
562 - Commands modify the ROM and should only be used when explicitly requested
563 - You can call multiple tools in one response
564 - Always use JSON format for tool results
565 - ALWAYS provide text_response after receiving tool results
566
5674. **Command Syntax:** Follow the exact syntax shown in examples
568 - Use correct flag names (--group, --id, --to, --from, etc.)
569 - Use hex format for colors (0xRRGGBB) and tile IDs (0xNNN)
570 - Coordinates are 0-based indices
571
5725. **Common Patterns:**
573 - Palette modifications: export → set-color → import
574 - Multiple tile placement: multiple overworld set-tile commands
575 - Validation: single rom validate command
576
5776. **Error Prevention:**
578 - Always export before modifying palettes
579 - Use temporary file names (temp_*.json) for intermediate files
580 - Validate coordinates are within bounds
581)";
582
583 if (!tool_specs_.empty()) {
584 oss << "\n# Available Tools for ROM Inspection\n\n";
585 oss << "You have access to the following tools to answer questions:\n\n";
586 oss << "```json\n";
588 oss << "\n```\n\n";
589 oss << "**Tool Call Example (Initial Request):**\n";
590 oss << "```json\n";
591 oss << R"({
592 "tool_calls": [
593 {
594 "tool_name": "resource-list",
595 "args": {
596 "type": "dungeon"
597 }
598 }
599 ],
600 "reasoning": "I need to call the resource-list tool to get the dungeon information."
601})";
602 oss << "\n```\n\n";
603 oss << "**Tool Result Response (After Tool Executes):**\n";
604 oss << "```json\n";
605 oss << R"({
606 "text_response": "I found the following dungeons in the ROM: Hyrule Castle, Eastern Palace, Desert Palace, Tower of Hera, Palace of Darkness, Swamp Palace, Skull Woods, Thieves' Town, Ice Palace, Misery Mire, Turtle Rock, and Ganon's Tower.",
607 "reasoning": "The tool returned a list of 12 dungeons which I've formatted into a readable response."
608})";
609 oss << "\n```\n";
610 }
611
612 if (!tile_reference_.empty()) {
613 oss << "\n" << BuildTileReferenceSection();
614 }
615
616 return oss.str();
617}
618
620 std::ostringstream oss;
621 oss << "# Tile16 Reference (ALTTP)\n\n";
622
623 for (const auto& [alias, value] : tile_reference_) {
624 oss << "- " << alias << ": " << value << "\n";
625 }
626
627 oss << "\n";
628 return oss.str();
629}
630
631std::string PromptBuilder::BuildContextSection(const RomContext& context) {
632 std::ostringstream oss;
633
634 oss << "# Current ROM Context\n\n";
635
636 // Use ResourceContextBuilder if a ROM is available
637 if (rom_ && rom_->is_loaded()) {
640 std::make_unique<ResourceContextBuilder>(rom_);
641 }
642 auto resource_context_or =
643 resource_context_builder_->BuildResourceContext();
644 if (resource_context_or.ok()) {
645 oss << resource_context_or.value();
646 }
647 }
648
649 if (context.rom_loaded) {
650 oss << "- **ROM Loaded:** Yes (" << context.rom_path << ")\n";
651 } else {
652 oss << "- **ROM Loaded:** No\n";
653 }
654
655 if (!context.current_editor.empty()) {
656 oss << "- **Active Editor:** " << context.current_editor << "\n";
657 }
658
659 if (!context.editor_state.empty()) {
660 oss << "- **Editor State:**\n";
661 for (const auto& [key, value] : context.editor_state) {
662 oss << " - " << key << ": " << value << "\n";
663 }
664 }
665
666 oss << "\n";
667 return oss.str();
668}
669
671 // Try to load from file first using FindAsset
672 auto file_path = util::PlatformPaths::FindAsset("agent/system_prompt.txt");
673 if (file_path.ok()) {
674 std::ifstream file(file_path->string());
675 if (file.is_open()) {
676 std::string content((std::istreambuf_iterator<char>(file)),
677 std::istreambuf_iterator<char>());
678 if (!content.empty()) {
679 std::ostringstream oss;
680 oss << content;
681
682 // Add command reference if available
683 if (catalogue_loaded_ && !command_docs_.empty()) {
684 oss << "\n\n" << BuildCommandReference();
685 }
686
687 // Add tool reference if available
688 if (!tool_specs_.empty()) {
689 oss << "\n\n" << BuildToolReference();
690 }
691
692 return oss.str();
694 }
695 }
696
697 // Fallback to embedded version if file not found
698 std::ostringstream oss;
699
700 oss << "You are an expert ROM hacking assistant for The Legend of Zelda: "
701 << "A Link to the Past (ALTTP).\n\n";
702
703 oss << "Your task is to generate a sequence of z3ed CLI commands to achieve "
704 << "the user's request.\n\n";
705
706 if (catalogue_loaded_) {
707 if (!command_docs_.empty()) {
708 oss << BuildCommandReference();
709 }
710 if (!tool_specs_.empty()) {
711 oss << BuildToolReference();
712 }
713 }
714
716
717 oss << "\n**Response Format:**\n";
718 oss << "```json\n";
719 oss << "[\"command1 --flag value\", \"command2 --flag value\"]\n";
720 oss << "```\n";
721
722 return oss.str();
723}
724
726 std::ostringstream oss;
727
728 oss << BuildSystemInstruction();
729 oss << "\n---\n\n";
731
732 return oss.str();
733}
734
735std::string PromptBuilder::BuildContextualPrompt(const std::string& user_prompt,
736 const RomContext& context) {
737 std::ostringstream oss;
738
739 if (context.rom_loaded || !context.current_editor.empty()) {
740 oss << BuildContextSection(context);
741 oss << "---\n\n";
742 }
743
744 oss << "**User Request:** " << user_prompt << "\n\n";
745 oss << "Generate the appropriate z3ed commands as a JSON array.";
746
747 return oss.str();
748}
749
751 const std::vector<agent::ChatMessage>& history) {
752 std::ostringstream oss;
753 oss << "This is a conversation between a user and an expert ROM hacking "
754 "assistant.\n\n";
755
756 for (const auto& msg : history) {
757 if (msg.sender == agent::ChatMessage::Sender::kUser) {
758 oss << "User: " << msg.message << "\n";
759 } else {
760 oss << "Agent: " << msg.message << "\n";
761 }
762 }
763 oss << "\nBased on this conversation, provide a response in the required "
764 "JSON "
765 "format.";
766 return oss.str();
767}
768
769void PromptBuilder::AddFewShotExample(const FewShotExample& example) {
770 examples_.push_back(example);
771}
772
773std::vector<FewShotExample> PromptBuilder::GetExamplesForCategory(
774 const std::string& category) {
775 std::vector<FewShotExample> result;
776
777 for (const auto& example : examples_) {
778 // Simple category matching based on keywords
779 if (category == "palette" &&
780 (example.user_prompt.find("palette") != std::string::npos ||
781 example.user_prompt.find("color") != std::string::npos)) {
782 result.push_back(example);
783 } else if (category == "overworld" &&
784 (example.user_prompt.find("place") != std::string::npos ||
785 example.user_prompt.find("tree") != std::string::npos ||
786 example.user_prompt.find("house") != std::string::npos)) {
787 result.push_back(example);
788 } else if (category == "validation" &&
789 example.user_prompt.find("validate") != std::string::npos) {
790 result.push_back(example);
791 }
792 }
793
794 return result;
795}
796
797} // namespace cli
798} // namespace yaze
bool is_loaded() const
Definition rom.h:132
std::string BuildContextualPrompt(const std::string &user_prompt, const RomContext &context)
std::vector< FewShotExample > examples_
std::map< std::string, std::string > command_docs_
absl::Status ParseTools(const nlohmann::json &tools)
std::map< std::string, std::string > tile_reference_
std::unique_ptr< ResourceContextBuilder > resource_context_builder_
const std::map< std::string, std::string > & tile_reference() const
std::string BuildConstraintsSection() const
std::string BuildTileReferenceSection() const
void AddFewShotExample(const FewShotExample &example)
std::string BuildFunctionCallSchemas() const
std::string BuildSystemInstructionWithExamples()
std::string BuildPromptFromHistory(const std::vector< agent::ChatMessage > &history)
std::string BuildToolReference() const
std::string BuildContextSection(const RomContext &context)
void ParseTileReference(const nlohmann::json &tile_reference)
std::string BuildSystemInstruction()
std::string LookupTileId(const std::string &alias) const
absl::Status ParseExamples(const nlohmann::json &examples)
std::string BuildFewShotExamplesSection() const
std::vector< ToolSpecification > tool_specs_
std::string BuildCommandReference() const
absl::StatusOr< std::string > ResolveCataloguePath(const std::string &yaml_path) const
absl::Status LoadResourceCatalogue(const std::string &yaml_path)
absl::Status ParseCommands(const nlohmann::json &commands)
std::vector< FewShotExample > GetExamplesForCategory(const std::string &category)
static nlohmann::json BuildFunctionDeclarations(const std::vector< ToolSpecification > &tool_specs)
static absl::StatusOr< std::filesystem::path > FindAsset(const std::string &relative_path)
Find an asset file in multiple standard locations.
std::vector< ToolCall > tool_calls
std::vector< std::string > expected_commands
std::map< std::string, std::string > args
Definition common.h:14
std::string tool_name
Definition common.h:13
std::vector< ToolArgument > arguments