diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index c4afdaff4..84f279b27 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -2,18 +2,6 @@ cmake_minimum_required(VERSION 3.5) project(cortex C CXX) -# Build using CMAKE-JS -if(DEFINED CMAKE_JS_INC) - if(WIN32) - add_definitions( - -DV8_COMPRESS_POINTERS - -DV8_REVERSE_JSARGS - -DV8_COMPRESS_POINTERS_IN_ISOLATE_CAGE - ) - endif() - include_directories(${CMAKE_JS_INC}) -endif() - include(CheckIncludeFileCXX) check_include_file_cxx(any HAS_ANY) @@ -33,8 +21,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) set(OPENSSL_USE_STATIC_LIBS TRUE) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# set(CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/build-deps/_install) -# This is the critical line for installing another package if(MSVC) add_compile_options( @@ -43,16 +29,6 @@ if(MSVC) $<$:/MT> #--| ) endif() - -if(LLAMA_CUDA) - cmake_minimum_required(VERSION 3.17) - - find_package(CUDAToolkit) - if(CUDAToolkit_FOUND) - message(STATUS "cuBLAS found") - add_compile_definitions(GGML_USE_CUDA) - endif() -endif() if(DEBUG) message(STATUS "CORTEX-CPP DEBUG IS ON") @@ -63,6 +39,17 @@ if(NOT DEFINED CORTEX_VARIANT) set(CORTEX_VARIANT "prod") endif() +set(TARGET_NAME ${PROJECT_NAME}) +if(RENAME_EXE) + if(CORTEX_VARIANT STREQUAL "beta") + set(TARGET_NAME "${PROJECT_NAME}-beta") + elseif(CORTEX_VARIANT STREQUAL "nightly") + set(TARGET_NAME "${PROJECT_NAME}-nightly") + else() + set(TARGET_NAME ${PROJECT_NAME}) + endif() +endif() + if(NOT DEFINED CORTEX_CONFIG_FILE_PATH) set(CORTEX_CONFIG_FILE_PATH "user_home") endif() @@ -70,22 +57,6 @@ endif() if(NOT DEFINED CORTEX_CPP_VERSION) set(CORTEX_CPP_VERSION "default_version") endif() - -if(APPLE) - if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm.*|ARM64)$") - # MacOS silicon - set(LLAMA_METAL_EMBED_LIBRARY ON) - set(WHISPER_COREML 1) - else() - # MacOS amd64 - set(LLAMA_METAL OFF) - endif() -endif() - -if(DEFINED CMAKE_JS_INC) - # define NPI_VERSION - add_compile_definitions(NAPI_VERSION=8) -endif() add_compile_definitions(CORTEX_VARIANT="${CORTEX_VARIANT}") add_compile_definitions(CORTEX_CPP_VERSION="${CORTEX_CPP_VERSION}") @@ -108,73 +79,29 @@ find_package(LibArchive REQUIRED) find_package(tabulate CONFIG REQUIRED) find_package(CURL REQUIRED) -# Build using CMAKE-JS -if(DEFINED CMAKE_JS_INC) - if(("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") - endif() - - add_library(${PROJECT_NAME} SHARED addon.cc +add_executable(${TARGET_NAME} main.cc ${CMAKE_CURRENT_SOURCE_DIR}/utils/cpuid/cpu_info.cc ${CMAKE_CURRENT_SOURCE_DIR}/utils/file_logger.cc - ${CMAKE_JS_SRC} ) - if(WIN32) - target_link_libraries(${PROJECT_NAME} - PRIVATE - msvcprt.lib - msvcrt.lib - vcruntime.lib - ucrt.lib - ${CMAKE_JS_LIB} - ) - endif() -else() # Official build - add_executable(${PROJECT_NAME} main.cc - ${CMAKE_CURRENT_SOURCE_DIR}/utils/cpuid/cpu_info.cc - ${CMAKE_CURRENT_SOURCE_DIR}/utils/file_logger.cc - ) -endif() - -# ############################################################################## -# If you include the drogon source code locally in your project, use this method -# to add drogon add_subdirectory(cortex-cpp-deps) -# target_link_libraries(${PROJECT_NAME} PRIVATE cortex-cpp-deps) -# -# and comment out the following lines - -target_link_libraries(${PROJECT_NAME} PRIVATE httplib::httplib) -target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json) -target_link_libraries(${PROJECT_NAME} PRIVATE jinja2cpp) -target_link_libraries(${PROJECT_NAME} PRIVATE CLI11::CLI11) -target_link_libraries(${PROJECT_NAME} PRIVATE unofficial::minizip::minizip) -target_link_libraries(${PROJECT_NAME} PRIVATE LibArchive::LibArchive) -target_link_libraries(${PROJECT_NAME} PRIVATE tabulate::tabulate) -target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl) - -# Build using CMAKE-JS -if(DEFINED CMAKE_JS_INC) - set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node") - - target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon - ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_JS_LIB}) - - if(MSVC AND CMAKE_JS_NODELIB_DEF AND CMAKE_JS_NODELIB_TARGET) - # Generate node.lib - execute_process(COMMAND ${CMAKE_AR} /def:${CMAKE_JS_NODELIB_DEF} /out:${CMAKE_JS_NODELIB_TARGET} ${CMAKE_STATIC_LINKER_FLAGS}) - endif() -else() - target_link_libraries(${PROJECT_NAME} PRIVATE JsonCpp::JsonCpp Drogon::Drogon OpenSSL::SSL OpenSSL::Crypto yaml-cpp::yaml-cpp +target_link_libraries(${TARGET_NAME} PRIVATE httplib::httplib) +target_link_libraries(${TARGET_NAME} PRIVATE nlohmann_json::nlohmann_json) +target_link_libraries(${TARGET_NAME} PRIVATE jinja2cpp) +target_link_libraries(${TARGET_NAME} PRIVATE CLI11::CLI11) +target_link_libraries(${TARGET_NAME} PRIVATE unofficial::minizip::minizip) +target_link_libraries(${TARGET_NAME} PRIVATE LibArchive::LibArchive) +target_link_libraries(${TARGET_NAME} PRIVATE tabulate::tabulate) +target_link_libraries(${TARGET_NAME} PRIVATE CURL::libcurl) +target_link_libraries(${TARGET_NAME} PRIVATE JsonCpp::JsonCpp Drogon::Drogon OpenSSL::SSL OpenSSL::Crypto yaml-cpp::yaml-cpp ${CMAKE_THREAD_LIBS_INIT}) -endif() + # ############################################################################## if(CMAKE_CXX_STANDARD LESS 17) # With C++14, use boost to support any and std::string_view message(STATUS "use c++14") find_package(Boost 1.61.0 REQUIRED) - target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) + target_include_directories(${TARGET_NAME} PRIVATE ${Boost_INCLUDE_DIRS}) elseif(CMAKE_CXX_STANDARD LESS 20) message(STATUS "use c++17") else() @@ -189,6 +116,6 @@ aux_source_directory(cortex-common CORTEX_COMMON) aux_source_directory(config CONFIG_SRC) aux_source_directory(commands COMMANDS_SRC) -target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) +target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ) -target_sources(${PROJECT_NAME} PRIVATE ${COMMANDS_SRC} ${CONFIG_SRC} ${CTL_SRC} ${COMMON_SRC} ${SERVICES_SRC}) +target_sources(${TARGET_NAME} PRIVATE ${COMMANDS_SRC} ${CONFIG_SRC} ${CTL_SRC} ${COMMON_SRC} ${SERVICES_SRC}) diff --git a/engine/commands/cortex_upd_cmd.cc b/engine/commands/cortex_upd_cmd.cc index 7423168f1..7c2a1f423 100644 --- a/engine/commands/cortex_upd_cmd.cc +++ b/engine/commands/cortex_upd_cmd.cc @@ -23,93 +23,101 @@ void CortexUpdCmd::Exec(std::string v) { ssc.Exec(); } } - if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) { - if (!GetNightly(v)) + if (CORTEX_VARIANT == file_manager_utils::kProdVariant) { + if (!GetStable(v)) + return; + } else if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) { + if (!GetBeta(v)) return; } else { - if (!GetStableAndBeta(v)) + if (!GetNightly(v)) return; } CLI_LOG("Update cortex sucessfully"); } -bool CortexUpdCmd::GetStableAndBeta(const std::string& v) { +bool CortexUpdCmd::GetStable(const std::string& v) { auto system_info = system_info_utils::GetSystemInfo(); CTL_INF("OS: " << system_info.os << ", Arch: " << system_info.arch); // Download file - constexpr auto github_host = "https://api.github.com"; - // std::string version = v.empty() ? "latest" : std::move(v); - // TODO(sang): support download with version - std::string version = "latest"; - std::ostringstream release_path; - release_path << "/repos/janhq/cortex.cpp/releases/" << version; - CTL_INF("Engine release path: " << github_host << release_path.str()); + auto github_host = GetHostName(); + auto release_path = GetReleasePath(); + CTL_INF("Engine release path: " << github_host << release_path); httplib::Client cli(github_host); - if (auto res = cli.Get(release_path.str())) { + if (auto res = cli.Get(release_path)) { if (res->status == httplib::StatusCode::OK_200) { try { - auto jsonResponse = nlohmann::json::parse(res->body); - auto assets = jsonResponse["assets"]; - auto os_arch{system_info.os + "-" + system_info.arch}; - - std::string matched_variant = ""; - for (auto& asset : assets) { - auto asset_name = asset["name"].get(); - if (asset_name.find(kCortexBinary) != std::string::npos && - asset_name.find(os_arch) != std::string::npos) { - matched_variant = asset_name; - break; - } - CTL_INF(asset_name); + auto json_data = nlohmann::json::parse(res->body); + if (json_data.empty()) { + CLI_LOG("Version not found: " << v); + return false; } - if (matched_variant.empty()) { - CTL_ERR("No variant found for " << os_arch); + + if (!HandleGithubRelease(json_data["assets"], + {system_info.os + "-" + system_info.arch})) { return false; } - CTL_INF("Matched variant: " << matched_variant); - - for (auto& asset : assets) { - auto asset_name = asset["name"].get(); - if (asset_name == matched_variant) { - auto download_url = - asset["browser_download_url"].get(); - auto file_name = asset["name"].get(); - CTL_INF("Download url: " << download_url); - - auto local_path = - file_manager_utils::GetExecutableFolderContainerPath() / - "cortex" / asset_name; - auto download_task{DownloadTask{.id = "cortex", - .type = DownloadType::Cortex, - .items = {DownloadItem{ - .id = "cortex", - .downloadUrl = download_url, - .localPath = local_path, - }}}}; - - DownloadService().AddDownloadTask( - download_task, [](const DownloadTask& finishedTask) { - // try to unzip the downloaded file - CTL_INF("Downloaded engine path: " - << finishedTask.items[0].localPath.string()); - - auto extract_path = finishedTask.items[0] - .localPath.parent_path() - .parent_path(); - - archive_utils::ExtractArchive( - finishedTask.items[0].localPath.string(), - extract_path.string()); - - CTL_INF("Finished!"); - }); + } catch (const nlohmann::json::parse_error& e) { + CTL_ERR("JSON parse error: " << e.what()); + return false; + } + } else { + CTL_ERR("HTTP error: " << res->status); + return false; + } + } else { + auto err = res.error(); + CTL_ERR("HTTP error: " << httplib::to_string(err)); + return false; + } + + // Replace binary file + auto executable_path = file_manager_utils::GetExecutableFolderContainerPath(); + auto src = std::filesystem::temp_directory_path() / "cortex" / kCortexBinary / + GetCortexBinary(); + auto dst = executable_path / GetCortexBinary(); + return ReplaceBinaryInflight(src, dst); +} + +bool CortexUpdCmd::GetBeta(const std::string& v) { + auto system_info = system_info_utils::GetSystemInfo(); + CTL_INF("OS: " << system_info.os << ", Arch: " << system_info.arch); + + // Download file + auto github_host = GetHostName(); + auto release_path = GetReleasePath(); + CTL_INF("Engine release path: " << github_host << release_path); + + httplib::Client cli(github_host); + if (auto res = cli.Get(release_path)) { + if (res->status == httplib::StatusCode::OK_200) { + try { + auto json_res = nlohmann::json::parse(res->body); + + nlohmann::json json_data; + for (auto& jr : json_res) { + // Get the latest beta or match version + if (auto tag = jr["tag_name"].get(); + (v.empty() && tag.find(kBetaComp) != std::string::npos) || + (tag == v)) { + json_data = jr; break; } } + + if (json_data.empty()) { + CLI_LOG("Version not found: " << v); + return false; + } + + if (!HandleGithubRelease(json_data["assets"], + {system_info.os + "-" + system_info.arch})) { + return false; + } } catch (const nlohmann::json::parse_error& e) { - std::cerr << "JSON parse error: " << e.what() << std::endl; + CTL_ERR("JSON parse error: " << e.what()); return false; } } else { @@ -124,12 +132,77 @@ bool CortexUpdCmd::GetStableAndBeta(const std::string& v) { // Replace binary file auto executable_path = file_manager_utils::GetExecutableFolderContainerPath(); - auto src = std::filesystem::temp_directory_path() / "cortex" / kCortexBinary / - GetCortexBinary(); + auto src = + std::filesystem::temp_directory_path() / "cortex" / GetCortexBinary(); auto dst = executable_path / GetCortexBinary(); return ReplaceBinaryInflight(src, dst); } +bool CortexUpdCmd::HandleGithubRelease(const nlohmann::json& assets, + const std::string& os_arch) { + std::string matched_variant = ""; + for (auto& asset : assets) { + auto asset_name = asset["name"].get(); + if (asset_name.find(kCortexBinary) != std::string::npos && + asset_name.find(os_arch) != std::string::npos && + asset_name.find(kReleaseFormat) != std::string::npos) { + matched_variant = asset_name; + break; + } + CTL_INF(asset_name); + } + if (matched_variant.empty()) { + CTL_ERR("No variant found for " << os_arch); + return false; + } + CTL_INF("Matched variant: " << matched_variant); + + for (auto& asset : assets) { + auto asset_name = asset["name"].get(); + if (asset_name == matched_variant) { + auto download_url = asset["browser_download_url"].get(); + auto file_name = asset["name"].get(); + CTL_INF("Download url: " << download_url); + + auto local_path = + std::filesystem::temp_directory_path() / "cortex" / asset_name; + try { + if (!std::filesystem::exists(local_path.parent_path())) { + std::filesystem::create_directories(local_path.parent_path()); + } + } catch (const std::filesystem::filesystem_error& e) { + CTL_ERR("Failed to create directories: " << e.what()); + return false; + } + auto download_task{DownloadTask{.id = "cortex", + .type = DownloadType::Cortex, + .items = {DownloadItem{ + .id = "cortex", + .downloadUrl = download_url, + .localPath = local_path, + }}}}; + + DownloadService().AddDownloadTask( + download_task, [](const DownloadTask& finishedTask) { + // try to unzip the downloaded file + CTL_INF("Downloaded engine path: " + << finishedTask.items[0].localPath.string()); + + auto extract_path = + finishedTask.items[0].localPath.parent_path().parent_path(); + + archive_utils::ExtractArchive( + finishedTask.items[0].localPath.string(), + extract_path.string()); + + CTL_INF("Finished!"); + }); + break; + } + } + return true; +} + bool CortexUpdCmd::GetNightly(const std::string& v) { auto system_info = system_info_utils::GetSystemInfo(); CTL_INF("OS: " << system_info.os << ", Arch: " << system_info.arch); @@ -153,8 +226,15 @@ bool CortexUpdCmd::GetNightly(const std::string& v) { CTL_INF("Engine release path: " << url_parser::FromUrl(url_obj)); std::filesystem::path localPath = - file_manager_utils::GetExecutableFolderContainerPath() / "cortex" / - path_list.back(); + std::filesystem::temp_directory_path() / "cortex" / path_list.back(); + try { + if (!std::filesystem::exists(localPath.parent_path())) { + std::filesystem::create_directories(localPath.parent_path()); + } + } catch (const std::filesystem::filesystem_error& e) { + CTL_ERR("Failed to create directories: " << e.what()); + return false; + } auto download_task = DownloadTask{.id = "cortex", .type = DownloadType::Cortex, @@ -181,8 +261,8 @@ bool CortexUpdCmd::GetNightly(const std::string& v) { // Replace binary file auto executable_path = file_manager_utils::GetExecutableFolderContainerPath(); - auto src = std::filesystem::temp_directory_path() / "cortex" / kCortexBinary / - GetCortexBinary(); + auto src = + std::filesystem::temp_directory_path() / "cortex" / GetCortexBinary(); auto dst = executable_path / GetCortexBinary(); return ReplaceBinaryInflight(src, dst); } diff --git a/engine/commands/cortex_upd_cmd.h b/engine/commands/cortex_upd_cmd.h index 24aae5725..682fb95d2 100644 --- a/engine/commands/cortex_upd_cmd.h +++ b/engine/commands/cortex_upd_cmd.h @@ -13,6 +13,8 @@ namespace commands { constexpr const auto kNightlyHost = "delta.jan.ai"; constexpr const auto kNightlyFileName = "cortex-nightly.tar.gz"; const std::string kCortexBinary = "cortex"; +constexpr const auto kBetaComp = "-rc"; +constexpr const auto kReleaseFormat = ".tar.gz"; inline std::string GetCortexBinary() { #if defined(_WIN32) @@ -41,6 +43,8 @@ inline std::string GetHostName() { inline std::string GetReleasePath() { if (CORTEX_VARIANT == file_manager_utils::kNightlyVariant) { return "/cortex/latest/version.json"; + } else if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) { + return "/repos/janhq/cortex.cpp/releases"; } else { return "/repos/janhq/cortex.cpp/releases/latest"; } @@ -55,8 +59,31 @@ inline void CheckNewUpdate() { if (auto res = cli.Get(release_path)) { if (res->status == httplib::StatusCode::OK_200) { try { + auto get_latest = [](const nlohmann::json& data) -> std::string { + if (data.empty()) { + return ""; + } + + if (CORTEX_VARIANT == file_manager_utils::kBetaVariant) { + for (auto& d : data) { + if (auto tag = d["tag_name"].get(); + tag.find(kBetaComp) != std::string::npos) { + return tag; + } + } + return data[0]["tag_name"].get(); + } else { + return data["tag_name"].get(); + } + return ""; + }; + auto json_res = nlohmann::json::parse(res->body); - std::string latest_version = json_res["tag_name"].get(); + std::string latest_version = get_latest(json_res); + if (latest_version.empty()) { + CTL_WRN("Release not found!"); + return; + } std::string current_version = CORTEX_CPP_VERSION; if (current_version != latest_version) { CLI_LOG("\nA new release of cortex is available: " @@ -84,6 +111,7 @@ inline bool ReplaceBinaryInflight(const std::filesystem::path& src, // Already has the newest return true; } + std::filesystem::path temp = dst.parent_path() / "cortex_temp"; try { @@ -110,12 +138,20 @@ inline bool ReplaceBinaryInflight(const std::filesystem::path& src, return true; } +// This class manages the 'cortex update' command functionality +// There are three release types available: +// - Stable: Only retrieves the latest version +// - Beta: Allows retrieval of the latest beta version and specific versions using the -v flag +// - Nightly: Enables retrieval of the latest nightly build and specific versions using the -v flag class CortexUpdCmd { public: void Exec(std::string version); private: - bool GetStableAndBeta(const std::string& v); + bool GetStable(const std::string& v); + bool GetBeta(const std::string& v); + bool HandleGithubRelease(const nlohmann::json& assets, + const std::string& os_arch); bool GetNightly(const std::string& v); }; } // namespace commands diff --git a/engine/controllers/command_line_parser.cc b/engine/controllers/command_line_parser.cc index c21f893fd..79080a104 100644 --- a/engine/controllers/command_line_parser.cc +++ b/engine/controllers/command_line_parser.cc @@ -184,7 +184,7 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) { CLI_LOG("default"); #endif }; - app_.add_flag_function("-v", cb, "Cortex version"); + app_.add_flag_function("-v,--version", cb, "Cortex version"); std::string cortex_version; bool check_update = true;