feat(asset): implement asset path resolution and streamline file loading
- Introduced a new utility function `FindAsset` in `PlatformPaths` to locate asset files across multiple standard directories, enhancing flexibility in asset management. - Updated various components to utilize `FindAsset` for loading configuration and prompt files, replacing hardcoded search paths with a more robust solution. - Improved error handling and logging for asset loading failures, ensuring clearer feedback during runtime. - Refactored existing code in `gemini_ai_service`, `prompt_builder`, and `platform_paths` to leverage the new asset resolution mechanism, promoting code consistency and maintainability.
This commit is contained in:
@@ -11,13 +11,13 @@
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_split.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "util/platform_paths.h"
|
||||
|
||||
#ifdef YAZE_WITH_JSON
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include "httplib.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// OpenSSL initialization for HTTPS support
|
||||
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
|
||||
@@ -76,34 +76,29 @@ GeminiAIService::GeminiAIService(const GeminiConfig& config)
|
||||
std::cerr << "[DEBUG] Building system instruction..." << std::endl;
|
||||
}
|
||||
|
||||
// Try to load version-specific system prompt file
|
||||
// Try to load version-specific system prompt file using FindAsset
|
||||
std::string prompt_file;
|
||||
if (config_.prompt_version == "v3") {
|
||||
prompt_file = "assets/agent/system_prompt_v3.txt";
|
||||
prompt_file = "agent/system_prompt_v3.txt";
|
||||
} else if (config_.prompt_version == "v2") {
|
||||
prompt_file = "assets/agent/system_prompt_v2.txt";
|
||||
prompt_file = "agent/system_prompt_v2.txt";
|
||||
} else {
|
||||
prompt_file = "assets/agent/system_prompt.txt";
|
||||
prompt_file = "agent/system_prompt.txt";
|
||||
}
|
||||
|
||||
std::vector<std::string> search_paths = {
|
||||
prompt_file,
|
||||
"../" + prompt_file,
|
||||
"../../" + prompt_file
|
||||
};
|
||||
|
||||
auto prompt_path = util::PlatformPaths::FindAsset(prompt_file);
|
||||
bool loaded = false;
|
||||
for (const auto& path : search_paths) {
|
||||
std::ifstream file(path);
|
||||
|
||||
if (prompt_path.ok()) {
|
||||
std::ifstream file(prompt_path->string());
|
||||
if (file.good()) {
|
||||
std::stringstream buffer;
|
||||
buffer << file.rdbuf();
|
||||
config_.system_instruction = buffer.str();
|
||||
if (config_.verbose) {
|
||||
std::cerr << "[DEBUG] Loaded prompt: " << path << std::endl;
|
||||
std::cerr << "[DEBUG] Loaded prompt: " << prompt_path->string() << std::endl;
|
||||
}
|
||||
loaded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,38 +143,22 @@ std::string GeminiAIService::BuildFunctionCallSchemas() {
|
||||
return schemas;
|
||||
}
|
||||
|
||||
// Fallback: Search for function_schemas.json
|
||||
const std::vector<std::string> search_paths = {
|
||||
"assets/agent/function_schemas.json",
|
||||
"../assets/agent/function_schemas.json",
|
||||
"../../assets/agent/function_schemas.json",
|
||||
};
|
||||
// Fallback: Search for function_schemas.json using FindAsset
|
||||
auto schema_path_or = util::PlatformPaths::FindAsset("agent/function_schemas.json");
|
||||
|
||||
fs::path schema_path;
|
||||
bool found = false;
|
||||
|
||||
for (const auto& candidate : search_paths) {
|
||||
fs::path resolved = fs::absolute(candidate);
|
||||
if (fs::exists(resolved)) {
|
||||
schema_path = resolved;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
std::cerr << "⚠️ Function schemas file not found. Tried paths:" << std::endl;
|
||||
for (const auto& path : search_paths) {
|
||||
std::cerr << " - " << fs::absolute(path).string() << std::endl;
|
||||
if (!schema_path_or.ok()) {
|
||||
if (config_.verbose) {
|
||||
std::cerr << "⚠️ Function schemas file not found: "
|
||||
<< schema_path_or.status().message() << std::endl;
|
||||
}
|
||||
return "[]"; // Return empty array as fallback
|
||||
}
|
||||
|
||||
// Load and parse the JSON file
|
||||
std::ifstream file(schema_path);
|
||||
std::ifstream file(schema_path_or->string());
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "⚠️ Failed to open function schemas file: "
|
||||
<< schema_path.string() << std::endl;
|
||||
<< schema_path_or->string() << std::endl;
|
||||
return "[]";
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "util/platform_paths.h"
|
||||
#include "yaml-cpp/yaml.h"
|
||||
|
||||
namespace yaze {
|
||||
@@ -18,8 +19,6 @@ namespace cli {
|
||||
|
||||
namespace {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool IsYamlBool(const std::string& value) {
|
||||
const std::string lower = absl::AsciiStrToLower(value);
|
||||
return lower == "true" || lower == "false" || lower == "yes" ||
|
||||
@@ -64,44 +63,6 @@ nlohmann::json YamlToJson(const YAML::Node& node) {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<fs::path> BuildCatalogueSearchPaths(const std::string& explicit_path) {
|
||||
std::vector<fs::path> paths;
|
||||
if (!explicit_path.empty()) {
|
||||
paths.emplace_back(explicit_path);
|
||||
}
|
||||
|
||||
if (const char* env_path = std::getenv("YAZE_AGENT_CATALOGUE")) {
|
||||
if (*env_path != '\0') {
|
||||
paths.emplace_back(env_path);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to get executable directory for better path resolution
|
||||
fs::path exe_dir;
|
||||
try {
|
||||
exe_dir = fs::current_path();
|
||||
} catch (...) {
|
||||
exe_dir = ".";
|
||||
}
|
||||
|
||||
const std::vector<std::string> defaults = {
|
||||
"assets/agent/prompt_catalogue.yaml",
|
||||
"../assets/agent/prompt_catalogue.yaml",
|
||||
"../../assets/agent/prompt_catalogue.yaml",
|
||||
"../../../assets/agent/prompt_catalogue.yaml", // From build/bin/
|
||||
"../../../../assets/agent/prompt_catalogue.yaml", // From build/bin/yaze.app/Contents/MacOS/
|
||||
"../Resources/assets/agent/prompt_catalogue.yaml", // macOS app bundle
|
||||
"assets/z3ed/prompt_catalogue.yaml",
|
||||
"../assets/z3ed/prompt_catalogue.yaml",
|
||||
};
|
||||
|
||||
for (const auto& candidate : defaults) {
|
||||
paths.emplace_back(candidate);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PromptBuilder::PromptBuilder() = default;
|
||||
@@ -116,35 +77,35 @@ void PromptBuilder::ClearCatalogData() {
|
||||
|
||||
absl::StatusOr<std::string> PromptBuilder::ResolveCataloguePath(
|
||||
const std::string& yaml_path) const {
|
||||
const auto search_paths = BuildCatalogueSearchPaths(yaml_path);
|
||||
for (const auto& candidate : search_paths) {
|
||||
fs::path resolved = candidate;
|
||||
if (resolved.is_relative()) {
|
||||
try {
|
||||
resolved = fs::absolute(resolved);
|
||||
} catch (const std::exception& e) {
|
||||
// If we can't resolve the absolute path (e.g., cwd doesn't exist),
|
||||
// just try the relative path as-is
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (fs::exists(resolved)) {
|
||||
return resolved.string();
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
// If checking existence fails, just continue to next path
|
||||
continue;
|
||||
// If an explicit path is provided, check it first
|
||||
if (!yaml_path.empty()) {
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists(yaml_path, ec) && !ec) {
|
||||
return yaml_path;
|
||||
}
|
||||
}
|
||||
|
||||
return absl::NotFoundError(
|
||||
absl::StrCat("Prompt catalogue not found. Checked paths: ",
|
||||
absl::StrJoin(search_paths, ", ",
|
||||
[](std::string* out, const fs::path& path) {
|
||||
absl::StrAppend(out, path.string());
|
||||
})));
|
||||
|
||||
// Check environment variable override
|
||||
if (const char* env_path = std::getenv("YAZE_AGENT_CATALOGUE")) {
|
||||
if (*env_path != '\0') {
|
||||
std::error_code ec;
|
||||
if (std::filesystem::exists(env_path, ec) && !ec) {
|
||||
return std::string(env_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use PlatformPaths to find the asset in standard locations
|
||||
// Try the requested path (default is prompt_catalogue.yaml)
|
||||
std::string relative_path = yaml_path.empty() ?
|
||||
"agent/prompt_catalogue.yaml" : yaml_path;
|
||||
|
||||
auto result = util::PlatformPaths::FindAsset(relative_path);
|
||||
if (result.ok()) {
|
||||
return result->string();
|
||||
}
|
||||
|
||||
return result.status();
|
||||
}
|
||||
|
||||
absl::Status PromptBuilder::LoadResourceCatalogue(const std::string& yaml_path) {
|
||||
@@ -548,15 +509,10 @@ std::string PromptBuilder::BuildFewShotExamplesSection() const {
|
||||
}
|
||||
|
||||
std::string PromptBuilder::BuildConstraintsSection() const {
|
||||
// Try to load from file first
|
||||
const std::vector<std::string> search_paths = {
|
||||
"assets/agent/tool_calling_instructions.txt",
|
||||
"../assets/agent/tool_calling_instructions.txt",
|
||||
"../../assets/agent/tool_calling_instructions.txt",
|
||||
};
|
||||
|
||||
for (const auto& path : search_paths) {
|
||||
std::ifstream file(path);
|
||||
// Try to load from file first using FindAsset
|
||||
auto file_path = util::PlatformPaths::FindAsset("agent/tool_calling_instructions.txt");
|
||||
if (file_path.ok()) {
|
||||
std::ifstream file(file_path->string());
|
||||
if (file.is_open()) {
|
||||
std::string content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
@@ -743,15 +699,10 @@ std::string PromptBuilder::BuildContextSection(const RomContext& context) {
|
||||
}
|
||||
|
||||
std::string PromptBuilder::BuildSystemInstruction() {
|
||||
// Try to load from file first
|
||||
const std::vector<std::string> search_paths = {
|
||||
"assets/agent/system_prompt.txt",
|
||||
"../assets/agent/system_prompt.txt",
|
||||
"../../assets/agent/system_prompt.txt",
|
||||
};
|
||||
|
||||
for (const auto& path : search_paths) {
|
||||
std::ifstream file(path);
|
||||
// Try to load from file first using FindAsset
|
||||
auto file_path = util::PlatformPaths::FindAsset("agent/system_prompt.txt");
|
||||
if (file_path.ok()) {
|
||||
std::ifstream file(file_path->string());
|
||||
if (file.is_open()) {
|
||||
std::string content((std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
@@ -876,3 +827,4 @@ std::vector<FewShotExample> PromptBuilder::GetExamplesForCategory(
|
||||
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
|
||||
@@ -89,9 +89,11 @@ add_executable(
|
||||
cli/service/testing/test_suite_writer.cc
|
||||
)
|
||||
|
||||
# ============================================================================
|
||||
target_compile_definitions(z3ed PRIVATE YAZE_ASSETS_PATH="${CMAKE_SOURCE_DIR}/assets")
|
||||
|
||||
# ============================================================================
|
||||
# AI Agent Support (Consolidated via Z3ED_AI flag)
|
||||
# ============================================================================
|
||||
# ============================================================================
|
||||
if(Z3ED_AI OR YAZE_WITH_JSON)
|
||||
target_compile_definitions(z3ed PRIVATE YAZE_WITH_JSON)
|
||||
message(STATUS "✓ z3ed AI agent enabled (Ollama + Gemini support)")
|
||||
@@ -100,9 +102,9 @@ if(Z3ED_AI OR YAZE_WITH_JSON)
|
||||
target_link_libraries(z3ed PRIVATE nlohmann_json::nlohmann_json)
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# ============================================================================
|
||||
# SSL/HTTPS Support (Optional - Required for Gemini API)
|
||||
# ============================================================================
|
||||
# ============================================================================
|
||||
# SSL is only enabled when AI features are active
|
||||
# Ollama (localhost) works without SSL, Gemini (HTTPS) requires it
|
||||
if((Z3ED_AI OR YAZE_WITH_JSON) AND (YAZE_WITH_GRPC OR Z3ED_AI))
|
||||
@@ -158,9 +160,9 @@ else()
|
||||
)
|
||||
endif()
|
||||
|
||||
# ============================================================================
|
||||
# ============================================================================
|
||||
# Optional gRPC Support for CLI Agent Test Command
|
||||
# ============================================================================
|
||||
# ============================================================================
|
||||
if(YAZE_WITH_GRPC)
|
||||
message(STATUS "Adding gRPC support to z3ed CLI")
|
||||
|
||||
@@ -183,4 +185,4 @@ if(YAZE_WITH_GRPC)
|
||||
libprotobuf)
|
||||
|
||||
message(STATUS "✓ gRPC CLI automation integrated")
|
||||
endif()
|
||||
endif()
|
||||
@@ -11,44 +11,63 @@
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <climits> // For PATH_MAX
|
||||
#ifdef __APPLE__
|
||||
#include <mach-o/dyld.h> // For _NSGetExecutablePath
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace yaze {
|
||||
namespace util {
|
||||
|
||||
std::filesystem::path PlatformPaths::GetHomeDirectory() {
|
||||
try {
|
||||
#ifdef _WIN32
|
||||
// Windows: Use USERPROFILE environment variable
|
||||
const char* userprofile = std::getenv("USERPROFILE");
|
||||
if (userprofile && *userprofile) {
|
||||
return std::filesystem::path(userprofile);
|
||||
}
|
||||
|
||||
// Fallback to HOMEDRIVE + HOMEPATH
|
||||
const char* homedrive = std::getenv("HOMEDRIVE");
|
||||
const char* homepath = std::getenv("HOMEPATH");
|
||||
if (homedrive && homepath) {
|
||||
return std::filesystem::path(std::string(homedrive) + std::string(homepath));
|
||||
}
|
||||
|
||||
// Last resort: use temp directory
|
||||
return std::filesystem::temp_directory_path();
|
||||
// Windows: Use USERPROFILE environment variable
|
||||
const char* userprofile = std::getenv("USERPROFILE");
|
||||
if (userprofile && *userprofile) {
|
||||
return std::filesystem::path(userprofile);
|
||||
}
|
||||
|
||||
// Fallback to HOMEDRIVE + HOMEPATH
|
||||
const char* homedrive = std::getenv("HOMEDRIVE");
|
||||
const char* homepath = std::getenv("HOMEPATH");
|
||||
if (homedrive && homepath) {
|
||||
return std::filesystem::path(std::string(homedrive) + std::string(homepath));
|
||||
}
|
||||
|
||||
// Last resort: use temp directory
|
||||
std::error_code ec;
|
||||
auto temp = std::filesystem::temp_directory_path(ec);
|
||||
if (!ec) {
|
||||
return temp;
|
||||
}
|
||||
return std::filesystem::path(".");
|
||||
#else
|
||||
// Unix/macOS: Use HOME environment variable
|
||||
const char* home = std::getenv("HOME");
|
||||
if (home && *home) {
|
||||
return std::filesystem::path(home);
|
||||
}
|
||||
|
||||
// Fallback: try getpwuid
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
if (pw && pw->pw_dir) {
|
||||
return std::filesystem::path(pw->pw_dir);
|
||||
}
|
||||
|
||||
// Last resort: current directory
|
||||
return std::filesystem::current_path();
|
||||
// Unix/macOS: Use HOME environment variable
|
||||
const char* home = std::getenv("HOME");
|
||||
if (home && *home) {
|
||||
return std::filesystem::path(home);
|
||||
}
|
||||
|
||||
// Fallback: try getpwuid
|
||||
struct passwd* pw = getpwuid(getuid());
|
||||
if (pw && pw->pw_dir) {
|
||||
return std::filesystem::path(pw->pw_dir);
|
||||
}
|
||||
|
||||
// Last resort: current directory (with error handling)
|
||||
std::error_code ec;
|
||||
auto cwd = std::filesystem::current_path(ec);
|
||||
if (!ec) {
|
||||
return cwd;
|
||||
}
|
||||
return std::filesystem::path(".");
|
||||
#endif
|
||||
} catch (...) {
|
||||
// If everything fails, return current directory placeholder
|
||||
return std::filesystem::path(".");
|
||||
}
|
||||
}
|
||||
|
||||
absl::StatusOr<std::filesystem::path> PlatformPaths::GetAppDataDirectory() {
|
||||
@@ -70,23 +89,11 @@ absl::StatusOr<std::filesystem::path> PlatformPaths::GetAppDataDirectory() {
|
||||
return status;
|
||||
}
|
||||
return app_data;
|
||||
#elif defined(__APPLE__)
|
||||
#else
|
||||
// Unix/macOS: Use ~/.yaze for simplicity and consistency
|
||||
// This is simpler than XDG or Application Support for a dev tool
|
||||
std::filesystem::path home = GetHomeDirectory();
|
||||
std::filesystem::path app_data = home / "Library" / "Application Support" / "yaze";
|
||||
auto status = EnsureDirectoryExists(app_data);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
return app_data;
|
||||
#else // Linux and other Unix-like systems
|
||||
const char* xdg_config_home = std::getenv("XDG_CONFIG_HOME");
|
||||
std::filesystem::path config_dir;
|
||||
if (xdg_config_home && *xdg_config_home) {
|
||||
config_dir = std::filesystem::path(xdg_config_home);
|
||||
} else {
|
||||
config_dir = GetHomeDirectory() / ".config";
|
||||
}
|
||||
std::filesystem::path app_data = config_dir / "yaze";
|
||||
std::filesystem::path app_data = home / ".yaze";
|
||||
auto status = EnsureDirectoryExists(app_data);
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
@@ -174,5 +181,163 @@ std::string PlatformPaths::ToNativePath(const std::filesystem::path& path) {
|
||||
return path.string();
|
||||
}
|
||||
|
||||
absl::StatusOr<std::filesystem::path> PlatformPaths::FindAsset(
|
||||
const std::string& relative_path) {
|
||||
std::vector<std::filesystem::path> search_paths;
|
||||
|
||||
try {
|
||||
// 1. Check compile-time YAZE_ASSETS_PATH if defined
|
||||
#ifdef YAZE_ASSETS_PATH
|
||||
try {
|
||||
search_paths.push_back(std::filesystem::path(YAZE_ASSETS_PATH) / relative_path);
|
||||
} catch (...) {
|
||||
// Skip if path construction fails
|
||||
}
|
||||
#endif
|
||||
|
||||
// 2. Current working directory + assets/ (cached to avoid repeated calls)
|
||||
static std::filesystem::path cached_cwd;
|
||||
static bool cwd_cached = false;
|
||||
|
||||
if (!cwd_cached) {
|
||||
std::error_code ec;
|
||||
cached_cwd = std::filesystem::current_path(ec);
|
||||
cwd_cached = true; // Only try once to avoid repeated slow calls
|
||||
}
|
||||
|
||||
if (!cached_cwd.empty()) {
|
||||
try {
|
||||
search_paths.push_back(cached_cwd / "assets" / relative_path);
|
||||
} catch (...) {
|
||||
// Skip if path construction fails
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Executable directory + assets/ (cached to avoid repeated OS calls)
|
||||
static std::filesystem::path cached_exe_dir;
|
||||
static bool exe_dir_cached = false;
|
||||
|
||||
if (!exe_dir_cached) {
|
||||
try {
|
||||
#ifdef __APPLE__
|
||||
char exe_path[PATH_MAX];
|
||||
uint32_t size = sizeof(exe_path);
|
||||
if (_NSGetExecutablePath(exe_path, &size) == 0) {
|
||||
cached_exe_dir = std::filesystem::path(exe_path).parent_path();
|
||||
}
|
||||
#elif defined(__linux__)
|
||||
char exe_path[PATH_MAX];
|
||||
ssize_t len = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
if (len != -1) {
|
||||
exe_path[len] = '\0';
|
||||
cached_exe_dir = std::filesystem::path(exe_path).parent_path();
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
wchar_t exe_path[MAX_PATH];
|
||||
if (GetModuleFileNameW(NULL, exe_path, MAX_PATH) != 0) {
|
||||
cached_exe_dir = std::filesystem::path(exe_path).parent_path();
|
||||
}
|
||||
#endif
|
||||
} catch (...) {
|
||||
// Skip if exe path detection fails
|
||||
}
|
||||
exe_dir_cached = true; // Only try once
|
||||
}
|
||||
|
||||
if (!cached_exe_dir.empty()) {
|
||||
try {
|
||||
search_paths.push_back(cached_exe_dir / "assets" / relative_path);
|
||||
// Also check parent (for build/bin/yaze case)
|
||||
search_paths.push_back(cached_exe_dir.parent_path() / "assets" / relative_path);
|
||||
} catch (...) {
|
||||
// Skip if path construction fails
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Parent directories (for running from build subdirectories)
|
||||
if (!cached_cwd.empty()) {
|
||||
try {
|
||||
auto parent = cached_cwd.parent_path();
|
||||
if (!parent.empty() && parent != cached_cwd) {
|
||||
search_paths.push_back(parent / "assets" / relative_path);
|
||||
auto grandparent = parent.parent_path();
|
||||
if (!grandparent.empty() && grandparent != parent) {
|
||||
search_paths.push_back(grandparent / "assets" / relative_path);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Skip if path operations fail
|
||||
}
|
||||
}
|
||||
|
||||
// 5. User directory ~/.yaze/assets/ (cached home directory)
|
||||
static std::filesystem::path cached_home;
|
||||
static bool home_cached = false;
|
||||
|
||||
if (!home_cached) {
|
||||
try {
|
||||
cached_home = GetHomeDirectory();
|
||||
} catch (...) {
|
||||
// Skip if home lookup fails
|
||||
}
|
||||
home_cached = true; // Only try once
|
||||
}
|
||||
|
||||
if (!cached_home.empty() && cached_home != ".") {
|
||||
try {
|
||||
search_paths.push_back(cached_home / ".yaze" / "assets" / relative_path);
|
||||
} catch (...) {
|
||||
// Skip if path construction fails
|
||||
}
|
||||
}
|
||||
|
||||
// 6. System-wide installation (Unix only)
|
||||
#ifndef _WIN32
|
||||
try {
|
||||
search_paths.push_back(std::filesystem::path("/usr/local/share/yaze/assets") / relative_path);
|
||||
search_paths.push_back(std::filesystem::path("/usr/share/yaze/assets") / relative_path);
|
||||
} catch (...) {
|
||||
// Skip if path construction fails
|
||||
}
|
||||
#endif
|
||||
|
||||
// Search all paths and return the first one that exists
|
||||
// Limit search to prevent infinite loops on weird filesystems
|
||||
const size_t max_paths_to_check = 20;
|
||||
size_t checked = 0;
|
||||
|
||||
for (const auto& candidate : search_paths) {
|
||||
if (++checked > max_paths_to_check) {
|
||||
break; // Safety limit
|
||||
}
|
||||
|
||||
try {
|
||||
// Use std::filesystem::exists with error code to avoid exceptions
|
||||
std::error_code exists_ec;
|
||||
if (std::filesystem::exists(candidate, exists_ec) && !exists_ec) {
|
||||
// Double-check it's a regular file or directory, not a broken symlink
|
||||
auto status = std::filesystem::status(candidate, exists_ec);
|
||||
if (!exists_ec && status.type() != std::filesystem::file_type::not_found) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
// Skip this candidate if checking fails
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If not found, return a simple error
|
||||
return absl::NotFoundError(
|
||||
absl::StrCat("Asset not found: ", relative_path));
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
return absl::InternalError(
|
||||
absl::StrCat("Exception while searching for asset: ", e.what()));
|
||||
} catch (...) {
|
||||
return absl::InternalError("Unknown exception while searching for asset");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace yaze
|
||||
|
||||
@@ -35,8 +35,7 @@ class PlatformPaths {
|
||||
* and cache files. The directory is created if it doesn't exist.
|
||||
*
|
||||
* - Windows: `%APPDATA%\yaze` (e.g., C:\Users\user\AppData\Roaming\yaze)
|
||||
* - macOS: `~/Library/Application Support/yaze`
|
||||
* - Linux: `$XDG_CONFIG_HOME/yaze` or `~/.config/yaze`
|
||||
* - macOS/Unix: `~/.yaze`
|
||||
*
|
||||
* @return StatusOr with path to the application data directory.
|
||||
*/
|
||||
@@ -46,13 +45,10 @@ class PlatformPaths {
|
||||
* @brief Get the user-specific configuration directory for YAZE.
|
||||
*
|
||||
* This is the standard location for storing user configuration files.
|
||||
* It often maps to the same location as GetAppDataDirectory but provides
|
||||
* a more semantically correct API for config files.
|
||||
* The directory is created if it doesn't exist.
|
||||
*
|
||||
* - Windows: `%APPDATA%\yaze`
|
||||
* - macOS: `~/Library/Application Support/yaze`
|
||||
* - Linux: `$XDG_CONFIG_HOME/yaze` or `~/.config/yaze`
|
||||
* - macOS/Unix: `~/.yaze`
|
||||
*
|
||||
* @return StatusOr with path to the configuration directory.
|
||||
*/
|
||||
@@ -117,6 +113,23 @@ class PlatformPaths {
|
||||
* @return Native path string
|
||||
*/
|
||||
static std::string ToNativePath(const std::filesystem::path& path);
|
||||
|
||||
/**
|
||||
* @brief Find an asset file in multiple standard locations
|
||||
*
|
||||
* Searches for an asset file in the following order:
|
||||
* 1. YAZE_ASSETS_PATH (if defined at compile time) + relative_path
|
||||
* 2. Current working directory + assets/ + relative_path
|
||||
* 3. Executable directory + assets/ + relative_path
|
||||
* 4. Parent directory + assets/ + relative_path
|
||||
* 5. ~/.yaze/assets/ + relative_path (user-installed assets)
|
||||
* 6. /usr/local/share/yaze/assets/ + relative_path (system-wide on Unix)
|
||||
*
|
||||
* @param relative_path Path relative to assets directory (e.g., "agent/prompt_catalogue.yaml")
|
||||
* @return StatusOr with absolute path to found asset file
|
||||
*/
|
||||
static absl::StatusOr<std::filesystem::path> FindAsset(
|
||||
const std::string& relative_path);
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
|
||||
Reference in New Issue
Block a user