Skip to content
This repository was archived by the owner on Jul 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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; \
}
}