This repository was archived by the owner on Jul 4, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 182
feat: cortex pull and cortex engines install CLI uses API server #1550
Merged
Merged
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
1eb34b2
fix: add ws and indicators
sangjanai ff2888c
Merge branch 'dev' of https://github.com/janhq/cortex.cpp into feat/w…
sangjanai 0c12e5c
fix: more
sangjanai 9547b45
fix: pull models info from server
sangjanai 727eb23
Merge branch 'dev' of https://github.com/janhq/cortex.cpp into feat/c…
sangjanai 808ff24
fix: model_source
sangjanai 9405117
fix: rename
sangjanai 53d85de
fix: download cortexso
sangjanai c43d972
fix: pull models
sangjanai a601ad8
fix: remove comments
sangjanai 1979cb8
Merge branch 'dev' of https://github.com/janhq/cortex.cpp into feat/c…
sangjanai b762a19
fix: rename
sangjanai 84653ae
fix: change download UI
sangjanai 0146cc2
fix: comment out
sangjanai 303e70f
fix: e2e tests
sangjanai aed339a
Merge branch 'dev' into feat/cli-websockets
vansangpfiev 3f2454e
fix: run, start
sangjanai daa0473
Merge branch 'feat/cli-websockets' of https://github.com/janhq/cortex…
sangjanai f756839
fix: start server
sangjanai beb44ed
fix: e2e
sangjanai ca1b436
fix: remove
vansangpfiev a30da37
fix: abort model
sangjanai 2703b98
fix: build
vansangpfiev 300f199
fix: clean code
sangjanai d355bf3
fix: clean more
sangjanai cbe37e0
fix: normalize engine id
sangjanai 4fd7d5d
fix: use auto
sangjanai c92bc68
fix: use vcpkg for indicators
sangjanai e7aabe7
fix: download progress
sangjanai File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,68 @@ | ||
| #include "engine_install_cmd.h" | ||
| #include "server_start_cmd.h" | ||
| #include "utils/download_progress.h" | ||
| #include "utils/engine_constants.h" | ||
| #include "utils/json_helper.h" | ||
| #include "utils/logging_utils.h" | ||
|
|
||
| namespace commands { | ||
|
|
||
| void EngineInstallCmd::Exec(const std::string& engine, | ||
| bool EngineInstallCmd::Exec(const std::string& engine, | ||
| const std::string& version, | ||
| const std::string& src) { | ||
| auto result = engine_service_.InstallEngine(engine, version, src); | ||
| if (result.has_error()) { | ||
| CLI_LOG(result.error()); | ||
| } else if(result && result.value()){ | ||
| CLI_LOG("Engine " << engine << " installed successfully!"); | ||
| // Handle local install, if fails, fallback to remote install | ||
| if (!src.empty()) { | ||
| auto res = engine_service_.UnzipEngine(engine, version, src); | ||
| if (res.has_error()) { | ||
| CLI_LOG(res.error()); | ||
| return false; | ||
| } | ||
| if (res.value()) { | ||
| CLI_LOG("Engine " << engine << " installed successfully!"); | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| // Start server if server is not started yet | ||
| if (!commands::IsServerAlive(host_, port_)) { | ||
| CLI_LOG("Starting server ..."); | ||
| commands::ServerStartCmd ssc; | ||
| if (!ssc.Exec(host_, port_)) { | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| httplib::Client cli(host_ + ":" + std::to_string(port_)); | ||
| Json::Value json_data; | ||
| auto data_str = json_data.toStyledString(); | ||
| cli.set_read_timeout(std::chrono::seconds(60)); | ||
| auto res = cli.Post("/v1/engines/install/" + engine, httplib::Headers(), | ||
| data_str.data(), data_str.size(), "application/json"); | ||
|
|
||
| if (res) { | ||
| if (res->status != httplib::StatusCode::OK_200) { | ||
| auto root = json_helper::ParseJsonString(res->body); | ||
| CLI_LOG(root["message"].asString()); | ||
| return false; | ||
| } | ||
| } else { | ||
| auto err = res.error(); | ||
| CTL_ERR("HTTP error: " << httplib::to_string(err)); | ||
| return false; | ||
| } | ||
|
|
||
| CLI_LOG("Start downloading ...") | ||
| DownloadProgress dp; | ||
| dp.Connect(host_, port_); | ||
| if (!dp.Handle(engine)) | ||
| return false; | ||
|
|
||
| bool check_cuda_download = !system_info_utils::GetCudaVersion().empty(); | ||
| if (check_cuda_download) { | ||
| if (!dp.Handle("cuda")) | ||
| return false; | ||
| } | ||
|
|
||
| CLI_LOG("Engine " << engine << " downloaded successfully!") | ||
| return true; | ||
| } | ||
| }; // namespace commands |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,183 @@ | ||
| #include "model_pull_cmd.h" | ||
| #include <memory> | ||
| #include "cli/utils/easywsclient.hpp" | ||
| #include "cli/utils/indicators.hpp" | ||
| #include "common/event.h" | ||
| #include "database/models.h" | ||
| #include "server_start_cmd.h" | ||
| #include "utils/cli_selection_utils.h" | ||
| #include "utils/download_progress.h" | ||
| #include "utils/format_utils.h" | ||
| #include "utils/huggingface_utils.h" | ||
| #include "utils/json_helper.h" | ||
| #include "utils/logging_utils.h" | ||
| #include "utils/scope_exit.h" | ||
| #include "utils/string_utils.h" | ||
| #if defined(_WIN32) | ||
| #include <signal.h> | ||
| #endif | ||
|
|
||
| namespace commands { | ||
| void ModelPullCmd::Exec(const std::string& input) { | ||
| auto result = model_service_.DownloadModel(input); | ||
| if (result.has_error()) { | ||
| CLI_LOG(result.error()); | ||
| std::function<void(int)> shutdown_handler; | ||
| inline void signal_handler(int signal) { | ||
| if (shutdown_handler) { | ||
| shutdown_handler(signal); | ||
| } | ||
| } | ||
| std::optional<std::string> ModelPullCmd::Exec(const std::string& host, int port, | ||
| const std::string& input) { | ||
|
|
||
| // model_id: use to check the download progress | ||
| // model: use as a parameter for pull API | ||
| std::string model_id = input; | ||
| std::string model = input; | ||
|
|
||
| // Start server if server is not started yet | ||
| if (!commands::IsServerAlive(host, port)) { | ||
| CLI_LOG("Starting server ..."); | ||
| commands::ServerStartCmd ssc; | ||
| if (!ssc.Exec(host, port)) { | ||
| return std::nullopt; | ||
| } | ||
| } | ||
|
|
||
| // Get model info from Server | ||
| httplib::Client cli(host + ":" + std::to_string(port)); | ||
| cli.set_read_timeout(std::chrono::seconds(60)); | ||
| Json::Value j_data; | ||
| j_data["model"] = input; | ||
| auto d_str = j_data.toStyledString(); | ||
| auto res = cli.Post("/models/pull/info", httplib::Headers(), d_str.data(), | ||
| d_str.size(), "application/json"); | ||
|
|
||
| if (res) { | ||
| if (res->status == httplib::StatusCode::OK_200) { | ||
| // CLI_LOG(res->body); | ||
| auto root = json_helper::ParseJsonString(res->body); | ||
| std::string id = root["id"].asString(); | ||
| bool is_cortexso = root["modelSource"].asString() == "cortexso"; | ||
| std::string default_branch = root["defaultBranch"].asString(); | ||
vansangpfiev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| std::vector<std::string> downloaded; | ||
| for (auto const& v : root["downloadedModels"]) { | ||
| downloaded.push_back(v.asString()); | ||
| } | ||
| std::vector<std::string> avails; | ||
| for (auto const& v : root["availableModels"]) { | ||
| avails.push_back(v.asString()); | ||
| } | ||
| std::string download_url = root["downloadUrl"].asString(); | ||
vansangpfiev marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if (downloaded.empty() && avails.empty()) { | ||
| model_id = id; | ||
| model = download_url; | ||
| } else { | ||
| if (is_cortexso) { | ||
| auto selection = cli_selection_utils::PrintModelSelection( | ||
| downloaded, avails, | ||
| default_branch.empty() | ||
| ? std::nullopt | ||
| : std::optional<std::string>(default_branch)); | ||
|
|
||
| if (!selection.has_value()) { | ||
| CLI_LOG("Invalid selection"); | ||
| return std::nullopt; | ||
| } | ||
| model_id = selection.value(); | ||
| model = model_id; | ||
| } else { | ||
| auto selection = cli_selection_utils::PrintSelection(avails); | ||
| CLI_LOG("Selected: " << selection.value()); | ||
| model_id = id + ":" + selection.value(); | ||
| model = download_url + selection.value(); | ||
| } | ||
| } | ||
| } else { | ||
| auto root = json_helper::ParseJsonString(res->body); | ||
| CLI_LOG(root["message"].asString()); | ||
| return std::nullopt; | ||
| } | ||
| } else { | ||
| auto err = res.error(); | ||
| CTL_ERR("HTTP error: " << httplib::to_string(err)); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| // Send request download model to server | ||
| Json::Value json_data; | ||
| json_data["model"] = model; | ||
| auto data_str = json_data.toStyledString(); | ||
| cli.set_read_timeout(std::chrono::seconds(60)); | ||
| res = cli.Post("/v1/models/pull", httplib::Headers(), data_str.data(), | ||
| data_str.size(), "application/json"); | ||
|
|
||
| if (res) { | ||
| if (res->status != httplib::StatusCode::OK_200) { | ||
| auto root = json_helper::ParseJsonString(res->body); | ||
| CLI_LOG(root["message"].asString()); | ||
| return std::nullopt; | ||
| } | ||
| } else { | ||
| auto err = res.error(); | ||
| CTL_ERR("HTTP error: " << httplib::to_string(err)); | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| CLI_LOG("Start downloading ...") | ||
| DownloadProgress dp; | ||
| bool force_stop = false; | ||
|
|
||
| shutdown_handler = [this, &dp, &host, &port, &model_id, &force_stop](int) { | ||
| force_stop = true; | ||
| AbortModelPull(host, port, model_id); | ||
| dp.ForceStop(); | ||
| }; | ||
|
|
||
| utils::ScopeExit se([]() { shutdown_handler = {}; }); | ||
| #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) | ||
| struct sigaction sigint_action; | ||
| sigint_action.sa_handler = signal_handler; | ||
| sigemptyset(&sigint_action.sa_mask); | ||
| sigint_action.sa_flags = 0; | ||
| sigaction(SIGINT, &sigint_action, NULL); | ||
| sigaction(SIGTERM, &sigint_action, NULL); | ||
| #elif defined(_WIN32) | ||
| auto console_ctrl_handler = +[](DWORD ctrl_type) -> BOOL { | ||
| return (ctrl_type == CTRL_C_EVENT) ? (signal_handler(SIGINT), true) : false; | ||
| }; | ||
| SetConsoleCtrlHandler( | ||
| reinterpret_cast<PHANDLER_ROUTINE>(console_ctrl_handler), true); | ||
| #endif | ||
| dp.Connect(host, port); | ||
| if (!dp.Handle(model_id)) | ||
| return std::nullopt; | ||
| if (force_stop) | ||
| return std::nullopt; | ||
| CLI_LOG("Model " << model_id << " downloaded successfully!") | ||
| return model_id; | ||
| } | ||
|
|
||
| bool ModelPullCmd::AbortModelPull(const std::string& host, int port, | ||
| const std::string& task_id) { | ||
| Json::Value json_data; | ||
| json_data["taskId"] = task_id; | ||
| auto data_str = json_data.toStyledString(); | ||
| httplib::Client cli(host + ":" + std::to_string(port)); | ||
| cli.set_read_timeout(std::chrono::seconds(60)); | ||
| auto res = cli.Delete("/v1/models/pull", httplib::Headers(), data_str.data(), | ||
| data_str.size(), "application/json"); | ||
| if (res) { | ||
| if (res->status == httplib::StatusCode::OK_200) { | ||
| CTL_INF("Abort model pull successfully: " << task_id); | ||
| return true; | ||
| } else { | ||
| auto root = json_helper::ParseJsonString(res->body); | ||
| CLI_LOG(root["message"].asString()); | ||
| return false; | ||
| } | ||
| } else { | ||
| auto err = res.error(); | ||
| CTL_ERR("HTTP error: " << httplib::to_string(err)); | ||
| return false; | ||
| } | ||
| } | ||
| }; // namespace commands | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.