/** * @file services/prompt_directory.cc * @brief PromptDirectory implementation: validates the directory at * construction and loads named prompt files on demand with in-process caching. */ #include "services/prompting/prompt_directory.h" #include #include #include #include #include #include #include #include // --------------------------------------------------------------------------- // PromptDirectory // --------------------------------------------------------------------------- PromptDirectory::PromptDirectory(const std::filesystem::path& prompt_dir) : PromptDirectory(prompt_dir, nullptr) {} PromptDirectory::PromptDirectory(const std::filesystem::path& prompt_dir, std::shared_ptr logger) : prompt_dir_(prompt_dir), logger_(std::move(logger)) { std::error_code ec; // Scenario 4: directory must exist. if (!std::filesystem::exists(prompt_dir_, ec) || ec) { throw std::runtime_error( "PromptDirectory: prompt directory does not exist: " + prompt_dir_.string()); } // Scenario 4: path must be a directory, not a file. if (!std::filesystem::is_directory(prompt_dir_, ec) || ec) { throw std::runtime_error( "PromptDirectory: prompt directory path is not a directory: " + prompt_dir_.string()); } // Scenario 4: directory must be readable (probe with directory_iterator). std::filesystem::directory_iterator probe(prompt_dir_, ec); if (ec) { throw std::runtime_error(std::format( "PromptDirectory: prompt directory is not readable: {} ({})", prompt_dir_.string(), ec.message())); } if (logger_) { logger_->Log( {.level = LogLevel::Info, .phase = PipelinePhase::Startup, .message = std::string("[PromptDirectory] Resolved prompt directory: ") + prompt_dir_.string()}); } } std::string PromptDirectory::Load(std::string_view key) { const std::string key_str(key); // Return cached content if already loaded during this run. const auto cache_it = cache_.find(key_str); if (cache_it != cache_.end()) { return cache_it->second; } // Scenario 3: resolve /.md and require it to exist. const std::filesystem::path file_path = prompt_dir_ / std::filesystem::path(std::format("{}.md", key_str)); std::ifstream file(file_path); if (!file.is_open()) { throw std::runtime_error( std::format("PromptDirectory: prompt file not found for key '{}': {}", key_str, file_path.string())); } std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); file.close(); if (content.empty()) { throw std::runtime_error( std::format("PromptDirectory: prompt file for key '{}' is empty: {}", key_str, file_path.string())); } if (logger_) { logger_->Log( {.level = LogLevel::Info, .phase = PipelinePhase::Startup, .message = std::format( "[PromptDirectory] Loaded prompt '{}' from '{}' ({} chars)", key_str, file_path.string(), content.size())}); } cache_.emplace(key_str, content); return content; }