Skip to content

Commit

Permalink
feat: cortex update command (#1095)
Browse files Browse the repository at this point in the history
  • Loading branch information
vansangpfiev committed Sep 6, 2024
1 parent 209c06a commit 390111c
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 2 deletions.
171 changes: 171 additions & 0 deletions engine/commands/cortex_upd_cmd.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// clang-format off
#include "utils/cortex_utils.h"
// clang-format on
#include "cortex_upd_cmd.h"
#include "httplib.h"
#include "nlohmann/json.hpp"
#include "services/download_service.h"
#include "utils/archive_utils.h"
#include "utils/file_manager_utils.h"
#include "utils/logging_utils.h"
#include "utils/system_info_utils.h"

namespace commands {

namespace {
const std::string kCortexBinary = "cortex-cpp";
}

CortexUpdCmd::CortexUpdCmd() {}

void CortexUpdCmd::Exec(std::string v) {
// TODO(sang) stop server if it is running
// Check if the architecture and OS are 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;
}
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());

httplib::Client cli(github_host);
if (auto res = cli.Get(release_path.str())) {
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<std::string>();
if (asset_name.find("cortex-cpp") != std::string::npos &&
asset_name.find(os_arch) != 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;
}
CTL_INF("Matched variant: " << matched_variant);

for (auto& asset : assets) {
auto asset_name = asset["name"].get<std::string>();
if (asset_name == matched_variant) {
std::string host{"https://github.com"};

auto full_url = asset["browser_download_url"].get<std::string>();
std::string path = full_url.substr(host.length());

auto fileName = asset["name"].get<std::string>();
CTL_INF("URL: " << full_url);

auto download_task = DownloadTask{.id = "cortex",
.type = DownloadType::Cortex,
.error = std::nullopt,
.items = {DownloadItem{
.id = "cortex",
.host = host,
.fileName = fileName,
.type = DownloadType::Cortex,
.path = path,
}}};

DownloadService download_service;
download_service.AddDownloadTask(
download_task,
[this](const std::string& absolute_path, bool unused) {
// try to unzip the downloaded file
std::filesystem::path download_path{absolute_path};
CTL_INF("Downloaded engine path: " << download_path.string());

std::filesystem::path extract_path =
download_path.parent_path().parent_path();

archive_utils::ExtractArchive(download_path.string(),
extract_path.string());

// remove the downloaded file
// TODO(any) Could not delete file on Windows because it is currently hold by httplib(?)
// Not sure about other platforms
try {
std::filesystem::remove(absolute_path);
} catch (const std::exception& e) {
CTL_WRN("Could not delete file: " << e.what());
}
CTL_INF("Finished!");
});
break;
}
}
} catch (const nlohmann::json::parse_error& e) {
std::cerr << "JSON parse error: " << e.what() << std::endl;
return;
}
} else {
CTL_ERR("HTTP error: " << res->status);
return;
}
} else {
auto err = res.error();
CTL_ERR("HTTP error: " << httplib::to_string(err));
return;
}
#if defined(_WIN32)
auto executable_path = file_manager_utils::GetExecutableFolderContainerPath();
auto temp = executable_path / "cortex_tmp.exe";
remove(temp.string().c_str()); // ignore return code

auto src =
executable_path / "cortex" / kCortexBinary / (kCortexBinary + ".exe");
auto dst = executable_path / (kCortexBinary + ".exe");
// Rename
rename(dst.string().c_str(), temp.string().c_str());
// Update
CopyFile(const_cast<char*>(src.string().c_str()),
const_cast<char*>(dst.string().c_str()), false);
auto download_folder = executable_path / "cortex";
remove(download_folder);
remove(temp.string().c_str());
#else
auto executable_path = file_manager_utils::GetExecutableFolderContainerPath();
auto temp = executable_path / "cortex_tmp";
auto src = executable_path / "cortex" / kCortexBinary / kCortexBinary;
auto dst = executable_path / kCortexBinary;
if (std::rename(dst.string().c_str(), temp.string().c_str())) {
CTL_ERR("Failed to rename from " << dst.string() << " to "
<< temp.string());
return;
}
try {
std::filesystem::copy_file(
src, dst, std::filesystem::copy_options::overwrite_existing);
std::filesystem::permissions(dst, std::filesystem::perms::owner_all |
std::filesystem::perms::group_all |
std::filesystem::perms::others_read |
std::filesystem::perms::others_exec);
std::filesystem::remove(temp);
auto download_folder = executable_path / "cortex/";
std::filesystem::remove_all(download_folder);
} catch (const std::exception& e) {
CTL_WRN("Something wrong happened: " << e.what());
return;
}
#endif
CLI_LOG("Update cortex sucessfully");
}
} // namespace commands
13 changes: 13 additions & 0 deletions engine/commands/cortex_upd_cmd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#pragma once
#include <string>
#include <optional>

