From a1641230cfb64d026dea909fce01759111b26a39 Mon Sep 17 00:00:00 2001 From: Selim Arguel Date: Tue, 1 Apr 2025 14:09:15 +0300 Subject: [PATCH] Small refactoring --- .../internal/application/application_impl.cc | 180 +++++++++++++ .../internal/application/application_impl.h | 79 ++++++ .../internal/base/command_registry_impl.cc | 110 ++++++++ .../cli/internal/base/command_registry_impl.h | 36 +++ .../cli/internal/chat/chat_completion_cmd.cc | 113 +++++++++ .../cli/internal/chat/chat_completion_cmd.h | 72 ++++++ engine/cli/main.cc | 240 ++---------------- engine/cli/public/application.h | 68 +++++ engine/cli/public/command.h | 63 +++++ engine/cli/public/command_registry.h | 80 ++++++ engine/cli/public/commands.h | 86 +++++++ 11 files changed, 906 insertions(+), 221 deletions(-) create mode 100644 engine/cli/internal/application/application_impl.cc create mode 100644 engine/cli/internal/application/application_impl.h create mode 100644 engine/cli/internal/base/command_registry_impl.cc create mode 100644 engine/cli/internal/base/command_registry_impl.h create mode 100644 engine/cli/internal/chat/chat_completion_cmd.cc create mode 100644 engine/cli/internal/chat/chat_completion_cmd.h create mode 100644 engine/cli/public/application.h create mode 100644 engine/cli/public/command.h create mode 100644 engine/cli/public/command_registry.h create mode 100644 engine/cli/public/commands.h diff --git a/engine/cli/internal/application/application_impl.cc b/engine/cli/internal/application/application_impl.cc new file mode 100644 index 000000000..5135db09f --- /dev/null +++ b/engine/cli/internal/application/application_impl.cc @@ -0,0 +1,180 @@ +// commands/internal/application/application_impl.cc +#include "public/application.h" +#include "application_impl.h" +#include "public/command_registry.h" +#include "internal/chat/chat_completion_cmd.h" + +#include +#include "services/database_service.h" +#include "services/download_service.h" +#include "utils/system_info_utils.h" + +namespace cortex { + +Application::Application() : impl_(std::make_unique()) { + // Application constructor initializes the implementation +} + +Application::~Application() = default; + +int Application::Run(int argc, char* argv[]) { + // Check system compatibility + if (!impl_->IsSystemSupported()) { + std::cerr << "Unsupported system" << std::endl; + return 1; + } + + // Initialize system + if (!impl_->InitializeSystem()) { + std::cerr << "Failed to initialize system" << std::endl; + return 1; + } + + // Initialize services + if (!impl_->InitializeServices()) { + std::cerr << "Failed to initialize services" << std::endl; + return 1; + } + + // Register commands + impl_->RegisterCommands(); + + // Parse command line arguments + std::string command_name; + std::vector args; + std::unordered_map options; + impl_->ParseArgs(argc, argv, command_name, args, options); + + // Handle help command or no command + if (command_name.empty() || command_name == "help") { + if (args.empty()) { + impl_->command_registry_->PrintHelp(); + } else { + impl_->command_registry_->PrintCommandHelp(args[0]); + } + return 0; + } + + // Execute the command + auto status = impl_->command_registry_->ExecuteCommand(command_name, args, options); + + // Cleanup + impl_->CleanupSystem(); + + return status == commands::CommandStatus::Success ? 0 : 1; +} + +int Application::RunCommand(const std::string& command, const std::vector& args) { + // Execute a specific command programmatically + if (!impl_->command_registry_) { + if (!impl_->InitializeServices()) { + return 1; + } + impl_->RegisterCommands(); + } + + auto status = impl_->command_registry_->ExecuteCommand(command, args, {}); + return status == commands::CommandStatus::Success ? 0 : 1; +} + +// Implementation class methods + +Application::Impl::Impl() = default; + +Application::Impl::~Impl() = default; + +bool Application::Impl::InitializeServices() { + try { + // Initialize services + db_service_ = std::make_shared(); + download_service_ = std::make_shared(); + + // Create command registry + command_registry_ = commands::CommandRegistry::Create(); + + return true; + } catch (const std::exception& e) { + std::cerr << "Error initializing services: " << e.what() << std::endl; + return false; + } +} + +void Application::Impl::RegisterCommands() { + // Register chat command + command_registry_->RegisterCommand( + std::make_shared(db_service_)); + + // Register other commands + // command_registry_->RegisterCommand( + // std::make_shared(db_service_)); + // ... +} + +void Application::Impl::ParseArgs(int argc, char* argv[], + std::string& command_name, + std::vector& args, + std::unordered_map& options) { + // Extract command name (first argument) + if (argc > 1) { + command_name = argv[1]; + } + + // Process remaining arguments + for (int i = 2; i < argc; ++i) { + std::string arg = argv[i]; + + // Handle options (--option=value or --flag) + if (arg.size() > 2 && arg.substr(0, 2) == "--") { + size_t pos = arg.find('='); + if (pos != std::string::npos) { + // --option=value format + std::string option_name = arg.substr(2, pos - 2); + std::string option_value = arg.substr(pos + 1); + options[option_name] = option_value; + } else { + // --flag format + options[arg.substr(2)] = "true"; + } + } else if (arg.size() > 1 && arg[0] == '-') { + // Handle short options (-f) + std::string option_name = arg.substr(1); + + // If next argument isn't an option, treat it as this option's value + if (i + 1 < argc && argv[i + 1][0] != '-') { + options[option_name] = argv[i + 1]; + ++i; // Skip the value in the next iteration + } else { + // Flag option + options[option_name] = "true"; + } + } else { + // Regular argument + args.push_back(arg); + } + } +} + +bool Application::Impl::IsSystemSupported() const { + // Check system compatibility + auto system_info = system_info_utils::GetSystemInfo(); + return !(system_info->arch == system_info_utils::kUnsupported || + system_info->os == system_info_utils::kUnsupported); +} + +bool Application::Impl::InitializeSystem() { + try { + // Initialize libraries and resources + // This would include things like SSL_library_init() and curl_global_init() + return true; + } catch (const std::exception& e) { + std::cerr << "Error initializing system: " << e.what() << std::endl; + return false; + } +} + +void Application::Impl::CleanupSystem() { + // Clean up global resources + // This would include things like curl_global_cleanup() +} + +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/internal/application/application_impl.h b/engine/cli/internal/application/application_impl.h new file mode 100644 index 000000000..604f5a953 --- /dev/null +++ b/engine/cli/internal/application/application_impl.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include + +// Forward declarations to avoid including unnecessary headers +namespace cortex { +namespace commands { +class CommandRegistry; +} +} + +class DatabaseService; +class DownloadService; + +namespace cortex { + +// Implementation class for Application (Pimpl pattern) +class Application::Impl { +public: + Impl(); + ~Impl(); + + // Services + std::shared_ptr db_service_; + std::shared_ptr download_service_; + + // Command registry + std::shared_ptr command_registry_; + + /** + * @brief Initialize services + * + * @return bool True if initialization was successful + */ + bool InitializeServices(); + + /** + * @brief Register commands with the registry + */ + void RegisterCommands(); + + /** + * @brief Parse command line arguments + * + * @param argc Argument count + * @param argv Argument values + * @param command_name Output parameter for command name + * @param args Output parameter for command arguments + * @param options Output parameter for command options + */ + void ParseArgs(int argc, char* argv[], + std::string& command_name, + std::vector& args, + std::unordered_map& options); + + /** + * @brief Check if system is supported + * + * @return bool True if system is supported + */ + bool IsSystemSupported() const; + + /** + * @brief Initialize system components + * + * @return bool True if initialization was successful + */ + bool InitializeSystem(); + + /** + * @brief Cleanup system resources + */ + void CleanupSystem(); +}; + +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/internal/base/command_registry_impl.cc b/engine/cli/internal/base/command_registry_impl.cc new file mode 100644 index 000000000..f1abee236 --- /dev/null +++ b/engine/cli/internal/base/command_registry_impl.cc @@ -0,0 +1,110 @@ +#include "command_registry_impl.h" +#include +#include + +namespace cortex { +namespace commands { + +bool CommandRegistryImpl::RegisterCommand(std::shared_ptr command) { + if (!command) { + return false; + } + + const auto& name = command->GetName(); + if (name.empty() || commands_.find(name) != commands_.end()) { + return false; + } + + commands_[name] = std::move(command); + return true; +} + +std::shared_ptr CommandRegistryImpl::FindCommand(const std::string& name) const { + auto it = commands_.find(name); + if (it != commands_.end()) { + return it->second; + } + return nullptr; +} + +std::vector> CommandRegistryImpl::GetAllCommands() const { + std::vector> result; + result.reserve(commands_.size()); + + for (const auto& [_, command] : commands_) { + result.push_back(command); + } + + // Sort commands by name for consistent output + std::sort(result.begin(), result.end(), + [](const std::shared_ptr& a, const std::shared_ptr& b) { + return a->GetName() < b->GetName(); + }); + + return result; +} + +CommandStatus CommandRegistryImpl::ExecuteCommand( + const std::string& command_name, + const std::vector& args, + const std::unordered_map& options) { + + auto command = FindCommand(command_name); + if (!command) { + std::cerr << "Unknown command: " << command_name << std::endl; + PrintHelp(); + return CommandStatus::NotFound; + } + + return command->Execute(args, options); +} + +void CommandRegistryImpl::PrintHelp() const { + std::cout << "Cortex Command Line Interface" << std::endl; + std::cout << "Usage: cortex [command] [options]" << std::endl; + std::cout << std::endl; + + std::cout << "Available commands:" << std::endl; + + auto all_commands = GetAllCommands(); + size_t max_name_length = 0; + + // Find the longest command name for alignment + for (const auto& command : all_commands) { + max_name_length = std::max(max_name_length, command->GetName().length()); + } + + // Print all commands + for (const auto& command : all_commands) { + std::cout << " " << command->GetName(); + + // Add padding for alignment + for (size_t i = command->GetName().length(); i < max_name_length + 2; ++i) { + std::cout << " "; + } + + std::cout << command->GetDescription() << std::endl; + } + + std::cout << std::endl; + std::cout << "For more information about a specific command, type 'cortex help [command]'" << std::endl; +} + +bool CommandRegistryImpl::PrintCommandHelp(const std::string& command_name) const { + auto command = FindCommand(command_name); + if (!command) { + std::cout << "Unknown command: " << command_name << std::endl; + return false; + } + + std::cout << command->GetHelp() << std::endl; + return true; +} + +// Static factory method implementation +std::shared_ptr CommandRegistry::Create() { + return std::make_shared(); +} + +} // namespace commands +} // namespace cortex diff --git a/engine/cli/internal/base/command_registry_impl.h b/engine/cli/internal/base/command_registry_impl.h new file mode 100644 index 000000000..11a5e28af --- /dev/null +++ b/engine/cli/internal/base/command_registry_impl.h @@ -0,0 +1,36 @@ +#pragma once + +#include "public/command_registry.h" +#include + +namespace cortex { +namespace commands { + +/** + * @brief Implementation of command registry + */ +class CommandRegistryImpl : public CommandRegistry { +public: + CommandRegistryImpl() = default; + + bool RegisterCommand(std::shared_ptr command) override; + + std::shared_ptr FindCommand(const std::string& name) const override; + + std::vector> GetAllCommands() const override; + + CommandStatus ExecuteCommand( + const std::string& command_name, + const std::vector& args, + const std::unordered_map& options) override; + + void PrintHelp() const override; + + bool PrintCommandHelp(const std::string& command_name) const override; + +private: + std::unordered_map> commands_; +}; + +} // namespace commands +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/internal/chat/chat_completion_cmd.cc b/engine/cli/internal/chat/chat_completion_cmd.cc new file mode 100644 index 000000000..fb35654ab --- /dev/null +++ b/engine/cli/internal/chat/chat_completion_cmd.cc @@ -0,0 +1,113 @@ +// commands/internal/chat/chat_completion_cmd.cc +#include "chat_completion_cmd.h" +#include +#include "config/yaml_config.h" +#include "model_status_cmd.h" +#include "server_start_cmd.h" +#include "utils/engine_constants.h" +#include "utils/logging_utils.h" +#include "utils/string_utils.h" + +// For convenience, using the existing commands namespace +using namespace commands; + +namespace cortex { +namespace commands { + +ChatCompletionCommand::ChatCompletionCommand(std::shared_ptr db_service) + : db_service_(std::move(db_service)) {} + +CommandStatus ChatCompletionCommand::Execute( + const std::vector& args, + const std::unordered_map& options) { + + // Extract host and port from options (or use defaults) + std::string host = "127.0.0.1"; + int port = 39281; + + auto it = options.find("host"); + if (it != options.end()) { + host = it->second; + } + + it = options.find("port"); + if (it != options.end()) { + try { + port = std::stoi(it->second); + } catch (...) { + std::cerr << "Invalid port number: " << it->second << std::endl; + return CommandStatus::InvalidArguments; + } + } + + // Extract model handle (from args or options) + std::string model_handle; + if (!args.empty()) { + model_handle = args[0]; + } else { + it = options.find("model"); + if (it != options.end()) { + model_handle = it->second; + } else { + std::cerr << "Model handle is required" << std::endl; + return CommandStatus::InvalidArguments; + } + } + + // Extract message (if any) + std::string msg; + it = options.find("message"); + if (it != options.end()) { + msg = it->second; + } + + // Call the original implementation + try { + Exec(host, port, model_handle, msg); + return CommandStatus::Success; + } catch (const std::exception& e) { + std::cerr << "Error: " << e.what() << std::endl; + return CommandStatus::GeneralError; + } +} + +std::string ChatCompletionCommand::GetName() const { + return "chat"; +} + +std::string ChatCompletionCommand::GetDescription() const { + return "Chat with a model interactively or send a single message"; +} + +std::string ChatCompletionCommand::GetHelp() const { + return "Usage: cortex chat [options] MODEL_ID\n" + "\n" + "Start a chat session with the specified model.\n" + "\n" + "Arguments:\n" + " MODEL_ID Model ID to chat with\n" + "\n" + "Options:\n" + " --message=MESSAGE Initial message (optional, interactive mode if not provided)\n" + " --host=HOST Server host (default: 127.0.0.1)\n" + " --port=PORT Server port (default: 39281)\n"; +} + +// Reusing the original implementation +void ChatCompletionCommand::Exec(const std::string& host, int port, + const std::string& model_handle, std::string msg) { + // Reuse the existing implementation + ::commands::ChatCompletionCmd original_cmd(db_service_); + original_cmd.Exec(host, port, model_handle, msg); +} + +void ChatCompletionCommand::Exec(const std::string& host, int port, + const std::string& model_handle, + const config::ModelConfig& mc, std::string msg) { + // Reuse the existing implementation + ::commands::ChatCompletionCmd original_cmd(db_service_); + original_cmd.Exec(host, port, model_handle, mc, msg); +} + +} // namespace commands +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/internal/chat/chat_completion_cmd.h b/engine/cli/internal/chat/chat_completion_cmd.h new file mode 100644 index 000000000..345ced231 --- /dev/null +++ b/engine/cli/internal/chat/chat_completion_cmd.h @@ -0,0 +1,72 @@ +// commands/internal/chat/chat_completion_cmd.h +#pragma once + +#include "public/command.h" +#include +#include +#include +#include "config/model_config.h" +#include "services/database_service.h" + +namespace cortex { +namespace commands { + +/** + * @brief Command implementation for chat completions + */ +class ChatCompletionCommand : public Command { +public: + /** + * @brief Construct a new Chat Completion Command + * + * @param db_service Database service for model information + */ + explicit ChatCompletionCommand(std::shared_ptr db_service); + + /** + * @brief Execute the chat command + * + * @param args Positional arguments + * @param options Command options + * @return CommandStatus Execution status + */ + CommandStatus Execute( + const std::vector& args, + const std::unordered_map& options) override; + + /** + * @brief Get the command name + * + * @return std::string Command name + */ + std::string GetName() const override; + + /** + * @brief Get the command description + * + * @return std::string Command description + */ + std::string GetDescription() const override; + + /** + * @brief Get the command help text + * + * @return std::string Help text + */ + std::string GetHelp() const override; + +private: + // The original implementation uses these methods + void Exec(const std::string& host, int port, + const std::string& model_handle, std::string msg); + + void Exec(const std::string& host, int port, + const std::string& model_handle, + const config::ModelConfig& mc, std::string msg); + + std::shared_ptr db_service_; + std::vector histories_; +}; + +} // namespace commands +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/main.cc b/engine/cli/main.cc index a4e6c38cc..43b9541a1 100644 --- a/engine/cli/main.cc +++ b/engine/cli/main.cc @@ -1,224 +1,22 @@ -#include -#include "command_line_parser.h" -#include "commands/cortex_upd_cmd.h" -#include "openssl/ssl.h" -#include "services/download_service.h" -#include "utils/archive_utils.h" -#include "utils/cortex_utils.h" -#include "utils/file_logger.h" -#include "utils/file_manager_utils.h" -#include "utils/logging_utils.h" -#include "utils/system_info_utils.h" -#include "utils/widechar_conv.h" - -#if defined(__APPLE__) && defined(__MACH__) -#include // for dirname() -#include -#include -#elif defined(__linux__) -#include // for dirname() -#include -#include -#include // for readlink() -#elif defined(_WIN32) -#include -#undef max -#else -#error "Unsupported platform!" -#endif - -#include -#include - -void RemoveBinaryTempFileIfExists() { - auto temp = - file_manager_utils::GetExecutableFolderContainerPath() / "cortex_temp"; - if (std::filesystem::exists(temp)) { - try { - std::filesystem::remove(temp); - } catch (const std::exception& e) { - std::cerr << e.what() << '\n'; - } - } -} - -void SetupLogger(trantor::FileLogger& async_logger, bool verbose) { - if (!verbose) { - auto config = file_manager_utils::GetCortexConfig(); - - std::filesystem::create_directories( -#if defined(_WIN32) - std::filesystem::path(cortex::wc::Utf8ToWstring(config.logFolderPath)) / -#else - std::filesystem::path(config.logFolderPath) / -#endif - std::filesystem::path(cortex_utils::logs_folder)); - - // Do not need to use u8path here because trantor handles itself - async_logger.setFileName( - (std::filesystem::path(config.logFolderPath) / - std::filesystem::path(cortex_utils::logs_cli_base_name)) - .string()); - async_logger.setMaxLines(config.maxLogLines); // Keep last 100000 lines - async_logger.startLogging(); - trantor::Logger::setOutputFunction( - [&](const char* msg, const uint64_t len) { - async_logger.output_(msg, len); - }, - [&]() { async_logger.flush(); }); - } -} - -void InstallServer() { -#if !defined(_WIN32) - if (getuid()) { - CLI_LOG("Error: Not root user. Please run with sudo."); - return; - } -#endif - auto cuc = commands::CortexUpdCmd(std::make_shared()); - cuc.Exec({}, true /*force*/); -} +// main.cc +#include "public/application.h" +#include +#include int main(int argc, char* argv[]) { - // Stop the program if the system is not supported - auto system_info = system_info_utils::GetSystemInfo(); - if (system_info->arch == system_info_utils::kUnsupported || - system_info->os == system_info_utils::kUnsupported) { - CTL_ERR("Unsupported OS or architecture: " << system_info->os << ", " - << system_info->arch); - return 1; - } - - SSL_library_init(); - curl_global_init(CURL_GLOBAL_DEFAULT); - - bool should_install_server = false; - bool verbose = false; - for (int i = 0; i < argc; i++) { - if (strcmp(argv[i], "--config_file_path") == 0) { - file_manager_utils::cortex_config_file_path = argv[i + 1]; - - } else if (strcmp(argv[i], "--data_folder_path") == 0) { - file_manager_utils::cortex_data_folder_path = argv[i + 1]; - } else if ((strcmp(argv[i], "--server") == 0) && - (strcmp(argv[i - 1], "update") == 0)) { - should_install_server = true; - } else if (strcmp(argv[i], "--verbose") == 0) { - verbose = true; - } - } - - { - auto result = file_manager_utils::CreateConfigFileIfNotExist(); - if (result.has_error()) { - CTL_ERR("Error creating config file: " << result.error()); + // Initialize global libraries + SSL_library_init(); + curl_global_init(CURL_GLOBAL_DEFAULT); + + // Create and run the application + int result = 0; + { + cortex::Application app; + result = app.Run(argc, argv); } - namespace fmu = file_manager_utils; - // Override data folder path if it is configured and changed - if (!fmu::cortex_data_folder_path.empty()) { - auto cfg = file_manager_utils::GetCortexConfig(); - if (cfg.dataFolderPath != fmu::cortex_data_folder_path || - cfg.logFolderPath != fmu::cortex_data_folder_path) { - cfg.dataFolderPath = fmu::cortex_data_folder_path; - cfg.logFolderPath = fmu::cortex_data_folder_path; - auto config_path = file_manager_utils::GetConfigurationPath(); - auto result = - config_yaml_utils::CortexConfigMgr::GetInstance().DumpYamlConfig( - cfg, config_path.string()); - if (result.has_error()) { - CTL_ERR("Error update " << config_path.string() << result.error()); - } - } - } - } - - RemoveBinaryTempFileIfExists(); - - auto should_check_for_latest_llamacpp_version = true; - auto now = std::chrono::system_clock::now(); - - // read the yaml to see the last time we check for update - auto config = file_manager_utils::GetCortexConfig(); - if (config.checkedForLlamacppUpdateAt != 0) { - // if it passed a day, then we should check - auto last_check = - std::chrono::system_clock::time_point( - std::chrono::milliseconds(config.checkedForLlamacppUpdateAt)) + - std::chrono::hours(24); - should_check_for_latest_llamacpp_version = now > last_check; - } - - if (should_check_for_latest_llamacpp_version) { - std::thread t1([]() { - // TODO: namh current we only check for llamacpp. Need to add support for other engine - auto get_latest_version = []() -> cpp::result { - try { - auto res = github_release_utils::GetReleaseByVersion( - "menloresearch", "cortex.llamacpp", "latest"); - if (res.has_error()) { - CTL_ERR("Failed to get latest llama.cpp version: " << res.error()); - return cpp::fail("Failed to get latest llama.cpp version: " + - res.error()); - } - CTL_INF("Latest llamacpp version: " << res->tag_name); - return res->tag_name; - } catch (const std::exception& e) { - CTL_ERR("Failed to get latest llama.cpp version: " << e.what()); - return cpp::fail("Failed to get latest llama.cpp version: " + - std::string(e.what())); - } - }; - - auto res = get_latest_version(); - if (res.has_error()) { - CTL_ERR("Failed to get latest llama.cpp version: " << res.error()); - return; - } - - auto now = std::chrono::system_clock::now(); - CTL_DBG("latest llama.cpp version: " << res.value()); - auto config = file_manager_utils::GetCortexConfig(); - config.checkedForLlamacppUpdateAt = - std::chrono::duration_cast( - now.time_since_epoch()) - .count(); - config.latestLlamacppRelease = res.value(); - - auto upd_config_res = - config_yaml_utils::CortexConfigMgr::GetInstance().DumpYamlConfig( - config, file_manager_utils::GetConfigurationPath().string()); - if (upd_config_res.has_error()) { - CTL_ERR("Failed to update config file: " << upd_config_res.error()); - } else { - CTL_INF("Updated config file with latest llama.cpp version: " - << res.value()); - } - }); - t1.detach(); - } - - static trantor::FileLogger async_file_logger; - SetupLogger(async_file_logger, verbose); - - if (should_install_server) { - InstallServer(); - return 0; - } - - // Check if server exists, if not notify to user to install server - auto exe = commands::GetCortexServerBinary(); - auto server_binary_path = - file_manager_utils::GetExecutableFolderContainerPath() / exe; - if (!std::filesystem::exists(server_binary_path)) { - std::cout << CORTEX_CPP_VERSION - << " requires server binary, to install server, run: " - << commands::GetRole() << commands::GetCortexBinary() - << " update --server" << std::endl; - return 0; - } - - CommandLineParser clp; - clp.SetupCommand(argc, argv); - return 0; -} + + // Cleanup global resources + curl_global_cleanup(); + + return result; +} \ No newline at end of file diff --git a/engine/cli/public/application.h b/engine/cli/public/application.h new file mode 100644 index 000000000..76038af1b --- /dev/null +++ b/engine/cli/public/application.h @@ -0,0 +1,68 @@ +// commands/public/application.h +#pragma once + +#include +#include + +namespace cortex { + +/** + * @brief Main application class + * + * This class handles command-line arguments, initializes the application, and + * orchestrates command execution. + */ +class Application { +public: + /** + * @brief Construct a new Application + */ + Application(); + + /** + * @brief Destructor + */ + ~Application(); + + /** + * @brief Disable copy construction + */ + Application(const Application&) = delete; + + /** + * @brief Disable assignment + */ + Application& operator=(const Application&) = delete; + + /** + * @brief Run the application with command line arguments + * + * This method initializes the application, parses command line arguments, + * and executes the appropriate command. + * + * @param argc Argument count + * @param argv Argument values + * @return int Return code (0 for success) + */ + int Run(int argc, char* argv[]); + + /** + * @brief Run with a specific command and arguments + * + * This method is useful for testing and programmatic use of the application. + * + * @param command Command name + * @param args Command arguments + * @return int Return code (0 for success) + */ + int RunCommand(const std::string& command, const std::vector& args); + +private: + // Forward declaration of implementation + class Impl; + + // Private implementation (Pimpl pattern) + std::unique_ptr impl_; +}; + +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/public/command.h b/engine/cli/public/command.h new file mode 100644 index 000000000..24c85f659 --- /dev/null +++ b/engine/cli/public/command.h @@ -0,0 +1,63 @@ +// commands/public/command.h +#pragma once + +#include +#include +#include + +namespace cortex { +namespace commands { + +/** + * @brief Status codes for command execution + */ +enum class CommandStatus { + GENERAL_ERROR = -1, + GENERAL_SUCCESS = 1, +}; + +/** + * @brief Base interface for all CLI commands + * + * This abstract class defines the interface that all command implementations + * must follow. Commands can have arguments, options, and execute operations. + */ +class Command { +public: + virtual ~Command() = default; + + /** + * @brief Execute the command with the given arguments and options + * + * @param args Positional arguments for the command + * @param options Key-value pairs for command options + * @return CommandStatus Return status code + */ + virtual CommandStatus Execute( + const std::vector& args, + const std::unordered_map& options) = 0; + + /** + * @brief Get the name of the command + * + * @return std::string Command name + */ + virtual std::string GetName() const = 0; + + /** + * @brief Get a description of the command + * + * @return std::string Command description + */ + virtual std::string GetDescription() const = 0; + + /** + * @brief Get help information for the command + * + * @return std::string Help text + */ + virtual std::string GetHelp() const = 0; +}; + +} // namespace commands +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/public/command_registry.h b/engine/cli/public/command_registry.h new file mode 100644 index 000000000..52da2bc6b --- /dev/null +++ b/engine/cli/public/command_registry.h @@ -0,0 +1,80 @@ +// commands/public/command_registry.h +#pragma once + +#include +#include +#include +#include "command.h" + +namespace cortex { +namespace commands { + +/** + * @brief Registry for CLI commands + * + * This class manages a collection of command implementations and provides + * lookup functionality. + */ +class CommandRegistry { +public: + /** + * @brief Create a new command registry + * + * @return std::shared_ptr New command registry instance + */ + static std::shared_ptr Create(); + + virtual ~CommandRegistry() = default; + + /** + * @brief Register a command + * + * @param command Command implementation to register + * @return bool True if registration was successful + */ + virtual bool RegisterCommand(std::shared_ptr command) = 0; + + /** + * @brief Find a command by name + * + * @param name Command name to find + * @return std::shared_ptr Command if found, nullptr otherwise + */ + virtual std::shared_ptr FindCommand(const std::string& name) const = 0; + + /** + * @brief Get all registered commands + * + * @return std::vector> List of all commands + */ + virtual std::vector> GetAllCommands() const = 0; + + /** + * @brief Execute a command by name + * + * @param command_name Command name + * @param args Command arguments + * @param options Command options + * @return CommandStatus Execution status + */ + virtual CommandStatus ExecuteCommand( + const std::string& command_name, + const std::vector& args, + const std::unordered_map& options) = 0; + + /** + * @brief Print help for all commands + */ + virtual void PrintHelp() const = 0; + + /** + * @brief Print help for a specific command + * + * @param command_name Command name + * @return bool True if command was found and help printed + */ + virtual bool PrintCommandHelp(const std::string& command_name) const = 0; +}; + +} // namespace commands +} // namespace cortex \ No newline at end of file diff --git a/engine/cli/public/commands.h b/engine/cli/public/commands.h new file mode 100644 index 000000000..b2e704500 --- /dev/null +++ b/engine/cli/public/commands.h @@ -0,0 +1,86 @@ +// commands/public/commands.h +#pragma once + +#include "command.h" +#include "command_registry.h" + +// Forward declarations of all command classes +namespace cortex { +namespace commands { + +// Core commands +class ConfigGetCommand; +class ConfigUpdateCommand; +class ServerStartCommand; +class ServerStopCommand; +class HelpCommand; +class VersionCommand; +class PSCommand; + +// Engine commands +class EngineGetCommand; +class EngineInstallCommand; +class EngineListCommand; +class EngineLoadCommand; +class EngineUninstallCommand; +class EngineUnloadCommand; +class EngineUpdateCommand; +class EngineUseCommand; + +// Hardware commands +class HardwareActivateCommand; +class HardwareListCommand; + +// Model commands +class ModelDeleteCommand; +class ModelGetCommand; +class ModelImportCommand; +class ModelListCommand; +class ModelPullCommand; +class ModelSourceAddCommand; +class ModelSourceDeleteCommand; +class ModelSourceListCommand; +class ModelStartCommand; +class ModelStatusCommand; +class ModelStopCommand; +class ModelUpdateCommand; + +// Interaction commands +class ChatCompletionCommand; +class RunCommand; + +/** + * @brief Register all available commands with the registry + * + * @param registry Command registry + * @param service_provider Service provider with all required services + */ +void RegisterAllCommands( + std::shared_ptr registry, + std::shared_ptr service_provider); + +/** + * @brief Factory function to create a chat completion command + * + * @param db_service Database service + * @return std::shared_ptr Chat completion command + */ +std::shared_ptr CreateChatCompletionCommand( + std::shared_ptr db_service); + +/** + * @brief Factory function to create a run command + * + * @param db_service Database service + * @param engine_service Engine service + * @return std::shared_ptr Run command + */ +std::shared_ptr CreateRunCommand( + std::shared_ptr db_service, + std::shared_ptr engine_service); + +// Add factory functions for all other commands +// ... + +} // namespace commands +} // namespace cortex \ No newline at end of file