155 lines
4.7 KiB
C++
155 lines
4.7 KiB
C++
#include "cli/service/ai/ai_service.h"
|
|
#include "cli/service/agent/conversational_agent_service.h"
|
|
|
|
#include <algorithm>
|
|
#include <cctype>
|
|
|
|
#include "absl/strings/ascii.h"
|
|
#include "absl/strings/match.h"
|
|
#include "absl/strings/numbers.h"
|
|
#include "absl/strings/str_format.h"
|
|
|
|
namespace yaze {
|
|
namespace cli {
|
|
|
|
namespace {
|
|
|
|
std::string ExtractRoomId(const std::string& normalized_prompt) {
|
|
size_t hex_pos = normalized_prompt.find("0x");
|
|
if (hex_pos != std::string::npos) {
|
|
std::string hex_value;
|
|
for (size_t i = hex_pos; i < normalized_prompt.size(); ++i) {
|
|
char c = normalized_prompt[i];
|
|
if (std::isxdigit(static_cast<unsigned char>(c)) || c == 'x') {
|
|
hex_value.push_back(c);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (hex_value.size() > 2) {
|
|
return hex_value;
|
|
}
|
|
}
|
|
|
|
// Fallback: look for decimal digits, then convert to hex string.
|
|
std::string digits;
|
|
for (char c : normalized_prompt) {
|
|
if (std::isdigit(static_cast<unsigned char>(c))) {
|
|
digits.push_back(c);
|
|
} else if (!digits.empty()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!digits.empty()) {
|
|
int value = 0;
|
|
if (absl::SimpleAtoi(digits, &value)) {
|
|
return absl::StrFormat("0x%03X", value);
|
|
}
|
|
}
|
|
|
|
return "0x000";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
absl::StatusOr<AgentResponse> MockAIService::GenerateResponse(
|
|
const std::string& prompt) {
|
|
AgentResponse response;
|
|
const std::string normalized = absl::AsciiStrToLower(prompt);
|
|
|
|
if (normalized.empty()) {
|
|
response.text_response =
|
|
"Let's start with a prompt about the overworld or dungeons.";
|
|
return response;
|
|
}
|
|
|
|
if (absl::StrContains(normalized, "place") &&
|
|
absl::StrContains(normalized, "tree")) {
|
|
response.text_response =
|
|
"Sure, I can do that. Here's the command to place a tree.";
|
|
response.commands.push_back("overworld set-tile --map 0 --x 10 --y 20 --tile 0x02E");
|
|
response.reasoning =
|
|
"The user asked to place a tree tile16, so I generated the matching set-tile command.";
|
|
return response;
|
|
}
|
|
|
|
if (absl::StrContains(normalized, "list") &&
|
|
absl::StrContains(normalized, "resource")) {
|
|
std::string resource_type = "dungeon";
|
|
if (absl::StrContains(normalized, "overworld")) {
|
|
resource_type = "overworld";
|
|
} else if (absl::StrContains(normalized, "sprite")) {
|
|
resource_type = "sprite";
|
|
} else if (absl::StrContains(normalized, "palette")) {
|
|
resource_type = "palette";
|
|
}
|
|
|
|
ToolCall call;
|
|
call.tool_name = "resource-list";
|
|
call.args.emplace("type", resource_type);
|
|
response.text_response =
|
|
absl::StrFormat("Fetching %s labels from the ROM...", resource_type);
|
|
response.reasoning =
|
|
"Using the resource-list tool keeps the LLM in sync with project labels.";
|
|
response.tool_calls.push_back(call);
|
|
return response;
|
|
}
|
|
|
|
if (absl::StrContains(normalized, "sprite") &&
|
|
absl::StrContains(normalized, "room")) {
|
|
ToolCall call;
|
|
call.tool_name = "dungeon-list-sprites";
|
|
call.args.emplace("room", ExtractRoomId(normalized));
|
|
response.text_response =
|
|
"Let me inspect the dungeon room sprites for you.";
|
|
response.reasoning =
|
|
"Calling the sprite inspection tool provides precise coordinates for the agent.";
|
|
response.tool_calls.push_back(call);
|
|
return response;
|
|
}
|
|
|
|
response.text_response =
|
|
"I'm just a mock service. Please load a provider like ollama or gemini.";
|
|
return response;
|
|
}
|
|
|
|
absl::StatusOr<AgentResponse> MockAIService::GenerateResponse(
|
|
const std::vector<agent::ChatMessage>& history) {
|
|
if (history.empty()) {
|
|
return absl::InvalidArgumentError("History cannot be empty.");
|
|
}
|
|
|
|
// If the last message in history is a tool output, synthesize a summary.
|
|
for (auto it = history.rbegin(); it != history.rend(); ++it) {
|
|
if (it->sender == agent::ChatMessage::Sender::kAgent &&
|
|
(absl::StrContains(it->message, "=== ") ||
|
|
absl::StrContains(it->message, "\"id\"") ||
|
|
absl::StrContains(it->message, "\n{"))) {
|
|
AgentResponse response;
|
|
response.text_response =
|
|
"Here's what I found:\n" + it->message +
|
|
"\nLet me know if you'd like to make a change.";
|
|
response.reasoning =
|
|
"Summarized the latest tool output for the user.";
|
|
return response;
|
|
}
|
|
}
|
|
|
|
auto user_it = std::find_if(history.rbegin(), history.rend(),
|
|
[](const agent::ChatMessage& message) {
|
|
return message.sender ==
|
|
agent::ChatMessage::Sender::kUser;
|
|
});
|
|
if (user_it == history.rend()) {
|
|
return absl::InvalidArgumentError(
|
|
"History does not contain a user message.");
|
|
}
|
|
|
|
return GenerateResponse(user_it->message);
|
|
}
|
|
|
|
} // namespace cli
|
|
} // namespace yaze
|
|
|