namespace commands {

class CortexUpdCmd{
public:
CortexUpdCmd();
void Exec(std::string version);
};

} // namespace commands
61 changes: 61 additions & 0 deletions engine/controllers/command_line_parser.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "command_line_parser.h"
#include "commands/chat_cmd.h"
#include "commands/cmd_info.h"
#include "commands/cortex_upd_cmd.h"
#include "commands/engine_get_cmd.h"
#include "commands/engine_init_cmd.h"
#include "commands/engine_list_cmd.h"
Expand All @@ -13,6 +14,7 @@
#include "commands/run_cmd.h"
#include "commands/server_stop_cmd.h"
#include "config/yaml_config.h"
#include "httplib.h"
#include "services/engine_service.h"
#include "utils/cortex_utils.h"
#include "utils/logging_utils.h"
Expand All @@ -21,6 +23,7 @@ CommandLineParser::CommandLineParser()
: app_("Cortex.cpp CLI"), engine_service_{EngineService()} {}

bool CommandLineParser::SetupCommand(int argc, char** argv) {

std::string model_id;

// Models group commands
Expand Down Expand Up @@ -158,7 +161,65 @@ bool CommandLineParser::SetupCommand(int argc, char** argv) {

app_.add_flag("--verbose", log_verbose, "Verbose logging");

// cortex version
auto cb = [&](int c) {
#ifdef CORTEX_CPP_VERSION
CLI_LOG(CORTEX_CPP_VERSION);
#else
CLI_LOG("default");
#endif
};
app_.add_flag_function("-v", cb, "Cortex version");

std::string cortex_version;
bool check_update = true;
{
auto update_cmd = app_.add_subcommand("update", "Update cortex version");

update_cmd->add_option("-v", cortex_version, "");
update_cmd->callback([&cortex_version, &check_update] {
commands::CortexUpdCmd cuc;
cuc.Exec(cortex_version);
check_update = false;
});
}

CLI11_PARSE(app_, argc, argv);

// Check new update, only check for stable release for now
#ifdef CORTEX_CPP_VERSION
if (check_update) {
constexpr auto github_host = "https://api.github.com";
std::ostringstream release_path;
release_path << "/repos/janhq/cortex.cpp/releases/latest";
CTL_INF("Engine release path: " << github_host << release_path.str());

httplib::Client cli(github_host);
if (auto res = cli.Get(release_path.str())) {
if (res->status == httplib::StatusCode::OK_200) {
try {
auto json_res = nlohmann::json::parse(res->body);
std::string latest_version = json_res["tag_name"].get<std::string>();
std::string current_version = CORTEX_CPP_VERSION;
if (current_version != latest_version) {
CLI_LOG("\nA new release of cortex is available: "
<< current_version << " -> " << latest_version);
CLI_LOG("To upgrade, run: cortex update");
CLI_LOG(json_res["html_url"].get<std::string>());
}
} catch (const nlohmann::json::parse_error& e) {
CTL_INF("JSON parse error: " << e.what());
}
} else {
CTL_INF("HTTP error: " << res->status);
}
} else {
auto err = res.error();
CTL_INF("HTTP error: " << httplib::to_string(err));
}
}
#endif

return true;
}

Expand Down
2 changes: 1 addition & 1 deletion engine/services/download_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include <optional>
#include <vector>

enum class DownloadType { Model, Engine, Miscellaneous, CudaToolkit };
enum class DownloadType { Model, Engine, Miscellaneous, CudaToolkit, Cortex };

enum class DownloadStatus {
Pending,
Expand Down
4 changes: 4 additions & 0 deletions engine/utils/file_manager_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ inline std::filesystem::path GetContainerFolderPath(
container_folder_path = GetEnginesContainerPath();
} else if (type == "CudaToolkit") {
container_folder_path = current_path;
} else if (type == "Cortex") {
container_folder_path = current_path / "cortex";
} else {
container_folder_path = current_path / "misc";
}
Expand All @@ -158,6 +160,8 @@ inline std::string downloadTypeToString(DownloadType type) {
return "Misc";
case DownloadType::CudaToolkit:
return "CudaToolkit";
case DownloadType::Cortex:
return "Cortex";
default:
return "UNKNOWN";
}
Expand Down
3 changes: 2 additions & 1 deletion engine/utils/logging_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ inline bool log_verbose = false;
LOG_INFO << msg; \
} else { \
std::cout << msg << std::endl; \
}
}

0 comments on commit 390111c

Please sign in to comment.